note description: "[ Perform arithmetic (+, -, *, /) on real numbers of arbitrary precision. In the case of division, the default precision is 35 digits after the decimal point. This can be changed via set_division_precision. The queries is_integer, is_natural and is_natural1 may be used to check that the value is an integer or natural number. out provides a string represesentation rounded to two decimal places, and precise_out is a string representation of the precise value. The corresponding C# class was the inspiration for this class. ]" author: "Vlad Gerchikov for the Software Engineering Lab at sel@cse.yorku.ca" date: "$April 15, 2008$" revision: "$1.0$" expanded class VALUE create make_from_string, make, make_from_int, default_create convert make_from_string ({STRING_8}), as_double: {REAL_64} feature -- Access generating_type: TYPE [detachable VALUE] -- 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: VALUE): 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: VALUE): 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: VALUE): 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: VALUE): 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: VALUE): VALUE -- 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: VALUE): VALUE -- 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: VALUE): 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: VALUE): 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 -- Conversion as_double: REAL_64 -- represent as a DOUBLE do Result := precise_out.to_double 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: VALUE) -- 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: VALUE) -- 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: VALUE -- 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: VALUE) -- 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: VALUE -- 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: VALUE -- 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 VALUE 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 VALUE -- 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: VALUE): BOOLEAN -- check whether other is equal to current or not require -- from ANY other_not_void: other /= Void local r1i, r2i, d1, d2: INTEGER_32 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me r1.aligndecimal (r2) r1.alignwhole (r2) Result := True from r1i := 1 r2i := 1 until r1i > r1.s_.count or r2i > r2.s_.count loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 /= d2 then Result := False r1i := r1.s_.count + 1 end r1i := r1i + 1 r2i := r2i + 1 end 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: VALUE): BOOLEAN -- whether current is less than other require -- from PART_COMPARABLE other_exists: other /= Void local r1i, r2i, d1, d2: INTEGER_32 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then Result := True elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then Result := False elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then r1.negate r2.negate Result := r2 < r1 else r1.aligndecimal (r2) r1.alignwhole (r2) Result := False from r1i := 1 r2i := 1 until r1i > r1.s_.count or r2i > r2.s_.count loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 < d2 then Result := True r1i := r1.s_.count + 1 elseif d1 > d2 then Result := False r1i := r1.s_.count + 1 end r1i := r1i + 1 r2i := r2i + 1 end end ensure then -- from COMPARABLE asymmetric: Result implies not (other < Current) end feature {NONE} -- constructors default_create -- create an empty MONEY_VALUE object (it's equivalent to 0) require -- from ANY True local empty: STRING_8 do divisiondecimalprecision_ := Default_precision empty := "0" s_ := empty.twin ensure then s_.is_equal ("0") end make_from_int (int: INTEGER_32) -- create a MONEY_VALUE object from integer m local ch: CHARACTER_8 m, n, d: INTEGER_32 neg: BOOLEAN do divisiondecimalprecision_ := Default_precision m := int if m = 0 then create s_.make_from_string ("0") else neg := m < 0 if neg then m := m * -1 end from n := m create s_.make_empty until n = 0 loop d := n \\ 10 n := n // 10 ch := tochar (d) s_.insert_character (ch, 1) end if neg then negate end normalize end end make_from_string (s: STRING_8) -- create a MONEY_VALUE object from string s require non_void: s /= Void non_empty: not s.is_empty has_correct_format: ensurevalid (s) do divisiondecimalprecision_ := Default_precision s_ := s.twin normalize end feature -- operations add (other: VALUE): VALUE -- adds current to other and returns a new object -- Was declared in VALUE as synonym of plus. local res, empty: STRING_8 carry, r1i, r2i, d1, d2, d: INTEGER_32 ch: CHARACTER_8 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r2 - r1).clone_me elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 - r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 + r2).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 + d2 if d >= 10 then carry := d // 10 d := d - 10 else carry := 0 end ch := tochar (d) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end if carry > 0 then res.insert_character (tochar (carry), 1) end create Result.make_from_string (res) end end divide (other: VALUE): VALUE -- divides current by other and returns a new object -- Was declared in VALUE as synonym of quotient. local r1, r2, tr, diff, x, sum, prod: VALUE r1dotpos, r2dotpos, r1precision, r2precision, r1numtrailingzeros, r2numtrailingzeros, scalefactor, i, startpos, r1i, d: INTEGER_32 s, res, empty: STRING_8 pad: detachable STRING_8 stoploop: BOOLEAN ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.is_equal (zero) then Result := zero elseif r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 / r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) r1precision := 0 r2precision := 0 if r1dotpos > 0 then r1precision := r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) r1.normalize end if r2dotpos > 0 then r2precision := r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) r2.normalize end pad := Void if r1precision > r2precision then pad := r2.s_ elseif r2precision > r1precision then pad := r1.s_ end if pad /= Void then create s.make_filled ('0', (r1precision - r2precision).abs) pad.append (s) end r1numtrailingzeros := 0 r2numtrailingzeros := 0 scalefactor := 0 from i := r1.s_.count until i < 1 loop if r1.s_.item (i) = '0' then r1numtrailingzeros := r1numtrailingzeros + 1 else i := 0 end i := i - 1 end from i := r2.s_.count until i < 1 loop if r2.s_.item (i) = '0' then r2numtrailingzeros := r2numtrailingzeros + 1 else i := 0 end i := i - 1 end if r1numtrailingzeros < r2numtrailingzeros then create s.make_filled ('0', r2numtrailingzeros - r1numtrailingzeros) r1.s_.append (s) scalefactor := scalefactor + r2numtrailingzeros - r1numtrailingzeros r1numtrailingzeros := r2numtrailingzeros end if r1numtrailingzeros - r2numtrailingzeros < divisiondecimalprecision_ + 1 then scalefactor := scalefactor + divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros) create s.make_filled ('0', divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros)) r1.s_.append (s) end if r1 < r2 then scalefactor := scalefactor + r2.s_.count - r1.s_.count + 1 create s.make_filled ('0', r2.s_.count - r1.s_.count + 1) r1.s_.append (s) end if r1.s_.count - r2.s_.count < divisiondecimalprecision_ then scalefactor := scalefactor + divisiondecimalprecision_ - (r1.s_.count - r2.s_.count) create s.make_filled ('0', divisiondecimalprecision_ - (r1.s_.count - r2.s_.count)) r1.s_.append (s) end startpos := r2.s_.count + 1 if r1.s_.count > r2.s_.count then from stoploop := False until startpos > r1.s_.count or stoploop loop s := r1.s_.substring (1, startpos - 1) create tr.make_from_string (s) if tr >= r2 then stoploop := True else startpos := startpos + 1 end end end empty := "" res := empty.twin create diff from r1i := startpos - 1 until r1i > r1.s_.count loop if r1i = startpos - 1 then create x.make_from_string (r1.s_.substring (1, r1i)) else diff.s_.insert_character (r1.s_.item (r1i), diff.s_.count + 1) create x x := diff.clone_me end d := 0 create sum from stoploop := False until stoploop loop sum := sum + r2 if sum > x then stoploop := True else d := d + 1 end end ch := tochar (d) res.insert_character (ch, res.count + 1) create prod create s.make_filled (ch, 1) create tr.make_from_string (s) prod := tr * r2 diff := x - prod r1i := r1i + 1 end if scalefactor > 0 then if scalefactor > res.count then create s.make_filled ('0', scalefactor - res.count) res.insert_string (s, 1) end res.insert_character ('.', res.count - scalefactor + 1) end create Result.make_from_string (res) end end divisible (other: VALUE): BOOLEAN -- may current object be divided by other? require -- from NUMERIC other_exists: other /= Void do Result := not other.is_equal (zero) end exponentiable (other: NUMERIC): BOOLEAN -- may current object be elevated to the power other? require -- from NUMERIC other_exists: other /= Void do Result := False end identity alias "+": VALUE -- unary plus do create Result.make_from_string (s_) ensure -- from NUMERIC result_exists: Result /= Void end is_integer: BOOLEAN do Result := s_.index_of ('.', 1) = 0 end is_natural: BOOLEAN do Result := is_integer and Current >= zero end is_natural1: BOOLEAN do Result := is_integer and Current >= one end minus alias "-" (other: VALUE): VALUE -- subtracts other from current and returns a new object -- Was declared in VALUE as synonym of subtract. require -- from NUMERIC other_exists: other /= Void local res, empty: STRING_8 r1, r2: VALUE r1i, r2i, d1, d2, dp, i, j: INTEGER_32 ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 + r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me else if r1 < r2 then Result := (r2 - r1).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 < d2 then from i := r1i - 1 until i < 1 loop if r1.s_.item (i) /= '.' then dp := todigit (r1.s_.item (i)) if dp > 0 then r1.s_.put (tochar (dp - 1), i) from j := i + 1 until j > r1i - 1 loop if r1.s_.item (j) /= '.' then r1.s_.put (tochar (todigit (r1.s_.item (j)) + 9), j) end j := j + 1 end d1 := d1 + 10 i := 0 end end i := i - 1 end end ch := tochar (d1 - d2) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end create Result.make_from_string (res) end end ensure -- from NUMERIC result_exists: Result /= Void end multiply (other: VALUE): VALUE -- multiplies current by other and returns a new object -- Was declared in VALUE as synonym of product. local r1, r2, tr1, tr2, tr: VALUE r1dotpos, r2dotpos, precision, r1i, r2i, carry, d1, d2, d, diff, i: INTEGER_32 res, cres, tmp, empty: STRING_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 * r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) precision := 0 if r1dotpos > 0 then precision := precision + r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) end if r2dotpos > 0 then precision := precision + r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) end empty := "" res := empty.twin from r2i := r2.s_.count until r2i < 1 loop cres := empty.twin carry := 0 from r1i := r1.s_.count until r1i < 1 loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 * d2 if d >= 10 then carry := d // 10 d := d \\ 10 else carry := 0 end cres.insert_character (tochar (d), 1) r1i := r1i - 1 end if carry > 0 then cres.insert_character (tochar (carry), 1) end if res.count = 0 then res := cres.twin else create tmp.make_filled ('0', r2.s_.count - r2i) cres.append (tmp) create tr1.make_from_string (cres) create tr2.make_from_string (res) tr := tr1 + tr2 res := tr.s_.twin end r2i := r2i - 1 end if precision > 0 then if precision > res.count then diff := precision - res.count from i := 1 until i > diff loop res.insert_character ('0', 1) i := i + 1 end end res.insert_character ('.', res.count - precision + 1) end create Result.make_from_string (res) end end one: VALUE -- neutral element for "*" and "/" do create Result.make_from_string ("1") ensure -- from NUMERIC result_exists: Result /= Void end opposite alias "-": VALUE -- unary minus do create Result.make_from_string (s_) Result.negate ensure -- from NUMERIC result_exists: Result /= Void end plus alias "+" (other: VALUE): VALUE -- adds current to other and returns a new object -- Was declared in VALUE as synonym of add. require -- from NUMERIC other_exists: other /= Void local res, empty: STRING_8 carry, r1i, r2i, d1, d2, d: INTEGER_32 ch: CHARACTER_8 r1, r2: VALUE do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r2 - r1).clone_me elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 - r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 + r2).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 + d2 if d >= 10 then carry := d // 10 d := d - 10 else carry := 0 end ch := tochar (d) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end if carry > 0 then res.insert_character (tochar (carry), 1) end create Result.make_from_string (res) end ensure -- from NUMERIC result_exists: Result /= Void commutative: Result ~ (other + Current) end product alias "*" (other: VALUE): VALUE -- multiplies current by other and returns a new object -- Was declared in VALUE as synonym of multiply. require -- from NUMERIC other_exists: other /= Void local r1, r2, tr1, tr2, tr: VALUE r1dotpos, r2dotpos, precision, r1i, r2i, carry, d1, d2, d, diff, i: INTEGER_32 res, cres, tmp, empty: STRING_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 * r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 * r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) precision := 0 if r1dotpos > 0 then precision := precision + r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) end if r2dotpos > 0 then precision := precision + r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) end empty := "" res := empty.twin from r2i := r2.s_.count until r2i < 1 loop cres := empty.twin carry := 0 from r1i := r1.s_.count until r1i < 1 loop d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) d := carry + d1 * d2 if d >= 10 then carry := d // 10 d := d \\ 10 else carry := 0 end cres.insert_character (tochar (d), 1) r1i := r1i - 1 end if carry > 0 then cres.insert_character (tochar (carry), 1) end if res.count = 0 then res := cres.twin else create tmp.make_filled ('0', r2.s_.count - r2i) cres.append (tmp) create tr1.make_from_string (cres) create tr2.make_from_string (res) tr := tr1 + tr2 res := tr.s_.twin end r2i := r2i - 1 end if precision > 0 then if precision > res.count then diff := precision - res.count from i := 1 until i > diff loop res.insert_character ('0', 1) i := i + 1 end end res.insert_character ('.', res.count - precision + 1) end create Result.make_from_string (res) end ensure -- from NUMERIC result_exists: Result /= Void end quotient alias "/" (other: VALUE): VALUE -- divides current by other and returns a new object -- Was declared in VALUE as synonym of divide. require -- from NUMERIC other_exists: other /= Void good_divisor: divisible (other) local r1, r2, tr, diff, x, sum, prod: VALUE r1dotpos, r2dotpos, r1precision, r2precision, r1numtrailingzeros, r2numtrailingzeros, scalefactor, i, startpos, r1i, d: INTEGER_32 s, res, empty: STRING_8 pad: detachable STRING_8 stoploop: BOOLEAN ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.is_equal (zero) then Result := zero elseif r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 / r2).clone_me Result.negate elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r1.negate r2.negate Result := (r1 / r2).clone_me else r1dotpos := r1.s_.index_of ('.', 1) r2dotpos := r2.s_.index_of ('.', 1) r1precision := 0 r2precision := 0 if r1dotpos > 0 then r1precision := r1.s_.count - r1dotpos r1.s_.remove (r1dotpos) r1.normalize end if r2dotpos > 0 then r2precision := r2.s_.count - r2dotpos r2.s_.remove (r2dotpos) r2.normalize end pad := Void if r1precision > r2precision then pad := r2.s_ elseif r2precision > r1precision then pad := r1.s_ end if pad /= Void then create s.make_filled ('0', (r1precision - r2precision).abs) pad.append (s) end r1numtrailingzeros := 0 r2numtrailingzeros := 0 scalefactor := 0 from i := r1.s_.count until i < 1 loop if r1.s_.item (i) = '0' then r1numtrailingzeros := r1numtrailingzeros + 1 else i := 0 end i := i - 1 end from i := r2.s_.count until i < 1 loop if r2.s_.item (i) = '0' then r2numtrailingzeros := r2numtrailingzeros + 1 else i := 0 end i := i - 1 end if r1numtrailingzeros < r2numtrailingzeros then create s.make_filled ('0', r2numtrailingzeros - r1numtrailingzeros) r1.s_.append (s) scalefactor := scalefactor + r2numtrailingzeros - r1numtrailingzeros r1numtrailingzeros := r2numtrailingzeros end if r1numtrailingzeros - r2numtrailingzeros < divisiondecimalprecision_ + 1 then scalefactor := scalefactor + divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros) create s.make_filled ('0', divisiondecimalprecision_ + 1 - (r1numtrailingzeros - r2numtrailingzeros)) r1.s_.append (s) end if r1 < r2 then scalefactor := scalefactor + r2.s_.count - r1.s_.count + 1 create s.make_filled ('0', r2.s_.count - r1.s_.count + 1) r1.s_.append (s) end if r1.s_.count - r2.s_.count < divisiondecimalprecision_ then scalefactor := scalefactor + divisiondecimalprecision_ - (r1.s_.count - r2.s_.count) create s.make_filled ('0', divisiondecimalprecision_ - (r1.s_.count - r2.s_.count)) r1.s_.append (s) end startpos := r2.s_.count + 1 if r1.s_.count > r2.s_.count then from stoploop := False until startpos > r1.s_.count or stoploop loop s := r1.s_.substring (1, startpos - 1) create tr.make_from_string (s) if tr >= r2 then stoploop := True else startpos := startpos + 1 end end end empty := "" res := empty.twin create diff from r1i := startpos - 1 until r1i > r1.s_.count loop if r1i = startpos - 1 then create x.make_from_string (r1.s_.substring (1, r1i)) else diff.s_.insert_character (r1.s_.item (r1i), diff.s_.count + 1) create x x := diff.clone_me end d := 0 create sum from stoploop := False until stoploop loop sum := sum + r2 if sum > x then stoploop := True else d := d + 1 end end ch := tochar (d) res.insert_character (ch, res.count + 1) create prod create s.make_filled (ch, 1) create tr.make_from_string (s) prod := tr * r2 diff := x - prod r1i := r1i + 1 end if scalefactor > 0 then if scalefactor > res.count then create s.make_filled ('0', scalefactor - res.count) res.insert_string (s, 1) end res.insert_character ('.', res.count - scalefactor + 1) end create Result.make_from_string (res) end ensure -- from NUMERIC result_exists: Result /= Void end set_division_precision (i: INTEGER_32) -- sets divisionDecimalPrecision_ to 'i' require greater_than_zero: i > 0 do divisiondecimalprecision_ := i end subtract (other: VALUE): VALUE -- subtracts other from current and returns a new object -- Was declared in VALUE as synonym of minus. local res, empty: STRING_8 r1, r2: VALUE r1i, r2i, d1, d2, dp, i, j: INTEGER_32 ch: CHARACTER_8 do r1 := Current.clone_me r2 := other.clone_me if r1.s_.item (1) = '-' and r2.s_.item (1) /= '-' then create Result r1.negate Result := (r1 + r2).clone_me Result.negate elseif r1.s_.item (1) /= '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me elseif r1.s_.item (1) = '-' and r2.s_.item (1) = '-' then create Result r2.negate Result := (r1 + r2).clone_me else if r1 < r2 then Result := (r2 - r1).clone_me Result.negate else r1.aligndecimal (r2) r1.alignwhole (r2) empty := "" res := empty.twin from r1i := r1.s_.count r2i := r2.s_.count until r1i < 1 or r2i < 1 loop ch := '0' if r1.s_.item (r1i) /= '.' and r2.s_.item (r2i) /= '.' then d1 := todigit (r1.s_.item (r1i)) d2 := todigit (r2.s_.item (r2i)) if d1 < d2 then from i := r1i - 1 until i < 1 loop if r1.s_.item (i) /= '.' then dp := todigit (r1.s_.item (i)) if dp > 0 then r1.s_.put (tochar (dp - 1), i) from j := i + 1 until j > r1i - 1 loop if r1.s_.item (j) /= '.' then r1.s_.put (tochar (todigit (r1.s_.item (j)) + 9), j) end j := j + 1 end d1 := d1 + 10 i := 0 end end i := i - 1 end end ch := tochar (d1 - d2) elseif r1.s_.item (r1i) = '.' and r2.s_.item (r2i) = '.' then ch := '.' end res.insert_character (ch, 1) r1i := r1i - 1 r2i := r2i - 1 end create Result.make_from_string (res) end end end zero: VALUE -- neutral element for "+" and "-" do create Result.make_from_string ("0") ensure -- from NUMERIC result_exists: Result /= Void end feature -- printing out: STRING_8 -- return a representation of the number rounded to two decimal places require -- from DEBUG_OUTPUT True require -- from ANY True local myclone: VALUE precision, missingzeros, idxdot, rounddigits: INTEGER_32 pad: STRING_8 do myclone := Current.clone_me rounddigits := 2 myclone.round (rounddigits) idxdot := myclone.s_.index_of ('.', 1) if idxdot = 0 then myclone.s_.append_character ('.') create pad.make_filled ('0', rounddigits) myclone.s_.append_string (pad) else precision := myclone.s_.count - idxdot missingzeros := rounddigits - precision if missingzeros > 0 then create pad.make_filled ('0', missingzeros) myclone.s_.append_string (pad) end end Result := myclone.s_.twin ensure -- from DEBUG_OUTPUT result_not_void: Result /= Void ensure -- from ANY out_not_void: Result /= Void end precise_out: STRING_8 -- what is the string representation of VALUE_IMP do Result := s_.twin end feature {VALUE} -- private fields clone_me: VALUE local l_s: STRING_8 do create l_s.make_from_string (s_) create Result.make (l_s, divisiondecimalprecision_) end Default_precision: INTEGER_32 = 35 divisiondecimalprecision_: INTEGER_32 -- the minimum accuracy of division results; division results will be accurate to AT LEAST this many digits make (s: STRING_8; dv: INTEGER_32) -- create a MONEY_VALUE object from string s require non_void: s /= Void non_empty: not s.is_empty has_correct_format: ensurevalid (s) do divisiondecimalprecision_ := dv s_ := s end s_: STRING_8 feature {VALUE} -- private methods aligndecimal (other: VALUE) -- used to align the fractional parts of the given parameters local mydotpos, otherdotpos, myprec, otherprec, numtopad, i: INTEGER_32 pad: detachable STRING_8 do mydotpos := s_.index_of ('.', 1) otherdotpos := other.s_.index_of ('.', 1) if mydotpos /= 0 and otherdotpos /= 0 then myprec := s_.count - mydotpos otherprec := other.s_.count - otherdotpos if myprec /= otherprec then pad := Void if myprec < otherprec then pad := s_ else pad := other.s_ end numtopad := (myprec - otherprec).abs from i := 1 until i > numtopad loop pad.append_character ('0') i := i + 1 end end elseif mydotpos /= 0 or otherdotpos /= 0 then if mydotpos /= 0 then myprec := s_.count - mydotpos else myprec := 0 end if otherdotpos /= 0 then otherprec := other.s_.count - otherdotpos else otherprec := 0 end pad := Void if myprec < otherprec then pad := s_ else pad := other.s_ end numtopad := (myprec - otherprec).abs pad.append_character ('.') from i := 1 until i > numtopad loop pad.append_character ('0') i := i + 1 end end end alignwhole (other: VALUE) -- used to align the integer parts of the given parameters local mydotpos, otherdotpos, mywholelength, otherwholelength, menegoffset, othernegoffset, inspos, numtopad, i: INTEGER_32 meneg, otherneg: BOOLEAN pad: detachable STRING_8 do mydotpos := s_.index_of ('.', 1) otherdotpos := other.s_.index_of ('.', 1) meneg := s_.item (1) = '-' otherneg := other.s_.item (1) = '-' if mydotpos /= 0 then if meneg then menegoffset := 1 else menegoffset := 0 end mywholelength := mydotpos - menegoffset - 1 else mywholelength := s_.count end if otherdotpos /= 0 then if otherneg then othernegoffset := 1 else othernegoffset := 0 end otherwholelength := otherdotpos - othernegoffset - 1 else otherwholelength := other.s_.count end if mywholelength /= otherwholelength then pad := Void if mywholelength < otherwholelength then pad := s_ else pad := other.s_ end inspos := 1 if pad.item (1) = '-' then inspos := inspos + 1 end numtopad := (mywholelength - otherwholelength).abs from i := 1 until i > numtopad loop pad.insert_character ('0', inspos) i := i + 1 end end end 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) = '.' 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 normalize -- standardizes the format of the real number require non_empty: not s_.is_empty local i, inspos, dotpos, firstnonzeropos, lastnonzeropos: INTEGER_32 seenminus, prependzero, hasdot: BOOLEAN whole, fraction, empty, tmp, zerostr: STRING_8 do empty := "" zerostr := "0" seenminus := False prependzero := False from i := 1 until i > s_.count loop if s_.item (i) = '-' then seenminus := True elseif s_.item (i) = '.' then if i = 1 or seenminus then prependzero := True end end i := i + 1 end if prependzero then inspos := 1 if seenminus then inspos := 2 end s_.insert_character ('0', inspos) end if (seenminus) then negate end dotpos := s_.index_of ('.', 1) if dotpos = 0 then hasdot := False whole := s_.twin fraction := empty.twin else hasdot := True tmp := s_.substring (1, dotpos - 1) whole := tmp.twin tmp := s_.substring (dotpos + 1, s_.count) fraction := tmp.twin end if not whole.is_empty then firstnonzeropos := whole.count + 1 from i := 1 until i > whole.count loop if whole.item (i) /= '0' then firstnonzeropos := i i := whole.count + 1 end i := i + 1 end if firstnonzeropos = whole.count + 1 then whole := zerostr.twin else tmp := whole.substring (firstnonzeropos, whole.count) whole := tmp.twin end end if not fraction.is_empty then lastnonzeropos := -1 from i := fraction.count until i < 1 loop if fraction.item (i) /= '0' then lastnonzeropos := i i := 0 end i := i - 1 end if lastnonzeropos = -1 then fraction := empty.twin else tmp := fraction.substring (1, lastnonzeropos) fraction := tmp.twin end end s_ := whole.twin if hasdot and not fraction.is_empty then s_.append (".") s_.append (fraction) end if seenminus and not s_.is_equal ("0") then s_.insert_character ('-', 1) end end tochar (d: INTEGER_32): CHARACTER_8 -- converts a digit to a character local chzero: CHARACTER_8 do chzero := '0' Result := (chzero.code + d).to_character_8 end todigit (ch: CHARACTER_8): INTEGER_32 -- converts a character to a digit local chzero: CHARACTER_8 do chzero := '0' Result := ch.code - chzero.code end feature -- rounding absolute: VALUE do if Current >= Current.zero then Result := Current.clone_me else Result := opposite.clone_me end ensure Result >= Result.zero end Epsilon_singleton: VALUE_SINGLETON once create Result end is_imprecise_equal alias "|~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current = other else Result := (Current - other).absolute <= Epsilon_singleton.epsilon end end is_imprecise_equal_negation alias "|/~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current /= other else Result := not (Current |~ other) end end is_imprecise_greater alias "|>" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current > other else Result := Current > (other + Epsilon_singleton.epsilon) end end is_imprecise_greater_equal alias "|>~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current >= other else Result := (Current |~ other) or (Current > (other + Epsilon_singleton.epsilon)) end end is_imprecise_less alias "|<" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current < other else Result := Current < (other - Epsilon_singleton.epsilon) end end is_imprecise_less_equal alias "|<~" (other: VALUE): BOOLEAN do if Epsilon_singleton.epsilon = create {VALUE}.make_from_string ("0.0") then Result := Current <= other else Result := (Current |~ other) or (Current < (other - Epsilon_singleton.epsilon)) end end negate -- negates the number local tmp: STRING_8 do if s_.item (1) = '-' then tmp := s_.substring (2, s_.count) s_ := tmp.twin else s_.insert_character ('-', 1) end end precise_out_to (digits: INTEGER_32): STRING_8 -- returns the precise string representation to 'digits' require digits >= 0 local dotpos: INTEGER_32 do Result := precise_out dotpos := Result.index_of ('.', 1) if dotpos > 0 then Result := Result.substring (1, dotpos + digits) end end round (digits: INTEGER_32) -- rounds this instance to the specified number of digits require digits >= 0 local dotpos: INTEGER_32 neg: BOOLEAN s, tmp: STRING_8 x, y: VALUE do dotpos := s_.index_of ('.', 1) if dotpos > 0 then neg := False if s_.item (1) = '-' then negate neg := True end create s.make_filled ('0', digits) s.append ("5") s.insert_string ("0.", 1) create x.make_from_string (s) y := Current + x dotpos := y.s_.index_of ('.', 1) if dotpos /= 0 then tmp := y.s_.substring (1, dotpos + digits) else tmp := y.precise_out end s_ := tmp.twin if neg then negate end end normalize end round_to (digits: INTEGER_32): VALUE -- rounds the current VALUE_IMP to 'digits' -- symmetric rounding as in Excel http://support.microsoft.com/kb/196652 -- -2.6 is rounded to -3 require digits >= 0 do Result := Current.clone_me Result.round (digits) end set_epsilon (v: STRING_8) do Epsilon_singleton.set_epsilon (v) end invariant divisiondecimalprecision_ > 0 -- from COMPARABLE irreflexive_comparison: not (Current < Current) -- from ANY reflexive_equality: standard_is_equal (Current) reflexive_conformance: conforms_to (Current) end -- class VALUE
Generated by ISE EiffelStudio