vector_number 0.2.5 → 0.3.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: b8fc99d66449b0cbc1ac4f2871e540ff921dee5d291b487221193edaefca1b2f
4
- data.tar.gz: 38f15f406bff5348debb50b5468bf416e2fd3bca4be69257e3b59b99fbebd6ed
3
+ metadata.gz: c55a93e873d8e98092a5be77940f3f91cf06e90abb973f16d2fc91f0df0276ca
4
+ data.tar.gz: 446cb27d60ff3b588ffbc6fa49489013d6a2cfc8e6f2b577abc4a40b47587658
5
5
  SHA512:
6
- metadata.gz: 23f819566a189655bcefaa7d22005e7de4e7349ad99718497e13faf97a748d6c9c63788146379473d28caed9bc7063a13787074f0c87241a1a36859a30a126a7
7
- data.tar.gz: 33c4ce966c7ce9d3d1a486acd6119af50d9458c8048e2a6f07348625bb50bcb11d1db50c8dbee75ba81be996afe53bcb25af2b356ab9be0e1a6689a67d3a14dd
6
+ metadata.gz: 2a069408f786816659721505e8e09d50aeaf891e6dcc85c35d008a161500548e74c3cb0224a8992521c3e9385cb8cabe6c9b565874981c0098d5fb5c081ee65f
7
+ data.tar.gz: 99c2b90210dfceaeec85f6f8fd897074296c27ba6e9a6cb8ffcd0d407b3f65482b445b721fd5a41139a339d5d54f8ce81f4860dfdffccc0db1909002744fa8fe
data/CHANGELOG.md CHANGED
@@ -6,6 +6,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Next]
9
+ ## [v0.3.0] — 2025-05-12
10
+
11
+ **Added**
12
+ - Add aliases to other operators:
13
+ `#neg` for `#-@`, `#add` for `#+`, `#sub` for `#-`, `#mult` for `#*`.
14
+ `#+@` was already practically aliased by `#dup`.
15
+
16
+ **Changed**
17
+ - [Breaking] Long-existing but broken options feature is now fixed.
18
+ When creating new vector through any operation, participating vector's options
19
+ are copied to the new one. When several vectors are present, only first one matters.
20
+ - Both `#+@` and `#dup` are now aliases of `#itself` instead of full methods.
21
+ - [🚀 CI] Refactor workflows to reduce duplication.
22
+
23
+ ## [v0.2.6] — 2025-04-30
24
+
25
+ **Added**
26
+ - Add `#div`, `#%` (aliased as `#modulo`), `#divmod` and `#remainder` methods.
27
+ - Add `#quo` alias to `#/`.
28
+
29
+ **Fixed**
30
+ - `#/`, `#fdiv` as well as new division methods now properly check for division by zero.
31
+ VectorNumber does not support this as not all Numeric classes do.
32
+
33
+ **Changed**
34
+ - [🚀 CI] Add Ruby 3.4 to CI.
35
+
9
36
  ## [v0.2.5] — 2025-02-26
10
37
 
11
38
  Technical update after release to rubygems.org.
@@ -17,12 +44,13 @@ README was updated to reflect this change.
17
44
  - Add hash-like methods `#[]` and `#unit?` (aliased as `#key?`).
18
45
 
19
46
  **Changed**
20
- - [Breaking] Change `positive?` and `negative?` to no longer return `nil`,
21
- those cases will now return `false`.
47
+ - [Breaking] Change `positive?` and `negative?` to no longer return `nil`
48
+ when number is neither strictly positive or negative,
49
+ these cases will now return `false`.
22
50
  - Make `VectorNumber.new` accept options when initializing from a VectorNumber
23
51
  instead of only copying. Options will be merged.
24
52
  - Remove `Initializing` module, move its methods to the actual class.
25
- - Updated development gem versions.
53
+ - Update development gems' versions.
26
54
 
27
55
  **Fixed**
28
56
  - `#dup` and `#clone` now behave exactly like Numeric versions, preventing unfreezing.
@@ -39,9 +67,9 @@ README was updated to reflect this change.
39
67
  - Add `#ceil`, `#floor` and `#round`.
40
68
 
41
69
  **Changed**
42
- - Add ruby 3.1.0, covering the earliest supported version, and ruby-next (3.4) to CI.
43
- - Add JRuby and TruffleRuby to CI, without full support.
44
- - Make tests runnable even without available `bigdecimal` gem.
70
+ - [🚀 CI] Add ruby 3.1.0, covering the earliest supported version, and ruby-next (3.4) to CI.
71
+ - [🚀 CI] Add JRuby and TruffleRuby to CI, without full support.
72
+ - [🚀 CI] Make tests runnable even without available `bigdecimal` gem.
45
73
 
46
74
  **Fixed**
47
75
  - `Kernel#BigDecimal` refinement now correctly works without `ndigits` argument.
@@ -49,7 +77,7 @@ README was updated to reflect this change.
49
77
  ## [v0.2.1] — 2024-08-24
50
78
 
51
79
  **Added**
52
- - Add back `#*` and `#/` for working with real numbers.
80
+ - Add `#*` and `#/` for working with real numbers.
53
81
  - Add `#fdiv`, `#truncate`, `#nonnumeric?` and `#integer?`.
54
82
 
55
83
  **Changed**
@@ -72,3 +100,5 @@ README was updated to reflect this change.
72
100
  ## [v0.1.0] — 2024-05-09
73
101
 
74
102
  - Initial work.
103
+
104
+ [🚀 CI]: https://github.com/trinistr/vector_number/actions/workflows/CI.yaml
data/README.md CHANGED
@@ -1,9 +1,6 @@
1
1
  # VectorNumber
2
2
 
3
- ![CRuby validation](https://github.com/trinistr/vector_number/actions/workflows/cruby.yaml/badge.svg)
4
- <!--
5
- ![TruffleRuby validation](https://github.com/trinistr/vector_number/actions/workflows/truffleruby.yaml/badge.svg)
6
- -->
3
+ [![CI](https://github.com/trinistr/vector_number/actions/workflows/CI.yaml/badge.svg)](https://github.com/trinistr/vector_number/actions/workflows/CI.yaml)
7
4
 
8
5
  A library to add together anything: be it a number, string or random Object, it can be added together in an infinite-dimensional vector space, with math operations available on results.
9
6
 
@@ -16,7 +13,9 @@ Similar projects:
16
13
 
17
14
  ## Installation
18
15
 
19
- Install with `gem`:
16
+ VectorNumber does not have any dependencies and does not include extensions.
17
+
18
+ Install with `gem` (available from 0.2.4):
20
19
  ```sh
21
20
  gem install vector_number
22
21
  ```
@@ -58,9 +57,9 @@ VectorNumber.new([4, "death", "death", 13, nil])
58
57
  ## Ruby engine support status
59
58
 
60
59
  VectorNumber is developed on MRI (CRuby) but should work on other engines too.
61
- - JRuby: should work, but currently CI does not pass.
62
60
  - TruffleRuby: works as expected, but there are differences in core Ruby code, so some tests fail.
63
- - Other engines: untested, but should work.
61
+ - JRuby: significant problems (at least on CI), but may work.
62
+ - Other engines: untested, but should work, depending on compatibility.
64
63
 
65
64
  ## Development
66
65
 
@@ -3,10 +3,10 @@
3
3
  class VectorNumber
4
4
  # Various mathematical operations that are also conversions.
5
5
  module MathConverting
6
- # Return the absolute value of a vector, i.e. its length.
6
+ # Return the absolute value of the vector, i.e. its length.
7
7
  # @return [Float]
8
8
  def abs
9
- Math.sqrt(coefficients.reduce(0.0) { |result, coefficient| result + coefficient.abs2 })
9
+ Math.sqrt(coefficients.sum(&:abs2)) # rubocop:disable Naming/VariableNumber
10
10
  end
11
11
 
12
12
  alias magnitude abs
@@ -18,9 +18,7 @@ class VectorNumber
18
18
 
19
19
  # Return self.
20
20
  # @return [VectorNumber]
21
- def +@
22
- self
23
- end
21
+ alias +@ itself
24
22
 
25
23
  # Return new vector with negated coefficients.
26
24
  # This preserves order of units.
@@ -29,6 +27,8 @@ class VectorNumber
29
27
  new(&:-@)
30
28
  end
31
29
 
30
+ alias neg -@
31
+
32
32
  # Return new vector as a sum of this and other value.
33
33
  # This is analogous to {VectorNumber.[]}.
34
34
  # @param other [Object]
@@ -37,6 +37,8 @@ class VectorNumber
37
37
  new([self, other])
38
38
  end
39
39
 
40
+ alias add +
41
+
40
42
  # Return new vector as a sum of this and negative of the other value.
41
43
  # This is analogous to {VectorNumber.[]}, but allows to negate anything.
42
44
  # @param other [Object]
@@ -45,6 +47,8 @@ class VectorNumber
45
47
  self + new([other], &:-@)
46
48
  end
47
49
 
50
+ alias sub -
51
+
48
52
  # Multiply all coefficients by a real number, returning new vector.
49
53
  # This effectively multiplies magnitude by the specified factor.
50
54
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
@@ -63,13 +67,16 @@ class VectorNumber
63
67
  end
64
68
  end
65
69
 
70
+ alias mult *
71
+
66
72
  # Divide all coefficients by a real number, returning new vector.
67
- # This effectively multiplies magnitude by reciprocal of the specified factor.
73
+ # This effectively multiplies magnitude by reciprocal of +other+.
68
74
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
69
75
  # @return [VectorNumber]
70
76
  # @raise [RangeError] if +other+ is not a number or is not a real number
77
+ # @raise [ZeroDivisionError] if +other+ is zero
71
78
  def /(other)
72
- raise RangeError, "can't divide #{self} by #{other}" unless real_number?(other)
79
+ check_divisibility(other)
73
80
 
74
81
  other = other.real
75
82
  # Prevent integer division, but without loss of accuracy.
@@ -78,16 +85,87 @@ class VectorNumber
78
85
  new { _1 / other }
79
86
  end
80
87
 
88
+ alias quo /
89
+
81
90
  # Divide all coefficients by a real number using +fdiv+, returning new vector
82
91
  # with Float coefficients.
83
92
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
84
93
  # @return [VectorNumber]
85
94
  # @raise [RangeError] if +other+ is not a number or is not a real number
95
+ # @raise [ZeroDivisionError] if +other+ is zero
86
96
  def fdiv(other)
87
- raise RangeError, "can't divide #{self} by #{other}" unless real_number?(other)
97
+ check_divisibility(other)
88
98
 
89
99
  other = other.real
90
100
  new { _1.fdiv(other) }
91
101
  end
102
+
103
+ # Divide all coefficients by +other+, rounding results with {#floor}.
104
+ # This is requal to +(self / other).floor+.
105
+ # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
106
+ # @return [VectorNumber]
107
+ # @raise [RangeError] if +other+ is not a number or is not a real number
108
+ # @raise [ZeroDivisionError] if +other+ is zero
109
+ # @see #divmod
110
+ # @see #%
111
+ def div(other)
112
+ check_divisibility(other)
113
+
114
+ other = other.real
115
+ new { _1.div(other) }
116
+ end
117
+
118
+ # Return the modulus of dividing self by +other+ as a vector.
119
+ # This is equal to +self - other * (self/other).floor+.
120
+ # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
121
+ # @return [VectorNumber]
122
+ # @raise [RangeError] if +other+ is not a number or is not a real number
123
+ # @raise [ZeroDivisionError] if +other+ is zero
124
+ # @see #divmod
125
+ # @see #div
126
+ # @see #remainder for alternative
127
+ # @see Numeric#% for examples
128
+ def %(other)
129
+ check_divisibility(other)
130
+
131
+ other = other.real
132
+ new { _1 % other }
133
+ end
134
+
135
+ alias modulo %
136
+
137
+ # Return the quotient and modulus of dividing self by +other+.
138
+ # There is no performance benefit compared to calling {#div} and {#%} separately.
139
+ # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
140
+ # @return [Array(VectorNumber, VectorNumber)]
141
+ # @raise [RangeError] if +other+ is not a number or is not a real number
142
+ # @raise [ZeroDivisionError] if +other+ is zero
143
+ # @see #div
144
+ # @see #%
145
+ def divmod(other)
146
+ [div(other), modulo(other)]
147
+ end
148
+
149
+ # Return the remainder of dividing self by +other+ as a vector.
150
+ # This is equal to +self - other * (self/other).truncate+.
151
+ # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
152
+ # @return [VectorNumber]
153
+ # @raise [RangeError] if +other+ is not a number or is not a real number
154
+ # @raise [ZeroDivisionError] if +other+ is zero
155
+ # @see #% for alternative
156
+ # @see Numeric#remainder for examples
157
+ def remainder(other)
158
+ check_divisibility(other)
159
+
160
+ other = other.real
161
+ new { _1.remainder(other) }
162
+ end
163
+
164
+ private
165
+
166
+ def check_divisibility(other)
167
+ raise RangeError, "can't divide #{self} by #{other}", caller unless real_number?(other)
168
+ raise ZeroDivisionError, "divided by 0", caller if other.zero?
169
+ end
92
170
  end
93
171
  end
@@ -65,14 +65,14 @@ class VectorNumber
65
65
  !zero? && all? { |_u, c| c.negative? }
66
66
  end
67
67
 
68
- # Always returns +false+.
68
+ # Always returns +false+, as vectors are never real numbers.
69
69
  # @see #numeric?
70
70
  # @return [false]
71
71
  def real?
72
72
  false
73
73
  end
74
74
 
75
- # Always returns +false+.
75
+ # Always returns +false+, as vectors are not +Integer+s.
76
76
  # @return [false]
77
77
  def integer?
78
78
  false
@@ -36,6 +36,7 @@ class VectorNumber
36
36
 
37
37
  # @return [String]
38
38
  def inspect
39
+ # TODO: Probably make this independent of options.
39
40
  "(#{self})"
40
41
  end
41
42
 
@@ -48,7 +49,7 @@ class VectorNumber
48
49
  # @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
49
50
  def value_to_s(unit, coefficient, mult:)
50
51
  if !mult.is_a?(String) && !MULT_STRINGS.key?(mult)
51
- raise ArgumentError, "unknown key :#{mult}", caller
52
+ raise ArgumentError, "unknown key #{mult.inspect}", caller
52
53
  end
53
54
 
54
55
  case unit
@@ -2,5 +2,5 @@
2
2
 
3
3
  class VectorNumber # rubocop:disable Style/StaticClass
4
4
  # @return [String]
5
- VERSION = "0.2.5"
5
+ VERSION = "0.3.0"
6
6
  end
data/lib/vector_number.rb CHANGED
@@ -54,18 +54,15 @@ class VectorNumber
54
54
  # @param values [Array, Hash{Object => Integer, Float, Rational, BigDecimal}, VectorNumber]
55
55
  # values for this number, hashes are treated like plain vector numbers
56
56
  # @param options [Hash{Symbol => Object}]
57
- # options for this number, if +values+ is a VectorNumber,
58
- # these will be merged with options from +values.options+
57
+ # options for this number, if +values+ is a VectorNumber or contains it,
58
+ # these will be merged with options from its +options+
59
59
  # @option options [Symbol, String] :mult
60
60
  # text to use between unit and coefficient, see {Stringifying#to_s} for explanation
61
61
  # @yieldparam coefficient [Integer, Float, Rational, BigDecimal]
62
62
  # @yieldreturn [Integer, Float, Rational, BigDecimal] new coefficient
63
63
  # @raise [RangeError] if any pesky non-reals get where they shouldn't
64
- def initialize(values = nil, options = {}.freeze, &)
65
- # TODO: propagate options properly.
66
- # > (VectorNumber[1, 'a', mult: :invisible] + 2).options
67
- # => {:mult=>:dot}
68
- super()
64
+ def initialize(values = nil, options = nil, &)
65
+ # @type var options: Hash[Symbol, Symbol]
69
66
  initialize_from(values)
70
67
  apply_transform(&)
71
68
  finalize_contents
@@ -78,9 +75,7 @@ class VectorNumber
78
75
  # Return self.
79
76
  #
80
77
  # @return [VectorNumber]
81
- def dup
82
- self
83
- end
78
+ alias dup itself
84
79
 
85
80
  # Return self.
86
81
  #
@@ -106,7 +101,7 @@ class VectorNumber
106
101
  # @yieldreturn [Integer, Float, Rational, BigDecimal] new coefficient
107
102
  # @return [VectorNumber]
108
103
  def new(from = self, &)
109
- self.class.new(from, &)
104
+ self.class.new(from, options, &)
110
105
  end
111
106
 
112
107
  # @param value [Object]
@@ -183,15 +178,26 @@ class VectorNumber
183
178
  case [options, values]
184
179
  in [{} | nil, VectorNumber]
185
180
  values.options
181
+ in [{} | nil, [*, VectorNumber => vector, *]]
182
+ vector.options
186
183
  in Hash, VectorNumber
187
- values.options.merge(options).slice(*known_options)
184
+ merge_options(values.options, options)
185
+ in Hash, [*, VectorNumber => vector, *]
186
+ merge_options(vector.options, options)
188
187
  in Hash, _ unless options.empty?
189
- default_options.merge(options).slice(*known_options)
188
+ merge_options(default_options, options)
190
189
  else
191
190
  default_options
192
191
  end
193
192
  end
194
193
 
194
+ def merge_options(base_options, added_options)
195
+ # Optimization for the common case of passing options through #new.
196
+ return base_options if added_options.equal?(base_options)
197
+
198
+ base_options.merge(added_options).slice(*known_options)
199
+ end
200
+
195
201
  # Compact coefficients, calculate size and freeze data.
196
202
  # @return [void]
197
203
  def finalize_contents
@@ -1,3 +1,19 @@
1
+ type vector_type = VectorNumber
2
+ type real_number = Integer | Float | Rational | BigDecimal
3
+
4
+ type in_value_type = untyped
5
+ type unit_type = untyped
6
+ type coefficient_type = real_number
7
+
8
+ type list[T] = Array[T]
9
+
10
+ type plain_vector_type = Hash[unit_type, coefficient_type]
11
+ type units_list_type = list[unit_type]
12
+ type coefficients_list_type = list[coefficient_type]
13
+ type each_value_type = [unit_type, coefficient_type]
14
+
15
+ type options_type = Hash[Symbol, untyped]
16
+
1
17
  interface _BaseMethods
2
18
  def size: -> Integer
3
19
  def options: -> options_type
@@ -35,7 +51,7 @@ class VectorNumber
35
51
  def self.[]: (*unit_type values, **options_type options) -> vector_type
36
52
 
37
53
  def initialize:
38
- (?(units_list_type | plain_vector_type | vector_type)? values, ?options_type options)
54
+ (?(units_list_type | plain_vector_type | vector_type)? values, ?options_type? options)
39
55
  ?{ (coefficient_type coefficient) -> coefficient_type }
40
56
  -> void
41
57
 
@@ -74,16 +90,34 @@ class VectorNumber
74
90
  def +@: () -> self
75
91
 
76
92
  def -@: () -> vector_type
93
+ alias neg -@
77
94
 
78
95
  def +: (in_value_type) -> vector_type
96
+ alias add +
79
97
 
80
98
  def -: (in_value_type) -> vector_type
99
+ alias sub -
81
100
 
82
101
  def *: (real_number | vector_type) -> vector_type
102
+ alias mult *
83
103
 
84
104
  def /: (real_number | vector_type) -> vector_type
105
+ alias quo /
85
106
 
86
107
  def fdiv: (real_number | vector_type) -> vector_type
108
+
109
+ def div: (real_number | vector_type) -> vector_type
110
+
111
+ def %: (real_number | vector_type) -> vector_type
112
+ alias modulo %
113
+
114
+ def divmod: (real_number | vector_type) -> [vector_type, vector_type]
115
+
116
+ def remainder: (real_number | vector_type) -> vector_type
117
+
118
+ private
119
+
120
+ def check_divisibility: (real_number | vector_type) -> void
87
121
  end
88
122
 
89
123
  # Various mathematical operations that are also conversions.
@@ -100,6 +134,8 @@ class VectorNumber
100
134
  def ceil: (Integer) -> vector_type
101
135
 
102
136
  def floor: (Integer) -> vector_type
137
+
138
+ def round: (Integer, ?half: (:up | :down | :even)) -> vector_type
103
139
  end
104
140
 
105
141
  # Methods for converting to different number classes.
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vector_number
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandr Bulancov
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-25 00:00:00.000000000 Z
10
+ date: 2025-05-12 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  executables: []
13
13
  extensions: []
@@ -27,7 +27,6 @@ files:
27
27
  - lib/vector_number/querying.rb
28
28
  - lib/vector_number/stringifying.rb
29
29
  - lib/vector_number/version.rb
30
- - sig/definitions.rbs
31
30
  - sig/vector_number.rbs
32
31
  homepage: https://github.com/trinistr/vector_number
33
32
  licenses:
data/sig/definitions.rbs DELETED
@@ -1,15 +0,0 @@
1
- type vector_type = VectorNumber
2
- type real_number = Integer | Float | Rational | BigDecimal
3
-
4
- type in_value_type = untyped
5
- type unit_type = untyped
6
- type coefficient_type = real_number
7
-
8
- type list[T] = Array[T]
9
-
10
- type plain_vector_type = Hash[unit_type, coefficient_type]
11
- type units_list_type = list[unit_type]
12
- type coefficients_list_type = list[coefficient_type]
13
- type each_value_type = [unit_type, coefficient_type]
14
-
15
- type options_type = Hash[Symbol, untyped]