vector_number 0.2.4
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 +7 -0
- data/CHANGELOG.md +69 -0
- data/README.md +74 -0
- data/lib/vector_number/comparing.rb +55 -0
- data/lib/vector_number/converting.rb +72 -0
- data/lib/vector_number/enumerating.rb +71 -0
- data/lib/vector_number/math_converting.rb +59 -0
- data/lib/vector_number/mathing.rb +93 -0
- data/lib/vector_number/numeric_refinements.rb +54 -0
- data/lib/vector_number/querying.rb +81 -0
- data/lib/vector_number/stringifying.rb +66 -0
- data/lib/vector_number/version.rb +6 -0
- data/lib/vector_number.rb +210 -0
- data/sig/definitions.rbs +15 -0
- data/sig/vector_number.rbs +230 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d8a4ba56677fa1eab6fc6dba7e8a7cc9d4afd769d9d0a4186ee51b2f400f2bab
|
4
|
+
data.tar.gz: 57f4d03d9495c2f6e98afb25008f659f63cf5cf0723664f1a3e7633568a8a179
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 020ae7c79a25d4d18b6c2798d1c903e5f3a9699f8a2822ad7617ef3ada0ed4e2ef158a324548dfdc68de1a2bd70789487929d8d68def9b47133e077a4654d277
|
7
|
+
data.tar.gz: d34fb46be17a6cec0916257afcaccb842cdd24b7aa055377c1dbd343e6ffd90295df2a9725bf549709f6997276f88e1c1766f079c4848dc002b645180678e912
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Next]
|
9
|
+
## [v0.2.4] — 2025-02-26
|
10
|
+
|
11
|
+
**Added**
|
12
|
+
- Add hash-like methods `#[]` and `#unit?` (aliased as `#key?`).
|
13
|
+
|
14
|
+
**Changed**
|
15
|
+
- [Breaking] Change `positive?` and `negative?` to no longer return `nil`,
|
16
|
+
those cases will now return `false`.
|
17
|
+
- Make `VectorNumber.new` accept options when initializing from a VectorNumber
|
18
|
+
instead of only copying. Options will be merged.
|
19
|
+
- Remove `Initializing` module, move its methods to the actual class.
|
20
|
+
- Updated development gem versions.
|
21
|
+
|
22
|
+
**Fixed**
|
23
|
+
- `#dup` and `#clone` now behave exactly like Numeric versions, preventing unfreezing.
|
24
|
+
|
25
|
+
## [v0.2.3] — 2024-10-15
|
26
|
+
|
27
|
+
**Fixed**
|
28
|
+
- BigDecimal tests are now properly skipped when BigDecimal is not available, instead of always.
|
29
|
+
|
30
|
+
## [v0.2.2] — 2024-10-07
|
31
|
+
|
32
|
+
**Added**
|
33
|
+
- Add `#abs` (aliased as `#magnitude`) and `#abs2`.
|
34
|
+
- Add `#ceil`, `#floor` and `#round`.
|
35
|
+
|
36
|
+
**Changed**
|
37
|
+
- Add ruby 3.1.0, covering the earliest supported version, and ruby-next (3.4) to CI.
|
38
|
+
- Add JRuby and TruffleRuby to CI, without full support.
|
39
|
+
- Make tests runnable even without available `bigdecimal` gem.
|
40
|
+
|
41
|
+
**Fixed**
|
42
|
+
- `Kernel#BigDecimal` refinement now correctly works without `ndigits` argument.
|
43
|
+
|
44
|
+
## [v0.2.1] — 2024-08-24
|
45
|
+
|
46
|
+
**Added**
|
47
|
+
- Add back `#*` and `#/` for working with real numbers.
|
48
|
+
- Add `#fdiv`, `#truncate`, `#nonnumeric?` and `#integer?`.
|
49
|
+
|
50
|
+
**Changed**
|
51
|
+
- Allow to use fully real VectorNumbers as real numbers.
|
52
|
+
|
53
|
+
**Fixed**
|
54
|
+
- Fix reversed result in refined `#<=>`.
|
55
|
+
|
56
|
+
**Removed**
|
57
|
+
- Remove `#to_str`, as VectorNumber is not a String-like object.
|
58
|
+
- Due to the above, `Kernel.BigDecimal` no longer works without refinements.
|
59
|
+
|
60
|
+
## [v0.2.0] — 2024-08-19
|
61
|
+
|
62
|
+
**Added**
|
63
|
+
- VectorNumbers can be created from any object or collection.
|
64
|
+
- Addition and subtraction are supported.
|
65
|
+
- VectorNumbers are mostly interoperable with core numbers.
|
66
|
+
|
67
|
+
## [v0.1.0] — 2024-05-09
|
68
|
+
|
69
|
+
- Initial work.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# VectorNumber
|
2
|
+
|
3
|
+

|
4
|
+
<!--
|
5
|
+

|
6
|
+
-->
|
7
|
+
|
8
|
+
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
|
+
|
10
|
+
This is similar in a sense to hypercomplex numbers, such as quaternions, but with a focus on arbitrary dimensions.
|
11
|
+
|
12
|
+
Similar projects:
|
13
|
+
- [vector_space](https://github.com/tomstuart/vector_space) aims to provide typed vector spaces with limited dimensions and nice formatting.
|
14
|
+
- [named_vector](https://rubygems.org/gems/named_vector) provides simple vectors with named dimensions.
|
15
|
+
- Various quaternion libraries.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add gem to your Gemfile:
|
20
|
+
```ruby
|
21
|
+
gem "vector_number", git: "https://github.com/trinistr/vector_number.git"
|
22
|
+
```
|
23
|
+
|
24
|
+
Installation through `gem` is not currently supported.
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
Usage is pretty simple and intuitive:
|
29
|
+
```ruby
|
30
|
+
require "vector_number"
|
31
|
+
VectorNumber["string"] + "str" # => (1⋅'string' + 1⋅'str')
|
32
|
+
VectorNumber[5] + VectorNumber["string"] - 0.5 # => (4.5 + 1⋅'string')
|
33
|
+
VectorNumber["string", "string", "string", "str"] # => (3⋅'string' + 1⋅'str')
|
34
|
+
VectorNumber[:s] * 2 + VectorNumber["string"] * 0.3 # => (2⋅s + 0.3⋅'string')
|
35
|
+
VectorNumber[:s] / 3 # => (1/3⋅s)
|
36
|
+
1/3r * VectorNumber[[]] # => (1/3⋅[])
|
37
|
+
```
|
38
|
+
|
39
|
+
VectorNumbers are mostly useful for summing up heterogeneous objects:
|
40
|
+
```ruby
|
41
|
+
sum = VectorNumber[]
|
42
|
+
[4, "death", "death", 13, nil].each { sum = sum + _1 }
|
43
|
+
sum # => (17 + 2⋅'death' + 1⋅)
|
44
|
+
sum.to_a # => [[(0+0i), 17], ["death", 2], [nil, 1]]
|
45
|
+
sum.to_h # => {(0+0i)=>17, "death"=>2, nil=>1}
|
46
|
+
```
|
47
|
+
|
48
|
+
Alternatively, the same result can be equivalently (and more efficiently) achieved by
|
49
|
+
passing all values to a constructor:
|
50
|
+
```ruby
|
51
|
+
VectorNumber[4, "death", "death", 13, nil]
|
52
|
+
VectorNumber.new([4, "death", "death", 13, nil])
|
53
|
+
```
|
54
|
+
|
55
|
+
## Ruby engine support status
|
56
|
+
|
57
|
+
VectorNumber is developed on MRI (CRuby) but should work on other engines too.
|
58
|
+
- JRuby: should work, but currently CI does not pass.
|
59
|
+
- TruffleRuby: works as expected, but there are differences in core Ruby code, so some tests fail.
|
60
|
+
- Other engines: untested, but should work.
|
61
|
+
|
62
|
+
## Development
|
63
|
+
|
64
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests, `rake rubocop` to check code, `rake steep` to check typing or just `rake` to do everything. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
65
|
+
|
66
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, change Next version in `CHANGELOG.md`, commit changes and tag the commit. Alternatively, an appropriate `rake bump:` command can be used.
|
67
|
+
|
68
|
+
## Contributing
|
69
|
+
|
70
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/trinistr/vector_number]().
|
71
|
+
|
72
|
+
## License
|
73
|
+
|
74
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Methods for comparing with other numbers.
|
5
|
+
module Comparing
|
6
|
+
include ::Comparable
|
7
|
+
|
8
|
+
# Compare to +other+ for equality.
|
9
|
+
#
|
10
|
+
# Values are considered equal if
|
11
|
+
# - +other+ is a VectorNumber and it is +eql?+ to this one, or
|
12
|
+
# - +other+ is a Numeric equal in value to this number.
|
13
|
+
#
|
14
|
+
# @param other [Object]
|
15
|
+
# @return [Boolean]
|
16
|
+
def ==(other)
|
17
|
+
return true if eql?(other)
|
18
|
+
|
19
|
+
case other
|
20
|
+
when Numeric
|
21
|
+
numeric?(2) && other.real == real && other.imaginary == imaginary
|
22
|
+
else
|
23
|
+
# Can't compare a number-like value to a non-number.
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param other [Object]
|
29
|
+
# @return [Boolean]
|
30
|
+
def eql?(other)
|
31
|
+
return true if equal?(other)
|
32
|
+
|
33
|
+
if other.is_a?(VectorNumber)
|
34
|
+
other.size == size && other.each_pair.all? { |u, c| @data[u] == c }
|
35
|
+
else
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param other [Object]
|
41
|
+
# @return [Integer, nil]
|
42
|
+
def <=>(other)
|
43
|
+
return nil unless numeric?(1)
|
44
|
+
|
45
|
+
case other
|
46
|
+
when VectorNumber
|
47
|
+
other.numeric?(1) ? real <=> other.real : nil
|
48
|
+
when Numeric
|
49
|
+
other.imaginary.zero? ? real <=> other.real : nil
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Methods for converting to different number classes.
|
5
|
+
module Converting
|
6
|
+
# Return real part of the number.
|
7
|
+
# @return [Integer, Float, Rational, BigDecimal]
|
8
|
+
def real
|
9
|
+
@data[R]
|
10
|
+
end
|
11
|
+
|
12
|
+
# Return imaginary part of the number.
|
13
|
+
# @return [Integer, Float, Rational, BigDecimal]
|
14
|
+
def imaginary
|
15
|
+
@data[I]
|
16
|
+
end
|
17
|
+
|
18
|
+
alias imag imaginary
|
19
|
+
|
20
|
+
# Return value as an Integer, truncating it, if only real part is non-zero.
|
21
|
+
# @return [Integer]
|
22
|
+
# @raise [RangeError] if any non-real part is non-zero
|
23
|
+
def to_i
|
24
|
+
numeric?(1) ? real.to_i : raise_convert_error(Integer)
|
25
|
+
end
|
26
|
+
|
27
|
+
alias to_int to_i
|
28
|
+
|
29
|
+
# Return value as a Float if only real part is non-zero.
|
30
|
+
# @return [Float]
|
31
|
+
# @raise [RangeError] if any non-real part is non-zero
|
32
|
+
def to_f
|
33
|
+
numeric?(1) ? real.to_f : raise_convert_error(Float)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return value as a Rational if only real part is non-zero.
|
37
|
+
# @return [Rational]
|
38
|
+
# @raise [RangeError] if any non-real part is non-zero
|
39
|
+
def to_r
|
40
|
+
numeric?(1) ? real.to_r : raise_convert_error(Rational)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return value as a BigDecimal if only real part is non-zero.
|
44
|
+
# @param ndigits [Integer] precision
|
45
|
+
# @return [BigDecimal]
|
46
|
+
# @raise [RangeError] if any non-real part is non-zero
|
47
|
+
# @raise [NameError] if BigDecimal is not defined
|
48
|
+
def to_d(ndigits = nil)
|
49
|
+
if numeric?(1)
|
50
|
+
return BigDecimal(real, ndigits) if ndigits
|
51
|
+
return BigDecimal(real, Float::DIG) if real.is_a?(Float)
|
52
|
+
|
53
|
+
BigDecimal(real)
|
54
|
+
else
|
55
|
+
raise_convert_error(BigDecimal)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return value as a Complex if only real and/or imaginary parts are non-zero.
|
60
|
+
# @return [Complex]
|
61
|
+
# @raise [RangeError] if any non-real, non-imaginary part is non-zero
|
62
|
+
def to_c
|
63
|
+
numeric?(2) ? Complex(real, imaginary) : raise_convert_error(Complex)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def raise_convert_error(klass)
|
69
|
+
raise RangeError, "can't convert #{self} into #{klass}", caller
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Methods for enumerating values of the number.
|
5
|
+
module Enumerating
|
6
|
+
include ::Enumerable
|
7
|
+
|
8
|
+
# Iterate through every pair of unit and coefficient.
|
9
|
+
# Returns {::Enumerator} if no block is given.
|
10
|
+
# @overload each
|
11
|
+
# @yieldparam unit [Object]
|
12
|
+
# @yieldparam coefficient [Integer, Float, Rational, BigDecimal]
|
13
|
+
# @yieldreturn [void]
|
14
|
+
# @return [VectorNumber] self
|
15
|
+
# @overload each
|
16
|
+
# @return [Enumerator]
|
17
|
+
def each(&)
|
18
|
+
return to_enum { size } unless block_given?
|
19
|
+
|
20
|
+
@data.each(&)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
alias each_pair each
|
25
|
+
|
26
|
+
# @return [Array<Object>]
|
27
|
+
def units
|
28
|
+
@data.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
alias keys units
|
32
|
+
|
33
|
+
# @return [Array<Integer, Float, Rational, BigDecimal>]
|
34
|
+
def coefficients
|
35
|
+
@data.values
|
36
|
+
end
|
37
|
+
|
38
|
+
alias values coefficients
|
39
|
+
|
40
|
+
# @return [Hash{Object => Integer, Float, Rational, BigDecimal}]
|
41
|
+
def to_h(&)
|
42
|
+
if block_given?
|
43
|
+
@data.to_h(&)
|
44
|
+
else
|
45
|
+
@data.dup
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get the coefficient for the unit.
|
50
|
+
#
|
51
|
+
# If the +unit?(unit)+ is false, 0 is returned.
|
52
|
+
# Note that units for real and imaginary parts are
|
53
|
+
# VectorNumber::R and VectorNumber::I respectively.
|
54
|
+
#
|
55
|
+
# @param unit [Object]
|
56
|
+
# @return [Integer, Float, Rational, BigDecimal]
|
57
|
+
def [](unit)
|
58
|
+
@data[unit] || 0
|
59
|
+
end
|
60
|
+
|
61
|
+
# Check if a unit has a non-zero coefficient.
|
62
|
+
#
|
63
|
+
# @param unit [Object]
|
64
|
+
# @return [Boolean]
|
65
|
+
def unit?(unit)
|
66
|
+
@data.key?(unit)
|
67
|
+
end
|
68
|
+
|
69
|
+
alias key? unit?
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Various mathematical operations that are also conversions.
|
5
|
+
module MathConverting
|
6
|
+
# Return the absolute value of a vector, i.e. its length.
|
7
|
+
# @return [Float]
|
8
|
+
def abs
|
9
|
+
Math.sqrt(coefficients.reduce(0.0) { |result, coefficient| result + coefficient.abs2 })
|
10
|
+
end
|
11
|
+
|
12
|
+
alias magnitude abs
|
13
|
+
|
14
|
+
# Return the square of absolute value.
|
15
|
+
# @return [Float]
|
16
|
+
def abs2 # rubocop:disable Naming/VariableNumber
|
17
|
+
abs**2
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return a new vector with every coefficient truncated using their +#truncate+.
|
21
|
+
# @param digits [Integer]
|
22
|
+
# @return [VectorNumber]
|
23
|
+
def truncate(digits = 0)
|
24
|
+
new { _1.truncate(digits) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return a new vector with every coefficient rounded using their +#ceil+.
|
28
|
+
# @param digits [Integer]
|
29
|
+
# @return [VectorNumber]
|
30
|
+
def ceil(digits = 0)
|
31
|
+
new { _1.ceil(digits) }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return a new vector with every coefficient rounded using their +#floor+.
|
35
|
+
# @param digits [Integer]
|
36
|
+
# @return [VectorNumber]
|
37
|
+
def floor(digits = 0)
|
38
|
+
new { _1.floor(digits) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return a new vector with every coefficient rounded using their +#round+.
|
42
|
+
# @param digits [Integer]
|
43
|
+
# @param half [Symbol, nil] one of +:up+, +:down+ or +:even+, see +Float#round+ for meaning
|
44
|
+
# @return [VectorNumber]
|
45
|
+
def round(digits = 0, half: :up)
|
46
|
+
if defined?(BigDecimal)
|
47
|
+
bd_mode =
|
48
|
+
case half
|
49
|
+
when :down then :half_down
|
50
|
+
when :even then :half_even
|
51
|
+
else :half_up
|
52
|
+
end
|
53
|
+
new { _1.is_a?(BigDecimal) ? _1.round(digits, bd_mode) : _1.round(digits, half:) }
|
54
|
+
else
|
55
|
+
new { _1.round(digits, half:) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Methods for performing actual math.
|
5
|
+
module Mathing
|
6
|
+
# The coerce method provides support for Ruby type coercion.
|
7
|
+
# Unlike most numeric types, VectorNumber can coerce *anything*.
|
8
|
+
# @param other [Object]
|
9
|
+
# @return [Array(VectorNumber, VectorNumber)]
|
10
|
+
def coerce(other)
|
11
|
+
case other
|
12
|
+
when VectorNumber
|
13
|
+
[other, self]
|
14
|
+
else
|
15
|
+
[new([other]), self]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return self.
|
20
|
+
# @return [VectorNumber]
|
21
|
+
def +@
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return new vector with negated coefficients.
|
26
|
+
# This preserves order of units.
|
27
|
+
# @return [VectorNumber]
|
28
|
+
def -@
|
29
|
+
new(&:-@)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return new vector as a sum of this and other value.
|
33
|
+
# This is analogous to {VectorNumber.[]}.
|
34
|
+
# @param other [Object]
|
35
|
+
# @return [VectorNumber]
|
36
|
+
def +(other)
|
37
|
+
new([self, other])
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return new vector as a sum of this and negative of the other value.
|
41
|
+
# This is analogous to {VectorNumber.[]}, but allows to negate anything.
|
42
|
+
# @param other [Object]
|
43
|
+
# @return [VectorNumber]
|
44
|
+
def -(other)
|
45
|
+
self + new([other], &:-@)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Multiply all coefficients by a real number, returning new vector.
|
49
|
+
# This effectively multiplies magnitude by the specified factor.
|
50
|
+
# @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
|
51
|
+
# @return [VectorNumber]
|
52
|
+
# @raise [RangeError] if +other+ is not a number or +other+ can't be multiplied by this one
|
53
|
+
def *(other)
|
54
|
+
if real_number?(other)
|
55
|
+
other = other.real
|
56
|
+
# @type var other: Float
|
57
|
+
new { _1 * other }
|
58
|
+
elsif real_number?(self) && other.is_a?(self.class)
|
59
|
+
# @type var other: untyped
|
60
|
+
other * self
|
61
|
+
else
|
62
|
+
raise RangeError, "can't multiply #{self} and #{other}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Divide all coefficients by a real number, returning new vector.
|
67
|
+
# This effectively multiplies magnitude by reciprocal of the specified factor.
|
68
|
+
# @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
|
69
|
+
# @return [VectorNumber]
|
70
|
+
# @raise [RangeError] if +other+ is not a number or is not a real number
|
71
|
+
def /(other)
|
72
|
+
raise RangeError, "can't divide #{self} by #{other}" unless real_number?(other)
|
73
|
+
|
74
|
+
other = other.real
|
75
|
+
# Prevent integer division, but without loss of accuracy.
|
76
|
+
other = Rational(other) if other.is_a?(Integer)
|
77
|
+
# @type var other: Float
|
78
|
+
new { _1 / other }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Divide all coefficients by a real number using +fdiv+, returning new vector
|
82
|
+
# with Float coefficients.
|
83
|
+
# @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
|
84
|
+
# @return [VectorNumber]
|
85
|
+
# @raise [RangeError] if +other+ is not a number or is not a real number
|
86
|
+
def fdiv(other)
|
87
|
+
raise RangeError, "can't divide #{self} by #{other}" unless real_number?(other)
|
88
|
+
|
89
|
+
other = other.real
|
90
|
+
new { _1.fdiv(other) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Refinements of Numeric classes to better work with VectorNumber and similar classes.
|
5
|
+
module NumericRefinements
|
6
|
+
# Refinement module to provide a +#<=>+ method that can work backwards.
|
7
|
+
# @note Currently only applies to Complex on *3.1*,
|
8
|
+
# as other numeric classes rely on +#coerce+.
|
9
|
+
module CommutativeShuttle
|
10
|
+
# 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
|
19
|
+
def <=>(other)
|
20
|
+
comparison = super
|
21
|
+
return comparison if comparison || !other.respond_to?(:<=>)
|
22
|
+
|
23
|
+
comparison = other <=> self
|
24
|
+
-comparison if comparison
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if (Complex(1, 0) <=> VectorNumber[1]).nil?
|
29
|
+
refine(Complex) { import_methods CommutativeShuttle }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Refinement module to change Kernel#BigDecimal so it works with +#to_d+.
|
33
|
+
# @note `BigDecimal` needs to be defined for this refinement to activate.
|
34
|
+
module BigDecimalToD
|
35
|
+
# BigDecimal() that first tries to use #to_d.
|
36
|
+
# @param value [Object]
|
37
|
+
# @param exception [Boolean]
|
38
|
+
# @overload BigDecimal(value, exception: true)
|
39
|
+
# @overload BigDecimal(value, ndigits, exception: true)
|
40
|
+
# @param ndigits [Integer]
|
41
|
+
# @return [BigDecimal, nil]
|
42
|
+
# @raise [TypeError]
|
43
|
+
def BigDecimal(value, ndigits = nil, exception: true) # rubocop:disable Naming/MethodName
|
44
|
+
if value.respond_to?(:to_d)
|
45
|
+
ndigits.nil? ? value.to_d : value.to_d(ndigits)
|
46
|
+
else
|
47
|
+
ndigits.nil? ? super(value, exception:) : super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
refine(Kernel) { import_methods BigDecimalToD } if defined?(BigDecimal)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Methods for querying state of the number.
|
5
|
+
# Mostly modeled after {::Complex}.
|
6
|
+
module Querying
|
7
|
+
# Whether this VectorNumber can be considered strictly numeric, e.g. real or complex.
|
8
|
+
# @param dimensions [Integer] number of dimensions to consider "numeric"
|
9
|
+
# - 0 — zero
|
10
|
+
# - 1 — real number
|
11
|
+
# - 2 — complex number, etc.
|
12
|
+
# @return [Boolean]
|
13
|
+
# @raise [ArgumentError] if +dimensions+ is negative
|
14
|
+
def numeric?(dimensions = 2)
|
15
|
+
raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0
|
16
|
+
|
17
|
+
size <= dimensions && (1..dimensions).count { @data[UNIT[_1]].nonzero? } == size
|
18
|
+
end
|
19
|
+
|
20
|
+
# Whether this VectorNumber contains any non-numeric parts.
|
21
|
+
# @param (see #numeric?)
|
22
|
+
# @return (see #numeric?)
|
23
|
+
# @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
|
29
|
+
|
30
|
+
# Returns +true+ if all coefficients are finite, +false+ otherwise.
|
31
|
+
# @return [Boolean]
|
32
|
+
def finite?
|
33
|
+
all? { |_u, v| v.finite? }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns +1+ if any coefficients are infinite, +nil+ otherwise.
|
37
|
+
# @return [1, nil]
|
38
|
+
def infinite?
|
39
|
+
finite? ? nil : 1 # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns +true+ if there are no non-zero coefficients, and +false+ otherwise.
|
43
|
+
# @return [Boolean]
|
44
|
+
def zero?
|
45
|
+
size.zero?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns +self+ if there are any non-zero coefficients, +nil+ otherwise.
|
49
|
+
# @return [VectorNumber, nil]
|
50
|
+
def nonzero?
|
51
|
+
zero? ? nil : self # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns +true+ if number is non-zero and all non-zero coefficients are positive,
|
55
|
+
# and +false+ otherwise.
|
56
|
+
# @return [Boolean]
|
57
|
+
def positive?
|
58
|
+
!zero? && all? { |_u, c| c.positive? }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns +true+ if number is non-zero and all non-zero coefficients are negative,
|
62
|
+
# and +false+ otherwise.
|
63
|
+
# @return [Boolean]
|
64
|
+
def negative?
|
65
|
+
!zero? && all? { |_u, c| c.negative? }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Always returns +false+.
|
69
|
+
# @see #numeric?
|
70
|
+
# @return [false]
|
71
|
+
def real?
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
# Always returns +false+.
|
76
|
+
# @return [false]
|
77
|
+
def integer?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class VectorNumber
|
4
|
+
# Methods and options for string representation.
|
5
|
+
module Stringifying
|
6
|
+
# Predefined symbols for multiplication to display between unit and coefficient.
|
7
|
+
# @return [Hash{Symbol => String}]
|
8
|
+
MULT_STRINGS = {
|
9
|
+
asterisk: "*", # U+002A
|
10
|
+
cross: "×", # U+00D7
|
11
|
+
dot: "⋅", # U+22C5
|
12
|
+
invisible: "", # U+2062, zero-width multiplication operator
|
13
|
+
space: " ",
|
14
|
+
none: ""
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
# @param mult [Symbol, String]
|
18
|
+
# text to use between coefficient and unit,
|
19
|
+
# can be one of the keys in {MULT_STRINGS} or an arbitrary string
|
20
|
+
# @return [String]
|
21
|
+
# @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
|
22
|
+
def to_s(mult: options[:mult])
|
23
|
+
return "0" if zero?
|
24
|
+
|
25
|
+
result = +""
|
26
|
+
each_with_index do |(unit, coefficient), index|
|
27
|
+
if index.zero?
|
28
|
+
result << "-" if coefficient.negative?
|
29
|
+
else
|
30
|
+
result << (coefficient.positive? ? " + " : " - ")
|
31
|
+
end
|
32
|
+
result << value_to_s(unit, coefficient.abs, mult:)
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String]
|
38
|
+
def inspect
|
39
|
+
"(#{self})"
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# @param unit [Object]
|
45
|
+
# @param coefficient [Numeric]
|
46
|
+
# @param mult [Symbol, String]
|
47
|
+
# @return [String]
|
48
|
+
# @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
|
49
|
+
def value_to_s(unit, coefficient, mult:)
|
50
|
+
if !mult.is_a?(String) && !MULT_STRINGS.key?(mult)
|
51
|
+
raise ArgumentError, "unknown key :#{mult}", caller
|
52
|
+
end
|
53
|
+
|
54
|
+
case unit
|
55
|
+
when R
|
56
|
+
coefficient.to_s
|
57
|
+
when I
|
58
|
+
"#{coefficient}i"
|
59
|
+
else
|
60
|
+
unit = "'#{unit}'" if unit.is_a?(String)
|
61
|
+
operator = mult.is_a?(String) ? mult : MULT_STRINGS[mult]
|
62
|
+
"#{coefficient}#{operator}#{unit}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "vector_number/version"
|
4
|
+
require_relative "vector_number/mathing"
|
5
|
+
require_relative "vector_number/math_converting"
|
6
|
+
require_relative "vector_number/converting"
|
7
|
+
require_relative "vector_number/enumerating"
|
8
|
+
require_relative "vector_number/comparing"
|
9
|
+
require_relative "vector_number/querying"
|
10
|
+
require_relative "vector_number/stringifying"
|
11
|
+
|
12
|
+
# A class to add together anything.
|
13
|
+
class VectorNumber
|
14
|
+
include Mathing
|
15
|
+
include MathConverting
|
16
|
+
include Converting
|
17
|
+
include Enumerating
|
18
|
+
include Comparing
|
19
|
+
include Querying
|
20
|
+
include Stringifying
|
21
|
+
|
22
|
+
# @return [Array<Symbol>]
|
23
|
+
KNOWN_OPTIONS = %i[mult].freeze
|
24
|
+
|
25
|
+
# @return [Hash{Symbol => Object}]
|
26
|
+
DEFAULT_OPTIONS = { mult: :dot }.freeze
|
27
|
+
|
28
|
+
# Get a unit for +n+th numeric dimension, where 1 is real, 2 is imaginary.
|
29
|
+
UNIT = ->(n) { (n - 1).i }.freeze
|
30
|
+
# Constant for real unit.
|
31
|
+
R = UNIT[1]
|
32
|
+
# Constant for imaginary unit.
|
33
|
+
I = UNIT[2]
|
34
|
+
|
35
|
+
# Number of non-zero dimensions.
|
36
|
+
# @return [Integer]
|
37
|
+
attr_reader :size
|
38
|
+
# @return [Hash{Symbol => Object}]
|
39
|
+
attr_reader :options
|
40
|
+
|
41
|
+
# Create new VectorNumber from +values+.
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# VectorNumber[1, 2, 3] #=> (6)
|
45
|
+
# VectorNumber[[1, 2, 3]] #=> (1⋅[1, 2, 3])
|
46
|
+
# VectorNumber['b', VectorNumber::I, mult: :asterisk] #=> (1*'b' + 1i)
|
47
|
+
# @param values [Array<Object>] values to put in the number
|
48
|
+
# @param options [Hash{Symbol => Object}] options for the number
|
49
|
+
# @return [VectorNumber]
|
50
|
+
def self.[](*values, **options)
|
51
|
+
new(values, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param values [Array, Hash{Object => Integer, Float, Rational, BigDecimal}, VectorNumber]
|
55
|
+
# values for this number, hashes are treated like plain vector numbers
|
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+
|
59
|
+
# @option options [Symbol, String] :mult
|
60
|
+
# text to use between unit and coefficient, see {Stringifying#to_s} for explanation
|
61
|
+
# @yieldparam coefficient [Integer, Float, Rational, BigDecimal]
|
62
|
+
# @yieldreturn [Integer, Float, Rational, BigDecimal] new coefficient
|
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()
|
69
|
+
initialize_from(values)
|
70
|
+
apply_transform(&)
|
71
|
+
finalize_contents
|
72
|
+
save_options(options, values:)
|
73
|
+
@options.freeze
|
74
|
+
@data.freeze
|
75
|
+
freeze
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return self.
|
79
|
+
#
|
80
|
+
# @return [VectorNumber]
|
81
|
+
def dup
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return self.
|
86
|
+
#
|
87
|
+
# Raises ArgumentError if +freeze+ is not +true+ or +nil+.
|
88
|
+
#
|
89
|
+
# @return [VectorNumber]
|
90
|
+
def clone(freeze: true)
|
91
|
+
case freeze
|
92
|
+
when true, nil
|
93
|
+
self
|
94
|
+
when false
|
95
|
+
raise ArgumentError, "can't unfreeze #{self.class}"
|
96
|
+
else
|
97
|
+
raise ArgumentError, "unexpected value for freeze: #{freeze.class}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# Create new VectorNumber from a value or self, optionally applying a transform.
|
104
|
+
# @param from [Object] self if not specified
|
105
|
+
# @yieldparam coefficient [Integer, Float, Rational, BigDecimal]
|
106
|
+
# @yieldreturn [Integer, Float, Rational, BigDecimal] new coefficient
|
107
|
+
# @return [VectorNumber]
|
108
|
+
def new(from = self, &)
|
109
|
+
self.class.new(from, &)
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param value [Object]
|
113
|
+
# @return [Boolean]
|
114
|
+
def real_number?(value)
|
115
|
+
(value.is_a?(Numeric) && value.real?) || (value.is_a?(self.class) && value.numeric?(1))
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param values [Array, Hash{Object => Integer, Float, Rational, BigDecimal}, VectorNumber, nil]
|
119
|
+
# @return [void]
|
120
|
+
def initialize_from(values)
|
121
|
+
@data = Hash.new(0)
|
122
|
+
|
123
|
+
case values
|
124
|
+
when VectorNumber, Hash
|
125
|
+
add_vector_to_data(values)
|
126
|
+
when Array
|
127
|
+
values.each { |value| add_value_to_data(value) }
|
128
|
+
else
|
129
|
+
# Don't add anything.
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# @param value [VectorNumber, Numeric, Object]
|
134
|
+
# @return [void]
|
135
|
+
def add_value_to_data(value)
|
136
|
+
case value
|
137
|
+
when Numeric
|
138
|
+
add_numeric_value_to_data(value)
|
139
|
+
when VectorNumber
|
140
|
+
add_vector_to_data(value)
|
141
|
+
else
|
142
|
+
@data[value] += 1
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# @param value [Numeric]
|
147
|
+
# @return [void]
|
148
|
+
def add_numeric_value_to_data(value)
|
149
|
+
@data[R] += value.real
|
150
|
+
@data[I] += value.imaginary
|
151
|
+
end
|
152
|
+
|
153
|
+
# @param vector [VectorNumber, Hash{Object => Integer, Float, Rational, BigDecimal}]
|
154
|
+
# @return [void]
|
155
|
+
def add_vector_to_data(vector)
|
156
|
+
vector.each_pair do |unit, coefficient|
|
157
|
+
raise RangeError, "#{coefficient} is not a real number" unless real_number?(coefficient)
|
158
|
+
|
159
|
+
@data[unit] += coefficient.real
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# @yieldparam coefficient [Integer, Float, Rational, BigDecimal]
|
164
|
+
# @yieldreturn [Integer, Float, Rational, BigDecimal]
|
165
|
+
# @return [void]
|
166
|
+
# @raise [RangeError]
|
167
|
+
def apply_transform
|
168
|
+
return unless block_given?
|
169
|
+
|
170
|
+
@data.transform_values! do |coefficient|
|
171
|
+
new_value = yield coefficient
|
172
|
+
next new_value.real if real_number?(new_value)
|
173
|
+
|
174
|
+
raise RangeError, "transform returned non-real value for #{coefficient}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param options [Hash{Symbol => Object}, nil]
|
179
|
+
# @param values [Object] initializing object
|
180
|
+
# @return [void]
|
181
|
+
def save_options(options, values:)
|
182
|
+
@options =
|
183
|
+
case [options, values]
|
184
|
+
in [{} | nil, VectorNumber]
|
185
|
+
values.options
|
186
|
+
in Hash, VectorNumber
|
187
|
+
values.options.merge(options).slice(*known_options)
|
188
|
+
in Hash, _ unless options.empty?
|
189
|
+
default_options.merge(options).slice(*known_options)
|
190
|
+
else
|
191
|
+
default_options
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Compact coefficients, calculate size and freeze data.
|
196
|
+
# @return [void]
|
197
|
+
def finalize_contents
|
198
|
+
@data.delete_if { |_u, c| c.zero? }
|
199
|
+
@data.freeze
|
200
|
+
@size = @data.size
|
201
|
+
end
|
202
|
+
|
203
|
+
def default_options
|
204
|
+
DEFAULT_OPTIONS
|
205
|
+
end
|
206
|
+
|
207
|
+
def known_options
|
208
|
+
KNOWN_OPTIONS
|
209
|
+
end
|
210
|
+
end
|
data/sig/definitions.rbs
ADDED
@@ -0,0 +1,15 @@
|
|
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]
|
@@ -0,0 +1,230 @@
|
|
1
|
+
interface _BaseMethods
|
2
|
+
def size: -> Integer
|
3
|
+
def options: -> options_type
|
4
|
+
|
5
|
+
# private
|
6
|
+
def new: () { (coefficient_type value) -> coefficient_type } -> vector_type
|
7
|
+
| (in_value_type) { (coefficient_type value) -> coefficient_type } -> vector_type
|
8
|
+
| () -> vector_type
|
9
|
+
| (in_value_type) -> vector_type
|
10
|
+
def real_number?: (real_number value) -> true
|
11
|
+
| (vector_type) -> bool
|
12
|
+
| (in_value_type value) -> false
|
13
|
+
end
|
14
|
+
|
15
|
+
# A class to add together anything.
|
16
|
+
class VectorNumber
|
17
|
+
include _BaseMethods
|
18
|
+
include VectorNumber::Mathing
|
19
|
+
include VectorNumber::MathConverting
|
20
|
+
include VectorNumber::Converting
|
21
|
+
include VectorNumber::Enumerating
|
22
|
+
include VectorNumber::Comparing
|
23
|
+
include VectorNumber::Querying
|
24
|
+
include VectorNumber::Stringifying
|
25
|
+
|
26
|
+
VERSION: String
|
27
|
+
|
28
|
+
KNOWN_OPTIONS: list[Symbol]
|
29
|
+
DEFAULT_OPTIONS: options_type
|
30
|
+
|
31
|
+
UNIT: ^(Integer) -> Complex
|
32
|
+
R: Complex
|
33
|
+
I: Complex
|
34
|
+
|
35
|
+
def self.[]: (*unit_type values, **options_type options) -> vector_type
|
36
|
+
|
37
|
+
def initialize:
|
38
|
+
(?(units_list_type | plain_vector_type | vector_type)? values, ?options_type options)
|
39
|
+
?{ (coefficient_type coefficient) -> coefficient_type }
|
40
|
+
-> void
|
41
|
+
|
42
|
+
def dup: () -> self
|
43
|
+
|
44
|
+
def clone: (?freeze: bool?) -> self
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
@size: Integer
|
49
|
+
@options: options_type
|
50
|
+
@data: plain_vector_type
|
51
|
+
|
52
|
+
def initialize_from: ((units_list_type | plain_vector_type | vector_type)? values) -> void
|
53
|
+
|
54
|
+
def add_value_to_data: ((vector_type | Numeric | unit_type) value) -> void
|
55
|
+
|
56
|
+
def add_numeric_value_to_data: (Numeric value) -> void
|
57
|
+
|
58
|
+
def add_vector_to_data: ((vector_type | plain_vector_type) vector) -> void
|
59
|
+
|
60
|
+
def apply_transform: () ?{ (coefficient_type value) -> coefficient_type } -> void
|
61
|
+
|
62
|
+
def save_options: (options_type? options, values: in_value_type) -> void
|
63
|
+
def default_options: () -> options_type
|
64
|
+
def known_options: () -> list[Symbol]
|
65
|
+
|
66
|
+
def finalize_contents: () -> void
|
67
|
+
|
68
|
+
# Methods for performing actual math.
|
69
|
+
module Mathing
|
70
|
+
include _BaseMethods
|
71
|
+
|
72
|
+
def coerce: (in_value_type) -> [vector_type, self]
|
73
|
+
|
74
|
+
def +@: () -> self
|
75
|
+
|
76
|
+
def -@: () -> vector_type
|
77
|
+
|
78
|
+
def +: (in_value_type) -> vector_type
|
79
|
+
|
80
|
+
def -: (in_value_type) -> vector_type
|
81
|
+
|
82
|
+
def *: (real_number | vector_type) -> vector_type
|
83
|
+
|
84
|
+
def /: (real_number | vector_type) -> vector_type
|
85
|
+
|
86
|
+
def fdiv: (real_number | vector_type) -> vector_type
|
87
|
+
end
|
88
|
+
|
89
|
+
# Various mathematical operations that are also conversions.
|
90
|
+
module MathConverting
|
91
|
+
include _BaseMethods
|
92
|
+
include Enumerating
|
93
|
+
|
94
|
+
def abs: () -> Float
|
95
|
+
|
96
|
+
def abs2: () -> Float
|
97
|
+
|
98
|
+
def truncate: (Integer) -> vector_type
|
99
|
+
|
100
|
+
def ceil: (Integer) -> vector_type
|
101
|
+
|
102
|
+
def floor: (Integer) -> vector_type
|
103
|
+
end
|
104
|
+
|
105
|
+
# Methods for converting to different number classes.
|
106
|
+
module Converting
|
107
|
+
include _BaseMethods
|
108
|
+
include Querying
|
109
|
+
|
110
|
+
def real: () -> real_number
|
111
|
+
|
112
|
+
def imaginary: () -> real_number
|
113
|
+
alias imag imaginary
|
114
|
+
|
115
|
+
def to_i: () -> Integer
|
116
|
+
| () -> void
|
117
|
+
alias to_int to_i
|
118
|
+
|
119
|
+
def to_f: () -> Float
|
120
|
+
| () -> void
|
121
|
+
|
122
|
+
def to_r: () -> Rational
|
123
|
+
| () -> void
|
124
|
+
|
125
|
+
def to_d: (?Integer?) -> BigDecimal
|
126
|
+
| (?Integer?) -> void
|
127
|
+
|
128
|
+
def to_c: () -> Complex
|
129
|
+
| () -> void
|
130
|
+
|
131
|
+
def truncate: (?Integer digits) -> vector_type
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def raise_convert_error: (Class) -> void
|
136
|
+
end
|
137
|
+
|
138
|
+
# Methods for enumerating values of the number.
|
139
|
+
module Enumerating
|
140
|
+
include _BaseMethods
|
141
|
+
include _Each[each_value_type]
|
142
|
+
include Enumerable[each_value_type]
|
143
|
+
|
144
|
+
def each: () { (unit_type unit, coefficient_type coefficient) -> void } -> self
|
145
|
+
| () -> Enumerator[each_value_type, Integer]
|
146
|
+
| ...
|
147
|
+
alias each_pair each
|
148
|
+
|
149
|
+
def units: () -> units_list_type
|
150
|
+
alias keys units
|
151
|
+
|
152
|
+
def coefficients: () -> coefficients_list_type
|
153
|
+
alias values coefficients
|
154
|
+
|
155
|
+
def to_h: () -> plain_vector_type
|
156
|
+
| () { (unit_type, coefficient_type) -> each_value_type } -> plain_vector_type
|
157
|
+
|
158
|
+
def []: (unit_type unit) -> coefficient_type
|
159
|
+
|
160
|
+
def unit?: (unit_type unit) -> bool
|
161
|
+
alias key? unit?
|
162
|
+
end
|
163
|
+
|
164
|
+
# Methods for comparing with other numbers.
|
165
|
+
module Comparing
|
166
|
+
include _BaseMethods
|
167
|
+
include Enumerating
|
168
|
+
include Converting
|
169
|
+
include Querying
|
170
|
+
|
171
|
+
def ==: (in_value_type other) -> bool
|
172
|
+
|
173
|
+
def eql?: (in_value_type other) -> bool
|
174
|
+
|
175
|
+
def <=>: (in_value_type other) -> Integer?
|
176
|
+
end
|
177
|
+
|
178
|
+
# Methods for querying state of the number.
|
179
|
+
# Mostly modeled after {::Complex}.
|
180
|
+
module Querying
|
181
|
+
include Enumerating
|
182
|
+
|
183
|
+
def numeric?: (?Integer) -> bool
|
184
|
+
|
185
|
+
def nonnumeric?: (?Integer) -> bool
|
186
|
+
|
187
|
+
def finite?: () -> bool
|
188
|
+
|
189
|
+
def infinite?: () -> Integer?
|
190
|
+
|
191
|
+
def zero?: () -> bool
|
192
|
+
|
193
|
+
def nonzero?: () -> self?
|
194
|
+
|
195
|
+
def positive?: () -> bool
|
196
|
+
|
197
|
+
def negative?: () -> bool
|
198
|
+
|
199
|
+
def real?: () -> false
|
200
|
+
|
201
|
+
def integer?: () -> false
|
202
|
+
end
|
203
|
+
|
204
|
+
# Methods and options for string representation.
|
205
|
+
module Stringifying
|
206
|
+
include Querying
|
207
|
+
|
208
|
+
MULT_STRINGS: Hash[Symbol, String]
|
209
|
+
|
210
|
+
def to_s: (?mult: (Symbol | String)) -> String
|
211
|
+
|
212
|
+
def inspect: () -> String
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
def value_to_s: (unit_type unit, coefficient_type coefficient, mult: (Symbol | String)) -> String
|
217
|
+
end
|
218
|
+
|
219
|
+
# Refinements of Numeric classes to better work with VectorNumber and similar classes.
|
220
|
+
module NumericRefinements
|
221
|
+
module CommutativeShuttle
|
222
|
+
def <=>: (in_value_type other) -> Integer?
|
223
|
+
end
|
224
|
+
|
225
|
+
module BigDecimalToD
|
226
|
+
def BigDecimal: (untyped value, exception: bool) -> BigDecimal?
|
227
|
+
| (untyped value, Integer ndigits, exception: bool) -> BigDecimal?
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vector_number
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexandr Bulancov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-02-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files:
|
18
|
+
- README.md
|
19
|
+
- CHANGELOG.md
|
20
|
+
files:
|
21
|
+
- CHANGELOG.md
|
22
|
+
- README.md
|
23
|
+
- lib/vector_number.rb
|
24
|
+
- lib/vector_number/comparing.rb
|
25
|
+
- lib/vector_number/converting.rb
|
26
|
+
- lib/vector_number/enumerating.rb
|
27
|
+
- lib/vector_number/math_converting.rb
|
28
|
+
- lib/vector_number/mathing.rb
|
29
|
+
- lib/vector_number/numeric_refinements.rb
|
30
|
+
- lib/vector_number/querying.rb
|
31
|
+
- lib/vector_number/stringifying.rb
|
32
|
+
- lib/vector_number/version.rb
|
33
|
+
- sig/definitions.rbs
|
34
|
+
- sig/vector_number.rbs
|
35
|
+
homepage: https://github.com/trinistr/vector_number
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
metadata:
|
39
|
+
homepage_uri: https://github.com/trinistr/vector_number
|
40
|
+
source_code_uri: https://github.com/trinistr/vector_number
|
41
|
+
changelog_uri: https://github.com/trinistr/vector_number/CHANGELOG.md
|
42
|
+
rubygems_mfa_required: 'true'
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- "--main"
|
46
|
+
- README.md
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 3.1.0
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubygems_version: 3.5.21
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: A library to add together anything.
|
64
|
+
test_files: []
|