note
	description: "DECIMAL numbers. Following the 'General Decimal Arithmetic Specification'."
	library: "Gobo Eiffel Decimal Arithmetic Library"
	copyright: "Copyright (c) 2004-2018, Paul G. Crismer and others"
	license: "MIT License"
	date: "$Date: 2019-02-07 22:54:15 +0000 (Thu, 07 Feb 2019) $"
	revision: "$Revision: 102807 $"

class 
	MA_DECIMAL

inherit
	KL_NUMERIC
		rename
			plus as binary_plus alias "+",
			minus as binary_minus alias "-"
		redefine
			out,
			is_equal,
			copy
		end

	HASHABLE
		undefine
			out,
			is_equal,
			copy
		end

	KL_PART_COMPARABLE
		undefine
			out,
			is_equal,
			copy
		end

	MA_SHARED_DECIMAL_CONTEXT
		undefine
			out,
			is_equal,
			copy
		end

	MA_SHARED_DECIMAL_CONSTANTS
		undefine
			out,
			is_equal,
			copy
		end

	MA_DECIMAL_CONTEXT_CONSTANTS
		export
			{NONE} all
		undefine
			out,
			is_equal,
			copy
		end

	KL_IMPORTED_INTEGER_ROUTINES
		export
			{NONE} all
		undefine
			out,
			is_equal,
			copy
		end

	KL_IMPORTED_DOUBLE_ROUTINES
		export
			{NONE} all
		undefine
			out,
			is_equal,
			copy
		end

	KL_IMPORTED_STRING_ROUTINES
		export
			{NONE} all
		undefine
			out,
			is_equal,
			copy
		end

	KL_SHARED_PLATFORM
		export
			{NONE} all
		undefine
			out,
			is_equal,
			copy
		end

create 
	make_from_integer,
	make_from_string,
	make_from_string_ctx,
	make_copy,
	make_zero,
	make_one,
	make


create {MA_DECIMAL}
	make_infinity,
	make_nan,
	make_snan,
	make_special


create {MA_DECIMAL_TEXT_PARSER}
	make_from_parser

feature {NONE} -- Initialization

	make (a_precision: INTEGER_32)
			-- Create a new decimal using a_precision digits.
		require
			a_precision_positive: a_precision > 0
		do
			create {MA_DECIMAL_COEFFICIENT_IMP} coefficient.make (a_precision)
			set_positive
			coefficient.put (0, 0)
		ensure
			zero: is_zero
		end

	make_copy (other: like Current)
			-- Make a copy of other.
		require
			other_not_void: other /= Void
		do
			if not other.is_special then
				make (other.count)
				copy (other)
			else
				make_special (other.special)
				is_negative := other.is_negative
			end
		ensure
			special_copy: special = other.special
			coefficient_copy: coefficient.is_equal (other.coefficient)
			sign_copy: sign = other.sign
			exponent_copy: exponent = other.exponent
		end

	make_zero
			-- Make zero.
		do
			coefficient := Special_coefficient
		ensure
			zero: is_zero
		end

	make_one
			-- Make one.
		do
			make (1)
			coefficient.put (1, 0)
		ensure
			is_one: is_one
			positive: not is_negative
		end

	make_from_integer (a_value: INTEGER_32)
			-- Make a new decimal from integer a_value.
		local
			temp_value, v, index, ten_exponent: INTEGER_32
		do
			temp_value := a_value
			if a_value >= 0 then
				set_positive
				temp_value := - temp_value
			else
				set_negative
			end
			v := temp_value
			from
				ten_exponent := 0
			until
				v = 0
			loop
				v := Integer_.div (v, 10)
				if v /= 0 then
					ten_exponent := ten_exponent + 1
				end
			end
			create {MA_DECIMAL_COEFFICIENT_IMP} coefficient.make (ten_exponent + 1)
			if temp_value = 0 then
				coefficient.put (0, 0)
			else
				from
					index := 0
					v := temp_value
				until
					v = 0
				loop
					coefficient.put (- Integer_.mod (v, 10), index)
					v := Integer_.div (v, 10)
					if v /= 0 then
						index := index + 1
					end
				end
			end
		ensure
			equal_to_value: to_integer = a_value
		end

	make_from_string_ctx (value_string: STRING_8; ctx: MA_DECIMAL_CONTEXT)
			-- Make a new decimal from value_string relative to ctx.
		require
			value_string_not_void: value_string /= Void
			context_not_void: ctx /= Void
		local
			l_parser: like Parser
		do
			l_parser := Parser
			l_parser.decimal_parse (value_string)
			make_from_parser (l_parser, ctx)
		end

	make_from_parser (a_decimal_parser: MA_DECIMAL_TEXT_PARSER; a_context: MA_DECIMAL_CONTEXT)
			-- Make from a_decimal_parser, relative to a_context.
		require
			a_decimal_parser_not_void: a_decimal_parser /= Void
			a_context_not_void: a_context /= Void
		do
			if not attached a_decimal_parser.last_parsed as l_last_parsed or else a_decimal_parser.error then
				if a_context.is_extended then
					make_nan
				else
					make_snan
				end
			else
				if a_decimal_parser.is_infinity then
					make_infinity (Parser.sign)
				elseif a_decimal_parser.is_snan then
					make_snan
				elseif a_decimal_parser.is_nan then
					make_nan
				else
					if a_decimal_parser.sign < 0 then
						set_negative
					else
						set_positive
					end
					if a_decimal_parser.has_exponent then
						if a_decimal_parser.exponent_as_double > Platform.Maximum_integer.to_double then
							exponent := a_context.Maximum_exponent + a_context.digits + a_decimal_parser.exponent_count + 2
						else
							exponent := Double_.truncated_to_integer (a_decimal_parser.exponent_as_double)
						end
						if Parser.exponent_sign < 0 then
							exponent := - exponent
						end
					else
						exponent := 0
					end
					if a_decimal_parser.has_point then
						exponent := exponent - a_decimal_parser.fractional_part_count
					end
					create {MA_DECIMAL_COEFFICIENT_IMP} coefficient.make ((a_context.digits + 1).max (a_decimal_parser.coefficient_count))
					coefficient.set_from_substring (l_last_parsed, a_decimal_parser.coefficient_begin, a_decimal_parser.coefficient_end)
					clean_up (a_context)
				end
			end
		end

	make_from_string (value_string: STRING_8)
			-- Make a new decimal from string value_string relative to shared_decimal_context.
		require
			value_string_not_void: value_string /= Void
		do
			make_from_string_ctx (value_string, shared_decimal_context)
		end

	make_special (code_special: INTEGER_32)
			-- Make special from code.
		require
			valid_code_special: code_special = Special_infinity or else code_special = Special_quiet_nan or else code_special = Special_signaling_nan
		do
			coefficient := Special_coefficient
			special := code_special
			exponent := 0
		ensure
			is_special: is_special
			exponent_zero: exponent = 0
		end

	make_nan
			-- Make quiet 'Not a Number'.
		do
			make_special (Special_quiet_nan)
		ensure
			is_nan: is_quiet_nan
		end

	make_snan
			-- Make Signaling 'Not a Number'.
		do
			make_special (Special_signaling_nan)
		ensure
			is_snan: is_signaling_nan
		end

	make_infinity (a_sign: INTEGER_32)
			-- Make Infinity.
		require
			a_sign_valid: a_sign = -1 or else a_sign = 1
		do
			make_special (Special_infinity)
			is_negative := a_sign < 0
		ensure
			is_infinity: is_infinity
			sign_set: sign = a_sign
		end
	
feature -- Access

	sign: INTEGER_32
			-- Sign: positive = 1; negative = -1
		do
			if is_negative then
				Result := -1
			else
				Result := 1
			end
		ensure
			definition1: Result = -1 implies is_negative
			definition2: Result = 1 implies is_positive
		end

	exponent: INTEGER_32
			-- Current exponent

	hash_code: INTEGER_32
			-- Hash code value
		local
			i: INTEGER_32
			l_exponent, l_coefficient_lsd, l_coefficient_msd: INTEGER_32
			l_is_zero: BOOLEAN
		do
			l_is_zero := is_zero
			Result := special
			Result := Result + Integer_.bit_shift_left (Result, 10)
			Result := Integer_.bit_xor (Result, Integer_.bit_shift_right (Result, 6))
			if not l_is_zero then
				Result := Result + sign
			end
			Result := Result + Integer_.bit_shift_left (Result, 10)
			Result := Integer_.bit_xor (Result, Integer_.bit_shift_right (Result, 6))
			if not is_special then
				from
					l_coefficient_msd := coefficient.msd_index
					l_coefficient_lsd := coefficient.lower
					l_exponent := exponent
				until
					l_coefficient_lsd > l_coefficient_msd or else coefficient.item (l_coefficient_lsd) /= 0
				loop
					l_coefficient_lsd := l_coefficient_lsd + 1
					l_exponent := l_exponent + 1
				end
				if not l_is_zero then
					Result := Result + l_exponent
				end
				Result := Result + Integer_.bit_shift_left (Result, 10)
				Result := Integer_.bit_xor (Result, Integer_.bit_shift_right (Result, 6))
				from
					i := l_coefficient_lsd
				until
					i > l_coefficient_msd
				loop
					Result := Result + coefficient.item (i)
					Result := Result + Integer_.bit_shift_left (Result, 10)
					Result := Integer_.bit_xor (Result, Integer_.bit_shift_right (Result, 6))
					i := i + 1
				end
			end
			Result := Result + Integer_.bit_shift_left (Result, 3)
			Result := Integer_.bit_xor (Result, Integer_.bit_shift_right (Result, 11))
			Result := Result + Integer_.bit_shift_left (Result, 15)
			Result := Integer_.bit_and (Result, Platform.Maximum_integer)
		end
	
feature -- Constants

	one: like Current
			-- Neutral element for "*" and "/"
		do
			Result := Once_one
		ensure then
			instance_free: class
			one_is_one: Result.is_one
			one_is_positive: not Result.is_negative
		end

	Minus_one: MA_DECIMAL
			-- Minus one
		once
			create Result.make_copy (one)
			Result.set_negative
		ensure
			instance_free: class
			minus_one_not_void: Result /= Void
			is_minus_one: Result.is_one and then Result.is_negative
		end

	zero: like Current
			-- Neutral element for "+" and "-"
		do
			Result := Once_zero
		ensure then
			instance_free: class
			is_zero: Result.is_zero
		end

	Negative_zero: MA_DECIMAL
			-- Negative zero
		once
			create Result.make_zero
			Result.set_negative
		ensure
			instance_free: class
			negative_zero_not_void: Result /= Void
			is_zero: Result.is_zero
			is_negative: Result.is_negative
		end

	Nan: MA_DECIMAL
			-- Not a Number
		once
			create Result.make_nan
		ensure
			instance_free: class
			nan_not_void: Result /= Void
			is_nan: Result.is_nan
		end

	Snan: MA_DECIMAL
			-- Signaling Not a Number
		once
			create Result.make_snan
		ensure
			instance_free: class
			snan_not_void: Result /= Void
			is_snan: Result.is_signaling_nan
		end

	Infinity: MA_DECIMAL
			-- Infinity
		once
			create Result.make_infinity (1)
		ensure
			instance_free: class
			infinity_not_void: Result /= Void
			is_infinity: Result.is_infinity
			is_positive: Result.is_positive
		end

	Negative_infinity: MA_DECIMAL
			-- Negative infinity
		once
			create Result.make_infinity (-1)
		ensure
			instance_free: class
			negative_infinity_not_void: Result /= Void
			is_infinity: Result.is_infinity
			is_negative: Result.is_negative
		end
	
feature {MA_DECIMAL} -- Access

	adjusted_exponent: INTEGER_32
			-- Exponent of the most significant digit; see SDAS page 5
		do
			Result := exponent + count - 1
		ensure
			definition: Result = (exponent + count - 1)
		end
	
feature {MA_DECIMAL, MA_DECIMAL_PARSER, MA_DECIMAL_HANDLER} -- Access

	coefficient: MA_DECIMAL_COEFFICIENT
			-- Storage for digits
	
feature -- Status report

	is_integer: BOOLEAN
			-- Is this an integer?
			-- (i.e no fractional part other than all zeroes)
		local
			fractional_count, index: INTEGER_32
		do
			if is_zero then
				Result := True
			elseif exponent < 0 then
				if adjusted_exponent >= 0 then
					fractional_count := - exponent
					from
						index := fractional_count.min (count)
					variant
							index
					until
						index <= 0 or else coefficient.item (index - 1) /= 0
					loop
						index := index - 1
					end
					Result := index = 0
				else
					Result := False
				end
			else
				Result := True
			end
		end

	is_double: BOOLEAN
			-- Is this a double?
		local
			str: STRING_8
		do
			str := to_scientific_string
			Result := str.is_double
		end

	divisible (other: like Current): BOOLEAN
			-- May current object be divided by other?
		do
			Result := not other.is_zero
		ensure then
			definition: Result = not other.is_zero
		end

	exponentiable (other: NUMERIC): BOOLEAN
			-- May current object be elevated to the power other?
		do
		end

	is_negative: BOOLEAN
			-- Is the number negative?

	is_positive: BOOLEAN
			-- Is the number positive?
		do
			Result := not is_negative
		ensure
			definition: Result = not is_negative
		end

	is_nan: BOOLEAN
			-- Is this "Not a Number" (NaN)?
		do
			Result := is_signaling_nan or is_quiet_nan
		ensure
			definition: Result = (is_signaling_nan or is_quiet_nan)
		end

	is_special: BOOLEAN
			-- Is this a special value?
		do
			Result := (special /= Special_none)
		ensure
			definition: Result = (is_nan or else is_infinity)
		end

	is_signaling_nan: BOOLEAN
			-- Is this a "Signaling NaN"?
		do
			Result := (special = Special_signaling_nan)
		end

	is_quiet_nan: BOOLEAN
			-- Is this a "Quiet NaN"?
		do
			Result := (special = Special_quiet_nan)
		end

	is_infinity: BOOLEAN
			-- Is this an Infinity?
		do
			Result := (special = Special_infinity)
		end

	is_zero: BOOLEAN
			-- Is this a Zero value?
		do
			if not is_special and then coefficient.is_zero then
				Result := True
			end
		ensure
			definition: Result = (not is_special and then coefficient.is_zero)
		end

	is_one: BOOLEAN
			-- Is this a One ?
		do
			if not is_special and then exponent = 0 and then coefficient.is_one then
				Result := True
			end
		ensure
			definition: Result = (not is_special and then exponent = 0 and then coefficient.is_one)
		end
	
feature -- Basic operations

	product alias "*" (other: like Current): like Current
			-- Product by other
		do
			Result := multiply (other, shared_decimal_context)
		ensure then
			product_not_void: Result /= Void
		end

	identity alias "+": like Current
			-- Unary plus
		do
			Result := plus (shared_decimal_context)
		ensure then
			unary_plus_not_void: Result /= Void
		end

	binary_plus alias "+" (other: like Current): like Current
			-- Sum with other (commutative)
		do
			Result := add (other, shared_decimal_context)
		ensure then
			sum_not_void: Result /= Void
		end

	opposite alias "-": like Current
			-- Unary minus
		do
			Result := minus (shared_decimal_context)
		ensure then
			unary_minus_not_void: Result /= Void
		end

	binary_minus alias "-" (other: like Current): like Current
			-- Result of subtracting other
		do
			Result := subtract (other, shared_decimal_context)
		ensure then
			subtract_not_void: Result /= Void
		end

	quotient alias "/" (other: like Current): like Current
			-- Division by other
		do
			Result := divide (other, shared_decimal_context)
		ensure then
			division_not_void: Result /= Void
		end

	integer_remainder alias "\\" (other: like Current): like Current
			-- Remainder of integer division
		do
			Result := remainder (other, shared_decimal_context)
		ensure
			remainder_not_void: Result /= Void
		end

	integer_quotient alias "//" (other: like Current): like Current
			-- Integer division
		do
			Result := divide_integer (other, shared_decimal_context)
		ensure
			integer_division_not_void: Result /= Void
		end

	power alias "^" (other: NUMERIC): MA_DECIMAL
			-- Current decimal to the power other
		do
			Result := Nan
		end

	is_less alias "<" (other: like Current): BOOLEAN
			-- Is current decimal less than other?
		local
			res: MA_DECIMAL
		do
			res := compare (other, shared_decimal_context)
			if res.is_negative then
				Result := True
			end
		end
	
feature -- Measurement

	count: INTEGER_32
			-- Count of significant digits
		do
			if is_special then
				Result := 0
			else
				Result := coefficient.count
			end
		ensure
			zero_when_special: is_special implies Result = 0
		end
	
feature -- Comparison

	is_equal (other: like Current): BOOLEAN
			-- Are Current and other considered equal?
		local
			comparison_result: like Current
		do
			if is_nan and then other.is_nan then
				if is_quiet_nan then
					Result := other.is_quiet_nan
				elseif is_signaling_nan then
					Result := other.is_signaling_nan
				end
			elseif is_infinity and then other.is_infinity then
				Result := (sign = other.sign)
			else
				comparison_result := compare (other, shared_decimal_context)
				if comparison_result.is_zero then
					Result := True
				end
			end
		end
	
feature -- Conversion

	out: STRING_8
			-- Printable representation
		do
			create Result.make (0)
			Result.append_string ("[")
			if is_negative then
				Result.append_string ("1")
			else
				Result.append_string ("0")
			end
			Result.append_string (",")
			if is_infinity then
				Result.append_string ("inf")
			elseif is_signaling_nan then
				Result.append_string ("sNaN")
			elseif is_quiet_nan then
				Result.append_string ("qNaN")
			else
				Result.append_string (coefficient.out)
				Result.append_string (",")
				Result.append_string (exponent.out)
			end
			Result.append_string ("]")
		end

	to_double: REAL_64
			-- Current as a DOUBLE
		require
			is_double: is_double
		local
			str: STRING_8
		do
			str := to_scientific_string
			Result := str.to_double
		end

	to_integer: INTEGER_32
			-- Current as an INTEGER
		require
			is_integer: is_integer
			large_enough: Current >= Decimal.Minimum_integer
			small_enough: Current <= Decimal.Maximum_integer
		local
			ctx: MA_DECIMAL_CONTEXT
		do
			create ctx.make_double
			Result := to_integer_ctx (ctx)
		end

	to_integer_ctx (ctx: MA_DECIMAL_CONTEXT): INTEGER_32
			-- Current as an INTEGER wrt ctx
		require
			is_integer: is_integer
			large_enough: Current >= Decimal.Minimum_integer
			small_enough: Current <= Decimal.Maximum_integer
		local
			temp: like Current
			index: INTEGER_32
		do
			temp := round_to_integer (ctx)
			from
				index := temp.count - 1
				Result := 0
			variant
					index + 1
			until
				index < 0
			loop
				Result := Result * 10 + temp.coefficient.item (index)
				index := index - 1
			end
			if is_negative then
				Result := - Result
			end
		end

	to_engineering_string: STRING_8
			-- Current as a string in engineering notation format
		do
			Result := to_string_general (True)
		ensure
			to_string_not_void: Result /= Void
		end

	to_scientific_string: STRING_8
			-- Current as a string in scientific notation format
		do
			Result := to_string_general (False)
		ensure
			to_string_not_void: Result /= Void
		end
	
feature -- Duplication

	copy (other: like Current)
			-- Copy other to current decimal.
		do
			if other /= Current then
				if coefficient = Void or else coefficient = Special_coefficient then
					coefficient := other.coefficient.to_twin
				else
					coefficient.copy (other.coefficient)
				end
				exponent := other.exponent
				is_negative := other.is_negative
				special := other.special
			end
		end
	
feature -- Basic operations

	add (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Add other with respect to the ctx context
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			operand_a, operand_b: like Current
		do
			if is_special or else other.is_special then
				Result := add_special (other, ctx)
			else
				create operand_a.make (ctx.digits + 1)
				operand_a.copy (Current)
				create operand_b.make (ctx.digits + 1)
				operand_b.copy (other)
				if is_negative and then other.is_positive then
					operand_b.unsigned_subtract (operand_a, ctx)
					Result := operand_b
				elseif is_negative and then other.is_negative then
					operand_a.unsigned_add (operand_b, ctx)
					Result := operand_a
					Result.set_negative
				elseif is_positive and then other.is_negative then
					operand_a.unsigned_subtract (operand_b, ctx)
					Result := operand_a
				else
					operand_a.unsigned_add (operand_b, ctx)
					Result := operand_a
				end
				if Result.is_zero then
					if (is_negative and then other.is_negative) or else (ctx.rounding_mode = Round_floor and then sign /= other.sign) then
						Result.set_negative
					else
						Result.set_positive
					end
				end
				Result.clean_up (ctx)
			end
		ensure
			add_not_void: Result /= Void
		end

	subtract (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Subtract other with respect to the ctx context
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			operand_b: like Current
		do
			if is_special or else other.is_special then
				Result := subtract_special (other, ctx)
			else
				create operand_b.make_copy (other)
				if operand_b.is_positive then
					operand_b.set_negative
				else
					operand_b.set_positive
				end
				Result := add (operand_b, ctx)
			end
		ensure
			subtract_not_void: Result /= Void
		end

	multiply (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Multiply other with respect to ctx
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			operand_a, operand_b: like Current
		do
			if is_special or else other.is_special then
				if is_nan or else other.is_nan then
					if is_signaling_nan or else other.is_signaling_nan then
						ctx.signal (Signal_invalid_operation, "sNan in multiply")
					end
					Result := Nan
				elseif is_infinity or else other.is_infinity then
					if is_zero or else other.is_zero then
						ctx.signal (Signal_invalid_operation, "0 * Inf")
						Result := Nan
					else
						if sign = other.sign then
							Result := Infinity
						else
							Result := Negative_infinity
						end
					end
				else
					Result := Nan
				end
			else
				if is_zero or else other.is_zero then
					create Result.make (ctx.digits)
					Result.set_exponent (exponent + other.exponent)
					if sign = other.sign then
						Result.set_positive
					else
						Result.set_negative
					end
				else
					operand_a := Current
					operand_b := other
					create Result.make (operand_a.count + operand_b.count + 2)
					Result.coefficient.integer_multiply (operand_a.coefficient, operand_b.coefficient)
					Result.set_exponent (operand_a.exponent + operand_b.exponent)
					if sign = other.sign then
						Result.set_positive
					else
						Result.set_negative
					end
					Result.clean_up (ctx)
				end
			end
		ensure
			multiply_not_void: Result /= Void
		end

	divide (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Divide Current by other whith respect to ctx
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		do
			Result := do_divide (other, ctx, Division_standard)
		ensure
			divide_not_void: Result /= Void
		end

	divide_integer (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Integer division of Current by other whith respect to ctx
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		do
			Result := do_divide (other, ctx, Division_integer)
		ensure
			divide_integer_not_void: Result /= Void
		end

	remainder (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Remainder of integer division of Current by other whith respect to ctx
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		do
			if is_special or else other.is_special then
				if is_nan or else other.is_nan then
					if is_signaling_nan or else other.is_signaling_nan then
						ctx.signal (Signal_invalid_operation, "sNan in remainder")
					end
					Result := Nan
				elseif is_infinity then
					ctx.signal (Signal_invalid_operation, "[+-] Inf dividend in remainder")
					Result := Nan
				elseif other.is_infinity then
					create Result.make_copy (Current)
					if is_negative then
						Result.set_negative
					end
				else
					Result := Nan
				end
			else
				if other.is_zero then
					ctx.signal (Signal_invalid_operation, "Zero divisor in remainder")
					Result := Nan
				elseif is_zero then
					create Result.make_zero
					if exponent < 0 then
						Result.set_exponent (exponent)
					else
						Result.set_exponent (0)
					end
					if is_negative then
						Result.set_negative
					end
				else
					Result := internal_divide (other, ctx, Division_remainder)
					if is_negative then
						Result.set_negative
					end
					Result.clean_up (ctx)
				end
			end
		ensure
			remainder_not_void: Result /= Void
		end

	rescale (new_exponent: INTEGER_32; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Decimal from Current rescaled to new_exponent
		require
			ctx_not_void: ctx /= Void
		local
			shared_digits, digits_upto_new_exponent, exponent_delta: INTEGER_32
			saved_exponent_limit, result_count: INTEGER_32
		do
			if not (new_exponent <= ctx.exponent_limit and then new_exponent >= ctx.e_tiny) then
				ctx.signal (Signal_invalid_operation, "new exponent is not within limits [Etiny..Emax]")
				Result := Nan
			else
				if is_special then
					create Result.make_copy (Current)
					Result.do_rescale_special (ctx)
				elseif exponent < new_exponent then
					if not is_zero then
						shared_digits := adjusted_exponent - new_exponent + 1
						if shared_digits < 0 then
							result_count := ctx.digits + count + 1
						elseif shared_digits = 0 then
							result_count := ctx.digits + count
						else
							result_count := ctx.digits + (count - shared_digits)
						end
						create Result.make (result_count)
						Result.copy (Current)
						Result.coefficient.set_count (result_count)
						Result.round (ctx)
						Result.strip_leading_zeroes
						if Result.is_underflow (ctx) then
							Result.do_underflow (ctx)
						end
						if ctx.is_flagged (Signal_subnormal) and then ctx.is_flagged (Signal_inexact) then
							ctx.signal (Signal_underflow, "Underflow when rescaling")
						end
						if Result.is_overflow (ctx) then
							Result.do_overflow (ctx)
						end
						if exponent > new_exponent then
							Result.shift_left (exponent - new_exponent)
						end
					else
						create Result.make_copy (Current)
					end
					Result.set_exponent (new_exponent)
				elseif exponent > new_exponent then
					if not is_zero then
						digits_upto_new_exponent := adjusted_exponent - new_exponent + 1
						if digits_upto_new_exponent > ctx.digits then
							create Result.make (count + 1)
							Result.copy (Current)
							Result.shift_left (1)
							saved_exponent_limit := ctx.exponent_limit
							ctx.set_exponent_limit (count - 1)
							Result.set_exponent (1)
							Result.do_overflow (ctx)
							if not Result.is_special then
								Result.set_exponent (new_exponent)
							end
							ctx.set_exponent_limit (saved_exponent_limit)
						else
							exponent_delta := exponent - new_exponent
							create Result.make (count + exponent_delta)
							Result.copy (Current)
							Result.shift_left (exponent_delta)
						end
					else
						if new_exponent < 0 and then exponent < 0 and then count > 1 then
							digits_upto_new_exponent := - new_exponent + 1
						else
							digits_upto_new_exponent := 1
						end
						if digits_upto_new_exponent > ctx.digits then
							create Result.make (count + 1)
							Result.copy (Current)
							Result.shift_left (1)
							saved_exponent_limit := ctx.exponent_limit
							ctx.set_exponent_limit (Result.adjusted_exponent - 1)
							Result.do_overflow (ctx)
							ctx.set_exponent_limit (saved_exponent_limit)
						else
							if digits_upto_new_exponent > 1 then
								exponent_delta := exponent - new_exponent
								create Result.make (count + exponent_delta)
								Result.copy (Current)
								Result.shift_left (exponent_delta)
							else
								create Result.make_copy (Current)
							end
							Result.set_exponent (new_exponent)
						end
					end
					Result.clean_up (ctx)
				else
					create Result.make_copy (Current)
					Result.clean_up (ctx)
				end
			end
		ensure
			rescale_not_void: Result /= Void
		end

	rescale_decimal (new_exponent: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Rescale using decimal new_exponent
		require
			new_exponent_not_void: new_exponent /= Void
			ctx_not_void: ctx /= Void
		local
			e_max, e_min: MA_DECIMAL
			new_integer_exponent: INTEGER_32
			temp_ctx: MA_DECIMAL_CONTEXT
		do
			if new_exponent.is_special or else is_special then
				if new_exponent.is_signaling_nan or else is_signaling_nan then
					ctx.signal (Signal_invalid_operation, "sNaN as new exponent in 'rescale_decimal'")
					Result := Nan
				elseif new_exponent.is_quiet_nan then
					Result := Nan
				elseif new_exponent.is_infinity then
					ctx.signal (Signal_invalid_operation, "Inf as new exponent in 'rescale_decimal'")
					Result := Nan
				else
					create Result.make_copy (Current)
					Result.do_rescale_special (ctx)
				end
			else
				create e_max.make_from_integer (ctx.exponent_limit)
				create e_min.make_from_integer (ctx.e_tiny)
				if new_exponent <= e_max and then new_exponent >= e_min then
					create temp_ctx.make_double
					if new_exponent.is_integer then
						new_integer_exponent := new_exponent.to_integer_ctx (temp_ctx)
						if new_integer_exponent <= ctx.exponent_limit and then new_integer_exponent >= ctx.e_tiny then
							Result := rescale (new_integer_exponent, ctx)
						else
							ctx.signal (Signal_invalid_operation, "new exponent is not within limits [Etiny..Emax]")
							Result := Nan
						end
					else
						ctx.signal (Signal_invalid_operation, "new exponent has fractional part in 'rescale_decimal'")
						Result := Nan
					end
				else
					ctx.signal (Signal_invalid_operation, "new exponent if not within limits [Etiny..Emax]")
					Result := Nan
				end
			end
		ensure
			rescale_decimal_not_void: Result /= Void
		end

	round_to_integer (ctx: MA_DECIMAL_CONTEXT): like Current
			-- Round to an integer with exponent 0
		require
			ctx_not_void: ctx /= Void
		do
			Result := rescale (0, ctx)
		ensure
			round_to_integer_not_void: Result /= Void
			definition: not Result.is_special implies Result.exponent = 0
		end

	plus (ctx: MA_DECIMAL_CONTEXT): like Current
			-- Prefix "+" with respect to the ctx context
		require
			ctx_not_void: ctx /= Void
		local
			l_zero: like Current
		do
			create l_zero.make (ctx.digits + 1)
			l_zero.set_exponent (exponent)
			Result := l_zero.add (Current, ctx)
		ensure
			plus_not_void: Result /= Void
		end

	normalize: like Current
			-- Normalized version of current decimal
		local
			l_count, trailing_zeroes: INTEGER_32
		do
			Result := plus (shared_decimal_context)
			if Result.is_zero then
				Result.coefficient.keep_head (1)
				Result.set_exponent (0)
			elseif not Result.is_special then
				from
					trailing_zeroes := 0
					l_count := Result.count
				until
					trailing_zeroes >= count or else Result.coefficient.item (trailing_zeroes) /= 0
				loop
					trailing_zeroes := trailing_zeroes + 1
				end
				if trailing_zeroes > 0 then
					Result.shift_right (trailing_zeroes)
					Result.coefficient.keep_head (Result.coefficient.msd_index + 1)
				end
			end
			if is_negative then
				Result.set_negative
			else
				Result.set_positive
			end
		ensure
			normalize_not_void: Result /= Void
		end

	minus (ctx: MA_DECIMAL_CONTEXT): like Current
			-- Prefix "-" with respect to the ctx context
		require
			ctx_not_void: ctx /= Void
		local
			l_zero: like Current
		do
			create l_zero.make (ctx.digits + 1)
			l_zero.set_exponent (exponent)
			Result := l_zero.subtract (Current, ctx)
		ensure
			minus_not_void: Result /= Void
		end

	abs: like Current
			-- Absolute value of Current
		do
			Result := abs_ctx (shared_decimal_context)
		ensure
			abs_not_void: Result /= Void
		end

	abs_ctx (ctx: MA_DECIMAL_CONTEXT): like Current
			-- Absolute value of Current relative to ctx
		require
			ctx_not_void: ctx /= Void
		do
			if is_negative then
				Result := minus (ctx)
			else
				Result := plus (ctx)
			end
		ensure
			abs_ctx_not_void: Result /= Void
			definition: Result.sign >= 0
		end

	max_ctx (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Max between Current and other relative to ctx
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			comparison_result: MA_DECIMAL
		do
			if is_nan or else other.is_nan then
				if is_signaling_nan or else other.is_signaling_nan then
					ctx.signal (Signal_invalid_operation, "sNan in max")
				end
				Result := Nan
			else
				comparison_result := compare (other, ctx)
				if comparison_result.is_negative then
					Result := other
				else
					Result := Current
				end
				Result.clean_up (ctx)
			end
		ensure
			max_ctx_not_void: Result /= Void
		end

	min_ctx (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Min between Current and other relative to ctx
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			comparison_result: MA_DECIMAL
		do
			if is_nan or else other.is_nan then
				if is_signaling_nan or else other.is_signaling_nan then
					ctx.signal (Signal_invalid_operation, "sNan in max")
				end
				Result := Nan
			else
				comparison_result := compare (other, ctx)
				if comparison_result.is_negative or comparison_result.is_zero then
					Result := Current
				else
					Result := other
				end
				Result.clean_up (ctx)
			end
		ensure
			min_ctx_not_void: Result /= Void
		end

	compare (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Compare value of Current and other;
			-- Result = 0 if Current = other,
			-- Result = -1 if Current < other,
			-- Result = +1 if Current > other.
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			operand_a, operand_b: like Current
			temp_ctx: MA_DECIMAL_CONTEXT
		do
			if is_special or else other.is_special then
				if is_nan or else other.is_nan then
					Result := Nan
					if is_signaling_nan or else other.is_signaling_nan then
						ctx.signal (Signal_invalid_operation, "sNaN in 'compare'")
					end
				elseif is_infinity then
					if other.is_infinity and then is_negative = other.is_negative then
						Result := zero
					elseif is_negative then
						Result := Minus_one
					else
						Result := one
					end
				elseif other.is_infinity then
					if is_infinity and then is_negative = other.is_negative then
						Result := zero
					elseif other.is_negative then
						Result := one
					else
						Result := Minus_one
					end
				else
					Result := Nan
				end
			else
				create operand_a.make_copy (Current)
				create operand_b.make_copy (other)
				if is_negative /= other.is_negative then
					if is_zero then
						create operand_a.make_zero
					else
						create operand_a.make_one
						if is_negative then
							operand_a.set_negative
						end
					end
					if other.is_zero then
						create operand_b.make_zero
					else
						create operand_b.make_one
						if other.is_negative then
							operand_b.set_negative
						end
					end
				end
				temp_ctx := ctx.cloned_object
				temp_ctx.reset_flags
				Result := operand_a.subtract (operand_b, temp_ctx)
				if Result.is_zero and then not temp_ctx.is_flagged (Signal_subnormal) then
					Result := zero
				else
					if Result.is_negative then
						Result := Minus_one
					else
						Result := one
					end
				end
			end
		ensure
			compare_not_void: Result /= Void
		end
	
feature {MA_DECIMAL, MA_DECIMAL_PARSER} -- Element change

	set_exponent (e: like exponent)
			-- Set exponent to e.
		do
			exponent := e
		ensure
			exponent_set: exponent = e
		end

	set_negative
			-- Set negative.
		do
			is_negative := True
		ensure
			negative: is_negative
		end

	set_positive
			-- Set positive.
		do
			is_negative := False
		ensure
			positive: is_positive
		end
	
feature {NONE} -- Constants

	Align_hint_current: INTEGER_32 = 1

	Align_hint_other: INTEGER_32 = 2

	Align_hint_both: INTEGER_32 = 3

	Align_hint_current_zero: INTEGER_32 = 4

	Align_hint_other_zero: INTEGER_32 = 5
	
feature {MA_DECIMAL} -- Status setting

	special: INTEGER_32
			-- Special status

	set_quiet_nan
			-- Set to qNaN.
		do
			make_nan
		ensure
			qnan: is_quiet_nan
		end
	
feature {MA_DECIMAL} -- Basic operations

	add_special (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Add special numbers.
		require
			other_not_void: other /= Void
			special: is_special or else other.is_special
			ctx_not_void: ctx /= Void
		do
			if is_nan or else other.is_nan then
				if is_signaling_nan or else other.is_signaling_nan then
					ctx.signal (Signal_invalid_operation, "sNaN operand in add")
				end
				Result := Nan
			elseif is_infinity and then other.is_infinity then
				if sign /= other.sign then
					ctx.signal (Signal_invalid_operation, "+Inf and -Inf operands in add")
					Result := Nan
				else
					if is_negative then
						Result := Negative_infinity
					else
						Result := Infinity
					end
				end
			elseif is_infinity or else other.is_infinity then
				if is_infinity then
					if is_negative then
						Result := Negative_infinity
					else
						Result := Infinity
					end
				else
					if other.is_negative then
						Result := Negative_infinity
					else
						Result := Infinity
					end
				end
			else
				Result := Nan
			end
		ensure
			add_special_not_void: Result /= Void
		end

	subtract_special (other: like Current; ctx: MA_DECIMAL_CONTEXT): like Current
			-- Subtract special numbers.
		require
			other_not_void: other /= Void
			special: is_special or else other.is_special
			ctx_not_void: ctx /= Void
		do
			if is_nan or else other.is_nan then
				if is_signaling_nan or else other.is_signaling_nan then
					ctx.signal (Signal_invalid_operation, "sNaN operand in subtract")
				end
				Result := Nan
			elseif is_infinity and then other.is_infinity then
				if sign = other.sign then
					ctx.signal (Signal_invalid_operation, "Inf and Inf operands in subtract")
					Result := Nan
				else
					if is_negative then
						Result := Negative_infinity
					else
						Result := Infinity
					end
				end
			elseif is_infinity or else other.is_infinity then
				Result := Infinity
				if is_infinity then
					if is_negative then
						Result := Negative_infinity
					end
				else
					if other.is_positive then
						Result := Negative_infinity
					else
						Result := Infinity
					end
				end
			else
				Result := Nan
			end
		ensure
			subtract_special_not_void: Result /= Void
		end

	unsigned_add (other: like Current; ctx: MA_DECIMAL_CONTEXT)
			-- Add other to Current.
			-- Note: this will alter other and Current.
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			align_hint: INTEGER_32
			msd: INTEGER_32
		do
			align_hint := align_and_hint (other, ctx.digits)
			if align_hint = Align_hint_current then
				shift_left (ctx.digits + 2 - count)
				coefficient.put (other.coefficient.item (other.coefficient.msd_index), 0)
				ctx.signal (Signal_inexact, "")
			elseif align_hint = Align_hint_other then
				msd := coefficient.item (coefficient.msd_index)
				copy (other)
				shift_left (ctx.digits + 2 - count)
				coefficient.put (msd, 0)
				ctx.signal (Signal_inexact, "")
			elseif align_hint = Align_hint_other_zero then
				copy (other)
			elseif align_hint = Align_hint_current_zero then
			else
				coefficient.integer_add (other.coefficient)
				exponent := exponent.min (other.exponent)
			end
		end

	unsigned_subtract (other: like Current; ctx: MA_DECIMAL_CONTEXT)
			-- Subtract other without taking the sign into account.
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			test, align_hint: INTEGER_32
		do
			align_hint := align_and_hint (other, ctx.digits)
			if align_hint = Align_hint_current then
				shift_left (ctx.digits + 2 - count)
				coefficient.integer_quick_subtract_msd (1, coefficient.count)
			elseif align_hint = Align_hint_other then
				copy (other)
				shift_left (ctx.digits + 2 - count)
				coefficient.integer_quick_subtract_msd (1, coefficient.count)
				set_negative
			elseif align_hint = Align_hint_current_zero then
			elseif align_hint = Align_hint_other_zero then
				copy (other)
				set_negative
			else
				test := coefficient.three_way_comparison (other.coefficient)
				if test = 0 then
					coefficient.keep_head (1)
					coefficient.put (0, 0)
				elseif test > 0 then
					coefficient.integer_subtract (other.coefficient)
				else
					other.coefficient.integer_subtract (coefficient)
					coefficient.copy (other.coefficient)
					set_negative
				end
				exponent := exponent.min (other.exponent)
			end
		end

	round (ctx: MA_DECIMAL_CONTEXT)
			-- Round Current according to ctx.rounding_mode.
		require
			not_special: not is_special
			roundable: ctx.digits > 0 and then count > ctx.digits
		do
			if is_special then
				if is_signaling_nan then
					ctx.signal (Signal_invalid_operation, "sNaN in 'round'")
				end
			elseif count > ctx.digits then
				ctx.signal (Signal_rounded, "Argument rounded")
				if lost_digits (ctx) then
					ctx.signal (Signal_inexact, "Inexact when rouding")
				end
				inspect ctx.rounding_mode
				when Round_up then
					do_round_up (ctx)
				when Round_down then
					do_round_down (ctx)
				when Round_ceiling then
					do_round_ceiling (ctx)
				when Round_floor then
					do_round_floor (ctx)
				when Round_half_up then
					do_round_half_up (ctx)
				when Round_half_down then
					do_round_half_down (ctx)
				when Round_half_even then
					do_round_half_even (ctx)
				else
				end
			end
		ensure
			rounded: count <= ctx.digits
		end

	align_and_hint (other: like Current; precision: INTEGER_32): INTEGER_32
			-- Align Current and other with respect to precision
			-- and give hint for further operations
		local
			new_count, new_exponent: INTEGER_32
			limited_precision: BOOLEAN
			shift_count: INTEGER_32
		do
			limited_precision := (precision > 0)
			if exponent = other.exponent then
				new_count := count.max (other.count)
				if new_count > count then
					grow (new_count)
				end
				if new_count > other.count then
					other.grow (new_count)
				end
				Result := Align_hint_both
			elseif exponent > other.exponent then
				new_exponent := exponent.min (adjusted_exponent - (precision + 1))
				if new_exponent > other.adjusted_exponent then
					if is_zero then
						Result := Align_hint_other_zero
					else
						if limited_precision then
							if other.is_zero then
								Result := Align_hint_current_zero
							else
								Result := Align_hint_current
							end
							shift_count := precision + 1 - count
							if shift_count > 0 then
								shift_left (shift_count)
							end
						end
					end
				else
					if limited_precision then
						align_overlapped (other, precision)
					else
						align_unlimited (other)
					end
					Result := Align_hint_both
				end
			else
				new_exponent := other.exponent.min (other.adjusted_exponent - (precision + 1))
				if new_exponent > adjusted_exponent then
					if other.is_zero then
						Result := Align_hint_current_zero
					else
						if limited_precision then
							if is_zero then
								Result := Align_hint_other_zero
							else
								Result := Align_hint_other
							end
							shift_count := precision + 1 - other.count
							if shift_count > 0 then
								other.shift_left (shift_count)
							end
						end
					end
				else
					if limited_precision then
						other.align_overlapped (Current, precision)
					else
						other.align_unlimited (Current)
					end
					Result := Align_hint_both
				end
			end
		ensure
			hint_both_is_same_count: Result = Align_hint_both implies count = other.count
			hint_both_is_same_exponent: Result = Align_hint_both implies exponent = other.exponent
		end

	align_overlapped (other: like Current; precision: INTEGER_32)
			-- Align overlapping numbers.
		require
			other_not_void: other /= Void
			exponent_greater: exponent > other.exponent
		local
			exponent_delta, new_digits: INTEGER_32
		do
			exponent_delta := exponent - other.exponent
			if exponent_delta > 0 then
				shift_left (exponent_delta)
			end
			new_digits := count.max (other.count)
			if new_digits > other.count then
				other.grow (new_digits)
			end
			if new_digits > count then
				grow (new_digits)
			end
		ensure
			same_count: count = other.count
			same_exponent: exponent = other.exponent
		end

	align_unlimited (other: like Current)
			-- Align unlimited.
		require
			other_not_void: other /= Void
			exponent_greater: exponent > other.exponent
		local
			count_alignment: INTEGER_32
		do
			shift_left (exponent - other.exponent)
			count_alignment := count - other.count
			if count_alignment > 0 then
				other.grow (count)
			elseif count_alignment < 0 then
				grow (other.count)
			end
		ensure
			same_count: count = other.count
			same_exponent: exponent = other.exponent
		end

	shift_left (a_count: INTEGER_32)
			-- Shift the coefficient left a_count position and adjust exponent.
			-- value still must be the same as with the original exponent.
		require
			not_special: not is_special
			a_count_positive: a_count > 0
		do
			coefficient.shift_left (a_count)
			exponent := exponent - a_count
		ensure
			count_adapted: count = old count + a_count
			exponent_adapted: exponent = old exponent - a_count
		end

	shift_right (a_count: INTEGER_32)
			-- Shift the coefficient right a_count position and adjust exponent.
			-- Digits are lost.
		require
			not_special: not is_special
			a_count_positive: a_count > 0
		do
			coefficient.shift_right (a_count)
			exponent := exponent + a_count
		ensure
			exponent_adapted: exponent = old exponent + a_count
		end

	grow (a_count: INTEGER_32)
			-- Grow coefficient so that it can accommodate a_count digits.
		require
			not_special: not is_special
			a_count_greater_zero: a_count > 0
			a_count_less_10_000: a_count < 10000
		do
			coefficient.grow (a_count)
		ensure
			count_set: count = a_count
		end

	do_round_up (ctx: MA_DECIMAL_CONTEXT)
			-- Round away from zero.
		require
			ctx_not_void: ctx /= Void
		local
			old_count, exponent_increment: INTEGER_32
		do
			if lost_digits (ctx) then
				old_count := count
				coefficient.integer_quick_add_msd (1, ctx.digits)
				exponent_increment := old_count - ctx.digits
				exponent := exponent + exponent_increment
			end
			if count > ctx.digits then
				shift_right (count - ctx.digits)
				coefficient.keep_head (ctx.digits)
			end
		end

	do_round_down (ctx: MA_DECIMAL_CONTEXT)
			-- Round towards zero.
		require
			ctx_not_void: ctx /= Void
			positive_precision: ctx.digits >= 1
		local
			exponent_increment: INTEGER_32
			l_count: INTEGER_32
		do
			l_count := count - ctx.digits
			coefficient.shift_right (l_count)
			exponent_increment := l_count
			exponent := exponent + exponent_increment
			coefficient.keep_head (ctx.digits)
		end

	do_round_ceiling (ctx: MA_DECIMAL_CONTEXT)
			-- Round to a more positive number.
		require
			ctx_not_void: ctx /= Void
		do
			if is_negative or else not lost_digits (ctx) then
				do_round_down (ctx)
			else
				do_round_up (ctx)
			end
		end

	do_round_floor (ctx: MA_DECIMAL_CONTEXT)
			-- Round to a more negative number.
		require
			ctx_not_void: ctx /= Void
		do
			if is_positive or else not lost_digits (ctx) then
				do_round_down (ctx)
			else
				do_round_up (ctx)
			end
		end

	do_round_half_up (ctx: MA_DECIMAL_CONTEXT)
			-- Round to nearest neighbor, where an equidistant value is rounded up.
			-- If the discarded digits represent greater than or equal to half (0.5 times) the value
			-- of a one in the next position then the result should be rounded up (away from zero).
			-- Otherwise the discarded digits are ignored.
		require
			ctx_not_void: ctx /= Void
		do
			inspect three_way_compare_discarded_to_half (ctx)
			when 1, 0 then
				do_round_up (ctx)
			else
				do_round_down (ctx)
			end
		end

	do_round_half_down (ctx: MA_DECIMAL_CONTEXT)
			-- Round to nearest neighbor, where an equidistant value is rounded down.
			-- If the discarded digits represent greater than half (0.5 times)
			-- the value of a one in the next position then the result should be
			-- rounded up (away from zero). Otherwise the discarded digits are ignored.
		require
			ctx_not_void: ctx /= Void
		do
			inspect three_way_compare_discarded_to_half (ctx)
			when -1 then
				do_round_down (ctx)
			when 1 then
				do_round_up (ctx)
			else
				do_round_down (ctx)
			end
		end

	three_way_compare_discarded_to_half (ctx: MA_DECIMAL_CONTEXT): INTEGER_32
			-- Compare discarded digits greater than 0.5
		require
			ctx_not_void: ctx /= Void
		local
			digit, index: INTEGER_32
		do
			digit := coefficient.item (count - ctx.digits - 1)
			if digit > 5 then
				Result := 1
			elseif digit < 5 then
				Result := -1
			else
				index := count - ctx.digits - 2
				from
				until
					index < 0 or else coefficient.item (index) /= 0
				loop
					index := index - 1
				end
				if index >= 0 then
					Result := 1
				else
					Result := 0
				end
			end
		ensure
			definition: Result >= -1 and then Result <= 1
		end

	do_round_half_even (ctx: MA_DECIMAL_CONTEXT)
			-- Round to nearest neighbor, where an equidistant value is rounded to the nearest even neighbor.
			-- If the discarded digits represent greater than half (0.5 times) the value of a one in the
			-- next position then the result should be rounded up (away from zero).
			-- If they represent less than half, then the result should be rounded down.
			-- Otherwise (they represent exactly half) the result is rounded down if its rightmost digit
			-- is even, or rounded up if its rightmost digit is odd (to make an even digit).
		require
			ctx_not_void: ctx /= Void
		do
			inspect three_way_compare_discarded_to_half (ctx)
			when -1 then
				do_round_down (ctx)
			when 1 then
				do_round_up (ctx)
			else
				if (coefficient.item (count - ctx.digits) \\ 2 = 0) then
					do_round_down (ctx)
				else
					do_round_up (ctx)
				end
			end
		end

	lost_digits (ctx: MA_DECIMAL_CONTEXT): BOOLEAN
			-- Should Current loose digits if rounded wrt ctx?
		require
			ctx_not_void: ctx /= Void
		local
			index: INTEGER_32
		do
			from
				index := count - ctx.digits - 1
			until
				index < 0 or else coefficient.item (index) /= 0
			loop
				index := index - 1
			end
			Result := index >= 0
		ensure
			definition1: (count - ctx.digits - 1) >= 0 implies Result = (coefficient.subcoefficient (0, count - ctx.digits - 1)).is_significant
			definition2: (count - ctx.digits - 1) < 0 implies not Result
		end

	is_overflow (ctx: MA_DECIMAL_CONTEXT): BOOLEAN
			-- Is there an overflow condition wrt ctx?
		require
			ctx_not_void: ctx /= Void
		do
			Result := adjusted_exponent > ctx.exponent_limit
		ensure
			definition: Result = (adjusted_exponent > ctx.exponent_limit)
		end

	is_underflow (ctx: MA_DECIMAL_CONTEXT): BOOLEAN
			-- Is there an underflow condition wrt ctx?
		require
			ctx_not_void: ctx /= Void
		do
			Result := adjusted_exponent < - ctx.exponent_limit
		ensure
			definition: Result = (adjusted_exponent < - ctx.exponent_limit)
		end

	clean_up (ctx: MA_DECIMAL_CONTEXT)
			-- Clean up Current wrt ctx, rounding it if necessary.
		require
			ctx_not_void: ctx /= Void
		local
			lost_digits_trap, lost_digits_flag: BOOLEAN
		do
			if not is_special then
				lost_digits_trap := ctx.is_trapped (Signal_lost_digits)
				lost_digits_flag := ctx.is_flagged (Signal_lost_digits)
				ctx.disable_trap (Signal_lost_digits)
				ctx.reset_flag (Signal_lost_digits)
				strip_leading_zeroes
				if is_underflow (ctx) then
					do_underflow (ctx)
				else
					if ctx.digits > 0 and then count > ctx.digits then
						round (ctx)
					end
					if is_overflow (ctx) then
						do_overflow (ctx)
					end
				end
				if lost_digits_trap then
					ctx.enable_trap (Signal_lost_digits)
				end
				if lost_digits_flag or else ctx.is_flagged (Signal_lost_digits) then
					ctx.set_flag (Signal_lost_digits)
				else
					ctx.reset_flag (Signal_lost_digits)
				end
			end
		end

	strip_leading_zeroes
			-- Strip leading zeroes.
		require
			not_special: not is_special
		do
			coefficient.strip_leading_zeroes
		end

	set_largest (ctx: MA_DECIMAL_CONTEXT)
			-- Set to largest finite number that can be represented with ctx.precision.
		require
			ctx_not_void: ctx /= Void
		local
			index: INTEGER_32
		do
			if count < ctx.digits then
				grow (ctx.digits)
			end
			from
				index := 0
			until
				index >= count
			loop
				coefficient.put (9, index)
				index := index + 1
			end
			coefficient.keep_head (ctx.digits)
			if exponent < 0 then
				exponent := - ctx.exponent_limit + (count - 1)
			else
				exponent := ctx.exponent_limit - (count - 1)
			end
		end

	promote_to_infinity (a_sign: INTEGER_32)
			-- Promote to infinity.
		do
			make_infinity (a_sign)
		ensure
			infinity: is_infinity
			sign_set: sign = a_sign
		end

	do_overflow (ctx: MA_DECIMAL_CONTEXT)
			-- Do overflow.
		require
			ctx_not_void: ctx /= Void
			overflow: is_overflow (ctx)
		do
			if not is_zero then
				ctx.signal (Signal_overflow, "")
				inspect ctx.rounding_mode
				when Round_half_up, Round_half_even, Round_half_down, Round_up then
					promote_to_infinity (sign)
				when Round_down then
					set_largest (ctx)
				when Round_ceiling then
					if is_negative then
						set_largest (ctx)
					else
						promote_to_infinity (sign)
					end
				when Round_floor then
					if is_positive then
						set_largest (ctx)
					else
						promote_to_infinity (sign)
					end
				end
				ctx.signal (Signal_inexact, "do_overflow")
				ctx.signal (Signal_rounded, "do_overflow")
			else
				set_exponent (ctx.exponent_limit)
			end
		end

	do_underflow (ctx: MA_DECIMAL_CONTEXT)
			-- Do underflow.
		require
			ctx_not_void: ctx /= Void
			underflow: is_underflow (ctx)
		local
			e_tiny, shared_digits, subnormal_count, count_upto_elimit, saved_digits: INTEGER_32
			l_is_zero, l_was_rounded: BOOLEAN
			l_reason: detachable STRING_8
			value: INTEGER_32
		do
			l_is_zero := is_zero
			if not l_is_zero then
				ctx.signal (Signal_subnormal, "")
			else
				l_was_rounded := ctx.is_flagged (Signal_rounded)
				l_reason := ctx.reason
			end
			e_tiny := ctx.e_tiny
			if exponent < e_tiny then
				saved_digits := ctx.digits
				shared_digits := adjusted_exponent - e_tiny + 1
				if shared_digits < 0 then
					saved_digits := ctx.digits
					ctx.force_digits (coefficient.count - 1)
					value := 0
					inspect ctx.rounding_mode
					when Round_up then
						value := 1
					when Round_ceiling then
						if is_positive and then lost_digits (ctx) then
							value := 1
						end
					when Round_floor then
						if is_positive or else not lost_digits (ctx) then
							value := 0
						else
							value := 1
						end
					else
						value := 0
					end
					ctx.set_digits (saved_digits)
					coefficient.put (value, 0)
					coefficient.keep_head (1)
					exponent := e_tiny
					ctx.signal (Signal_inexact, "Rescaling to e_tiny")
					ctx.signal (Signal_rounded, "Rescaling to e_tiny")
					ctx.signal (Signal_underflow, "Rescaling to e_tiny")
				else
					if shared_digits = 0 then
						ctx.set_digits (1)
						grow (count + 1)
					else
						check
						end
						count_upto_elimit := - ctx.exponent_limit - exponent + 1
						if count < count_upto_elimit then
							grow (count_upto_elimit)
						end
						subnormal_count := - ctx.exponent_limit - e_tiny + 1
						ctx.set_digits (subnormal_count)
					end
					round (ctx)
					ctx.set_digits (saved_digits)
					strip_leading_zeroes
					if ctx.is_flagged (Signal_subnormal) and then ctx.is_flagged (Signal_inexact) then
						ctx.signal (Signal_underflow, "Underflow when rescaling")
					end
					if is_overflow (ctx) then
						do_overflow (ctx)
					end
				end
				exponent := e_tiny
				if l_is_zero then
					if l_was_rounded and then l_reason /= Void then
						ctx.signal (Signal_rounded, l_reason)
					else
						ctx.reset_flag (Signal_rounded)
					end
				end
			end
		end

	Division_standard: INTEGER_32 = 1

	Division_integer: INTEGER_32 = 2

	Division_remainder: INTEGER_32 = 3
			-- Division types

	do_divide (other: like Current; ctx: MA_DECIMAL_CONTEXT; division_type: INTEGER_32): like Current
			-- Do a division_type of Current by other
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			integer_division: BOOLEAN
		do
			integer_division := (division_type = Division_integer) or else (division_type = Division_remainder)
			if is_special or else other.is_special then
				if is_nan or else other.is_nan then
					if is_signaling_nan or else other.is_signaling_nan then
						ctx.signal (Signal_invalid_operation, "sNan in divide")
					end
					Result := Nan
				elseif is_infinity and then other.is_infinity then
					ctx.signal (Signal_invalid_operation, "[+-] Inf / [+-] Inf")
					Result := Nan
				elseif is_infinity then
					if sign = other.sign then
						Result := Infinity
					else
						Result := Negative_infinity
					end
					if other.is_zero then
						ctx.signal (Signal_division_by_zero, "[+-] Inf / [+-] 0")
					end
				elseif other.is_infinity then
					if sign = other.sign then
						Result := zero
					else
						Result := Negative_zero
					end
				else
					Result := Nan
				end
			else
				if other.is_zero then
					if is_zero then
						ctx.signal (Signal_invalid_operation, "Division Undefined : O/O")
						Result := Nan
					else
						ctx.signal (Signal_division_by_zero, "Division by zero")
						if sign = other.sign then
							Result := Infinity
						else
							Result := Negative_infinity
						end
					end
				elseif is_zero then
					create Result.make_zero
					if integer_division then
						Result.set_exponent (0)
					else
						Result.set_exponent (exponent - other.adjusted_exponent)
					end
					if sign = other.sign then
						Result.set_positive
					else
						Result.set_negative
					end
					Result.clean_up (ctx)
				else
					Result := internal_divide (other, ctx, division_type)
					if sign = other.sign then
						Result.set_positive
					else
						Result.set_negative
					end
					Result.clean_up (ctx)
				end
			end
		ensure
			divide_not_void: Result /= Void
		end

	internal_divide (other: like Current; ctx: MA_DECIMAL_CONTEXT; division_type: INTEGER_32): like Current
			-- Divide Current by other whith respect to ctx
		require
			other_not_void: other /= Void
			ctx_not_void: ctx /= Void
		local
			dividend, divisor, local_remainder: like Current
			adjust, divisor_adjust, dividend_adjust, current_digit_exponent, new_exponent: INTEGER_32
			original_dividend_exponent, original_divisor_exponent, bias: INTEGER_32
			done, integer_division, impossible, is_negative_exponent, dividend_is_zero: BOOLEAN
		do
			integer_division := (division_type /= Division_standard)
			create dividend.make_copy (Current)
			create divisor.make_copy (other)
			original_divisor_exponent := divisor.exponent
			original_dividend_exponent := dividend.exponent
			if dividend.is_zero then
				dividend_is_zero := True
			end
			create Result.make (ctx.digits + 1)
			adjust := 0
			divisor_adjust := 0
			dividend_adjust := 0
			from
			until
				dividend.coefficient >= divisor.coefficient
			loop
				dividend.shift_left (1)
				adjust := adjust + 1
				dividend_adjust := dividend_adjust + 1
			end
			check
					dividend.coefficient >= divisor.coefficient
			end
			from
				divisor.shift_left (1)
			until
				dividend.coefficient < divisor.coefficient
			loop
				adjust := adjust - 1
				divisor.shift_left (1)
				divisor_adjust := divisor_adjust + 1
			end
			check
					dividend.coefficient < divisor.coefficient
			end
			divisor.shift_right (1)
			divisor.coefficient.keep_head (divisor.coefficient.count - 1)
			from
				if integer_division then
					current_digit_exponent := (original_dividend_exponent - (original_divisor_exponent + adjust))
					impossible := (current_digit_exponent >= ctx.digits)
					is_negative_exponent := (current_digit_exponent) < 0
					done := is_negative_exponent or else impossible
				else
					impossible := False
					done := False
				end
				if not done then
					Result.coefficient.grow (ctx.digits + 1)
					Result.coefficient.keep_head (1)
				end
			until
				done
			loop
				from
				until
					divisor.coefficient > dividend.coefficient
				loop
					dividend.coefficient.integer_subtract (divisor.coefficient)
					Result.coefficient.integer_quick_add_msd (1, Result.count)
				end
				inspect division_type
				when Division_standard then
					if (dividend.is_zero and then adjust >= 0) or else (Result.count = ctx.digits) then
						done := True
					end
				else
					if current_digit_exponent = 0 then
						done := True
					end
				end
				if not done then
					Result.coefficient.shift_left (1)
					dividend.coefficient.shift_left (1)
					adjust := adjust + 1
					current_digit_exponent := current_digit_exponent - 1
				end
			end
			if impossible then
				Result.set_quiet_nan
				ctx.signal (Signal_invalid_operation, "Division impossible")
			else
				local_remainder := dividend
				inspect division_type
				when Division_standard then
					if local_remainder.is_zero then
						if adjust < 0 then
							ctx.signal (Signal_rounded, "Artificial rounding in division where remainder is zero")
						end
					else
						Result.coefficient.shift_left (1)
						adjust := adjust + 1
						divisor.coefficient.integer_subtract (local_remainder.coefficient)
						inspect divisor.coefficient.three_way_comparison (local_remainder.coefficient)
						when 0 then
							Result.coefficient.put (5, 0)
						when 1 then
							Result.coefficient.put (4, 0)
						else
							Result.coefficient.put (6, 0)
						end
					end
					if dividend.is_zero then
						Result.set_exponent (original_dividend_exponent - (original_divisor_exponent + adjust))
					else
						Result.set_exponent (exponent - (original_divisor_exponent + adjust))
					end
				when Division_integer then
					Result.set_exponent (0)
				else
					Result := local_remainder
					if is_negative_exponent then
						create Result.make_copy (Current)
					else
						new_exponent := original_dividend_exponent.min (original_divisor_exponent)
						bias := new_exponent - (dividend.exponent.min (divisor.exponent))
						if Result.is_zero then
							if dividend_is_zero then
								new_exponent := original_dividend_exponent
							elseif new_exponent >= 0 then
								new_exponent := 0
							else
								new_exponent := exponent - (original_divisor_exponent + adjust)
							end
						else
							if bias /= 0 then
								Result.coefficient.shift_right (bias.abs)
							end
						end
						Result.set_exponent (new_exponent)
					end
				end
			end
		ensure
			divide_not_void: Result /= Void
		end

	do_rescale_special (ctx: MA_DECIMAL_CONTEXT)
			-- Rescale special numbers.
		require
			is_special: is_special
			not_constant_infinity: Current /= Infinity
			not_constant_negative_infinity: Current /= Negative_infinity
			not_constant_nan: Current /= Nan
			not_constant_snan: Current /= Snan
		do
			if is_quiet_nan then
			elseif is_signaling_nan then
				ctx.signal (Signal_invalid_operation, "sNaN as operand in rescale")
				set_quiet_nan
			elseif is_infinity then
			end
		end

	to_string_general (is_engineering: BOOLEAN): STRING_8
			-- Current as a number in engineering notation if is_engineering
			-- is True, in scientific notation otherwise.
		local
			str_coefficient: STRING_8
			str_zero_pad: STRING_8
			index, after_point_count, the_exponent, printed_exponent, exponent_difference: INTEGER_32
			digits_before_point: INTEGER_32
			exponential: BOOLEAN
		do
			create Result.make (0)
			if is_special then
				if is_quiet_nan then
					Result.append_string ("NaN")
				elseif is_signaling_nan then
					Result.append_string ("sNaN")
				else
					if is_negative then
						Result.append_string ("-")
					end
					Result.append_string ("Infinity")
				end
			else
				if is_negative then
					Result.append_string ("-")
				end
				create str_coefficient.make (count)
				from
					index := count - 1
				until
					index < 0
				loop
					str_coefficient.append_character (Integer_.to_character (('0').code + coefficient.item (index)))
					index := index - 1
				end
				the_exponent := adjusted_exponent
				exponential := not (exponent <= 0 and then adjusted_exponent >= -6)
				if exponential then
					printed_exponent := the_exponent
					if is_engineering then
						from
						until
							printed_exponent \\ 3 = 0
						loop
							printed_exponent := printed_exponent - 1
						end
						exponent_difference := the_exponent - printed_exponent
						if not is_zero then
							digits_before_point := 1 + exponent_difference
							from
							until
								str_coefficient.count >= digits_before_point
							loop
								str_coefficient.append_character ('0')
							end
						else
							digits_before_point := 1
						end
					else
						digits_before_point := 1
					end
					if str_coefficient.count > digits_before_point then
						Result.append_string (str_coefficient.substring (1, digits_before_point))
						Result.append_character ('.')
						Result.append_string (str_coefficient.substring (digits_before_point + 1, str_coefficient.count))
					else
						Result.append_string (str_coefficient)
					end
					if printed_exponent /= 0 then
						Result.append_character ('E')
						if the_exponent < 0 then
							Result.append_character ('-')
						else
							Result.append_character ('+')
						end
						Result.append_string ((printed_exponent.abs).out)
					end
				else
					if exponent < 0 then
						after_point_count := exponent.abs
						if after_point_count > str_coefficient.count then
							create str_zero_pad.make_filled ('0', after_point_count - str_coefficient.count)
							Result.append_string ("0.")
							Result.append_string (str_zero_pad)
							Result.append_string (str_coefficient)
						elseif after_point_count = str_coefficient.count then
							Result.append_string ("0.")
							Result.append_string (str_coefficient)
						else
							Result.append_string (str_coefficient.substring (1, str_coefficient.count - after_point_count))
							Result.append_string (".")
							Result.append_string (str_coefficient.substring (str_coefficient.count - after_point_count + 1, str_coefficient.count))
						end
					else
						Result.append_string (str_coefficient)
					end
				end
			end
		ensure
			to_string_not_void: Result /= Void
		end
	
feature {NONE} -- Implementation

	Parser: MA_DECIMAL_TEXT_PARSER
			-- Decimal text parser
		once
			create Result.make
		ensure
			parser_not_void: Result /= Void
		end

	Once_zero: MA_DECIMAL
			-- Shared Zero
		once
			create Result.make_zero
		ensure
			instance_free: class
			zero_not_void: Result /= Void
			is_zero: Result.is_zero
		end

	Once_one: MA_DECIMAL
			-- Shared One
		once
			create Result.make_one
		ensure
			instance_free: class
			one_not_void: Result /= Void
			is_one: Result.is_one
		end

	Special_coefficient: MA_DECIMAL_COEFFICIENT
		once
			create {MA_DECIMAL_COEFFICIENT_IMP} Result.make (1)
			Result.put (0, 0)
		ensure
			instance_free: class
			special_coefficient_not_void: Result /= Void
			zero: Result.is_zero
		end
	
invariant
	special_values: special >= Special_none and then special <= Special_quiet_nan
	coefficient_not_void: coefficient /= Void
	special_share_coefficient: is_special implies coefficient = Special_coefficient
	special_has_exponent_zero: is_special implies exponent = 0
	special_coefficient_is_zero: Special_coefficient.is_zero

end -- class MA_DECIMAL

Generated by ISE EiffelStudio