kdtree 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/LICENSE +20 -0
  2. data/ext/extconf.rb +3 -0
  3. data/ext/kdtree.c +488 -0
  4. data/test/test.rb +138 -0
  5. metadata +61 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Adam Doppelt
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile("kdtree")
@@ -0,0 +1,488 @@
1
+ #include "ruby.h"
2
+ #include "rubyio.h"
3
+ #include "version.h"
4
+
5
+ #ifndef HAVE_RB_IO_T
6
+ #define rb_io_t OpenFile
7
+ #endif
8
+
9
+ //
10
+ // interface
11
+ //
12
+
13
+ typedef struct kdtree_data
14
+ {
15
+ int root;
16
+ int len;
17
+ struct kdtree_node *nodes;
18
+ } kdtree_data;
19
+
20
+ typedef struct kdtree_node
21
+ {
22
+ float x, y;
23
+ int id;
24
+ int left;
25
+ int right;
26
+ } kdtree_node;
27
+
28
+ #define KDTREEP \
29
+ struct kdtree_data *kdtreep; \
30
+ Data_Get_Struct(kdtree, struct kdtree_data, kdtreep);
31
+
32
+ static VALUE kdtree_alloc(VALUE klass);
33
+ static void kdtree_free(struct kdtree_data *kdtreep);
34
+ static VALUE kdtree_initialize(VALUE kdtree, VALUE points);
35
+ static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y);
36
+ static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k);
37
+ static VALUE kdtree_persist(VALUE kdtree, VALUE io);
38
+ static VALUE kdtree_to_s(VALUE kdtree);
39
+
40
+ // helpers
41
+ static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth);
42
+ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth);
43
+ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float y, int k, int depth);
44
+
45
+ #define KDTREE_MAGIC "KdTr"
46
+
47
+ //
48
+ // implementation
49
+ //
50
+
51
+ static VALUE kdtree_alloc(VALUE klass)
52
+ {
53
+ struct kdtree_data *kdtreep;
54
+ VALUE obj = Data_Make_Struct(klass, struct kdtree_data, 0, kdtree_free, kdtreep);
55
+ kdtreep->root = -1;
56
+ return obj;
57
+ }
58
+
59
+ static void kdtree_free(struct kdtree_data *kdtreep)
60
+ {
61
+ if (kdtreep) {
62
+ free(kdtreep->nodes);
63
+ }
64
+ }
65
+
66
+ static void read_all(struct rb_io_t *fptr, char *buf, int len)
67
+ {
68
+ while (len > 0) {
69
+ int n = rb_io_fread(buf, len, fptr->f);
70
+ if (n == 0) {
71
+ rb_eof_error();
72
+ }
73
+ buf += n;
74
+ len -= n;
75
+ }
76
+ }
77
+
78
+ /*
79
+ * call-seq:
80
+ * KDTree.new(points) => kdtree
81
+ * KDTree.new(io) => kdtree
82
+ *
83
+ * Returns a new <code>KDTree</code>. To construct a tree, pass an array of
84
+ * <i>points</i>. Each point should be an array of the form <code>[x, y,
85
+ * id]</code>, where <i>x</i> and <i>y</i> are floats and <i>id</i> is an
86
+ * integer. The <i>id</i> is arbitrary and will be returned to you whenever you
87
+ * search with nearest or nearestk.
88
+ *
89
+ * # create a new tree
90
+ * points = []
91
+ * points << [47.6, -122.3, 1] # Seattle
92
+ * points << [40.7, -74.0, 2] # New York
93
+ * kd = KDTree.new(points)
94
+ *
95
+ * Alternately, you can pass in an <i>IO</i> object containing a persisted
96
+ * kdtree. This makes it possible to build the tree in advance, persist it, and
97
+ * start it up quickly later. See persist for more information.
98
+ */
99
+ static VALUE kdtree_initialize(VALUE kdtree, VALUE arg)
100
+ {
101
+ KDTREEP;
102
+
103
+ if (TYPE(arg) == T_ARRAY) {
104
+ // init from array of pints
105
+ VALUE points = arg;
106
+ kdtreep->len = RARRAY_LEN(points);
107
+ kdtreep->nodes = ALLOC_N(struct kdtree_node, kdtreep->len);
108
+
109
+ int i;
110
+ for (i = 0; i < RARRAY_LEN(points); ++i) {
111
+ struct kdtree_node *n = kdtreep->nodes + i;
112
+
113
+ VALUE ptr = RARRAY_PTR(points)[i];
114
+ VALUE v = rb_check_array_type(ptr);
115
+ if (NIL_P(v) || RARRAY_LEN(v) != 3) {
116
+ continue;
117
+ }
118
+ VALUE *a = RARRAY_PTR(ptr);
119
+ n->x = NUM2DBL(a[0]);
120
+ n->y = NUM2DBL(a[1]);
121
+ n->id = NUM2INT(a[2]);
122
+ }
123
+
124
+ // now build the tree
125
+ kdtreep->root = kdtree_build(kdtreep, 0, kdtreep->len, 0);
126
+ } else if (rb_respond_to(arg, rb_intern("read"))) {
127
+ VALUE io = arg;
128
+ if (rb_respond_to(io, rb_intern("binmode"))) {
129
+ rb_funcall2(io, rb_intern("binmode"), 0, 0);
130
+ }
131
+
132
+ struct rb_io_t *fptr = RFILE(rb_io_taint_check(io))->fptr;
133
+ rb_io_check_readable(fptr);
134
+
135
+ // check magic
136
+ char buf[4];
137
+ read_all(fptr, buf, 4);
138
+ if (memcmp(KDTREE_MAGIC, buf, 4) != 0) {
139
+ rb_raise(rb_eRuntimeError, "wrong magic number in kdtree file");
140
+ }
141
+
142
+ // read start of the struct
143
+ read_all(fptr, (char *)kdtreep, sizeof(struct kdtree_data) - sizeof(struct kdtree_node *));
144
+ // read the nodes
145
+ kdtreep->nodes = ALLOC_N(struct kdtree_node, kdtreep->len);
146
+ read_all(fptr, (char *)kdtreep->nodes, sizeof(struct kdtree_node) * kdtreep->len);
147
+ } else {
148
+ rb_raise(rb_eTypeError, "array or IO required to init KDTree");
149
+ }
150
+
151
+ return kdtree;
152
+ }
153
+
154
+ static int comparex(const void *pa, const void *pb)
155
+ {
156
+ float a = ((const struct kdtree_node*)pa)->x;
157
+ float b = ((const struct kdtree_node*)pb)->x;
158
+ return (a < b) ? -1 : ((a > b) ? 1 : 0);
159
+ }
160
+
161
+ static int comparey(const void *pa, const void *pb)
162
+ {
163
+ float a = ((const struct kdtree_node*)pa)->y;
164
+ float b = ((const struct kdtree_node*)pb)->y;
165
+ return (a < b) ? -1 : ((a > b) ? 1 : 0);
166
+ }
167
+
168
+ static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth)
169
+ {
170
+ if (max <= min) {
171
+ return -1;
172
+ }
173
+
174
+ // sort nodes from min to max
175
+ int(*compar)(const void *, const void *) = (depth % 2) ? comparex : comparey;
176
+ qsort(kdtreep->nodes + min, max - min, sizeof(struct kdtree_node), compar);
177
+
178
+ int median = (min + max) / 2;
179
+ struct kdtree_node *m = kdtreep->nodes + median;
180
+ m->left = kdtree_build(kdtreep, min, median, depth + 1);
181
+ m->right = kdtree_build(kdtreep, median + 1, max, depth + 1);
182
+ return median;
183
+ }
184
+
185
+ //
186
+ // nearest
187
+ //
188
+
189
+ static int n_index;
190
+ static float n_dist;
191
+
192
+ /*
193
+ * call-seq:
194
+ * kd.nearest(x, y) => id
195
+ *
196
+ * Finds the point closest to <i>x</i>, <i>y</i> and returns the id for that
197
+ * point. Returns -1 if the tree is empty.
198
+ *
199
+ * points = []
200
+ * points << [47.6, -122.3, 1] # Seattle
201
+ * points << [40.7, -74.0, 2] # New York
202
+ * kd = KDTree.new(points)
203
+ *
204
+ * # which city is closest to Portland?
205
+ * kd.nearest(45.5, -122.8) #=> 1
206
+ * # which city is closest to Boston?
207
+ * kd.nearest(42.4, -71.1) #=> 2
208
+ */
209
+ static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y)
210
+ {
211
+ KDTREEP;
212
+
213
+ n_index = -1;
214
+ n_dist = INT_MAX;
215
+ kdtree_nearest0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), 0);
216
+ if (n_index == -1) {
217
+ return -1;
218
+ }
219
+ return INT2NUM((kdtreep->nodes + n_index)->id);
220
+ }
221
+
222
+ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth)
223
+ {
224
+ if (i == -1) {
225
+ return;
226
+ }
227
+
228
+ struct kdtree_node *n = kdtreep->nodes + i;
229
+
230
+ float ad = (depth % 2) ? (x - n->x) : (y - n->y);
231
+
232
+ //
233
+ // recurse near, and perhaps far as well
234
+ //
235
+
236
+ int near, far;
237
+ if (ad <= 0) {
238
+ near = n->left; far = n->right;
239
+ } else {
240
+ near = n->right; far = n->left;
241
+ }
242
+ kdtree_nearest0(kdtreep, near, x, y, depth + 1);
243
+ if (ad * ad < n_dist) {
244
+ kdtree_nearest0(kdtreep, far, x, y, depth + 1);
245
+ }
246
+
247
+ //
248
+ // do we beat the old distance?
249
+ //
250
+
251
+ float dx = (x - n->x) * (x - n->x);
252
+ if (dx < n_dist) {
253
+ float d = dx + ((y - n->y) * (y - n->y));
254
+ if (d < n_dist) {
255
+ n_index = i;
256
+ n_dist = d;
257
+ }
258
+ }
259
+ }
260
+
261
+ //
262
+ // nearestK
263
+ //
264
+
265
+ #define MAX_K 255
266
+
267
+ typedef struct kresult {
268
+ int index;
269
+ float distance;
270
+ } kresult;
271
+ // note I leave an extra slot here at the end because of the way our binary insert works
272
+ static struct kresult k_list[MAX_K + 1];
273
+ static int k_len;
274
+ static float k_dist;
275
+
276
+ /*
277
+ * call-seq:
278
+ * kd.nearestk(x, y, k) => array
279
+ *
280
+ * Finds the <i>k</i> points closest to <i>x</i>, <i>y</i>. Returns an array of
281
+ * ids, sorted by distance. Returns an empty array if the tree is empty. Note
282
+ * that <i>k</i> is capped at 255.
283
+ *
284
+ * points = []
285
+ * points << [47.6, -122.3, 1] # Seattle
286
+ * points << [45.5, -122.8, 2] # Portland
287
+ * points << [40.7, -74.0, 3] # New York
288
+ * kd = KDTree.new(points)
289
+ *
290
+ * # which two cities are closest to San Francisco?
291
+ * kd.nearest(34.1, -118.2) #=> [2, 1]
292
+ */
293
+ static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k)
294
+ {
295
+ KDTREEP;
296
+
297
+ k_len = 0;
298
+ k_dist = INT_MAX;
299
+
300
+ int ki = NUM2INT(k);
301
+ if (ki < 1) {
302
+ ki = 1;
303
+ } else if (ki > MAX_K) {
304
+ ki = MAX_K;
305
+ }
306
+ kdtree_nearestk0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), ki, 0);
307
+
308
+ // convert result to ruby array
309
+ VALUE ary = rb_ary_new();
310
+ int i;
311
+ for (i = 0; i < k_len; ++i) {
312
+ rb_ary_push(ary, INT2NUM(kdtreep->nodes[k_list[i].index].id));
313
+ }
314
+ return ary;
315
+ }
316
+
317
+ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float y, int k, int depth)
318
+ {
319
+ if (i == -1) {
320
+ return;
321
+ }
322
+
323
+ struct kdtree_node *n = kdtreep->nodes + i;
324
+
325
+ float ad = (depth % 2) ? (x - n->x) : (y - n->y);
326
+
327
+ //
328
+ // recurse near, and then perhaps far as well
329
+ //
330
+
331
+ int near, far;
332
+ if (ad <= 0) {
333
+ near = n->left; far = n->right;
334
+ } else {
335
+ near = n->right; far = n->left;
336
+ }
337
+ kdtree_nearestk0(kdtreep, near, x, y, k, depth + 1);
338
+ if (ad * ad < k_dist) {
339
+ kdtree_nearestk0(kdtreep, far, x, y, k, depth + 1);
340
+ }
341
+
342
+ //
343
+ // do we beat the old distance?
344
+ //
345
+
346
+ float dx = (x - n->x) * (x - n->x);
347
+ if (dx < k_dist) {
348
+ float d = dx + ((y - n->y) * (y - n->y));
349
+ if (d < k_dist) {
350
+ //
351
+ // find spot to insert
352
+ //
353
+ int lo = 0, hi = k_len;
354
+ while (lo < hi) {
355
+ int mid = (lo + hi) / 2;
356
+ if (k_list[mid].distance < d) {
357
+ lo = mid + 1;
358
+ } else {
359
+ hi = mid;
360
+ }
361
+ }
362
+
363
+ //
364
+ // insert
365
+ //
366
+
367
+ memmove(k_list + lo + 1, k_list + lo, (k_len - lo) * sizeof(struct kresult));
368
+ k_list[lo].index = i;
369
+ k_list[lo].distance = d;
370
+
371
+ //
372
+ // adjust len/dist if necessary
373
+ //
374
+
375
+ if (k_len < k) {
376
+ ++k_len;
377
+ } else {
378
+ k_dist = k_list[k - 1].distance;
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ /*
385
+ * call-seq:
386
+ * kd.persist(io)
387
+ *
388
+ * Writes the tree out to <i>io</i> so you can quickly load it later with
389
+ * KDTree.new. This avoids the startup cost of initializing a tree. Apart from a
390
+ * small header, the size of the file is proportional to the number of points,
391
+ * requiring 20 bytes per point.
392
+ *
393
+ * This file is <b>NOT PORTABLE</b> across different architectures due to endian
394
+ * issues.
395
+ *
396
+ * points = []
397
+ * points << [47.6, -122.3, 1] # Seattle
398
+ * points << [45.5, -122.8, 2] # Portland
399
+ * points << [40.7, -74.0, 3] # New York
400
+ * kd = KDTree.new(points)
401
+ *
402
+ * # persist the tree to disk
403
+ * File.open("treefile", "w") { |f| kd.persist(f) }
404
+ *
405
+ * ...
406
+ *
407
+ * # later, read the tree from disk
408
+ * kd2 = File.open("treefile") { |f| KDTree.new(f) }
409
+ */
410
+ static VALUE kdtree_persist(VALUE kdtree, VALUE io)
411
+ {
412
+ KDTREEP;
413
+
414
+ if (!rb_respond_to(io, rb_intern("write"))) {
415
+ rb_raise(rb_eTypeError, "instance of IO needed");
416
+ }
417
+ if (rb_respond_to(io, rb_intern("binmode"))) {
418
+ rb_funcall2(io, rb_intern("binmode"), 0, 0);
419
+ }
420
+
421
+ VALUE str = rb_str_buf_new(0);
422
+ rb_str_buf_cat(str, KDTREE_MAGIC, 4);
423
+ rb_str_buf_cat(str, (char*)kdtreep, sizeof(struct kdtree_data) - sizeof(struct kdtree_node *));
424
+ rb_str_buf_cat(str, (char*)kdtreep->nodes, sizeof(struct kdtree_node) * kdtreep->len);
425
+ rb_io_write(io, str);
426
+ return io;
427
+ }
428
+
429
+ /*
430
+ * call-seq:
431
+ * kd.to_s => string
432
+ *
433
+ * A string that tells you a bit about the tree.
434
+ */
435
+ static VALUE kdtree_to_s(VALUE kdtree)
436
+ {
437
+ KDTREEP;
438
+
439
+ char buf[256];
440
+ sprintf(buf, "#<%s:%p nodes=%d>", rb_obj_classname(kdtree), (void*)kdtree, kdtreep->len);
441
+ return rb_str_new(buf, strlen(buf));
442
+ }
443
+
444
+ //
445
+ // entry point
446
+ //
447
+
448
+ /*
449
+ * KDTree is an insanely fast data structure for finding the nearest
450
+ * neighbor(s) to a given point. This implementation only supports 2d
451
+ * points. Also, it only supports static points - there is no way to edit the
452
+ * tree after it has been initialized. KDTree should scale to millions of
453
+ * points, though it's only been tested with around 1 million.
454
+ *
455
+ * Once the tree is constructed, it can be searched with nearest and nearestk.
456
+ *
457
+ * To avoid the startup costs associated with creating a new tree, use persist
458
+ * to write the tree to disk. You can then construct the tree later from that
459
+ * file.
460
+ *
461
+ * points = []
462
+ * points << [47.6, -122.3, 1] # Seattle
463
+ * points << [45.5, -122.8, 2] # Portland
464
+ * points << [40.7, -74.0, 3] # New York
465
+ * kd = KDTree.new(points)
466
+ *
467
+ * # which city is closest to San Francisco?
468
+ * kd.nearest(34.1, -118.2) #=> 2
469
+ * # which two cities are closest to San Francisco?
470
+ * kd.nearest(34.1, -118.2) #=> [2, 1]
471
+ *
472
+ * For more information on kd trees, see:
473
+ *
474
+ * http://en.wikipedia.org/wiki/Kd-tree
475
+ */
476
+ void Init_kdtree()
477
+ {
478
+ static VALUE clazz;
479
+
480
+ clazz = rb_define_class("KDTree", rb_cObject);
481
+
482
+ rb_define_alloc_func(clazz, kdtree_alloc);
483
+ rb_define_method(clazz, "initialize", kdtree_initialize, 1);
484
+ rb_define_method(clazz, "nearest", kdtree_nearest, 2);
485
+ rb_define_method(clazz, "nearestk", kdtree_nearestk, 3);
486
+ rb_define_method(clazz, "persist", kdtree_persist, 1);
487
+ rb_define_method(clazz, "to_s", kdtree_to_s, 0);
488
+ }
@@ -0,0 +1,138 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../ext/kdtree.o"
2
+ require "test/unit"
3
+ require "tempfile"
4
+
5
+ #
6
+ # create a tree
7
+ #
8
+
9
+ class KDTreeTest < Test::Unit::TestCase
10
+ TMP = "#{Dir.tmpdir}/kdtree_test"
11
+
12
+ def test_nearest
13
+ setup_tree(1000)
14
+ 100.times do
15
+ pt = [rand_coord, rand_coord]
16
+
17
+ # kdtree search
18
+ id = @kdtree.nearest(pt[0], pt[1])
19
+ kdpt = @points[id]
20
+
21
+ # slow search
22
+ sortpt = @points.sort_by { |i| distance(i, pt) }.first
23
+
24
+ # assert
25
+ kdd = distance(kdpt, pt)
26
+ sortd = distance(sortpt, pt)
27
+ assert((kdd - sortd).abs < 0.0000001, "kdtree didn't return the closest result")
28
+ end
29
+ end
30
+
31
+ def test_nearestk
32
+ setup_tree(1000)
33
+ 100.times do
34
+ pt = [rand_coord, rand_coord]
35
+
36
+ # kdtree search
37
+ list = @kdtree.nearestk(pt[0], pt[1], 5)
38
+ kdpt = @points[list.last]
39
+
40
+ # slow search
41
+ sortpt = @points.sort_by { |i| distance(i, pt) }[list.length - 1]
42
+
43
+ # assert
44
+ kdd = distance(kdpt, pt)
45
+ sortd = distance(sortpt, pt)
46
+ assert((kdd - sortd).abs < 0.0000001, "kdtree didn't return the closest result")
47
+ end
48
+ end
49
+
50
+ def test_persist
51
+ setup_tree(1000)
52
+
53
+ begin
54
+ # write
55
+ File.open(TMP, "w") { |f| @kdtree.persist(f) }
56
+ # read
57
+ kdtree2 = File.open(TMP, "r") { |f| KDTree.new(f) }
58
+
59
+ # now test some random points
60
+ 100.times do
61
+ pt = [rand_coord, rand_coord]
62
+ id1 = @kdtree.nearest(*pt)
63
+ id2 = kdtree2.nearest(*pt)
64
+ assert(id1 == id2, "kdtree2 differed from kdtree")
65
+ end
66
+ ensure
67
+ File.unlink(TMP)
68
+ end
69
+
70
+ # now test magic problems
71
+ begin
72
+ File.open(TMP, "w") { |f| f.puts "That ain't right" }
73
+ assert_raise RuntimeError do
74
+ File.open(TMP, "r") { |f| KDTree.new(f) }
75
+ end
76
+ ensure
77
+ File.unlink(TMP)
78
+ end
79
+ end
80
+
81
+ def dont_test_speed
82
+ printf("\n")
83
+ sizes = [1, 100, 1000, 10000, 100000, 1000000]
84
+ ks = [1, 5, 50, 255]
85
+ sizes.each do |s|
86
+ points = (0...s).map { |i| [rand_coord, rand_coord, i] }
87
+
88
+ # build
89
+ tm = Time.now
90
+ kdtree = KDTree.new(points)
91
+ printf "build %d took %.6fs\n", s, Time.now - tm
92
+
93
+ begin
94
+ # write
95
+ tm = Time.now
96
+ File.open(TMP, "w") { |f| kdtree.persist(f) }
97
+ printf "write %d took %.6fs\n", s, Time.now - tm
98
+ # read
99
+ tm = Time.now
100
+ File.open(TMP, "r") { |f| KDTree.new(f) }
101
+ printf "read %d took %.6fs\n", s, Time.now - tm
102
+ ensure
103
+ File.unlink(TMP)
104
+ end
105
+
106
+ ks.each do |k|
107
+ total = count = 0
108
+ 100.times do
109
+ tm = Time.now
110
+ if k == 1
111
+ kdtree.nearest(rand_coord, rand_coord)
112
+ else
113
+ kdtree.nearestk(rand_coord, rand_coord, k)
114
+ end
115
+ total += Time.now - tm
116
+ count += 1
117
+ end
118
+ printf "avg query time = %.6fs [%d/%d]\n", total / count, s, k
119
+ end
120
+ end
121
+ end
122
+
123
+ protected
124
+
125
+ def setup_tree(len)
126
+ @points = (0...len).map { |i| [rand_coord, rand_coord, i] }
127
+ @kdtree = KDTree.new(@points)
128
+ end
129
+
130
+ def distance(a, b)
131
+ x, y = a[0] - b[0], a[1] - b[1]
132
+ x * x + y * y
133
+ end
134
+
135
+ def rand_coord
136
+ rand(0) * 10 - 5
137
+ end
138
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kdtree
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Adam Doppelt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-21 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: amd@gurge.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - ext/extconf.rb
26
+ - ext/kdtree.c
27
+ - LICENSE
28
+ - test/test.rb
29
+ has_rdoc: true
30
+ homepage:
31
+ licenses: []
32
+
33
+ post_install_message:
34
+ rdoc_options:
35
+ - --exclude
36
+ - test
37
+ - --exclude
38
+ - extconf
39
+ require_paths:
40
+ - .
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 1.8.5
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.3.5
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: Blazingly fast 2d kdtree.
60
+ test_files:
61
+ - test/test.rb