more_math 1.5.0 → 1.6.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.
@@ -1,7 +1,49 @@
1
1
  module MoreMath
2
+ # StringNumeral provides a way to treat strings as numbers using a base-N
3
+ # system where N is the size of the alphabet. This allows for arithmetic
4
+ # operations on strings, converting them to numeric representations and back.
5
+ #
6
+ # The implementation uses the Cantor pairing function approach for encoding
7
+ # strings into integers and vice versa, making it suitable for applications
8
+ # requiring ordered string enumeration or Gödel numbering.
9
+ #
10
+ # @example Basic usage
11
+ # include MoreMath
12
+ #
13
+ # # Convert a string to its numeric representation
14
+ # str_num = "abc".to_string_numeral
15
+ # puts str_num.number # => 731 (assuming 'a'..'z' alphabet)
16
+ #
17
+ # # Convert back to string
18
+ # puts 731.to_string_numeral.string # => "abc"
19
+ #
20
+ # @example Arithmetic operations
21
+ # a = "hello".to_string_numeral
22
+ # b = "world".to_string_numeral
23
+ #
24
+ # # Addition
25
+ # sum = a + b
26
+ # puts sum.string # => "hello" + "world" (in numeric sense)
27
+ #
28
+ # @example With custom alphabet
29
+ # custom_alphabet = ['a', 'b', 'c']
30
+ # str_num = StringNumeral.from("abc", custom_alphabet)
31
+ # puts str_num.number # => 18 (base-3 representation)
2
32
  class StringNumeral
3
33
  include ::MoreMath::NumberifyStringFunction
4
34
 
35
+ # Creates a StringNumeral instance from an object.
36
+ #
37
+ # This is the primary factory method that handles conversion from various
38
+ # input types:
39
+ # - Symbol: converts to string first
40
+ # - String: uses directly as string representation
41
+ # - Integer: converts to numeric representation
42
+ # - Other objects: calls to_str or to_int as appropriate
43
+ #
44
+ # @param object [Object] The input object to convert
45
+ # @param alphabet [Array<String>, Range<String>] The alphabet to use for conversion
46
+ # @return [StringNumeral] A new StringNumeral instance
5
47
  def self.from(object, alphabet = 'a'..'z')
6
48
  if Symbol === object
7
49
  StringNumeral.from_string(object.to_s, alphabet)
@@ -14,14 +56,33 @@ module MoreMath
14
56
  end
15
57
  end
16
58
 
59
+ # Creates a StringNumeral instance from a string.
60
+ #
61
+ # @param string [String] The string to convert
62
+ # @param alphabet [Array<String>, Range<String>] The alphabet to use
63
+ # @return [StringNumeral] A new StringNumeral instance
17
64
  def self.from_string(string, alphabet)
18
65
  new string, nil, alphabet
19
66
  end
20
67
 
68
+ # Creates a StringNumeral instance from a number.
69
+ #
70
+ # @param number [Integer] The number to convert
71
+ # @param alphabet [Array<String>, Range<String>] The alphabet to use
72
+ # @return [StringNumeral] A new StringNumeral instance
21
73
  def self.from_number(number, alphabet)
22
74
  new nil, number, alphabet
23
75
  end
24
76
 
77
+ # Initializes a StringNumeral instance.
78
+ #
79
+ # This private constructor handles the internal state setup for either
80
+ # string-to-number or number-to-string conversion.
81
+ #
82
+ # @param string [String, nil] The string representation (if converting from string)
83
+ # @param number [Integer, nil] The numeric representation (if converting from number)
84
+ # @param alphabet [Array<String>, Range<String>] The alphabet to use
85
+ # @raise [ArgumentError] If the string contains characters not in the alphabet
25
86
  def initialize(string, number, alphabet)
26
87
  @alphabet = NumberifyStringFunction.convert_alphabet(alphabet).freeze
27
88
  if string
@@ -36,96 +97,185 @@ module MoreMath
36
97
  end
37
98
  private_class_method :new
38
99
 
100
+ # Returns the numeric representation of this StringNumeral.
101
+ #
102
+ # This method converts the internal string representation to its numeric value
103
+ # using the specified alphabet. The conversion follows a positional numeral system
104
+ # where each character position represents a power of the alphabet size.
105
+ #
106
+ # @return [Integer] The numeric representation
39
107
  def number
40
108
  @number ||= numberify_string(@string, @alphabet)
41
109
  end
42
110
  alias to_i number
43
111
  alias to_int number
44
112
 
113
+ # Returns the string representation of this StringNumeral.
114
+ #
115
+ # This method converts the internal numeric representation back to its string form
116
+ # using the specified alphabet. It's the inverse operation of {#number}.
117
+ #
118
+ # @return [String] The string representation
45
119
  def string
46
120
  @string ||= stringify_number(@number, @alphabet).freeze
47
121
  end
48
122
  alias to_s string
49
123
  alias to_str string
50
124
 
125
+ # Returns a string representation for debugging.
126
+ #
127
+ # @return [String] A debug-friendly representation of this instance
51
128
  def inspect
52
129
  "#<#{self.class}: #{string.inspect} #{number.inspect}>"
53
130
  end
54
131
 
132
+ # Returns the alphabet used by this StringNumeral.
133
+ #
134
+ # @return [Array<String>] The alphabet array
55
135
  attr_reader :alphabet
56
136
 
137
+ # Converts another object to a numeric value for arithmetic operations.
138
+ #
139
+ # This method is used in Ruby's coercion protocol to enable mixed-type arithmetic.
140
+ #
141
+ # @param other [Object] The other operand
142
+ # @return [Array<Integer>] Array containing the naturalized values
57
143
  def coerce(other)
58
144
  [ naturalize(other), number ]
59
145
  end
60
146
 
61
- def *(other)
62
- self.class.from_number(number * naturalize(other), @alphabet)
63
- end
64
-
147
+ # Performs addition with another StringNumeral or numeric value.
148
+ #
149
+ # @param other [StringNumeral, Integer] The value to add
150
+ # @return [StringNumeral] A new StringNumeral instance representing the sum
65
151
  def +(other)
66
152
  self.class.from_number(number + naturalize(other), @alphabet)
67
153
  end
68
154
 
155
+ # Performs multiplication with another StringNumeral or numeric value.
156
+ #
157
+ # @param other [StringNumeral, Integer] The value to multiply by
158
+ # @return [StringNumeral] A new StringNumeral instance representing the product
159
+ def *(other)
160
+ self.class.from_number(number * naturalize(other), @alphabet)
161
+ end
162
+
163
+ # Performs subtraction with another StringNumeral or numeric value.
164
+ #
165
+ # @param other [StringNumeral, Integer] The value to subtract
166
+ # @return [StringNumeral] A new StringNumeral instance representing the difference
69
167
  def -(other)
70
168
  self.class.from_number(naturalize(number - other), @alphabet)
71
169
  end
72
170
 
171
+ # Performs division with another StringNumeral or numeric value.
172
+ #
173
+ # @param other [StringNumeral, Integer] The divisor
174
+ # @return [StringNumeral] A new StringNumeral instance representing the quotient
73
175
  def /(other)
74
176
  self.class.from_number((number / naturalize(other)), @alphabet)
75
177
  end
76
178
 
179
+ # Performs modulo operation with another StringNumeral or numeric value.
180
+ #
181
+ # @param other [StringNumeral, Integer] The divisor for modulo
182
+ # @return [StringNumeral] A new StringNumeral instance representing the remainder
77
183
  def %(other)
78
184
  self.class.from_number((number % naturalize(other)), @alphabet)
79
185
  end
80
186
 
187
+ # Performs exponentiation with another StringNumeral or numeric value.
188
+ #
189
+ # @param other [StringNumeral, Integer] The exponent
190
+ # @return [StringNumeral] A new StringNumeral instance representing the power
81
191
  def **(other)
82
192
  self.class.from_number(number ** naturalize(other), @alphabet)
83
193
  end
84
194
 
195
+ # Performs left bit shift with another numeric value.
196
+ #
197
+ # @param other [Integer] The number of bits to shift
198
+ # @return [StringNumeral] A new StringNumeral instance representing the shifted value
85
199
  def <<(other)
86
200
  self.class.from_number(number << naturalize(other), @alphabet)
87
201
  end
88
202
 
203
+ # Performs right bit shift with another numeric value.
204
+ #
205
+ # @param other [Integer] The number of bits to shift
206
+ # @return [StringNumeral] A new StringNumeral instance representing the shifted value
89
207
  def >>(other)
90
208
  self.class.from_number(number >> naturalize(other), @alphabet)
91
209
  end
92
210
 
211
+ # Performs bitwise XOR with another numeric value.
212
+ #
213
+ # @param other [Integer] The value to XOR with
214
+ # @return [StringNumeral] A new StringNumeral instance representing the result
93
215
  def ^(other)
94
216
  self.class.from_number(number ^ naturalize(other), @alphabet)
95
217
  end
96
218
 
219
+ # Performs bitwise AND with another numeric value.
220
+ #
221
+ # @param other [Integer] The value to AND with
222
+ # @return [StringNumeral] A new StringNumeral instance representing the result
97
223
  def &(other)
98
224
  self.class.from_number(number & naturalize(other), @alphabet)
99
225
  end
100
226
 
227
+ # Performs bitwise OR with another numeric value.
228
+ #
229
+ # @param other [Integer] The value to OR with
230
+ # @return [StringNumeral] A new StringNumeral instance representing the result
101
231
  def |(other)
102
232
  self.class.from_number(number | naturalize(other), @alphabet)
103
233
  end
104
234
 
235
+ # Performs indexing operation on the numeric value.
236
+ #
237
+ # @param other [Integer] The index to access
238
+ # @return [StringNumeral] A new StringNumeral instance representing the indexed result
105
239
  def [](other)
106
240
  self.class.from_number(number[other.to_i], @alphabet)
107
241
  end
108
242
 
243
+ # Returns the successor of this StringNumeral.
244
+ #
245
+ # @return [StringNumeral] A new StringNumeral with number incremented by 1
109
246
  def succ
110
247
  self.class.from_number(number + 1, @alphabet)
111
248
  end
112
249
 
250
+ # Increments this StringNumeral in place.
251
+ #
252
+ # @return [self] Returns self after incrementing
113
253
  def succ!
114
254
  @number += 1
115
255
  @string = nil
116
256
  self
117
257
  end
118
258
 
259
+ # Returns the predecessor of this StringNumeral.
260
+ #
261
+ # @return [StringNumeral] A new StringNumeral with number decremented by 1
119
262
  def pred
120
263
  self.class.from_number(naturalize(number - 1), @alphabet)
121
264
  end
122
265
 
266
+ # Decrements this StringNumeral in place.
267
+ #
268
+ # @return [self] Returns self after decrementing
123
269
  def pred!
124
270
  @number = naturalize(@number - 1)
125
271
  @string = nil
126
272
  self
127
273
  end
128
274
 
275
+ # Checks equality with another object.
276
+ #
277
+ # @param other [Object] The object to compare with
278
+ # @return [Boolean] true if equal, false otherwise
129
279
  def eql?(other)
130
280
  if other.respond_to?(:to_int)
131
281
  to_int == other.to_int
@@ -136,28 +286,46 @@ module MoreMath
136
286
 
137
287
  alias == eql?
138
288
 
289
+ # Returns a hash value for this StringNumeral.
290
+ #
291
+ # @return [Integer] The hash value
139
292
  def hash
140
293
  number.hash
141
294
  end
142
295
 
143
296
  private
144
297
 
298
+ # Ensures a value is treated as a non-negative integer.
299
+ #
300
+ # @param number [Object] The number to normalize
301
+ # @return [Integer] The normalized non-negative integer
145
302
  def naturalize(number)
146
303
  number = number.to_i
147
304
  number < 0 ? 0 : number
148
305
  end
149
306
 
307
+ # Module containing convenience methods for StringNumeral conversion.
150
308
  module Functions
309
+ # Creates a StringNumeral from an object.
310
+ #
311
+ # @param other [Object] The object to convert
312
+ # @param alphabet [Array<String>, Range<String>] The alphabet to use
313
+ # @return [StringNumeral] A new StringNumeral instance
151
314
  def StringNumeral(other, alphabet = 'a'..'z')
152
315
  ::MoreMath::StringNumeral.from(other, alphabet)
153
316
  end
154
317
 
318
+ # Converts this object to a StringNumeral.
319
+ #
320
+ # @param alphabet [Array<String>, Range<String>] The alphabet to use
321
+ # @return [StringNumeral] A new StringNumeral instance
155
322
  def to_string_numeral(alphabet = 'a'..'z')
156
323
  StringNumeral(self, alphabet)
157
324
  end
158
325
  end
159
326
  end
160
327
 
328
+ # Extends the Object class with StringNumeral conversion methods.
161
329
  class ::Object
162
330
  include StringNumeral::Functions
163
331
  end
@@ -1,18 +1,49 @@
1
1
  require 'more_math/ranking_common'
2
2
 
3
3
  module MoreMath
4
+ # A subset represents a selection of elements from a set, where each element
5
+ # can either be included (1) or excluded (0) from the subset. Subsets are
6
+ # ranked in lexicographic order, allowing efficient enumeration and indexing.
7
+ #
8
+ # @example Create a subset of size 3
9
+ # subset = Subset.new(3)
10
+ # # => #<Subset:0x6ae34 @last=7, @rank=0, @size=3>
11
+ #
12
+ # @example Create a subset with specific rank
13
+ # subset = Subset.new(3, 5)
14
+ # # => #<Subset:0x6ae34 @last=7, @rank=5, @size=3>
15
+ #
16
+ # @example Get the subset as an array of indices
17
+ # subset = Subset.new(3, 5)
18
+ # subset.value
19
+ # # => [0, 2]
20
+ #
21
+ # @example Project onto actual data
22
+ # subset = Subset.new(3, 5)
23
+ # subset.project(['a', 'b', 'c'])
24
+ # # => ['a', 'c']
4
25
  class Subset
5
26
  include Enumerable
6
27
  include RankingCommon
7
28
 
8
29
  # Returns a Subset instance for a collection of size +size+ with the rank
9
30
  # +rank+.
31
+ # Creates a new Subset instance of <code>size</code> (and ranked with
32
+ # <code>rank</code>).
33
+ #
34
+ # @param size [Integer] The size of the subset
35
+ # @param rank [Integer] The rank of the subset (default: 0)
10
36
  def initialize(size, rank = 0)
11
37
  @size, self.rank = size, rank
12
38
  @last = (1 << size) - 1
13
39
  end
14
40
 
15
- # Returns the subset of the collection +collection+ for the rank +rank+.
41
+ # Creates a new Subset instance for a collection of size +size+ with the
42
+ # rank +rank+.
43
+ #
44
+ # @param collection [Object] collection to use for projection
45
+ # @param rank [Integer] the rank of this subset (default: 0)
46
+ # @return [Subset] new subset instance
16
47
  def self.for(collection, rank = 0)
17
48
  subset = new(collection.size, rank)
18
49
  subset.instance_variable_set(:@collection, collection)
@@ -20,30 +51,43 @@ module MoreMath
20
51
  end
21
52
 
22
53
  # Returns the power set of the collection +collection+.
54
+ #
55
+ # @param collection [Object] the collection to generate power set for
56
+ # @return [Array<Array>] array of all possible subsets
23
57
  def self.power_set(collection)
24
58
  self.for(collection).map(&:value)
25
59
  end
26
60
 
27
- # Assigns <code>m</code> to the rank attribute of this object.
61
+ # Assigns <code>m</code> to the rank attribute of this Subset instance.
62
+ # That implies that the indices produced by a call to the Subset#value method
63
+ # of this instance is the subset ranked with this new <code>rank</code>.
64
+ #
65
+ # @param m [Integer] new rank value
66
+ # @return [Integer] assigned rank
28
67
  def rank=(m)
29
68
  @rank = m % (1 << size)
30
69
  end
31
70
 
32
71
  # Returns the subset for rank #rank and #collection. (If no collection was
33
72
  # set it applies to the array [ 0, 1, ..., size - 1 ] instead.)
73
+ #
74
+ # @return [Array<Integer>] array of indices representing this subset
34
75
  def value
35
76
  result = []
36
77
  c = @collection || (0...size).to_a
37
78
  r = @rank
38
- 0.upto(size) do |i|
79
+ size.times do |i|
39
80
  r[i] == 1 and result << c[i]
40
81
  end
41
82
  result
42
83
  end
43
84
 
44
- # This method maps elements from a given dataset based on the
45
- # subset's indices determined by its rank and returns the result, while
85
+ # This method maps elements from a given dataset based on the subset's
86
+ # indices determined by its rank and returns the result, while
46
87
  # ensuring the input data size matches the subset's size.
88
+ #
89
+ # @param data [Object] data to project onto (optional)
90
+ # @return [Array] projected result
47
91
  def project(data = nil)
48
92
  data ||= @collection || (0...size).to_a
49
93
  raise ArgumentError, "data size is != #{size}!" if data.size != size
@@ -1,6 +1,6 @@
1
1
  module MoreMath
2
2
  # MoreMath version
3
- VERSION = '1.5.0'
3
+ VERSION = '1.6.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/lib/more_math.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # Main namespace module for the MoreMath library.
1
2
  module MoreMath
2
3
  unless defined?(::MoreMath::Infinity) == 'constant'
3
4
  Infinity = 1.0 / 0 # Refers to floating point infinity.
data/more_math.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: more_math 1.5.0 ruby lib
2
+ # stub: more_math 1.6.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "more_math".freeze
6
- s.version = "1.5.0".freeze
6
+ s.version = "1.6.0".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
@@ -23,12 +23,13 @@ Gem::Specification.new do |s|
23
23
 
24
24
  s.specification_version = 4
25
25
 
26
- s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 2.4".freeze])
26
+ s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 2.7".freeze])
27
27
  s.add_development_dependency(%q<rake>.freeze, [">= 0".freeze])
28
28
  s.add_development_dependency(%q<simplecov>.freeze, [">= 0".freeze])
29
29
  s.add_development_dependency(%q<test-unit>.freeze, [">= 0".freeze])
30
30
  s.add_development_dependency(%q<debug>.freeze, [">= 0".freeze])
31
31
  s.add_development_dependency(%q<all_images>.freeze, [">= 0".freeze])
32
+ s.add_development_dependency(%q<context_spook>.freeze, [">= 0".freeze])
32
33
  s.add_runtime_dependency(%q<tins>.freeze, ["~> 1".freeze])
33
34
  s.add_runtime_dependency(%q<mize>.freeze, [">= 0".freeze])
34
35
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: more_math
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '2.4'
18
+ version: '2.7'
19
19
  type: :development
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '2.4'
25
+ version: '2.7'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rake
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +93,20 @@ dependencies:
93
93
  - - ">="
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: context_spook
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
96
110
  - !ruby/object:Gem::Dependency
97
111
  name: tins
98
112
  requirement: !ruby/object:Gem::Requirement