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/rounding.rb,
lib/vector_number/comparing.rb,
lib/vector_number/vectoring.rb,
lib/vector_number/converting.rb,
lib/vector_number/similarity.rb,
lib/vector_number/enumerating.rb,
lib/vector_number/special_unit.rb,
lib/vector_number/stringifying.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 reasonable.

All instances are frozen after creation.

Working with numeric units

Real and imaginary units are represented by R and I constants, not numbers, as may be expected. They are instances of SpecialUnit class which has useful behavior. However, they are not equal to any object besides themselves, which necessitates always using these constants, not new objects.

User must use these specific constants when

  • initializing vector using a hash,

  • accessing vector by unit.

Customizing #to_s result should also check for these using VectorNumber.numeric_unit?, or, alternatively, for all SpecialUnits with VectorNumber.unit?.

Since:

  • 0.1.0

Defined Under Namespace

Classes: SpecialUnit

Constant Summary collapse

NUMERIC_UNITS =

List of special numeric unit constants.

Since:

  • 0.6.0

[SpecialUnit.new("1", "").freeze, SpecialUnit.new("i").freeze].freeze
R =

Constant for real unit (1).

Its string representation is an empty string.

Since:

  • 0.6.0

I =

Constant for imaginary unit (i).

Its string representation is “i”.

Since:

  • 0.6.0

VERSION =

Returns:

  • (String)

Since:

  • 0.1.0

"0.7.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

Miscellaneous methods collapse

Creation collapse

Miscellaneous methods collapse

Arithmetic operations collapse

Querying collapse

Rounding collapse

Comparing collapse

Vector operations collapse

Type conversion collapse

Similarity measures collapse

Hash-like operations collapse

Constructor Details

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

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

Using VectorNumber.new directly is more efficient than VectorNumber[...].

values can be:

  • an array of values;

  • a hash of units and coefficients;

  • a VectorNumber to copy, most useful with a block;

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

Examples:

VectorNumber.new(1, 2, 3) # ArgumentError
VectorNumber.new([1, 2, 3]) # => (6)
VectorNumber.new(["b", VectorNumber::I]) # => (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({VectorNumber::R => 15, "a" => 3.4, nil => -3})
  # => (15 + 3.4⋅"a" - 3⋅nil)
v.to_h # => {unit/1 => 15, "a" => 3.4, nil => -3}
VectorNumber.new({15 => 1}) # RangeError

Parameters:

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

    values for this vector

Yield Parameters:

  • coefficient (Numeric)

    a real number

Yield Returns:

  • (Numeric)

    new coefficient

Raises:

  • (RangeError)

    if a coefficient is not a real number

Since:

  • 0.1.0



180
181
182
183
184
185
186
# File 'lib/vector_number.rb', line 180

def initialize(values = nil, **nil, &transform)
  initialize_from(values)
  apply_transform(&transform)
  finalize_contents
  @data.freeze
  freeze
end

Instance Attribute Details

#sizeInteger (readonly)

Number of non-zero dimensions.

Returns:

  • (Integer)

Since:

  • 0.1.0



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

def size
  @size
end

Class Method Details

.[](*values) ⇒ VectorNumber .[](**hash_values) ⇒ VectorNumber

Create new VectorNumber from a list of values or a hash.

Examples:

list mode

VectorNumber[1, 2, 3] # => (6)
VectorNumber[[1, 2, 3]] # => (1⋅[1, 2, 3])
VectorNumber[] # => (0)
VectorNumber["b", VectorNumber["b"]] # => (2⋅"b")
VectorNumber["a", "b", "a"] # => (2⋅"a" + 1⋅"b")
VectorNumber[{"a" => 2, "b" => 1.5}] # => (1⋅{"a" => 2, "b" => 1.5})

hash mode

VectorNumber["a" => 2, "b" => 1.5] # => (2⋅"a" + 1.5⋅"b")
VectorNumber["a" => 2, ["b"] => 1.5, {a: 2} => -1] # => (2⋅"a" + 1.5⋅["b"] - 1⋅{a: 2})
VectorNumber[:s => 5, VectorNumber::R => 13, :l => -3, VectorNumber::I => -2.5]
  # => (5⋅:s + 13 - 3⋅:l - 2.5i)

mixing modes doesn’t work

VectorNumber["b", "a", "a" => 2] # ArgumentError
VectorNumber["a" => 2, "b", "a"] # SyntaxError

Overloads:

  • .[](*values) ⇒ VectorNumber

    Parameters:

    • values (Array<Any>)

      values to add together to produce a vector

  • .[](**hash_values) ⇒ VectorNumber

    Parameters:

    • hash_values (Hash{Any => Numeric})

      units and coefficients to create a vector

Returns:

Raises:

  • (ArgumentError, RangeError)

Since:

  • 0.1.0



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/vector_number.rb', line 83

def self.[](*values, **hash_values)
  raise ArgumentError, "no block accepted" if block_given?

  if !values.empty? # rubocop:disable Style/NegatedIfElseCondition
    unless hash_values.empty?
      raise ArgumentError, "either list of values or hash can be used, not both"
    end

    # @type var values : list[unit_type]
    new(values)
  else
    # @type var hash_values : Hash[unit_type, coefficient_type]
    new(hash_values)
  end
end

.numeric_unit?(unit) ⇒ Boolean

Check if an object is a unit representing a numeric dimension (real or imaginary unit).

Examples:

VectorNumber.numeric_unit?(VectorNumber::R) # => true
VectorNumber.numeric_unit?(VectorNumber::I) # => true
VectorNumber.numeric_unit?(VectorNumber::SpecialUnit.new("my")) # => false
VectorNumber.numeric_unit?(:i) # => false

Parameters:

  • unit (Any)

Returns:

  • (Boolean)

See Also:

Since:

  • 0.6.0



117
118
119
# File 'lib/vector_number.rb', line 117

def self.numeric_unit?(unit)
  NUMERIC_UNITS.include?(unit) # steep:ignore ArgumentTypeMismatch
end

.special_unit?(unit) ⇒ Boolean Also known as: unit?

Check if an object is a SpecialUnit. This includes numeric units.

Examples:

VectorNumber.special_unit?(VectorNumber::R) # => true
VectorNumber.special_unit?(VectorNumber::SpecialUnit.new("my")) # => true
VectorNumber.special_unit?(:i) # => false

Parameters:

  • unit (Any)

Returns:

  • (Boolean)

See Also:

Since:

  • 0.7.0



134
135
136
# File 'lib/vector_number.rb', line 134

def self.special_unit?(unit)
  SpecialUnit === unit
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:

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



293
294
295
296
297
298
299
# File 'lib/vector_number/mathing.rb', line 293

def %(other)
  check_divisibility(other)

  other = other.real
  # @type var other: Float
  new { _1 % other }
end

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

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

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:

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) && VectorNumber === other
    # @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 (Any)

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



195
196
# File 'lib/vector_number.rb', line 195

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 (Any)

Returns:

Since:

  • 0.2.0



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

def -(other)
  new([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 scaled by reciprocal of other.

This effectively divides #magnitude by 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:

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:
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 (Any)

Returns:

  • (Integer)
  • (nil)

    if self or other isn’t a real number.

See Also:

Since:

  • 0.2.0



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

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 (Any)

Returns:

  • (Boolean)

Since:

  • 0.2.0



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

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

  case other
  when VectorNumber
    # HACK: Using instance variable access to avoid hash copying overhead.
    size == other.size && @data == other.instance_variable_get(:@data)
  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][VectorNumber::R] # => 6
VectorNumber["a", "b", 6]["c"] # => 0

Parameters:

  • unit (Any)

Returns:

  • (Numeric)

See Also:

Since:

  • 0.2.4



106
107
108
# File 'lib/vector_number/enumerating.rb', line 106

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

#abs2Numeric

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:

  • (Numeric)

Since:

  • 0.2.2



35
36
37
# File 'lib/vector_number/vectoring.rb', line 35

def abs2
  @data.values.sum(&:abs2)
end

#angle(other) ⇒ Numeric

Calculate the angle between this vector and other vector in radians.

Result of this method is particularly imprecise due to the nature of the arccosine function. It should not be used if vectors are suspected to be collinear, use #collinear? family of methods instead.

Examples:

v = VectorNumber[2, "a"]
v.angle(v) # => 0.0
v.angle(1) # => 0.7853981633974483
v.angle("b") # => 1.5707963267948966
v.angle(-v) # => 3.141592653589793
v.angle(0) # ZeroDivisionError
VectorNumber[0].angle(v) # ZeroDivisionError

Parameters:

Returns:

  • (Numeric)

Raises:

  • (ZeroDivisionError)

    if either self or other is a zero vector

See Also:

Since:

  • 0.7.0



241
242
243
# File 'lib/vector_number/vectoring.rb', line 241

def angle(other)
  Math.acos(cosine(other))
end

#assoc(unit) ⇒ Array(Any, Numeric)

Get a 2-element array containing a given unit and associated coefficient.

Examples:

VectorNumber["a", "b", 6].assoc("a") # => ["a", 1]
VectorNumber["a", "b", 6].assoc(VectorNumber::R) # => [unit/1, 6]
VectorNumber["a", "b", 6].assoc("c") # => ["c", 0]

Parameters:

  • unit (Any)

Returns:

  • (Array(Any, Numeric))

See Also:

Since:

  • 0.7.0



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

def assoc(unit)
  @data.assoc(unit) || [unit, 0]
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



36
37
38
# File 'lib/vector_number/rounding.rb', line 36

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

#ceildiv(other) ⇒ VectorNumber

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

This is equal to (self / other).ceil.

Examples:

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

Parameters:

Returns:

Raises:

  • (RangeError)

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

  • (ZeroDivisionError)

    if other is zero

See Also:

Since:

  • 0.7.0



254
255
256
257
258
259
260
261
# File 'lib/vector_number/mathing.rb', line 254

def ceildiv(other)
  check_divisibility(other)

  other = other.real
  other = Rational(other) if other.integer?
  # @type var other: Float
  new { (_1 / other).ceil }
end

#clone(freeze: true) ⇒ VectorNumber

Return self.

Returns:

Raises:

  • (ArgumentError)

    if freeze is not true or nil.

Since:

  • 0.2.4



205
206
207
208
209
210
211
212
213
214
215
# File 'lib/vector_number.rb', line 205

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

#codirectional?(other) ⇒ Boolean

Determine if this vector is codirectional with other vector.

If either vector is a zero vector, they are not considered codirectional.

Examples:

v = VectorNumber[2, "a"]
v.codirectional?(2) # => false
v.codirectional?(v) # => true
v.codirectional?(0) # => false
v.codirectional?(VectorNumber[4, "a", "a"]) # => true
v.codirectional?(-VectorNumber[4, "a", "a"]) # => false

Parameters:

Returns:

  • (Boolean)

See Also:

Since:

  • 0.7.0



340
341
342
# File 'lib/vector_number/vectoring.rb', line 340

def codirectional?(other)
  scale_factor(other)&.positive? || false
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



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

def coefficients = @data.values

#coefficients_at(*units) ⇒ Array<Numeric> Also known as: values_at

Get a list of coefficients corresponding to units.

Examples:

VectorNumber["a", "b", 6].coefficients_at("a", "b") # => [1, 1]
VectorNumber["a", "b", 6].coefficients_at("a", "c") # => [1, 0]
VectorNumber["a", "b", 6].coefficients_at("c", "b", "c") # => [0, 1, 0]
VectorNumber["a", "b", 6].coefficients_at) # => []

Parameters:

  • units (Array<Any>)

Returns:

  • (Array<Numeric>)

See Also:

Since:

  • 0.7.0



81
82
83
# File 'lib/vector_number/enumerating.rb', line 81

def coefficients_at(*units)
  @data.values_at(*units) # : Array[coefficient_type]
end

#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 (Any)

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

#collinear?(other) ⇒ Boolean

Determine if this vector is collinear with other vector.

If either vector is a zero vector, they are considered collinear.

Examples:

v = VectorNumber[2, "a"]
v.collinear?(2) # => false
v.collinear?(v) # => true
v.collinear?(0) # => true
v.collinear?(VectorNumber[4, "a", "a"]) # => true
v.collinear?(-VectorNumber[4, "a", "a"]) # => true

Parameters:

Returns:

  • (Boolean)

See Also:

Since:

  • 0.7.0



294
295
296
# File 'lib/vector_number/vectoring.rb', line 294

def collinear?(other)
  !!scale_factor(other)
end

#cosine(other) ⇒ Numeric Also known as: cosine_similarity

Calculate cosine between this vector and other.

Cosine can be used as a measure of similarity.

Examples:

v = VectorNumber[2, "a"]
v.cosine(v) # => 1.0
v.cosine_similarity(1) # => 0.8944271909999159
v.cosine("b") # => 0.0
v.cosine_similarity(-v) # => -1.0
v.cosine(0) # ZeroDivisionError
VectorNumber[0].cosine(v) # ZeroDivisionError

Parameters:

Returns:

  • (Numeric)

Raises:

  • (ZeroDivisionError)

    if either self or other is a zero vector

See Also:

Since:

  • 0.7.0



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/vector_number/similarity.rb', line 26

def cosine(other)
  has_direction?
  return 1.0 if equal?(other)

  other = new([other]) unless VectorNumber === other
  has_direction?(other)
  return 0.0 if (product = dot_product(other)).zero?

  # Due to precision errors, the result might be slightly outside [-1, 1], so we clamp.
  (product / magnitude / other.magnitude).clamp(-1.0, 1.0)
end

#cross_product(other, basis = %i[x y z])) ⇒ VectorNumber Also known as: vector_product

Calculate the cross product (vector product) of this vector with other vector.

As cross product is normally defined only for regular 3D space, this method has unusual properties:

  • basis argument specifies which triplet of dimensions to work in,

  • all other dimensions are discarded.

Examples:

v = VectorNumber[x: 3, y: 4, z: 5]
v.cross_product(:x) # => (5⋅:y - 4⋅:z)
v.vector_product(:x, [:y, :x, :z]) # => (-5⋅:y + 4⋅:z)
v.cross_product(VectorNumber[:a, :x, :z], [:a, :b, :x]) # => (3⋅:b)
v.cross_product(-v) # => (0)
v.cross_product(0) # => (0)

Parameters:

  • other (VectorNumber, Any)
  • basis (Array(Any, Any, Any)) (defaults to: %i[x y z]))

    units specifying basis to use

Returns:

Raises:

  • (ArgumentError)

    if basis does not contain exactly 3 units

Since:

  • 0.7.0



201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/vector_number/vectoring.rb', line 201

def cross_product(other, basis = i[x y z]) # rubocop:disable Metrics/AbcSize
  raise ArgumentError, "`basis` must contain exactly 3 units" if basis.size != 3

  other = new([other]) unless VectorNumber === other
  return VectorNumber.new if collinear?(other)

  i, j, k = *basis
  new({
    i => (self[j] * other[k] - self[k] * other[j]), # steep:ignore UnresolvedOverloading
    j => (self[k] * other[i] - self[i] * other[k]), # steep:ignore UnresolvedOverloading
    k => (self[i] * other[j] - self[j] * other[i]), # steep:ignore UnresolvedOverloading
  })
end

#dig(*identifiers) ⇒ Numeric

Finds and returns the object in nested objects that is specified by identifiers.

As nested objects for a VectorNumber are numeric coefficients, digging deeper will most probably result in an error.

Examples:

VectorNumber["a", "b", 6].dig("a") # => 1
VectorNumber["a", "b", 6].dig("c") # => 0
VectorNumber["a", "b", 6].dig("a", 1) # TypeError
{ 1 => VectorNumber["a", "b", 6] }.dig(1, "a") # => 1

Parameters:

  • identifiers (Array<Any>)

Returns:

  • (Numeric)

Since:

  • 0.7.0



142
143
144
# File 'lib/vector_number/enumerating.rb', line 142

def dig(*identifiers)
  @data.dig(*identifiers)
end

#div(other) ⇒ VectorNumber

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

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

Examples:

VectorNumber[10].div(3) # => (3)
VectorNumber["a"].div(2) # => (0)
VectorNumber["a"].div(VectorNumber[2]) # => (0)
VectorNumber[-10, "string"].div(100) # => (-1)
# 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:

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



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

def div(other)
  check_divisibility(other)

  other = other.real
  # @type var other: Float
  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), (1⋅"a")]
VectorNumber["a"].divmod(VectorNumber[2]) # => [(0), (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:

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



327
328
329
# File 'lib/vector_number/mathing.rb', line 327

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

#dot_product(other) ⇒ Numeric Also known as: inner_product, scalar_product

Calculate the dot product (inner product/scalar product) of this vector with other vector.

Examples:

VectorNumber[2].dot_product(VectorNumber[3]) # => 6
v = VectorNumber[2, "a"]
v.dot_product(VectorNumber[3, "s"]) # => 6
v.inner_product(3) # => 6
v.scalar_product("b") # => 0
v.dot_product(0) # => 0
v.inner_product(2 * VectorNumber[-0.5, "a"]) # => 0
VectorNumber[0].scalar_product(v) # => 0
v.dot_product(v) == v.abs2 # => true

Parameters:

Returns:

  • (Numeric)

Since:

  • 0.7.0



165
166
167
168
169
170
171
172
173
# File 'lib/vector_number/vectoring.rb', line 165

def dot_product(other)
  return 0 if zero?
  return abs2 if equal?(other)

  other = new([other]) unless VectorNumber === other
  return 0 if other.zero?

  @data.sum { |u, c| c * other[u] } # steep:ignore UnresolvedOverloading
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 VectorNumber.numeric_unit?(u) } # => (1⋅"a" + 1⋅"b" + 6)
units # => ["a", "b"]

Enumerator

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

Overloads:

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

    Returns self.

    Yield Parameters:

    • unit (Any)
    • coefficient (Numeric)

    Yield Returns:

    • (void)

    Returns:

  • #eachEnumerator

    Returns:

    • (Enumerator)

See Also:

  • Enumerable
  • Enumerator

Since:

  • 0.1.0



35
36
37
38
39
40
41
# File 'lib/vector_number/enumerating.rb', line 35

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

  # @type var block: ^([unit_type, coefficient_type]) -> untyped
  @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`.

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 (Any)

Returns:

  • (Boolean)

Since:

  • 0.1.0



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/vector_number/comparing.rb', line 62

def eql?(other)
  return true if equal?(other)
  return false unless self.class == other.class

  # @type var other : vector_instance
  size.eql?(other.size) && @data.eql?(other.instance_variable_get(:@data))
rescue NoMethodError => e
  # :nocov:
  raise unless e.receiver.equal?(other)
  # :nocov:

  # Should only happen if `other.class` is undefined.
  false
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:

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) } # steep:ignore BlockBodyTypeMismatch
end

#fetch(unit) ⇒ Numeric #fetch(unit, default_value) ⇒ Any #fetch(unit) {|unit| ... } ⇒ Any

Get the coefficient for the unit, treating 0 coefficients as missing.

If self.unit?(unit) is true, returns the associated coefficient. Otherwise:

  • if neither block, not default_value is given, raises KeyError;

  • if block is given, returns the result of the block;

  • if default_value is given, returns default_value.

Examples:

VectorNumber["a", "b", 6].fetch("a") # => 1
VectorNumber["a", "b", 6].fetch("c") # KeyError
VectorNumber["a", "b", 6].fetch("c", "default") # => "default"
VectorNumber["a", "b", 6].fetch("c") { |u| "default #{u}" } # => "default c"

Overloads:

  • #fetch(unit) ⇒ Numeric

    Parameters:

    • unit (Any)

    Returns:

    • (Numeric)
  • #fetch(unit, default_value) ⇒ Any

    Parameters:

    • unit (Any)
    • default_value (Any)

    Returns:

    • (Any)
  • #fetch(unit) {|unit| ... } ⇒ Any

    Parameters:

    • unit (Any)

    Yield Parameters:

    • unit (Any)

    Yield Returns:

    • (Any)

    Returns:

    • (Any)

Raises:

  • (KeyError)

    if default value was needed, but not provided

See Also:

Since:

  • 0.7.0



179
180
181
# File 'lib/vector_number/enumerating.rb', line 179

def fetch(...)
  @data.fetch(...) # steep:ignore UnresolvedOverloading
end

#fetch_coefficients(*units) ⇒ Array<Numeric> #fetch_coefficients(*units) {|unit| ... } ⇒ Array<Any> Also known as: fetch_values

Get coefficients for multiple units in the same way as #fetch.

Examples:

VectorNumber["a", "b", 6].fetch_coefficients(VectorNumber::R, "a") # => [6, 1]
VectorNumber["a", "b", 6].fetch_coefficients("a", "c") # KeyError
VectorNumber["a", "b", 6].fetch_coefficients("a", "c") { 0 } # => [1, 0]
VectorNumber["a", "b", 6].fetch_coefficients("a", "a", "a") # => [1, 1, 1]
VectorNumber["a", "b", 6].fetch_coefficients # => []

Overloads:

  • #fetch_coefficients(*units) ⇒ Array<Numeric>

    Parameters:

    • units (Array<Any>)

    Returns:

    • (Array<Numeric>)
  • #fetch_coefficients(*units) {|unit| ... } ⇒ Array<Any>

    Parameters:

    • units (Array<Any>)

    Yield Parameters:

    • unit (Any)

    Yield Returns:

    • (Any)

    Returns:

    • (Array<Any>)

Raises:

  • (KeyError)

    if default value was needed, but not provided

See Also:

Since:

  • 0.7.0



207
208
209
# File 'lib/vector_number/enumerating.rb', line 207

def fetch_coefficients(...)
  @data.fetch_values(...) # steep:ignore UnresolvedOverloading
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



62
63
64
# File 'lib/vector_number/querying.rb', line 62

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



53
54
55
56
# File 'lib/vector_number/rounding.rb', line 53

def floor(digits = 0)
  # Why is there a problem here, but not in #truncate and #ceil? I dunno.
  new { _1.floor(digits) } # steep:ignore BlockBodyTypeMismatch
end

#hashInteger

Generate an Integer hash value for self.

Examples:

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

Returns:

  • (Integer)

Since:

  • 0.4.2



87
88
89
# File 'lib/vector_number/comparing.rb', line 87

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

#imaginaryNumeric Also known as: imag

Get imaginary part of the vector.

Examples:

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

Returns:

  • (Numeric)

Since:

  • 0.1.0



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

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



76
77
78
# File 'lib/vector_number/querying.rb', line 76

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

#inspectString

Return string representation of the vector suitable for display.

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



69
70
71
72
73
# File 'lib/vector_number/stringifying.rb', line 69

def inspect
  return "(0)" if zero?

  "(#{build_string("⋅")})"
end

#integer?false

Always returns false, as vectors are not Integers.

Returns:

  • (false)

Since:

  • 0.2.1



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

def integer? = false

#jaccard_index(other) ⇒ Rational

Calculate Jaccard index of similarity between this vector and other.

This measure is binary: it considers only sets of non-zero dimensions in vectors, ignoring coefficients.

Examples:

v = VectorNumber[2, "a"]
v.jaccard_index(v) # => (1/1)
v.jaccard_index(1) # => (1/2)
v.jaccard_index("b") # => (0/1)
v.jaccard_index(-v) # => (1/1)
v.jaccard_index(0) # => (0/1)
VectorNumber[0].jaccard_index(v) # => (0/1)
VectorNumber[0].jaccard_index(0) # ZeroDivisionError

Parameters:

Returns:

  • (Rational)

Raises:

  • (ZeroDivisionError)

    if both vectors are zero vectors

See Also:

Since:

  • 0.7.0



63
64
65
66
67
# File 'lib/vector_number/similarity.rb', line 63

def jaccard_index(other)
  other = new([other]) unless VectorNumber === other
  intersection = units.intersection(other.units)
  Rational(intersection.size, size + other.size - intersection.size)
end

#jaccard_similarity(other) ⇒ Rational

Calculate weighted Jaccard similarity index between this vector and other.

This measure only makes sense for non-negative vectors.

Examples:

v = VectorNumber[2, "a"]
v.jaccard_similarity(v) # => (1/1)
v.jaccard_similarity(1) # => (1/3)
v.jaccard_similarity("b") # => (0/1)
v.jaccard_similarity(-v) # => (-1/1)
v.jaccard_similarity(0) # => (0/1)
VectorNumber[0].jaccard_similarity(v) # => (0/1)
VectorNumber[0].jaccard_similarity(0) # ZeroDivisionError

Parameters:

Returns:

  • (Rational)

Raises:

  • (ZeroDivisionError)

    if both vectors are zero vectors

See Also:

Since:

  • 0.7.0



90
91
92
93
94
95
96
# File 'lib/vector_number/similarity.rb', line 90

def jaccard_similarity(other)
  other = new([other]) unless VectorNumber === other
  Rational(
    @data.sum { |u, c| [c, other[u]].min },
    units.union(other.units).sum { |u| [self[u], other[u]].max }
  )
end

#magnitudeNumeric Also known as: abs

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

This is also known as Euclidean norm or 2-norm.

Examples:

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

Returns:

  • (Numeric)

Since:

  • 0.2.2



18
19
20
# File 'lib/vector_number/vectoring.rb', line 18

def magnitude
  abs2**0.5
end

#maximum_normNumeric Also known as: infinity_norm

Calculate the maximum norm (infinity norm) of the vector.

Examples:

VectorNumber[5.3].maximum_norm # => 5.3
VectorNumber[-5.3i].maximum_norm # => 5.3
VectorNumber[-5.3i, "i"].maximum_norm # => 5.3
VectorNumber["a", "s"].maximum_norm # => 1

Returns:

  • (Numeric)

Since:

  • 0.7.0



67
68
69
70
71
# File 'lib/vector_number/vectoring.rb', line 67

def maximum_norm
  return 0 if zero?

  @data.values.map(&:abs).max
end

#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



133
134
135
# File 'lib/vector_number/querying.rb', line 133

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

#nonnumeric?(dimensions = 2) ⇒ Boolean

Returns true if this VectorNumber contains any non-zero dimensions with non-numeric units, and false otherwise.

This is exactly the opposite of #numeric?.

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

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)

    if dimensions is negative

Since:

  • 0.2.1



50
51
52
# File 'lib/vector_number/querying.rb', line 50

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

#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



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

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

#numeric?(dimensions = 2) ⇒ Boolean

Returns true if all non-zero dimensions in this VectorNumber are numeric (real or complex), and false otherwise.

This is exactly the opposite of #nonnumeric?.

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

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)

    if dimensions is negative

Since:

  • 0.2.0



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

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

  size <= dimensions &&
    (0...dimensions).count { (unit = NUMERIC_UNITS[_1]) && @data[unit].nonzero? } == size
end

#opposite?(other) ⇒ Boolean

Determine if this vector is opposite with other vector.

If either vector is a zero vector, they are not considered opposite.

Examples:

v = VectorNumber[2, "a"]
v.opposite?(2) # => false
v.opposite?(v) # => false
v.opposite?(0) # => false
v.opposite?(VectorNumber[4, "a", "a"]) # => false
v.opposite?(-VectorNumber[4, "a", "a"]) # => true

Parameters:

Returns:

  • (Boolean)

See Also:

Since:

  • 0.7.0



364
365
366
# File 'lib/vector_number/vectoring.rb', line 364

def opposite?(other)
  scale_factor(other)&.negative? || false
end

#orthogonal?(other) ⇒ Boolean

Determine if this vector is orthogonal with other vector.

If either vector is a zero vector, they are not considered orthogonal. If vectors have no non-zero dimensions in common, they are orthogonal.

Examples:

v = VectorNumber[2, "a"]
v.orthogonal?(2) # => false
v.orthogonal?(v) # => false
v.orthogonal?(0) # => false
v.orthogonal?(-2 * VectorNumber[-0.5, "a"]) # => true
v.orthogonal?(VectorNumber["b", :c]) # => true

Parameters:

Returns:

  • (Boolean)

See Also:

Since:

  • 0.7.0



264
265
266
267
268
269
270
271
# File 'lib/vector_number/vectoring.rb', line 264

def orthogonal?(other)
  return false if zero?

  other = new([other]) unless VectorNumber === other
  return false if other.zero?

  dot_product(other).zero?
end

#p_norm(p) ⇒ Numeric

Calculate the p-norm of the vector.

Examples:

VectorNumber[5.3].p_norm(2) # => 5.3
VectorNumber[-5.3i].p_norm(2) # => 5.3
VectorNumber[-5.3i, "i"].p_norm(2) # => 5.3935146240647205
VectorNumber[-5.3i, "i"].p_norm(1) # => 6.3
VectorNumber[-5.3i, "i"].p_norm(0.5) # => 10.904345773288535

Parameters:

  • p (Numeric)

Returns:

  • (Numeric)

Since:

  • 0.7.0



52
53
54
# File 'lib/vector_number/vectoring.rb', line 52

def p_norm(p) # rubocop:disable Naming/MethodParameterName
  @data.values.sum { _1.abs**p }**(1.0 / p) # steep:ignore UnresolvedOverloading
end

#parallel?(other) ⇒ Boolean

Determine if this vector is collinear with other vector and both are non-zero.

Examples:

v = VectorNumber[2, "a"]
v.parallel?(2) # => false
v.parallel?(v) # => true
v.parallel?(0) # => false
v.parallel?(VectorNumber[4, "a", "a"]) # => true
v.parallel?(-VectorNumber[4, "a", "a"]) # => true

Parameters:

Returns:

  • (Boolean)

See Also:

Since:

  • 0.7.0



316
317
318
# File 'lib/vector_number/vectoring.rb', line 316

def parallel?(other)
  !!scale_factor(other)&.nonzero?
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



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

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

#pretty_print(pp) ⇒ void

This method returns an undefined value.

Support for PP, usually outputs the same text as #inspect.

Parameters:

  • pp (PP)

Since:

  • 0.7.0



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/vector_number/stringifying.rb', line 81

def pretty_print(pp) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
  pp.text("(0)") and return if zero?

  pp.group(1, "(", ")") do
    # This should use `pp.fill_breakable`, but PrettyPrint::SingleLine haven't had it.
    pp.seplist(@data, -> { fill_breakable(pp) }, :each_with_index) do |(unit, coefficient), i|
      pp.text("-") if coefficient.negative?
      unless i.zero?
        pp.text("+") if coefficient.positive?
        fill_breakable(pp)
      end
      pp.pp(coefficient.abs)
      if SpecialUnit === unit
        pp.text(unit.to_s)
      else
        pp.text("⋅")
        pp.pp(unit)
      end
    end
  end
end

#realNumeric

Get real part of the vector.

Examples:

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

Returns:

  • (Numeric)

Since:

  • 0.1.0



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

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



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

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:

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



358
359
360
361
362
363
364
# File 'lib/vector_number/mathing.rb', line 358

def remainder(other)
  check_divisibility(other)

  other = other.real
  # @type var other: Float
  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



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/vector_number/rounding.rb', line 81

def round(digits = 0, half: :up) # rubocop:disable Metrics/MethodLength
  if defined?(BigDecimal)
    bd_mode =
      case half
      when :down then :half_down
      when :even then :half_even
      else :half_up
      end
    new do |c|
      if BigDecimal === c
        c.round(digits, bd_mode)
      else
        c.round(digits, half: half) # steep:ignore UnresolvedOverloading
      end
    end
  # :nocov:
  else
    new { _1.round(digits, half: half) } # steep:ignore UnresolvedOverloading
  end
  # :nocov:
end

#scalar_projection(other) ⇒ Numeric

Calculate the scalar projection of this vector onto other vector.

Absolute value of scalar projection is equal to #magnitude of #vector_projection. Sign of scalar projection depends on the #angle between vectors.

Examples:

v = VectorNumber[2, "a"]
v.scalar_projection(v) # => 2.23606797749979
v.scalar_projection(1) # => 2.0
v.scalar_projection("b") # => 0.0
v.scalar_projection(-v) # => -2.23606797749979
v.scalar_projection(2 * VectorNumber[-0.5, "a"]) # => 0.0
VectorNumber[0].scalar_projection(v) # => 0
v.scalar_projection(0) # ZeroDivisionError

Parameters:

Returns:

  • (Numeric)

Raises:

  • (ZeroDivisionError)

    if other is a zero vector

Since:

  • 0.7.0



414
415
416
417
418
419
420
421
# File 'lib/vector_number/vectoring.rb', line 414

def scalar_projection(other)
  return magnitude if equal?(other) && has_direction?

  other = new([other]) unless VectorNumber === other
  has_direction?(other)

  dot_product(other) / other.magnitude
end

#scalar_rejection(other) ⇒ Numeric

Calculate the scalar rejection of this vector from other vector.

Scalar rejection is equal to #magnitude of #vector_rejection, and is always non-negative.

Examples:

v = VectorNumber[2, "a"]
v.scalar_rejection(v) # => 0.0
v.scalar_rejection(1) # => 1.0
v.scalar_rejection("b") # => 2.23606797749979
v.scalar_rejection(-v) # => 0.0
v.scalar_rejection(2 * VectorNumber[-0.5, "a"]) # => 2.23606797749979
VectorNumber[0].scalar_rejection(v) # => 0.0
v.scalar_rejection(0) # ZeroDivisionError

Parameters:

Returns:

  • (Numeric)

Raises:

  • (ZeroDivisionError)

    if other is a zero vector

Since:

  • 0.7.0



471
472
473
474
475
476
477
478
# File 'lib/vector_number/vectoring.rb', line 471

def scalar_rejection(other)
  return 0.0 if equal?(other) && has_direction?

  other = new([other]) unless VectorNumber === other
  has_direction?(other)

  (abs2 - dot_product(other)**2 / other.abs2)**0.5 # steep:ignore NoMethod
end

#subspace_basisArray<VectorNumber>

Return an array of vectors forming orthonormal basis of linear subspace this vector belongs to.

Examples:

v = VectorNumber[1.2, "a"] * 2 - "s" # => (2.4 + 2⋅"a" - 1⋅"s")
v.subspace_basis # => [(1), (1⋅"a"), (1⋅"s")]
VectorNumber[0].subspace_basis # => []

Returns:

See Also:

Since:

  • 0.7.0



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

def subspace_basis
  units.map { new([_1]) }
end

#subspace_projectionsArray<VectorNumber>

Return an array of vectors representing projections onto basis vectors of linear subspace this vector belongs to.

Examples:

v = VectorNumber[1.2, "a"] * 2 - "s" # => (2.4 + 2⋅"a" - 1⋅"s")
v.subspace_projections # => [(2.4), (2⋅"a"), (-1⋅"s")]

Returns:

See Also:

Since:

  • 0.7.0



105
106
107
# File 'lib/vector_number/vectoring.rb', line 105

def subspace_projections
  map { |u, c| new({ u => c }) }
end

#to_cComplex

Convert vector to a Complex if only real and/or imaginary dimensions 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 dimension is non-zero

Since:

  • 0.1.0



130
131
132
133
134
# File 'lib/vector_number/converting.rb', line 130

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

  Complex(real, imaginary)
end

#to_d(ndigits = nil) ⇒ BigDecimal

Convert vector to a BigDecimal if only real dimension 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:
BigDecimal(VectorNumber[2]) # TypeError
# #to_s can be used as a workaround:
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 dimension is non-zero

  • (NameError)

    if BigDecimal is not defined

See Also:

  • Kernel.BigDecimal

Since:

  • 0.1.0



108
109
110
111
112
113
114
115
# File 'lib/vector_number/converting.rb', line 108

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

  return BigDecimal(real, ndigits) if ndigits
  return BigDecimal(real, Float::DIG) if Float === real

  BigDecimal(real)
end

#to_fFloat

Convert vector to a Float if only real dimension 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 dimension is non-zero

Since:

  • 0.1.0



62
63
64
65
66
# File 'lib/vector_number/converting.rb', line 62

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

  real.to_f
end

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

Get mutable hash with vector’s data.

Returned hash has a default value of 0.

Examples:

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

Returns:

  • (Hash{Any => Numeric})

Since:

  • 0.1.0



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

def to_h(&block)
  # TODO: Remove block argument.
  if block_given?
    # @type var block: ^(unit_type, coefficient_type) -> each_value_type
    @data.to_h(&block)
  else
    @data.dup
  end
end

#to_iInteger Also known as: to_int

Convert vector to an Integer, if only real dimension 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 dimension is non-zero

Since:

  • 0.1.0



41
42
43
44
45
# File 'lib/vector_number/converting.rb', line 41

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

  real.to_i
end

#to_rRational

Convert vector to a Rational if only real dimension 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 dimension is non-zero

Since:

  • 0.1.0



81
82
83
84
85
# File 'lib/vector_number/converting.rb', line 81

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

  real.to_r
end

#to_s(mult: :dot) {|unit, coefficient, index, operator| ... } ⇒ String

Return string representation of the vector suitable for output.

An optional block can be supplied to provide customized substrings for each unit and coefficient pair. Care needs to be taken in handling VectorNumber::R and VectorNumber::I units. numeric_unit?/special_unit? can be used to check if a particular unit requires different logic.

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"
(-VectorNumber[5, :s]).to_s(mult: "~~~") # => "-5 - 1~~~:s"

with a block

VectorNumber[5, :s].to_s { |k, v| "#{format("%+.0f", v)}%#{k}" } # => "+5%1+1%s"
VectorNumber[5, :s].to_s(mult: :cross) { |k, v, i, op|
  "#{',' unless i.zero?}#{v}#{op+k.inspect unless k == VectorNumber::R}"
} # => "5,1×:s"

Parameters:

  • mult (Symbol, String) (defaults to: :dot)

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

Yield Parameters:

  • unit (Any)
  • coefficient (Numeric)
  • index (Integer)
  • operator (String)

Yield Returns:

  • (String)

    a string for this unit and coefficient

Returns:

  • (String)

Raises:

  • (ArgumentError)

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

Since:

  • 0.1.0



49
50
51
52
53
54
55
56
57
# File 'lib/vector_number/stringifying.rb', line 49

def to_s(mult: :dot, &block)
  if !(String === mult) && !MULT_STRINGS.key?(mult)
    raise ArgumentError, "unknown key #{mult.inspect}", caller
  end
  return "0" if zero?

  operator = (String === mult) ? mult : MULT_STRINGS[mult]
  build_string(operator, &block)
end

#transform_coefficients(mapping) ⇒ VectorNumber #transform_coefficients {|coefficient| ... } ⇒ VectorNumber #transform_coefficients(mapping) {|coefficient| ... } ⇒ VectorNumber #transform_coefficientsEnumerator Also known as: transform_values

Return a new VectorNumber with coefficients transformed by the mapping hash and/or a block.

An optional mapping argument can be provided to map coefficients to new coefficients. Any coefficient not given in mapping will be mapped using the provided block, or remain the same if no block is given. If neither mapping nor block is given, an enumerator is returned.

Examples:

VectorNumber["a", "b", 6].transform_coefficients { _1 * 2 } # => (2⋅"a" + 2⋅"b" + 12)
VectorNumber["a", "b", 6].transform_values(1 => 2) # => (2⋅"a" - 2⋅"b" + 6)
VectorNumber["a", "b", 6].transform_coefficients(1 => 2) { _1 / 2 } # => (2⋅"a" - 2⋅"b" + 3)
VectorNumber["a", "b", 6].transform_values # => Enumerator

Overloads:

  • #transform_coefficients(mapping) ⇒ VectorNumber

    Parameters:

    • mapping (Hash{Numeric => Numeric})

    Returns:

  • #transform_coefficients {|coefficient| ... } ⇒ VectorNumber

    Yield Parameters:

    • coefficient (Numeric)

    Yield Returns:

    • (Numeric)

    Returns:

  • #transform_coefficients(mapping) {|coefficient| ... } ⇒ VectorNumber

    Parameters:

    • mapping (Hash{Numeric => Numeric})

    Yield Parameters:

    • coefficient (Numeric)

    Yield Returns:

    • (Numeric)

    Returns:

  • #transform_coefficientsEnumerator

    Returns:

    • (Enumerator)

Since:

  • 0.7.0



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/vector_number/enumerating.rb', line 261

def transform_coefficients(mapping = nil, &transform)
  if mapping
    if block_given?
      # @type var transform: ^(coefficient_type) -> coefficient_type
      new { |c| mapping.fetch(c) { yield(c) } }
    else
      new { |c| mapping.fetch(c, c) }
    end
  elsif block_given?
    # @type var transform: ^(coefficient_type) -> coefficient_type
    new(&transform)
  else
    to_enum(:transform_coefficients) { size } # rubocop:disable Lint/ToEnumArguments
  end
end

#transform_units(mapping) ⇒ VectorNumber #transform_units {|unit| ... } ⇒ VectorNumber #transform_units(mapping) {|unit| ... } ⇒ VectorNumber #transform_unitsEnumerator Also known as: transform_keys

Return a new VectorNumber with units transformed by the mapping hash and/or a block.

An optional mapping argument can be provided to map units to new units. Any unit not given in mapping will be mapped using the provided block, or remain the same if no block is given. If neither mapping nor block is given, an enumerator is returned.

Examples:

VectorNumber["a", "b", 6].transform_units("a" => "c") # => (1⋅"c" + 1⋅"b" + 6)
VectorNumber["a", "b", 6].transform_keys { _1.is_a?(String) ? _1.to_sym : _1 }
  # => (1⋅:a + 1⋅:b + 6)
VectorNumber["a", "b", 6].transform_units("a" => "c") { _1.to_s }
  # => (1⋅"c" + 1⋅"b" + 6⋅"")
VectorNumber["a", "b", 6].transform_keys # => Enumerator

Overloads:

  • #transform_units(mapping) ⇒ VectorNumber

    Parameters:

    • mapping (Hash{Any => Any})

    Returns:

  • #transform_units {|unit| ... } ⇒ VectorNumber

    Yield Parameters:

    • unit (Any)

    Yield Returns:

    • (Any)

    Returns:

  • #transform_units(mapping) {|unit| ... } ⇒ VectorNumber

    Parameters:

    • mapping (Hash{Any => Any})

    Yield Parameters:

    • unit (Any)

    Yield Returns:

    • (Any)

    Returns:

  • #transform_unitsEnumerator

    Returns:

    • (Enumerator)

Since:

  • 0.7.0



312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/vector_number/enumerating.rb', line 312

def transform_units(mapping = nil, &transform)
  if block_given?
    # @type var transform: ^(unit_type unit) -> unit_type
    if mapping
      new(@data.transform_keys(mapping, &transform))
    else
      new(@data.transform_keys(&transform))
    end
  elsif mapping
    new(@data.transform_keys(mapping))
  else
    to_enum(:transform_units) { size } # rubocop:disable Lint/ToEnumArguments
  end
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



19
20
21
# File 'lib/vector_number/rounding.rb', line 19

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

#uniform_vectorVectorNumber

Return a new vector with the same non-zero dimensions, but with coefficients equal to 1.

Resulting vector is equal to self.subspace_basis.sum.

Examples:

v = VectorNumber[1.2, "a"] * 2 - "s" # => (2.4 + 2⋅"a" - 1⋅"s")
v.uniform_vector # => (1 + 1⋅"a" + 1⋅"s")
VectorNumber[0].uniform_vector # => (0)

Returns:

See Also:

Since:

  • 0.7.0



124
125
126
# File 'lib/vector_number/vectoring.rb', line 124

def uniform_vector
  new { 1 }
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 (Any)

Returns:

  • (Boolean)

Since:

  • 0.2.4



224
225
226
# File 'lib/vector_number/enumerating.rb', line 224

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

#unit_vectorVectorNumber

Return a unit vector (vector with magnitude 1) in the direction of this vector.

Magnitude of resulting vector may not be exactly 1 due to rounding errors.

Examples:

v = VectorNumber[1.2, "a"] * 2 - "s" # => (2.4 + 2⋅"a" - 1⋅"s")
v.unit_vector # => (0.7316529130196309 + 0.6097107608496924⋅"a" - 0.3048553804248462⋅"s")
v.unit_vector.magnitude # => 1.0
VectorNumber[0].unit_vector # ZeroDivisionError

Returns:

Raises:

  • (ZeroDivisionError)

    if self is a zero vector

Since:

  • 0.7.0



142
143
144
145
146
# File 'lib/vector_number/vectoring.rb', line 142

def unit_vector
  has_direction?

  self / magnitude
end

#unitsArray<Any> Also known as: keys

Get a list of units with non-zero coefficients.

Examples:

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

Returns:

  • (Array<Any>)

Since:

  • 0.1.0



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

def units = @data.keys

#vector_projection(other) ⇒ VectorNumber

Calculate the vector projection of this vector onto other vector.

Examples:

v = VectorNumber[2, "a"]
v.vector_projection(v) # => (2.0 + 1.0⋅"a")
v.vector_projection(1) # => (2.0)
v.vector_projection("b") # => (0)
v.vector_projection(-v) # => (-2.0 - 1.0⋅"a")
v.vector_projection(2 * VectorNumber[-0.5, "a"]) # => (0)
VectorNumber[0].vector_projection(v) # => (0)
v.vector_projection(0) # ZeroDivisionError

Parameters:

Returns:

Raises:

  • (ZeroDivisionError)

    if other is a zero vector

Since:

  • 0.7.0



385
386
387
388
389
390
391
392
# File 'lib/vector_number/vectoring.rb', line 385

def vector_projection(other)
  return self if equal?(other) && has_direction?

  other = new([other]) unless VectorNumber === other
  has_direction?(other)

  other * dot_product(other) / other.abs2
end

#vector_rejection(other) ⇒ VectorNumber

Calculate the vector rejection of this vector from other vector.

Vector rejection is equal to self - self.vector_projection(other).

Examples:

v = VectorNumber[2, "a"]
v.vector_rejection(v) # => (0)
v.vector_rejection(1) # => (1⋅"a")
v.vector_rejection("b") # => (2 + 1⋅"a")
v.vector_rejection(-v) # => (0)
v.vector_rejection(2 * VectorNumber[-0.5, "a"]) # => (2 + 1⋅"a")
VectorNumber[0].vector_rejection(v) # => (0)
v.vector_rejection(0) # ZeroDivisionError

Parameters:

Returns:

Raises:

  • (ZeroDivisionError)

    if other is a zero vector

Since:

  • 0.7.0



442
443
444
445
446
447
448
449
# File 'lib/vector_number/vectoring.rb', line 442

def vector_rejection(other)
  return VectorNumber.new if equal?(other) && has_direction?

  other = new([other]) unless VectorNumber === other
  has_direction?(other)

  self - vector_projection(other)
end

#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



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

def zero? = size.zero?