Skip to content

Module WhittakerTech::Midas::Coin::Arithmetic

Arithmetic defines the arithmetic and equality semantics of a Coin.

Design invariants

  • Immutable — all operations return new, frozen Coin objects.
  • Closed — operations always return a Coin, never a primitive.
  • Policy-aware only where unavoidable — division accepts a rounding policy; all other operations are exact.
  • Currency-strict — binary operations (+, -, ==) raise ArgumentError when the operands have different currency codes.

Convenience division helpers

For each key in WhittakerTech::Midas::ROUNDING_POLICIES a named shorthand is generated:

coin.divide_round(3)   # same as coin.divide(3, rounding_policy: :round)
coin.divide_ceil(3)
coin.divide_floor(3)
coin.divide_bankers(3)
  • @since 0.1.0

Public Instance Methods

%(other)

Returns the remainder after dividing by an integer.

  • @param other [Integer] the divisor
  • @raise [TypeError] if other is not an Integer
  • @raise [ZeroDivisionError] if other is zero
  • @return [Coin] a new, frozen Coin representing the remainder
  • @since 0.1.0

@example

Coin.value(1001, 'USD') % 3  # => Coin($0.02)  (1001 % 3 == 2 cents)

*(other)

Multiplies the Coin by an integer scalar.

  • @param other [Integer] the multiplier
  • @raise [TypeError] if other is not an Integer
  • @return [Coin] a new, frozen Coin
  • @since 0.1.0

@example

Coin.value(500, 'USD') * 3  # => Coin($15.00)

+(other)

Adds two Coins and returns the sum as a new Coin.

  • @param other [Coin] must have the same currency code as the receiver
  • @raise [TypeError] if other is not a Coin
  • @raise [ArgumentError] if the currencies do not match
  • @return [Coin] a new, frozen Coin
  • @since 0.1.0

@example

a = Coin.value(1000, 'USD')
b = Coin.value(500,  'USD')
a + b  # => Coin($15.00)

-(other)

Subtracts another Coin from the receiver and returns the difference.

  • @param other [Coin] must have the same currency code as the receiver
  • @raise [TypeError] if other is not a Coin
  • @raise [ArgumentError] if the currencies do not match
  • @return [Coin] a new, frozen Coin
  • @since 0.1.0

@example

a = Coin.value(1000, 'USD')
b = Coin.value(300,  'USD')
a - b  # => Coin($7.00)

-@()

Unary minus operator — shorthand for #negate.

  • @return [Coin]
  • @since 0.1.0

==(other)

Tests value equality with another Coin.

Two Coins are equal when they have the same currency code and the same minor-unit amount. A Coin is never equal to a non-Coin object.

  • @param other [Object] the object to compare
  • @return [Boolean]
  • @since 0.1.0

divide(divisor, rounding_policy: = WhittakerTech::Midas::DEFAULT_ROUNDING_POLICY)

Divides the Coin by a numeric divisor and returns the quotient.

Division is the only arithmetic operation that requires a rounding policy because dividing integer minor units can produce a non-integer result.

  • @param divisor [Numeric] the divisor; must be non-zero
  • @param rounding_policy [Symbol] one of the keys in WhittakerTech::Midas::ROUNDING_POLICIES (default: WhittakerTech::Midas::DEFAULT_ROUNDING_POLICY)

  • @raise [TypeError] if divisor is not Numeric

  • @raise [ZeroDivisionError] if divisor is zero
  • @return [Coin] a new, frozen Coin
  • @since 0.1.0

@example

Coin.value(1000, 'USD').divide(3, rounding_policy: :ceil)   # => Coin($3.34)
Coin.value(1000, 'USD').divide(3, rounding_policy: :floor)  # => Coin($3.33)
Coin.value(1000, 'USD').divide(3, rounding_policy: :bankers)# => Coin($3.33)

hash()

Returns a hash value consistent with #eql?.

Coins with the same currency code and minor-unit amount produce the same hash, making them interchangeable as Hash keys or Set members.

  • @return [Integer]
  • @since 0.1.0

negate()

Returns a new Coin with the sign reversed.

  • @return [Coin] a new, frozen Coin
  • @since 0.1.0

@example

Coin.value(500, 'USD').negate  # => Coin(-$5.00)