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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05d6547284c73b585a149026d1a0a123f5914e13c81856e2419180f44f05404c
4
- data.tar.gz: 2b942db9aa53c4e8027540a630355f2defd64de96d3c90bbd55e539844f32bfa
3
+ metadata.gz: f1f7cc1d90279440f3387287efeabb4a4c0cb40fe370b671d07623f192cccc68
4
+ data.tar.gz: c89b5bf8d9ad8fef73942cd3112b42cbbcbd515ca29bd61dcad0be274c5a7b0f
5
5
  SHA512:
6
- metadata.gz: a8d079e1d0915ae151ac2dcb28621f672365e55efa0cb505ad3e846eb3619eababa4e39e5654098bb305fafa2ff602a1085c913c5d6fc8ea02922688087fb09b
7
- data.tar.gz: f09e3d71187e9c202ff1a4960cbf19b7a02f679dc6b49b7b1e96e42e074a66990507262ab1648182217b8dacc980fe0ed777c48ac76db44734dccfdf34a7b488
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 [MIT License](https://opensource.org/licenses/MIT), see [LICENSE.txt](https://github.com/trinistr/vector_number/blob/main/LICENSE.txt).
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
- # Methods for comparing with other numbers.
5
- #
6
- # +Comparable+ is included for parity with numbers.
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
- module Comparing
12
- # @since 0.2.0
13
- include ::Comparable
11
+ #
12
+ # @since 0.2.0
13
+ include ::Comparable
14
14
 
15
- # Compare to +other+ for equality.
16
- #
17
- # Values are considered equal if
18
- # - +other+ is a VectorNumber and it is +eql?+ to this one, 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 eql?(other)
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
- case other
36
- when Numeric
37
- numeric?(2) && other.real == real && other.imaginary == imaginary
38
- else
39
- # Can't compare a number-like value to a non-number.
40
- false
41
- end
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
- # Compare to +other+ for strict equality.
45
- #
46
- # Values are considered equal only if +other+ is a VectorNumber
47
- # and it has exactly the same units and coefficients, though possibly in a different order.
48
- # Additionally, `a.eql?(b)` implies `a.hash == b.hash`.
49
- #
50
- # Note that {#options} are not considered for equality.
51
- #
52
- # @example
53
- # VectorNumber["a", "b", "c"].eql? VectorNumber["c", "b", "a"] # => true
54
- # VectorNumber[3.13].eql? 3.13 # => false
55
- # VectorNumber[1.4, 1.5i].eql? Complex(1.4, 1.5) # => false
56
- # VectorNumber["a", 14].eql? 14 # => false
57
- # VectorNumber["a"].eql? "a" # => false
58
- #
59
- # @param other [Object]
60
- # @return [Boolean]
61
- #
62
- # @since 0.1.0
63
- def eql?(other)
64
- return true if equal?(other)
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
- if other.is_a?(VectorNumber)
67
- other.size == size && other.each_pair.all? { |u, c| @data[u] == c }
68
- else
69
- false
70
- end
71
- end
69
+ size.eql?(other.size) && @data.eql?(other.to_h)
70
+ end
72
71
 
73
- # Generate an Integer hash value for self.
74
- #
75
- # Hash values are stable during runtime, but not between processes.
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
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
- # Compare to +other+ and return -1, 0, or 1
90
- # if +self+ is less than, equal, or larger than +other+.
91
- #
92
- # @example
93
- # VectorNumber[130] <=> 12 # => 1
94
- # 1 <=> VectorNumber[13] # => -1
95
- # VectorNumber[12.1] <=> Complex(12.1, 0) # => 0
96
- # # This doesn't work as expected without NumericRefinements:
97
- # Complex(12.1, 0) <=> VectorNumber[12.1] # => nil
98
- #
99
- # # Any non-real comparison returns nil:
100
- # VectorNumber[12.1] <=> Complex(12.1, 1) # => nil
101
- # VectorNumber[12.1i] <=> 2 # => nil
102
- # VectorNumber["a"] <=> 2 # => nil
103
- #
104
- # @param other [Object]
105
- # @return [Integer]
106
- # @return [nil] if +self+ or +other+ isn't a real number.
107
- #
108
- # @see Comparable
109
- # @see NumericRefinements
110
- #
111
- # @since 0.2.0
112
- def <=>(other)
113
- return nil unless numeric?(1)
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
- case other
116
- when VectorNumber
117
- other.numeric?(1) ? real <=> other.real : nil
118
- when Numeric
119
- other.imaginary.zero? ? real <=> other.real : nil
120
- else
121
- nil
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
- # Methods for converting to different number classes.
5
- module Converting
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
- numeric?(1) ? real.to_i : raise_convert_error(Integer)
49
- end
50
-
51
- # @since 0.1.0
52
- alias to_int to_i
53
-
54
- # Return value as a Float if only real part is non-zero.
55
- #
56
- # @example
57
- # VectorNumber[13.5].to_f # => 13.5
58
- # VectorNumber[13r/12].to_f # => 1.0833333333333333
59
- # VectorNumber[2, 2i].to_f # RangeError
60
- # VectorNumber[2, :i].to_f # RangeError
61
- #
62
- # Float(VectorNumber[2]) # => 2.0
63
- # Float(VectorNumber[2, "i"]) # RangeError
64
- #
65
- # @return [Float]
66
- # @raise [RangeError] if any non-real part is non-zero
67
- #
68
- # @since 0.1.0
69
- def to_f
70
- numeric?(1) ? real.to_f : raise_convert_error(Float)
71
- end
72
-
73
- # Return value as a Rational if only real part is non-zero.
74
- #
75
- # @example
76
- # VectorNumber[13.5].to_r # => (27/2)
77
- # VectorNumber[13r/12].to_r # => (13/12)
78
- # VectorNumber[2, 2i].to_r # RangeError
79
- # VectorNumber[2, :i].to_r # RangeError
80
- #
81
- # Rational(VectorNumber[2]) # => (2/1)
82
- # Rational(VectorNumber[2, "i"]) # RangeError
83
- #
84
- # @return [Rational]
85
- # @raise [RangeError] if any non-real part is non-zero
86
- #
87
- # @since 0.1.0
88
- def to_r
89
- numeric?(1) ? real.to_r : raise_convert_error(Rational)
90
- end
91
-
92
- # Return value as a BigDecimal if only real part is non-zero.
93
- #
94
- # @example
95
- # VectorNumber[13.5].to_d # => 0.135e2
96
- # VectorNumber[13r/12].to_d # ArgumentError
97
- # VectorNumber[13r/12].to_d(5) # => 0.10833e1
98
- # VectorNumber[2, 2i].to_d # RangeError
99
- # VectorNumber[2, :i].to_d # RangeError
100
- #
101
- # # This does't work without NumericRefinements:
102
- # BigDecimal(VectorNumber[2]) # TypeError
103
- # # #to_s can be used as a workaround if refinements aren't used:
104
- # BigDecimal(VectorNumber[2].to_s) # => 0.2e1
105
- # BigDecimal(VectorNumber[2, "i"].to_s) # => ArgumentError
106
- #
107
- # @param ndigits [Integer] precision
108
- # @return [BigDecimal]
109
- # @raise [RangeError] if any non-real part is non-zero
110
- # @raise [NameError] if BigDecimal is not defined
111
- #
112
- # @see Kernel.BigDecimal
113
- # @see NumericRefinements
114
- #
115
- # @since 0.1.0
116
- def to_d(ndigits = nil)
117
- if numeric?(1)
118
- return BigDecimal(real, ndigits) if ndigits
119
- return BigDecimal(real, Float::DIG) if real.is_a?(Float)
120
-
121
- BigDecimal(real)
122
- else
123
- raise_convert_error(BigDecimal)
124
- end
125
- end
126
-
127
- # Return value as a Complex if only real and/or imaginary parts are non-zero.
128
- #
129
- # @example
130
- # VectorNumber[13.5].to_c # => (13.5+0i)
131
- # VectorNumber[13r/12].to_c # => ((13/12)+0i)
132
- # VectorNumber[2, 2i].to_c # => (2+2i)
133
- # VectorNumber[2, :i].to_c # RangeError
134
- #
135
- # Complex(VectorNumber[2]) # => (2+0i)
136
- # Complex(VectorNumber[2, "i"]) # RangeError
137
- #
138
- # @return [Complex]
139
- # @raise [RangeError] if any non-real, non-imaginary part is non-zero
140
- #
141
- # @since 0.1.0
142
- def to_c
143
- numeric?(2) ? Complex(real, imaginary) : raise_convert_error(Complex)
144
- end
145
-
146
- private
147
-
148
- def raise_convert_error(klass)
149
- raise RangeError, "can't convert #{self} into #{klass}", caller
150
- end
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