Class: VectorNumber

Inherits:
Object
  • Object
show all
Includes:
Comparable, Enumerable
Defined in:
lib/vector_number.rb,
lib/vector_number/mathing.rb,
lib/vector_number/version.rb,
lib/vector_number/querying.rb,
lib/vector_number/comparing.rb,
lib/vector_number/converting.rb,
lib/vector_number/enumerating.rb,
lib/vector_number/stringifying.rb,
lib/vector_number/math_converting.rb,
lib/vector_number/numeric_refinements.rb

Overview

VectorNumber provides a Numeric-like experience for doing arithmetics on heterogeneous objects, with more advanced operations based on real vector spaces available when needed.

VectorNumber inherits from Object and includes Enumerable and Comparable. It implements mostly the same interface as Numeric classes, but can #coerce any value. Its behavior follows Complex when possible.

All instances are frozen after creation.

Creation

Comparing

  • #==: test for equal value

  • #eql?: test for equal value and type

  • #<=>: compare real values

  • #hash: calculate hash value for use in Hashes

Querying

Unary math operations

Arithmetic operations

  • #coerce: convert any object to a VectorNumber

  • #+/#add: add object

  • #-/#sub: subtract object

  • #*/#mult: multiply (scale) by a real number

  • #//#quo: divide (scale) by a real number

  • #fdiv: divide (scale) by a real number, using fdiv

  • #div: perform integer division

  • #%/#modulo: return modulus from integer division

  • #divmod: combination of #div and #modulo

  • #remainder: return remainder from integer division

Rounding

  • #round: round each coefficient

  • #ceil: round each coefficient up towards +∞

  • #floor: round each coefficient down towards -∞

  • #truncate: truncate each coefficient towards 0

Type conversion

  • #real: return real part

  • #imag/#imaginary: return imaginary part

  • #to_i/#to_int: convert to Integer if possible

  • #to_f: convert to Float if possible

  • #to_r: convert to Rational if possible

  • #to_d: convert to BigDecimal if possible

  • #to_c: convert to Complex if possible

Hash-like operations

Miscellaneous methods

  • #size: number of non-zero coefficients

  • #options: hash of options

  • #dup/#+: return self

  • #clone: return self

  • #to_s: return string representation suitable for printing

  • #inspect: return string representation suitable for display

Defined Under Namespace

Modules: NumericRefinements

Constant Summary collapse

KNOWN_OPTIONS =

Keys for possible options. Unknown options will be rejected when creating a vector.

Returns:

  • (Array<Symbol>)

Since:

  • 0.2.0

i[mult].freeze
DEFAULT_OPTIONS =

Default values for options.

Returns:

  • (Hash{Symbol => Object})

Since:

  • 0.2.0

{ mult: :dot }.freeze
UNIT =

Get a unit for nth numeric dimension, where 1 is real, 2 is imaginary.

Since:

  • 0.2.0

->(n) { n }.freeze
R =

Constant for real unit.

Since:

  • 0.2.0

UNIT[1]
I =

Constant for imaginary unit.

Since:

  • 0.1.0

UNIT[2]
VERSION =

Returns:

  • (String)
"0.5.0"
MULT_STRINGS =

Predefined symbols for multiplication to display between unit and coefficient.

Returns:

  • (Hash{Symbol => String})

Since:

  • 0.1.0

{
  asterisk: "*", # U+002A
  cross: "×", # U+00D7
  dot: "⋅", # U+22C5
  invisible: "⁢", # U+2062, zero-width multiplication operator
  space: " ",
  none: "",
}.freeze

Instance Attribute Summary collapse

Creation collapse

Miscellaneous methods collapse

Mathematical operations collapse

Querying collapse

Comparing collapse

Converting to different numeric classes collapse

Hash-like operations collapse

Constructor Details

#initialize(values = nil, options = nil) {|coefficient| ... } ⇒ VectorNumber

Create new VectorNumber from values, possibly specifying options, possibly modifying coefficients with a block.

values can be:

  • an array of values (see []);

  • a VectorNumber to copy;

  • a hash in the format returned by #to_h;

  • nil to specify a 0-dimensional vector (same as an empty array or hash).

Using a hash as values is an advanced technique which allows to quickly construct a VectorNumber with desired units and coefficients, but it can also lead to unexpected results if care is not taken to provide only valid keys and values.

Examples:

VectorNumber.new(1, 2, 3) # ArgumentError
VectorNumber.new([1, 2, 3]) # => (6)
VectorNumber.new(["b", VectorNumber::I], mult: :asterisk) # => (1*'b' + 1i)
VectorNumber.new # => (0)

with a block

VectorNumber.new(["a", "b", "c", 3]) { _1 * 2 } # => (2⋅'a' + 2⋅'b' + 2⋅'c' + 6)
VectorNumber.new(["a", "b", "c", 3], &:-@) # => (-1⋅'a' - 1⋅'b' - 1⋅'c' - 3)
VectorNumber.new(["a", "b", "c", 3], &:digits) # RangeError

using hash for values

v = VectorNumber.new({1 => 15, "a" => 3.4, nil => -3}) # => (15 + 3.4⋅'a' - 3⋅)
v.to_h # => {1 => 15, "a" => 3.4, nil => -3}

Parameters:

  • values (Array, VectorNumber, Hash{Object => Numeric}, nil) (defaults to: nil)

    values for this vector

  • options (Hash{Symbol => Object}, nil) (defaults to: nil)

    options for this vector, if values is a VectorNumber or contains it, these will be merged with options from its options

Options Hash (options):

  • :mult (Symbol, String)

    Multiplication symbol, either a key from MULT_STRINGS or a literal string to use

Yield Parameters:

  • coefficient (Numeric)

    a real number

Yield Returns:

  • (Numeric)

    new coefficient

Raises:

  • (RangeError)

    if a block is used and it returns a non-number or non-real number



196
197
198
199
200
201
202
203
204
205
# File 'lib/vector_number.rb', line 196

def initialize(values = nil, options = nil, &transform)
  # @type var options: Hash[Symbol, Object]
  initialize_from(values)
  apply_transform(&transform)
  finalize_contents
  save_options(options, values)
  @options.freeze
  @data.freeze
  freeze
end

Instance Attribute Details

#optionsHash{Symbol => Object} (readonly)

Options used for this vector.

Returns:

  • (Hash{Symbol => Object})

See Also:

Since:

  • 0.1.0



157
158
159
# File 'lib/vector_number.rb', line 157

def options
  @options
end

#sizeInteger (readonly)

Number of non-zero dimensions.

Returns:

  • (Integer)

Since:

  • 0.1.0



148
149
150
# File 'lib/vector_number.rb', line 148

def size
  @size
end

Class Method Details

.[](*values, **options) ⇒ VectorNumber

Create new VectorNumber from a list of values, possibly specifying options.

Examples:

VectorNumber[1, 2, 3] # => (6)
VectorNumber[[1, 2, 3]] # => (1⋅[1, 2, 3])
VectorNumber["b", VectorNumber::I, mult: :asterisk] # => (1*'b' + 1i)
VectorNumber[] # => (0)
VectorNumber["b", VectorNumber["b"]] # => (2⋅'b')
VectorNumber["a", "b", "a"] # => (2⋅'a' + 1⋅'b')

Parameters:

  • values (Array<Object>)

    values to put in the number

  • options (Hash{Symbol => Object})

    options for the number

Options Hash (**options):

  • :mult (Symbol, String)

    Multiplication symbol, either a key from MULT_STRINGS or a literal string to use

Returns:

Since:

  • 0.1.0



138
139
140
# File 'lib/vector_number.rb', line 138

def self.[](*values, **options)
  new(values, options)
end

Instance Method Details

#%(other) ⇒ VectorNumber Also known as: modulo

Return the modulus of dividing self by a real other as a vector.

This is equal to self - other * (self/other).floor, or, alternatively, self - other * self.div(other).

Examples:

VectorNumber[10] % 3 # => (1)
VectorNumber["a", "b", 6].modulo(2) # => (1⋅'a' + 1⋅'b')
-VectorNumber["a"] % VectorNumber[2] # => (1⋅'a')
# Can't divide by a non-real:
VectorNumber["a"] % VectorNumber["b"] # RangeError

numeric types can be divided in reverse

3 % VectorNumber[10] # => (3)
# Can't divide by a non-real:
3 % VectorNumber["a"] # RangeError

compare to #remainder

VectorNumber[-5] % 3 # => (1)
VectorNumber[-5].remainder(3) # => (-2)

Parameters:

  • other (Integer, Float, Rational, BigDecimal, VectorNumber)

Returns:

Raises:

  • (RangeError)

    if other is not a number or is not a real number

  • (ZeroDivisionError)

    if other is zero

See Also:

Since:

  • 0.2.6



259
260
261
262
263
264
# File 'lib/vector_number/mathing.rb', line 259

def %(other)
  check_divisibility(other)

  other = other.real
  new { _1 % other }
end

#*(other) ⇒ VectorNumber Also known as: mult

Multiply all coefficients by a real other, returning new vector.

This effectively multiplies #magnitude by other.

Examples:

VectorNumber[5] * 2 # => (10)
VectorNumber["a", "b", 6].mult(2) # => (2⋅'a' + 2⋅'b' + 12)
VectorNumber["a"] * VectorNumber[2] # => (2⋅'a')
# Can't multiply by a non-real:
VectorNumber["a"] * VectorNumber["b"] # RangeError

numeric types can be multiplied in reverse

2 * VectorNumber[5] # => (10)
2 * VectorNumber["a"] # => (2⋅'a')

Parameters:

  • other (Integer, Float, Rational, BigDecimal, VectorNumber)

Returns:

Raises:

  • (RangeError)

    if other is not a number or other can’t be multiplied by this one

Since:

  • 0.2.1



115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/vector_number/mathing.rb', line 115

def *(other)
  if real_number?(other)
    other = other.real
    # @type var other: Float
    new { _1 * other }
  elsif real_number?(self) && other.is_a?(self.class)
    # @type var other: untyped
    other * self
  else
    raise RangeError, "can't multiply #{self} and #{other}"
  end
end

#+(other) ⇒ VectorNumber Also known as: add

Return new vector as a sum of this and other value. This is analogous to [].

Examples:

VectorNumber[5] + 10 # => (15)
VectorNumber["a"].add(VectorNumber["b"]) # => (1⋅'a' + 1⋅'b')
VectorNumber["a"] + "b" # => (1⋅'a' + 1⋅'b')

numeric types can be added in reverse

10 + VectorNumber[5] # => (15)
10 + VectorNumber["a"] # => (10 + 1⋅'a')

Parameters:

  • other (Object)

Returns:

Since:

  • 0.2.0



66
67
68
# File 'lib/vector_number/mathing.rb', line 66

def +(other)
  new([self, other])
end

#+@VectorNumber Also known as: dup

Return self.

Returns:

Since:

  • 0.2.0



214
215
# File 'lib/vector_number.rb', line 214

def +@ = self
# @since 0.2.4

#-(other) ⇒ VectorNumber Also known as: sub

Return new vector as a sum of this and additive inverse of other value.

This is implemented through #+ and #-@.

Examples:

VectorNumber[5] - 3 # => (2)
VectorNumber["a"].sub(VectorNumber["b"]) # => (1⋅'a' - 1⋅'b')
VectorNumber["a"] - "b" # => (1⋅'a' - 1⋅'b')

numeric types can be subtracted in reverse

3 - VectorNumber[5] # => (-2)
3 - VectorNumber["a"] # => (3 - 1⋅'a')

Parameters:

  • other (Object)

Returns:

Since:

  • 0.2.0



89
90
91
# File 'lib/vector_number/mathing.rb', line 89

def -(other)
  self + new([other], &:-@)
end

#-@VectorNumber Also known as: neg

Return new vector with negated coefficients (additive inverse).

Examples:

-VectorNumber[12, "i"] # => (-12 - 1⋅'i')
VectorNumber["a", "b", "a"].neg # => (-2⋅'a' - 1⋅'b')
-VectorNumber["a"] + VectorNumber["a"] # => (0)

Returns:

Since:

  • 0.2.0



44
45
46
# File 'lib/vector_number/mathing.rb', line 44

def -@
  new(&:-@)
end

#/(other) ⇒ VectorNumber Also known as: quo

Note:

This method never does integer division.

Divide all coefficients by a real other, returning new vector.

This effectively multiplies #magnitude by reciprocal of other.

Examples:

VectorNumber[10] / 2 # => (5)
VectorNumber["a", "b", 6].quo(2) # => (1/2⋅'a' + 1/2⋅'b' + 3/1)
VectorNumber["a"] / VectorNumber[2] # => (1/2⋅'a')
# Can't divide by a non-real:
VectorNumber["a"] / VectorNumber["b"] # RangeError

numeric types can be divided in reverse

2 / VectorNumber[10] # => (1/5)
# Can't divide by a non-real:
2 / VectorNumber["a"] # RangeError

Parameters:

  • other (Integer, Float, Rational, BigDecimal, VectorNumber)

Returns:

Raises:

  • (RangeError)

    if other is not a number or is not a real number

  • (ZeroDivisionError)

    if other is zero

Since:

  • 0.2.1



153
154
155
156
157
158
159
160
161
# File 'lib/vector_number/mathing.rb', line 153

def /(other)
  check_divisibility(other)

  other = other.real
  # Prevent integer division, but without loss of accuracy.
  other = Rational(other) if other.integer?
  # @type var other: Float
  new { _1 / other }
end

#<=>(other) ⇒ Integer?

Compare to other and return -1, 0, or 1 if self is less than, equal, or larger than other on real number line, or nil if any or both values are non-real.

Most VectorNumbers are non-real and therefore not comparable with this method.

Examples:

VectorNumber[130] <=> 12 # => 1
1 <=> VectorNumber[13] # => -1
VectorNumber[12.1] <=> Complex(12.1, 0) # => 0
# This doesn't work as expected without NumericRefinements:
Complex(12.1, 0) <=> VectorNumber[12.1] # => nil

# Any non-real comparison returns nil:
VectorNumber[12.1] <=> Complex(12.1, 1) # => nil
VectorNumber[12.1i] <=> 2 # => nil
VectorNumber["a"] <=> 2 # => nil

Parameters:

  • other (Object)

Returns:

  • (Integer)
  • (nil)

    if self or other isn’t a real number.

See Also:

Since:

  • 0.2.0



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/vector_number/comparing.rb', line 116

def <=>(other)
  return nil unless numeric?(1)

  case other
  when VectorNumber
    other.numeric?(1) ? real <=> other.real : nil
  when Numeric
    other.imaginary.zero? ? real <=> other.real : nil
  else
    nil
  end
end

#==(other) ⇒ Boolean

Test whether other has the same value with == semantics.

Values are considered equal if

  • other is a VectorNumber and it has the same units and equal coefficients, or

  • other is a Numeric equal in value to this (real or complex) number.

Examples:

VectorNumber[3.13] == 3.13 # => true
VectorNumber[1.4, 1.5i] == Complex(1.4, 1.5) # => true
VectorNumber["a", "b", "c"] == VectorNumber["c", "b", "a"] # => true
VectorNumber["a", 14] == 14 # => false
VectorNumber["a"] == "a" # => false

Parameters:

  • other (Object)

Returns:

  • (Boolean)

Since:

  • 0.2.0



32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/vector_number/comparing.rb', line 32

def ==(other)
  return true if equal?(other)

  case other
  when VectorNumber
    size == other.size && @data == other.to_h
  when Numeric
    numeric?(2) && other.real == real && other.imaginary == imaginary
  else
    # Can't compare a number-like value to a non-number.
    false
  end
end

#[](unit) ⇒ Numeric

Get the coefficient for the unit.

If the unit?(unit) is false, 0 is returned. Note that units for real and imaginary parts are VectorNumber::R and VectorNumber::I respectively.

Examples:

VectorNumber["a", "b", 6]["a"] # => 1
VectorNumber["a", "b", 6]["c"] # => 0

Parameters:

  • unit (Object)

Returns:

  • (Numeric)

Since:

  • 0.2.4



111
# File 'lib/vector_number/enumerating.rb', line 111

def [](unit) = @data[unit]

#absFloat Also known as: magnitude

Calculate the absolute value of the vector (its length).

Examples:

VectorNumber[5.3].abs # => 5.3
VectorNumber[-5.3i].magnitude # => 5.3
VectorNumber[-5.3i, "i"].abs # => 5.3935146240647205

Returns:

  • (Float)

Since:

  • 0.2.2



16
17
18
# File 'lib/vector_number/math_converting.rb', line 16

def abs
  Math.sqrt(abs2)
end

#abs2Float

Calculate the square of absolute value.

Examples:

VectorNumber[5.3].abs2 # => 5.3
VectorNumber[-5.3i].abs2 # => 5.3
VectorNumber[-5.3i, "i"].abs2 # => 29.09

Returns:

  • (Float)

Since:

  • 0.2.2



32
33
34
# File 'lib/vector_number/math_converting.rb', line 32

def abs2
  coefficients.sum(&:abs2)
end

#ceil(digits = 0) ⇒ VectorNumber

Return a new vector with every coefficient rounded using their #ceil.

Examples:

VectorNumber[5.39].ceil # => (6)
VectorNumber[-5.35i].ceil # => (-5i)
VectorNumber[-5.35i, "i"].ceil # => (-5i + 1⋅'i')
VectorNumber[-5.35i, "i"].ceil(1) # => (-5.3i + 1⋅'i')
VectorNumber[-5.35i, "i"].ceil(-1) # => (10⋅'i')

Parameters:

  • digits (Integer) (defaults to: 0)

Returns:

Since:

  • 0.2.2



66
67
68
# File 'lib/vector_number/math_converting.rb', line 66

def ceil(digits = 0)
  new { _1.ceil(digits) }
end

#clone(freeze: true) ⇒ VectorNumber

Return self.

Returns:

Raises:

  • (ArgumentError)

    if freeze is not true or nil.

Since:

  • 0.2.4



224
225
226
227
228
229
230
231
232
233
# File 'lib/vector_number.rb', line 224

def clone(freeze: true)
  case freeze
  when true, nil
    self
  when false
    raise ArgumentError, "can't unfreeze #{self.class}"
  else
    raise ArgumentError, "unexpected value for freeze: #{freeze.class}"
  end
end

#coefficientsArray<Numeric> Also known as: values

Get a list of non-zero coefficients.

Examples:

VectorNumber["a", "b", 6].coefficients # => [1, 1, 6]
VectorNumber.new.values # => []

Returns:

  • (Array<Numeric>)

Since:

  • 0.1.0



72
# File 'lib/vector_number/enumerating.rb', line 72

def coefficients = @data.values

#coerce(other) ⇒ Array(VectorNumber, VectorNumber)

The coerce method provides support for Ruby type coercion.

Unlike other numeric types, VectorNumber can coerce anything.

Examples:

VectorNumber["a"].coerce(5) # => [(5), (1⋅'a')]
VectorNumber[7].coerce([]) # => [(1⋅[]), (7)]
VectorNumber["a"] + 5 # => (1⋅'a' + 5)
# Direct reverse coercion doesn't work, but Numeric types know how to call #coerce:
5.coerce(VectorNumber["a"]) # RangeError
5 + VectorNumber["a"] # => (5 + 1⋅'a')

Parameters:

  • other (Object)

Returns:

Since:

  • 0.2.0



25
26
27
28
29
30
31
32
# File 'lib/vector_number/mathing.rb', line 25

def coerce(other)
  case other
  when VectorNumber
    [other, self]
  else
    [new([other]), self]
  end
end

#div(other) ⇒ VectorNumber

Divide all coefficients by a real other, converting results to integers using #floor.

This is requal to (self / other).floor.

Examples:

VectorNumber[10].div(3) # => (3)
VectorNumber["a"].div(2) # => (0⋅'a')
VectorNumber["a"].div(VectorNumber[2]) # => (0⋅'a')
# Can't divide by a non-real:
VectorNumber["a"].div(VectorNumber["b"]) # RangeError

numeric types can be divided in reverse

2.div(VectorNumber[10]) # => (0)
# Can't divide by a non-real:
2.div(VectorNumber["a"]) # RangeError

Parameters:

  • other (Integer, Float, Rational, BigDecimal, VectorNumber)

Returns:

Raises:

  • (RangeError)

    if other is not a number or is not a real number

  • (ZeroDivisionError)

    if other is zero

See Also:

Since:

  • 0.2.6



222
223
224
225
226
227
# File 'lib/vector_number/mathing.rb', line 222

def div(other)
  check_divisibility(other)

  other = other.real
  new { _1.div(other) }
end

#divmod(other) ⇒ Array(VectorNumber, VectorNumber)

Return the quotient and modulus of dividing self by a real other. There is no performance benefit compared to calling #div and #% separately.

Examples:

VectorNumber[10].divmod(3) # => [(3), (1)]
VectorNumber["a"].divmod(2) # => [(0⋅'a'), (1⋅'a')]
VectorNumber["a"].divmod(VectorNumber[2]) # => [(0⋅'a'), (1⋅'a')]
# Can't divide by a non-real:
VectorNumber["a"].divmod(VectorNumber["b"]) # RangeError

numeric types can be divided in reverse

3.divmod(VectorNumber[10]) # => [(0), (3)]
# Can't divide by a non-real:
3.divmod(VectorNumber["a"]) # RangeError

Parameters:

  • other (Integer, Float, Rational, BigDecimal, VectorNumber)

Returns:

Raises:

  • (RangeError)

    if other is not a number or is not a real number

  • (ZeroDivisionError)

    if other is zero

See Also:

Since:

  • 0.2.6



292
293
294
# File 'lib/vector_number/mathing.rb', line 292

def divmod(other)
  [div(other), modulo(other)]
end

#each {|unit, coefficient| ... } ⇒ VectorNumber #eachEnumerator Also known as: each_pair

Iterate through every pair of unit and coefficient. Returns Enumerator (with set size) if no block is given.

Examples:

v = VectorNumber["a", "b", 6]
units = []
v.each { |u, c| units << u unless u.is_a?(Numeric) } # => (1⋅'a' + 1⋅'b' + 6)
units # => ["a", "b"]

Enumerator

v.each.size # => 3
(v.each + [["d", 0]]).map(&:first) # => ["a", "b", 1, "d"]
v.each_pair.peek # => ["a", 1]

Overloads:

  • #each {|unit, coefficient| ... } ⇒ VectorNumber

    Returns self.

    Yield Parameters:

    • unit (Object)
    • coefficient (Numeric)

    Yield Returns:

    • (void)

    Returns:

  • #eachEnumerator

    Returns:

    • (Enumerator)

See Also:

  • Enumerable
  • Enumerator

Since:

  • 0.1.0



39
40
41
42
43
44
# File 'lib/vector_number/enumerating.rb', line 39

def each(&block)
  return to_enum { size } unless block_given?

  @data.each(&block)
  self
end

#eql?(other) ⇒ Boolean

Test whether other is VectorNumber and has the same value with eql? semantics.

Values are considered equal only if other is a VectorNumber and it has exactly the same units and coefficients, though possibly in a different order. Additionally, ‘a.eql?(b)` implies `a.hash == b.hash`.

Note that #options are not considered for equality.

Examples:

VectorNumber["a", "b", "c"].eql? VectorNumber["c", "b", "a"] # => true
VectorNumber[3.13].eql? 3.13 # => false
VectorNumber[1.4, 1.5i].eql? Complex(1.4, 1.5) # => false
VectorNumber["a", 14].eql? 14 # => false
VectorNumber["a"].eql? "a" # => false

Parameters:

  • other (Object)

Returns:

  • (Boolean)

Since:

  • 0.1.0



65
66
67
68
69
70
# File 'lib/vector_number/comparing.rb', line 65

def eql?(other)
  return true if equal?(other)
  return false unless other.is_a?(VectorNumber)

  size.eql?(other.size) && @data.eql?(other.to_h)
end

#fdiv(other) ⇒ VectorNumber

Divide all coefficients by a real other using fdiv, returning new vector with decimal coefficients.

There isn’t much benefit to this method, as #/ doesn’t do integer division, but it is provided for consistency.

Examples:

VectorNumber[10].fdiv(2) # => (5.0)
VectorNumber["a", "b", 6].fdiv(2) # => (0.5⋅'a' + 0.5⋅'b' + 3.0)
VectorNumber["a"].fdiv(VectorNumber[2]) # => (0.5⋅'a')
# Can't divide by a non-real:
VectorNumber["a"].fdiv(VectorNumber["b"]) # RangeError

reverse division may return non-vector results

2.fdiv(VectorNumber[10]) # => 0.2 (Float)
2.0.fdiv(VectorNumber[10]) # => (0.2) (VectorNumber)

Parameters:

  • other (Integer, Float, Rational, BigDecimal, VectorNumber)

Returns:

Raises:

  • (RangeError)

    if other is not a number or is not a real number

  • (ZeroDivisionError)

    if other is zero

Since:

  • 0.2.1



189
190
191
192
193
194
# File 'lib/vector_number/mathing.rb', line 189

def fdiv(other)
  check_divisibility(other)

  other = other.real
  new { _1.fdiv(other) }
end

#finite?Boolean

Returns true if all coefficients are finite, false otherwise.

Examples:

VectorNumber[2].finite? # => true
VectorNumber[Float::NAN].finite? # => false
VectorNumber["a"].mult(Float::INFINITY).finite? # => false

Returns:

  • (Boolean)

Since:

  • 0.1.0



55
56
57
# File 'lib/vector_number/querying.rb', line 55

def finite?
  all? { |_u, v| v.finite? }
end

#floor(digits = 0) ⇒ VectorNumber

Return a new vector with every coefficient rounded using their #floor.

Examples:

VectorNumber[5.39].floor # => (5)
VectorNumber[-5.35i].floor # => (-6i)
VectorNumber[-5.35i, "i"].floor # => (-6i + 1⋅'i')
VectorNumber[-5.35i, "i"].floor(1) # => (-5.4i + 1⋅'i')
VectorNumber[-5.35i, "i"].floor(-1) # => (-10i)

Parameters:

  • digits (Integer) (defaults to: 0)

Returns:

Since:

  • 0.2.2



83
84
85
# File 'lib/vector_number/math_converting.rb', line 83

def floor(digits = 0)
  new { _1.floor(digits) }
end

#hashInteger

Generate an Integer hash value for self.

Hash values are stable during runtime, but not between processes. Options are disregarded for hash calculation.

Examples:

VectorNumber["b", "a"].hash # => 3081872088394655324
VectorNumber["a", "b", mult: :cross].hash # => 3081872088394655324
VectorNumber["b", "c"].hash # => -1002381358514682371

Returns:

  • (Integer)

Since:

  • 0.4.2



85
86
87
# File 'lib/vector_number/comparing.rb', line 85

def hash
  [self.class, @data].hash
end

#imaginaryInteger, ... Also known as: imag

Return imaginary part of the number.

Examples:

VectorNumber[23, "a"].imaginary # => 0
VectorNumber["a", Complex(1, 2r)].imag # => (2/1)

Returns:

  • (Integer, Float, Rational, BigDecimal)

Since:

  • 0.1.0



26
# File 'lib/vector_number/converting.rb', line 26

def imaginary = @data[I]

#infinite?1?

Returns 1 if any coefficients are non-finite, nil otherwise.

This behavior is the same as Complex‘s.

Examples:

VectorNumber[2].infinite? # => nil
VectorNumber[Float::NAN].infinite? # => 1
VectorNumber["a"].mult(-Float::INFINITY).infinite? # => 1

Returns:

  • (1, nil)

Since:

  • 0.1.0



71
72
73
# File 'lib/vector_number/querying.rb', line 71

def infinite?
  finite? ? nil : 1 # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
end

#inspectString

Return string representation of the vector.

This is similar to Complex#inspect: it returns result of #to_s in round brackets.

Examples:

VectorNumber[5, "s"].inspect # => "(5 + 1⋅'s')"

Returns:

  • (String)

See Also:

Since:

  • 0.1.0



65
66
67
68
# File 'lib/vector_number/stringifying.rb', line 65

def inspect
  # TODO: Probably make this independent of options.
  "(#{self})"
end

#integer?false

Always returns false, as vectors are not Integers.

Returns:

  • (false)

Since:

  • 0.2.1



156
# File 'lib/vector_number/querying.rb', line 156

def integer? = false

#negative?Boolean

Returns true if number is non-zero and all non-zero coefficients are negative, and false otherwise.

Examples:

VectorNumber["a"].neg.negative? # => true
VectorNumber[-2].neg.negative? # => false
(VectorNumber["1"] - VectorNumber[1]).negative? # => false
VectorNumber[0].negative? # => false

Returns:

  • (Boolean)

Since:

  • 0.1.0



136
137
138
# File 'lib/vector_number/querying.rb', line 136

def negative?
  !zero? && all? { |_u, c| c.negative? }
end

#nonnumeric?(dimensions = 2) ⇒ Boolean

Whether this VectorNumber contains any non-numeric parts.

Examples:

VectorNumber[2].nonnumeric? # => false
VectorNumber[2, 3i].nonnumeric? # => false
VectorNumber[2, "a"].nonnumeric? # => true
VectorNumber[2, 3i].nonnumeric?(1) # => true

Parameters:

  • dimensions (Integer) (defaults to: 2)

    number of dimensions to consider “numeric”

    • 0 — zero

    • 1 — real number

    • 2 — complex number, etc.

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)

    if dimensions is negative

Since:

  • 0.2.1



43
# File 'lib/vector_number/querying.rb', line 43

def nonnumeric?(dimensions = 2) = !numeric?(dimensions)

#nonzero?VectorNumber?

Returns self if there are any non-zero coefficients, nil otherwise.

This is synonymous with size not being equal to 0. Behavior of returning self or nil is the same as Numeric‘s.

Examples:

VectorNumber["ab", "cd"].nonzero? # => (1⋅'ab' + 1⋅'cd')
VectorNumber[].nonzero? # => nil

Returns:

See Also:

Since:

  • 0.1.0



104
105
106
# File 'lib/vector_number/querying.rb', line 104

def nonzero?
  zero? ? nil : self # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
end

#numeric?(dimensions = 2) ⇒ Boolean

Whether this VectorNumber can be considered strictly numeric — real or complex.

Examples:

VectorNumber[2].numeric? # => true
VectorNumber[2, 3i].numeric? # => true
VectorNumber[2, "a"].numeric? # => false
VectorNumber[2, 3i].numeric?(1) # => false

Parameters:

  • dimensions (Integer) (defaults to: 2)

    number of dimensions to consider “numeric”

    • 0 — zero

    • 1 — real number

    • 2 — complex number, etc.

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)

    if dimensions is negative

Since:

  • 0.2.0



24
25
26
27
28
# File 'lib/vector_number/querying.rb', line 24

def numeric?(dimensions = 2)
  raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0

  size <= dimensions && (1..dimensions).count { @data[UNIT[_1]].nonzero? } == size
end

#positive?Boolean

Returns true if number is non-zero and all non-zero coefficients are positive, and false otherwise.

Examples:

VectorNumber["a"].positive? # => true
VectorNumber[2].neg.positive? # => false
(VectorNumber["1"] - VectorNumber[1]).positive? # => false
VectorNumber[0].positive? # => false

Returns:

  • (Boolean)

Since:

  • 0.1.0



120
121
122
# File 'lib/vector_number/querying.rb', line 120

def positive?
  !zero? && all? { |_u, c| c.positive? }
end

#realInteger, ...

Return real part of the number.

Examples:

VectorNumber[23, "a"].real # => 23
VectorNumber["a"].real # => 0

Returns:

  • (Integer, Float, Rational, BigDecimal)

Since:

  • 0.1.0



15
# File 'lib/vector_number/converting.rb', line 15

def real = @data[R]

#real?false

Always returns false, as vectors are not real numbers.

This behavior is the same as Complex‘s.

Returns:

  • (false)

See Also:

Since:

  • 0.1.0



149
# File 'lib/vector_number/querying.rb', line 149

def real? = false

#remainder(other) ⇒ VectorNumber

Return the remainder of dividing self by a real other as a vector.

This is equal to self - other * (self/other).truncate.

Examples:

VectorNumber[10].remainder(3) # => (1)
VectorNumber["a"].remainder(2) # => (1⋅'a')
-VectorNumber["a"].remainder(VectorNumber[2]) # => (-1⋅'a')
# Can't divide by a non-real:
VectorNumber["a"].remainder(VectorNumber["b"]) # RangeError

numeric types can be divided in reverse

3.remainder(VectorNumber[10]) # => (3)
# Can't divide by a non-real:
3.remainder(VectorNumber["a"]) # RangeError

compare to #%

VectorNumber[-5] % 3 # => (1)
VectorNumber[-5].remainder(3) # => (-2)

Parameters:

  • other (Integer, Float, Rational, BigDecimal, VectorNumber)

Returns:

Raises:

  • (RangeError)

    if other is not a number or is not a real number

  • (ZeroDivisionError)

    if other is zero

See Also:

  • #%
  • Numeric#remainder

Since:

  • 0.2.6



323
324
325
326
327
328
# File 'lib/vector_number/mathing.rb', line 323

def remainder(other)
  check_divisibility(other)

  other = other.real
  new { _1.remainder(other) }
end

#round(digits = 0, half: :up) ⇒ VectorNumber

Return a new vector with every coefficient rounded using their #round.

In the case of BigDecimal coefficients, the half parameter is converted to the corresponding BigDecimal rounding mode (one of :half_up, :half_down, or :half_even). Other modes can not be specified.

Examples:

VectorNumber[-4.5i, "i"].round(half: :up) # => (-5i + 1⋅'i')
VectorNumber[-4.5i, "i"].round(half: :even) # => (-4i + 1⋅'i')
VectorNumber[-5.5i, "i"].round(half: :even) # => (-6i + 1⋅'i')
VectorNumber[-5.5i, "i"].round(half: :down) # => (-5i + 1⋅'i')
VectorNumber[-5.35i, "i"].round(1) # => (-5.4i + 1⋅'i')
VectorNumber[-5.35i, "i"].round(-1) # => (-10i)

Parameters:

  • digits (Integer) (defaults to: 0)
  • half (Symbol, nil) (defaults to: :up)

    one of :up, :down or :even, see Float#round for meaning

Returns:

See Also:

  • Float#round

Since:

  • 0.2.2



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/vector_number/math_converting.rb', line 110

def round(digits = 0, half: :up)
  if defined?(BigDecimal)
    bd_mode =
      case half
      when :down then :half_down
      when :even then :half_even
      else :half_up
      end
    new { _1.is_a?(BigDecimal) ? _1.round(digits, bd_mode) : _1.round(digits, half: half) }
  # :nocov:
  else
    new { _1.round(digits, half: half) }
  end
  # :nocov:
end

#to_cComplex

Return value as a Complex if only real and/or imaginary parts are non-zero.

Examples:

VectorNumber[13.5].to_c # => (13.5+0i)
VectorNumber[13r/12].to_c # => ((13/12)+0i)
VectorNumber[2, 2i].to_c # => (2+2i)
VectorNumber[2, :i].to_c # RangeError

Complex(VectorNumber[2]) # => (2+0i)
Complex(VectorNumber[2, "i"]) # RangeError

Returns:

  • (Complex)

Raises:

  • (RangeError)

    if any non-real, non-imaginary part is non-zero

Since:

  • 0.1.0



146
147
148
149
150
# File 'lib/vector_number/converting.rb', line 146

def to_c
  raise_convert_error(Complex) unless numeric?(2)

  Complex(real, imaginary)
end

#to_d(ndigits = nil) ⇒ BigDecimal

Return value as a BigDecimal if only real part is non-zero.

Examples:

VectorNumber[13.5].to_d # => 0.135e2
VectorNumber[13r/12].to_d # ArgumentError
VectorNumber[13r/12].to_d(5) # => 0.10833e1
VectorNumber[2, 2i].to_d # RangeError
VectorNumber[2, :i].to_d # RangeError

# This does't work without NumericRefinements:
BigDecimal(VectorNumber[2]) # TypeError
# #to_s can be used as a workaround if refinements aren't used:
BigDecimal(VectorNumber[2].to_s) # => 0.2e1
BigDecimal(VectorNumber[2, "i"].to_s) # => ArgumentError

Parameters:

  • ndigits (Integer) (defaults to: nil)

    precision

Returns:

  • (BigDecimal)

Raises:

  • (RangeError)

    if any non-real part is non-zero

  • (NameError)

    if BigDecimal is not defined

See Also:

Since:

  • 0.1.0



122
123
124
125
126
127
128
129
# File 'lib/vector_number/converting.rb', line 122

def to_d(ndigits = nil)
  raise_convert_error(BigDecimal) unless numeric?(1)

  return BigDecimal(real, ndigits) if ndigits
  return BigDecimal(real, Float::DIG) if real.is_a?(Float)

  BigDecimal(real)
end

#to_fFloat

Return value as a Float if only real part is non-zero.

Examples:

VectorNumber[13.5].to_f # => 13.5
VectorNumber[13r/12].to_f # => 1.0833333333333333
VectorNumber[2, 2i].to_f # RangeError
VectorNumber[2, :i].to_f # RangeError

Float(VectorNumber[2]) # => 2.0
Float(VectorNumber[2, "i"]) # RangeError

Returns:

  • (Float)

Raises:

  • (RangeError)

    if any non-real part is non-zero

Since:

  • 0.1.0



71
72
73
74
75
# File 'lib/vector_number/converting.rb', line 71

def to_f
  raise_convert_error(Float) unless numeric?(1)

  real.to_f
end

#to_h(&block) ⇒ Hash{Object => Numeric}

Get mutable hash with vector’s data.

Returned hash has a default value of 0.

Examples:

VectorNumber["a", "b", 6].to_h # => {"a"=>1, "b"=>1, 1=>6}
VectorNumber["a", "b", 6].to_h["c"] # => 0

Returns:

  • (Hash{Object => Numeric})

Since:

  • 0.1.0



88
89
90
91
92
93
94
95
# File 'lib/vector_number/enumerating.rb', line 88

def to_h(&block)
  # TODO: Remove block argument.
  if block_given?
    @data.to_h(&block)
  else
    @data.dup
  end
end

#to_iInteger Also known as: to_int

Return value as an Integer, truncating it, if only real part is non-zero.

Examples:

VectorNumber[13.5].to_i # => 13
VectorNumber[13r/12].to_int # => 1
[1.1, 2.2, 3.3][VectorNumber[2]] # => 3.3
VectorNumber[2, 2i].to_i # RangeError
VectorNumber[2, :i].to_f # RangeError

Integer(VectorNumber[2]) # => 2
Integer(VectorNumber[2, "i"]) # RangeError

Returns:

  • (Integer)

Raises:

  • (RangeError)

    if any non-real part is non-zero

Since:

  • 0.1.0



47
48
49
50
51
# File 'lib/vector_number/converting.rb', line 47

def to_i
  raise_convert_error(Integer) unless numeric?(1)

  real.to_i
end

#to_rRational

Return value as a Rational if only real part is non-zero.

Examples:

VectorNumber[13.5].to_r # => (27/2)
VectorNumber[13r/12].to_r # => (13/12)
VectorNumber[2, 2i].to_r # RangeError
VectorNumber[2, :i].to_r # RangeError

Rational(VectorNumber[2]) # => (2/1)
Rational(VectorNumber[2, "i"]) # RangeError

Returns:

  • (Rational)

Raises:

  • (RangeError)

    if any non-real part is non-zero

Since:

  • 0.1.0



92
93
94
95
96
# File 'lib/vector_number/converting.rb', line 92

def to_r
  raise_convert_error(Rational) unless numeric?(1)

  real.to_r
end

#to_s(mult: options[:mult]) ⇒ String

Return string representation of the vector.

Examples:

VectorNumber[5, "s"].to_s # => "5 + 1⋅'s'"
VectorNumber["s", 5].to_s # => "1⋅'s' + 5"

with :mult argument

VectorNumber[5, "s"].to_s(mult: :asterisk) # => "5 + 1*'s'"

:mult option specified for the vector

VectorNumber[5, "s", mult: :none].to_s # => "5 + 1's'"

Parameters:

  • mult (Symbol, String) (defaults to: options[:mult])

    text to use between coefficient and unit, can be one of the keys in MULT_STRINGS or an arbitrary string

Returns:

  • (String)

Raises:

  • (ArgumentError)

    if mult is not a String and is not in MULT_STRINGS‘s keys

Since:

  • 0.1.0



38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/vector_number/stringifying.rb', line 38

def to_s(mult: options[:mult])
  return "0" if zero?

  result = +""
  each_with_index do |(unit, coefficient), index|
    if index.zero?
      result << "-" if coefficient.negative?
    else
      result << (coefficient.positive? ? " + " : " - ")
    end
    result << value_to_s(unit, coefficient.abs, mult: mult)
  end
  result
end

#truncate(digits = 0) ⇒ VectorNumber

Return a new vector with every coefficient truncated using their #truncate.

Examples:

VectorNumber[5.39].truncate # => (5)
VectorNumber[-5.35i].truncate # => (-5i)
VectorNumber[-5.35i, "i"].truncate # => (-5i + 1⋅'i')
VectorNumber[-5.35i, "i"].truncate(1) # => (-5.3i + 1⋅'i')
VectorNumber[-5.35i, "i"].truncate(-1) # => (0)

Parameters:

  • digits (Integer) (defaults to: 0)

Returns:

Since:

  • 0.2.1



49
50
51
# File 'lib/vector_number/math_converting.rb', line 49

def truncate(digits = 0)
  new { _1.truncate(digits) }
end

#unit?(unit) ⇒ Boolean Also known as: key?

Check if a unit has a non-zero coefficient.

Examples:

VectorNumber["a", "b", 6].unit?("a") # => true
VectorNumber["a", "b", 6].key?("c") # => false

Parameters:

  • unit (Object)

Returns:

  • (Boolean)

Since:

  • 0.2.4



123
# File 'lib/vector_number/enumerating.rb', line 123

def unit?(unit) = @data.key?(unit)

#unitsArray<Object> Also known as: keys

Get a list of units with non-zero coefficients.

Examples:

VectorNumber["a", "b", 6].units # => ["a", "b", 1]
VectorNumber.new.keys # => []

Returns:

  • (Array<Object>)

Since:

  • 0.1.0



58
# File 'lib/vector_number/enumerating.rb', line 58

def units = @data.keys

#zero?Boolean

Returns true if there are no non-zero coefficients, and false otherwise.

This is synonymous with size being 0.

Examples:

VectorNumber["c"].zero? # => false
VectorNumber[].zero? # => true

Returns:

  • (Boolean)

See Also:

Since:

  • 0.1.0



88
# File 'lib/vector_number/querying.rb', line 88

def zero? = size.zero?