Module: Sevgi::Function::Math

Included in:
Sevgi::Function
Defined in:
lib/sevgi/function/math.rb

Overview

Numeric and trigonometric methods promoted to Sevgi::F. This module owns the public thread-local Math.precision configuration but is not otherwise a consumer mixin contract.

Constant Summary collapse

PRECISION =

Default decimal precision used by approximate comparisons.

6

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.precisionInteger

Returns the current thread's default numeric precision.

Returns:

  • (Integer)

    current thread precision, or PRECISION when no override is set



19
20
21
22
23
# File 'lib/sevgi/function/math.rb', line 19

def self.precision
  precision = Thread.current.thread_variable_get(PRECISION_KEY)

  precision.nil? ? PRECISION : precision
end

.precision=(precision) ⇒ Integer?

Sets or clears the current thread's default numeric precision.

Parameters:

  • precision (Integer, nil)

    precision override, or nil to return to PRECISION

Returns:

  • (Integer, nil)

    assigned precision

Raises:



29
30
31
32
33
34
35
# File 'lib/sevgi/function/math.rb', line 29

def self.precision=(precision)
  unless precision.nil? || precision.is_a?(::Integer)
    ArgumentError.("Precision must be an Integer or nil: #{precision.inspect}")
  end

  Thread.current.thread_variable_set(PRECISION_KEY, precision)
end

Instance Method Details

#acos(value) ⇒ Float

Returns the inverse cosine in degrees.

Parameters:

  • value (Numeric)

    cosine value

Returns:

  • (Float)

Raises:

  • (Sevgi::ArgumentError)

    when value is not a finite real number

  • (::Math::DomainError)

    when value is outside -1..1



42
# File 'lib/sevgi/function/math.rb', line 42

def acos(value) = to_degrees(::Math.acos(finite_real(:value, value)))

#acot(value) ⇒ Float

Returns the inverse cotangent in degrees.

Parameters:

  • value (Numeric)

    cotangent value

Returns:

  • (Float)

Raises:



48
# File 'lib/sevgi/function/math.rb', line 48

def acot(value) = 90.0 - to_degrees(::Math.atan(finite_real(:value, value)))

#approx(float, precision = nil) ⇒ Numeric

Rounds a float with an explicit or thread-local precision.

Parameters:

  • float (Numeric)

    value to round

  • precision (Integer, nil) (defaults to: nil)

    explicit precision, or nil to use precision

Returns:

  • (Numeric)

    rounded value

Raises:



55
56
57
58
# File 'lib/sevgi/function/math.rb', line 55

def approx(float, precision = nil)
  precision = precision.nil? ? Function::Math.precision : valid_precision(precision)
  valid_real(:value, float).round(precision)
end

#asin(value) ⇒ Float

Returns the inverse sine in degrees.

Parameters:

  • value (Numeric)

    sine value

Returns:

  • (Float)

Raises:

  • (Sevgi::ArgumentError)

    when value is not a finite real number

  • (::Math::DomainError)

    when value is outside -1..1



65
# File 'lib/sevgi/function/math.rb', line 65

def asin(value) = to_degrees(::Math.asin(finite_real(:value, value)))

#atan(value) ⇒ Float

Returns the inverse tangent in degrees.

Parameters:

  • value (Numeric)

    tangent value

Returns:

  • (Float)

Raises:



71
# File 'lib/sevgi/function/math.rb', line 71

def atan(value) = to_degrees(::Math.atan(finite_real(:value, value)))

#atan2(y, x) ⇒ Float

Returns the quadrant-aware inverse tangent in degrees.

Parameters:

  • y (Numeric)

    y component

  • x (Numeric)

    x component

Returns:

  • (Float)

Raises:



78
# File 'lib/sevgi/function/math.rb', line 78

def atan2(y, x) = to_degrees(::Math.atan2(finite_real(:y, y), finite_real(:x, x)))

#cos(degrees) ⇒ Float

Returns the cosine of an angle expressed in degrees. Integer quarter turns return exact -1.0, 0.0, or 1.0; other angles use Ruby's floating-point Math implementation.

Parameters:

  • degrees (Numeric)

    angle in degrees

Returns:

  • (Float)

Raises:



85
86
87
88
# File 'lib/sevgi/function/math.rb', line 85

def cos(degrees)
  degrees = finite_real(:degrees, degrees)
  quadrant_value(degrees, QUADRANT_COSINES) { ::Math.cos(radians(degrees)) }
end

#cot(degrees) ⇒ Float

Returns the cotangent of an angle expressed in degrees.

Parameters:

  • degrees (Numeric)

    angle in degrees

Returns:

  • (Float)

Raises:



94
# File 'lib/sevgi/function/math.rb', line 94

def cot(degrees) = 1.0 / ::Math.tan(to_radians(degrees))

#count(length, division) ⇒ Integer

Counts complete divisions in a length.

Parameters:

  • length (Numeric)

    finite real total length

  • division (Numeric)

    finite real, non-zero division size

Returns:

  • (Integer)

Raises:



101
102
103
104
105
106
107
# File 'lib/sevgi/function/math.rb', line 101

def count(length, division)
  length = finite_real(:length, length)
  divisor = finite_real(:division, division)
  ArgumentError.("Division must not be zero") if divisor.zero?

  (length / divisor).to_i
end

#eq?(left, right, precision: nil) ⇒ Boolean

Compares two numeric values after approximate rounding.

Parameters:

  • left (Numeric)

    left operand

  • right (Numeric)

    right operand

  • precision (Integer, nil) (defaults to: nil)

    explicit precision, or nil to use precision

Returns:

  • (Boolean)

Raises:



115
# File 'lib/sevgi/function/math.rb', line 115

def eq?(left, right, precision: nil) = approx(left, precision) == approx(right, precision)

#ge?(left, right, precision: nil) ⇒ Boolean

Checks whether the rounded left operand is greater than or equal to the rounded right operand.

Parameters:

  • left (Numeric)

    left operand

  • right (Numeric)

    right operand

  • precision (Integer, nil) (defaults to: nil)

    explicit precision, or nil to use precision

Returns:

  • (Boolean)

Raises:



123
# File 'lib/sevgi/function/math.rb', line 123

def ge?(left, right, precision: nil) = approx(left, precision) >= approx(right, precision)

#gt?(left, right, precision: nil) ⇒ Boolean

Checks whether the rounded left operand is greater than the rounded right operand.

Parameters:

  • left (Numeric)

    left operand

  • right (Numeric)

    right operand

  • precision (Integer, nil) (defaults to: nil)

    explicit precision, or nil to use precision

Returns:

  • (Boolean)

Raises:



131
# File 'lib/sevgi/function/math.rb', line 131

def gt?(left, right, precision: nil) = approx(left, precision) > approx(right, precision)

#le?(left, right, precision: nil) ⇒ Boolean

Checks whether the rounded left operand is less than or equal to the rounded right operand.

Parameters:

  • left (Numeric)

    left operand

  • right (Numeric)

    right operand

  • precision (Integer, nil) (defaults to: nil)

    explicit precision, or nil to use precision

Returns:

  • (Boolean)

Raises:



139
# File 'lib/sevgi/function/math.rb', line 139

def le?(left, right, precision: nil) = approx(left, precision) <= approx(right, precision)

#lt?(left, right, precision: nil) ⇒ Boolean

Checks whether the rounded left operand is less than the rounded right operand.

Parameters:

  • left (Numeric)

    left operand

  • right (Numeric)

    right operand

  • precision (Integer, nil) (defaults to: nil)

    explicit precision, or nil to use precision

Returns:

  • (Boolean)

Raises:



147
# File 'lib/sevgi/function/math.rb', line 147

def lt?(left, right, precision: nil) = approx(left, precision) < approx(right, precision)

#round(float, precision) ⇒ Numeric

Rounds a value only when precision is present.

Parameters:

  • float (Numeric)

    value to round

  • precision (Integer, nil)

    explicit precision, or nil to return float unchanged

Returns:

  • (Numeric)

Raises:



154
155
156
157
# File 'lib/sevgi/function/math.rb', line 154

def round(float, precision)
  number = valid_real(:value, float)
  precision.nil? ? number : number.round(valid_precision(precision))
end

#sin(degrees) ⇒ Float

Returns the sine of an angle expressed in degrees. Integer quarter turns return exact -1.0, 0.0, or 1.0; other angles use Ruby's floating-point Math implementation.

Parameters:

  • degrees (Numeric)

    angle in degrees

Returns:

  • (Float)

Raises:



164
165
166
167
# File 'lib/sevgi/function/math.rb', line 164

def sin(degrees)
  degrees = finite_real(:degrees, degrees)
  quadrant_value(degrees, QUADRANT_SINES) { ::Math.sin(radians(degrees)) }
end

#tan(degrees) ⇒ Float

Returns the tangent of an angle expressed in degrees.

Parameters:

  • degrees (Numeric)

    angle in degrees

Returns:

  • (Float)

Raises:



173
# File 'lib/sevgi/function/math.rb', line 173

def tan(degrees) = ::Math.tan(to_radians(degrees))

#to_degrees(radians) ⇒ Float

Converts radians to degrees.

Parameters:

  • radians (Numeric)

    angle in radians

Returns:

  • (Float)

Raises:



179
# File 'lib/sevgi/function/math.rb', line 179

def to_degrees(radians) = finite_real(:radians, radians) * 180 / ::Math::PI

#to_radians(degrees) ⇒ Float

Converts degrees to radians.

Parameters:

  • degrees (Numeric)

    angle in degrees

Returns:

  • (Float)

Raises:



185
# File 'lib/sevgi/function/math.rb', line 185

def to_radians(degrees) = radians(finite_real(:degrees, degrees))

#with_precision(precision) { ... } ⇒ Object

Runs a block with a current-thread precision override.

Examples:

Compare values under a temporary precision

Sevgi::F.with_precision(2) { Sevgi::F.eq?(1.001, 1.0) } # => true
Sevgi::Function::Math.precision # => 6

Parameters:

  • precision (Integer, nil)

    scoped precision, or nil to use PRECISION

Yields:

  • block executed with the scoped precision

Yield Returns:

  • (Object)

Returns:

  • (Object)

    block return value

Raises:



197
198
199
200
201
202
203
204
205
# File 'lib/sevgi/function/math.rb', line 197

def with_precision(precision, &block)
  ArgumentError.("Block required") unless block

  previous = Thread.current.thread_variable_get(PRECISION_KEY)
  Function::Math.precision = precision
  block.call
ensure
  Function::Math.precision = previous if block
end

#zero?(value, precision: nil) ⇒ Boolean

Checks whether a value is approximately zero.

Parameters:

  • value (Numeric)

    value to check

  • precision (Integer, nil) (defaults to: nil)

    explicit precision, or nil to use precision

Returns:

  • (Boolean)

Raises:



212
# File 'lib/sevgi/function/math.rb', line 212

def zero?(value, precision: nil) = eq?(value, 0.0, precision:)