-- 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
--
class SMALL_INTEGER
--
-- To implement NUMBER (do not use this class, see NUMBER).
--

-- The Maximum_integer number is 2147483647 so a SMALL INTEGER must
-- be on 9 digits, so the biggest SMALL INTEGER is 999999999 and the
-- smallest SMALL INTEGER is -999999999

inherit ABSTRACT_INTEGER

creation make

feature

   is_zero: BOOLEAN is
      do
	 Result := value = 0
      end

   is_one: BOOLEAN is
      do
	 Result := value = 1
      end

   is_positive: BOOLEAN is
      do
	 Result := value >= 0
      end

   is_negative: BOOLEAN is
      do
	 Result := value < 0
      end

   is_integer_value: BOOLEAN is
      do
	 Result := true
      end

   to_integer: INTEGER is
      do
	 Result := value
      end

   to_double: DOUBLE is
      do
	 Result := value
      end


   append_in(str:STRING) is
      do
	 str.append(value.to_string)
      end

   prefix "-" : NUMBER is
      do
	 !SMALL_INTEGER!Result.make(- value)
      end

   infix "+" (other: NUMBER): NUMBER is
      do
	 Result := other @+ value
      end

   infix "@+" (other: INTEGER): NUMBER is
      local
	 sum : INTEGER
	 an : NUMBER
      do
	 sum := value + other
	 if other >= Base then
	    !LARGE_POSITIVE_INTEGER!an.make_smaller(other)
	    Result := an @+ value
	 elseif other <= -Base then
	    !LARGE_NEGATIVE_INTEGER!an.make_smaller(other)
	    Result := an @+ value
	 else
	    if sum >= Base then
	       !LARGE_POSITIVE_INTEGER!Result.make_smaller(sum)
	    elseif sum <= -Base then
	       !LARGE_NEGATIVE_INTEGER!Result.make_smaller(-sum)
	    else
	       !SMALL_INTEGER!Result.make(sum)
	    end
	 end
      end

--	    if value <= 0 and (other < 0) and (sum > 0) then
--	    !LARGE_NEGATIVE_INTEGER!Result.make_smaller(-sum - Base)
--	 elseif (sum = -Base) then
--	    Result := greater_large_negative_integer
--	 else
--	    !SMALL_INTEGER!Result.make(sum)
--	 end
--      end

   infix "*" (other: NUMBER): NUMBER is
      do
	 if is_zero then
	    Result := zero
	 else
	    Result := other @* value
	 end
      end

   infix "@*" (other : INTEGER): NUMBER is
      local
	 d1, d2: INTEGER
      do
	 if other = 0 or else value = 0 then
	    !SMALL_INTEGER!Result.make(0)
	 elseif other = 1 then
	    !SMALL_INTEGER!Result.make(value)
	 elseif value = 1 then
	    if other >= Base then
	       !LARGE_POSITIVE_INTEGER!Result.make_smaller(other)
	    elseif other <= -Base then
	       !LARGE_NEGATIVE_INTEGER!Result.make_smaller(other)
	    else
	       !SMALL_INTEGER!Result.make(other)
	    end
	 elseif other >= Base then
	    if value > 0 then
	       !LARGE_POSITIVE_INTEGER!Result.make_from_large_product(value, other)
	    else
	       !LARGE_NEGATIVE_INTEGER!Result.make_from_large_product(value, other)
	    end
	 elseif other <= -Base then
	    if value > 0 then
	       !LARGE_NEGATIVE_INTEGER!Result.make_from_large_product(value, other)
	    else
	       !LARGE_POSITIVE_INTEGER!Result.make_from_large_product(value, other)
	    end
	 else
	    d1 := value.abs
	    d2 := other.abs
	    if ((d1.log + d2.log - Log_base) * 10000).truncated_to_integer >= 0  or else d1 * d2 < 0 then
	       if value > 0 then
		  if other > 0 then
		     !LARGE_POSITIVE_INTEGER!Result.make_from_product(d1, d2)
		  else
		     !LARGE_NEGATIVE_INTEGER!Result.make_from_product(d1, d2)
		  end
	       else
		  if other > 0 then
		     !LARGE_NEGATIVE_INTEGER!Result.make_from_product(d1, d2)
		  else
		     !LARGE_POSITIVE_INTEGER!Result.make_from_product(d1, d2)
		  end
	       end
	    else
	       !SMALL_INTEGER!Result.make(value * other)
	    end
	 end
      end

   infix "@/" (other: INTEGER): NUMBER is
      local
	 tmp: INTEGER_FRACTION
	 n, d: ABSTRACT_INTEGER
	 val: INTEGER
      do
	 if (other = 1) then
	    Result := Current
	 else
	    if (value \\ other) = 0  then
	       val := value // other
	       if val >= Base then
		  !LARGE_POSITIVE_INTEGER!Result.make_smaller(val)
	       elseif val <= (-1 * Base) then
		  !LARGE_NEGATIVE_INTEGER!Result.make_smaller(val)
	       else
		  !SMALL_INTEGER!Result.make(value // other)
	       end
	    elseif other <= (-1 * Base) then
	       n ?= abs
	       !LARGE_NEGATIVE_INTEGER!d.make_smaller(other)
	       !NUMBER_FRACTION!Result.make( n, d, is_positive)
	    elseif other >= Base then
	       n ?= abs
	       !LARGE_NEGATIVE_INTEGER!d.make_smaller(other)
	       !NUMBER_FRACTION!Result.make( n, d, is_positive)
	    else
	       !!tmp.make(1,2)
	       Result := tmp.from_two_integer(value,other)
	    end
	 end
      end

   infix "//" (other: NUMBER): NUMBER is
      local
	 oth: ABSTRACT_INTEGER
      do
	 oth ?= other
	 Result := oth.integer_divide_small_integer(Current)
      end

   infix "@//" (other: INTEGER): NUMBER is
      local
	 tmp: INTEGER
      do
	 tmp := value // other
	 if tmp >= Base then
	    !LARGE_POSITIVE_INTEGER!Result.make_smaller(tmp)
	 elseif tmp <= (-1 * Base) then
	    !LARGE_NEGATIVE_INTEGER!Result.make_smaller(tmp)
	 else
	    !SMALL_INTEGER!Result.make(value // other)
	 end
      end

   infix "\\" (other: NUMBER): NUMBER is
      local
	 oth: ABSTRACT_INTEGER
      do
	 oth ?= other
	 Result := oth.remainder_of_divide_small_integer(Current)
      end

   infix "@\\" (other : INTEGER): NUMBER is
      local
	 tmp: INTEGER
      do
	 tmp := value \\ other
	 if tmp >= Base then
	    !LARGE_POSITIVE_INTEGER!Result.make_smaller(tmp)
	 elseif tmp <= (-1 * Base) then
	    !LARGE_NEGATIVE_INTEGER!Result.make_smaller(tmp)
	 else
	    !SMALL_INTEGER!Result.make(tmp)
	 end
      end

   infix "@=" (other: INTEGER): BOOLEAN is
      do
	 Result := value = other
      end

   infix "@<" (other: INTEGER): BOOLEAN is
      do
	 Result := value < other
      end

   infix "@<=" (other: INTEGER): BOOLEAN is
      do
	 Result := value <= other
      end

   infix "@>" (other: INTEGER): BOOLEAN is
      do
	 Result := value > other
      end

   infix "@>=" (other: INTEGER): BOOLEAN is
      do
	 Result := value >= other
      end

   infix "#=" (other: DOUBLE): BOOLEAN is
      do
	 Result := other = value
      end

   infix "#<" (other: DOUBLE): BOOLEAN is
      do
	 Result := value < other
      end

   infix "#<=" (other: DOUBLE): BOOLEAN is
      do
	 Result := value <= other
      end

   infix "#>" (other: DOUBLE): BOOLEAN is
      do
	 Result := value > other
      end

   infix "#>=" (other: DOUBLE): BOOLEAN is
      do
	 Result := value >= other
      end

   infix "<" (other: NUMBER): BOOLEAN is
      do
	 Result := other @> value
      end

   is_equal(other: NUMBER): BOOLEAN is
      local
	 n2: like Current
      do
	 n2 ?= other
	 if n2 /= Void then
	    Result := value = n2.value
	 end
      end

feature {NUMBER}

   value: INTEGER

   add_with_large_positive_integer(other: LARGE_POSITIVE_INTEGER): NUMBER is
      do
	 Result:= other @+ value
      end

   add_with_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): NUMBER is
      do
	 Result:= other @+ value
      end

   add_with_small_fraction (other: INTEGER_FRACTION ): NUMBER is
      do
	 Result:= other @+ value
      end

   add_with_large_fraction (other: NUMBER_FRACTION): NUMBER is
      do
	 Result := other @+ value
      end

   multiply_with_large_positive_integer(other: LARGE_POSITIVE_INTEGER): NUMBER is
      do
	 Result:=other.multiply_with_small_integer(Current)
      end

   multiply_with_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): NUMBER is
      do
	 Result:=other.multiply_with_small_integer(Current)
      end

   multiply_with_small_fraction (other: INTEGER_FRACTION): NUMBER is
      do
	 Result:=other.multiply_with_small_integer(Current)
      end

   multiply_with_large_fraction (other: NUMBER_FRACTION): NUMBER is
      do
	 Result := other.multiply_with_small_integer(Current)
      end

   integer_divide_small_integer(other: SMALL_INTEGER): ABSTRACT_INTEGER is
      do
	 Result ?= other @// value
      end

   integer_divide_large_positive_integer(other: LARGE_POSITIVE_INTEGER): ABSTRACT_INTEGER is
      do
	 Result ?= other @// value
      end

   integer_divide_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): ABSTRACT_INTEGER is
      do
	 Result ?= other @// value
      end

   remainder_of_divide_small_integer(other: SMALL_INTEGER): ABSTRACT_INTEGER is
      do
	 Result ?= other @\\ value
      end

   remainder_of_divide_large_positive_integer(other: LARGE_POSITIVE_INTEGER): ABSTRACT_INTEGER is
      do
	 Result ?= other @\\ value
      end

   remainder_of_divide_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): ABSTRACT_INTEGER is
      do
	 Result ?= other @\\ value
      end

   inverse: NUMBER is
      do
	 if (is_one) or else (Current @= -1) then
	    Result := Current
	 else
	    !INTEGER_FRACTION!Result.make(sign, value.abs)
	 end
      end

   greater_with_large_positive_integer(other: LARGE_POSITIVE_INTEGER): BOOLEAN is
      do
      end

   greater_with_large_negative_integer(other: LARGE_NEGATIVE_INTEGER): BOOLEAN is
      do
	 Result := true
      end

   greater_with_small_fraction(other: INTEGER_FRACTION): BOOLEAN is
      do
	 Result := (Current @* other.denominator) @> other.numerator
      end

   greater_with_large_fraction(other: NUMBER_FRACTION): BOOLEAN is
      do
	 if other.is_negative then
	     Result := (other.denominator * Current) > (- other.numerator)
	 else
	    Result := (other.denominator * Current) > other.numerator
	 end
      end

feature {NONE}

   make(val: INTEGER) is
      do
	 value := val
      ensure
	 Current.to_integer = val
      end

invariant

   value < Base and value > -Base

end