-- This file is  free  software, which  comes  along  with  SmartEiffel. This
-- software  is  distributed  in the hope that it will be useful, but WITHOUT
-- ANY  WARRANTY;  without  even  the  implied warranty of MERCHANTABILITY or
-- FITNESS  FOR A PARTICULAR PURPOSE. You can modify it as you want, provided
-- this header is kept unaltered, and a notification of the changes is added.
-- You  are  allowed  to  redistribute  it and sell it, alone or as a part of
-- another product.
--       Copyright (C) 1994-2002 LORIA - INRIA - U.H.P. Nancy 1 - FRANCE
--          Dominique COLNET and Suzanne COLLIN - SmartEiffel@loria.fr
--                       http://SmartEiffel.loria.fr
--
expanded class INTEGER_GENERAL
--
-- General integer abstraction to share feature definition of INTEGER_8,
-- INTEGER_16, INTEGER, INTEGER_32 and INTEGER_64.
--
-- If you need integers with bigger values, use NUMBER (lib/number cluster).
--

inherit
   NUMERIC
   COMPARABLE
      undefine
	 is_equal, infix "<", infix "<=", infix ">",  infix ">="
      redefine
	 fill_tagged_out_memory, out_in_tagged_out_memory
      end
   
feature
   
   infix "+" (other: like Current): like Current is
      external "SmartEiffel"
      end
   
   infix "-" (other: like Current): like Current is
      external "SmartEiffel"
      end
   
   infix "*" (other: like Current): like Current is
      external "SmartEiffel"
      end
   
   infix "/" (other: like Current): DOUBLE is
      external "SmartEiffel"
      end
   
   infix "//" (other: like Current): like Current is
      external "SmartEiffel"
      end
   
   infix "\\" (other: like Current): like Current is
      external "SmartEiffel"
      end
   
   infix "^" (exp: like Current): INTEGER_64 is
      do
         if exp = 0 then
            Result := 1
         elseif exp \\ 2 = 0 then
            Result := (item * item) ^ (exp // 2)
         else
            Result := item * (item ^ (exp - 1))
         end
      end
   
   abs: like Current is
         -- Absolute value of `Current'.
      do
         if item < 0 then
            Result := - item
         else
            Result := Current
         end
      end
   
   infix "<" (other: like Current): BOOLEAN is
         -- Is 'Current' strictly less than 'other'?
      external "SmartEiffel"
      end
   
   infix "<=" (other: like Current): BOOLEAN is
         -- Is 'Current' less or equal 'other'?
      external "SmartEiffel"
      end
   
   infix ">" (other: like Current): BOOLEAN is
         -- Is 'Current' strictly greater than 'other'?
      external "SmartEiffel"
      end
   
   infix ">=" (other: like Current): BOOLEAN is
         -- Is 'Current' greater or equal than 'other'?
      external "SmartEiffel"
      end
   
   prefix "+": like Current is
      do
         Result := Current
      end
   
   prefix "-": like Current is
      external "SmartEiffel"
      end
   
   odd: BOOLEAN is
         -- Is odd ?
      do
         Result := (item \\ 2) = 1
      end
   
   even: BOOLEAN is
         -- Is even ?
      do
         Result := (item \\ 2) = 0
      end
   
   sqrt: DOUBLE is
         -- Compute the square routine.
      do
         Result := to_double.sqrt
      end
   
   log: DOUBLE is
         -- (ANSI C log).
      do
         Result := to_double.log
      end

   log10: DOUBLE is
         -- (ANSI C log10).
      do
         Result := to_double.log10
      end

   gcd(other: like Current): like Current is
         -- Great Common Divisor of `Current' and `other'.
      require
         Current >= 0
         other >= 0
      do
         if other = 0 then
            Result := Current
         else
            Result := other.gcd(item \\ other)
         end
      ensure
         Result = other.gcd(Current)
      end

feature -- Conversions:

   to_real: REAL is
      do
         Result := Current
      end

   to_double: DOUBLE is
      do
         Result := Current
      end

   to_string: STRING is
         -- Convert the decimal view of `Current' into a new allocated 
         -- STRING. For example, if `Current' is -1 the new STRING is "-1".
         -- Note: see also `append_in' to save memory.
      do
	 string_buffer.clear
         append_in(string_buffer)
         Result := string_buffer.twin
      end

   to_boolean: BOOLEAN is
         -- Return false for 0, otherwise true.
      do
         Result := Current /= 0
      ensure
         Result = (Current /= 0)
      end

   fit_integer_8: BOOLEAN is
	 -- Does `Current' fit on an INTEGER_8 ?
      do
	 if item >= -128 then
	    Result := Current <= 127
	 end
      ensure
	 Result = Current.in_range(-128, 127)
      end
   
   to_integer_8: INTEGER_8 is
	 -- Explicit conversion to INTEGER_8.
      require
	 fit_integer_8
      external "SmartEiffel"
      ensure
	 Current = Result
      end

   fit_integer_16: BOOLEAN is
	 -- Does `Current' fit on an INTEGER_16 ?
      do
	 if item >= -32768 then
	    Result := Current <= 32767
	 end
      ensure
	 Result = Current.in_range(-32768, 32767)
      end
   
   to_integer_16: INTEGER_16 is
	 -- Explicit conversion to INTEGER_16.
      require
	 fit_integer_16
      external "SmartEiffel"
      ensure
	 Current = Result
      end
   
   fit_integer_32, fit_integer: BOOLEAN is
	 -- Does `Current' fit on an INTEGER_32 ?
      do
	 if item >= -2147483648 then
	    Result := item <= 2147483647
	 end
      ensure
	 Result = Current.in_range(-2147483648, 2147483647)
      end
   
   to_integer_32, to_integer: INTEGER_32 is
	 -- Explicit conversion to INTEGER_32.
      require
	 fit_integer_32
      external "SmartEiffel"
      ensure
	 Current = Result
      end
   
   to_integer_64: INTEGER_64 is
	 -- Explicit conversion to INTEGER_64.
      do
	 Result := Current
      ensure
	 Current = Result
      end
   
   to_number: NUMBER is
	 -- *** Note: the require assertion will be removed as soon as 
	 -- we will find some time to update the NUMBER hierarchy.
	 --                              September 20th 2002  D.Colnet
      require
	 fit_integer_32
      local
	 number_tools: NUMBER_TOOLS
      do
	 Result := number_tools.from_integer(Current.to_integer)
      ensure
	 Result @= Current.to_integer
      end

   to_bit: BIT Integer_bits is
         -- Portable BIT_N conversion.
      external "SmartEiffel"
      end

   append_in(buffer: STRING) is
         -- Append in the `buffer' the equivalent of `to_string'. No new 
         -- STRING creation during the process.
      require
         buffer /= Void
      local
         val: INTEGER_64; i, idx: INTEGER
      do
         if Current = 0 then
            buffer.extend('0')
         else
            if Current > 0 then
               from
                  i := buffer.count + 1
                  val := Current
               until
                  val = 0
               loop
                  buffer.extend((val \\ 10).digit)
                  val := val // 10
               end
            else
               buffer.extend('-')
               from
                  i := buffer.count + 1
                  val := Current
               until
                  val = 0
               loop
                  buffer.extend((-(val \\ 10)).digit)
                  val := val // 10
               end
            end
            from
               idx := buffer.count
            until
               i >= idx
            loop
               buffer.swap(i,idx)
               idx := idx - 1
               i := i + 1
            end
         end
      end

   to_string_format(s: INTEGER): STRING is
         -- Same as `to_string' but the result is on `s' character and the
         -- number is right aligned.
         -- Note: see `append_in_format' to save memory.
      require
         to_string.count <= s
      local
	 i: like Current
      do
	 string_buffer.clear
	 append_in(string_buffer)
         from
	    create Result.make(string_buffer.count.max(s))
	    i := s - string_buffer.count
         until
	    i <= 0
         loop
            Result.extend(' ')
	    i := i - 1
	 end
         Result.append(string_buffer)
      ensure
         Result.count = s
      end

   append_in_format(str: STRING; s: like Current) is
         -- Append the equivalent of `to_string_format' at the end of
         -- `str'. Thus you can save memory because no other
         -- STRING is allocate for the job.
      require
	 to_string.count <= s
      local
	 i: like Current
      do
	 string_buffer.clear
	 append_in(string_buffer)
         from
	    i := s - string_buffer.count
         until
            i <= 0
         loop
            str.extend(' ')
	    i := i - 1
         end
         str.append(string_buffer)
      ensure
         str.count >= (old str.count) + s
      end

   decimal_digit, digit: CHARACTER is
         -- Gives the corresponding CHARACTER for range 0..9.
      require
         in_range(0,9)
      do
         Result := (Current + ('0').code).to_character
      ensure
         (once "0123456789").has(Result)
         Result.value = Current
      end

   hexadecimal_digit: CHARACTER is
         -- Gives the corresponding CHARACTER for range 0..15.
      require
         in_range(0,15)
      do
         if item <= 9 then
            Result := digit
         else
            Result := (('A').code + (item - 10)).to_character
         end
      ensure
         (once "0123456789ABCDEF").has(Result)
      end

   to_character: CHARACTER is
         -- Return the coresponding ASCII character.
      require
         Current >= 0
      external "SmartEiffel"
      end

   to_octal: INTEGER is
         -- Gives coresponding octal value.
	 -- *** THE RESULT IS OF TYPE INTEGER (not like Current) ...
	 --     ... IS THIS THE RIGHT CHOICE ???
	 -- ***
      do
         if Current = 0 then
         elseif item < 0 then
            Result := -((-Current).to_octal)
         else
            from
               string_buffer.clear
               Result := Current.to_integer
            until
               Result = 0
            loop
               string_buffer.extend((Result \\ 8).digit)
               Result := Result // 8
            end
            string_buffer.reverse
            Result := string_buffer.to_integer
         end
      end

   to_hexadecimal: STRING is
         -- Convert the hexadecimal view of `Current' into a new allocated
         -- STRING. For example, if `Current' is -1 and if `Current' is a 
         -- 32 bits integer the `Result' is "FFFFFFFF".
         -- Note: see also `to_hexadecimal_in' to save memory.
      do
         string_buffer.clear
         to_hexadecimal_in(string_buffer)
         Result := string_buffer.twin
      ensure
         Result.count = object_size * 2
      end

   to_hexadecimal_in(buffer: STRING) is
         -- Append in `buffer' the equivalent of `to_hexadecimal'. No new 
         -- STRING creation during the process.
      local
         one_digit: like Current; index, times: INTEGER; value: like Current
      do
         from
            value := Current
            times := object_size * 2
            index := buffer.count + times
            buffer.extend_multiple(' ',times)
         until
            times = 0
         loop
            one_digit := value and 15 -- 0xF
            buffer.put(one_digit.hexadecimal_digit,index)
            index := index - 1
            value := value |>> 4
            times := times - 1
         end
      ensure
         buffer.count = old buffer.count + object_size * 2
      end

feature -- Hashing:

   hash_code: INTEGER is
      do
	 Result := item.to_integer_32
         if Result < 0 then
            Result := -(Result + 1)
         end
      end
   
feature -- Bitwise Logical Operators:

   bit_test(idx: INTEGER): BOOLEAN is
	 -- The value of the `idx'-ith bit.
      external "SmartEiffel"
      end
   
   bit_shift(s: INTEGER): like Current is 
	 -- Shift by `s' positions (positive `s' shifts right (sign bit 
	 -- copied), negative shifts left bits falling off the end are lost).
	 -- See also infix "|>>" and infix "|<<".
      external "SmartEiffel"
      end
    
   bit_shift_unsigned(s: INTEGER): like Current is 
         -- Shift by `s' positions (positive `s' shifts right (sign bit not 
	 -- copied), negative left bits falling off the end are lost).
         -- See also infix "|>>>" and infix "|<<".
       external "SmartEiffel"
       end
    
   bit_shift_right, infix "|>>" (s: INTEGER): like Current is 
	 -- Shift by `s' positions right (sign bit copied) bits falling 
	 -- off the end are lost.
      require 
	 s >= 0 
      external "SmartEiffel"
      end
    
   infix "@>>" (s: INTEGER): like Current is
      obsolete "Use %"|>>%" instead of %"@>>%" (september 2002)."
      require
         s > 0
      external "SmartEiffel"
      end

   bit_shift_right_unsigned, infix "|>>>" (s: INTEGER): like Current is 
	 -- Shift by `s' positions right (sign bit not copied) bits 
	 -- falling off the end are lost.
      require 
	 s >= 0
      external "SmartEiffel"
      end

   bit_shift_left, infix "|<<" (s: INTEGER): like Current is 
         -- Shift by `s' positions left bits falling off the end are lost.
      require 
         s >= 0 
      external "SmartEiffel"
      end

   infix "@<<" (s: INTEGER): like Current is
      obsolete "Use %"|<<%" instead of %"@<<%" (september 2002)."
      require
         s > 0
      external "SmartEiffel"
      end

   bit_rotate(s: INTEGER): like Current is 
         -- Rotate by `s' positions (positive `s' shifts
         -- right, negative left
         -- See also infix "#>>" and infix "#<<".
      external "SmartEiffel"
      end
    
   bit_rotate_right, infix "#>>" (s: INTEGER): like Current is 
         -- Rotate by `s' positions right.
      require 
         s >= 0
      external "SmartEiffel"
      end
     
   bit_rotate_left, infix "#<<" (s: INTEGER): like Current is 
        -- Rotate by `s' positions left.
      require 
         s >= 0
      external "SmartEiffel"
      end
    
   bit_not: like Current is 
        -- One's complement of `Current'.
      external "SmartEiffel"
      end
    
   bit_and, infix "&" (other: like Current): like Current is 
        -- Bitwise logical and of `Current' with `other'.
       external "SmartEiffel"
       end
    
   bit_or, infix "|" (other: like Current): like Current is 
        -- Bitwise logical inclusive or of `Current' with `other'.
       external "SmartEiffel"
       end
    
   bit_xor(other: like Current): like Current is 
        -- Bitwise logical exclusive or of `Current' with `other'.
       external "SmartEiffel"
       end
   
   infix "and" (other: like Current): like Current is
        -- Bitwise `and' of `Current' with `other'
      external "SmartEiffel"
      end

   prefix "not": like Current is
         -- Bitwise `not' of `Current'.
      external "SmartEiffel"
      end

   infix "or" (other: like Current): like Current is
         -- Bitwise `or' of `Current' with `other'
      external "SmartEiffel"
      end

   infix "xor" (other : like Current) : like Current is
         -- Bitwise `xor' of `Current' with `other'
      external "SmartEiffel"
      end

feature -- Object Printing:

   out_in_tagged_out_memory, fill_tagged_out_memory is
      do
         Current.append_in(tagged_out_memory)
      end

feature -- Miscellaneous:

   sign: INTEGER_8 is
         -- Sign of `Current' (0 -1 or 1).
      do
	 if item < 0 then
	    Result := -1
	 elseif 0 < item then
	    Result := 1
	 else
	    Result := 0
	 end
      end
   
   one: INTEGER_8 is 1
   
   zero: INTEGER_8 is 0
   
   divisible(other: like Current): BOOLEAN is
      do
         Result := (other /= 0)
      end

feature -- For experts only:
   
   item: like Current
      -- The value of `Current' in the corresponding reference type.
			      
feature {NONE}

   string_buffer: STRING is
      once
         create Result.make(128)
      end

end -- INTEGER_GENERAL
