vector_number 0.4.2 → 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.
@@ -8,12 +8,18 @@ class VectorNumber
8
8
  # - refinement for +Complex#<=>+ to work with classes implementing +<=>+;
9
9
  # - refinement for +Kernel#BigDecimal+ to work with classes implementing +to_d+.
10
10
  #
11
+ # @note Refinements won't work on Ruby 3.0.
12
+ #
11
13
  # @example activating refinements
12
14
  # require "vector_number/numeric_refinements"
13
15
  # using VectorNumber::NumericRefinements
14
16
  #
15
17
  # @since 0.2.0
16
18
  module NumericRefinements
19
+ # CommutativeShuttle refinement works only on 3.1, so it almost never actually runs.
20
+ # There are tests for the correct behavior, however, so it's fine.
21
+ # :nocov:
22
+
17
23
  # Refinement module to provide a +#<=>+ method that can work backwards.
18
24
  #
19
25
  # @note Currently only applies to Complex on *3.1*,
@@ -41,8 +47,13 @@ class VectorNumber
41
47
  end
42
48
 
43
49
  if (Complex(1, 0) <=> VectorNumber[1]).nil?
44
- refine(Complex) { import_methods CommutativeShuttle }
50
+ refine(Complex) do
51
+ import_methods CommutativeShuttle
52
+ rescue
53
+ warn "Numeric refinements are not available on Ruby < 3.1"
54
+ end
45
55
  end
56
+ # :nocov:
46
57
 
47
58
  # Refinement module to change Kernel#BigDecimal so it works with +#to_d+.
48
59
  #
@@ -68,11 +79,19 @@ class VectorNumber
68
79
  if value.respond_to?(:to_d)
69
80
  ndigits.nil? ? value.to_d : value.to_d(ndigits)
70
81
  else
71
- ndigits.nil? ? super(value, exception:) : super
82
+ ndigits.nil? ? super(value, exception: exception) : super
72
83
  end
73
84
  end
74
85
  end
75
86
 
76
- refine(Kernel) { import_methods BigDecimalToD } if defined?(BigDecimal)
87
+ # :nocov:
88
+ if defined?(BigDecimal)
89
+ refine(Kernel) do
90
+ import_methods BigDecimalToD
91
+ rescue
92
+ warn "Numeric refinements are not available on Ruby < 3.1"
93
+ end
94
+ end
95
+ # :nocov:
77
96
  end
78
97
  end
@@ -1,150 +1,157 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
- #
9
- # @example
10
- # VectorNumber[2].numeric? # => true
11
- # VectorNumber[2, 3i].numeric? # => true
12
- # VectorNumber[2, "a"].numeric? # => false
13
- # VectorNumber[2, 3i].numeric?(1) # => false
14
- #
15
- # @param dimensions [Integer] number of dimensions to consider "numeric"
16
- # - 0 — zero
17
- # - 1 — real number
18
- # - 2 — complex number, etc.
19
- # @return [Boolean]
20
- # @raise [ArgumentError] if +dimensions+ is negative
21
- #
22
- # @since 0.2.0
23
- def numeric?(dimensions = 2)
24
- raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0
4
+ # @group Querying
5
+ #
6
+ # Mostly modelled after {::Complex}.
25
7
 
26
- size <= dimensions && (1..dimensions).count { @data[UNIT[_1]].nonzero? } == size
27
- end
8
+ # Whether this VectorNumber can be considered strictly numeric — real or complex.
9
+ #
10
+ # @example
11
+ # VectorNumber[2].numeric? # => true
12
+ # VectorNumber[2, 3i].numeric? # => true
13
+ # VectorNumber[2, "a"].numeric? # => false
14
+ # VectorNumber[2, 3i].numeric?(1) # => false
15
+ #
16
+ # @param dimensions [Integer] number of dimensions to consider "numeric"
17
+ # - 0 — zero
18
+ # - 1 — real number
19
+ # - 2 — complex number, etc.
20
+ # @return [Boolean]
21
+ # @raise [ArgumentError] if +dimensions+ is negative
22
+ #
23
+ # @since 0.2.0
24
+ def numeric?(dimensions = 2)
25
+ raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0
28
26
 
29
- # Whether this VectorNumber contains any non-numeric parts.
30
- #
31
- # @example
32
- # VectorNumber[2].nonnumeric? # => false
33
- # VectorNumber[2, 3i].nonnumeric? # => false
34
- # VectorNumber[2, "a"].nonnumeric? # => true
35
- # VectorNumber[2, 3i].nonnumeric?(1) # => true
36
- #
37
- # @param (see #numeric?)
38
- # @return (see #numeric?)
39
- # @raise (see #numeric?)
40
- #
41
- # @since 0.2.1
42
- def nonnumeric?(dimensions = 2) = !numeric?(dimensions)
43
-
44
- # Returns +true+ if all coefficients are finite, +false+ otherwise.
45
- #
46
- # @example
47
- # VectorNumber[2].finite? # => true
48
- # VectorNumber[Float::NAN].finite? # => false
49
- # VectorNumber["a"].mult(Float::INFINITY).finite? # => false
50
- #
51
- # @return [Boolean]
52
- #
53
- # @since 0.1.0
54
- def finite?
55
- all? { |_u, v| v.finite? }
56
- end
27
+ size <= dimensions && (1..dimensions).count { @data[UNIT[_1]].nonzero? } == size
28
+ end
57
29
 
58
- # Returns +1+ if any coefficients are infinite, +nil+ otherwise.
59
- #
60
- # This behavior is the same as +Complex+'s.
61
- #
62
- # @example
63
- # VectorNumber[2].infinite? # => nil
64
- # VectorNumber[Float::NAN].infinite? # => 1
65
- # VectorNumber["a"].mult(-Float::INFINITY).infinite? # => 1
66
- #
67
- # @return [1, nil]
68
- #
69
- # @since 0.1.0
70
- def infinite?
71
- finite? ? nil : 1 # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
72
- end
30
+ # Whether this VectorNumber contains any non-numeric parts.
31
+ #
32
+ # @example
33
+ # VectorNumber[2].nonnumeric? # => false
34
+ # VectorNumber[2, 3i].nonnumeric? # => false
35
+ # VectorNumber[2, "a"].nonnumeric? # => true
36
+ # VectorNumber[2, 3i].nonnumeric?(1) # => true
37
+ #
38
+ # @param (see #numeric?)
39
+ # @return (see #numeric?)
40
+ # @raise (see #numeric?)
41
+ #
42
+ # @since 0.2.1
43
+ def nonnumeric?(dimensions = 2) = !numeric?(dimensions)
73
44
 
74
- # Returns +true+ if there are no non-zero coefficients, and +false+ otherwise.
75
- #
76
- # @example
77
- # VectorNumber["c"].zero? # => false
78
- # VectorNumber[].zero? # => true
79
- #
80
- # @return [Boolean]
81
- #
82
- # @since 0.1.0
83
- def zero? = size.zero?
45
+ # Returns +true+ if all coefficients are finite, +false+ otherwise.
46
+ #
47
+ # @example
48
+ # VectorNumber[2].finite? # => true
49
+ # VectorNumber[Float::NAN].finite? # => false
50
+ # VectorNumber["a"].mult(Float::INFINITY).finite? # => false
51
+ #
52
+ # @return [Boolean]
53
+ #
54
+ # @since 0.1.0
55
+ def finite?
56
+ all? { |_u, v| v.finite? }
57
+ end
84
58
 
85
- # Returns +self+ if there are any non-zero coefficients, +nil+ otherwise.
86
- #
87
- # This behavior is the same as +Numeric+'s.
88
- #
89
- # @example
90
- # VectorNumber["ab", "cd"].nonzero? # => (1⋅'ab' + 1⋅'cd')
91
- # VectorNumber[].nonzero? # => nil
92
- #
93
- # @return [VectorNumber, nil]
94
- #
95
- # @since 0.1.0
96
- def nonzero?
97
- zero? ? nil : self # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
98
- end
59
+ # Returns +1+ if any coefficients are non-finite, +nil+ otherwise.
60
+ #
61
+ # This behavior is the same as +Complex+'s.
62
+ #
63
+ # @example
64
+ # VectorNumber[2].infinite? # => nil
65
+ # VectorNumber[Float::NAN].infinite? # => 1
66
+ # VectorNumber["a"].mult(-Float::INFINITY).infinite? # => 1
67
+ #
68
+ # @return [1, nil]
69
+ #
70
+ # @since 0.1.0
71
+ def infinite?
72
+ finite? ? nil : 1 # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
73
+ end
99
74
 
100
- # Returns +true+ if number is non-zero and all non-zero coefficients are positive,
101
- # and +false+ otherwise.
102
- #
103
- # @example
104
- # VectorNumber["a"].positive? # => true
105
- # VectorNumber[2].neg.positive? # => false
106
- # (VectorNumber["1"] - VectorNumber[1]).positive? # => false
107
- # VectorNumber[0].positive? # => false
108
- #
109
- # @return [Boolean]
110
- #
111
- # @since 0.1.0
112
- def positive?
113
- !zero? && all? { |_u, c| c.positive? }
114
- end
75
+ # Returns +true+ if there are no non-zero coefficients, and +false+ otherwise.
76
+ #
77
+ # This is synonymous with +size+ being 0.
78
+ #
79
+ # @example
80
+ # VectorNumber["c"].zero? # => false
81
+ # VectorNumber[].zero? # => true
82
+ #
83
+ # @see #size
84
+ #
85
+ # @return [Boolean]
86
+ #
87
+ # @since 0.1.0
88
+ def zero? = size.zero?
115
89
 
116
- # Returns +true+ if number is non-zero and all non-zero coefficients are negative,
117
- # and +false+ otherwise.
118
- #
119
- # @example
120
- # VectorNumber["a"].neg.negative? # => true
121
- # VectorNumber[-2].neg.negative? # => false
122
- # (VectorNumber["1"] - VectorNumber[1]).negative? # => false
123
- # VectorNumber[0].negative? # => false
124
- #
125
- # @return [Boolean]
126
- #
127
- # @since 0.1.0
128
- def negative?
129
- !zero? && all? { |_u, c| c.negative? }
130
- end
90
+ # Returns +self+ if there are any non-zero coefficients, +nil+ otherwise.
91
+ #
92
+ # This is synonymous with +size+ not being equal to 0.
93
+ # Behavior of returning self or +nil+ is the same as +Numeric+'s.
94
+ #
95
+ # @example
96
+ # VectorNumber["ab", "cd"].nonzero? # => (1⋅'ab' + 1⋅'cd')
97
+ # VectorNumber[].nonzero? # => nil
98
+ #
99
+ # @see #size
100
+ #
101
+ # @return [VectorNumber, nil]
102
+ #
103
+ # @since 0.1.0
104
+ def nonzero?
105
+ zero? ? nil : self # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
106
+ end
131
107
 
132
- # Always returns +false+, as vectors are not real numbers.
133
- #
134
- # This behavior is the same as +Complex+'s.
135
- #
136
- # @see #numeric?
137
- #
138
- # @return [false]
139
- #
140
- # @since 0.1.0
141
- def real? = false
108
+ # Returns +true+ if number is non-zero and all non-zero coefficients are positive,
109
+ # and +false+ otherwise.
110
+ #
111
+ # @example
112
+ # VectorNumber["a"].positive? # => true
113
+ # VectorNumber[2].neg.positive? # => false
114
+ # (VectorNumber["1"] - VectorNumber[1]).positive? # => false
115
+ # VectorNumber[0].positive? # => false
116
+ #
117
+ # @return [Boolean]
118
+ #
119
+ # @since 0.1.0
120
+ def positive?
121
+ !zero? && all? { |_u, c| c.positive? }
122
+ end
142
123
 
143
- # Always returns +false+, as vectors are not +Integer+s.
144
- #
145
- # @return [false]
146
- #
147
- # @since 0.2.1
148
- def integer? = false
124
+ # Returns +true+ if number is non-zero and all non-zero coefficients are negative,
125
+ # and +false+ otherwise.
126
+ #
127
+ # @example
128
+ # VectorNumber["a"].neg.negative? # => true
129
+ # VectorNumber[-2].neg.negative? # => false
130
+ # (VectorNumber["1"] - VectorNumber[1]).negative? # => false
131
+ # VectorNumber[0].negative? # => false
132
+ #
133
+ # @return [Boolean]
134
+ #
135
+ # @since 0.1.0
136
+ def negative?
137
+ !zero? && all? { |_u, c| c.negative? }
149
138
  end
139
+
140
+ # Always returns +false+, as vectors are not real numbers.
141
+ #
142
+ # This behavior is the same as +Complex+'s.
143
+ #
144
+ # @see #numeric?
145
+ #
146
+ # @return [false]
147
+ #
148
+ # @since 0.1.0
149
+ def real? = false
150
+
151
+ # Always returns +false+, as vectors are not +Integer+s.
152
+ #
153
+ # @return [false]
154
+ #
155
+ # @since 0.2.1
156
+ def integer? = false
150
157
  end
@@ -1,96 +1,93 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
- #
8
- # @return [Hash{Symbol => String}]
9
- #
10
- # @since 0.1.0
11
- MULT_STRINGS = {
12
- asterisk: "*", # U+002A
13
- cross: "×", # U+00D7
14
- dot: "", # U+22C5
15
- invisible: "", # U+2062, zero-width multiplication operator
16
- space: " ",
17
- none: "",
18
- }.freeze
4
+ # Predefined symbols for multiplication to display between unit and coefficient.
5
+ #
6
+ # @return [Hash{Symbol => String}]
7
+ #
8
+ # @since 0.1.0
9
+ MULT_STRINGS = {
10
+ asterisk: "*", # U+002A
11
+ cross: "×", # U+00D7
12
+ dot: "", # U+22C5
13
+ invisible: "", # U+2062, zero-width multiplication operator
14
+ space: " ",
15
+ none: "",
16
+ }.freeze
19
17
 
20
- # Get a string representation of the vector.
21
- #
22
- # @example
23
- # VectorNumber[5, "s"].to_s # => "5 + 1⋅'s'"
24
- # VectorNumber["s", 5].to_s # => "1⋅'s' + 5"
25
- # @example with :mult argument
26
- # VectorNumber[5, "s"].to_s(mult: :asterisk) # => "5 + 1*'s'"
27
- # @example :mult option specified for the vector
28
- # VectorNumber[5, "s", mult: :none].to_s # => "5 + 1's'"
29
- #
30
- # @param mult [Symbol, String]
31
- # text to use between coefficient and unit,
32
- # can be one of the keys in {MULT_STRINGS} or an arbitrary string
33
- # @return [String]
34
- # @raise [ArgumentError]
35
- # if +mult+ is not a String and is not in {MULT_STRINGS}'s keys
36
- #
37
- # @since 0.1.0
38
- def to_s(mult: options[:mult])
39
- return "0" if zero?
18
+ # @group Miscellaneous methods
40
19
 
41
- result = +""
42
- each_with_index do |(unit, coefficient), index|
43
- if index.zero?
44
- result << "-" if coefficient.negative?
45
- else
46
- result << (coefficient.positive? ? " + " : " - ")
47
- end
48
- result << value_to_s(unit, coefficient.abs, mult:)
20
+ # Return string representation of the vector.
21
+ #
22
+ # @example
23
+ # VectorNumber[5, "s"].to_s # => "5 + 1⋅'s'"
24
+ # VectorNumber["s", 5].to_s # => "1⋅'s' + 5"
25
+ # @example with :mult argument
26
+ # VectorNumber[5, "s"].to_s(mult: :asterisk) # => "5 + 1*'s'"
27
+ # @example :mult option specified for the vector
28
+ # VectorNumber[5, "s", mult: :none].to_s # => "5 + 1's'"
29
+ #
30
+ # @param mult [Symbol, String]
31
+ # text to use between coefficient and unit,
32
+ # can be one of the keys in {MULT_STRINGS} or an arbitrary string
33
+ # @return [String]
34
+ # @raise [ArgumentError]
35
+ # if +mult+ is not a String and is not in {MULT_STRINGS}'s keys
36
+ #
37
+ # @since 0.1.0
38
+ def to_s(mult: options[:mult])
39
+ return "0" if zero?
40
+
41
+ result = +""
42
+ each_with_index do |(unit, coefficient), index|
43
+ if index.zero?
44
+ result << "-" if coefficient.negative?
45
+ else
46
+ result << (coefficient.positive? ? " + " : " - ")
49
47
  end
50
- result
48
+ result << value_to_s(unit, coefficient.abs, mult: mult)
51
49
  end
50
+ result
51
+ end
52
52
 
53
- # Get a string representation of the vector.
54
- #
55
- # This is similar to +Complex#inspect+: it returns result of {#to_s} in round brackets.
56
- #
57
- # @example
58
- # VectorNumber[5, "s"].inspect # => "(5 + 1⋅'s')"
59
- #
60
- # @return [String]
61
- #
62
- # @see to_s
63
- #
64
- # @since 0.1.0
65
- def inspect
66
- # TODO: Probably make this independent of options.
67
- "(#{self})"
68
- end
53
+ # Return string representation of the vector.
54
+ #
55
+ # This is similar to +Complex#inspect+: it returns result of {#to_s} in round brackets.
56
+ #
57
+ # @example
58
+ # VectorNumber[5, "s"].inspect # => "(5 + 1⋅'s')"
59
+ #
60
+ # @return [String]
61
+ #
62
+ # @see to_s
63
+ #
64
+ # @since 0.1.0
65
+ def inspect
66
+ # TODO: Probably make this independent of options.
67
+ "(#{self})"
68
+ end
69
69
 
70
- private
70
+ private
71
71
 
72
- # @param unit [Object]
73
- # @param coefficient [Numeric]
74
- # @param mult [Symbol, String]
75
- # @return [String]
76
- # @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
77
- #
78
- # @since 0.1.0
79
- def value_to_s(unit, coefficient, mult:)
80
- if !mult.is_a?(String) && !MULT_STRINGS.key?(mult)
81
- raise ArgumentError, "unknown key #{mult.inspect}", caller
82
- end
72
+ # @param unit [Object]
73
+ # @param coefficient [Numeric]
74
+ # @param mult [Symbol, String]
75
+ # @return [String]
76
+ # @raise [ArgumentError] if +mult+ is not in {MULT_STRINGS}'s keys
77
+ def value_to_s(unit, coefficient, mult:)
78
+ if !mult.is_a?(String) && !MULT_STRINGS.key?(mult)
79
+ raise ArgumentError, "unknown key #{mult.inspect}", caller
80
+ end
83
81
 
84
- case unit
85
- when R
86
- coefficient.to_s
87
- when I
88
- "#{coefficient}i"
89
- else
90
- unit = "'#{unit}'" if unit.is_a?(String)
91
- operator = mult.is_a?(String) ? mult : MULT_STRINGS[mult]
92
- "#{coefficient}#{operator}#{unit}"
93
- end
82
+ case unit
83
+ when R
84
+ coefficient.to_s
85
+ when I
86
+ "#{coefficient}i"
87
+ else
88
+ unit = "'#{unit}'" if unit.is_a?(String)
89
+ operator = mult.is_a?(String) ? mult : MULT_STRINGS[mult]
90
+ "#{coefficient}#{operator}#{unit}"
94
91
  end
95
92
  end
96
93
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  class VectorNumber
4
4
  # @return [String]
5
- VERSION = "0.4.2"
5
+ VERSION = "0.5.0"
6
6
  end