Class: GOBL::Num::Amount

Inherits:
Struct
  • Object
show all
Defined in:
lib/gobl/num/amount.rb

Overview

Represents a numeric quantity with optional decimal places that determine accuracy

Direct Known Subclasses

Percentage

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Struct

from_data, from_json!, #to_json

Constructor Details

#initialize(data) ⇒ Amount

Creates a new GOBL::Num::Amount from the given data object

Parameters:

  • data (Hash, String, #to_s, Amount)

    the data object. Supported types:

    • A ‘Hash` with a `:value` key and, optionally, an `:exp` one,

    • A ‘String` or any other object that can be coerced into one via `#to_s`,

    • Another GOBL::Num::Amount



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/gobl/num/amount.rb', line 20

def initialize(data)
  if data.is_a?(String)
    parse(data)
  elsif data.is_a?(Hash)
    @value = data[:value]
    @exp = data[:exp] || 0
  elsif data.is_a?(self.class)
    @value = data.value
    @exp = data.exp
  elsif data.respond_to?(:to_s)
    parse(data.to_s)
  else
    raise 'Unsupported input amount'
  end
end

Instance Attribute Details

#expObject (readonly)

The exponent, or number of significant figures of the amount



11
12
13
# File 'lib/gobl/num/amount.rb', line 11

def exp
  @exp
end

#valueObject (readonly)

The integer value of the amount



8
9
10
# File 'lib/gobl/num/amount.rb', line 8

def value
  @value
end

Class Method Details

.rescale_pair(a1, a2) ⇒ Array(Amount, Amount)

Rescales each GOBL::Num::Amount in the pair ensuring both have the same exponent

Parameters:

  • a1 (Amount)

    the first amount of the pair

  • a2 (Amount)

    the second amount of the pair

Returns:



64
65
66
67
68
# File 'lib/gobl/num/amount.rb', line 64

def self.rescale_pair(a1, a2)
  exp = a1.exp
  exp = a2.exp if a2.exp > exp
  [a1.rescale(exp), a2.rescale(exp)]
end

Instance Method Details

#==(other) ⇒ Boolean

Returns whether the current amount is equal to another one

Parameters:

Returns:

  • (Boolean)

    whether the two amounts are equal (‘true`) or not (`false`)



95
96
97
# File 'lib/gobl/num/amount.rb', line 95

def ==(other)
  compare(other).zero?
end

#add(a2) ⇒ Amount

Adds the current amount to a given one

Parameters:

  • a2 (Amount)

    the amount to add

Returns:

  • (Amount)

    the amount resulted from the operation



134
135
136
137
# File 'lib/gobl/num/amount.rb', line 134

def add(a2)
  a2 = a2.rescale(exp)
  self.class.new(value: value + a2.value, exp: exp)
end

#as_jsonObject

 @api private



191
192
193
# File 'lib/gobl/num/amount.rb', line 191

def as_json(*)
  to_s
end

#as_sString

Returns the string representation of the current amount

Returns:

  • (String)

    the string representation of the current amount



48
49
50
51
52
53
54
55
56
# File 'lib/gobl/num/amount.rb', line 48

def as_s
  return value.to_s if exp.zero?
  raise 'exponent too high' if exp > 100

  p = 10**exp
  v1, v2 = value.abs.divmod(p)
  sign = value.negative? ? '-' : ''
  format('%s%d.%0*d', sign, v1, exp, v2)
end

#compare(other) ⇒ Integer

Compares the current amount with another one

Parameters:

Returns:

  • (Integer)

    the result of the comparison:

    • ‘-1` if the current amount is lesser than the given one

    • ‘1` if current amount is greater than the given one

    • ‘0` if both amounts are equal



79
80
81
82
83
84
85
86
87
88
# File 'lib/gobl/num/amount.rb', line 79

def compare(other)
  a1, a2 = self.class.rescale_pair(self, other)
  if a1.value < a2.value
    -1
  elsif a1.value > a2.value
    1
  else
    0 # same
  end
end

#divide(a2) ⇒ Amount

Divides the current amount by the given one

Parameters:

  • a2 (Amount)

    the amount to divide by

Returns:

  • (Amount)

    the amount resulted from the operation



164
165
166
167
# File 'lib/gobl/num/amount.rb', line 164

def divide(a2)
  v = (value.to_f * (10**a2.exp)) / a2.value.to_f
  self.class.new(value: v.round, exp: exp)
end

#invertAmount

Inverts the sign of the current amount

Returns:

  • (Amount)

    the amount with the opposite sign



186
187
188
# File 'lib/gobl/num/amount.rb', line 186

def invert
  self.class.new(value: -value, exp: exp)
end

#multiply(a2) ⇒ Amount

Multiplies a given GOBL::Num::Amount with the current one

Parameters:

  • a2 (Amount)

    the amount to multiply with

Returns:

  • (Amount)

    the amount resulted from the operation



154
155
156
157
# File 'lib/gobl/num/amount.rb', line 154

def multiply(a2)
  v = (value * a2.value) / (10**a2.exp)
  self.class.new(value: v, exp: exp)
end

#rescale(e) ⇒ Amount

Changes the exponent of the GOBL::Num::Amount multipling or dividing its value as necessary. A lower exponent implies loosing accuracy.

Parameters:

  • e (Integer)

    the new exponent

Returns:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/gobl/num/amount.rb', line 105

def rescale(e)
  if exp > e
    # divide
    x = exp - e
    v = (value.to_f / (10**x)).round
    self.class.new(value: v, exp: e)
  elsif exp < e
    # multiply
    x = e - exp
    v = value * (10**x)
    self.class.new(value: v, exp: e)
  else
    # nothing
    self
  end
end

#split(x) ⇒ Array(Amount, Amount)

Splits the current amount into equal ‘x` parts providing a second amount with the remainder. This is like #divide, but will correctly account for rounding errors.

Parameters:

  • x (Integer)

    the number of parts to divide the amount into

Returns:

  • (Array(Amount, Amount))

    the split amount and the remider amount



176
177
178
179
180
181
# File 'lib/gobl/num/amount.rb', line 176

def split(x)
  a2 = divide(self.class.new(value: x, exp: 0))
  a3 = a2.multiply(self.class.new(value: x - 1, exp: 0))
  a3 = subtract(a3)
  [a2, a3]
end

#subtract(a2) ⇒ Amount

Subtracts a given GOBL::Num::Amount from the current one

Parameters:

  • a2 (Amount)

    the amount to subtract

Returns:

  • (Amount)

    the amount resulted from the operation



144
145
146
147
# File 'lib/gobl/num/amount.rb', line 144

def subtract(a2)
  a2 = a2.rescale(exp)
  self.class.new(value: value - a2.value, exp: exp)
end

#to_sString

Returns the string representation of the current amount

Returns:

  • (String)

    the string representation of the current amount

See Also:



41
42
43
# File 'lib/gobl/num/amount.rb', line 41

def to_s
  as_s
end

#zero?Boolean

Returns whether the current amount is equal to zero

Returns:

  • (Boolean)

    ‘true` if the current amount is equal to zero, `false`otherwise



125
126
127
# File 'lib/gobl/num/amount.rb', line 125

def zero?
  value.zero?
end