tupalo-kdtree 0.2.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 +7 -0
  3. data/ext/kdtree.c +490 -0
  4. data/test/test.rb +138 -0
  5. metadata +66 -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,7 @@
1
+ require 'mkmf'
2
+
3
+ if RUBY_VERSION =~ /1.9/ then
4
+ $CPPFLAGS += " -DRUBY_19"
5
+ end
6
+
7
+ create_makefile("kdtree")
data/ext/kdtree.c ADDED
@@ -0,0 +1,490 @@
1
+ #include "ruby.h"
2
+
3
+ #ifndef RUBY_19
4
+ #include "rubyio.h"
5
+ #else
6
+ #include "ruby/io.h"
7
+ #endif
8
+
9
+ #ifndef HAVE_RB_IO_T
10
+ #define rb_io_t OpenFile
11
+ #endif
12
+
13
+ #ifndef RUBY_19
14
+ #define rb_io_stdio_file(fptr) ((fptr)->f)
15
+ #else
16
+ #define rb_io_fread(buf,n,f) fread((buf),1,(n),(f))
17
+ #endif
18
+
19
+ //
20
+ // interface
21
+ //
22
+
23
+ typedef struct kdtree_data
24
+ {
25
+ int root;
26
+ int len;
27
+ struct kdtree_node *nodes;
28
+ } kdtree_data;
29
+
30
+ typedef struct kdtree_node
31
+ {
32
+ float x, y;
33
+ int id;
34
+ int left;
35
+ int right;
36
+ } kdtree_node;
37
+
38
+ typedef struct kresult {
39
+ int index;
40
+ float distance;
41
+ } kresult;
42
+
43
+ #define KDTREEP \
44
+ struct kdtree_data *kdtreep; \
45
+ Data_Get_Struct(kdtree, struct kdtree_data, kdtreep);
46
+
47
+ static VALUE kdtree_alloc(VALUE klass);
48
+ static void kdtree_free(struct kdtree_data *kdtreep);
49
+ static VALUE kdtree_initialize(VALUE kdtree, VALUE points);
50
+ static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y);
51
+ static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k);
52
+ static VALUE kdtree_persist(VALUE kdtree, VALUE io);
53
+ static VALUE kdtree_to_s(VALUE kdtree);
54
+
55
+ // helpers
56
+ static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth);
57
+ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth, int *n_index, float *n_dist);
58
+ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float y, int k, int depth, kresult *k_list, int *k_len, float *k_dist);
59
+
60
+ #define KDTREE_MAGIC "KdTr"
61
+
62
+ //
63
+ // implementation
64
+ //
65
+
66
+ static VALUE kdtree_alloc(VALUE klass)
67
+ {
68
+ struct kdtree_data *kdtreep;
69
+ VALUE obj = Data_Make_Struct(klass, struct kdtree_data, 0, kdtree_free, kdtreep);
70
+ kdtreep->root = -1;
71
+ return obj;
72
+ }
73
+
74
+ static void kdtree_free(struct kdtree_data *kdtreep)
75
+ {
76
+ if (kdtreep) {
77
+ free(kdtreep->nodes);
78
+ }
79
+ }
80
+
81
+ static void read_all(struct rb_io_t *fptr, char *buf, int len)
82
+ {
83
+ while (len > 0) {
84
+ int n = rb_io_fread(buf, len, rb_io_stdio_file (fptr));
85
+ if (n == 0) {
86
+ rb_eof_error();
87
+ }
88
+ buf += n;
89
+ len -= n;
90
+ }
91
+ }
92
+
93
+ /*
94
+ * call-seq:
95
+ * KDTree.new(points) => kdtree
96
+ * KDTree.new(io) => kdtree
97
+ *
98
+ * Returns a new <code>KDTree</code>. To construct a tree, pass an array of
99
+ * <i>points</i>. Each point should be an array of the form <code>[x, y,
100
+ * id]</code>, where <i>x</i> and <i>y</i> are floats and <i>id</i> is an
101
+ * integer. The <i>id</i> is arbitrary and will be returned to you whenever you
102
+ * search with nearest or nearestk.
103
+ *
104
+ * # create a new tree
105
+ * points = []
106
+ * points << [47.6, -122.3, 1] # Seattle
107
+ * points << [40.7, -74.0, 2] # New York
108
+ * kd = KDTree.new(points)
109
+ *
110
+ * Alternately, you can pass in an <i>IO</i> object containing a persisted
111
+ * kdtree. This makes it possible to build the tree in advance, persist it, and
112
+ * start it up quickly later. See persist for more information.
113
+ */
114
+ static VALUE kdtree_initialize(VALUE kdtree, VALUE arg)
115
+ {
116
+ KDTREEP;
117
+
118
+ if (TYPE(arg) == T_ARRAY) {
119
+ // init from array of pints
120
+ VALUE points = arg;
121
+ kdtreep->len = RARRAY_LEN(points);
122
+ kdtreep->nodes = ALLOC_N(struct kdtree_node, kdtreep->len);
123
+
124
+ int i;
125
+ for (i = 0; i < RARRAY_LEN(points); ++i) {
126
+ struct kdtree_node *n = kdtreep->nodes + i;
127
+
128
+ VALUE ptr = RARRAY_PTR(points)[i];
129
+ VALUE v = rb_check_array_type(ptr);
130
+ if (NIL_P(v) || RARRAY_LEN(v) != 3) {
131
+ continue;
132
+ }
133
+ VALUE *a = RARRAY_PTR(ptr);
134
+ n->x = NUM2DBL(a[0]);
135
+ n->y = NUM2DBL(a[1]);
136
+ n->id = NUM2INT(a[2]);
137
+ }
138
+
139
+ // now build the tree
140
+ kdtreep->root = kdtree_build(kdtreep, 0, kdtreep->len, 0);
141
+ } else if (rb_respond_to(arg, rb_intern("read"))) {
142
+ VALUE io = arg;
143
+ if (rb_respond_to(io, rb_intern("binmode"))) {
144
+ rb_funcall2(io, rb_intern("binmode"), 0, 0);
145
+ }
146
+
147
+ struct rb_io_t *fptr = RFILE(rb_io_taint_check(io))->fptr;
148
+ rb_io_check_readable(fptr);
149
+
150
+ // check magic
151
+ char buf[4];
152
+ read_all(fptr, buf, 4);
153
+ if (memcmp(KDTREE_MAGIC, buf, 4) != 0) {
154
+ rb_raise(rb_eRuntimeError, "wrong magic number in kdtree file");
155
+ }
156
+
157
+ // read start of the struct
158
+ read_all(fptr, (char *)kdtreep, sizeof(struct kdtree_data) - sizeof(struct kdtree_node *));
159
+ // read the nodes
160
+ kdtreep->nodes = ALLOC_N(struct kdtree_node, kdtreep->len);
161
+ read_all(fptr, (char *)kdtreep->nodes, sizeof(struct kdtree_node) * kdtreep->len);
162
+ } else {
163
+ rb_raise(rb_eTypeError, "array or IO required to init KDTree");
164
+ }
165
+
166
+ return kdtree;
167
+ }
168
+
169
+ static int comparex(const void *pa, const void *pb)
170
+ {
171
+ float a = ((const struct kdtree_node*)pa)->x;
172
+ float b = ((const struct kdtree_node*)pb)->x;
173
+ return (a < b) ? -1 : ((a > b) ? 1 : 0);
174
+ }
175
+
176
+ static int comparey(const void *pa, const void *pb)
177
+ {
178
+ float a = ((const struct kdtree_node*)pa)->y;
179
+ float b = ((const struct kdtree_node*)pb)->y;
180
+ return (a < b) ? -1 : ((a > b) ? 1 : 0);
181
+ }
182
+
183
+ static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth)
184
+ {
185
+ if (max <= min) {
186
+ return -1;
187
+ }
188
+
189
+ // sort nodes from min to max
190
+ int(*compar)(const void *, const void *) = (depth % 2) ? comparex : comparey;
191
+ qsort(kdtreep->nodes + min, max - min, sizeof(struct kdtree_node), compar);
192
+
193
+ int median = (min + max) / 2;
194
+ struct kdtree_node *m = kdtreep->nodes + median;
195
+ m->left = kdtree_build(kdtreep, min, median, depth + 1);
196
+ m->right = kdtree_build(kdtreep, median + 1, max, depth + 1);
197
+ return median;
198
+ }
199
+
200
+ /*
201
+ * call-seq:
202
+ * kd.nearest(x, y) => id
203
+ *
204
+ * Finds the point closest to <i>x</i>, <i>y</i> and returns the id for that
205
+ * point. Returns -1 if the tree is empty.
206
+ *
207
+ * points = []
208
+ * points << [47.6, -122.3, 1] # Seattle
209
+ * points << [40.7, -74.0, 2] # New York
210
+ * kd = KDTree.new(points)
211
+ *
212
+ * # which city is closest to Portland?
213
+ * kd.nearest(45.5, -122.8) #=> 1
214
+ * # which city is closest to Boston?
215
+ * kd.nearest(42.4, -71.1) #=> 2
216
+ */
217
+ static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y)
218
+ {
219
+ KDTREEP;
220
+
221
+ int n_index = -1;
222
+ float n_dist = INT_MAX;
223
+
224
+ kdtree_nearest0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), 0, &n_index, &n_dist);
225
+ if (n_index == -1) {
226
+ return -1;
227
+ }
228
+ return INT2NUM((kdtreep->nodes + n_index)->id);
229
+ }
230
+
231
+ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth, int *n_index, float *n_dist)
232
+ {
233
+ if (i == -1) {
234
+ return;
235
+ }
236
+
237
+ struct kdtree_node *n = kdtreep->nodes + i;
238
+
239
+ float ad = (depth % 2) ? (x - n->x) : (y - n->y);
240
+
241
+ //
242
+ // recurse near, and perhaps far as well
243
+ //
244
+
245
+ int near, far;
246
+ if (ad <= 0) {
247
+ near = n->left; far = n->right;
248
+ } else {
249
+ near = n->right; far = n->left;
250
+ }
251
+ kdtree_nearest0(kdtreep, near, x, y, depth + 1, n_index, n_dist);
252
+ if (ad * ad < *n_dist) {
253
+ kdtree_nearest0(kdtreep, far, x, y, depth + 1, n_index, n_dist);
254
+ }
255
+
256
+ //
257
+ // do we beat the old distance?
258
+ //
259
+
260
+ float dx = (x - n->x) * (x - n->x);
261
+ if (dx < *n_dist) {
262
+ float d = dx + ((y - n->y) * (y - n->y));
263
+ if (d < *n_dist) {
264
+ *n_index = i;
265
+ *n_dist = d;
266
+ }
267
+ }
268
+ }
269
+
270
+ //
271
+ // nearestK
272
+ //
273
+
274
+ #define MAX_K 255
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
+ // note I leave an extra slot here at the end because of the way our binary insert works
298
+ kresult k_list[MAX_K + 1];
299
+ int k_len = 0;
300
+ float k_dist = INT_MAX;
301
+
302
+ int ki = NUM2INT(k);
303
+ if (ki < 1) {
304
+ ki = 1;
305
+ } else if (ki > MAX_K) {
306
+ ki = MAX_K;
307
+ }
308
+ kdtree_nearestk0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), ki, 0, k_list, &k_len, &k_dist);
309
+
310
+ // convert result to ruby array
311
+ VALUE ary = rb_ary_new();
312
+ int i;
313
+ for (i = 0; i < k_len; ++i) {
314
+ rb_ary_push(ary, INT2NUM(kdtreep->nodes[k_list[i].index].id));
315
+ }
316
+ return ary;
317
+ }
318
+
319
+ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float y, int k, int depth, kresult *k_list, int *k_len, float *k_dist)
320
+ {
321
+ if (i == -1) {
322
+ return;
323
+ }
324
+
325
+ struct kdtree_node *n = kdtreep->nodes + i;
326
+
327
+ float ad = (depth % 2) ? (x - n->x) : (y - n->y);
328
+
329
+ //
330
+ // recurse near, and then perhaps far as well
331
+ //
332
+
333
+ int near, far;
334
+ if (ad <= 0) {
335
+ near = n->left; far = n->right;
336
+ } else {
337
+ near = n->right; far = n->left;
338
+ }
339
+ kdtree_nearestk0(kdtreep, near, x, y, k, depth + 1, k_list, k_len, k_dist);
340
+ if (ad * ad < *k_dist) {
341
+ kdtree_nearestk0(kdtreep, far, x, y, k, depth + 1, k_list, k_len, k_dist);
342
+ }
343
+
344
+ //
345
+ // do we beat the old distance?
346
+ //
347
+
348
+ float dx = (x - n->x) * (x - n->x);
349
+ if (dx < *k_dist) {
350
+ float d = dx + ((y - n->y) * (y - n->y));
351
+ if (d < *k_dist) {
352
+ //
353
+ // find spot to insert
354
+ //
355
+ int lo = 0, hi = *k_len;
356
+ while (lo < hi) {
357
+ int mid = (lo + hi) / 2;
358
+ if (k_list[mid].distance < d) {
359
+ lo = mid + 1;
360
+ } else {
361
+ hi = mid;
362
+ }
363
+ }
364
+
365
+ //
366
+ // insert
367
+ //
368
+
369
+ memmove(k_list + lo + 1, k_list + lo, (*k_len - lo) * sizeof(struct kresult));
370
+ k_list[lo].index = i;
371
+ k_list[lo].distance = d;
372
+
373
+ //
374
+ // adjust len/dist if necessary
375
+ //
376
+
377
+ if (*k_len < k) {
378
+ ++(*k_len);
379
+ } else {
380
+ *k_dist = k_list[k - 1].distance;
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+ /*
387
+ * call-seq:
388
+ * kd.persist(io)
389
+ *
390
+ * Writes the tree out to <i>io</i> so you can quickly load it later with
391
+ * KDTree.new. This avoids the startup cost of initializing a tree. Apart from a
392
+ * small header, the size of the file is proportional to the number of points,
393
+ * requiring 20 bytes per point.
394
+ *
395
+ * This file is <b>NOT PORTABLE</b> across different architectures due to endian
396
+ * issues.
397
+ *
398
+ * points = []
399
+ * points << [47.6, -122.3, 1] # Seattle
400
+ * points << [45.5, -122.8, 2] # Portland
401
+ * points << [40.7, -74.0, 3] # New York
402
+ * kd = KDTree.new(points)
403
+ *
404
+ * # persist the tree to disk
405
+ * File.open("treefile", "w") { |f| kd.persist(f) }
406
+ *
407
+ * ...
408
+ *
409
+ * # later, read the tree from disk
410
+ * kd2 = File.open("treefile") { |f| KDTree.new(f) }
411
+ */
412
+ static VALUE kdtree_persist(VALUE kdtree, VALUE io)
413
+ {
414
+ KDTREEP;
415
+
416
+ if (!rb_respond_to(io, rb_intern("write"))) {
417
+ rb_raise(rb_eTypeError, "instance of IO needed");
418
+ }
419
+ if (rb_respond_to(io, rb_intern("binmode"))) {
420
+ rb_funcall2(io, rb_intern("binmode"), 0, 0);
421
+ }
422
+
423
+ VALUE str = rb_str_buf_new(0);
424
+ rb_str_buf_cat(str, KDTREE_MAGIC, 4);
425
+ rb_str_buf_cat(str, (char*)kdtreep, sizeof(struct kdtree_data) - sizeof(struct kdtree_node *));
426
+ rb_str_buf_cat(str, (char*)kdtreep->nodes, sizeof(struct kdtree_node) * kdtreep->len);
427
+ rb_io_write(io, str);
428
+ return io;
429
+ }
430
+
431
+ /*
432
+ * call-seq:
433
+ * kd.to_s => string
434
+ *
435
+ * A string that tells you a bit about the tree.
436
+ */
437
+ static VALUE kdtree_to_s(VALUE kdtree)
438
+ {
439
+ KDTREEP;
440
+
441
+ char buf[256];
442
+ sprintf(buf, "#<%s:%p nodes=%d>", rb_obj_classname(kdtree), (void*)kdtree, kdtreep->len);
443
+ return rb_str_new(buf, strlen(buf));
444
+ }
445
+
446
+ //
447
+ // entry point
448
+ //
449
+
450
+ /*
451
+ * KDTree is an insanely fast data structure for finding the nearest
452
+ * neighbor(s) to a given point. This implementation only supports 2d
453
+ * points. Also, it only supports static points - there is no way to edit the
454
+ * tree after it has been initialized. KDTree should scale to millions of
455
+ * points, though it's only been tested with around 1 million.
456
+ *
457
+ * Once the tree is constructed, it can be searched with nearest and nearestk.
458
+ *
459
+ * To avoid the startup costs associated with creating a new tree, use persist
460
+ * to write the tree to disk. You can then construct the tree later from that
461
+ * file.
462
+ *
463
+ * points = []
464
+ * points << [47.6, -122.3, 1] # Seattle
465
+ * points << [45.5, -122.8, 2] # Portland
466
+ * points << [40.7, -74.0, 3] # New York
467
+ * kd = KDTree.new(points)
468
+ *
469
+ * # which city is closest to San Francisco?
470
+ * kd.nearest(34.1, -118.2) #=> 2
471
+ * # which two cities are closest to San Francisco?
472
+ * kd.nearest(34.1, -118.2) #=> [2, 1]
473
+ *
474
+ * For more information on kd trees, see:
475
+ *
476
+ * http://en.wikipedia.org/wiki/Kd-tree
477
+ */
478
+ void Init_kdtree()
479
+ {
480
+ static VALUE clazz;
481
+
482
+ clazz = rb_define_class("KDTree", rb_cObject);
483
+
484
+ rb_define_alloc_func(clazz, kdtree_alloc);
485
+ rb_define_method(clazz, "initialize", kdtree_initialize, 1);
486
+ rb_define_method(clazz, "nearest", kdtree_nearest, 2);
487
+ rb_define_method(clazz, "nearestk", kdtree_nearestk, 3);
488
+ rb_define_method(clazz, "persist", kdtree_persist, 1);
489
+ rb_define_method(clazz, "to_s", kdtree_to_s, 0);
490
+ }
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,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tupalo-kdtree
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 1
9
+ version: 0.2.1
10
+ platform: ruby
11
+ authors:
12
+ - Adam Doppelt
13
+ - Andreas Fuchs
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-28 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Tupalo-kdtree is a thread-safe fork of the kdtree Gem by Adam Doppelt.
23
+ email: developers@tupalo.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ files:
31
+ - LICENSE
32
+ - ext/extconf.rb
33
+ - ext/kdtree.c
34
+ - test/test.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/consti/tupalo-kdtree
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.6
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: Tupalo-kdtree is a thread-safe fork of the kdtree Gem by Adam Doppelt.
65
+ test_files:
66
+ - test/test.rb