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 interface
	DECIMAL

create 
	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)

	make_from_decimal (decimal: MA_DECIMAL)
			-- create DECIMAL_WRAPPER from MA_DECIMAL

	make_from_int (int: INTEGER_32)
			-- create a DECIMAL object from integer m

	default_create
			-- create an empty object (it's equivalent to 0)
		require -- from  ANY
			True
		ensure then
				item.is_equal ("0")

	make_from_string_ctx (value_string: STRING_8; ctx: MA_DECIMAL_CONTEXT)
			-- Make a new decimal from value_string relative to ctx

	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

	make_from_big_int (big_int: BIG_INTEGER)
			-- Create from BIG_INTEGER

	make_from_rational (rational: RATIONAL)
			-- Create from RATIONAL

convert
	make_from_rational ({STRING_8}),
	make_from_rational ({BIG_INTEGER}),
	make_from_rational ({RATIONAL}),
	make_from_rational: {REAL_64},
	make_from_rational: {BIG_INTEGER},
	make_from_rational: {RATIONAL},
	make_from_rational: {STRING_8}

feature -- Access

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

	generator: STRING_8
			-- Name of current object's generating class
			-- (base class of the type of which it is a direct instance)
			-- (from ANY)
		ensure -- from ANY
			generator_not_void: Result /= Void
			generator_not_empty: not Result.is_empty
	
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)
		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)

	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)
		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))

	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
		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)

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

	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
		ensure then -- from COMPARABLE
			definition: Result = (other <= Current)

	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
		ensure then -- from COMPARABLE
			definition: Result = ((Current < other) or (Current ~ other))

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

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

	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)
		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))

	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
		ensure -- from ANY
			same_type: Result implies same_type (other)
			symmetric: Result implies other.standard_is_equal (Current)

	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
		ensure -- from COMPARABLE
			equal_zero: (Result = 0) = (Current ~ other)
			smaller_negative: (Result = -1) = (Current < other)
			greater_positive: (Result = 1) = (Current > other)
	
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

	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
		ensure -- from ANY
			definition: Result = (conforms_to (other) and other.conforms_to (Current))
	
feature -- Duplication

	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)
		ensure -- from ANY
			is_equal: Current ~ other

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

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

	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)
		ensure -- from ANY
			is_standard_equal: standard_is_equal (other)

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

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

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

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

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

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

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

	print (o: detachable ANY)
			-- Write terse external representation of o
			-- on standard output.
			-- (from ANY)
		ensure -- from ANY
			instance_free: class

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

	Operating_environment: OPERATING_ENVIRONMENT
			-- Objects available from the operating system
			-- (from ANY)
		ensure -- from ANY
			instance_free: class
			operating_environment_not_void: Result /= Void
	
feature -- comparison

	is_equal (other: DECIMAL): BOOLEAN
			-- check whether other is equal to current or not
		require -- from ANY
			other_not_void: other /= Void
		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))

	is_less alias "<" (other: DECIMAL): BOOLEAN
			-- whether current is less than other
		require -- from PART_COMPARABLE
			other_exists: other /= Void
		ensure then -- from COMPARABLE
			asymmetric: Result implies not (other < Current)
	
feature -- conversion

	as_big_int: BIG_INTEGER
			-- Represent as BIG_INTEGER
		require
			is_integer: is_integer

	as_rational: RATIONAL
			-- Represent as RATIONAL

	as_real32: REAL_32
			-- represent as a REAL_32
		require
			valid_real32: is_real32

	as_real64: REAL_64
			-- represent as a REAL_64
		require
			valid_real64: is_real64
	
feature -- operations

	absolute: DECIMAL
			-- returns the absoulte value of Current

	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.

	ceiling: DECIMAL
			-- Returns the smallest integer greater than or equal to Current

	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

	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)

	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)

	floor: DECIMAL
			-- Returns the greatest integer less than or equal to Current

	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.

	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.

	negate
			-- negates Current

	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.

	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)

	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.

	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

	sqrt: DECIMAL
			-- calculates square root for this instance
		require
			non_negative: not is_negative

	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.
	
feature -- queries

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

	divisible (other: DECIMAL): BOOLEAN
			-- may current object be divided by other?

	exponentiable (other: DECIMAL): BOOLEAN
			-- may current object be elevated to the power other?

	identity alias "+": DECIMAL
			-- unary plus

	is_integer: BOOLEAN
			-- Is Current an integer number?

	is_natural: BOOLEAN
			-- Is Current an Natural number?

	is_natural1: BOOLEAN
			-- Is Current an Natural1 number?

	is_negative: BOOLEAN
			-- Is Current less than or equal to zero?

	is_real32: BOOLEAN

	is_real64: BOOLEAN
			-- Is Current a double?

	one: DECIMAL
			-- neutral element for "*" and "/"

	opposite alias "-": DECIMAL
			-- unary minus

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

	precise_out: STRING_8
			-- return precise representation of the number

	zero: DECIMAL
			-- neutral element for "+" and "-"
	
feature -- rounding

	get_precision: INTEGER_32
			-- returns the current number of digits being kept by the number

	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

	reset_precision
			-- resets the precision of Current for future operations to Default_precision

	round (digits: INTEGER_32)
			-- rounds this instance to the specified digits number of decimal places
		require
			positive_digits: digits >= 0

	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

	set_precision (precision: INTEGER_32)
			-- sets the precision of Current for future operations
		require
			precision_positive: precision > 0
	
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