ghazel-kdtree 0.1.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 +503 -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.
data/ext/extconf.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile("kdtree")
data/ext/kdtree.c ADDED
@@ -0,0 +1,503 @@
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
+ int i;
107
+ kdtreep->len = RARRAY_LEN(points);
108
+ kdtreep->nodes = ALLOC_N(struct kdtree_node, kdtreep->len);
109
+
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
+ VALUE *a;
116
+ if (NIL_P(v) || RARRAY_LEN(v) != 3) {
117
+ continue;
118
+ }
119
+ a = RARRAY_PTR(ptr);
120
+ n->x = NUM2DBL(a[0]);
121
+ n->y = NUM2DBL(a[1]);
122
+ n->id = NUM2INT(a[2]);
123
+ }
124
+
125
+ // now build the tree
126
+ kdtreep->root = kdtree_build(kdtreep, 0, kdtreep->len, 0);
127
+ } else if (rb_respond_to(arg, rb_intern("read"))) {
128
+ VALUE io = arg;
129
+ struct rb_io_t *fptr;
130
+ char buf[4];
131
+ if (rb_respond_to(io, rb_intern("binmode"))) {
132
+ rb_funcall2(io, rb_intern("binmode"), 0, 0);
133
+ }
134
+
135
+ fptr = RFILE(rb_io_taint_check(io))->fptr;
136
+ rb_io_check_readable(fptr);
137
+
138
+ // check magic
139
+ read_all(fptr, buf, 4);
140
+ if (memcmp(KDTREE_MAGIC, buf, 4) != 0) {
141
+ rb_raise(rb_eRuntimeError, "wrong magic number in kdtree file");
142
+ }
143
+
144
+ // read start of the struct
145
+ read_all(fptr, (char *)kdtreep, sizeof(struct kdtree_data) - sizeof(struct kdtree_node *));
146
+ // read the nodes
147
+ kdtreep->nodes = ALLOC_N(struct kdtree_node, kdtreep->len);
148
+ read_all(fptr, (char *)kdtreep->nodes, sizeof(struct kdtree_node) * kdtreep->len);
149
+ } else {
150
+ rb_raise(rb_eTypeError, "array or IO required to init KDTree");
151
+ }
152
+
153
+ return kdtree;
154
+ }
155
+
156
+ static int comparex(const void *pa, const void *pb)
157
+ {
158
+ float a = ((const struct kdtree_node*)pa)->x;
159
+ float b = ((const struct kdtree_node*)pb)->x;
160
+ return (a < b) ? -1 : ((a > b) ? 1 : 0);
161
+ }
162
+
163
+ static int comparey(const void *pa, const void *pb)
164
+ {
165
+ float a = ((const struct kdtree_node*)pa)->y;
166
+ float b = ((const struct kdtree_node*)pb)->y;
167
+ return (a < b) ? -1 : ((a > b) ? 1 : 0);
168
+ }
169
+
170
+ static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth)
171
+ {
172
+ int(*compar)(const void *, const void *);
173
+ struct kdtree_node *m;
174
+ int median;
175
+ if (max <= min) {
176
+ return -1;
177
+ }
178
+
179
+ // sort nodes from min to max
180
+ compar = (depth % 2) ? comparex : comparey;
181
+ qsort(kdtreep->nodes + min, max - min, sizeof(struct kdtree_node), compar);
182
+
183
+ median = (min + max) / 2;
184
+ m = kdtreep->nodes + median;
185
+ m->left = kdtree_build(kdtreep, min, median, depth + 1);
186
+ m->right = kdtree_build(kdtreep, median + 1, max, depth + 1);
187
+ return median;
188
+ }
189
+
190
+ //
191
+ // nearest
192
+ //
193
+
194
+ static int n_index;
195
+ static float n_dist;
196
+
197
+ /*
198
+ * call-seq:
199
+ * kd.nearest(x, y) => id
200
+ *
201
+ * Finds the point closest to <i>x</i>, <i>y</i> and returns the id for that
202
+ * point. Returns -1 if the tree is empty.
203
+ *
204
+ * points = []
205
+ * points << [47.6, -122.3, 1] # Seattle
206
+ * points << [40.7, -74.0, 2] # New York
207
+ * kd = KDTree.new(points)
208
+ *
209
+ * # which city is closest to Portland?
210
+ * kd.nearest(45.5, -122.8) #=> 1
211
+ * # which city is closest to Boston?
212
+ * kd.nearest(42.4, -71.1) #=> 2
213
+ */
214
+ static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y)
215
+ {
216
+ KDTREEP;
217
+
218
+ n_index = -1;
219
+ n_dist = INT_MAX;
220
+ kdtree_nearest0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), 0);
221
+ if (n_index == -1) {
222
+ return -1;
223
+ }
224
+ return INT2NUM((kdtreep->nodes + n_index)->id);
225
+ }
226
+
227
+ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth)
228
+ {
229
+ struct kdtree_node *n;
230
+ float ad;
231
+ int near_v, far_v;
232
+ float dx;
233
+ if (i == -1) {
234
+ return;
235
+ }
236
+
237
+ n = kdtreep->nodes + i;
238
+
239
+ ad = (depth % 2) ? (x - n->x) : (y - n->y);
240
+
241
+ //
242
+ // recurse near, and perhaps far as well
243
+ //
244
+
245
+ if (ad <= 0) {
246
+ near_v = n->left; far_v = n->right;
247
+ } else {
248
+ near_v = n->right; far_v = n->left;
249
+ }
250
+ kdtree_nearest0(kdtreep, near_v, x, y, depth + 1);
251
+ if (ad * ad < n_dist) {
252
+ kdtree_nearest0(kdtreep, far_v, x, y, depth + 1);
253
+ }
254
+
255
+ //
256
+ // do we beat the old distance?
257
+ //
258
+
259
+ dx = (x - n->x) * (x - n->x);
260
+ if (dx < n_dist) {
261
+ float d = dx + ((y - n->y) * (y - n->y));
262
+ if (d < n_dist) {
263
+ n_index = i;
264
+ n_dist = d;
265
+ }
266
+ }
267
+ }
268
+
269
+ //
270
+ // nearestK
271
+ //
272
+
273
+ #define MAX_K 255
274
+
275
+ typedef struct kresult {
276
+ int index;
277
+ float distance;
278
+ } kresult;
279
+ // note I leave an extra slot here at the end because of the way our binary insert works
280
+ static struct kresult k_list[MAX_K + 1];
281
+ static int k_len;
282
+ static float k_dist;
283
+
284
+ /*
285
+ * call-seq:
286
+ * kd.nearestk(x, y, k) => array
287
+ *
288
+ * Finds the <i>k</i> points closest to <i>x</i>, <i>y</i>. Returns an array of
289
+ * ids, sorted by distance. Returns an empty array if the tree is empty. Note
290
+ * that <i>k</i> is capped at 255.
291
+ *
292
+ * points = []
293
+ * points << [47.6, -122.3, 1] # Seattle
294
+ * points << [45.5, -122.8, 2] # Portland
295
+ * points << [40.7, -74.0, 3] # New York
296
+ * kd = KDTree.new(points)
297
+ *
298
+ * # which two cities are closest to San Francisco?
299
+ * kd.nearest(34.1, -118.2) #=> [2, 1]
300
+ */
301
+ static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k)
302
+ {
303
+ int ki;
304
+ VALUE ary;
305
+ int i;
306
+ KDTREEP;
307
+
308
+ k_len = 0;
309
+ k_dist = INT_MAX;
310
+
311
+ ki = NUM2INT(k);
312
+ if (ki < 1) {
313
+ ki = 1;
314
+ } else if (ki > MAX_K) {
315
+ ki = MAX_K;
316
+ }
317
+ kdtree_nearestk0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), ki, 0);
318
+
319
+ // convert result to ruby array
320
+ ary = rb_ary_new();
321
+ for (i = 0; i < k_len; ++i) {
322
+ rb_ary_push(ary, INT2NUM(kdtreep->nodes[k_list[i].index].id));
323
+ }
324
+ return ary;
325
+ }
326
+
327
+ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float y, int k, int depth)
328
+ {
329
+ struct kdtree_node *n;
330
+ int near_v, far_v;
331
+ float ad;
332
+ float dx;
333
+ if (i == -1) {
334
+ return;
335
+ }
336
+
337
+ n = kdtreep->nodes + i;
338
+
339
+ ad = (depth % 2) ? (x - n->x) : (y - n->y);
340
+
341
+ //
342
+ // recurse near, and then perhaps far as well
343
+ //
344
+
345
+
346
+ if (ad <= 0) {
347
+ near_v = n->left; far_v = n->right;
348
+ } else {
349
+ near_v = n->right; far_v = n->left;
350
+ }
351
+ kdtree_nearestk0(kdtreep, near_v, x, y, k, depth + 1);
352
+ if (ad * ad < k_dist) {
353
+ kdtree_nearestk0(kdtreep, far_v, x, y, k, depth + 1);
354
+ }
355
+
356
+ //
357
+ // do we beat the old distance?
358
+ //
359
+
360
+ dx = (x - n->x) * (x - n->x);
361
+ if (dx < k_dist) {
362
+ float d = dx + ((y - n->y) * (y - n->y));
363
+ if (d < k_dist) {
364
+ //
365
+ // find spot to insert
366
+ //
367
+ int lo = 0, hi = k_len;
368
+ while (lo < hi) {
369
+ int mid = (lo + hi) / 2;
370
+ if (k_list[mid].distance < d) {
371
+ lo = mid + 1;
372
+ } else {
373
+ hi = mid;
374
+ }
375
+ }
376
+
377
+ //
378
+ // insert
379
+ //
380
+
381
+ memmove(k_list + lo + 1, k_list + lo, (k_len - lo) * sizeof(struct kresult));
382
+ k_list[lo].index = i;
383
+ k_list[lo].distance = d;
384
+
385
+ //
386
+ // adjust len/dist if necessary
387
+ //
388
+
389
+ if (k_len < k) {
390
+ ++k_len;
391
+ } else {
392
+ k_dist = k_list[k - 1].distance;
393
+ }
394
+ }
395
+ }
396
+ }
397
+
398
+ /*
399
+ * call-seq:
400
+ * kd.persist(io)
401
+ *
402
+ * Writes the tree out to <i>io</i> so you can quickly load it later with
403
+ * KDTree.new. This avoids the startup cost of initializing a tree. Apart from a
404
+ * small header, the size of the file is proportional to the number of points,
405
+ * requiring 20 bytes per point.
406
+ *
407
+ * This file is <b>NOT PORTABLE</b> across different architectures due to endian
408
+ * issues.
409
+ *
410
+ * points = []
411
+ * points << [47.6, -122.3, 1] # Seattle
412
+ * points << [45.5, -122.8, 2] # Portland
413
+ * points << [40.7, -74.0, 3] # New York
414
+ * kd = KDTree.new(points)
415
+ *
416
+ * # persist the tree to disk
417
+ * File.open("treefile", "w") { |f| kd.persist(f) }
418
+ *
419
+ * ...
420
+ *
421
+ * # later, read the tree from disk
422
+ * kd2 = File.open("treefile") { |f| KDTree.new(f) }
423
+ */
424
+ static VALUE kdtree_persist(VALUE kdtree, VALUE io)
425
+ {
426
+ VALUE str;
427
+ KDTREEP;
428
+
429
+ if (!rb_respond_to(io, rb_intern("write"))) {
430
+ rb_raise(rb_eTypeError, "instance of IO needed");
431
+ }
432
+ if (rb_respond_to(io, rb_intern("binmode"))) {
433
+ rb_funcall2(io, rb_intern("binmode"), 0, 0);
434
+ }
435
+
436
+ str = rb_str_buf_new(0);
437
+ rb_str_buf_cat(str, KDTREE_MAGIC, 4);
438
+ rb_str_buf_cat(str, (char*)kdtreep, sizeof(struct kdtree_data) - sizeof(struct kdtree_node *));
439
+ rb_str_buf_cat(str, (char*)kdtreep->nodes, sizeof(struct kdtree_node) * kdtreep->len);
440
+ rb_io_write(io, str);
441
+ return io;
442
+ }
443
+
444
+ /*
445
+ * call-seq:
446
+ * kd.to_s => string
447
+ *
448
+ * A string that tells you a bit about the tree.
449
+ */
450
+ static VALUE kdtree_to_s(VALUE kdtree)
451
+ {
452
+ char buf[256];
453
+ KDTREEP;
454
+
455
+ sprintf(buf, "#<%s:%p nodes=%d>", rb_obj_classname(kdtree), (void*)kdtree, kdtreep->len);
456
+ return rb_str_new(buf, strlen(buf));
457
+ }
458
+
459
+ //
460
+ // entry point
461
+ //
462
+
463
+ /*
464
+ * KDTree is an insanely fast data structure for finding the nearest
465
+ * neighbor(s) to a given point. This implementation only supports 2d
466
+ * points. Also, it only supports static points - there is no way to edit the
467
+ * tree after it has been initialized. KDTree should scale to millions of
468
+ * points, though it's only been tested with around 1 million.
469
+ *
470
+ * Once the tree is constructed, it can be searched with nearest and nearestk.
471
+ *
472
+ * To avoid the startup costs associated with creating a new tree, use persist
473
+ * to write the tree to disk. You can then construct the tree later from that
474
+ * file.
475
+ *
476
+ * points = []
477
+ * points << [47.6, -122.3, 1] # Seattle
478
+ * points << [45.5, -122.8, 2] # Portland
479
+ * points << [40.7, -74.0, 3] # New York
480
+ * kd = KDTree.new(points)
481
+ *
482
+ * # which city is closest to San Francisco?
483
+ * kd.nearest(34.1, -118.2) #=> 2
484
+ * # which two cities are closest to San Francisco?
485
+ * kd.nearest(34.1, -118.2) #=> [2, 1]
486
+ *
487
+ * For more information on kd trees, see:
488
+ *
489
+ * http://en.wikipedia.org/wiki/Kd-tree
490
+ */
491
+ void Init_kdtree()
492
+ {
493
+ static VALUE clazz;
494
+
495
+ clazz = rb_define_class("KDTree", rb_cObject);
496
+
497
+ rb_define_alloc_func(clazz, kdtree_alloc);
498
+ rb_define_method(clazz, "initialize", kdtree_initialize, 1);
499
+ rb_define_method(clazz, "nearest", kdtree_nearest, 2);
500
+ rb_define_method(clazz, "nearestk", kdtree_nearestk, 3);
501
+ rb_define_method(clazz, "persist", kdtree_persist, 1);
502
+ rb_define_method(clazz, "to_s", kdtree_to_s, 0);
503
+ }
data/test/test.rb ADDED
@@ -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: ghazel-kdtree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Adam Doppelt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-22 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