note
	description: "[
				An arbitrary precision library for decimal numbers. 
				Following the 'General Decimal Arithmetic Specification' of gobo MA_DECIMAL.
				
				Creation
					make_from_string
					make_from_decimal
					make_from_int
					make_from_big_int
					make_from_rational
		
				Queries
					as_big_int: BIG_INTEGER
					as_rational: RATIONAL
					as_real32: REAL_32
					as_real64: REAL_64
					exp alias "^" (power: DECIMAL): DECIMAL
					identity alias "+": DECIMAL
					is_equal (other: DECIMAL): BOOLEAN
					is_greater alias ">" (other: DECIMAL): BOOLEAN
					is_greater_equal alias ">=" (other: DECIMAL): BOOLEAN
					is_integer: BOOLEAN
					is_less alias "<" (other: DECIMAL): BOOLEAN
					is_less_equal alias "<=" (other: DECIMAL): BOOLEAN
					is_natural: BOOLEAN
					is_natural1: BOOLEAN
					is_negative: BOOLEAN
					is_real32: BOOLEAN
					is_real64: BOOLEAN
					max (other: DECIMAL): DECIMAL
					min (other: DECIMAL): DECIMAL
					minus alias "-" (other: DECIMAL): DECIMAL
					one: DECIMAL
					opposite alias "-": DECIMAL
					out: STRING
					plus alias "+" (other: DECIMAL): DECIMAL
					precise_out: STRING
					precise_out_to (digits: INTEGER): STRING
					product alias "*" (other: DECIMAL): DECIMAL
					quotient alias "/" (other: DECIMAL): DECIMAL
					sqrt: DECIMAL
					round_to (digits: INTEGER): DECIMAL
					zero: DECIMAL
		
				Commands
					round (digits: INTEGER)
					set_precision (precision: INTEGER)
	]"
	author: "JSO, DC, and CD"
	date: "$Date$"
	revision: "$Revision$"

class 
	DECIMAL

create 
	make_from_string,
	make_from_decimal,
	make_from_int,
	default_create,
	make_from_string_ctx,
	make_with_precision,
	make_from_big_int,
	make_from_rational

convert
	make_from_string ({STRING_8}),
	make_from_big_int ({BIG_INTEGER}),
	make_from_rational ({RATIONAL}),
	as_real64: {REAL_64},
	as_big_int: {BIG_INTEGER},
	as_rational: {RATIONAL},
	out: {STRING_8}

feature -- Access

	generating_type: TYPE [detachable DECIMAL]
			-- Type of current object
			-- (type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generating_type_not_void: Result /= Void
		end

	generator: STRING_8
			-- Name of current object's generating class
			-- (base class of the type of which it is a direct instance)
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			generator_not_void: Result /= Void
			generator_not_empty: not Result.is_empty
		end
	
feature -- Comparison

	frozen deep_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void
			-- or attached to isomorphic object structures?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_deep_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			shallow_implies_deep: standard_equal (a, b) implies Result
			both_or_none_void: (a = Void) implies (Result = (b = Void))
			same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))
			symmetric: Result implies deep_equal (b, a)
		end

	frozen equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached
			-- to objects considered equal?
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.is_equal (b))
		end

	frozen is_deep_equal (other: DECIMAL): BOOLEAN
			-- Are Current and other attached to isomorphic object structures?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			shallow_implies_deep: standard_is_equal (other) implies Result
			same_type: Result implies same_type (other)
			symmetric: Result implies other.is_deep_equal (Current)
		end

	is_greater alias ">" (other: DECIMAL): BOOLEAN
			-- Is current object greater than other?
			-- (from COMPARABLE)
		require -- from PART_COMPARABLE
			other_exists: other /= Void
		do
			Result := other < Current
		ensure then -- from COMPARABLE
			definition: Result = (other < Current)
		end

	is_greater_equal alias ">=" (other: DECIMAL): BOOLEAN
			-- Is current object greater than or equal to other?
			-- (from COMPARABLE)
		require -- from PART_COMPARABLE
			other_exists: other /= Void
		do
			Result := not (Current < other)
		ensure then -- from COMPARABLE
			definition: Result = (other <= Current)
		end

	is_less_equal alias "<=" (other: DECIMAL): BOOLEAN
			-- Is current object less than or equal to other?
			-- (from COMPARABLE)
		require -- from PART_COMPARABLE
			other_exists: other /= Void
		do
			Result := not (other < Current)
		ensure then -- from COMPARABLE
			definition: Result = ((Current < other) or (Current ~ other))
		end

	max (other: DECIMAL): DECIMAL
			-- The greater of current object and other
			-- (from COMPARABLE)
		require -- from COMPARABLE
			other_exists: other /= Void
		do
			if Current >= other then
				Result := Current
			else
				Result := other
			end
		ensure -- from COMPARABLE
			current_if_not_smaller: Current >= other implies Result = Current
			other_if_smaller: Current < other implies Result = other
		end

	min (other: DECIMAL): DECIMAL
			-- The smaller of current object and other
			-- (from COMPARABLE)
		require -- from COMPARABLE
			other_exists: other /= Void
		do
			if Current <= other then
				Result := Current
			else
				Result := other
			end
		ensure -- from COMPARABLE
			current_if_not_greater: Current <= other implies Result = Current
			other_if_greater: Current > other implies Result = other
		end

	frozen standard_equal (a: detachable ANY; b: like arg #1): BOOLEAN
			-- Are a and b either both void or attached to
			-- field-by-field identical objects of the same type?
			-- Always uses default object comparison criterion.
			-- (from ANY)
		do
			if a = Void then
				Result := b = Void
			else
				Result := b /= Void and then a.standard_is_equal (b)
			end
		ensure -- from ANY
			instance_free: class
			definition: Result = (a = Void and b = Void) or else ((a /= Void and b /= Void) and then a.standard_is_equal (b))
		end

	frozen standard_is_equal (other: DECIMAL): BOOLEAN
			-- Is other attached to an object of the same type
			-- as current object, and field-by-field identical to it?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			same_type: Result implies same_type (other)
			symmetric: Result implies other.standard_is_equal (Current)
		end

	three_way_comparison (other: DECIMAL): INTEGER_32
			-- If current object equal to other, 0;
			-- if smaller, -1; if greater, 1
			-- (from COMPARABLE)
		require -- from COMPARABLE
			other_exists: other /= Void
		do
			if Current < other then
				Result := -1
			elseif other < Current then
				Result := 1
			end
		ensure -- from COMPARABLE
			equal_zero: (Result = 0) = (Current ~ other)
			smaller_negative: (Result = -1) = (Current < other)
			greater_positive: (Result = 1) = (Current > other)
		end
	
feature -- Status report

	conforms_to (other: ANY): BOOLEAN
			-- Does type of current object conform to type
			-- of other (as per Eiffel: The Language, chapter 13)?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		end

	same_type (other: ANY): BOOLEAN
			-- Is type of current object identical to type of other?
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		external
			"built_in"
		ensure -- from ANY
			definition: Result = (conforms_to (other) and other.conforms_to (Current))
		end
	
feature -- Duplication

	frozen clone (other: detachable ANY): like other
		obsolete "Use `twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- equal to other
			--
			-- For non-void other, clone calls copy;
			-- to change copying/cloning semantics, redefine copy.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.twin
			end
		ensure -- from ANY
			instance_free: class
			equal: Result ~ other
		end

	copy (other: DECIMAL)
			-- Update current object using fields of object attached
			-- to other, so as to yield equal objects.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_equal: Current ~ other
		end

	frozen deep_clone (other: detachable ANY): like other
		obsolete "Use `deep_twin' instead. [2017-05-31]"
			-- Void if other is void: otherwise, new object structure
			-- recursively duplicated from the one attached to other
			-- (from ANY)
		do
			if other /= Void then
				Result := other.deep_twin
			end
		ensure -- from ANY
			instance_free: class
			deep_equal: deep_equal (other, Result)
		end

	frozen deep_copy (other: DECIMAL)
			-- Effect equivalent to that of:
			--		copy (other . deep_twin)
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
		do
			copy (other.deep_twin)
		ensure -- from ANY
			deep_equal: deep_equal (Current, other)
		end

	frozen deep_twin: DECIMAL
			-- New object structure recursively duplicated from Current.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			deep_twin_not_void: Result /= Void
			deep_equal: deep_equal (Current, Result)
		end

	frozen standard_clone (other: detachable ANY): like other
		obsolete "Use `standard_twin' instead. [2017-05-31]"
			-- Void if other is void; otherwise new object
			-- field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		do
			if other /= Void then
				Result := other.standard_twin
			end
		ensure -- from ANY
			instance_free: class
			equal: standard_equal (Result, other)
		end

	frozen standard_copy (other: DECIMAL)
			-- Copy every field of other onto corresponding field
			-- of current object.
			-- (from ANY)
		require -- from ANY
			other_not_void: other /= Void
			type_identity: same_type (other)
		external
			"built_in"
		ensure -- from ANY
			is_standard_equal: standard_is_equal (other)
		end

	frozen standard_twin: DECIMAL
			-- New object field-by-field identical to other.
			-- Always uses default copying semantics.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			standard_twin_not_void: Result /= Void
			equal: standard_equal (Result, Current)
		end

	frozen twin: DECIMAL
			-- New object equal to Current
			-- twin calls copy; to change copying/twinning semantics, redefine copy.
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			twin_not_void: Result /= Void
			is_equal: Result ~ Current
		end
	
feature -- Basic operations

	frozen as_attached: attached DECIMAL
		obsolete "Remove calls to this feature. [2017-05-31]"
			-- Attached version of Current.
			-- (Can be used during transitional period to convert
			-- non-void-safe classes to void-safe ones.)
			-- (from ANY)
		do
			Result := Current
		end

	frozen default: detachable DECIMAL
			-- Default value of object's type
			-- (from ANY)
		do
		end

	frozen default_pointer: POINTER
			-- Default value of type POINTER
			-- (Avoid the need to write p.default for
			-- some p of type POINTER.)
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end

	default_rescue
			-- Process exception for routines with no Rescue clause.
			-- (Default: do nothing.)
			-- (from ANY)
		do
		end

	frozen do_nothing
			-- Execute a null action.
			-- (from ANY)
		do
		ensure -- from ANY
			instance_free: class
		end
	
feature -- Output

	Io: STD_FILES
			-- Handle to standard file setup
			-- (from ANY)
		once
			create Result
			Result.set_output_default
		ensure -- from ANY
			instance_free: class
			io_not_void: Result /= Void
		end

	print (o: detachable ANY)
			-- Write terse external representation of o
			-- on standard output.
			-- (from ANY)
		do
			if o /= Void then
				Io.put_string (o.out)
			end
		ensure -- from ANY
			instance_free: class
		end

	frozen tagged_out: STRING_8
			-- New string containing terse printable representation
			-- of current object
			-- (from ANY)
		external
			"built_in"
		ensure -- from ANY
			tagged_out_not_void: Result /= Void
		end
	
feature -- Platform

	Operating_environment: OPERATING_ENVIRONMENT
			-- Objects available from the operating system
			-- (from ANY)
		once
			create Result
		ensure -- from ANY
			instance_free: class
			operating_environment_not_void: Result /= Void
		end
	
feature {NONE} -- Retrieval

	frozen internal_correct_mismatch
			-- Called from runtime to perform a proper dynamic dispatch on correct_mismatch
			-- from MISMATCH_CORRECTOR.
			-- (from ANY)
		local
			l_msg: STRING_8
			l_exc: EXCEPTIONS
		do
			if attached {MISMATCH_CORRECTOR} Current as l_corrector then
				l_corrector.correct_mismatch
			else
				create l_msg.make_from_string ("Mismatch: ")
				create l_exc
				l_msg.append (generating_type.name)
				l_exc.raise_retrieval_exception (l_msg)
			end
		end
	
feature -- comparison

	is_equal (other: DECIMAL): BOOLEAN
			-- check whether other is equal to current or not
		require -- from ANY
			other_not_void: other /= Void
		do
			Result := ma_decimal.is_equal (other.ma_decimal)
		ensure -- from ANY
			symmetric: Result implies other ~ Current
			consistent: standard_is_equal (other) implies Result
		ensure then -- from COMPARABLE
			trichotomy: Result = (not (Current < other) and not (other < Current))
		end

	is_less alias "<" (other: DECIMAL): BOOLEAN
			-- whether current is less than other
		require -- from PART_COMPARABLE
			other_exists: other /= Void
		do
			Result := ma_decimal < other.ma_decimal
		ensure then -- from COMPARABLE
			asymmetric: Result implies not (other < Current)
		end
	
feature {NONE} -- constructors

	default_create
			-- create an empty object (it's equivalent to 0)
		require -- from  ANY
			True
		local
			empty: STRING_8
		do
			empty := "0"
			item := empty.twin
			ma_decimal := decimal_setup (item, Default_precision + 1)
		ensure then
				item.is_equal ("0")
		end

	make_from_big_int (big_int: BIG_INTEGER)
			-- Create from BIG_INTEGER
		do
			make_from_string (big_int.out)
		end

	make_from_int (int: INTEGER_32)
			-- create a DECIMAL object from integer m
		do
			item := int.out
			ma_decimal_ctx := decimal_ctx_for_value
			ma_decimal_ctx.set_digits (item.count + Default_precision)
			create ma_decimal.make_from_integer (int)
		end

	make_from_rational (rational: RATIONAL)
			-- Create from RATIONAL
		do
			make_from_string (rational.out)
		end

	make_from_string (s: STRING_8)
			-- create an object from string 's'
		require
			non_void: s /= Void
			non_empty: not s.is_empty
			has_correct_format: ensurevalid (s)
		local
			precision: INTEGER_32
			value: LIST [STRING_8]
		do
			value := s.split (Exponent_char.to_character_32)
			precision := value [1].count
			if precision < (Default_precision + value [1].split (Decimal_char.to_character_32) [1].count) then
				precision := Default_precision + value [1].split (Decimal_char.to_character_32) [1].count
			end
			ma_decimal := decimal_setup (s.twin, precision)
			item := decimal_2_value_string
		end

	make_with_precision (a_value: STRING_8; a_precision: INTEGER_32)
			-- Make a new decimal from a_value with `a_precision
		require
			positive_precision: a_precision > 0
		local
			l_ma_decimal_ctx: MA_DECIMAL_CONTEXT
		do
			create l_ma_decimal_ctx.make (a_precision, 4)
			create ma_decimal.make_from_string_ctx (a_value, l_ma_decimal_ctx)
			ma_decimal_ctx := l_ma_decimal_ctx
			item := decimal_2_value_string
		end
	
feature -- conversion

	as_big_int: BIG_INTEGER
			-- Represent as BIG_INTEGER
		require
			is_integer: is_integer
		do
			create Result.make_from_string (item)
		end

	as_rational: RATIONAL
			-- Represent as RATIONAL
		do
			create Result.make_from_string (item)
		end

	as_real32: REAL_32
			-- represent as a REAL_32
		require
			valid_real32: is_real32
		do
			Result := item.to_real_32
		end

	as_real64: REAL_64
			-- represent as a REAL_64
		require
			valid_real64: is_real64
		do
			Result := ma_decimal.to_double
		end
	
feature -- operations

	absolute: DECIMAL
			-- returns the absoulte value of Current
		do
			create Result.make_from_decimal (ma_decimal.abs)
		end

	add (other: DECIMAL): DECIMAL
			-- adds current to other and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of plus.
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.add (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end

	ceiling: DECIMAL
			-- Returns the smallest integer greater than or equal to Current
		local
			curr_abs: DECIMAL
			floored_absolute: DECIMAL
		do
			curr_abs := Current.absolute
			create floored_absolute.make_from_string (curr_abs.value_precise_out.split (Decimal_char.to_character_32) [1])
			if is_integer then
				Result := Current.twin
			else
				if is_negative then
					floored_absolute.negate
					Result := floored_absolute
				else
					Result := floored_absolute + create {DECIMAL}.make_from_string ("1")
				end
			end
		end

	divide (other: DECIMAL): DECIMAL
			-- divides current by other and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of quotient.
		require
			other_non_zero: other /~ zero
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.divide (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end

	exp alias "^" (p: DECIMAL): DECIMAL
			-- calculates the value of current instance raised to 'power' (capped to 500 digits of precision)
			-- Was declared in DECIMAL as synonym of exponent and power.
		require
			is_exponentiable: exponentiable (p)
		do
			if p.is_integer then
				Result := integer_exp (Current, p.ma_decimal.to_integer.abs)
			else
				Result := real_exp (Current, p.absolute)
			end
			if p.is_negative then
				Result := one / Result
			end
		end

	exponent (p: DECIMAL): DECIMAL
			-- calculates the value of current instance raised to 'power' (capped to 500 digits of precision)
			-- Was declared in DECIMAL as synonym of exp and power.
		require
			is_exponentiable: exponentiable (p)
		do
			if p.is_integer then
				Result := integer_exp (Current, p.ma_decimal.to_integer.abs)
			else
				Result := real_exp (Current, p.absolute)
			end
			if p.is_negative then
				Result := one / Result
			end
		end

	floor: DECIMAL
			-- Returns the greatest integer less than or equal to Current
		local
			curr_abs: DECIMAL
			floored_absolute: DECIMAL
		do
			curr_abs := Current.absolute
			create floored_absolute.make_from_string (curr_abs.value_precise_out.split (Decimal_char.to_character_32) [1])
			if is_integer then
				Result := Current.twin
			else
				if is_negative then
					floored_absolute.negate
					Result := floored_absolute - create {DECIMAL}.make_from_string ("1")
				else
					Result := floored_absolute
				end
			end
		end

	minus alias "-" (other: DECIMAL): DECIMAL
			-- subtracts other from current and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of subtract.
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.subtract (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end

	multiply (other: DECIMAL): DECIMAL
			-- multiplies current by other and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of product.
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.multiply (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end

	negate
			-- negates Current
		local
			l_decimal: MA_DECIMAL
		do
			l_decimal := ma_decimal.opposite
			Current.make_from_decimal (l_decimal)
		end

	plus alias "+" (other: DECIMAL): DECIMAL
			-- adds current to other and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of add.
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.add (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end

	power (p: DECIMAL): DECIMAL
			-- calculates the value of current instance raised to 'power' (capped to 500 digits of precision)
			-- Was declared in DECIMAL as synonym of exp and exponent.
		require
			is_exponentiable: exponentiable (p)
		do
			if p.is_integer then
				Result := integer_exp (Current, p.ma_decimal.to_integer.abs)
			else
				Result := real_exp (Current, p.absolute)
			end
			if p.is_negative then
				Result := one / Result
			end
		end

	product alias "*" (other: DECIMAL): DECIMAL
			-- multiplies current by other and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of multiply.
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.multiply (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end

	quotient alias "/" (other: DECIMAL): DECIMAL
			-- divides current by other and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of divide.
		require
			other_non_zero: other /~ zero
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.divide (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end

	sqrt: DECIMAL
			-- calculates square root for this instance
		require
			non_negative: not is_negative
		local
			l_epsilon: DECIMAL
			l_root: DECIMAL
		do
			l_epsilon := create {attached DECIMAL}.make_from_string (Epsilon)
			if out ~ zero.out then
				Result := zero
			else
				from
					l_root := clone_me
				until
					((l_root - (Current / l_root)).absolute <= create {DECIMAL}.make_from_string (Epsilon) * l_root)
				loop
					l_root := ((Current / l_root) + l_root) / create {DECIMAL}.make_from_string ("2.0")
				end
				Result := l_root
			end
		end

	subtract (other: DECIMAL): DECIMAL
			-- subtracts other from current and returns a new object
			-- The new DECIMAL will have a precision based on the criteria in calculate_new_precision
			-- Was declared in DECIMAL as synonym of minus.
		local
			l_decimal: MA_DECIMAL
			new_context: MA_DECIMAL_CONTEXT
			digits: INTEGER_32
		do
			digits := calculate_new_precision (other)
			create new_context.make (digits, Default_rounding)
			create l_decimal.make_zero
			l_decimal.set_shared_decimal_context (new_context)
			l_decimal := Current.ma_decimal.subtract (other.ma_decimal, l_decimal.shared_decimal_context)
			create Result.make_from_decimal (l_decimal)
		end
	
feature {DECIMAL} -- private fields

	calculate_new_precision (other: DECIMAL): INTEGER_32
			-- Used in primary operations to calculate new precision
			-- The new precision is the largest value of: Current, other, Default_precision,
			-- or the sum of Current's precision + other's precision.
		local
			curr_dig, other_dig, str_digs, digits: INTEGER_32
			curr_string, other_string: STRING_8
		do
			curr_string := Current.precise_out
			other_string := other.precise_out
			curr_dig := curr_string.count
			other_dig := other_string.count
			str_digs := curr_dig + other_dig
			digits := Default_precision
			if digits < ma_decimal_ctx.digits then
				digits := ma_decimal_ctx.digits
			end
			if digits < other.ma_decimal_ctx.digits then
				digits := other.ma_decimal_ctx.digits
			end
			if digits < str_digs then
				digits := str_digs
			end
			Result := digits
		end

	clone_me: DECIMAL
		local
			l_s: STRING_8
		do
			create l_s.make_from_string (item)
			create Result.make_from_decimal (ma_decimal)
		end

	decimal_2_value_string: STRING_8
			-- converts scientific_notation to value.precise_out
			-- *should be deprecated once MA_DECIMAL allows precise string representation
		local
			str_list: LIST [STRING_8]
			coefficient_list: LIST [STRING_8]
			decimal_left: STRING_8
			decimal_right: STRING_8
			coefficients: STRING_8
			zero_pad: STRING_8
			l_exponent: STRING_8
			pad_count: INTEGER_32
			int_exponent: INTEGER_32
			decimal_count: INTEGER_32
			index: INTEGER_32
		do
			if ma_decimal.is_zero then
				Result := "0"
			else
				str_list := ma_decimal.to_scientific_string.split (Exponent_char.to_character_32)
				coefficients := str_list [1]
				create Result.make_empty
				if str_list.count > 1 then
					l_exponent := str_list [2]
					int_exponent := l_exponent.to_integer
					coefficient_list := coefficients.split (Decimal_char.to_character_32)
					if coefficient_list.count > 1 then
						decimal_count := coefficient_list [2].count
					else
						decimal_count := 0
					end
					if int_exponent.abs >= decimal_count then
						Result.append (coefficient_list [1])
						if decimal_count > 0 then
							Result.append (coefficient_list [2])
						end
						create zero_pad.make_empty
						if int_exponent > 0 then
							zero_pad.make_filled ('0', int_exponent - decimal_count)
							Result.append (zero_pad)
						else
							zero_pad.make_filled ('0', int_exponent.abs - 1)
							Result.prepend (zero_pad)
							Result.prepend ("0.")
						end
					elseif int_exponent < 0 then
						Result.append ("0")
						Result.append (create {STRING_8}.make_filled (Decimal_char, 1))
						pad_count := int_exponent.abs - coefficient_list [1].count
						create zero_pad.make_filled ('0', pad_count)
						Result.append (zero_pad)
						Result.append (coefficient_list [1])
						Result.append (coefficient_list [2])
					else
						if int_exponent = 0 then
							Result := coefficients
						else
							decimal_left := coefficient_list [2].substring (1, int_exponent)
							decimal_right := coefficient_list [2].substring (int_exponent, decimal_count)
							Result.append (coefficient_list [1])
							Result.append (decimal_left)
							Result.append (create {STRING_8}.make_filled (Decimal_char, 1))
							Result.append (decimal_right)
						end
					end
				else
					coefficient_list := coefficients.split (Decimal_char.to_character_32)
					if coefficient_list.count > 1 and then zero.is_equal (create {DECIMAL}.make_from_string (coefficient_list [2])) then
						Result := coefficient_list [1]
					else
						Result := coefficients
					end
				end
				if Result.has (Decimal_char) then
					from
						index := Result.count
					until
						not (Result.at (index) ~ '0')
					loop
						index := index - 1
						Result := Result.substring (1, index)
					end
				end
			end
		end

	Decimal_char: CHARACTER_8 = '.'

	decimal_ctx_for_value: MA_DECIMAL_CONTEXT
			-- create MA_DECIMAL_CONTEXT configured according to VALUE
		do
			create Result.make (Default_precision, Default_rounding)
		end

	decimal_setup (s: STRING_8; dv: INTEGER_32): MA_DECIMAL
			-- create new MA_DECIMAL object from value "s" with precision "dv"
		require
			non_void: s /= Void
			non_empty: not s.is_empty
			has_correct_format: ensurevalid (s)
		do
			ma_decimal_ctx := decimal_ctx_for_value
			ma_decimal_ctx.set_digits (dv)
			create Result.make_from_string_ctx (s, ma_decimal_ctx)
			Result.set_shared_decimal_context (ma_decimal_ctx)
		end

	Default_precision: INTEGER_32 = 36
			-- the minimum accuracy of division results;
			-- division results will be accurate to AT LEAST this many digits.

	Default_rounding: INTEGER_32 = 4
			-- Use half-round-up rounding

	ensurevalid (s: STRING_8): BOOLEAN
			-- check if the given string is of the correct format
		require
			non_void: s /= Void
			non_empty: not s.is_empty
		local
			seendot: BOOLEAN
			i: INTEGER_32
		do
			seendot := False
			Result := True
			from
				i := 1
			until
				i > s.count
			loop
				if s.item (i) = '-' then
					if i > 1 then
						Result := False
						i := s.count + 1
					end
				elseif s.item (i) = Decimal_char then
					if i = s.count or seendot then
						Result := False
						i := s.count + 1
					end
					seendot := True
				elseif not s.item (i).is_digit then
					Result := False
					i := s.count + 1
				end
				i := i + 1
			end
		end

	Epsilon: STRING_8 = "0.000000000000001"
			-- for root computation

	Exponent_char: CHARACTER_8 = 'E'

	integer_exp (val: DECIMAL; p: INTEGER_32): DECIMAL
			-- recursively calculate 'val' to positive integer power 'power'
			-- Fast exponentiation, capped to 500 digits of precision
		local
			square: DECIMAL
		do
			if p = 0 then
				Result := create {attached DECIMAL}.make_from_string ("1")
			else
				square := val * val
				square.set_precision (500)
				Result := integer_exp (square, p // 2)
				if p \\ 2 = 1 then
					Result := Result * val
				end
			end
			Result.set_precision (500)
		end

	item: STRING_8
			-- String representation of the number

	ma_decimal: MA_DECIMAL
			-- ma_decimal is the underlying implementation

	ma_decimal_ctx: MA_DECIMAL_CONTEXT
			-- underlying implementation

	make_from_decimal (decimal: MA_DECIMAL)
			-- create DECIMAL_WRAPPER from MA_DECIMAL
		do
			create ma_decimal.make_copy (decimal)
			ma_decimal_ctx := ma_decimal.shared_decimal_context
			item := decimal_2_value_string
		end

	make_from_string_ctx (value_string: STRING_8; ctx: MA_DECIMAL_CONTEXT)
			-- Make a new decimal from value_string relative to ctx
		do
			create ma_decimal.make_from_string_ctx (value_string, ctx)
			ma_decimal_ctx := ctx
			item := decimal_2_value_string
		end

	real_exp (val: DECIMAL; p: DECIMAL): DECIMAL
			-- recursively calculate 'val' to real power 'power'
			-- Fast exponentiation until power < 1. Then binary search on fractional exponent
			-- Capped to 500 decimals of precision
		local
			temp: DECIMAL
			low: DECIMAL
			high: DECIMAL
			mid: DECIMAL
			l_root: DECIMAL
			acc: DECIMAL
			two: DECIMAL
		do
			two := create {DECIMAL}.make_from_string ("2.0")
			if p >= one then
				temp := real_exp (val, p / two)
				temp.set_precision (500)
				Result := temp * temp
			else
				from
					low := zero
					high := one
					l_root := val.sqrt
					acc := l_root
					mid := high / two
				until
					((mid - p).absolute <= create {DECIMAL}.make_from_string (Epsilon))
				loop
					l_root := l_root.sqrt
					if mid <= p then
						low := mid
						acc := acc * l_root
					else
						high := mid
						acc := acc * (one / l_root)
					end
					mid := (low + high) / two
				end
				Result := acc
			end
			Result.set_precision (500)
		end

	value_out: STRING_8
			-- String representation of object upto 2 decimal places
		do
			Result := precise_out_to (2)
		end

	value_precise_out: STRING_8
			-- Precise string representation of object
		do
			Result := item.twin
		end
	
feature -- queries

	debug_output: STRING_8
			-- String that should be displayed in debugger to represent Current.
		do
			Result := out
		ensure -- from DEBUG_OUTPUT
			result_not_void: Result /= Void
		end

	divisible (other: DECIMAL): BOOLEAN
			-- may current object be divided by other?
		do
			Result := not other.is_equal (zero)
		end

	exponentiable (other: DECIMAL): BOOLEAN
			-- may current object be elevated to the power other?
		do
			Result := True
		end

	identity alias "+": DECIMAL
			-- unary plus
		do
			create Result.make_from_decimal (ma_decimal)
		end

	is_integer: BOOLEAN
			-- Is Current an integer number?
		do
			Result := ma_decimal.is_integer
		end

	is_natural: BOOLEAN
			-- Is Current an Natural number?
		do
			Result := is_integer and Current >= zero
		end

	is_natural1: BOOLEAN
			-- Is Current an Natural1 number?
		do
			Result := is_integer and Current >= one
		end

	is_negative: BOOLEAN
			-- Is Current less than or equal to zero?
		do
			Result := ma_decimal.is_negative and Current /~ zero
		end

	is_real32: BOOLEAN
		local
			correct_length, in_range: BOOLEAN
			upper, lower: DECIMAL
		do
			create upper.make_from_string ("-3.4")
			create lower.make_from_string ("3.4")
			upper := upper * (create {DECIMAL}.make_from_string ("10")).exp (create {DECIMAL}.make_from_string ("38"))
			lower := lower * (create {DECIMAL}.make_from_string ("10")).exp (create {DECIMAL}.make_from_string ("38"))
			correct_length := item.count <= 7
			in_range := Current > create {DECIMAL}.make_from_string ("-3.4")
		end

	is_real64: BOOLEAN
			-- Is Current a double?
		do
			Result := ma_decimal.is_double
		end

	one: DECIMAL
			-- neutral element for "*" and "/"
		do
			create Result.make_from_string ("1")
		end

	opposite alias "-": DECIMAL
			-- unary minus
		do
			create Result.make_from_decimal (ma_decimal.opposite)
		end

	out: STRING_8
			-- return a representation of the number rounded to two decimal places
		do
			Result := value_out
		ensure -- from ANY
			out_not_void: Result /= Void
		end

	precise_out: STRING_8
			-- return precise representation of the number
		do
			Result := value_precise_out
		end

	zero: DECIMAL
			-- neutral element for "+" and "-"
		do
			create Result.make_from_string ("0")
		end
	
feature -- rounding

	get_precision: INTEGER_32
			-- returns the current number of digits being kept by the number
		do
			Result := ma_decimal_ctx.digits.twin
		end

	precise_out_to (digits: INTEGER_32): STRING_8
			-- returns the precise string representation to rounded to the specified 'digits' number of decimal places
		require
			positive_digits: digits >= 0
		local
			digit_list: LIST [STRING_8]
			decimal_left: STRING_8
			decimal_right: STRING_8
			rounded_up: INTEGER_32
			l_decimal, unrounded_dec, rounded_dec, carry_dec: DECIMAL
			zero_pad: STRING_8
		do
			create Result.make_empty
			l_decimal := round_to (digits)
			digit_list := l_decimal.precise_out.split (Decimal_char.to_character_32)
			decimal_left := digit_list [1]
			Result.append (decimal_left)
			Result.append (create {STRING_8}.make_filled (Decimal_char, 1))
			if digit_list.count > 1 then
				decimal_right := digit_list [2]
				if decimal_right.count < digits then
					create zero_pad.make_filled ('0', digits - decimal_right.count)
					Result.append (decimal_right)
					Result.append (zero_pad)
				else
					Result.append (decimal_right.substring (1, digits))
					if decimal_right.count > digits and then decimal_right.substring (digits + 1, digits + 1).to_integer >= 5 then
						rounded_up := decimal_right.substring (digits, digits).to_integer + 1
						if rounded_up < 10 then
							Result.remove_tail (1)
							Result.append_integer (rounded_up)
						else
							unrounded_dec := create {attached DECIMAL}.make_from_string (Result)
							create zero_pad.make_filled ('0', digits - 1)
							zero_pad.prepend ("0.")
							zero_pad.append ("1")
							carry_dec := create {attached DECIMAL}.make_from_string (zero_pad)
							rounded_dec := unrounded_dec + carry_dec
							create Result.make_from_string (rounded_dec.out)
						end
					end
				end
			else
				create zero_pad.make_filled ('0', digits)
				Result.append (zero_pad)
			end
		end

	reset_precision
			-- resets the precision of Current for future operations to Default_precision
		local
			l_decimal_ctx: MA_DECIMAL_CONTEXT
		do
			create l_decimal_ctx.make (Default_precision, ma_decimal_ctx.rounding_mode)
			create ma_decimal.make_from_string_ctx (item, l_decimal_ctx)
			ma_decimal_ctx := l_decimal_ctx
			ma_decimal.set_shared_decimal_context (l_decimal_ctx)
			item := decimal_2_value_string
		end

	round (digits: INTEGER_32)
			-- rounds this instance to the specified digits number of decimal places
		require
			positive_digits: digits >= 0
		local
			l_decimal_ctx: MA_DECIMAL_CONTEXT
			decimal_left: INTEGER_32
		do
			decimal_left := value_precise_out.split (Decimal_char.to_character_32) [1].count
			if is_negative then
				decimal_left := decimal_left - 1
			end
			if (value_precise_out.split (Decimal_char.to_character_32) [1] [1]).out ~ "0" or (is_negative and then (value_precise_out.split (Decimal_char.to_character_32) [1] [2]).out ~ "0") then
				decimal_left := decimal_left - 1
			end
			create l_decimal_ctx.make (decimal_left + digits, ma_decimal_ctx.rounding_mode)
			Current.make_from_decimal (create {MA_DECIMAL}.make_from_string_ctx (item, l_decimal_ctx))
		end

	round_to (digits: INTEGER_32): DECIMAL
			-- Returns a decimal that is the current value rounded to the specified 'digits' number of decimal places
			-- Uses default strategy as set in ma_decimal_ctx (half up)
		require
			positive_digits: digits >= 0
		local
			l_decimal_ctx: MA_DECIMAL_CONTEXT
			decimal_left: INTEGER_32
			decimal_left_int: BIG_INTEGER
		do
			decimal_left := value_precise_out.split (Decimal_char.to_character_32) [1].count
			if is_negative then
				decimal_left := decimal_left - 1
			end
			decimal_left_int := create {attached BIG_INTEGER}.make_from_string (value_precise_out.split (Decimal_char.to_character_32) [1])
			if decimal_left_int ~ create {BIG_INTEGER}.make_from_string ("0") then
				decimal_left := 0
			end
			if digits + decimal_left <= 0 then
				decimal_left := 1
			end
			create l_decimal_ctx.make (decimal_left + digits, ma_decimal_ctx.rounding_mode)
			create Result.make_from_decimal (create {MA_DECIMAL}.make_from_string_ctx (item, l_decimal_ctx))
		end

	set_precision (precision: INTEGER_32)
			-- sets the precision of Current for future operations
		require
			precision_positive: precision > 0
		local
			l_decimal_ctx: MA_DECIMAL_CONTEXT
		do
			create l_decimal_ctx.make (precision, ma_decimal_ctx.rounding_mode)
			create ma_decimal.make_from_string_ctx (item, l_decimal_ctx)
			ma_decimal_ctx := l_decimal_ctx
			ma_decimal.set_shared_decimal_context (l_decimal_ctx)
			item := decimal_2_value_string
		end
	
invariant
	consistent_rounding: Default_rounding = {MA_DECIMAL_CONTEXT}.round_half_up

		-- from COMPARABLE
	irreflexive_comparison: not (Current < Current)

		-- from ANY
	reflexive_equality: standard_is_equal (Current)
	reflexive_conformance: conforms_to (Current)

note
	info: "[
		 For an oracle see http://delaneyrm.com/MPCalcRB.html.
		 
		 test: BOOLEAN
			local
				d1, d2, d3, d4, d5, d6: DECIMAL
				r64: REAL_64
				i: INTEGER
				r: RATIONAL
			do
				comment ("test: Mortgage Calculation in REAL_64 vs DECIMAL")
				-- floating point arithmetic cannot distinguish
				-- between 0.4 and 0.40000000000000002
				r64 := 0.1 + 0.3
				Result := r64 = 0.4 and r64 = 0.40000000000000002
				check Result end
				-- decimal arithmetic can
				d1 := "0.1" ; d2 := "0.3"
				d3 := d1 + d2
				Result := d3 ~ "0.4"and d3 /~ "0.40000000000000002"
				check Result end
				assert_equal ("precise_out test", "0.4", d3.precise_out)
				assert_equal ("out test1", "0.40", d3.out)
				r64 := 100000*(1+0.05/12)^360
				Result := r64 = 446774.43140061089
				check Result end
				d1 := "100000" d2 := "0.05"; d3 := "12"; d4 := "360"
				d2.set_precision(120)
				d5 := d1 * (d1.one + d2/d3)^d4
				Result := d5.precise_out.starts_with ("446774.431400613221242807011041301589597772674487270698002439224547330881614835011308578390042190134313238565062087470576")
				check Result end
				assert_equal ("out test2", "446774.43", d5.out)
				d6 := d5 - "446774.43140061089"
				Result := d6.precise_out.starts_with ("0.000000002331242807011041301589597772674487270698002439224547330881614835011308578390042190134313238565062087470576")
				check
					 Result
				end
			end
	]"

end -- class DECIMAL

Generated by ISE EiffelStudio