-- 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
--
deferred class  NUMBER
   --
   -- This abstract definition of a NUMBER is intended to be the unique
   -- view of the client (do not use sub-classes names at all in the
   -- client code). In order to create NUMBERs without using concrete
   -- class name, the client code can inherit NUMBER_TOOLS. (See directory
   -- ${SmartEiffel}/lib_show/number for example.)
   --

inherit
   HASHABLE
      undefine
	 is_equal
      redefine
	 fill_tagged_out_memory, out_in_tagged_out_memory
      end

feature -- Binary operators for two NUMBERs:

   infix "+" (other: NUMBER): NUMBER is
	 -- Sum of `Current' and `other'.
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   infix "-" (other: NUMBER): NUMBER is
	 -- Difference of `Current' and `other'.
      require
	 other /= Void
      do
	 Result := Current + (- other)
      ensure
	 Result /= Void
      end

   infix "*" (other: NUMBER): NUMBER is
	 -- Product of `Current' and `other'.
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   infix "/" (other: NUMBER): NUMBER is
	 -- Quotient of `Current' and `other'.
      require
	 not (other @= 0)
	 divisible(other)
      do
	 Result := Current * other.inverse
      ensure
	 Result /= Void
      end

   infix "//" (other: NUMBER): NUMBER is
	 -- Divide `Current' by `other' (Integer division).
      require
	 is_abstract_integer
	 other.is_abstract_integer
	 divisible (other)
      deferred
      ensure
	 Result.is_abstract_integer
      end

   infix "\\" (other: NUMBER): NUMBER is
	 -- Remainder of division of `Current' by `other'.
      require
	 is_abstract_integer
	 other.is_abstract_integer
	 divisible (other)
      deferred
      ensure
	 Result.is_abstract_integer
      end

   infix "^" (exp: NUMBER): NUMBER is
	 -- `Current' raised to `exp'-th power.
      require
	 exp.is_abstract_integer
	 exp.is_positive
	 not (is_zero and then exp.is_zero)
      local
	 e: NUMBER
	 factor: NUMBER
      do
	 Result := one
	 factor := Current
	 from
	    e := exp
	 until
	    e.is_zero
	 loop
	    if (e @\\ 2) @= 1 then
	       Result := Result * factor
	    end
	    e := e @// 2
	    factor := factor * factor
	 end
      ensure
	 Result /= Void
	 exp.is_zero implies Result.is_one
      end

   infix "<" (other: NUMBER): BOOLEAN is
	 -- Is `Current' strictly less than `other'?
      require
	 other_exists: other /= Void
      deferred
      ensure
	 asymmetric: Result implies not (other <= Current)
      end

   infix "<=" (other: NUMBER): BOOLEAN is
	 -- Is `Current' less or equal than `other'?
      require
	 other_exists: other /= Void
      do
	 Result := not (other < Current)
      ensure
	 definition: Result = (Current < other) or
		     is_equal(other)
      end

   infix ">" (other: NUMBER): BOOLEAN is
	 -- Is `Current' strictly greater than `other'?
      require
	 other_exists: other /= Void
      do
	 Result := (other < Current)
      ensure
	 definition: Result = (other < Current)
      end

   infix ">=" (other: NUMBER): BOOLEAN is
	 -- Is `Current' greater or equal than `other'?
      require
	 other_exists: other /= Void
      do
	 Result := not (Current < other)
      ensure
	 definition: Result = (other <= Current)
      end

   gcd(other: NUMBER): ABSTRACT_INTEGER is
	 -- Great Common Divisor of `Current' and `other'.
      require
	 other.is_abstract_integer
	 is_abstract_integer
	 is_positive
	 other.is_positive
      do
	 if other.is_zero then
	    Result ?= Current
	    check
	       Result /= Void
	    end
	 else
	    Result := other.gcd(Current \\ other)
	 end
      end

feature -- Unary operators for two NUMBERs:

   frozen prefix "+" : NUMBER is
	 -- Unary plus of `Current'.
      do
	 Result := Current
      ensure
	 Result = Current
      end

   prefix "-" : NUMBER is
	 -- Opposite of `Current'.
      deferred
      ensure
	 Result /= Void
      end

feature -- To know more about a NUMBER:

   in_range(lower, upper: NUMBER): BOOLEAN is
	 -- Return true if `Current' is in range [`lower'..`upper']
      do
	 Result := (Current >= lower) and then (Current <= upper)
      ensure
	 Result = (Current >= lower and Current <= upper)
      end

   compare(other: NUMBER): INTEGER is
	 -- Compare `Current' with `other'.
	 -- `<'  <==> `Result < 0'
	 -- `>'  <==> `Result > 0'
	 -- Otherwise `Result = 0'.
      require
	 other /= Void
      do
	 if (Current < other) then
	    Result := -1
	 elseif (other < Current) then
	    Result := 1
	 end
      ensure
	 (Result < 0) = (Current < other)
	 (Result = 0) = not (Current < other or Current > other)
	 (Result > 0) = (Current > other)
      end

   min(other: NUMBER): NUMBER is
	 -- Minimum of `Current' and `other'.
      require
	 other /= Void
      do
	 if Current < other then
	    Result := Current
	 else
	    Result := other
	 end
      ensure
	 Result <= Current and then Result <= other
	 compare(Result) = 0 or else other.compare(Result) = 0
      end

   max(other: NUMBER): NUMBER is
	 -- Maximum of `Current' and `other'.
      require
	 other /= Void
      do
	 if (other < Current) then
	    Result := Current
	 else
	    Result := other
	 end
      ensure
	 (Result >= Current) and then (Result >= other)
	 compare(Result) = 0 or else other.compare(Result) = 0
      end

   is_zero: BOOLEAN is
         -- Is it 0 ?
      deferred
      ensure
	 Result = (Current @= 0)
      end

   is_one: BOOLEAN is
         -- Is it 1 ?
      deferred
      ensure
	 Result = (Current @= 1)
      end

   is_positive: BOOLEAN is
	 -- Is `Current' greater or equal zero ?
      deferred
      ensure
	 Result = (Current @>= 0)
      end

   is_negative: BOOLEAN is
	 -- Is `Current' < 0 ?
      deferred
      ensure
	 Result = (Current @< 0)
      end

   is_odd, odd: BOOLEAN is
	 -- Is `odd' ?
      require
	 is_abstract_integer
      do
	 Result := (Current @\\ 2).is_one
      end

   is_even, even: BOOLEAN is
	 -- Is `even' ?
      require
	 is_abstract_integer
      do
	 Result := (Current @\\ 2).is_zero
      end

   is_equal(other: NUMBER): BOOLEAN is
      deferred
      end

   frozen same_as(other: NUMBER): BOOLEAN is
	 -- Is it the same NUMBER as `other' ?
	 --
	 -- Note: because `is_equal' uses type like Current for
	 -- `other', this is impossible to use directely `is_equal'.
      obsolete "You now have to use `is_equal' (18th nov. 2002)."
      require
	 other /= Void
      do
	 Result := is_equal(other)
      ensure
	 Result implies other.is_equal(Current)
      end

   frozen is_abstract_integer: BOOLEAN is
      local
	 abstract_integer: ABSTRACT_INTEGER
      do
	 abstract_integer ?= Current
	 Result := abstract_integer /= Void
      end

   frozen is_abstract_fraction: BOOLEAN is
      local
	 abstract_fraction : ABSTRACT_FRACTION
      do
	 abstract_fraction ?= Current
	 Result := abstract_fraction /= Void
      end

   frozen is_integer: BOOLEAN is
	 -- Does `Current' value fit on an INTEGER ?
      local
	 abstract_integer: ABSTRACT_INTEGER
      do
	 abstract_integer ?= Current
	 if abstract_integer /= Void then
	    Result := abstract_integer.is_integer_value
	 end
       ensure
	 Result implies is_abstract_integer
      end

   frozen is_double: BOOLEAN is
      do
	 if Current #>= Minimum_double then
	    Result := Current #<= Maximum_double
	 end
      end

feature -- Conversions and printing:

   to_integer: INTEGER is
	 -- Conversion of `Current' in an INTEGER.
      require
	 is_integer
      deferred
      end

   to_double: DOUBLE is
	 -- Conversion of `Current' in a DOUBLE.
      require
	 is_double
      deferred
      end

   frozen to_string: STRING is
	 -- Convert the NUMBER into a new allocated STRING.
	 -- Note: see also `append_in' to save memory.
      do
	 Result := once "......... (This is a private buffer) ............"
	 Result.clear
	 append_in(Result)
	 Result := Result.twin
      end

   append_in(buffer: STRING) is
	 -- Append the equivalent of `to_string' at the end of `buffer'. 
	 -- Thus you can save memory because no other STRING is allocated 
	 -- for the job.
      require
	 buffer /= Void
      deferred
      end

   frozen to_decimal(digits: INTEGER; all_digits: BOOLEAN): STRING is
         -- Convert `Current' into its decimal view. A maximum of decimal 
         -- `digits' places will be used for the decimal part. If the 
         -- `all_digits' flag is true insignificant digits will be included 
         -- as well. (See also `decimal_in' to save memory.)
      require
	 non_negative_digits: digits >= 0
      do
	 Result := once "This is a local STRING buffer ...."
	 Result.clear
	 append_decimal_in(Result,digits,all_digits)
	 Result := Result.twin
      ensure
	 not Result.is_empty
      end

   append_decimal_in(buffer: STRING; digits: INTEGER; all_digits: BOOLEAN) is
	 -- Append the equivalent of `to_decimal' at the end of `buffer'. Thus 
	 -- you can save memory because no other STRING is allocated.
      require
	 non_negative_digits: digits >= 0
      deferred
      end

   frozen digit: CHARACTER is
	 -- Gives the corresponding CHARACTER for range 0..9.
      require
	 to_integer.in_range(0,9)
      do
	 Result := to_integer.digit
      ensure
	 (once "0123456789").has(Result)
	 Current @= Result.value
      end

feature -- To mimic NUMERIC:

   divisible(other: NUMBER): BOOLEAN is
	 -- Is `other' a valid divisor for `Current' ?
      require
	 other /= Void
      do
	 Result := not other.is_zero
      end

   one: NUMBER is
	 -- The neutral element of multiplication.
      once
	 !SMALL_INTEGER!Result.make(1)
      ensure
	 neutral_element: -- Result is the neutral element of
	 -- multiplication.
      end

   zero: NUMBER is
	 -- The neutral element of addition.
      once
	 !SMALL_INTEGER!Result.make(0)
      ensure
	 neutral_element: -- Result is the neutral element of
	 -- addition.
      end

   frozen sign: INTEGER is
      do
	 if is_positive then
	    Result := 1
	 elseif is_negative then
	    Result := - 1
	 end
      end

   sqrt: DOUBLE is
	 -- Compute the square routine.
      require
	 is_double
      do
	 Result := to_double.sqrt
      end

   frozen log: DOUBLE is
      require
	 is_double
      do
	 Result := to_double.log
      end

   abs: NUMBER is
      do
	 if is_negative then
	    Result := -Current
	 else
	    Result := Current
	 end
      end

feature -- To mix NUMBER and INTEGER:

   infix "@+" (other: INTEGER): NUMBER is
	 -- Sum of `Current' and `other'.
      deferred
      ensure
	 Result /= Void
      end

   infix "@-" (other: INTEGER): NUMBER is
	 -- Difference of `Current' and `other'.
      do
	 if other = Minimum_integer then
	    Result := (Current @+ 1)
	    Result := Result @+ Maximum_integer
	 else
	    Result := Current @+ (- other)
	 end
      ensure
	 Result /= Void
      end

   infix "@*" (other: INTEGER): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   infix "@/" (other: INTEGER): NUMBER is
	 -- Quotient of `Current' and `other'.
      require
	 other /= 0
      deferred
      ensure
	 Result /= Void
      end

   infix "@//" (other: INTEGER): NUMBER is
	 -- Divide `Current' by `other' (Integer division).
      require
	 is_abstract_integer
	 other /= 0
      deferred
      ensure
	 Result.is_abstract_integer
      end


   infix "@\\" (other: INTEGER): NUMBER is
	 -- Remainder of division of `Current' by `other'.
      require
	 is_abstract_integer
	 other /= 0
      deferred
      ensure
	 Result.is_abstract_integer
      end

   infix "@^" (exp: INTEGER): NUMBER is
      require
	 (exp > 0) or else not is_zero
      local
	 int : INTEGER
	 other : NUMBER
      do
	 int := exp.abs
	 inspect int
	 when 0 then
	    !SMALL_INTEGER!Result.make(1)
	 when 1 then
	    Result := Current
	 else
	    from
	       int := int - 1
	       other := Current
	       Result := Current
	    until
	       int < 2
	    loop
	       if int.odd then
		  Result := Result * other
	       end
	       other := other * other; -- methode sqrt : ^2
	       int := int // 2
	    end
	    Result := Result * other
	 end
	 if (exp < 0) then
	    Result := Result.inverse
	 end
      ensure
	 Result /= Void
	 Result /= Current implies (exp /= 1 or else not is_zero)
      end

   infix "@=" (other: INTEGER): BOOLEAN is
	 -- Is `Current' equal `other' ?
      deferred
      end

   infix "@<" (other: INTEGER): BOOLEAN is
	 -- Is `Current' strictly less than `other'?
      deferred
      ensure
	 Result = not (Current @>= other)
      end

   infix "@<=" (other: INTEGER): BOOLEAN is
	 -- Is `Current' less or equal `other'?
      deferred
      ensure
	 Result = not (Current @> other)
      end

   infix "@>" (other: INTEGER): BOOLEAN is
	 -- Is `Current' strictly greater than `other'?
      deferred
      ensure
	 Result = not (Current @<= other)
      end

   infix "@>=" (other: INTEGER): BOOLEAN is
	 -- Is `Current' greater or equal than `other'?
      deferred
      ensure
	 Result = not (Current @< other)
      end

feature -- To mix NUMBER and DOUBLE:

   infix "#=" (other: DOUBLE): BOOLEAN is
	 -- Is `Current' equal `other'?
      deferred
      end

   infix "#<" (other: DOUBLE): BOOLEAN is
	 -- Is `Current' strictly less than `other'?
      deferred
      ensure
	 Result implies not (Current #>= other)
      end

   infix "#<=" (other: DOUBLE): BOOLEAN is
	 -- Is `Current' less or equal `other'?
      deferred
      ensure
	 Result = not (Current #> other)
      end

   infix "#>" (other: DOUBLE): BOOLEAN is
	 -- Is `Current' strictly greater than `other'?
      deferred
      ensure
	 Result = not (Current #<= other)
      end

   infix "#>=" (other: DOUBLE): BOOLEAN is
	 -- Is `Current' greater or equal than `other'?
      deferred
      ensure
	 Result = not (Current #< other)
      end

feature -- Misc:

   out_in_tagged_out_memory, fill_tagged_out_memory is
      do
	 append_in(tagged_out_memory)
      end

   hash_code: INTEGER is
      do
	 if is_double then
	    Result := to_double.hash_code
	 else
	    not_yet_implemented
	 end
      end

   inverse: NUMBER is
      require
	 divisible(Current)
      deferred
      ensure
	 Result /= Void
      end

   factorial: NUMBER is
      require
	 is_abstract_integer
	 is_positive
      local
	 n: NUMBER
      do
	 if is_zero then
	    !SMALL_INTEGER!Result.make(1)
	 else
	    from
	       Result := Current
	       n := Current @- 1
	    until
	       n @= 0
	    loop
	       Result := n * Result
	       n := n @- 1
	    end
	 end
      ensure
	 Result.is_abstract_integer
	 Result.is_positive
      end

feature {NUMBER} -- Implementation:

   nb_c(int: INTEGER): INTEGER is
      -- Return the number of chiffers in int.
      local
	 i: INTEGER
      do
	 from
	    i := int
	    Result := 1
	 until
	    i < 10
	 loop
	    i := i // 10
	    Result := Result + 1
	 end
      end

   frozen is_small_integer: BOOLEAN is
	 -- Does `Current' value fit on an INTEGER ?
      do
	 if is_abstract_integer then
	    if Current @<= (Base - 1) then
	       Result := Current @>= (- (Base - 1))
	    end
	 end
      ensure
	 Result implies is_abstract_integer
      end

   frozen add_with_small_integer(other: SMALL_INTEGER): NUMBER is
      require
	 other /= Void
      do
	 Result := Current @+ other.to_integer
      ensure
	 Result /= Void
      end

   add_with_large_positive_integer(other: LARGE_POSITIVE_INTEGER): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   add_with_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   add_with_small_fraction(other: INTEGER_FRACTION): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   add_with_large_fraction(other: NUMBER_FRACTION): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   multiply_with_small_integer(other: SMALL_INTEGER): NUMBER is
      require
	 other /= Void
      do
	 Result := Current @* other.to_integer
      ensure
	 Result /= Void
      end

   multiply_with_large_positive_integer(other: LARGE_POSITIVE_INTEGER): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   multiply_with_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   multiply_with_small_fraction(other: INTEGER_FRACTION): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   multiply_with_large_fraction(other: NUMBER_FRACTION): NUMBER is
      require
	 other /= Void
      deferred
      ensure
	 Result /= Void
      end

   greater_with_small_integer(other: SMALL_INTEGER): BOOLEAN is
      require
	 other /= Void
      do
	 Result := Current @> other.to_integer
      end

   greater_with_large_positive_integer(other: LARGE_POSITIVE_INTEGER): BOOLEAN is
      require
	 other /= Void
      deferred
      end

   greater_with_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): BOOLEAN is
      require
	 other /= Void
      deferred
      end

   greater_with_small_fraction(other: INTEGER_FRACTION): BOOLEAN is
      require
	 other /= Void
      deferred
      end

   greater_with_large_fraction(other: NUMBER_FRACTION): BOOLEAN is
      require
	 other /= Void
      deferred
      end

feature {NONE}

   max_double: NUMBER is
      local
	 nt: NUMBER_TOOLS
	 tmp: STRING
      once
	 tmp := "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
	 tmp.clear
	 Maximum_double.append_in_format(tmp,0)
	 Result := nt.from_string(tmp)
      end

   min_double: NUMBER is
      local
	 nt: NUMBER_TOOLS
	 tmp: STRING
      once
	 tmp := "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
	 tmp.clear
	 Minimum_double.append_in_format(tmp,0)
	 Result := nt.from_string(tmp)
      end

   greater_large_negative_integer: LARGE_NEGATIVE_INTEGER is
      once
	 !LARGE_NEGATIVE_INTEGER!Result.make_smaller(0)
      end

   Base: INTEGER is
	 -- The Base is the grater number which is like 10^x and
	 -- which is inferior to the Maximum_integer value.
	 --
	 -- So if Maximum_number is 2147483647 :
	 -- The Base is :           1000000000.
	 -- A number has a value between 0-9 so a number in a
	 -- item of a FIXED_ARRAY[INTEGER] must be a succession
	 -- of 9 so the value of the greater item is 999999999.
         -- And the Base is the greater value of a item + 1.
      once
	 Result := (10 ^ (nb_c(Maximum_integer) - 1)).to_integer
      end; -- Base

   Half_base: INTEGER is
	 -- It's the value of the greater number like 10^x which is
	 -- inferior to Base.sqrt
	 -- For the multiplication, we can make multiplication without
	 -- depassement of capacity.
      once
	    Result := (10^(Base.sqrt.log10.truncated_to_integer)).to_integer
      end

   Half_base_2: INTEGER is
      once
	 Result := (Half_base ^ 2).to_integer
      end

   Rest_base: INTEGER is
      once
	 Result := Base // (Half_base ^ 2).to_integer
      end

   Double_base: DOUBLE is
	 -- It's the double value of the Base.
	 -- The conversion integer to double of the Base value.
	 -- This value is necessary to the division.
      once
	 Result := Base.to_double
      end

   Log_base: DOUBLE is
      once
	 Result := Base.log
      end

   Base_is_impair: BOOLEAN is
      once
	 Result := (Rest_base = 10)
      end

   -- This global variable is necessary to have a good memory gestion

   temp_2_digints: FIXED_ARRAY[INTEGER] is
	 -- Global variable to put an integer in a large_integer
      once
	 !!Result.make(2)
      end

   temp_1_digint: FIXED_ARRAY[INTEGER] is
	 -- Global varaible to put an integer in a small_integer
	 -- for few operations
      once
	 !!Result.make(1)
      end

   temp_from_mult: FIXED_ARRAY[INTEGER] is
	 -- Global variable
      once
	 !!Result.make(0)
      end

   temp_after_mult: FIXED_ARRAY[INTEGER] is
	 -- Global variable
      once
	 !!Result.make(0)
      end

   temp_quotient: FIXED_ARRAY[INTEGER] is
	 -- Global variable
      once
	 !!Result.make(0)
      end

   temp_remainder: FIXED_ARRAY[INTEGER] is
	 -- Global variable
      once
	 !!Result.make(0)
      end

   temp_division: FIXED_ARRAY[INTEGER] is
	 -- Global variable
      once
	 !!Result.make(0)
      end

end -- NUMBER
