vector_number 0.4.3 → 0.5.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 +4 -4
- data/README.md +1 -1
- data/lib/vector_number/comparing.rb +110 -107
- data/lib/vector_number/converting.rb +152 -147
- data/lib/vector_number/enumerating.rb +107 -108
- data/lib/vector_number/math_converting.rb +113 -109
- data/lib/vector_number/mathing.rb +305 -312
- data/lib/vector_number/numeric_refinements.rb +7 -0
- data/lib/vector_number/querying.rb +143 -136
- data/lib/vector_number/stringifying.rb +79 -82
- data/lib/vector_number/version.rb +1 -1
- data/lib/vector_number.rb +121 -51
- data/sig/vector_number.rbs +128 -161
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1f7cc1d90279440f3387287efeabb4a4c0cb40fe370b671d07623f192cccc68
|
|
4
|
+
data.tar.gz: c89b5bf8d9ad8fef73942cd3112b42cbbcbd515ca29bd61dcad0be274c5a7b0f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 199fa928aa2c46d6e5966d745def48284b1373c3cf3ab4b30046d0e39e7eaa91b0cf9beab690ac3a08fe3776f5a4f9726f341ff9f809b30766e645258eff6149
|
|
7
|
+
data.tar.gz: 484a9f87841252e26aad41db259304713b4deb0c93369a16254c3f413f2f986756c8a894be30b7555ef69ed1264e2b73ef33b3c0fa8b7ed9b07f2a2e74fd5137
|
data/README.md
CHANGED
|
@@ -147,4 +147,4 @@ Bug reports and pull requests are welcome on GitHub at [https://github.com/trini
|
|
|
147
147
|
|
|
148
148
|
## License
|
|
149
149
|
|
|
150
|
-
This gem is available as open source under the terms of the
|
|
150
|
+
This gem is available as open source under the terms of the MIT License, see [LICENSE.txt](https://github.com/trinistr/vector_number/blob/main/LICENSE.txt).
|
|
@@ -1,125 +1,128 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class VectorNumber
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# +Comparable+ is included for parity with
|
|
4
|
+
# @group Comparing
|
|
5
|
+
|
|
6
|
+
# +Comparable+ is included for parity with +Numeric+.
|
|
7
7
|
# @example using Comparable methods
|
|
8
8
|
# VectorNumber[12] < 0 # => false
|
|
9
9
|
# VectorNumber[12, "a"] < 0 # ArgumentError
|
|
10
10
|
# (VectorNumber[12]..VectorNumber[15]).include?(13) # => true
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
#
|
|
12
|
+
# @since 0.2.0
|
|
13
|
+
include ::Comparable
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
15
|
+
# Test whether +other+ has the same value with +==+ semantics.
|
|
16
|
+
#
|
|
17
|
+
# Values are considered equal if
|
|
18
|
+
# - +other+ is a VectorNumber and it has the same units and equal coefficients, or
|
|
19
|
+
# - +other+ is a Numeric equal in value to this (real or complex) number.
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# VectorNumber[3.13] == 3.13 # => true
|
|
23
|
+
# VectorNumber[1.4, 1.5i] == Complex(1.4, 1.5) # => true
|
|
24
|
+
# VectorNumber["a", "b", "c"] == VectorNumber["c", "b", "a"] # => true
|
|
25
|
+
# VectorNumber["a", 14] == 14 # => false
|
|
26
|
+
# VectorNumber["a"] == "a" # => false
|
|
27
|
+
#
|
|
28
|
+
# @param other [Object]
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
#
|
|
31
|
+
# @since 0.2.0
|
|
32
|
+
def ==(other)
|
|
33
|
+
return true if equal?(other)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
case other
|
|
36
|
+
when VectorNumber
|
|
37
|
+
size == other.size && @data == other.to_h
|
|
38
|
+
when Numeric
|
|
39
|
+
numeric?(2) && other.real == real && other.imaginary == imaginary
|
|
40
|
+
else
|
|
41
|
+
# Can't compare a number-like value to a non-number.
|
|
42
|
+
false
|
|
42
43
|
end
|
|
44
|
+
end
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
# Test whether +other+ is VectorNumber and has the same value with +eql?+ semantics.
|
|
47
|
+
#
|
|
48
|
+
# Values are considered equal only if +other+ is a VectorNumber
|
|
49
|
+
# and it has exactly the same units and coefficients, though possibly in a different order.
|
|
50
|
+
# Additionally, `a.eql?(b)` implies `a.hash == b.hash`.
|
|
51
|
+
#
|
|
52
|
+
# Note that {#options} are not considered for equality.
|
|
53
|
+
#
|
|
54
|
+
# @example
|
|
55
|
+
# VectorNumber["a", "b", "c"].eql? VectorNumber["c", "b", "a"] # => true
|
|
56
|
+
# VectorNumber[3.13].eql? 3.13 # => false
|
|
57
|
+
# VectorNumber[1.4, 1.5i].eql? Complex(1.4, 1.5) # => false
|
|
58
|
+
# VectorNumber["a", 14].eql? 14 # => false
|
|
59
|
+
# VectorNumber["a"].eql? "a" # => false
|
|
60
|
+
#
|
|
61
|
+
# @param other [Object]
|
|
62
|
+
# @return [Boolean]
|
|
63
|
+
#
|
|
64
|
+
# @since 0.1.0
|
|
65
|
+
def eql?(other)
|
|
66
|
+
return true if equal?(other)
|
|
67
|
+
return false unless other.is_a?(VectorNumber)
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
else
|
|
69
|
-
false
|
|
70
|
-
end
|
|
71
|
-
end
|
|
69
|
+
size.eql?(other.size) && @data.eql?(other.to_h)
|
|
70
|
+
end
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
72
|
+
# Generate an Integer hash value for self.
|
|
73
|
+
#
|
|
74
|
+
# Hash values are stable during runtime, but not between processes.
|
|
75
|
+
# Options are disregarded for hash calculation.
|
|
76
|
+
#
|
|
77
|
+
# @example
|
|
78
|
+
# VectorNumber["b", "a"].hash # => 3081872088394655324
|
|
79
|
+
# VectorNumber["a", "b", mult: :cross].hash # => 3081872088394655324
|
|
80
|
+
# VectorNumber["b", "c"].hash # => -1002381358514682371
|
|
81
|
+
#
|
|
82
|
+
# @return [Integer]
|
|
83
|
+
#
|
|
84
|
+
# @since 0.4.2
|
|
85
|
+
def hash
|
|
86
|
+
[self.class, @data].hash
|
|
87
|
+
end
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
89
|
+
# Compare to +other+ and return -1, 0, or 1
|
|
90
|
+
# if +self+ is less than, equal, or larger than +other+ on real number line,
|
|
91
|
+
# or +nil+ if any or both values are non-real.
|
|
92
|
+
#
|
|
93
|
+
# Most VectorNumbers are non-real and therefore not comparable with this method.
|
|
94
|
+
#
|
|
95
|
+
# @example
|
|
96
|
+
# VectorNumber[130] <=> 12 # => 1
|
|
97
|
+
# 1 <=> VectorNumber[13] # => -1
|
|
98
|
+
# VectorNumber[12.1] <=> Complex(12.1, 0) # => 0
|
|
99
|
+
# # This doesn't work as expected without NumericRefinements:
|
|
100
|
+
# Complex(12.1, 0) <=> VectorNumber[12.1] # => nil
|
|
101
|
+
#
|
|
102
|
+
# # Any non-real comparison returns nil:
|
|
103
|
+
# VectorNumber[12.1] <=> Complex(12.1, 1) # => nil
|
|
104
|
+
# VectorNumber[12.1i] <=> 2 # => nil
|
|
105
|
+
# VectorNumber["a"] <=> 2 # => nil
|
|
106
|
+
#
|
|
107
|
+
# @see #numeric?
|
|
108
|
+
# @see Comparable
|
|
109
|
+
# @see NumericRefinements
|
|
110
|
+
#
|
|
111
|
+
# @param other [Object]
|
|
112
|
+
# @return [Integer]
|
|
113
|
+
# @return [nil] if +self+ or +other+ isn't a real number.
|
|
114
|
+
#
|
|
115
|
+
# @since 0.2.0
|
|
116
|
+
def <=>(other)
|
|
117
|
+
return nil unless numeric?(1)
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
end
|
|
119
|
+
case other
|
|
120
|
+
when VectorNumber
|
|
121
|
+
other.numeric?(1) ? real <=> other.real : nil
|
|
122
|
+
when Numeric
|
|
123
|
+
other.imaginary.zero? ? real <=> other.real : nil
|
|
124
|
+
else
|
|
125
|
+
nil
|
|
123
126
|
end
|
|
124
127
|
end
|
|
125
128
|
end
|
|
@@ -1,152 +1,157 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class VectorNumber
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
4
|
+
# @group Converting to different numeric classes
|
|
5
|
+
|
|
6
|
+
# Return real part of the number.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# VectorNumber[23, "a"].real # => 23
|
|
10
|
+
# VectorNumber["a"].real # => 0
|
|
11
|
+
#
|
|
12
|
+
# @return [Integer, Float, Rational, BigDecimal]
|
|
13
|
+
#
|
|
14
|
+
# @since 0.1.0
|
|
15
|
+
def real = @data[R]
|
|
16
|
+
|
|
17
|
+
# Return imaginary part of the number.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# VectorNumber[23, "a"].imaginary # => 0
|
|
21
|
+
# VectorNumber["a", Complex(1, 2r)].imag # => (2/1)
|
|
22
|
+
#
|
|
23
|
+
# @return [Integer, Float, Rational, BigDecimal]
|
|
24
|
+
#
|
|
25
|
+
# @since 0.1.0
|
|
26
|
+
def imaginary = @data[I]
|
|
27
|
+
|
|
28
|
+
# @since 0.2.1
|
|
29
|
+
alias imag imaginary
|
|
30
|
+
|
|
31
|
+
# Return value as an Integer, truncating it, if only real part is non-zero.
|
|
32
|
+
#
|
|
33
|
+
# @example
|
|
34
|
+
# VectorNumber[13.5].to_i # => 13
|
|
35
|
+
# VectorNumber[13r/12].to_int # => 1
|
|
36
|
+
# [1.1, 2.2, 3.3][VectorNumber[2]] # => 3.3
|
|
37
|
+
# VectorNumber[2, 2i].to_i # RangeError
|
|
38
|
+
# VectorNumber[2, :i].to_f # RangeError
|
|
39
|
+
#
|
|
40
|
+
# Integer(VectorNumber[2]) # => 2
|
|
41
|
+
# Integer(VectorNumber[2, "i"]) # RangeError
|
|
42
|
+
#
|
|
43
|
+
# @return [Integer]
|
|
44
|
+
# @raise [RangeError] if any non-real part is non-zero
|
|
45
|
+
#
|
|
46
|
+
# @since 0.1.0
|
|
47
|
+
def to_i
|
|
48
|
+
raise_convert_error(Integer) unless numeric?(1)
|
|
49
|
+
|
|
50
|
+
real.to_i
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @since 0.1.0
|
|
54
|
+
alias to_int to_i
|
|
55
|
+
|
|
56
|
+
# Return value as a Float if only real part is non-zero.
|
|
57
|
+
#
|
|
58
|
+
# @example
|
|
59
|
+
# VectorNumber[13.5].to_f # => 13.5
|
|
60
|
+
# VectorNumber[13r/12].to_f # => 1.0833333333333333
|
|
61
|
+
# VectorNumber[2, 2i].to_f # RangeError
|
|
62
|
+
# VectorNumber[2, :i].to_f # RangeError
|
|
63
|
+
#
|
|
64
|
+
# Float(VectorNumber[2]) # => 2.0
|
|
65
|
+
# Float(VectorNumber[2, "i"]) # RangeError
|
|
66
|
+
#
|
|
67
|
+
# @return [Float]
|
|
68
|
+
# @raise [RangeError] if any non-real part is non-zero
|
|
69
|
+
#
|
|
70
|
+
# @since 0.1.0
|
|
71
|
+
def to_f
|
|
72
|
+
raise_convert_error(Float) unless numeric?(1)
|
|
73
|
+
|
|
74
|
+
real.to_f
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Return value as a Rational if only real part is non-zero.
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# VectorNumber[13.5].to_r # => (27/2)
|
|
81
|
+
# VectorNumber[13r/12].to_r # => (13/12)
|
|
82
|
+
# VectorNumber[2, 2i].to_r # RangeError
|
|
83
|
+
# VectorNumber[2, :i].to_r # RangeError
|
|
84
|
+
#
|
|
85
|
+
# Rational(VectorNumber[2]) # => (2/1)
|
|
86
|
+
# Rational(VectorNumber[2, "i"]) # RangeError
|
|
87
|
+
#
|
|
88
|
+
# @return [Rational]
|
|
89
|
+
# @raise [RangeError] if any non-real part is non-zero
|
|
90
|
+
#
|
|
91
|
+
# @since 0.1.0
|
|
92
|
+
def to_r
|
|
93
|
+
raise_convert_error(Rational) unless numeric?(1)
|
|
94
|
+
|
|
95
|
+
real.to_r
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Return value as a BigDecimal if only real part is non-zero.
|
|
99
|
+
#
|
|
100
|
+
# @example
|
|
101
|
+
# VectorNumber[13.5].to_d # => 0.135e2
|
|
102
|
+
# VectorNumber[13r/12].to_d # ArgumentError
|
|
103
|
+
# VectorNumber[13r/12].to_d(5) # => 0.10833e1
|
|
104
|
+
# VectorNumber[2, 2i].to_d # RangeError
|
|
105
|
+
# VectorNumber[2, :i].to_d # RangeError
|
|
106
|
+
#
|
|
107
|
+
# # This does't work without NumericRefinements:
|
|
108
|
+
# BigDecimal(VectorNumber[2]) # TypeError
|
|
109
|
+
# # #to_s can be used as a workaround if refinements aren't used:
|
|
110
|
+
# BigDecimal(VectorNumber[2].to_s) # => 0.2e1
|
|
111
|
+
# BigDecimal(VectorNumber[2, "i"].to_s) # => ArgumentError
|
|
112
|
+
#
|
|
113
|
+
# @param ndigits [Integer] precision
|
|
114
|
+
# @return [BigDecimal]
|
|
115
|
+
# @raise [RangeError] if any non-real part is non-zero
|
|
116
|
+
# @raise [NameError] if BigDecimal is not defined
|
|
117
|
+
#
|
|
118
|
+
# @see Kernel.BigDecimal
|
|
119
|
+
# @see NumericRefinements
|
|
120
|
+
#
|
|
121
|
+
# @since 0.1.0
|
|
122
|
+
def to_d(ndigits = nil)
|
|
123
|
+
raise_convert_error(BigDecimal) unless numeric?(1)
|
|
124
|
+
|
|
125
|
+
return BigDecimal(real, ndigits) if ndigits
|
|
126
|
+
return BigDecimal(real, Float::DIG) if real.is_a?(Float)
|
|
127
|
+
|
|
128
|
+
BigDecimal(real)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Return value as a Complex if only real and/or imaginary parts are non-zero.
|
|
132
|
+
#
|
|
133
|
+
# @example
|
|
134
|
+
# VectorNumber[13.5].to_c # => (13.5+0i)
|
|
135
|
+
# VectorNumber[13r/12].to_c # => ((13/12)+0i)
|
|
136
|
+
# VectorNumber[2, 2i].to_c # => (2+2i)
|
|
137
|
+
# VectorNumber[2, :i].to_c # RangeError
|
|
138
|
+
#
|
|
139
|
+
# Complex(VectorNumber[2]) # => (2+0i)
|
|
140
|
+
# Complex(VectorNumber[2, "i"]) # RangeError
|
|
141
|
+
#
|
|
142
|
+
# @return [Complex]
|
|
143
|
+
# @raise [RangeError] if any non-real, non-imaginary part is non-zero
|
|
144
|
+
#
|
|
145
|
+
# @since 0.1.0
|
|
146
|
+
def to_c
|
|
147
|
+
raise_convert_error(Complex) unless numeric?(2)
|
|
148
|
+
|
|
149
|
+
Complex(real, imaginary)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
def raise_convert_error(klass)
|
|
155
|
+
raise RangeError, "can't convert #{self} into #{klass}", caller
|
|
151
156
|
end
|
|
152
157
|
end
|