treemap 1.0.1 → 1.0.2
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 +4 -4
- data/lib/bounded_map.rb +380 -0
- data/lib/{treemap.rb → tree_map.rb} +84 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a76485540e11f6e78e43a81495c4d5b34db938aa
|
4
|
+
data.tar.gz: dcc1eb850aba5f6719a87bd9e7c1e8b1f709d086
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce57abdf11c507eeb59abd1b9690ac12d956d84405842a078e82555d068a088b2271cc695cff8bcd327d6de8a15ffc189a16286c64fb52e45bae8f0dd7ab94d7
|
7
|
+
data.tar.gz: c9b01d274b76a6caca18db701fc3495289cc70ad7464bd8eb97fb9cea5eede05c3e3a415dc52f28a0027e797de0c990ea22d1ededb612ba18a3b5caf6ae5f27f
|
data/lib/bounded_map.rb
ADDED
@@ -0,0 +1,380 @@
|
|
1
|
+
class TreeMap
|
2
|
+
module Bound
|
3
|
+
INCLUSIVE = 1
|
4
|
+
EXCLUSIVE = 2
|
5
|
+
NO_BOUND = 3
|
6
|
+
end
|
7
|
+
|
8
|
+
# A map with optional limits on its range.
|
9
|
+
# This is intended to be used only by the TreeMap class.
|
10
|
+
class BoundedMap
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
attr_accessor :treemap, :ascending, :from, :from_bound, :to, :to_bound
|
14
|
+
|
15
|
+
def initialize(treemap, ascending, from, from_bound, to, to_bound)
|
16
|
+
@treemap = treemap
|
17
|
+
@ascending = ascending
|
18
|
+
@from = from
|
19
|
+
@from_bound = from_bound
|
20
|
+
@to = to
|
21
|
+
@to_bound = to_bound
|
22
|
+
|
23
|
+
# Validate the bounds. In addition to checking that from <= to, we verify that the comparator supports our bound objects.
|
24
|
+
if from_bound != Bound::NO_BOUND && to_bound != Bound::NO_BOUND
|
25
|
+
raise "Invalid from and to arguments: #{from} (from) > #{to} (to)" if comparator.call(from, to) > 0
|
26
|
+
elsif from_bound != Bound::NO_BOUND
|
27
|
+
comparator.call(from, from)
|
28
|
+
elsif to_bound != Bound::NO_BOUND
|
29
|
+
comparator.call(to, to)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def empty?
|
34
|
+
endpoint(true).nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def get(key)
|
38
|
+
@treemap.get(key) if in_bounds?(key)
|
39
|
+
end
|
40
|
+
|
41
|
+
def contains_key?(key)
|
42
|
+
in_bounds?(key) && @treemap.contains_key?(key)
|
43
|
+
end
|
44
|
+
|
45
|
+
def put(key, value)
|
46
|
+
raise "Key out of bounds." unless in_bounds?(key)
|
47
|
+
put_internal(key, value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove(key)
|
51
|
+
@treemap.remove(key) if in_bounds?(key)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns true if the key is in bounds.
|
55
|
+
# Note: The reference implementation calls this function isInBounds
|
56
|
+
def in_bounds?(key)
|
57
|
+
in_closed_bounds?(key, @from_bound, @to_bound)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns true if the key is in bounds. Use this overload with
|
61
|
+
# NO_BOUND to skip bounds checking on either end.
|
62
|
+
# Note: The reference implementation calls this function isInBounds
|
63
|
+
def in_closed_bounds?(key, from_bound, to_bound)
|
64
|
+
if from_bound == Bound::INCLUSIVE
|
65
|
+
return false if comparator.call(key, from) < 0 # less than from
|
66
|
+
elsif from_bound == Bound::EXCLUSIVE
|
67
|
+
return false if comparator.call(key, from) <= 0 # less than or equal to from
|
68
|
+
end
|
69
|
+
if to_bound == Bound::INCLUSIVE
|
70
|
+
return false if comparator.call(key, to) > 0 # greater than 'to'
|
71
|
+
elsif to_bound == Bound::EXCLUSIVE
|
72
|
+
return false if comparator.call(key, to) >= 0 # greater than or equal to 'to'
|
73
|
+
end
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the entry if it is in bounds, or null if it is out of bounds.
|
78
|
+
def bound(node, from_bound, to_bound)
|
79
|
+
node if node && in_closed_bounds?(node.key, from_bound, to_bound)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Navigable methods
|
83
|
+
|
84
|
+
def first_entry
|
85
|
+
endpoint(true)
|
86
|
+
end
|
87
|
+
|
88
|
+
def poll_first_entry
|
89
|
+
result = endpoint(true)
|
90
|
+
@treemap.remove_internal(result) if result
|
91
|
+
result
|
92
|
+
end
|
93
|
+
|
94
|
+
def first_key
|
95
|
+
entry = endpoint(true)
|
96
|
+
raise "No such element" unless entry
|
97
|
+
entry.key
|
98
|
+
end
|
99
|
+
|
100
|
+
def last_entry
|
101
|
+
endpoint(false)
|
102
|
+
end
|
103
|
+
|
104
|
+
def poll_last_entry
|
105
|
+
result = endpoint(false)
|
106
|
+
@treemap.remove_internal(result) if result
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
def last_key
|
111
|
+
entry = endpoint(false)
|
112
|
+
raise "No such element" unless entry
|
113
|
+
entry.key
|
114
|
+
end
|
115
|
+
|
116
|
+
# <first> - true for the first element, false for the last
|
117
|
+
def endpoint(first)
|
118
|
+
node, from, to = if @ascending == first
|
119
|
+
node = case @from_bound
|
120
|
+
when Bound::NO_BOUND
|
121
|
+
@treemap.root.first if @treemap.root
|
122
|
+
when Bound::INCLUSIVE
|
123
|
+
@treemap.find(@from, Relation::CEILING)
|
124
|
+
when Bound::EXCLUSIVE
|
125
|
+
@treemap.find(@from, Relation::HIGHER)
|
126
|
+
else
|
127
|
+
raise "Undefined bound."
|
128
|
+
end
|
129
|
+
[node, Bound::NO_BOUND, @to_bound]
|
130
|
+
else
|
131
|
+
node = case @to_bound
|
132
|
+
when Bound::NO_BOUND
|
133
|
+
@treemap.root.last if @treemap.root
|
134
|
+
when Bound::INCLUSIVE
|
135
|
+
@treemap.find(@to, Relation::FLOOR)
|
136
|
+
when Bound::EXCLUSIVE
|
137
|
+
@treemap.find(@to, Relation::LOWER)
|
138
|
+
else
|
139
|
+
raise "Undefined bound."
|
140
|
+
end
|
141
|
+
[node, @from_bound, Bound::NO_BOUND]
|
142
|
+
end
|
143
|
+
bound(node, from, to)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Performs a find on the underlying tree after constraining it to the
|
147
|
+
# bounds of this view. Examples:
|
148
|
+
#
|
149
|
+
# bound is (A..C)
|
150
|
+
# find_bounded(B, FLOOR) stays source.find(B, FLOOR)
|
151
|
+
#
|
152
|
+
# bound is (A..C)
|
153
|
+
# find_bounded(C, FLOOR) becomes source.find(C, LOWER)
|
154
|
+
#
|
155
|
+
# bound is (A..C)
|
156
|
+
# find_bounded(D, LOWER) becomes source.find(C, LOWER)
|
157
|
+
#
|
158
|
+
# bound is (A..C]
|
159
|
+
# find_bounded(D, FLOOR) becomes source.find(C, FLOOR)
|
160
|
+
#
|
161
|
+
# bound is (A..C]
|
162
|
+
# find_bounded(D, LOWER) becomes source.find(C, FLOOR)
|
163
|
+
def find_bounded(key, relation)
|
164
|
+
relation = Relation.for_order(relation, @ascending)
|
165
|
+
from_bound_for_check = @from_bound
|
166
|
+
to_bound_for_check = @to_bound
|
167
|
+
if @to_bound != Bound::NO_BOUND && (relation == Relation::LOWER || relation == Relation::FLOOR)
|
168
|
+
comparison = comparator.call(@to, key)
|
169
|
+
if comparison <= 0
|
170
|
+
key = @to
|
171
|
+
if @to_bound == Bound::EXCLUSIVE
|
172
|
+
relation = Relation::LOWER # 'to' is too high
|
173
|
+
else comparison < 0
|
174
|
+
relation = Relation::FLOOR # we already went lower
|
175
|
+
end
|
176
|
+
end
|
177
|
+
to_bound_for_check = Bound::NO_BOUND # we've already checked the upper bound
|
178
|
+
end
|
179
|
+
if @from_bound != Bound::NO_BOUND && (relation == Relation::CEILING || relation == Relation::HIGHER)
|
180
|
+
comparison = comparator.call(@from, key)
|
181
|
+
if comparison >= 0
|
182
|
+
key = @from
|
183
|
+
if @from_bound == Bound::EXCLUSIVE
|
184
|
+
relation = Relation::HIGHER # 'from' is too low
|
185
|
+
else comparison > 0
|
186
|
+
relation = Relation::CEILING # we already went higher
|
187
|
+
end
|
188
|
+
end
|
189
|
+
from_bound_for_check = Bound::NO_BOUND # we've already checked the lower bound
|
190
|
+
end
|
191
|
+
bound(@treemap.find(key, relation), from_bound_for_check, to_bound_for_check)
|
192
|
+
end
|
193
|
+
|
194
|
+
def lower_entry(key)
|
195
|
+
find_bounded(key, Relation::LOWER)
|
196
|
+
end
|
197
|
+
|
198
|
+
def lower_key(key)
|
199
|
+
entry = find_bounded(key, Relation::LOWER)
|
200
|
+
entry.key if entry
|
201
|
+
end
|
202
|
+
|
203
|
+
def floor_entry(key)
|
204
|
+
find_bounded(key, Relation::FLOOR)
|
205
|
+
end
|
206
|
+
|
207
|
+
def floor_key(key)
|
208
|
+
entry = find_bounded(key, Relation::FLOOR)
|
209
|
+
entry.key if entry
|
210
|
+
end
|
211
|
+
|
212
|
+
def ceiling_entry(key)
|
213
|
+
find_bounded(key, Relation::CEILING)
|
214
|
+
end
|
215
|
+
|
216
|
+
def ceiling_key(key)
|
217
|
+
entry = find_bounded(key, Relation::CEILING)
|
218
|
+
entry.key if entry
|
219
|
+
end
|
220
|
+
|
221
|
+
def higher_entry(key)
|
222
|
+
find_bounded(key, Relation::HIGHER)
|
223
|
+
end
|
224
|
+
|
225
|
+
def higher_key(key)
|
226
|
+
entry = find_bounded(key, Relation::HIGHER)
|
227
|
+
entry.key if entry
|
228
|
+
end
|
229
|
+
|
230
|
+
def comparator
|
231
|
+
if @ascending
|
232
|
+
@treemap.comparator
|
233
|
+
else
|
234
|
+
->(this, that) { -@treemap.comparator.call(this, that) }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# View factory methods
|
239
|
+
|
240
|
+
def entry_set
|
241
|
+
Set.new(each_node.to_a)
|
242
|
+
end
|
243
|
+
|
244
|
+
def key_set
|
245
|
+
Set.new(each_node.map(&:key))
|
246
|
+
end
|
247
|
+
|
248
|
+
alias keys key_set
|
249
|
+
|
250
|
+
def values
|
251
|
+
each_node.map(&:value)
|
252
|
+
end
|
253
|
+
|
254
|
+
def descending_map
|
255
|
+
BoundedMap.new(@treemap, !@ascending, @from, @from_bound, @to, @to_bound)
|
256
|
+
end
|
257
|
+
|
258
|
+
# This can be called in 1 of 2 ways:
|
259
|
+
# sub_map(from_inclusive, to_exclusive)
|
260
|
+
# OR
|
261
|
+
# sub_map(from, from_inclusive, to, to_inclusive)
|
262
|
+
def sub_map(*args)
|
263
|
+
case args.count
|
264
|
+
when 2
|
265
|
+
from_inclusive, to_exclusive = *args
|
266
|
+
bounded_sub_map(from_inclusive, Bound::INCLUSIVE, to_exclusive, Bound::EXCLUSIVE)
|
267
|
+
when 4
|
268
|
+
from, from_inclusive, to, to_inclusive = *args
|
269
|
+
from_bound = from_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
270
|
+
to_bound = to_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
271
|
+
bounded_sub_map(from, from_bound, to, to_bound)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def bounded_sub_map(from, from_bound, to, to_bound)
|
276
|
+
if !@ascending
|
277
|
+
from, to = to, from
|
278
|
+
from_bound, to_bound = to_bound, from_bound
|
279
|
+
end
|
280
|
+
|
281
|
+
# If both the current and requested bounds are exclusive, the isInBounds check must be
|
282
|
+
# inclusive. For example, to create (C..F) from (A..F), the bound 'F' is in bounds.
|
283
|
+
if from_bound == Bound::NO_BOUND
|
284
|
+
from = @from
|
285
|
+
from_bound = @from_bound
|
286
|
+
else
|
287
|
+
from_bound_to_check = from_bound == @from_bound ? Bound::INCLUSIVE : @from_bound
|
288
|
+
raise out_of_bounds(to, from_bound_to_check, @to_bound) if !in_closed_bounds?(from, from_bound_to_check, @to_bound)
|
289
|
+
end
|
290
|
+
if to_bound == Bound::NO_BOUND
|
291
|
+
to = @to
|
292
|
+
to_bound = @to_bound
|
293
|
+
else
|
294
|
+
to_bound_to_check = to_bound == @to_bound ? Bound::INCLUSIVE : @to_bound
|
295
|
+
raise out_of_bounds(to, @from_bound, to_bound_to_check) if !in_closed_bounds?(to, @from_bound, to_bound_to_check)
|
296
|
+
end
|
297
|
+
BoundedMap.new(@treemap, ascending, from, from_bound, to, to_bound)
|
298
|
+
end
|
299
|
+
|
300
|
+
# This can be called in 1 of 2 ways:
|
301
|
+
# head_map(to_exclusive)
|
302
|
+
# OR
|
303
|
+
# head_map(to, inclusive)
|
304
|
+
def head_map(*args)
|
305
|
+
case args.count
|
306
|
+
when 1
|
307
|
+
to_exclusive = args.first
|
308
|
+
bounded_sub_map(nil, Bound::NO_BOUND, to_exclusive, Bound::EXCLUSIVE)
|
309
|
+
when 2
|
310
|
+
to, inclusive = *args
|
311
|
+
to_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
312
|
+
bounded_sub_map(nil, Bound::NO_BOUND, to, to_bound)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# This can be called in 1 of 2 ways:
|
317
|
+
# tail_map(from_inclusive)
|
318
|
+
# OR
|
319
|
+
# tail_map(from, inclusive)
|
320
|
+
def tail_map(*args)
|
321
|
+
case args.count
|
322
|
+
when 1
|
323
|
+
from_inclusive = args.first
|
324
|
+
bounded_sub_map(fromInclusive, Bound::INCLUSIVE, nil, Bound::NO_BOUND)
|
325
|
+
when 2
|
326
|
+
from, inclusive = *args
|
327
|
+
from_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
328
|
+
bounded_sub_map(from, from_bound, nil, Bound::NO_BOUND)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def out_of_bounds(value, from_bound, to_bound)
|
333
|
+
Exception.new("#{value} not in range #{from_bound.left_cap(@from)}..#{to_bound.right_cap(@to)}")
|
334
|
+
end
|
335
|
+
|
336
|
+
# Bounded view implementations
|
337
|
+
|
338
|
+
# in-order traversal of nodes in tree
|
339
|
+
class BoundedNodeIterator < ::TreeMap::NodeIterator
|
340
|
+
def initialize(bounded_map, next_node)
|
341
|
+
super(next_node)
|
342
|
+
@bounded_map = bounded_map
|
343
|
+
end
|
344
|
+
|
345
|
+
def step_forward
|
346
|
+
result = super
|
347
|
+
@next_node = nil if @next_node && !@bounded_map.in_closed_bounds?(@next_node.key, Bound::NO_BOUND, @bounded_map.to_bound)
|
348
|
+
result
|
349
|
+
end
|
350
|
+
|
351
|
+
def step_backward
|
352
|
+
result = super
|
353
|
+
@next_node = nil if @next_node && !@bounded_map.in_closed_bounds?(@next_node.key, @bounded_map.from_bound, Bound::NO_BOUND)
|
354
|
+
result
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# each {|k,v| puts "#{k}->#{v}"}
|
359
|
+
def each(&blk)
|
360
|
+
if block_given?
|
361
|
+
each_node {|node| blk.call(node.key, node.value) }
|
362
|
+
else
|
363
|
+
enum_for(:each)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# each_node {|node| puts "#{node.key}->#{node.value}"}
|
368
|
+
def each_node
|
369
|
+
if block_given?
|
370
|
+
iter = BoundedNodeIterator.new(self, endpoint(true))
|
371
|
+
while iter.has_next?
|
372
|
+
yield iter.step_forward()
|
373
|
+
end
|
374
|
+
else
|
375
|
+
enum_for(:each_node)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|
380
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pp'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
# TreeMap is a Ruby port of https://android.googlesource.com/platform/libcore.git/+/android-6.0.1_r32/luni/src/main/java/java/util/TreeMap.java
|
4
5
|
# This is an AVL tree based implementation of Java's java.util.TreeMap structure.
|
@@ -143,7 +144,7 @@ class TreeMap
|
|
143
144
|
|
144
145
|
NaturalOrder = ->(this, that) { this <=> that }
|
145
146
|
|
146
|
-
attr_accessor :size
|
147
|
+
attr_accessor :comparator, :root, :size
|
147
148
|
|
148
149
|
# comparator is a function of the form: (this, that) -> int ; where int is -1 if this < that, 0 if this == that, and 1 if this > that
|
149
150
|
def initialize(comparator = NaturalOrder)
|
@@ -550,7 +551,72 @@ class TreeMap
|
|
550
551
|
|
551
552
|
# View factory methods
|
552
553
|
|
553
|
-
|
554
|
+
def entry_set
|
555
|
+
Set.new(each_node.to_a)
|
556
|
+
end
|
557
|
+
|
558
|
+
def key_set
|
559
|
+
Set.new(each_node.map(&:key))
|
560
|
+
end
|
561
|
+
|
562
|
+
alias keys key_set
|
563
|
+
|
564
|
+
def values
|
565
|
+
each_node.map(&:value)
|
566
|
+
end
|
567
|
+
|
568
|
+
# This can be called in 1 of 2 ways:
|
569
|
+
# sub_map(from_inclusive, to_exclusive)
|
570
|
+
# OR
|
571
|
+
# sub_map(from, from_inclusive, to, to_inclusive)
|
572
|
+
def sub_map(*args)
|
573
|
+
case args.count
|
574
|
+
when 2
|
575
|
+
from_inclusive, to_exclusive = *args
|
576
|
+
BoundedMap.new(self, true, from_inclusive, Bound::INCLUSIVE, to_exclusive, Bound::EXCLUSIVE)
|
577
|
+
when 4
|
578
|
+
from, from_inclusive, to, to_inclusive = *args
|
579
|
+
from_bound = from_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
580
|
+
to_bound = to_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
581
|
+
BoundedMap.new(self, true, from, from_bound, to, to_bound);
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
# This can be called in 1 of 2 ways:
|
586
|
+
# head_map(to_exclusive)
|
587
|
+
# OR
|
588
|
+
# head_map(to, inclusive)
|
589
|
+
def head_map(*args)
|
590
|
+
case args.count
|
591
|
+
when 1
|
592
|
+
to_exclusive = args.first
|
593
|
+
BoundedMap.new(self, true, nil, Bound::NO_BOUND, to_exclusive, Bound::EXCLUSIVE)
|
594
|
+
when 2
|
595
|
+
to, inclusive = *args
|
596
|
+
to_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
597
|
+
BoundedMap.new(self, true, nil, Bound::NO_BOUND, to, to_bound)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
# This can be called in 1 of 2 ways:
|
602
|
+
# tail_map(from_inclusive)
|
603
|
+
# OR
|
604
|
+
# tail_map(from, inclusive)
|
605
|
+
def tail_map(*args)
|
606
|
+
case args.count
|
607
|
+
when 1
|
608
|
+
from_inclusive = args.first
|
609
|
+
BoundedMap.new(self, true, from_inclusive, Bound::INCLUSIVE, nil, Bound::NO_BOUND)
|
610
|
+
when 2
|
611
|
+
from, inclusive = *args
|
612
|
+
from_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
613
|
+
BoundedMap.new(self, true, from, from_bound, nil, Bound::NO_BOUND)
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
def descending_map
|
618
|
+
BoundedMap.new(self, false, nil, Bound::NO_BOUND, nil, Bound::NO_BOUND)
|
619
|
+
end
|
554
620
|
|
555
621
|
# Tree traversal methods
|
556
622
|
|
@@ -582,13 +648,26 @@ class TreeMap
|
|
582
648
|
end
|
583
649
|
end
|
584
650
|
|
585
|
-
|
651
|
+
# each {|k,v| puts "#{k}->#{v}"}
|
652
|
+
def each(&blk)
|
653
|
+
if block_given?
|
654
|
+
each_node {|node| blk.call(node.key, node.value) }
|
655
|
+
else
|
656
|
+
enum_for(:each)
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
# each_node {|node| puts "#{node.key}->#{node.value}"}
|
661
|
+
def each_node
|
586
662
|
if block_given?
|
587
663
|
iter = NodeIterator.new(@root.first)
|
588
664
|
while iter.has_next?
|
589
|
-
|
590
|
-
yield node.key, node.value
|
665
|
+
yield iter.step_forward()
|
591
666
|
end
|
667
|
+
else
|
668
|
+
enum_for(:each_node)
|
592
669
|
end
|
593
670
|
end
|
594
671
|
end
|
672
|
+
|
673
|
+
require_relative 'bounded_map'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: treemap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Ellis
|
@@ -20,7 +20,8 @@ executables: []
|
|
20
20
|
extensions: []
|
21
21
|
extra_rdoc_files: []
|
22
22
|
files:
|
23
|
-
- lib/
|
23
|
+
- lib/bounded_map.rb
|
24
|
+
- lib/tree_map.rb
|
24
25
|
homepage: https://github.com/davidkellis/treemap
|
25
26
|
licenses:
|
26
27
|
- MIT
|