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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -1
- data/LICENSE.txt +21 -0
- data/README.md +16 -7
- data/lib/vector_number/comparing.rb +55 -2
- data/lib/vector_number/converting.rb +86 -6
- data/lib/vector_number/enumerating.rb +70 -13
- data/lib/vector_number/math_converting.rb +66 -6
- data/lib/vector_number/mathing.rb +207 -33
- data/lib/vector_number/numeric_refinements.rb +33 -9
- data/lib/vector_number/querying.rb +84 -15
- data/lib/vector_number/stringifying.rb +31 -2
- data/lib/vector_number/version.rb +2 -2
- data/lib/vector_number.rb +97 -18
- data/sig/vector_number.rbs +3 -3
- metadata +12 -6
@@ -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
|
-
#
|
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
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
#
|
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
|
43
|
-
#
|
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
|
53
|
-
#
|
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
|
73
|
-
#
|
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.
|
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
|
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
|
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
|
-
#
|
110
|
-
# @
|
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
|
-
#
|
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
|
-
#
|
125
|
-
# @
|
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
|
-
#
|
144
|
-
# @
|
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
|
-
#
|
156
|
-
# @
|
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
|
-
|
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
|
-
#
|
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
|
-
|
25
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
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
|
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
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
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]
|
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
|