note
	description: "[
		Efficient value type for handling money objects
			in dollars and cents, with precision 
			and safety in plus and minus operators.
		See end of class for specification.
	]"
	author: "JSO"
	date: "$Date$"
	revision: "$Revision$"

expanded class interface
	MONEY

create 
	make,
	make_with_float,
	make_from_int,
	default_create

feature -- public

	cents: INTEGER_64

	amount: FLOAT
			-- float value of cents
		ensure
				Result = create {FLOAT}.make_from_real (cents / 100)
	
feature --compare

	is_equal (other: like Current): BOOLEAN
			-- Is other attached to an object of the same type
			-- as current object and identical to it?

	is_less alias "<" (other: MONEY): BOOLEAN
			-- Is current money less than other?
	
feature -- effect numeric

	plus alias "+" (other: MONEY): MONEY
			-- Sum with other safely
		require
			safe_to_do_plus: safe_plus (cents, other.cents)
		ensure
			correct: Result = (create {MONEY}.make_from_int (cents + other.cents))

	minus alias "-" (other: MONEY): MONEY
			-- minus with other safely
		require
			safe_to_do_minus: safe_minus (cents, other.cents)
		ensure
			correct: Result = (create {MONEY}.make_from_int (cents - other.cents))

	product alias "*" (arg: FLOAT): MONEY
			-- Product by arg
		ensure then
				Result = (create {MONEY}.make_with_float ((amount * arg)))

	one: like Current
			-- Neutral element for "*" and "/"

	zero: like Current
			-- Neutral element for "+" and "-"

	divisible (other: FLOAT): BOOLEAN
			-- May current object be divided by other?

	quotient alias "/" (other: FLOAT): MONEY
			-- Division by other
		require else
				other /= create {FLOAT}.make_from_real (0.0)

	identity alias "+": MONEY
			-- Unary plus

	opposite alias "-": MONEY
			-- Unary minus
	
feature -- allocation

	allocated (arg: INTEGER_32): MONEY_ARRAY
			-- Divide by arg into equal amounts
		require
				comment ("Avoid division by zero and within range")
			arg_positive: arg > 0 and arg.to_integer_64 <= cents
		ensure
				Result.count = arg
				Result.sum = Current
			result_correct: across
					1 |..| arg as j
				all
					(j.item.to_integer_64 <= cents \\ arg.to_integer_64 implies Result [j.item].cents = (cents // arg.to_integer_64) + 1) and (j.item.to_integer_64 > cents \\ arg.to_integer_64 implies Result [j.item].cents = (cents // arg.to_integer_64))
				end

	allocated_by_ratios (ratios: NUM_ARRAY [INTEGER_32]): MONEY_ARRAY
			-- Divide according to ratios
		require
				ratios /= Void and then ratios.sum /= ratios.sum.zero
		ensure
				Result.count = ratios.count
				Result.sum = Current
			result_correct: across
					1 |..| ratios.count as j
				all
					Result [j.item].cents = ((cents * ratios [j.item].to_integer_64) / ratios.sum.to_integer_64).floor.to_integer_64 or Result [j.item].cents = ((cents * ratios [j.item].to_integer_64) / ratios.sum.to_integer_64).floor + 1.to_integer_64
				end
	
feature -- out

	out: STRING_8
			-- New string containing terse printable representation
			-- of current object

	comment (s: STRING_8): BOOLEAN
	
invariant
	min: cents.Min_value = -9223372036854775808
	max: cents.Max_value = 9223372036854775807
	consistent_amount: amount = create {FLOAT}.make_from_real (cents / 100)

note
	specification: "[
			Efficient value type for handling money in dollars and cents.
			The class stores money as an INTEGER_64.
				MONEY values are within about 92,000 trillion dollars:
				$92,233,720,368,547,758.07
				Min_value: INTEGER_64 = -9223372036854775808
		    	Max_value: INTEGER_64 = 9223372036854775807
		    But the API works in terms of dollars and cents.
		    Thus create {MONEY}.make(10,966)creates $10.966.
		    Addition and subtraction are meaningful with type MONEY x MONEY -> MONEY.
		    Addition and subtraction are precise within the stated range,
		    	and are protected with preconditions to stay 
		    	safely within range without overflows.
		    For product, we have only one choice: MONEY x SCALAR -> MONEY
				where scalar can be float or int; we have float.
			For quotient we have MONEY x SCALAR -> MONEY
		    	which tells us amount / number_of_people
		    	i.e. approximately the amount that each person gets 
		    	dividing equally.Protected so that there is no division by zero.
		    These signatures satisfy dimensional analysis constraints.
		    As an alternative to the imprecise quotient (which can loose pennies),
		    	there are pecise allocation queries to divide money
		    	into equal amounts without loss.
		    Class MONEY uses a new type FLOAT
		    	with approximately equal operators.
		    	Division and multiplication using float is imprecise.
		    Feature 'out' displays money rounded to two decimal points.
	]"

end -- class MONEY

Generated by ISE EiffelStudio