vector_number 0.3.0 → 0.3.1

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.
@@ -2,11 +2,26 @@
2
2
 
3
3
  class VectorNumber
4
4
  # Methods for performing actual math.
5
+ #
6
+ # All operators (like +*+) have aliases (like +mult+)
7
+ # to make method chaining easier and more natural.
5
8
  module Mathing
6
9
  # The coerce method provides support for Ruby type coercion.
7
- # Unlike most numeric types, VectorNumber can coerce *anything*.
10
+ #
11
+ # Unlike other numeric types, VectorNumber can coerce *anything*.
12
+ #
13
+ # @example
14
+ # VectorNumber["a"].coerce(5) # => [(5), (1⋅'a')]
15
+ # VectorNumber[7].coerce([]) # => [(1⋅[]), (7)]
16
+ # VectorNumber["a"] + 5 # => (1⋅'a' + 5)
17
+ # # Direct reverse coercion doesn't work, but Numeric types know how to call #coerce:
18
+ # 5.coerce(VectorNumber["a"]) # RangeError
19
+ # 5 + VectorNumber["a"] # => (5 + 1⋅'a')
20
+ #
8
21
  # @param other [Object]
9
22
  # @return [Array(VectorNumber, VectorNumber)]
23
+ #
24
+ # @since 0.2.0
10
25
  def coerce(other)
11
26
  case other
12
27
  when VectorNumber
@@ -16,44 +31,87 @@ class VectorNumber
16
31
  end
17
32
  end
18
33
 
19
- # Return self.
20
- # @return [VectorNumber]
21
- alias +@ itself
22
-
23
- # Return new vector with negated coefficients.
24
- # This preserves order of units.
34
+ # Return new vector with negated coefficients (additive inverse).
35
+ #
36
+ # @example
37
+ # -VectorNumber[12, "i"] # => (-12 - 1⋅'i')
38
+ # VectorNumber["a", "b", "a"].neg # => (-2⋅'a' - 1⋅'b')
39
+ # -VectorNumber["a"] + VectorNumber["a"] # => (0)
40
+ #
25
41
  # @return [VectorNumber]
42
+ #
43
+ # @since 0.2.0
26
44
  def -@
27
45
  new(&:-@)
28
46
  end
29
47
 
48
+ # @since 0.3.0
30
49
  alias neg -@
31
50
 
32
- # Return new vector as a sum of this and other value.
51
+ # Return new vector as a sum of this and +other+ value.
33
52
  # This is analogous to {VectorNumber.[]}.
53
+ #
54
+ # @example
55
+ # VectorNumber[5] + 10 # => (15)
56
+ # VectorNumber["a"].add(VectorNumber["b"]) # => (1⋅'a' + 1⋅'b')
57
+ # VectorNumber["a"] + "b" # => (1⋅'a' + 1⋅'b')
58
+ # @example numeric types can be added in reverse
59
+ # 10 + VectorNumber[5] # => (15)
60
+ # 10 + VectorNumber["a"] # => (10 + 1⋅'a')
61
+ #
34
62
  # @param other [Object]
35
63
  # @return [VectorNumber]
64
+ #
65
+ # @since 0.2.0
36
66
  def +(other)
37
67
  new([self, other])
38
68
  end
39
69
 
70
+ # @since 0.3.0
40
71
  alias add +
41
72
 
42
- # Return new vector as a sum of this and negative of the other value.
43
- # This is analogous to {VectorNumber.[]}, but allows to negate anything.
73
+ # Return new vector as a sum of this and additive inverse of +other+ value.
74
+ #
75
+ # This is implemented through {#+} and {#-@}.
76
+ #
77
+ # @example
78
+ # VectorNumber[5] - 3 # => (2)
79
+ # VectorNumber["a"].sub(VectorNumber["b"]) # => (1⋅'a' - 1⋅'b')
80
+ # VectorNumber["a"] - "b" # => (1⋅'a' - 1⋅'b')
81
+ # @example numeric types can be subtracted in reverse
82
+ # 3 - VectorNumber[5] # => (-2)
83
+ # 3 - VectorNumber["a"] # => (3 - 1⋅'a')
84
+ #
44
85
  # @param other [Object]
45
86
  # @return [VectorNumber]
87
+ #
88
+ # @since 0.2.0
46
89
  def -(other)
47
90
  self + new([other], &:-@)
48
91
  end
49
92
 
93
+ # @since 0.3.0
50
94
  alias sub -
51
95
 
52
- # Multiply all coefficients by a real number, returning new vector.
53
- # This effectively multiplies magnitude by the specified factor.
96
+ # Multiply all coefficients by a real +other+, returning new vector.
97
+ #
98
+ # This effectively multiplies {#magnitude} by +other+.
99
+ #
100
+ # @example
101
+ # VectorNumber[5] * 2 # => (10)
102
+ # VectorNumber["a", "b", 6].mult(2) # => (2⋅'a' + 2⋅'b' + 12)
103
+ # VectorNumber["a"] * VectorNumber[2] # => (2⋅'a')
104
+ # # Can't multiply by a non-real:
105
+ # VectorNumber["a"] * VectorNumber["b"] # RangeError
106
+ # @example numeric types can be multiplied in reverse
107
+ # 2 * VectorNumber[5] # => (10)
108
+ # 2 * VectorNumber["a"] # => (2⋅'a')
109
+ #
54
110
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
55
111
  # @return [VectorNumber]
56
112
  # @raise [RangeError] if +other+ is not a number or +other+ can't be multiplied by this one
113
+ #
114
+ # @since 0.2.1
57
115
  def *(other)
58
116
  if real_number?(other)
59
117
  other = other.real
@@ -67,32 +125,67 @@ class VectorNumber
67
125
  end
68
126
  end
69
127
 
128
+ # @since 0.3.0
70
129
  alias mult *
71
130
 
72
- # Divide all coefficients by a real number, returning new vector.
73
- # This effectively multiplies magnitude by reciprocal of +other+.
131
+ # Divide all coefficients by a real +other+, returning new vector.
132
+ #
133
+ # This effectively multiplies {#magnitude} by reciprocal of +other+.
134
+ # @note This method never does integer division.
135
+ #
136
+ # @example
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')
140
+ # # Can't divide by a non-real:
141
+ # VectorNumber["a"] / VectorNumber["b"] # RangeError
142
+ # @example numeric types can be divided in reverse
143
+ # 2 / VectorNumber[10] # => (1/5)
144
+ # # Can't divide by a non-real:
145
+ # 2 / VectorNumber["a"] # RangeError
146
+ #
74
147
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
75
148
  # @return [VectorNumber]
76
149
  # @raise [RangeError] if +other+ is not a number or is not a real number
77
150
  # @raise [ZeroDivisionError] if +other+ is zero
151
+ #
152
+ # @since 0.2.1
78
153
  def /(other)
79
154
  check_divisibility(other)
80
155
 
81
156
  other = other.real
82
157
  # Prevent integer division, but without loss of accuracy.
83
- other = Rational(other) if other.is_a?(Integer)
158
+ other = Rational(other) if other.integer?
84
159
  # @type var other: Float
85
160
  new { _1 / other }
86
161
  end
87
162
 
163
+ # @since 0.2.6
88
164
  alias quo /
165
+ # to fix syntax highlighting: /
89
166
 
90
- # Divide all coefficients by a real number using +fdiv+, returning new vector
91
- # with Float coefficients.
167
+ # Divide all coefficients by a real +other+ using +fdiv+,
168
+ # returning new vector with Float (or BigDecimal) coefficients.
169
+ #
170
+ # There isn't much benefit to this method, as {#/} doesn't do integer division,
171
+ # but it is provided for consistency.
172
+ #
173
+ # @example
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')
177
+ # # Can't divide by a non-real:
178
+ # VectorNumber["a"].fdiv(VectorNumber["b"]) # RangeError
179
+ # @example reverse division may return non-vector results
180
+ # 2.fdiv(VectorNumber[10]) # => 0.2 (Float)
181
+ # 2.0.fdiv(VectorNumber[10]) # => (0.2) (VectorNumber)
182
+ #
92
183
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
93
184
  # @return [VectorNumber]
94
185
  # @raise [RangeError] if +other+ is not a number or is not a real number
95
186
  # @raise [ZeroDivisionError] if +other+ is zero
187
+ #
188
+ # @since 0.2.1
96
189
  def fdiv(other)
97
190
  check_divisibility(other)
98
191
 
@@ -100,14 +193,30 @@ class VectorNumber
100
193
  new { _1.fdiv(other) }
101
194
  end
102
195
 
103
- # Divide all coefficients by +other+, rounding results with {#floor}.
196
+ # Divide all coefficients by a real +other+, rounding results with +#floor+.
197
+ #
104
198
  # This is requal to +(self / other).floor+.
199
+ #
200
+ # @example
201
+ # VectorNumber[10].div(3) # => (3)
202
+ # VectorNumber["a"].div(2) # => (0⋅'a')
203
+ # VectorNumber["a"].div(VectorNumber[2]) # => (0⋅'a')
204
+ # # Can't divide by a non-real:
205
+ # VectorNumber["a"].div(VectorNumber["b"]) # RangeError
206
+ # @example numeric types can be divided in reverse
207
+ # 2.div(VectorNumber[10]) # => (0)
208
+ # # Can't divide by a non-real:
209
+ # 2.div(VectorNumber["a"]) # RangeError
210
+ #
211
+ # @see #divmod
212
+ # @see #%
213
+ #
105
214
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
106
215
  # @return [VectorNumber]
107
216
  # @raise [RangeError] if +other+ is not a number or is not a real number
108
217
  # @raise [ZeroDivisionError] if +other+ is zero
109
- # @see #divmod
110
- # @see #%
218
+ #
219
+ # @since 0.2.6
111
220
  def div(other)
112
221
  check_divisibility(other)
113
222
 
@@ -115,16 +224,36 @@ class VectorNumber
115
224
  new { _1.div(other) }
116
225
  end
117
226
 
118
- # Return the modulus of dividing self by +other+ as a vector.
119
- # This is equal to +self - other * (self/other).floor+.
227
+ # Return the modulus of dividing self by a real +other+ as a vector.
228
+ #
229
+ # This is equal to +self - other * (self/other).floor+,
230
+ # or, alternatively, +self - other * self.div(other)+.
231
+ #
232
+ # @example
233
+ # VectorNumber[10] % 3 # => (1)
234
+ # VectorNumber["a", "b", 6].modulo(2) # => (1⋅'a' + 1⋅'b')
235
+ # -VectorNumber["a"] % VectorNumber[2] # => (1⋅'a')
236
+ # # Can't divide by a non-real:
237
+ # VectorNumber["a"] % VectorNumber["b"] # RangeError
238
+ # @example numeric types can be divided in reverse
239
+ # 3 % VectorNumber[10] # => (3)
240
+ # # Can't divide by a non-real:
241
+ # 3 % VectorNumber["a"] # RangeError
242
+ # @example compare to #remainder
243
+ # VectorNumber[-5] % 3 # => (1)
244
+ # VectorNumber[-5].remainder(3) # => (-2)
245
+ #
246
+ # @see #divmod
247
+ # @see #div
248
+ # @see #remainder
249
+ # @see Numeric#%
250
+ #
120
251
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
121
252
  # @return [VectorNumber]
122
253
  # @raise [RangeError] if +other+ is not a number or is not a real number
123
254
  # @raise [ZeroDivisionError] if +other+ is zero
124
- # @see #divmod
125
- # @see #div
126
- # @see #remainder for alternative
127
- # @see Numeric#% for examples
255
+ #
256
+ # @since 0.2.6
128
257
  def %(other)
129
258
  check_divisibility(other)
130
259
 
@@ -132,28 +261,63 @@ class VectorNumber
132
261
  new { _1 % other }
133
262
  end
134
263
 
264
+ # @since 0.2.6
135
265
  alias modulo %
136
266
 
137
- # Return the quotient and modulus of dividing self by +other+.
267
+ # Return the quotient and modulus of dividing self by a real +other+.
138
268
  # There is no performance benefit compared to calling {#div} and {#%} separately.
269
+ #
270
+ # @example
271
+ # VectorNumber[10].divmod(3) # => [(3), (1)]
272
+ # VectorNumber["a"].divmod(2) # => [(0⋅'a'), (1⋅'a')]
273
+ # VectorNumber["a"].divmod(VectorNumber[2]) # => [(0⋅'a'), (1⋅'a')]
274
+ # # Can't divide by a non-real:
275
+ # VectorNumber["a"].divmod(VectorNumber["b"]) # RangeError
276
+ # @example numeric types can be divided in reverse
277
+ # 3.divmod(VectorNumber[10]) # => [(0), (3)]
278
+ # # Can't divide by a non-real:
279
+ # 3.divmod(VectorNumber["a"]) # RangeError
280
+ #
281
+ # @see #div
282
+ # @see #%
283
+ #
139
284
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
140
285
  # @return [Array(VectorNumber, VectorNumber)]
141
286
  # @raise [RangeError] if +other+ is not a number or is not a real number
142
287
  # @raise [ZeroDivisionError] if +other+ is zero
143
- # @see #div
144
- # @see #%
288
+ #
289
+ # @since 0.2.6
145
290
  def divmod(other)
146
291
  [div(other), modulo(other)]
147
292
  end
148
293
 
149
- # Return the remainder of dividing self by +other+ as a vector.
294
+ # Return the remainder of dividing self by a real +other+ as a vector.
295
+ #
150
296
  # This is equal to +self - other * (self/other).truncate+.
297
+ #
298
+ # @example
299
+ # VectorNumber[10].remainder(3) # => (1)
300
+ # VectorNumber["a"].remainder(2) # => (1⋅'a')
301
+ # -VectorNumber["a"].remainder(VectorNumber[2]) # => (-1⋅'a')
302
+ # # Can't divide by a non-real:
303
+ # VectorNumber["a"].remainder(VectorNumber["b"]) # RangeError
304
+ # @example numeric types can be divided in reverse
305
+ # 3.remainder(VectorNumber[10]) # => (3)
306
+ # # Can't divide by a non-real:
307
+ # 3.remainder(VectorNumber["a"]) # RangeError
308
+ # @example compare to #%
309
+ # VectorNumber[-5] % 3 # => (1)
310
+ # VectorNumber[-5].remainder(3) # => (-2)
311
+ #
312
+ # @see #%
313
+ # @see Numeric#remainder
314
+ #
151
315
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
152
316
  # @return [VectorNumber]
153
317
  # @raise [RangeError] if +other+ is not a number or is not a real number
154
318
  # @raise [ZeroDivisionError] if +other+ is zero
155
- # @see #% for alternative
156
- # @see Numeric#remainder for examples
319
+ #
320
+ # @since 0.2.6
157
321
  def remainder(other)
158
322
  check_divisibility(other)
159
323
 
@@ -163,8 +327,18 @@ class VectorNumber
163
327
 
164
328
  private
165
329
 
330
+ # @param other [Object]
331
+ # @return [void]
332
+ # @raise [RangeError] unless +other+ is a real number
333
+ # @raise [ZeroDivisionError]
334
+ #
335
+ # @see real_number?
336
+ #
337
+ # @since 0.2.6
166
338
  def check_divisibility(other)
167
- raise RangeError, "can't divide #{self} by #{other}", caller unless real_number?(other)
339
+ unless real_number?(other)
340
+ raise RangeError, "can't divide #{self} by #{other.inspect}", caller
341
+ end
168
342
  raise ZeroDivisionError, "divided by 0", caller if other.zero?
169
343
  end
170
344
  end
@@ -1,21 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class VectorNumber
4
- # Refinements of Numeric classes to better work with VectorNumber and similar classes.
4
+ # Refinements of Numeric classes and Kernel to better work with VectorNumber and similar classes.
5
+ #
6
+ # These do not depend on +VectorNumber+ and can technically be used separately.
7
+ # Currently includes:
8
+ # - refinement for +Complex#<=>+ to work with classes implementing +<=>+;
9
+ # - refinement for +Kernel#BigDecimal+ to work with classes implementing +to_d+.
10
+ #
11
+ # @example activating refinements
12
+ # require "vector_number/numeric_refinements"
13
+ # using VectorNumber::NumericRefinements
14
+ #
15
+ # @since 0.2.0
5
16
  module NumericRefinements
6
17
  # Refinement module to provide a +#<=>+ method that can work backwards.
18
+ #
7
19
  # @note Currently only applies to Complex on *3.1*,
8
20
  # as other numeric classes rely on +#coerce+.
21
+ # @example without refinements
22
+ # VectorNumber[2] <=> Complex(1, 0) #=> 1
23
+ # Complex(1, 0) <=> VectorNumber[2] #=> nil
24
+ # @example with refinements
25
+ # require "vector_number/numeric_refinements"
26
+ # using VectorNumber::NumericRefinements
27
+ # VectorNumber[2] <=> Complex(1, 0) #=> 1
28
+ # Complex(1, 0) <=> VectorNumber[2] #=> -1
29
+ #
30
+ # @since 0.2.1
9
31
  module CommutativeShuttle
10
32
  # Commutative +#<=>+.
11
- # @example without refinements
12
- # Complex(1, 0) <=> VectorNumber[2] # => nil
13
- # VectorNumber[2] <=> Complex(1, 0) # => 1
14
- # @example with refinements
15
- # require "vector_number/numeric_refinements"
16
- # using VectorNumber::NumericRefinements
17
- # Complex(1, 0) <=> VectorNumber[2] # => -1
18
- # VectorNumber[2] <=> Complex(1, 0) # => 1
33
+ # Tries to call +other <=> self+ if +self <=> other+ returns +nil+.
19
34
  def <=>(other)
20
35
  comparison = super
21
36
  return comparison if comparison || !other.respond_to?(:<=>)
@@ -30,7 +45,16 @@ class VectorNumber
30
45
  end
31
46
 
32
47
  # Refinement module to change Kernel#BigDecimal so it works with +#to_d+.
48
+ #
33
49
  # @note `BigDecimal` needs to be defined for this refinement to activate.
50
+ # @example without refinements
51
+ # BigDecimal(VectorNumber[2]) # can't convert VectorNumber into BigDecimal (TypeError)
52
+ # @example with refinements
53
+ # require "vector_number/numeric_refinements"
54
+ # using VectorNumber::NumericRefinements
55
+ # BigDecimal(VectorNumber[2]) #=> 0.2e1
56
+ #
57
+ # @since 0.2.1
34
58
  module BigDecimalToD
35
59
  # BigDecimal() that first tries to use #to_d.
36
60
  # @param value [Object]
@@ -5,12 +5,21 @@ class VectorNumber
5
5
  # Mostly modeled after {::Complex}.
6
6
  module Querying
7
7
  # Whether this VectorNumber can be considered strictly numeric, e.g. real or complex.
8
+ #
9
+ # @example
10
+ # VectorNumber[2].numeric? # => true
11
+ # VectorNumber[2, 3i].numeric? # => true
12
+ # VectorNumber[2, "a"].numeric? # => false
13
+ # VectorNumber[2, 3i].numeric?(1) # => false
14
+ #
8
15
  # @param dimensions [Integer] number of dimensions to consider "numeric"
9
16
  # - 0 — zero
10
17
  # - 1 — real number
11
18
  # - 2 — complex number, etc.
12
19
  # @return [Boolean]
13
20
  # @raise [ArgumentError] if +dimensions+ is negative
21
+ #
22
+ # @since 0.2.0
14
23
  def numeric?(dimensions = 2)
15
24
  raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0
16
25
 
@@ -18,64 +27,124 @@ class VectorNumber
18
27
  end
19
28
 
20
29
  # Whether this VectorNumber contains any non-numeric parts.
30
+ #
31
+ # @example
32
+ # VectorNumber[2].nonnumeric? # => false
33
+ # VectorNumber[2, 3i].nonnumeric? # => false
34
+ # VectorNumber[2, "a"].nonnumeric? # => true
35
+ # VectorNumber[2, 3i].nonnumeric?(1) # => true
36
+ #
21
37
  # @param (see #numeric?)
22
38
  # @return (see #numeric?)
23
39
  # @raise (see #numeric?)
24
- def nonnumeric?(dimensions = 2)
25
- raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0
26
-
27
- !numeric?(dimensions)
28
- end
40
+ #
41
+ # @since 0.2.1
42
+ def nonnumeric?(dimensions = 2) = !numeric?(dimensions)
29
43
 
30
44
  # Returns +true+ if all coefficients are finite, +false+ otherwise.
45
+ #
46
+ # @example
47
+ # VectorNumber[2].finite? # => true
48
+ # VectorNumber[Float::NAN].finite? # => false
49
+ # VectorNumber["a"].mult(Float::INFINITY).finite? # => false
50
+ #
31
51
  # @return [Boolean]
52
+ #
53
+ # @since 0.1.0
32
54
  def finite?
33
55
  all? { |_u, v| v.finite? }
34
56
  end
35
57
 
36
58
  # Returns +1+ if any coefficients are infinite, +nil+ otherwise.
59
+ #
60
+ # This behavior is the same as +Complex+'s.
61
+ #
62
+ # @example
63
+ # VectorNumber[2].infinite? # => nil
64
+ # VectorNumber[Float::NAN].infinite? # => 1
65
+ # VectorNumber["a"].mult(-Float::INFINITY).infinite? # => 1
66
+ #
37
67
  # @return [1, nil]
68
+ #
69
+ # @since 0.1.0
38
70
  def infinite?
39
71
  finite? ? nil : 1 # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
40
72
  end
41
73
 
42
74
  # Returns +true+ if there are no non-zero coefficients, and +false+ otherwise.
75
+ #
76
+ # @example
77
+ # VectorNumber["c"].zero? # => false
78
+ # VectorNumber[].zero? # => true
79
+ #
43
80
  # @return [Boolean]
44
- def zero?
45
- size.zero?
46
- end
81
+ #
82
+ # @since 0.1.0
83
+ def zero? = size.zero?
47
84
 
48
85
  # Returns +self+ if there are any non-zero coefficients, +nil+ otherwise.
86
+ #
87
+ # This behavior is the same as +Numeric+'s.
88
+ #
89
+ # @example
90
+ # VectorNumber["ab", "cd"].nonzero? # => (1⋅'ab' + 1⋅'cd')
91
+ # VectorNumber[].nonzero? # => nil
92
+ #
49
93
  # @return [VectorNumber, nil]
94
+ #
95
+ # @since 0.1.0
50
96
  def nonzero?
51
97
  zero? ? nil : self # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
52
98
  end
53
99
 
54
100
  # Returns +true+ if number is non-zero and all non-zero coefficients are positive,
55
101
  # and +false+ otherwise.
102
+ #
103
+ # @example
104
+ # VectorNumber["a"].positive? # => true
105
+ # VectorNumber[2].neg.positive? # => false
106
+ # (VectorNumber["1"] - VectorNumber[1]).positive? # => false
107
+ # VectorNumber[0].positive? # => false
108
+ #
56
109
  # @return [Boolean]
110
+ #
111
+ # @since 0.1.0
57
112
  def positive?
58
113
  !zero? && all? { |_u, c| c.positive? }
59
114
  end
60
115
 
61
116
  # Returns +true+ if number is non-zero and all non-zero coefficients are negative,
62
117
  # and +false+ otherwise.
118
+ #
119
+ # @example
120
+ # VectorNumber["a"].neg.negative? # => true
121
+ # VectorNumber[-2].neg.negative? # => false
122
+ # (VectorNumber["1"] - VectorNumber[1]).negative? # => false
123
+ # VectorNumber[0].negative? # => false
124
+ #
63
125
  # @return [Boolean]
126
+ #
127
+ # @since 0.1.0
64
128
  def negative?
65
129
  !zero? && all? { |_u, c| c.negative? }
66
130
  end
67
131
 
68
- # Always returns +false+, as vectors are never real numbers.
132
+ # Always returns +false+, as vectors are not real numbers.
133
+ #
134
+ # This behavior is the same as +Complex+'s.
135
+ #
69
136
  # @see #numeric?
137
+ #
70
138
  # @return [false]
71
- def real?
72
- false
73
- end
139
+ #
140
+ # @since 0.1.0
141
+ def real? = false
74
142
 
75
143
  # Always returns +false+, as vectors are not +Integer+s.
144
+ #
76
145
  # @return [false]
77
- def integer?
78
- false
79
- end
146
+ #
147
+ # @since 0.2.1
148
+ def integer? = false
80
149
  end
81
150
  end
@@ -4,21 +4,37 @@ class VectorNumber
4
4
  # Methods and options for string representation.
5
5
  module Stringifying
6
6
  # Predefined symbols for multiplication to display between unit and coefficient.
7
+ #
7
8
  # @return [Hash{Symbol => String}]
9
+ #
10
+ # @since 0.1.0
8
11
  MULT_STRINGS = {
9
12
  asterisk: "*", # U+002A
10
13
  cross: "×", # U+00D7
11
14
  dot: "⋅", # U+22C5
12
15
  invisible: "⁢", # U+2062, zero-width multiplication operator
13
16
  space: " ",
14
- none: ""
17
+ none: "",
15
18
  }.freeze
16
19
 
20
+ # Get a string representation of the vector.
21
+ #
22
+ # @example
23
+ # VectorNumber[5, "s"].to_s # => "5 + 1⋅'s'"
24
+ # VectorNumber["s", 5].to_s # => "1⋅'s' + 5"
25
+ # @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
+ #
17
30
  # @param mult [Symbol, String]
18
31
  # text to use between coefficient and unit,
19
32
  # can be one of the keys in {MULT_STRINGS} or an arbitrary string
20
33
  # @return [String]
21
- # @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
34
+ # @raise [ArgumentError]
35
+ # if +mult+ is not a String and is not in {MULT_STRINGS}'s keys
36
+ #
37
+ # @since 0.1.0
22
38
  def to_s(mult: options[:mult])
23
39
  return "0" if zero?
24
40
 
@@ -34,7 +50,18 @@ class VectorNumber
34
50
  result
35
51
  end
36
52
 
53
+ # Get a string representation of the vector.
54
+ #
55
+ # This is similar to +Complex#inspect+: it returns result of {#to_s} in round brackets.
56
+ #
57
+ # @example
58
+ # VectorNumber[5, "s"].inspect # => "(5 + 1⋅'s')"
59
+ #
37
60
  # @return [String]
61
+ #
62
+ # @see to_s
63
+ #
64
+ # @since 0.1.0
38
65
  def inspect
39
66
  # TODO: Probably make this independent of options.
40
67
  "(#{self})"
@@ -47,6 +74,8 @@ class VectorNumber
47
74
  # @param mult [Symbol, String]
48
75
  # @return [String]
49
76
  # @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
77
+ #
78
+ # @since 0.1.0
50
79
  def value_to_s(unit, coefficient, mult:)
51
80
  if !mult.is_a?(String) && !MULT_STRINGS.key?(mult)
52
81
  raise ArgumentError, "unknown key #{mult.inspect}", caller
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class VectorNumber # rubocop:disable Style/StaticClass
3
+ class VectorNumber
4
4
  # @return [String]
5
- VERSION = "0.3.0"
5
+ VERSION = "0.3.1"
6
6
  end