victory 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +11 -1
  4. data/README.md +4 -6
  5. data/Rakefile +11 -4
  6. data/USAGE.md +159 -0
  7. data/ext/algorithms/string/LICENSE.md +21 -0
  8. data/ext/algorithms/string/extconf.rb +4 -0
  9. data/ext/algorithms/string/string.c +68 -0
  10. data/ext/containers/bst/LICENSE.md +21 -0
  11. data/ext/containers/bst/bst.c +247 -0
  12. data/ext/containers/bst/extconf.rb +4 -0
  13. data/ext/containers/deque/LICENSE.md +21 -0
  14. data/ext/containers/deque/deque.c +247 -0
  15. data/ext/containers/deque/extconf.rb +4 -0
  16. data/ext/containers/rbtree_map/LICENSE.md +21 -0
  17. data/ext/containers/rbtree_map/extconf.rb +4 -0
  18. data/ext/containers/rbtree_map/rbtree.c +498 -0
  19. data/ext/containers/splaytree_map/LICENSE.md +21 -0
  20. data/ext/containers/splaytree_map/extconf.rb +4 -0
  21. data/ext/containers/splaytree_map/splaytree.c +419 -0
  22. data/ext/containers/xor_list/extconf.rb +4 -0
  23. data/ext/containers/xor_list/xor_list.c +122 -0
  24. data/lib/algorithms/search.rb +104 -0
  25. data/lib/algorithms/sort.rb +389 -0
  26. data/lib/algorithms/string.rb +29 -0
  27. data/lib/containers/deque.rb +193 -0
  28. data/lib/containers/heap.rb +524 -0
  29. data/lib/containers/kd_tree.rb +131 -0
  30. data/lib/containers/list.rb +81 -0
  31. data/lib/containers/prefix_tree.rb +61 -0
  32. data/lib/containers/priority_queue.rb +135 -0
  33. data/lib/containers/queue.rb +89 -0
  34. data/lib/containers/rb_tree_map.rb +420 -0
  35. data/lib/containers/splay_tree_map.rb +290 -0
  36. data/lib/containers/stack.rb +88 -0
  37. data/lib/containers/suffix_array.rb +92 -0
  38. data/lib/containers/trie.rb +204 -0
  39. data/lib/containers/tuple.rb +20 -0
  40. data/lib/victory/version.rb +1 -1
  41. data/lib/victory.rb +8 -1
  42. data/victory.gemspec +12 -3
  43. metadata +73 -12
  44. data/.idea/encodings.xml +0 -4
  45. data/.idea/misc.xml +0 -7
  46. data/.idea/modules.xml +0 -8
  47. data/.idea/victory.iml +0 -13
  48. data/.idea/workspace.xml +0 -233
  49. data/ext/victory/extconf.rb +0 -3
  50. data/ext/victory/victory.c +0 -9
  51. data/ext/victory/victory.h +0 -6
@@ -0,0 +1,524 @@
1
+ # rdoc
2
+ # A Heap is a container that satisfies the heap property that nodes are always smaller in
3
+ # value than their parent node.
4
+ #
5
+ # The Containers::Heap class is flexible and upon initialization, takes an optional block
6
+ # that determines how the items are ordered. Two versions that are included are the
7
+ # Containers::MaxHeap and Containers::MinHeap that return the largest and smallest items on
8
+ # each invocation, respectively.
9
+ #
10
+ # This library implements a Fibonacci heap, which allows O(1) complexity for most methods.
11
+ #
12
+ #
13
+ # MIT License
14
+ #
15
+ # Copyright (c) 2009 Kanwei Li
16
+ #
17
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ # of this software and associated documentation files (the "Software"), to deal
19
+ # in the Software without restriction, including without limitation the rights
20
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ # copies of the Software, and to permit persons to whom the Software is
22
+ # furnished to do so, subject to the following conditions:
23
+ #
24
+ # The above copyright notice and this permission notice shall be included in all
25
+ # copies or substantial portions of the Software.
26
+ #
27
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ # SOFTWARE.
34
+ class Containers::Heap
35
+ include Enumerable
36
+
37
+ # call-seq:
38
+ # size -> int
39
+ #
40
+ # Return the number of elements in the heap.
41
+ def size
42
+ @size
43
+ end
44
+ alias_method :length, :size
45
+
46
+ # call-seq:
47
+ # Heap.new(optional_array) { |x, y| optional_comparison_fn } -> new_heap
48
+ #
49
+ # If an optional array is passed, the entries in the array are inserted into the heap with
50
+ # equal key and value fields. Also, an optional block can be passed to define the function
51
+ # that maintains heap property. For example, a min-heap can be created with:
52
+ #
53
+ # minheap = Heap.new { |x, y| (x <=> y) == -1 }
54
+ # minheap.push(6)
55
+ # minheap.push(10)
56
+ # minheap.pop #=> 6
57
+ #
58
+ # Thus, smaller elements will be parent nodes. The heap defaults to a min-heap if no block
59
+ # is given.
60
+ def initialize(ary=[], &block)
61
+ @compare_fn = block || lambda { |x, y| (x <=> y) == -1 }
62
+ @next = nil
63
+ @size = 0
64
+ @stored = {}
65
+
66
+ ary.each { |n| push(n) } unless ary.empty?
67
+ end
68
+
69
+ # call-seq:
70
+ # push(key, value) -> value
71
+ # push(value) -> value
72
+ #
73
+ # Inserts an item with a given key into the heap. If only one parameter is given,
74
+ # the key is set to the value.
75
+ #
76
+ # Complexity: O(1)
77
+ #
78
+ # heap = MinHeap.new
79
+ # heap.push(1, "Cat")
80
+ # heap.push(2)
81
+ # heap.pop #=> "Cat"
82
+ # heap.pop #=> 2
83
+ def push(key, value=key)
84
+ raise ArgumentError, "Heap keys must not be nil." unless key
85
+ node = Node.new(key, value)
86
+ # Add new node to the left of the @next node
87
+ if @next
88
+ node.right = @next
89
+ node.left = @next.left
90
+ node.left.right = node
91
+ @next.left = node
92
+ if @compare_fn[key, @next.key]
93
+ @next = node
94
+ end
95
+ else
96
+ @next = node
97
+ end
98
+ @size += 1
99
+
100
+ arr = []
101
+ w = @next.right
102
+ until w == @next do
103
+ arr << w.value
104
+ w = w.right
105
+ end
106
+ arr << @next.value
107
+ @stored[key] ||= []
108
+ @stored[key] << node
109
+ value
110
+ end
111
+ alias_method :<<, :push
112
+
113
+ # call-seq:
114
+ # has_key?(key) -> true or false
115
+ #
116
+ # Returns true if heap contains the key.
117
+ #
118
+ # Complexity: O(1)
119
+ #
120
+ # minheap = MinHeap.new([1, 2])
121
+ # minheap.has_key?(2) #=> true
122
+ # minheap.has_key?(4) #=> false
123
+ def has_key?(key)
124
+ @stored[key] && !@stored[key].empty? ? true : false
125
+ end
126
+
127
+ # call-seq:
128
+ # next -> value
129
+ # next -> nil
130
+ #
131
+ # Returns the value of the next item in heap order, but does not remove it.
132
+ #
133
+ # Complexity: O(1)
134
+ #
135
+ # minheap = MinHeap.new([1, 2])
136
+ # minheap.next #=> 1
137
+ # minheap.size #=> 2
138
+ def next
139
+ @next && @next.value
140
+ end
141
+
142
+ # call-seq:
143
+ # next_key -> key
144
+ # next_key -> nil
145
+ #
146
+ # Returns the key associated with the next item in heap order, but does not remove the value.
147
+ #
148
+ # Complexity: O(1)
149
+ #
150
+ # minheap = MinHeap.new
151
+ # minheap.push(1, :a)
152
+ # minheap.next_key #=> 1
153
+ #
154
+ def next_key
155
+ @next && @next.key
156
+ end
157
+
158
+ # call-seq:
159
+ # clear -> nil
160
+ #
161
+ # Removes all elements from the heap, destructively.
162
+ #
163
+ # Complexity: O(1)
164
+ #
165
+ def clear
166
+ @next = nil
167
+ @size = 0
168
+ @stored = {}
169
+ nil
170
+ end
171
+
172
+ # call-seq:
173
+ # empty? -> true or false
174
+ #
175
+ # Returns true if the heap is empty, false otherwise.
176
+ def empty?
177
+ @next.nil?
178
+ end
179
+
180
+ # call-seq:
181
+ # merge!(otherheap) -> merged_heap
182
+ #
183
+ # Does a shallow merge of all the nodes in the other heap.
184
+ #
185
+ # Complexity: O(1)
186
+ #
187
+ # heap = MinHeap.new([5, 6, 7, 8])
188
+ # otherheap = MinHeap.new([1, 2, 3, 4])
189
+ # heap.merge!(otherheap)
190
+ # heap.size #=> 8
191
+ # heap.pop #=> 1
192
+ def merge!(otherheap)
193
+ raise ArgumentError, "Trying to merge a heap with something not a heap" unless otherheap.kind_of? Containers::Heap
194
+ other_root = otherheap.instance_variable_get("@next")
195
+ if other_root
196
+ @stored = @stored.merge(otherheap.instance_variable_get("@stored")) { |key, a, b| (a << b).flatten }
197
+ # Insert othernode's @next node to the left of current @next
198
+ @next.left.right = other_root
199
+ ol = other_root.left
200
+ other_root.left = @next.left
201
+ ol.right = @next
202
+ @next.left = ol
203
+
204
+ @next = other_root if @compare_fn[other_root.key, @next.key]
205
+ end
206
+ @size += otherheap.size
207
+ end
208
+
209
+ # call-seq:
210
+ # pop -> value
211
+ # pop -> nil
212
+ #
213
+ # Returns the value of the next item in heap order and removes it from the heap.
214
+ #
215
+ # Complexity: O(1)
216
+ #
217
+ # minheap = MinHeap.new([1, 2])
218
+ # minheap.pop #=> 1
219
+ # minheap.size #=> 1
220
+ def pop
221
+ return nil unless @next
222
+ popped = @next
223
+ if @size == 1
224
+ clear
225
+ return popped.value
226
+ end
227
+ # Merge the popped's children into root node
228
+ if @next.child
229
+ @next.child.parent = nil
230
+
231
+ # get rid of parent
232
+ sibling = @next.child.right
233
+ until sibling == @next.child
234
+ sibling.parent = nil
235
+ sibling = sibling.right
236
+ end
237
+
238
+ # Merge the children into the root. If @next is the only root node, make its child the @next node
239
+ if @next.right == @next
240
+ @next = @next.child
241
+ else
242
+ next_left, next_right = @next.left, @next.right
243
+ current_child = @next.child
244
+ @next.right.left = current_child
245
+ @next.left.right = current_child.right
246
+ current_child.right.left = next_left
247
+ current_child.right = next_right
248
+ @next = @next.right
249
+ end
250
+ else
251
+ @next.left.right = @next.right
252
+ @next.right.left = @next.left
253
+ @next = @next.right
254
+ end
255
+ consolidate
256
+
257
+ unless @stored[popped.key].delete(popped)
258
+ raise "Couldn't delete node from stored nodes hash"
259
+ end
260
+ @size -= 1
261
+
262
+ popped.value
263
+ end
264
+ alias_method :next!, :pop
265
+
266
+ # call-seq:
267
+ # change_key(key, new_key) -> [new_key, value]
268
+ # change_key(key, new_key) -> nil
269
+ #
270
+ # Changes the key from one to another. Doing so must not violate the heap property or
271
+ # an exception will be raised. If the key is found, an array containing the new key and
272
+ # value pair is returned, otherwise nil is returned.
273
+ #
274
+ # In the case of duplicate keys, an arbitrary key is changed. This will be investigated
275
+ # more in the future.
276
+ #
277
+ # Complexity: amortized O(1)
278
+ #
279
+ # minheap = MinHeap.new([1, 2])
280
+ # minheap.change_key(2, 3) #=> raise error since we can't increase the value in a min-heap
281
+ # minheap.change_key(2, 0) #=> [0, 2]
282
+ # minheap.pop #=> 2
283
+ # minheap.pop #=> 1
284
+ def change_key(key, new_key, delete=false)
285
+ return if @stored[key].nil? || @stored[key].empty? || (key == new_key)
286
+
287
+ # Must maintain heap property
288
+ raise "Changing this key would not maintain heap property!" unless (delete || @compare_fn[new_key, key])
289
+ node = @stored[key].shift
290
+ if node
291
+ node.key = new_key
292
+ @stored[new_key] ||= []
293
+ @stored[new_key] << node
294
+ parent = node.parent
295
+ if parent
296
+ # if heap property is violated
297
+ if delete || @compare_fn[new_key, parent.key]
298
+ cut(node, parent)
299
+ cascading_cut(parent)
300
+ end
301
+ end
302
+ if delete || @compare_fn[node.key, @next.key]
303
+ @next = node
304
+ end
305
+ return [node.key, node.value]
306
+ end
307
+ nil
308
+ end
309
+
310
+ # call-seq:
311
+ # delete(key) -> value
312
+ # delete(key) -> nil
313
+ #
314
+ # Deletes the item with associated key and returns it. nil is returned if the key
315
+ # is not found. In the case of nodes with duplicate keys, an arbitrary one is deleted.
316
+ #
317
+ # Complexity: amortized O(log n)
318
+ #
319
+ # minheap = MinHeap.new([1, 2])
320
+ # minheap.delete(1) #=> 1
321
+ # minheap.size #=> 1
322
+ def delete(key)
323
+ pop if change_key(key, nil, true)
324
+ end
325
+
326
+ # Node class used internally
327
+ class Node # :nodoc:
328
+ attr_accessor :parent, :child, :left, :right, :key, :value, :degree, :marked
329
+
330
+ def initialize(key, value)
331
+ @key = key
332
+ @value = value
333
+ @degree = 0
334
+ @marked = false
335
+ @right = self
336
+ @left = self
337
+ end
338
+
339
+ def marked?
340
+ @marked == true
341
+ end
342
+
343
+ end
344
+
345
+ # make node a child of a parent node
346
+ def link_nodes(child, parent)
347
+ # link the child's siblings
348
+ child.left.right = child.right
349
+ child.right.left = child.left
350
+
351
+ child.parent = parent
352
+
353
+ # if parent doesn't have children, make new child its only child
354
+ if parent.child.nil?
355
+ parent.child = child.right = child.left = child
356
+ else # otherwise insert new child into parent's children list
357
+ current_child = parent.child
358
+ child.left = current_child
359
+ child.right = current_child.right
360
+ current_child.right.left = child
361
+ current_child.right = child
362
+ end
363
+ parent.degree += 1
364
+ child.marked = false
365
+ end
366
+ private :link_nodes
367
+
368
+ # Makes sure the structure does not contain nodes in the root list with equal degrees
369
+ def consolidate
370
+ roots = []
371
+ root = @next
372
+ min = root
373
+ # find the nodes in the list
374
+ loop do
375
+ roots << root
376
+ root = root.right
377
+ break if root == @next
378
+ end
379
+ degrees = []
380
+ roots.each do |root|
381
+ min = root if @compare_fn[root.key, min.key]
382
+ # check if we need to merge
383
+ if degrees[root.degree].nil? # no other node with the same degree
384
+ degrees[root.degree] = root
385
+ next
386
+ else # there is another node with the same degree, consolidate them
387
+ degree = root.degree
388
+ until degrees[degree].nil? do
389
+ other_root_with_degree = degrees[degree]
390
+ if @compare_fn[root.key, other_root_with_degree.key] # determine which node is the parent, which one is the child
391
+ smaller, larger = root, other_root_with_degree
392
+ else
393
+ smaller, larger = other_root_with_degree, root
394
+ end
395
+ link_nodes(larger, smaller)
396
+ degrees[degree] = nil
397
+ root = smaller
398
+ degree += 1
399
+ end
400
+ degrees[degree] = root
401
+ min = root if min.key == root.key # this fixes a bug with duplicate keys not being in the right order
402
+ end
403
+ end
404
+ @next = min
405
+ end
406
+ private :consolidate
407
+
408
+ def cascading_cut(node)
409
+ p = node.parent
410
+ if p
411
+ if node.marked?
412
+ cut(node, p)
413
+ cascading_cut(p)
414
+ else
415
+ node.marked = true
416
+ end
417
+ end
418
+ end
419
+ private :cascading_cut
420
+
421
+ # remove x from y's children and add x to the root list
422
+ def cut(x, y)
423
+ x.left.right = x.right
424
+ x.right.left = x.left
425
+ y.degree -= 1
426
+ if (y.degree == 0)
427
+ y.child = nil
428
+ elsif (y.child == x)
429
+ y.child = x.right
430
+ end
431
+ x.right = @next
432
+ x.left = @next.left
433
+ @next.left = x
434
+ x.left.right = x
435
+ x.parent = nil
436
+ x.marked = false
437
+ end
438
+ private :cut
439
+
440
+ end
441
+
442
+ # A MaxHeap is a heap where the items are returned in descending order of key value.
443
+ class Containers::MaxHeap < Containers::Heap
444
+
445
+ # call-seq:
446
+ # MaxHeap.new(ary) -> new_heap
447
+ #
448
+ # Creates a new MaxHeap with an optional array parameter of items to insert into the heap.
449
+ # A MaxHeap is created by calling Heap.new { |x, y| (x <=> y) == 1 }, so this is a convenience class.
450
+ #
451
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
452
+ # maxheap.pop #=> 4
453
+ # maxheap.pop #=> 3
454
+ def initialize(ary=[])
455
+ super(ary) { |x, y| (x <=> y) == 1 }
456
+ end
457
+
458
+ # call-seq:
459
+ # max -> value
460
+ # max -> nil
461
+ #
462
+ # Returns the item with the largest key, but does not remove it from the heap.
463
+ #
464
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
465
+ # maxheap.max #=> 4
466
+ def max
467
+ self.next
468
+ end
469
+
470
+ # call-seq:
471
+ # max! -> value
472
+ # max! -> nil
473
+ #
474
+ # Returns the item with the largest key and removes it from the heap.
475
+ #
476
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
477
+ # maxheap.max! #=> 4
478
+ # maxheap.size #=> 3
479
+ def max!
480
+ self.pop
481
+ end
482
+ end
483
+
484
+ # A MinHeap is a heap where the items are returned in ascending order of key value.
485
+ class Containers::MinHeap < Containers::Heap
486
+
487
+ # call-seq:
488
+ # MinHeap.new(ary) -> new_heap
489
+ #
490
+ # Creates a new MinHeap with an optional array parameter of items to insert into the heap.
491
+ # A MinHeap is created by calling Heap.new { |x, y| (x <=> y) == -1 }, so this is a convenience class.
492
+ #
493
+ # minheap = MinHeap.new([1, 2, 3, 4])
494
+ # minheap.pop #=> 1
495
+ # minheap.pop #=> 2
496
+ def initialize(ary=[])
497
+ super(ary) { |x, y| (x <=> y) == -1 }
498
+ end
499
+
500
+ # call-seq:
501
+ # min -> value
502
+ # min -> nil
503
+ #
504
+ # Returns the item with the smallest key, but does not remove it from the heap.
505
+ #
506
+ # minheap = MinHeap.new([1, 2, 3, 4])
507
+ # minheap.min #=> 1
508
+ def min
509
+ self.next
510
+ end
511
+
512
+ # call-seq:
513
+ # min! -> value
514
+ # min! -> nil
515
+ #
516
+ # Returns the item with the smallest key and removes it from the heap.
517
+ #
518
+ # minheap = MinHeap.new([1, 2, 3, 4])
519
+ # minheap.min! #=> 1
520
+ # minheap.size #=> 3
521
+ def min!
522
+ self.pop
523
+ end
524
+ end
@@ -0,0 +1,131 @@
1
+ # rdoc
2
+ #
3
+ # A kd-tree is a binary tree that allows one to store points (of any space dimension: 2D, 3D, etc).
4
+ # The structure of the resulting tree makes it so that large portions of the tree are pruned
5
+ # during queries.
6
+ #
7
+ # One very good use of the tree is to allow nearest neighbor searching. Let's say you have a number
8
+ # of points in 2D space, and you want to find the nearest 2 points from a specific point:
9
+ #
10
+ # First, put the points into the tree:
11
+ #
12
+ # kdtree = Containers::KDTree.new( {0 => [4, 3], 1 => [3, 4], 2 => [-1, 2], 3 => [6, 4],
13
+ # 4 => [3, -5], 5 => [-2, -5] })
14
+ #
15
+ # Then, query on the tree:
16
+ #
17
+ # puts kd.find_nearest([0, 0], 2) => [[5, 2], [9, 1]]
18
+ #
19
+ # The result is an array of [distance, id] pairs. There seems to be a bug in this version.
20
+ #
21
+ # Note that the point queried on does not have to exist in the tree. However, if it does exist,
22
+ # it will be returned.
23
+ #
24
+ #
25
+ # MIT License
26
+ #
27
+ # Copyright (c) 2009 Kanwei Li
28
+ #
29
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
30
+ # of this software and associated documentation files (the "Software"), to deal
31
+ # in the Software without restriction, including without limitation the rights
32
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33
+ # copies of the Software, and to permit persons to whom the Software is
34
+ # furnished to do so, subject to the following conditions:
35
+ #
36
+ # The above copyright notice and this permission notice shall be included in all
37
+ # copies or substantial portions of the Software.
38
+ #
39
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
45
+ # SOFTWARE.
46
+
47
+ class Containers::KDTree
48
+ Node = Struct.new(:id, :coords, :left, :right)
49
+
50
+ # Points is a hash of id => [coord, coord] pairs.
51
+ def initialize(points)
52
+ raise "must pass in a hash" unless points.kind_of?(Hash)
53
+ @dimensions = points[ points.keys.first ].size
54
+ @root = build_tree(points.to_a)
55
+ @nearest = []
56
+ end
57
+
58
+ # Find k closest points to given coordinates
59
+ def find_nearest(target, k_nearest)
60
+ @nearest = []
61
+ nearest(@root, target, k_nearest, 0)
62
+ end
63
+
64
+ # points is an array
65
+ def build_tree(points, depth=0)
66
+ return if points.empty?
67
+
68
+ axis = depth % @dimensions
69
+
70
+ points.sort! { |a, b| a.last[axis] <=> b.last[axis] }
71
+ median = points.size / 2
72
+
73
+ node = Node.new(points[median].first, points[median].last, nil, nil)
74
+ node.left = build_tree(points[0...median], depth+1)
75
+ node.right = build_tree(points[median+1..-1], depth+1)
76
+ node
77
+ end
78
+ private :build_tree
79
+
80
+ # Euclidian distanced, squared, between a node and target coords
81
+ def distance2(node, target)
82
+ return nil if node.nil? or target.nil?
83
+ c = (node.coords[0] - target[0])
84
+ d = (node.coords[1] - target[1])
85
+ c * c + d * d
86
+ end
87
+ private :distance2
88
+
89
+ # Update array of nearest elements if necessary
90
+ def check_nearest(nearest, node, target, k_nearest)
91
+ d = distance2(node, target)
92
+ if nearest.size < k_nearest || d < nearest.last[0]
93
+ nearest.pop if nearest.size >= k_nearest
94
+ nearest << [d, node.id]
95
+ nearest.sort! { |a, b| a[0] <=> b[0] }
96
+ end
97
+ nearest
98
+ end
99
+ private :check_nearest
100
+
101
+ # Recursively find nearest coordinates, going down the appropriate branch as needed
102
+ def nearest(node, target, k_nearest, depth)
103
+ axis = depth % @dimensions
104
+
105
+ if node.left.nil? && node.right.nil? # Leaf node
106
+ @nearest = check_nearest(@nearest, node, target, k_nearest)
107
+ return
108
+ end
109
+
110
+ # Go down the nearest split
111
+ if node.right.nil? || (node.left && target[axis] <= node.coords[axis])
112
+ nearer = node.left
113
+ further = node.right
114
+ else
115
+ nearer = node.right
116
+ further = node.left
117
+ end
118
+ nearest(nearer, target, k_nearest, depth+1)
119
+
120
+ # See if we have to check other side
121
+ if further
122
+ if @nearest.size < k_nearest || (target[axis] - node.coords[axis])**2 < @nearest.last[0]
123
+ nearest(further, target, k_nearest, depth+1)
124
+ end
125
+ end
126
+
127
+ @nearest = check_nearest(@nearest, node, target, k_nearest)
128
+ end
129
+ private :nearest
130
+
131
+ end