fractional_indexing 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ec000613bb56c79ff44198f1fc0e45be734922f89c22b5ce9fb5ec5ec0ae2df
4
- data.tar.gz: ead7373a2c426ff75274a5b0fb87cf3f5e8332075ae4e2c70fa071d1795c5ea8
3
+ metadata.gz: 764de1e81ced54924f6f5f5c6e5e24a07e31fa761c0b6bc0f7a697ae16676051
4
+ data.tar.gz: ae443a576b2a888e6ba4af29cd13609629ebcd90eb14ad06c13ed34664e6164a
5
5
  SHA512:
6
- metadata.gz: 8914f607fd7028a06c1082901baa30aba1330f354643af03cf1a2b99985cd239378ba96aebf17c8a241bf2dbab2550fb9111086047e45456998a07392e1494af
7
- data.tar.gz: 2b2996841ca207d20a26670d30f4dffeef721334ab6ceb2a5143abd543f400ee43b8289867c5057c8827e4f5ddf7d934003316ca13cccad133b7c6b14c5a5cff
6
+ metadata.gz: 6a0ff4c208d23826268ec6d29e3f53dd8d1b1e9d8420556efd5a5cf520a8d3f08db11b0eb185da48247e512cdf1a5fafb39503d9a004b390030dbf3daf85e27a
7
+ data.tar.gz: 3bd8c4184d9dbafacfa1055ed17fbad178e08262418b98a4e4edfe652bf7469963ae5e537990c3b6d47ae8f9af0f707ac73a048ea440ace2538f5c5f251f5c63
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fractional_indexing (0.1.0)
4
+ fractional_indexing (0.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -11,6 +11,7 @@ GEM
11
11
 
12
12
  PLATFORMS
13
13
  x86_64-darwin-20
14
+ x86_64-linux
14
15
 
15
16
  DEPENDENCIES
16
17
  fractional_indexing!
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/fractional_indexing/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fractional_indexing"
7
+ spec.version = FractionalIndexing::VERSION
8
+ spec.authors = ["Andreas Alin"]
9
+ spec.email = ["andreas.alin@gmail.com"]
10
+
11
+ spec.summary = "Fractional Indexing in Ruby"
12
+ spec.homepage = "https://github.com/aalin/fractional_indexing.rb"
13
+ spec.license = " CC0-1.0"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(__dir__) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
24
+ end
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FractionalIndexing
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
@@ -25,22 +25,16 @@ module FractionalIndexing
25
25
  # digits is a string such as '0123456789' for base 10. Digits must be in
26
26
  # ascending character code order!
27
27
  raise Error, "#{a} >= #{b}" if b && a >= b
28
- raise Error, "trailing zero" if (a[-1] == "0") || (b && (b[-1] == "0"))
28
+ raise Error, "trailing zero" if a.end_with?("0") || b&.end_with?("0")
29
+
29
30
  if b
30
31
  # remove longest common prefix. pad `a` with 0s as we
31
32
  # go. note that we don't need to pad `b`, because it can't
32
33
  # end before `a` while traversing the common prefix.
33
- n = 0
34
- a = a.ljust(b.length, "0")
35
- for i in 0..(b.length - 1)
36
- if a[i] == b[i]
37
- n += 1
38
- else
39
- break
40
- end
34
+ n = b.each_char.with_index.count { |c, i| (a[i] || "0") == c }
35
+ if n > 0
36
+ return b[0...n] + midpoint(a[n..], b[n..], digits)
41
37
  end
42
-
43
- return b[0..(n - 1)] + midpoint(a[n..-1], b[n..-1], digits) if n > 0
44
38
  end
45
39
 
46
40
  # first digits (or lack of digit) are different
@@ -50,6 +44,7 @@ module FractionalIndexing
50
44
  min_digit = (0.5 * (digit_a + digit_b)).round
51
45
  return digits[min_digit]
52
46
  else
47
+ # first digits are consecutive
53
48
  if b && b.length > 1
54
49
  return b[0]
55
50
  else
@@ -59,45 +54,51 @@ module FractionalIndexing
59
54
  # given, for example, midpoint('49', '5'), return
60
55
  # '4' + midpoint('9', null), which will become
61
56
  # '4' + '9' + midpoint('', null), which is '495'
62
- digit_a = a.empty? ? 0 : digits.index(a[0])
63
- return digits[digit_a] + midpoint(a[1..-1], nil, digits)
57
+ return digits[digit_a] + midpoint(a[1..], nil, digits)
64
58
  end
65
59
  end
66
60
  end
67
61
 
68
- def self.validate_integer(i)
69
- unless i.length == get_integer_length(i[0])
70
- raise Error, "invalid integer part of order key: #{i}"
62
+ def self.validate_integer(int)
63
+ unless int.length == get_integer_length(int[0])
64
+ raise Error, "invalid integer part of order key: #{int}"
71
65
  end
72
66
  end
73
67
 
74
68
  def self.get_integer_length(head)
75
- if ('a'..'z').cover?(head)
76
- return head.ord - 'a'.ord + 2
77
- elsif ('A'..'Z').cover?(head)
78
- return 'Z'.ord - head.ord + 2
69
+ case head
70
+ in 'a'..'z'
71
+ head.ord - 'a'.ord + 2
72
+ in 'A'..'Z'
73
+ 'Z'.ord - head.ord + 2
74
+ else
75
+ raise Error, "invalid order key head: #{head}"
79
76
  end
80
- raise Error, "invalid order key head: " + head
81
77
  end
82
78
 
83
79
  def self.get_integer_part(key)
84
80
  integer_part_length = get_integer_length(key[0])
85
- raise Error, "invalid order key: #{key}" if integer_part_length > key.size
86
81
 
87
- key[0...integer_part_length]
82
+ if integer_part_length > key.length
83
+ raise Error, "invalid order key: #{key}"
84
+ end
85
+
86
+ key.slice(0, integer_part_length)
88
87
  end
89
88
 
90
89
  def self.validate_order_key(key)
91
- raise Error, "invalid order key: #{key}" if key == SMALLEST_INTEGER
90
+ if key == SMALLEST_INTEGER
91
+ raise Error, "invalid order key: #{key}"
92
+ end
92
93
 
93
94
  # get_integer_part() will throw if the first character is bad,
94
95
  # or the key is too short. we'd call it to check these things
95
96
  # even if we didn't need the result
96
97
  i = get_integer_part(key)
97
98
  f = key[i.size..]
98
- raise Error, "invalid order key: #{key}" if f[-1] == "0"
99
-
100
- nil
99
+ if f.end_with?("0")
100
+ raise Error, "invalid order key: #{key}"
101
+ end
101
102
  end
102
103
 
103
104
  def self.increment_integer(x, digits)
@@ -126,12 +127,16 @@ module FractionalIndexing
126
127
  else
127
128
  digs.pop
128
129
  end
129
- return h + digs.join
130
+ h + digs.join
130
131
  else
131
- return head + digs.join
132
+ head + digs.join
132
133
  end
133
134
  end
134
135
 
136
+ def self.increment_integer!(x, digits)
137
+ increment_integer(x, digits) or raise(Error, "cannot increment anymore")
138
+ end
139
+
135
140
  def self.decrement_integer(x, digits)
136
141
  validate_integer(x)
137
142
  head, *digs = x.chars
@@ -152,53 +157,61 @@ module FractionalIndexing
152
157
  elsif head == 'A'
153
158
  return nil
154
159
  end
155
- h = (head.ord - 1).chr
160
+ h = head.ord.pred.chr
156
161
  if h < 'Z'
157
162
  digs.push(digits[-1])
158
163
  else
159
164
  digs.pop
160
165
  end
161
- return h + digs.join
166
+ h + digs.join
162
167
  else
163
- return head + digs.join
168
+ head + digs.join
164
169
  end
165
170
  end
166
171
 
172
+ def self.decrement_integer!(x, digits)
173
+ decrement_integer(x, digits) or raise(Error, "cannot decrement anymore")
174
+ end
175
+
167
176
  def self.generate_key_between(a, b, digits = BASE_62_DIGITS)
168
177
  validate_order_key(a) if a
169
178
  validate_order_key(b) if b
170
- raise "a >= b" if a && b && a >= b
179
+ raise "#{a.inspect} >= #{b.inspect}" if a && b && a >= b
180
+
181
+ unless a
182
+ return INTEGER_ZERO unless b
171
183
 
172
- if a == nil
173
- return INTEGER_ZERO if b == nil
174
184
  ib = get_integer_part(b)
175
- fb = b[ib.length..-1]
176
- return ib + midpoint("", fb, digits) if ib == SMALLEST_INTEGER
177
- return ib if ib < b
178
- res = decrement_integer(ib, digits)
179
- raise "cannot decrement any more" if res == nil
180
- return res
185
+ if ib == SMALLEST_INTEGER
186
+ fb = b[ib.length..]
187
+ return ib + midpoint("", fb, digits)
188
+ elsif ib < b
189
+ return ib
190
+ else
191
+ return decrement_integer!(ib, digits)
192
+ end
181
193
  end
182
194
 
183
- if b == nil
195
+ unless b
184
196
  ia = get_integer_part(a)
185
- fa = a[ia.length..-1]
186
- i = increment_integer(ia, digits)
187
- return ia + midpoint(fa, nil, digits) if i == nil
188
- return i
197
+ fa = a[ia.length..]
198
+ return increment_integer(ia, digits) || (ia + midpoint(fa, nil, digits))
189
199
  end
190
200
 
191
201
  ia = get_integer_part(a)
192
- fa = a[ia.length..-1]
202
+ fa = a[ia.length..]
193
203
  ib = get_integer_part(b)
194
- fb = b[ib.length..-1]
195
- return ia + midpoint(fa, fb, digits) if ia == ib
196
- i = increment_integer(ia, digits)
197
- raise "cannot increment any more" if i == nil
204
+ fb = b[ib.length..]
205
+
206
+ if ia == ib
207
+ return ia + midpoint(fa, fb, digits)
208
+ end
209
+
210
+ i = increment_integer!(ia, digits)
198
211
 
199
212
  return i if i < b
200
213
 
201
- return ia + midpoint(fa, nil, digits)
214
+ ia + midpoint(fa, nil, digits)
202
215
  end
203
216
 
204
217
  # Returns an array of n distinct keys in sorted order.
@@ -206,42 +219,26 @@ module FractionalIndexing
206
219
  # If one or the other is nil, returns consecutive "integer"
207
220
  # keys. Otherwise, returns relatively short keys between.
208
221
  def self.generate_n_keys_between(a, b, n, digits = BASE_62_DIGITS)
209
- return [] if n.zero?
210
-
211
- return [generate_key_between(a, b, digits)] if n == 1
212
-
213
- unless b
214
- c = generate_key_between(a, b, digits)
215
- result = [c]
216
- (n - 1).times do
217
- c = generate_key_between(c, b, digits)
218
- result << c
219
- end
220
- return result
222
+ if n == 0
223
+ return []
224
+ elsif n == 1
225
+ return [generate_key_between(a, b, digits)]
221
226
  end
222
227
 
223
- unless a
224
- c = generate_key_between(a, b, digits)
225
- result = [c]
226
- (n - 1).times do
227
- c = generate_key_between(a, c, digits)
228
- result << c
229
- end
230
- return result.reverse
231
- end
228
+ return n.times.map do
229
+ a = generate_key_between(a, b, digits)
230
+ end unless b
232
231
 
233
- mid = n / 2
232
+ return n.times.map do
233
+ b = generate_key_between(a, b, digits)
234
+ end.reverse unless a
235
+
236
+ mid = (n / 2).floor
234
237
  c = generate_key_between(a, b, digits)
235
238
  [
236
- *generate_n_keys_between(a, c, mid.floor, digits),
239
+ *generate_n_keys_between(a, c, mid, digits),
237
240
  c,
238
- *generate_n_keys_between(c, b, n - mid.floor - 1, digits)
241
+ *generate_n_keys_between(c, b, n - mid - 1, digits)
239
242
  ]
240
243
  end
241
-
242
- # Rounds a float to an integer using decimal.Decimal.quantize with
243
- # decimal.ROUND_HALF_UP rounding method.
244
- def self.round_half_up(n)
245
- (n.to_d.round(0, half: :up)).to_i
246
- end
247
244
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fractional_indexing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Alin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-07 00:00:00.000000000 Z
11
+ date: 2023-04-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -22,6 +22,7 @@ files:
22
22
  - LICENSE
23
23
  - README.md
24
24
  - Rakefile
25
+ - fractional_indexing.gemspec
25
26
  - lib/fractional_indexing.rb
26
27
  - lib/fractional_indexing/version.rb
27
28
  - sig/fractional_indexing.rbs