fractional_indexer 0.3.0 → 0.4.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: c6ee81d4e4b26c262bb862ec3b52558808d483bfb67fdb4c32889a7b3cc6c822
4
- data.tar.gz: 2bd077740a8272885e6703f04abdc1361f3171c43156a59794b40bada2c608fd
3
+ metadata.gz: 9c3e15f1e2a102d07fb9fa39a650f28e959184a21bb347e8125df3c36437cfb5
4
+ data.tar.gz: c83b4e10bcf12bed2604dcc2b230f481c0ad46ce7bd6f2777f3cb1fad3b836ad
5
5
  SHA512:
6
- metadata.gz: 7cc2e6cc619a29bcb7cfae8c2fb6e2fbbda62ee17823b217717ac357e93de4bf24830fe3ffd98ce48ba5d19f293fb68d10df4bf4926b8619a61bcfd67be0f826
7
- data.tar.gz: ef547fa499d8bf94c57db52da6fb1b146fe6ce4ba4f14b24123c5e30e125da4e904605918dc314ec76dfebf60deff5af406ab50dbe096b1b24f288e39b4093c0
6
+ metadata.gz: ada9175bde75b29f21e7fe053cdcc0bb433c27c9fb9387de65d0b8816368f47a28108c410476b48f2bde9e5e4eb4855efe0aeef1fb0bce4291fd537a0403583b
7
+ data.tar.gz: 82ee500b85f2dd7ad0883e3fbdf7028141e335e47010e47b9d305837dd45fd6c55d7f6d2e6f12ec8e83ad01cc4790a57238e4a37e82583ee1838c11cc86ef591
data/README.md CHANGED
@@ -21,17 +21,22 @@ gem 'fractional_indexer'
21
21
 
22
22
  And then execute:
23
23
 
24
- $ bundle
24
+ ```sh
25
+ bundle
26
+ ```
25
27
 
26
28
  Or install it yourself as:
27
29
 
28
- $ gem install fractional_indexer
30
+ ```sh
31
+ gem install fractional_indexer
32
+ ```
29
33
 
30
34
  ## Usage
31
35
 
32
36
  ### Generate Order key
33
37
 
34
- To generate an order key, use the `FractionalIndexer.generate_key` method. This method can take two arguments: prev_key and next_key, both of which can be either nil or a string (excluding empty strings).
38
+ To generate an order key, use the `FractionalIndexer.generate_key` method.
39
+ This method can take two arguments: prev_key and next_key, both of which can be either nil or a string (excluding empty strings).
35
40
 
36
41
  The characters that can be used in the strings are available for review in `FractionalIndexer.configuration.digits`.
37
42
 
@@ -85,7 +90,8 @@ FractionalIndexer.generate_keys(prev_key: "b10", next_key: "b11", count: 5)
85
90
 
86
91
  ### base
87
92
 
88
- You can set the base (number system) used to represent each digit. The possible values are :base_10, :base_62, and :base_94. (The default is :base_62.)
93
+ You can set the base (number system) used to represent each digit.
94
+ The possible values are :base_10, :base_62, and :base_94. (The default is :base_62)
89
95
 
90
96
  Note: base_10 is primarily intended for operational verification and debugging purposes.
91
97
 
@@ -111,9 +117,52 @@ FractionalIndexer.configuration.digits.join
111
117
  # => "!\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
112
118
  ```
113
119
 
120
+ ## Order key
121
+
122
+ This section explains the structure of the string that represents an Order Key.
123
+ An Order Key string is broadly divided into two parts: the integer part and the fractional part.
124
+
125
+ Note: For ease of understanding, the explanation from here on will be based on the decimal system ('0' ~ '9')
126
+
127
+ ### Integer Part
128
+
129
+ The integer part of the Order Key begins with a `mandatory prefix` that indicates the number of digits. This prefix is represented by one of the letters A to Z or a to z.
130
+ a is 1 digit, b is 2 digits, c is 3 digits ...... and z represents 26 digits.
131
+ The number of characters following the prefix must match the number of digits indicated by the prefix.
132
+ For example, if the prefix is a, a valid key could be 'a8', and if the prefix is c, a valid key could be 'c135'.
133
+
134
+ ```ruby
135
+ 'a9' # Valid
136
+ 'b10' # Valid
137
+ 'b9' # Invalid (The prefix 'b' requires two digits)
138
+ 'c123' # Valid
139
+ 'c12' # Invalid (The prefix 'c' requires three digits)
140
+ ```
141
+
142
+ Additionally, leveraging the characteristic that uppercase letters have a lower ASCII value than lowercase letters, a to z represent positive integers, while A to Z represent negative integers.
143
+
144
+ ### Fractional Part
145
+
146
+ The Fractional Part refers to the portion of the string that follows the Integer Part, excluding the Integer Part itself.
147
+
148
+ ```ruby
149
+ 'a3012'
150
+ # 'a3' : Integer Part
151
+ # '012': Fractional Part
152
+ ```
153
+
154
+ The Fractional Part cannot end with the first character of the base being used (e.g., '0' in base_10). This rule mirrors the mathematical principle that, for example, 0.3 and 0.30 represent the same value.
155
+
156
+ ```ruby
157
+ 'a32' # Valid
158
+ 'a320' # Invalid (The Fractional Part cannot end with '0' in base_10)
159
+ ```
160
+
161
+ This section clarifies the concept and rules regarding the Fractional Part of an Order Key, with examples to illustrate what constitutes a valid or invalid Fractional Part.
162
+
114
163
  ## Contributing
115
164
 
116
- Bug reports and pull requests are welcome on GitHub at https://github.com/kazu-2020/fractional_indexer.
165
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/kazu-2020/fractional_indexer>.
117
166
 
118
167
  ## License
119
168
 
@@ -39,9 +39,5 @@ module FractionalIndexer
39
39
  def borrow?(index)
40
40
  index == -1
41
41
  end
42
-
43
- def digits
44
- FractionalIndexer.configuration.digits
45
- end
46
42
  end
47
43
  end
@@ -39,9 +39,5 @@ module FractionalIndexer
39
39
  def carry_over?(index)
40
40
  index == digits.length
41
41
  end
42
-
43
- def digits
44
- FractionalIndexer.configuration.digits
45
- end
46
42
  end
47
43
  end
@@ -45,19 +45,15 @@ module FractionalIndexer
45
45
 
46
46
  private
47
47
 
48
- def digits
49
- FractionalIndexer.configuration.digits
50
- end
51
-
52
48
  def validate_positions!(prev_pos, next_pos)
53
- raise FractionalIndexerError, "prev_pos must be less than next_pos" if !next_pos.empty? && prev_pos >= next_pos
49
+ raise Error, "prev_pos must be less than next_pos" if !next_pos.empty? && prev_pos >= next_pos
54
50
 
55
51
  # NOTE
56
52
  # In a string, "0.3" and "0.30" are mathematically equivalent.
57
53
  # Therefore, a trailing "0" is prohibited.
58
54
  return unless prev_pos.end_with?(digits.first) || next_pos.end_with?(digits.first)
59
55
 
60
- raise FractionalIndexerError, "prev_pos and next_pos cannot end with #{digits.first}"
56
+ raise Error, "prev_pos and next_pos cannot end with #{digits.first}"
61
57
  end
62
58
  end
63
59
  end
@@ -58,16 +58,16 @@ module FractionalIndexer
58
58
  integer == minimum_integer
59
59
  end
60
60
 
61
+ def minimum?
62
+ minimum_integer? && fractional.empty?
63
+ end
64
+
61
65
  def present?
62
66
  !(key.nil? || key.empty?)
63
67
  end
64
68
 
65
69
  private
66
70
 
67
- def digits
68
- FractionalIndexer.configuration.digits
69
- end
70
-
71
71
  def integer_digits(key = self.key)
72
72
  if self.class.positive?(key)
73
73
  key.ord - "a".ord + INTEGER_BASE_DIGIT
@@ -82,14 +82,14 @@ module FractionalIndexer
82
82
  "z#{digits.last * POSITIVE_SIGNS.count}"
83
83
  end
84
84
 
85
- def raise_error(description = nil)
86
- raise FractionalIndexerError, "invalid order key: '#{key}' description: #{description}"
87
- end
88
-
89
85
  def minimum_integer
90
86
  "A#{digits.first * POSITIVE_SIGNS.count}"
91
87
  end
92
88
 
89
+ def raise_error(description = nil)
90
+ raise Error, "invalid order key: '#{key}' description: #{description}"
91
+ end
92
+
93
93
  def valid_fractional?(fractional)
94
94
  !fractional.end_with?(digits.first)
95
95
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FractionalIndexer
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -2,17 +2,21 @@
2
2
 
3
3
  require "fractional_indexer/version"
4
4
 
5
- module FractionalIndexer
6
- autoload :Configuration, "fractional_indexer/configuration"
7
- autoload :Decrementer, "fractional_indexer/decrementer"
8
- autoload :Incrementer, "fractional_indexer/incrementer"
9
- autoload :Midpointer, "fractional_indexer/midpointer"
10
- autoload :OrderKey, "fractional_indexer/order_key"
5
+ require "fractional_indexer/configuration"
6
+ require "fractional_indexer/decrementer"
7
+ require "fractional_indexer/incrementer"
8
+ require "fractional_indexer/midpointer"
9
+ require "fractional_indexer/order_key"
11
10
 
12
- class FractionalIndexerError < StandardError; end
11
+ module FractionalIndexer
12
+ class Error < StandardError; end
13
13
 
14
14
  @configuration = Configuration.new
15
15
 
16
+ [Decrementer, Incrementer, Midpointer, OrderKey].each do |klass|
17
+ klass.send(:define_method, :digits) { FractionalIndexer.configuration.digits }
18
+ end
19
+
16
20
  def self.configure
17
21
  yield(configuration) if block_given?
18
22
 
@@ -26,10 +30,10 @@ module FractionalIndexer
26
30
  def self.generate_key(prev_key: nil, next_key: nil)
27
31
  return OrderKey.zero if prev_key.nil? && next_key.nil?
28
32
 
29
- raise FractionalIndexerError, "prev_key and next_key cannot be empty" if prev_key&.empty? || next_key&.empty?
33
+ raise Error, "prev_key and next_key cannot be empty" if prev_key&.empty? || next_key&.empty?
30
34
 
31
35
  if !prev_key.nil? && !next_key.nil? && prev_key >= next_key
32
- raise FractionalIndexerError, "#{prev_key} is not less than #{next_key}"
36
+ raise Error, "#{prev_key} is not less than #{next_key}"
33
37
  end
34
38
 
35
39
  prev_order_key = OrderKey.new(prev_key)
@@ -87,9 +91,20 @@ module FractionalIndexer
87
91
  end
88
92
 
89
93
  def self.decrement(order_key)
94
+ # NOTE: edge case
95
+ # If an Order key does not have a fractional part and its integer part is equal to
96
+ # the minimum value (for example, in base_62: 'A00000000000000000000000000'),
97
+ # further decrement operations become impossible and will fail.
98
+ # However, this situation is typically unlikely to occur.
99
+ if order_key.minimum?
100
+ raise Error, "order key not decremented: #{order_key} is the minimum value"
101
+ end
90
102
  return order_key.integer + Midpointer.execute("", order_key.fractional) if order_key.minimum_integer?
91
103
 
92
- order_key.integer < order_key.key ? order_key.integer : order_key.decrement
104
+ decremented_order_key = OrderKey.new(order_key.integer < order_key.key ? order_key.integer : order_key.decrement)
105
+ return decremented_order_key.integer + Midpointer.execute if decremented_order_key.minimum?
106
+
107
+ decremented_order_key.key
93
108
  end
94
109
 
95
110
  def self.increment(order_key)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fractional_indexer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kazu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-07 00:00:00.000000000 Z
11
+ date: 2024-03-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  FractionalIndexer is a Ruby gem designed to leverage fractional indexing for ordering within lists or data structures.