vector_number 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1f7cc1d90279440f3387287efeabb4a4c0cb40fe370b671d07623f192cccc68
4
- data.tar.gz: c89b5bf8d9ad8fef73942cd3112b42cbbcbd515ca29bd61dcad0be274c5a7b0f
3
+ metadata.gz: 686890049523273762a1dea1835f990e987d556bf9399356cf53572727df7ad9
4
+ data.tar.gz: 7c90c791c17eb9ffdcb1455174141ca857456642770acdff2e9d2ee3e19e4fab
5
5
  SHA512:
6
- metadata.gz: 199fa928aa2c46d6e5966d745def48284b1373c3cf3ab4b30046d0e39e7eaa91b0cf9beab690ac3a08fe3776f5a4f9726f341ff9f809b30766e645258eff6149
7
- data.tar.gz: 484a9f87841252e26aad41db259304713b4deb0c93369a16254c3f413f2f986756c8a894be30b7555ef69ed1264e2b73ef33b3c0fa8b7ed9b07f2a2e74fd5137
6
+ metadata.gz: d22a64381f58d7c8a1dd858510734e960326af64ca9f1d109c7fceee0f7e7fd2dfee2df7ba5b7a16fe5fc67dd1b192c60caa069215c20c03e231cee050a9acd0
7
+ data.tar.gz: 1c2de92ef2ae14f127c0fcd121ad361cb552875999a9c3eba31d90896fb104448c925793b34d47695785042a6a03420605d4ba017c91683348ac47f074cc7c86
data/README.md CHANGED
@@ -18,12 +18,7 @@ Features:
18
18
  - Enjoy a mix of vector-, complex- and polynomial-like behavior at appropriate times.
19
19
  - No dependencies, no extensions. It just works!
20
20
 
21
- Similar projects:
22
- - [vector_space](https://github.com/tomstuart/vector_space) aims to provide typed vector spaces with limited dimensions and nice formatting;
23
- - [named_vector](https://rubygems.org/gems/named_vector) provides simple vectors with named dimensions;
24
- - various quaternion libraries like [quaternion](https://github.com/tanahiro/quaternion) or [rmath3d](https://github.com/vaiorabbit/rmath3d).
25
-
26
- However, none of them have been updated in *years*.
21
+ Other people have created some similar gems over the years, like [vector_space](https://github.com/tomstuart/vector_space) or [named_vector](https://rubygems.org/gems/named_vector) but they don't have the characterics that I wanted, and none of them have been updated in *years*.
27
22
 
28
23
  ## Table of contents
29
24
 
@@ -31,10 +26,12 @@ However, none of them have been updated in *years*.
31
26
  - [Ruby engine support status](#ruby-engine-support-status)
32
27
  - [Usage](#usage)
33
28
  - [Basics](#basics)
29
+ - [Getting values back](#getting-values-back)
34
30
  - [(Somewhat) advanced usage](#somewhat-advanced-usage)
35
31
  - [Frozenness](#frozenness)
36
32
  - [Numerical behavior](#numerical-behavior)
37
33
  - [Enumeration and hash-like behavior](#enumeration-and-hash-like-behavior)
34
+ - [Conceptual basis](#conceptual-basis)
38
35
  - [Development](#development)
39
36
  - [Contributing](#contributing)
40
37
  - [License](#license)
@@ -54,8 +51,8 @@ gem "vector_number"
54
51
  ### Ruby engine support status
55
52
 
56
53
  VectorNumber is developed on MRI (CRuby) but should work on other engines too.
57
- - TruffleRuby: there are some minor differences in behavior, but otherwise works as expected.
58
- - JRuby: significant problems, but may work, currently not tested.
54
+ - TruffleRuby: minor differences in behavior, but otherwise works as expected.
55
+ - JRuby: minor differences in behavior, but otherwise works as expected.
59
56
  - Other engines: untested, but should work, depending on compatibility with MRI.
60
57
 
61
58
  ## Usage
@@ -66,12 +63,12 @@ VectorNumber is developed on MRI (CRuby) but should work on other engines too.
66
63
 
67
64
  ### Basics
68
65
 
69
- VectorNumbers are mostly useful for tallying up heterogeneous objects:
66
+ VectorNumbers are mostly useful for summing up heterogeneous objects:
70
67
  ```ruby
71
- sum = [4, "death", "death", 13, nil].reduce(VectorNumber[], :+)
72
- sum # => (17 + 2⋅'death' + 1⋅)
73
- sum.to_h # => {1=>17, "death"=>2, nil=>1}
74
- sum.to_a # => [[1, 17], ["death", 2], [nil, 1]]
68
+ sum = VectorNumber[4] + "death" + "death" + nil
69
+ sum # => (17 + 2⋅"death" + 1⋅)
70
+ sum.to_h # => {unit/1 => 17, "death" => 2, nil => 1}
71
+ sum.to_a # => [[unit/1, 17], ["death", 2], [nil, 1]]
75
72
 
76
73
  # Alternatively, the same result can be equivalently (and more efficiently)
77
74
  # achieved by passing all values to a constructor:
@@ -81,17 +78,36 @@ VectorNumber.new([4, "death", "death", 13, nil])
81
78
 
82
79
  Doing arithmetic with vectors is simple and intuitive:
83
80
  ```ruby
84
- VectorNumber["string"] + "string" # => (2⋅'string')
85
- VectorNumber["string"] - "str" # => (1⋅'string' - 1⋅'str')
86
- VectorNumber[5] + VectorNumber["string"] - 0.5 # => (4.5 + 1⋅'string')
87
- VectorNumber["string", "string", "string", "str"] # => (3⋅'string' + 1⋅'str')
81
+ VectorNumber["string"] + "string" # => (2⋅"string")
82
+ VectorNumber["string"] - "str" # => (1⋅"string" - 1⋅"str")
83
+ VectorNumber[5] + VectorNumber["string"] - 0.5 # => (4.5 + 1⋅"string")
84
+ VectorNumber["string", "string", "string", "str"] # => (3⋅"string" + 1⋅"str")
88
85
  # Multiply and divide by any real number:
89
- VectorNumber[:s] * 2 + VectorNumber["string"] * 0.3 # => (2⋅s + 0.3⋅'string')
86
+ VectorNumber[:s] * 2 + VectorNumber["string"] * 0.3 # => (2⋅s + 0.3⋅"string")
90
87
  VectorNumber[:s] / VectorNumber[3] # => (1/3⋅s)
91
- # Multiplication even works when the left operand is a regular number:
88
+ ```
89
+
90
+ Ruby numbers rely on `#coerce` to promote values to a common type. This allows using regular numbers as first operand in arithmetic operations:
91
+ ```ruby
92
+ 2 + VectorNumber["string"] # => (2 + 1⋅"string")
92
93
  1/3r * VectorNumber[[]] # => (1/3⋅[])
94
+ 13 / VectorNumber[2] # => (13/2)
95
+ ```
96
+
97
+ > [!NOTE]
98
+ > VectorNumbers don't perform "integer division" to prevent unexpected loss of precision. `#div` and rounding methods can achieve this if required.
99
+
100
+ #### Getting values back
101
+ The simplest way to get a value for a specific unit is to use the `#[]` method:
102
+ ```ruby
103
+ VectorNumber["string", "string", "string", "str"]["string"] # => 3
104
+ VectorNumber["string", "string", "string", "str"]["str"] # => 1
105
+ VectorNumber["string", "string", "string", "str"]["nonexistent"] # => 0
93
106
  ```
94
107
 
108
+ > [!NOTE]
109
+ > Accessing a unit that doesn't exist returns 0, not `nil` as you might expect.
110
+
95
111
  ### (Somewhat) advanced usage
96
112
 
97
113
  > [!TIP]
@@ -118,8 +134,17 @@ VectorNumbers implement `each` (`each_pair`) in the same way as Hash does, allow
118
134
 
119
135
  There are also the usual `[]`, `unit?` (`key?`), `units` (`keys`), `coefficients` (`values`) methods. `to_h` and `to_a` can be used if a regular Hash or Array is needed.
120
136
 
121
- > [!NOTE]
122
- > Be aware that `[]` always returns `0` for "missing" units. `unit?` will return `false` for them.
137
+ ## Conceptual basis
138
+
139
+ VectorNumbers are based on the concept of a vector space over the field of real numbers (real vector space). In the case of VectorNumber, the dimensionality of the vector space is countably infinite, as most distinct objects in Ruby signify a separate dimension.
140
+
141
+ For most dimensions, an object is that distinct dimension's unit. There are two exceptions currently: real unit (1) and imaginary unit (i) which define the real and imaginary dimensions and subsume all real and complex numbers. A VectorNumber can not be a unit itself. Distinction of objects is determined by `eql?`, same as for Hash.
142
+
143
+ Length of a VectorNumber in any given dimension is given by a real number, called its coefficient. All dimensions are linearly independent — change in one coefficient does not affect any other coefficient. There is no distinction between a dimension explicitly specified as having a 0 coefficient (or arriving at 0 through calculation) and a dimension not specified at all.
144
+
145
+ This might be more easily imagined as a geometric vector. For example, this is a graphic representation of a vector `3 * VectorNumber[1] + 2 * VectorNumber[1i] + 3 * VectorNumber["string"] + 4.5 * VectorNumber[[1,2,3]]` in the vector space:
146
+
147
+ ![Vector space](doc/vector_space.svg)
123
148
 
124
149
  ## Development
125
150
 
@@ -0,0 +1,94 @@
1
+ <svg width="500" height="400" viewBox="-250 -200 500 400" xmlns="http://www.w3.org/2000/svg">
2
+ <style>
3
+ text {
4
+ font-family: 'Arial', sans-serif;
5
+ font-size: 14px;
6
+ }
7
+ .axis-label {
8
+ font-weight: bold;
9
+ font-size: 16px;
10
+ }
11
+ .dim-label {
12
+ font-size: 12px;
13
+ fill: #666;
14
+ }
15
+ </style>
16
+
17
+ <!-- Background -->
18
+ <rect x="-250" y="-200" width="500" height="400" fill="#fff0cc"/>
19
+
20
+ <!-- Coordinate system center -->
21
+ <circle cx="0" cy="0" r="5" fill="#000"/>
22
+ <text x="-50" y="-5" class="dim-label">(0,0,...)</text>
23
+
24
+ <!-- 1 (numeric) axis -->
25
+ <line x1="0" y1="0" x2="150" y2="0" stroke="#2196F3" stroke-width="2"/>
26
+ <polygon points="155,0 145,-4 145,4" fill="#2196F3"/>
27
+ <text x="160" y="5" class="axis-label">1</text>
28
+
29
+ <!-- i (imaginary) axis -->
30
+ <line x1="0" y1="0" x2="0" y2="-150" stroke="#4CAF50" stroke-width="2"/>
31
+ <polygon points="0,-155 -4,-145 4,-145" fill="#4CAF50"/>
32
+ <text x="10" y="-145" class="axis-label">i</text>
33
+
34
+ <!-- [1,2,3] axis -->
35
+ <line x1="0" y1="0" x2="-150" y2="150" stroke="#FF9800" stroke-width="2"/>
36
+ <polygon points="-154,154 -150,144 -144,150" fill="#FF9800"/>
37
+ <text x="-205" y="140" class="axis-label">[1,2,3]</text>
38
+
39
+ <!-- "string" axis -->
40
+ <line x1="0" y1="0" x2="100" y2="150" stroke="#9C27B0" stroke-width="2"/>
41
+ <polygon points="103,155 101,144 93,149" fill="#9C27B0"/>
42
+ <text x="110" y="160" class="axis-label">"string"</text>
43
+
44
+ <!-- VectorNumber -->
45
+ <line x1="0" y1="0" x2="118" y2="-78" stroke="#F44336" stroke-width="3"/>
46
+ <polygon points="120,-80 107,-78 113,-68" fill="#F44336"/>
47
+ <text x="120" y="-90" text-anchor="middle" font-weight="bold" fill="#F44336">
48
+ (3 + 2i + 3⋅"string" + 4.5⋅[1,2,3])
49
+ </text>
50
+
51
+ <!-- Projection lines to axes -->
52
+ <!-- To numeric axis -->
53
+ <line x1="120" y1="-80" x2="120" y2="0" stroke="#2196F3" stroke-width="1" stroke-dasharray="5,5"/>
54
+ <circle cx="120" cy="0" r="3" fill="#2196F3"/>
55
+ <text x="120" y="15" class="dim-label">3</text>
56
+
57
+ <!-- To imaginary axis -->
58
+ <line x1="120" y1="-80" x2="0" y2="-80" stroke="#4CAF50" stroke-width="1" stroke-dasharray="5,5"/>
59
+ <circle cx="0" cy="-80" r="3" fill="#4CAF50"/>
60
+ <text x="-15" y="-80" class="dim-label">2</text>
61
+
62
+ <!-- To [1,2,3] axis -->
63
+ <line x1="120" y1="-80" x2="-90" y2="90" stroke="#FF9800" stroke-width="1" stroke-dasharray="5,5"/>
64
+ <circle cx="-90" cy="90" r="3" fill="#FF9800"/>
65
+ <text x="-115" y="90" class="dim-label">4.5</text>
66
+
67
+ <!-- To "string" axis -->
68
+ <line x1="120" y1="-80" x2="55" y2="83" stroke="#9C27B0" stroke-width="1" stroke-dasharray="5,5"/>
69
+ <circle cx="55" cy="83" r="3" fill="#9C27B0"/>
70
+ <text x="45" y="95" class="dim-label">3</text>
71
+
72
+ <!-- Legend -->
73
+ <rect x="-220" y="-180" width="180" height="145" fill="white" stroke="#ccc" stroke-width="1"/>
74
+
75
+ <line x1="-210" y1="-160" x2="-202" y2="-168" stroke="#F44336" stroke-width="3"/>
76
+ <polygon points="-200,-170 -205,-169 -201,-165" fill="#F44336"/>
77
+ <text x="-190" y="-160">VectorNumber</text>
78
+
79
+ <line x1="-212" y1="-145" x2="-200" y2="-145" stroke="#2196F3" stroke-width="2"/>
80
+ <text x="-190" y="-140">Real dimension</text>
81
+
82
+ <line x1="-212" y1="-125" x2="-200" y2="-125" stroke="#4CAF50" stroke-width="2"/>
83
+ <text x="-190" y="-120">Imaginary dimension</text>
84
+
85
+ <line x1="-212" y1="-105" x2="-200" y2="-105" stroke="#9C27B0" stroke-width="2"/>
86
+ <text x="-190" y="-100">"string" dimension</text>
87
+
88
+ <line x1="-212" y1="-85" x2="-200" y2="-85" stroke="#FF9800" stroke-width="2"/>
89
+ <text x="-190" y="-80">[1,2,3] dimension</text>
90
+
91
+ <line x1="-212" y1="-65" x2="-200" y2="-65" stroke="gray" stroke-width="2"/>
92
+ <text x="-190" y="-60">Zero dimensions</text>
93
+ <text x="-190" y="-45">(not portrayed)</text>
94
+ </svg>
@@ -49,8 +49,6 @@ class VectorNumber
49
49
  # and it has exactly the same units and coefficients, though possibly in a different order.
50
50
  # Additionally, `a.eql?(b)` implies `a.hash == b.hash`.
51
51
  #
52
- # Note that {#options} are not considered for equality.
53
- #
54
52
  # @example
55
53
  # VectorNumber["a", "b", "c"].eql? VectorNumber["c", "b", "a"] # => true
56
54
  # VectorNumber[3.13].eql? 3.13 # => false
@@ -60,8 +58,6 @@ class VectorNumber
60
58
  #
61
59
  # @param other [Object]
62
60
  # @return [Boolean]
63
- #
64
- # @since 0.1.0
65
61
  def eql?(other)
66
62
  return true if equal?(other)
67
63
  return false unless other.is_a?(VectorNumber)
@@ -71,12 +67,9 @@ class VectorNumber
71
67
 
72
68
  # Generate an Integer hash value for self.
73
69
  #
74
- # Hash values are stable during runtime, but not between processes.
75
- # Options are disregarded for hash calculation.
76
- #
77
70
  # @example
78
71
  # VectorNumber["b", "a"].hash # => 3081872088394655324
79
- # VectorNumber["a", "b", mult: :cross].hash # => 3081872088394655324
72
+ # VectorNumber["a", "b"].hash # => 3081872088394655324
80
73
  # VectorNumber["b", "c"].hash # => -1002381358514682371
81
74
  #
82
75
  # @return [Integer]
@@ -10,8 +10,6 @@ class VectorNumber
10
10
  # VectorNumber["a"].real # => 0
11
11
  #
12
12
  # @return [Integer, Float, Rational, BigDecimal]
13
- #
14
- # @since 0.1.0
15
13
  def real = @data[R]
16
14
 
17
15
  # Return imaginary part of the number.
@@ -21,8 +19,6 @@ class VectorNumber
21
19
  # VectorNumber["a", Complex(1, 2r)].imag # => (2/1)
22
20
  #
23
21
  # @return [Integer, Float, Rational, BigDecimal]
24
- #
25
- # @since 0.1.0
26
22
  def imaginary = @data[I]
27
23
 
28
24
  # @since 0.2.1
@@ -42,15 +38,12 @@ class VectorNumber
42
38
  #
43
39
  # @return [Integer]
44
40
  # @raise [RangeError] if any non-real part is non-zero
45
- #
46
- # @since 0.1.0
47
41
  def to_i
48
42
  raise_convert_error(Integer) unless numeric?(1)
49
43
 
50
44
  real.to_i
51
45
  end
52
46
 
53
- # @since 0.1.0
54
47
  alias to_int to_i
55
48
 
56
49
  # Return value as a Float if only real part is non-zero.
@@ -66,8 +59,6 @@ class VectorNumber
66
59
  #
67
60
  # @return [Float]
68
61
  # @raise [RangeError] if any non-real part is non-zero
69
- #
70
- # @since 0.1.0
71
62
  def to_f
72
63
  raise_convert_error(Float) unless numeric?(1)
73
64
 
@@ -87,8 +78,6 @@ class VectorNumber
87
78
  #
88
79
  # @return [Rational]
89
80
  # @raise [RangeError] if any non-real part is non-zero
90
- #
91
- # @since 0.1.0
92
81
  def to_r
93
82
  raise_convert_error(Rational) unless numeric?(1)
94
83
 
@@ -117,8 +106,6 @@ class VectorNumber
117
106
  #
118
107
  # @see Kernel.BigDecimal
119
108
  # @see NumericRefinements
120
- #
121
- # @since 0.1.0
122
109
  def to_d(ndigits = nil)
123
110
  raise_convert_error(BigDecimal) unless numeric?(1)
124
111
 
@@ -141,8 +128,6 @@ class VectorNumber
141
128
  #
142
129
  # @return [Complex]
143
130
  # @raise [RangeError] if any non-real, non-imaginary part is non-zero
144
- #
145
- # @since 0.1.0
146
131
  def to_c
147
132
  raise_convert_error(Complex) unless numeric?(2)
148
133
 
@@ -7,8 +7,6 @@ class VectorNumber
7
7
  # @example using Enumerable methods
8
8
  # VectorNumber["a", "b", 6].include?(["a", 1]) # => true
9
9
  # VectorNumber["a", "b", 6].select { |u, c| u.is_a?(String) } # => [["a", 1], ["b", 1]]
10
- #
11
- # @since 0.1.0
12
10
  include ::Enumerable
13
11
 
14
12
  # Iterate through every pair of unit and coefficient.
@@ -34,8 +32,6 @@ class VectorNumber
34
32
  #
35
33
  # @see Enumerable
36
34
  # @see Enumerator
37
- #
38
- # @since 0.1.0
39
35
  def each(&block)
40
36
  return to_enum { size } unless block_given?
41
37
 
@@ -43,7 +39,6 @@ class VectorNumber
43
39
  self
44
40
  end
45
41
 
46
- # @since 0.1.0
47
42
  alias each_pair each
48
43
 
49
44
  # Get a list of units with non-zero coefficients.
@@ -53,11 +48,8 @@ class VectorNumber
53
48
  # VectorNumber.new.keys # => []
54
49
  #
55
50
  # @return [Array<Object>]
56
- #
57
- # @since 0.1.0
58
51
  def units = @data.keys
59
52
 
60
- # @since 0.1.0
61
53
  alias keys units
62
54
 
63
55
  # Get a list of non-zero coefficients.
@@ -67,11 +59,8 @@ class VectorNumber
67
59
  # VectorNumber.new.values # => []
68
60
  #
69
61
  # @return [Array<Numeric>]
70
- #
71
- # @since 0.1.0
72
62
  def coefficients = @data.values
73
63
 
74
- # @since 0.1.0
75
64
  alias values coefficients
76
65
 
77
66
  # Get mutable hash with vector's data.
@@ -83,8 +72,6 @@ class VectorNumber
83
72
  # VectorNumber["a", "b", 6].to_h["c"] # => 0
84
73
  #
85
74
  # @return [Hash{Object => Numeric}]
86
- #
87
- # @since 0.1.0
88
75
  def to_h(&block)
89
76
  # TODO: Remove block argument.
90
77
  if block_given?
@@ -38,8 +38,8 @@ class VectorNumber
38
38
  # @example
39
39
  # VectorNumber[5.39].truncate # => (5)
40
40
  # VectorNumber[-5.35i].truncate # => (-5i)
41
- # VectorNumber[-5.35i, "i"].truncate # => (-5i + 1⋅'i')
42
- # VectorNumber[-5.35i, "i"].truncate(1) # => (-5.3i + 1⋅'i')
41
+ # VectorNumber[-5.35i, "i"].truncate # => (-5i + 1⋅"i")
42
+ # VectorNumber[-5.35i, "i"].truncate(1) # => (-5.3i + 1⋅"i")
43
43
  # VectorNumber[-5.35i, "i"].truncate(-1) # => (0)
44
44
  #
45
45
  # @param digits [Integer]
@@ -55,9 +55,9 @@ class VectorNumber
55
55
  # @example
56
56
  # VectorNumber[5.39].ceil # => (6)
57
57
  # VectorNumber[-5.35i].ceil # => (-5i)
58
- # VectorNumber[-5.35i, "i"].ceil # => (-5i + 1⋅'i')
59
- # VectorNumber[-5.35i, "i"].ceil(1) # => (-5.3i + 1⋅'i')
60
- # VectorNumber[-5.35i, "i"].ceil(-1) # => (10⋅'i')
58
+ # VectorNumber[-5.35i, "i"].ceil # => (-5i + 1⋅"i")
59
+ # VectorNumber[-5.35i, "i"].ceil(1) # => (-5.3i + 1⋅"i")
60
+ # VectorNumber[-5.35i, "i"].ceil(-1) # => (10⋅"i")
61
61
  #
62
62
  # @param digits [Integer]
63
63
  # @return [VectorNumber]
@@ -72,8 +72,8 @@ class VectorNumber
72
72
  # @example
73
73
  # VectorNumber[5.39].floor # => (5)
74
74
  # VectorNumber[-5.35i].floor # => (-6i)
75
- # VectorNumber[-5.35i, "i"].floor # => (-6i + 1⋅'i')
76
- # VectorNumber[-5.35i, "i"].floor(1) # => (-5.4i + 1⋅'i')
75
+ # VectorNumber[-5.35i, "i"].floor # => (-6i + 1⋅"i")
76
+ # VectorNumber[-5.35i, "i"].floor(1) # => (-5.4i + 1⋅"i")
77
77
  # VectorNumber[-5.35i, "i"].floor(-1) # => (-10i)
78
78
  #
79
79
  # @param digits [Integer]
@@ -92,11 +92,11 @@ class VectorNumber
92
92
  # Other modes can not be specified.
93
93
  #
94
94
  # @example
95
- # VectorNumber[-4.5i, "i"].round(half: :up) # => (-5i + 1⋅'i')
96
- # VectorNumber[-4.5i, "i"].round(half: :even) # => (-4i + 1⋅'i')
97
- # VectorNumber[-5.5i, "i"].round(half: :even) # => (-6i + 1⋅'i')
98
- # VectorNumber[-5.5i, "i"].round(half: :down) # => (-5i + 1⋅'i')
99
- # VectorNumber[-5.35i, "i"].round(1) # => (-5.4i + 1⋅'i')
95
+ # VectorNumber[-4.5i, "i"].round(half: :up) # => (-5i + 1⋅"i")
96
+ # VectorNumber[-4.5i, "i"].round(half: :even) # => (-4i + 1⋅"i")
97
+ # VectorNumber[-5.5i, "i"].round(half: :even) # => (-6i + 1⋅"i")
98
+ # VectorNumber[-5.5i, "i"].round(half: :down) # => (-5i + 1⋅"i")
99
+ # VectorNumber[-5.35i, "i"].round(1) # => (-5.4i + 1⋅"i")
100
100
  # VectorNumber[-5.35i, "i"].round(-1) # => (-10i)
101
101
  #
102
102
  # @param digits [Integer]
@@ -11,12 +11,12 @@ class VectorNumber
11
11
  # Unlike other numeric types, VectorNumber can coerce *anything*.
12
12
  #
13
13
  # @example
14
- # VectorNumber["a"].coerce(5) # => [(5), (1⋅'a')]
14
+ # VectorNumber["a"].coerce(5) # => [(5), (1⋅"a")]
15
15
  # VectorNumber[7].coerce([]) # => [(1⋅[]), (7)]
16
- # VectorNumber["a"] + 5 # => (1⋅'a' + 5)
16
+ # VectorNumber["a"] + 5 # => (1⋅"a" + 5)
17
17
  # # Direct reverse coercion doesn't work, but Numeric types know how to call #coerce:
18
18
  # 5.coerce(VectorNumber["a"]) # RangeError
19
- # 5 + VectorNumber["a"] # => (5 + 1⋅'a')
19
+ # 5 + VectorNumber["a"] # => (5 + 1⋅"a")
20
20
  #
21
21
  # @param other [Object]
22
22
  # @return [Array(VectorNumber, VectorNumber)]
@@ -34,8 +34,8 @@ class VectorNumber
34
34
  # Return new vector with negated coefficients (additive inverse).
35
35
  #
36
36
  # @example
37
- # -VectorNumber[12, "i"] # => (-12 - 1⋅'i')
38
- # VectorNumber["a", "b", "a"].neg # => (-2⋅'a' - 1⋅'b')
37
+ # -VectorNumber[12, "i"] # => (-12 - 1⋅"i")
38
+ # VectorNumber["a", "b", "a"].neg # => (-2⋅"a" - 1⋅"b")
39
39
  # -VectorNumber["a"] + VectorNumber["a"] # => (0)
40
40
  #
41
41
  # @return [VectorNumber]
@@ -53,11 +53,11 @@ class VectorNumber
53
53
  #
54
54
  # @example
55
55
  # VectorNumber[5] + 10 # => (15)
56
- # VectorNumber["a"].add(VectorNumber["b"]) # => (1⋅'a' + 1⋅'b')
57
- # VectorNumber["a"] + "b" # => (1⋅'a' + 1⋅'b')
56
+ # VectorNumber["a"].add(VectorNumber["b"]) # => (1⋅"a" + 1⋅"b")
57
+ # VectorNumber["a"] + "b" # => (1⋅"a" + 1⋅"b")
58
58
  # @example numeric types can be added in reverse
59
59
  # 10 + VectorNumber[5] # => (15)
60
- # 10 + VectorNumber["a"] # => (10 + 1⋅'a')
60
+ # 10 + VectorNumber["a"] # => (10 + 1⋅"a")
61
61
  #
62
62
  # @param other [Object]
63
63
  # @return [VectorNumber]
@@ -76,11 +76,11 @@ class VectorNumber
76
76
  #
77
77
  # @example
78
78
  # VectorNumber[5] - 3 # => (2)
79
- # VectorNumber["a"].sub(VectorNumber["b"]) # => (1⋅'a' - 1⋅'b')
80
- # VectorNumber["a"] - "b" # => (1⋅'a' - 1⋅'b')
79
+ # VectorNumber["a"].sub(VectorNumber["b"]) # => (1⋅"a" - 1⋅"b")
80
+ # VectorNumber["a"] - "b" # => (1⋅"a" - 1⋅"b")
81
81
  # @example numeric types can be subtracted in reverse
82
82
  # 3 - VectorNumber[5] # => (-2)
83
- # 3 - VectorNumber["a"] # => (3 - 1⋅'a')
83
+ # 3 - VectorNumber["a"] # => (3 - 1⋅"a")
84
84
  #
85
85
  # @param other [Object]
86
86
  # @return [VectorNumber]
@@ -99,13 +99,13 @@ class VectorNumber
99
99
  #
100
100
  # @example
101
101
  # VectorNumber[5] * 2 # => (10)
102
- # VectorNumber["a", "b", 6].mult(2) # => (2⋅'a' + 2⋅'b' + 12)
103
- # VectorNumber["a"] * VectorNumber[2] # => (2⋅'a')
102
+ # VectorNumber["a", "b", 6].mult(2) # => (2⋅"a" + 2⋅"b" + 12)
103
+ # VectorNumber["a"] * VectorNumber[2] # => (2⋅"a")
104
104
  # # Can't multiply by a non-real:
105
105
  # VectorNumber["a"] * VectorNumber["b"] # RangeError
106
106
  # @example numeric types can be multiplied in reverse
107
107
  # 2 * VectorNumber[5] # => (10)
108
- # 2 * VectorNumber["a"] # => (2⋅'a')
108
+ # 2 * VectorNumber["a"] # => (2⋅"a")
109
109
  #
110
110
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
111
111
  # @return [VectorNumber]
@@ -135,8 +135,8 @@ class VectorNumber
135
135
  #
136
136
  # @example
137
137
  # VectorNumber[10] / 2 # => (5)
138
- # VectorNumber["a", "b", 6].quo(2) # => (1/2⋅'a' + 1/2⋅'b' + 3/1)
139
- # VectorNumber["a"] / VectorNumber[2] # => (1/2⋅'a')
138
+ # VectorNumber["a", "b", 6].quo(2) # => (1/2⋅"a" + 1/2⋅"b" + 3/1)
139
+ # VectorNumber["a"] / VectorNumber[2] # => (1/2⋅"a")
140
140
  # # Can't divide by a non-real:
141
141
  # VectorNumber["a"] / VectorNumber["b"] # RangeError
142
142
  # @example numeric types can be divided in reverse
@@ -172,8 +172,8 @@ class VectorNumber
172
172
  #
173
173
  # @example
174
174
  # VectorNumber[10].fdiv(2) # => (5.0)
175
- # VectorNumber["a", "b", 6].fdiv(2) # => (0.5⋅'a' + 0.5⋅'b' + 3.0)
176
- # VectorNumber["a"].fdiv(VectorNumber[2]) # => (0.5⋅'a')
175
+ # VectorNumber["a", "b", 6].fdiv(2) # => (0.5⋅"a" + 0.5⋅"b" + 3.0)
176
+ # VectorNumber["a"].fdiv(VectorNumber[2]) # => (0.5⋅"a")
177
177
  # # Can't divide by a non-real:
178
178
  # VectorNumber["a"].fdiv(VectorNumber["b"]) # RangeError
179
179
  # @example reverse division may return non-vector results
@@ -200,8 +200,8 @@ class VectorNumber
200
200
  #
201
201
  # @example
202
202
  # VectorNumber[10].div(3) # => (3)
203
- # VectorNumber["a"].div(2) # => (0⋅'a')
204
- # VectorNumber["a"].div(VectorNumber[2]) # => (0⋅'a')
203
+ # VectorNumber["a"].div(2) # => (0)
204
+ # VectorNumber["a"].div(VectorNumber[2]) # => (0)
205
205
  # # Can't divide by a non-real:
206
206
  # VectorNumber["a"].div(VectorNumber["b"]) # RangeError
207
207
  # @example numeric types can be divided in reverse
@@ -233,8 +233,8 @@ class VectorNumber
233
233
  #
234
234
  # @example
235
235
  # VectorNumber[10] % 3 # => (1)
236
- # VectorNumber["a", "b", 6].modulo(2) # => (1⋅'a' + 1⋅'b')
237
- # -VectorNumber["a"] % VectorNumber[2] # => (1⋅'a')
236
+ # VectorNumber["a", "b", 6].modulo(2) # => (1⋅"a" + 1⋅"b")
237
+ # -VectorNumber["a"] % VectorNumber[2] # => (1⋅"a")
238
238
  # # Can't divide by a non-real:
239
239
  # VectorNumber["a"] % VectorNumber["b"] # RangeError
240
240
  # @example numeric types can be divided in reverse
@@ -271,8 +271,8 @@ class VectorNumber
271
271
  #
272
272
  # @example
273
273
  # VectorNumber[10].divmod(3) # => [(3), (1)]
274
- # VectorNumber["a"].divmod(2) # => [(0⋅'a'), (1⋅'a')]
275
- # VectorNumber["a"].divmod(VectorNumber[2]) # => [(0⋅'a'), (1⋅'a')]
274
+ # VectorNumber["a"].divmod(2) # => [(0), (1⋅"a")]
275
+ # VectorNumber["a"].divmod(VectorNumber[2]) # => [(0), (1⋅"a")]
276
276
  # # Can't divide by a non-real:
277
277
  # VectorNumber["a"].divmod(VectorNumber["b"]) # RangeError
278
278
  # @example numeric types can be divided in reverse
@@ -299,8 +299,8 @@ class VectorNumber
299
299
  #
300
300
  # @example
301
301
  # VectorNumber[10].remainder(3) # => (1)
302
- # VectorNumber["a"].remainder(2) # => (1⋅'a')
303
- # -VectorNumber["a"].remainder(VectorNumber[2]) # => (-1⋅'a')
302
+ # VectorNumber["a"].remainder(2) # => (1⋅"a")
303
+ # -VectorNumber["a"].remainder(VectorNumber[2]) # => (-1⋅"a")
304
304
  # # Can't divide by a non-real:
305
305
  # VectorNumber["a"].remainder(VectorNumber["b"]) # RangeError
306
306
  # @example numeric types can be divided in reverse
@@ -5,7 +5,10 @@ class VectorNumber
5
5
  #
6
6
  # Mostly modelled after {::Complex}.
7
7
 
8
- # Whether this VectorNumber can be considered strictly numeric real or complex.
8
+ # Returns +true+ if all non-zero dimensions in this VectorNumber are numeric (real or complex),
9
+ # and +false+ otherwise.
10
+ #
11
+ # This is exactly the opposite of {#nonnumeric?}.
9
12
  #
10
13
  # @example
11
14
  # VectorNumber[2].numeric? # => true
@@ -16,7 +19,7 @@ class VectorNumber
16
19
  # @param dimensions [Integer] number of dimensions to consider "numeric"
17
20
  # - 0 — zero
18
21
  # - 1 — real number
19
- # - 2 — complex number, etc.
22
+ # - 2 — complex number
20
23
  # @return [Boolean]
21
24
  # @raise [ArgumentError] if +dimensions+ is negative
22
25
  #
@@ -24,10 +27,14 @@ class VectorNumber
24
27
  def numeric?(dimensions = 2)
25
28
  raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0
26
29
 
27
- size <= dimensions && (1..dimensions).count { @data[UNIT[_1]].nonzero? } == size
30
+ size <= dimensions &&
31
+ (0...dimensions).count { (unit = NUMERIC_UNITS[_1]) && @data[unit].nonzero? } == size
28
32
  end
29
33
 
30
- # Whether this VectorNumber contains any non-numeric parts.
34
+ # Returns +true+ if this VectorNumber contains any non-zero dimensions with non-numeric units,
35
+ # and +false+ otherwise.
36
+ #
37
+ # This is exactly the opposite of {#numeric?}.
31
38
  #
32
39
  # @example
33
40
  # VectorNumber[2].nonnumeric? # => false
@@ -50,8 +57,6 @@ class VectorNumber
50
57
  # VectorNumber["a"].mult(Float::INFINITY).finite? # => false
51
58
  #
52
59
  # @return [Boolean]
53
- #
54
- # @since 0.1.0
55
60
  def finite?
56
61
  all? { |_u, v| v.finite? }
57
62
  end
@@ -66,8 +71,6 @@ class VectorNumber
66
71
  # VectorNumber["a"].mult(-Float::INFINITY).infinite? # => 1
67
72
  #
68
73
  # @return [1, nil]
69
- #
70
- # @since 0.1.0
71
74
  def infinite?
72
75
  finite? ? nil : 1 # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
73
76
  end
@@ -83,8 +86,6 @@ class VectorNumber
83
86
  # @see #size
84
87
  #
85
88
  # @return [Boolean]
86
- #
87
- # @since 0.1.0
88
89
  def zero? = size.zero?
89
90
 
90
91
  # Returns +self+ if there are any non-zero coefficients, +nil+ otherwise.
@@ -93,14 +94,12 @@ class VectorNumber
93
94
  # Behavior of returning self or +nil+ is the same as +Numeric+'s.
94
95
  #
95
96
  # @example
96
- # VectorNumber["ab", "cd"].nonzero? # => (1⋅'ab' + 1⋅'cd')
97
+ # VectorNumber["ab", "cd"].nonzero? # => (1⋅"ab" + 1⋅"cd")
97
98
  # VectorNumber[].nonzero? # => nil
98
99
  #
99
100
  # @see #size
100
101
  #
101
102
  # @return [VectorNumber, nil]
102
- #
103
- # @since 0.1.0
104
103
  def nonzero?
105
104
  zero? ? nil : self # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
106
105
  end
@@ -115,8 +114,6 @@ class VectorNumber
115
114
  # VectorNumber[0].positive? # => false
116
115
  #
117
116
  # @return [Boolean]
118
- #
119
- # @since 0.1.0
120
117
  def positive?
121
118
  !zero? && all? { |_u, c| c.positive? }
122
119
  end
@@ -131,8 +128,6 @@ class VectorNumber
131
128
  # VectorNumber[0].negative? # => false
132
129
  #
133
130
  # @return [Boolean]
134
- #
135
- # @since 0.1.0
136
131
  def negative?
137
132
  !zero? && all? { |_u, c| c.negative? }
138
133
  end
@@ -144,8 +139,6 @@ class VectorNumber
144
139
  # @see #numeric?
145
140
  #
146
141
  # @return [false]
147
- #
148
- # @since 0.1.0
149
142
  def real? = false
150
143
 
151
144
  # Always returns +false+, as vectors are not +Integer+s.
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class VectorNumber
4
+ # Class for representing special units.
5
+ #
6
+ # The public API consists of:
7
+ # - +#==+/+#eql?+/+#equal?+ (from +Object+)
8
+ # - +#hash+ (from +Object+)
9
+ # - {#to_s}
10
+ # - {#inspect}
11
+ #
12
+ # @since 0.6.0
13
+ class SpecialUnit
14
+ # @api private
15
+ # @param unit [#to_s] name for {#inspect}
16
+ # @param text [String] text for {#to_s}
17
+ def initialize(unit, text)
18
+ @unit = unit
19
+ @text = text
20
+ end
21
+
22
+ # Get predefined string representation of the unit.
23
+ #
24
+ # @return [String]
25
+ def to_s
26
+ @text
27
+ end
28
+
29
+ # Get string representation of the unit for debugging.
30
+ #
31
+ # @return [String]
32
+ def inspect
33
+ "unit/#{@unit}"
34
+ end
35
+ end
36
+ end
@@ -4,8 +4,6 @@ class VectorNumber
4
4
  # Predefined symbols for multiplication to display between unit and coefficient.
5
5
  #
6
6
  # @return [Hash{Symbol => String}]
7
- #
8
- # @since 0.1.0
9
7
  MULT_STRINGS = {
10
8
  asterisk: "*", # U+002A
11
9
  cross: "×", # U+00D7
@@ -19,35 +17,42 @@ class VectorNumber
19
17
 
20
18
  # Return string representation of the vector.
21
19
  #
20
+ # An optional block can be supplied to provide customized substrings
21
+ # for each unit and coefficient pair.
22
+ # Care needs to be taken in handling +VectorNumber::R+ and +VectorNumber::I+ units.
23
+ # {.numeric_unit?} can be used to check if a particular unit needs special handling.
24
+ #
22
25
  # @example
23
- # VectorNumber[5, "s"].to_s # => "5 + 1⋅'s'"
24
- # VectorNumber["s", 5].to_s # => "1⋅'s' + 5"
26
+ # VectorNumber[5, "s"].to_s # => "5 + 1⋅\"s\""
27
+ # VectorNumber["s", 5].to_s # => "1⋅\"s\" + 5"
25
28
  # @example with :mult argument
26
- # VectorNumber[5, "s"].to_s(mult: :asterisk) # => "5 + 1*'s'"
27
- # @example :mult option specified for the vector
28
- # VectorNumber[5, "s", mult: :none].to_s # => "5 + 1's'"
29
+ # VectorNumber[5, :s].to_s(mult: :asterisk) # => "5 + 1*s"
30
+ # (-VectorNumber[5, :s]).to_s(mult: "~~~") # => "-5 - 1~~~s"
31
+ # @example with a block
32
+ # VectorNumber[5, :s].to_s { |k, v| "#{format("%+.0f", v)}%#{k}" } # => "+5%1+1%s"
33
+ # VectorNumber[5, :s].to_s(mult: :cross) { |k, v, i, op|
34
+ # "#{',' unless i.zero?}#{v}#{op+k.to_s unless k == VectorNumber::R}"
35
+ # } # => "5,1×s"
29
36
  #
30
37
  # @param mult [Symbol, String]
31
38
  # text to use between coefficient and unit,
32
39
  # can be one of the keys in {MULT_STRINGS} or an arbitrary string
40
+ # @yieldparam unit [Object]
41
+ # @yieldparam coefficient [Numeric]
42
+ # @yieldparam index [Integer]
43
+ # @yieldparam operator [String]
44
+ # @yieldreturn [String] a string for this unit and coefficient
33
45
  # @return [String]
34
46
  # @raise [ArgumentError]
35
47
  # if +mult+ is not a String and is not in {MULT_STRINGS}'s keys
36
- #
37
- # @since 0.1.0
38
- def to_s(mult: options[:mult])
48
+ def to_s(mult: :dot, &block)
49
+ if !mult.is_a?(String) && !MULT_STRINGS.key?(mult)
50
+ raise ArgumentError, "unknown key #{mult.inspect}", caller
51
+ end
39
52
  return "0" if zero?
40
53
 
41
- result = +""
42
- each_with_index do |(unit, coefficient), index|
43
- if index.zero?
44
- result << "-" if coefficient.negative?
45
- else
46
- result << (coefficient.positive? ? " + " : " - ")
47
- end
48
- result << value_to_s(unit, coefficient.abs, mult: mult)
49
- end
50
- result
54
+ operator = mult.is_a?(String) ? mult : MULT_STRINGS[mult]
55
+ build_string(operator, &block)
51
56
  end
52
57
 
53
58
  # Return string representation of the vector.
@@ -55,38 +60,45 @@ class VectorNumber
55
60
  # This is similar to +Complex#inspect+: it returns result of {#to_s} in round brackets.
56
61
  #
57
62
  # @example
58
- # VectorNumber[5, "s"].inspect # => "(5 + 1⋅'s')"
63
+ # VectorNumber[5, :s].inspect # => "(5 + 1⋅s)"
59
64
  #
60
65
  # @return [String]
61
66
  #
62
67
  # @see to_s
63
- #
64
- # @since 0.1.0
65
68
  def inspect
66
- # TODO: Probably make this independent of options.
67
69
  "(#{self})"
68
70
  end
69
71
 
70
72
  private
71
73
 
72
- # @param unit [Object]
73
- # @param coefficient [Numeric]
74
- # @param mult [Symbol, String]
74
+ # @param operator [String]
75
75
  # @return [String]
76
- # @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
77
- def value_to_s(unit, coefficient, mult:)
78
- if !mult.is_a?(String) && !MULT_STRINGS.key?(mult)
79
- raise ArgumentError, "unknown key #{mult.inspect}", caller
76
+ def build_string(operator)
77
+ result = +""
78
+ each_with_index do |(unit, coefficient), index|
79
+ if block_given?
80
+ result << (yield unit, coefficient, index, operator).to_s
81
+ else
82
+ if index.zero?
83
+ result << "-" if coefficient.negative?
84
+ else
85
+ result << (coefficient.positive? ? " + " : " - ")
86
+ end
87
+ result << value_to_s(unit, coefficient.abs, operator)
88
+ end
80
89
  end
90
+ result
91
+ end
81
92
 
82
- case unit
83
- when R
84
- coefficient.to_s
85
- when I
86
- "#{coefficient}i"
93
+ # @param unit [Object]
94
+ # @param coefficient [Numeric]
95
+ # @param operator [String]
96
+ # @return [String]
97
+ def value_to_s(unit, coefficient, operator)
98
+ if NUMERIC_UNITS.include?(unit)
99
+ "#{coefficient}#{unit}"
87
100
  else
88
- unit = "'#{unit}'" if unit.is_a?(String)
89
- operator = mult.is_a?(String) ? mult : MULT_STRINGS[mult]
101
+ unit = unit.inspect if unit.is_a?(String)
90
102
  "#{coefficient}#{operator}#{unit}"
91
103
  end
92
104
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  class VectorNumber
4
4
  # @return [String]
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.0"
6
6
  end
data/lib/vector_number.rb CHANGED
@@ -73,12 +73,14 @@
73
73
  # - {#to_h}: convert to Hash
74
74
  #
75
75
  # **Miscellaneous** **methods**
76
- # - {#size}: number of non-zero coefficients
77
- # - {#options}: hash of options
78
- # - {#dup}/{#+}: return self
79
- # - {#clone}: return self
76
+ # - {.numeric_unit?}: check if a unit represents a numeric dimension
77
+ # - {#size}: number of non-zero dimensions
80
78
  # - {#to_s}: return string representation suitable for printing
81
79
  # - {#inspect}: return string representation suitable for display
80
+ # - {#dup}/{#+}: return self
81
+ # - {#clone}: return self
82
+ #
83
+ # @since 0.1.0
82
84
  class VectorNumber
83
85
  require_relative "vector_number/comparing"
84
86
  require_relative "vector_number/converting"
@@ -89,82 +91,73 @@ class VectorNumber
89
91
  require_relative "vector_number/stringifying"
90
92
  require_relative "vector_number/version"
91
93
 
92
- # Keys for possible options.
93
- # Unknown options will be rejected when creating a vector.
94
- #
95
- # @return [Array<Symbol>]
96
- #
97
- # @since 0.2.0
98
- KNOWN_OPTIONS = %i[mult].freeze
94
+ require_relative "vector_number/special_unit"
99
95
 
100
- # Default values for options.
96
+ # List of special numeric unit constants.
101
97
  #
102
- # @return [Hash{Symbol => Object}]
98
+ # @since 0.6.0
99
+ NUMERIC_UNITS = [SpecialUnit.new("1", "").freeze, SpecialUnit.new("i", "i").freeze].freeze
100
+ # Constant for real unit (1).
103
101
  #
104
- # @since 0.2.0
105
- DEFAULT_OPTIONS = { mult: :dot }.freeze
106
-
107
- # Get a unit for +n+th numeric dimension, where 1 is real, 2 is imaginary.
102
+ # Its string representation is an empty string.
108
103
  #
109
- # @since 0.2.0
110
- UNIT = ->(n) { n }.freeze
111
- # Constant for real unit.
104
+ # @since 0.6.0
105
+ R = NUMERIC_UNITS[0]
106
+ # Constant for imaginary unit (i).
112
107
  #
113
- # @since 0.2.0
114
- R = UNIT[1]
115
- # Constant for imaginary unit.
108
+ # Its string representation is "i".
116
109
  #
117
- # @since 0.1.0
118
- I = UNIT[2]
110
+ # @since 0.6.0
111
+ I = NUMERIC_UNITS[1]
119
112
 
120
113
  # @group Creation
121
- # Create new VectorNumber from a list of values, possibly specifying options.
114
+ # Create new VectorNumber from a list of values.
122
115
  #
123
116
  # @example
124
117
  # VectorNumber[1, 2, 3] # => (6)
125
118
  # VectorNumber[[1, 2, 3]] # => (1⋅[1, 2, 3])
126
- # VectorNumber["b", VectorNumber::I, mult: :asterisk] # => (1*'b' + 1i)
127
119
  # VectorNumber[] # => (0)
128
120
  # VectorNumber["b", VectorNumber["b"]] # => (2⋅'b')
129
121
  # VectorNumber["a", "b", "a"] # => (2⋅'a' + 1⋅'b')
130
122
  #
131
123
  # @param values [Array<Object>] values to put in the number
132
- # @param options [Hash{Symbol => Object}] options for the number
133
- # @option options [Symbol, String] :mult Multiplication symbol,
134
- # either a key from {MULT_STRINGS} or a literal string to use
135
- # @return [VectorNumber]
136
- #
137
- # @since 0.1.0
138
- def self.[](*values, **options)
139
- new(values, options)
124
+ def self.[](*values, **nil)
125
+ new(values)
140
126
  end
141
127
  # @endgroup
142
128
 
143
- # Number of non-zero dimensions.
144
- #
145
- # @return [Integer]
129
+ # Check if an object is a unit representing a numeric dimension
130
+ # (real or imaginary unit).
146
131
  #
147
- # @since 0.1.0
148
- attr_reader :size
149
-
150
- # Options used for this vector.
132
+ # @example
133
+ # VectorNumber.numeric_unit?(VectorNumber::R) # => true
134
+ # VectorNumber.numeric_unit?(VectorNumber::I) # => true
135
+ # VectorNumber.numeric_unit?(:i) # => false
151
136
  #
152
- # @see KNOWN_OPTIONS
137
+ # @param unit [Object]
138
+ # @return [Boolean]
153
139
  #
154
- # @return [Hash{Symbol => Object}]
140
+ # @since 0.6.0
141
+ def self.numeric_unit?(unit)
142
+ NUMERIC_UNITS.include?(unit)
143
+ end
144
+
145
+ # Number of non-zero dimensions.
155
146
  #
156
- # @since 0.1.0
157
- attr_reader :options
147
+ # @return [Integer]
148
+ attr_reader :size
158
149
 
159
150
  # @group Creation
160
- # Create new VectorNumber from +values+, possibly specifying +options+,
151
+ # Create new VectorNumber from an array of +values+,
161
152
  # possibly modifying coefficients with a block.
162
153
  #
154
+ # Using +VectorNumber.new([values...])+ directly is more efficient than +VectorNumber[values...]+.
155
+ #
163
156
  # +values+ can be:
164
157
  # - an array of values (see {.[]});
165
158
  # - a VectorNumber to copy;
166
159
  # - a hash in the format returned by {#to_h};
167
- # - +nil+ to specify a 0-dimensional vector (same as an empty array or hash).
160
+ # - +nil+ to specify a 0-sized vector (same as an empty array or hash).
168
161
  #
169
162
  # Using a hash as +values+ is an advanced technique which allows to quickly
170
163
  # construct a VectorNumber with desired units and coefficients,
@@ -174,32 +167,24 @@ class VectorNumber
174
167
  # @example
175
168
  # VectorNumber.new(1, 2, 3) # ArgumentError
176
169
  # VectorNumber.new([1, 2, 3]) # => (6)
177
- # VectorNumber.new(["b", VectorNumber::I], mult: :asterisk) # => (1*'b' + 1i)
170
+ # VectorNumber.new(["b", VectorNumber::I]) # => (1⋅"b" + 1i)
178
171
  # VectorNumber.new # => (0)
179
172
  # @example with a block
180
- # VectorNumber.new(["a", "b", "c", 3]) { _1 * 2 } # => (2⋅'a' + 2⋅'b' + 2⋅'c' + 6)
181
- # VectorNumber.new(["a", "b", "c", 3], &:-@) # => (-1⋅'a' - 1⋅'b' - 1⋅'c' - 3)
173
+ # VectorNumber.new(["a", "b", "c", 3]) { _1 * 2 } # => (2⋅"a" + 2⋅"b" + 2⋅"c" + 6)
174
+ # VectorNumber.new(["a", "b", "c", 3], &:-@) # => (-1⋅"a" - 1⋅"b" - 1⋅"c" - 3)
182
175
  # VectorNumber.new(["a", "b", "c", 3], &:digits) # RangeError
183
176
  # @example using hash for values
184
- # v = VectorNumber.new({1 => 15, "a" => 3.4, nil => -3}) # => (15 + 3.4⋅'a' - 3⋅)
177
+ # v = VectorNumber.new({1 => 15, "a" => 3.4, nil => -3}) # => (15 + 3.4⋅"a" - 3⋅)
185
178
  # v.to_h # => {1 => 15, "a" => 3.4, nil => -3}
186
179
  #
187
180
  # @param values [Array, VectorNumber, Hash{Object => Numeric}, nil] values for this vector
188
- # @param options [Hash{Symbol => Object}, nil]
189
- # options for this vector, if +values+ is a VectorNumber or contains it,
190
- # these will be merged with options from its +options+
191
- # @option options [Symbol, String] :mult Multiplication symbol,
192
- # either a key from {MULT_STRINGS} or a literal string to use
193
181
  # @yieldparam coefficient [Numeric] a real number
194
182
  # @yieldreturn [Numeric] new coefficient
195
183
  # @raise [RangeError] if a block is used and it returns a non-number or non-real number
196
- def initialize(values = nil, options = nil, &transform)
197
- # @type var options: Hash[Symbol, Object]
184
+ def initialize(values = nil, **nil, &transform)
198
185
  initialize_from(values)
199
186
  apply_transform(&transform)
200
187
  finalize_contents
201
- save_options(options, values)
202
- @options.freeze
203
188
  @data.freeze
204
189
  freeze
205
190
  end
@@ -241,7 +226,7 @@ class VectorNumber
241
226
  # @yieldreturn [Numeric] new coefficient
242
227
  # @return [VectorNumber]
243
228
  def new(from = self, &transform)
244
- self.class.new(from, options, &transform)
229
+ self.class.new(from, &transform)
245
230
  end
246
231
 
247
232
  # Check if +other+ is a real number.
@@ -319,32 +304,6 @@ class VectorNumber
319
304
  end
320
305
  end
321
306
 
322
- # @param options [Hash{Symbol => Object}, nil]
323
- # @param values [Object] initializing object
324
- # @return [void]
325
- def save_options(options, values)
326
- @options =
327
- case values
328
- in VectorNumber
329
- merge_options(values.options, options)
330
- in Array[*, VectorNumber => vector, *]
331
- merge_options(vector.options, options)
332
- else
333
- merge_options(DEFAULT_OPTIONS, options)
334
- end
335
- end
336
-
337
- # @param base_options [Hash{Symbol => Object}]
338
- # @param added_options [Hash{Symbol => Object}, nil]
339
- # @return [Hash{Symbol => Object}]
340
- def merge_options(base_options, added_options)
341
- return base_options if !added_options || added_options.empty?
342
- # Optimization for the common case of passing options through #new.
343
- return base_options if added_options.equal?(base_options)
344
-
345
- base_options.merge(added_options).slice(*KNOWN_OPTIONS)
346
- end
347
-
348
307
  # Compact coefficients, calculate size and freeze data.
349
308
  # @return [void]
350
309
  def finalize_contents
@@ -14,28 +14,24 @@ class VectorNumber
14
14
  type coefficients_list_type = list[coefficient_type]
15
15
  type each_value_type = [unit_type, coefficient_type]
16
16
 
17
- type options_type = Hash[Symbol, untyped]
18
-
19
17
  # ---- Constants ----
20
18
 
21
19
  VERSION: String
22
20
 
23
- KNOWN_OPTIONS: list[Symbol]
24
- DEFAULT_OPTIONS: options_type
25
-
26
- UNIT: ^(Integer) -> Complex
27
- R: Complex
28
- I: Complex
21
+ NUMERIC_UNITS: list[SpecialUnit]
22
+ R: SpecialUnit
23
+ I: SpecialUnit
29
24
 
30
25
  # ---- Public methods ----
31
26
 
32
- def self.[]: (*unit_type values, **options_type options) -> instance
27
+ def self.[]: (*unit_type values) -> instance
28
+
29
+ def self.numeric_unit?: (unit_type unit) -> bool
33
30
 
34
31
  def size: -> Integer
35
- def options: -> options_type
36
32
 
37
33
  def initialize:
38
- (?(units_list_type | plain_instance | instance)? values, ?options_type? options)
34
+ (?(units_list_type | plain_instance | instance)? values)
39
35
  ?{ (coefficient_type coefficient) -> coefficient_type }
40
36
  -> void
41
37
 
@@ -47,7 +43,6 @@ class VectorNumber
47
43
  private
48
44
 
49
45
  @size: Integer
50
- @options: options_type
51
46
  @data: plain_instance
52
47
 
53
48
  def new: () { (coefficient_type value) -> coefficient_type } -> instance
@@ -69,9 +64,6 @@ class VectorNumber
69
64
 
70
65
  def apply_transform: () ?{ (coefficient_type value) -> coefficient_type } -> void
71
66
 
72
- def save_options: (options_type? options, in_value_type values) -> void
73
- def merge_options: (options_type base_options, options_type added_options) -> options_type
74
-
75
67
  def finalize_contents: () -> void
76
68
  end
77
69
 
@@ -211,13 +203,16 @@ end
211
203
  class VectorNumber
212
204
  MULT_STRINGS: Hash[Symbol, String]
213
205
 
214
- def to_s: (?mult: (Symbol | String)) -> String
206
+ def to_s: (?mult: (Symbol | String)) { (unit_type, coefficient_type, ?Integer index, ?String operator) -> String } -> String
215
207
 
216
208
  def inspect: () -> String
217
209
 
218
210
  private
219
211
 
220
- def value_to_s: (unit_type unit, coefficient_type coefficient, mult: (Symbol | String)) -> String
212
+ def build_string: (String operator) -> String
213
+ | (String operator) { (unit_type, coefficient_type, ?Integer index, ?String operator) -> String } -> String
214
+
215
+ def value_to_s: (unit_type unit, coefficient_type coefficient, String operator) -> String
221
216
  end
222
217
 
223
218
  class VectorNumber
@@ -233,3 +228,14 @@ class VectorNumber
233
228
  end
234
229
  end
235
230
  end
231
+
232
+ class VectorNumber
233
+ # Class for representing special numerical units.
234
+ class SpecialUnit
235
+ def initialize: (Object unit, String text) -> void
236
+
237
+ def to_s: () -> String
238
+
239
+ def inspect: () -> String
240
+ end
241
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vector_number
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Bulancov
@@ -13,18 +13,20 @@ description: |
13
13
  VectorNumber provides a Numeric-like experience for doing arithmetics on heterogeneous objects, with more advanced operations based on real vector spaces available when needed.
14
14
 
15
15
  Features:
16
- - Add and subtract (almost) any object, with no setup or declaration.
17
- - Multiply and divide vectors by any real number to create 1.35 of an array and -2 of a string. What does that mean? Only you know!
18
- - Use vectors instead of inbuilt numbers in most situtations with no difference in behavior. Or, use familiar methods from numerics with sane semantics!
19
- - Enumerate vectors in a hash-like fashion, or transform to an array or hash as needed.
20
- - Enjoy a mix of vector-, complex- and polynomial-like behavior at appropriate times.
21
- - No dependencies, no extensions. It just works!
16
+ * Add and subtract (almost) any object, with no setup or declaration.
17
+ * Multiply and divide vectors by any real number to create 1.35 of an array and -2 of a string. What does that mean? Only you know!
18
+ * Use vectors instead of inbuilt numbers in most situtations with no difference in behavior. Or, use familiar methods from numerics with sane semantics!
19
+ * Enumerate vectors in a hash-like fashion, or transform to an array or hash as needed.
20
+ * Enjoy a mix of vector-, complex- and polynomial-like behavior at appropriate times.
21
+ * No dependencies, no extensions. It just works!
22
22
  executables: []
23
23
  extensions: []
24
24
  extra_rdoc_files:
25
25
  - README.md
26
+ - doc/vector_space.svg
26
27
  files:
27
28
  - README.md
29
+ - doc/vector_space.svg
28
30
  - lib/vector_number.rb
29
31
  - lib/vector_number/comparing.rb
30
32
  - lib/vector_number/converting.rb
@@ -33,6 +35,7 @@ files:
33
35
  - lib/vector_number/mathing.rb
34
36
  - lib/vector_number/numeric_refinements.rb
35
37
  - lib/vector_number/querying.rb
38
+ - lib/vector_number/special_unit.rb
36
39
  - lib/vector_number/stringifying.rb
37
40
  - lib/vector_number/version.rb
38
41
  - sig/vector_number.rbs
@@ -42,9 +45,9 @@ licenses:
42
45
  metadata:
43
46
  homepage_uri: https://github.com/trinistr/vector_number
44
47
  bug_tracker_uri: https://github.com/trinistr/vector_number/issues
45
- documentation_uri: https://rubydoc.info/gems/vector_number/0.5.0
46
- source_code_uri: https://github.com/trinistr/vector_number/tree/v0.5.0
47
- changelog_uri: https://github.com/trinistr/vector_number/blob/v0.5.0/CHANGELOG.md
48
+ documentation_uri: https://rubydoc.info/gems/vector_number/0.6.0
49
+ source_code_uri: https://github.com/trinistr/vector_number/tree/v0.6.0
50
+ changelog_uri: https://github.com/trinistr/vector_number/blob/v0.6.0/CHANGELOG.md
48
51
  rubygems_mfa_required: 'true'
49
52
  rdoc_options:
50
53
  - "--main"