kdtree 0.3 → 0.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8fa26faf959cacb1da1692bef2539c89031037f4a2c5f715eadc5fed72fa5590
4
+ data.tar.gz: 3377e4521781a8a675840bf5ac7a34998bbd43783643f0b2ab4ee9988d85465d
5
+ SHA512:
6
+ metadata.gz: 7f50f4e1721d3ad2359ac3c3ebf8436a5716a4b333e4cc6de3f1443624417b5cecdabb9a25a76f52baf89014b3a70ca699ae4920657ec384305275fcc01a2c3d
7
+ data.tar.gz: 2075eea9b92b86c547e70a5802df0c2af13554ec43ac92ddedd85d4012d02219ddfa646dabed4e81edb125af363ecc26ad5357b797931184a0a8f3dd8eba53b3
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Adam Doppelt
1
+ Copyright (c) 2025 Adam Doppelt
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,70 +1,115 @@
1
- ## Kdtree
1
+ ## Kdtree [![test](https://github.com/gurgeous/kdtree/actions/workflows/test.yml/badge.svg)](https://github.com/gurgeous/kdtree/actions/workflows/test.yml)
2
+
2
3
 
3
4
  A kd tree is a data structure that recursively partitions the world in order to rapidly answer nearest neighbor queries. A generic kd tree can support any number of dimensions, and can return either the nearest neighbor or a set of N nearest neighbors.
4
5
 
5
6
  This gem is a blazingly fast, native, 2d kdtree. It's specifically built to find the nearest neighbor when searching millions of points. It's used in production at Urbanspoon and several other companies.
6
7
 
7
- The first version of this gem was released back in 2009. See the original [blog post](http://gurge.com/2009/10/22/ruby-nearest-neighbor-fast-kdtree-gem/) for the full story. Wikipedia has a great [article on kdtrees](http://en.wikipedia.org/wiki/K-d_tree).
8
+ The first version of this gem was released back in 2009. Wikipedia has a great [article on kdtrees](http://en.wikipedia.org/wiki/K-d_tree).
9
+
10
+ Note: kdtree obsoletes these forks: ghazel-kdtree, groupon-kdtree, tupalo-kdree. Thanks guys!
11
+
12
+ ### Installation
13
+
14
+ ```ruby
15
+ # install gem
16
+ $ gem install kdtree
17
+
18
+ # or add to your Gemfile
19
+ gem "kdtree"
20
+ ```
8
21
 
9
22
  ### Usage
10
23
 
11
- Usage is very simple:
24
+ It's easy to use:
25
+
26
+ - **Kdtree.new(points)** - construct a new tree. Each point should be of the form `[x, y, id]`, where `x/y` are floats and `id` is an int. Not a string, not an object, **just an int**.
27
+ - **kd.nearest(x, y)** - find the nearest point. Returns an id.
28
+ - **kd.nearestk(x, y, k)** - find the nearest `k` points. Returns an array of ids.
12
29
 
13
- * **Kdtree.new(points)** - construct a new tree. Each point should be of the form `[x, y, id]`, where `x/y` are floats and `id` is an int. Not a string, not an object, just an int.
14
- * **kd.nearest(x, y)** - find the nearest point. Returns an id.
15
- * **kd.nearestk(x, y, k)** - find the nearest `k` points. Returns an array of ids.
30
+ For example:
16
31
 
17
- Also, I made it possible to **persist** the tree to disk and load it later. That way you can calculate the tree offline and load it quickly at some future point. Loading a persisted tree w/ 1 millions points takes less than a second, as opposed to the 3.5 second startup time shown above. For example:
32
+ ```ruby
33
+ # construct the tree
34
+ points = []
35
+ points << [47.6, -122.3, 1] # Seattle id=1
36
+ points << [45.5, -122.8, 2] # Portland id=2
37
+ points << [40.7, -74.0, 3] # New York id=3
38
+ kd = Kdtree.new(points)
39
+
40
+ # which city is closest to San Francisco?
41
+ p kd.nearest(34.1, -118.2) # => 2
42
+ # which two cities are closest to San Francisco?
43
+ p kd.nearestk(34.1, -118.2, 2) # => [2, 1]
44
+ ```
45
+
46
+ Also, I made it possible to **persist** the tree to disk and load it later. That way you can calculate the tree offline and load it quickly at some future point. Loading a persisted tree w/ 1 millions points takes half a second, as opposed to the 3.5 second build time shown below. At Urbanspoon we persist the tree and rsync it out to other machines. For example:
18
47
 
19
48
  ```ruby
20
49
  File.open("treefile", "w") { |f| kd.persist(f) }
21
- ... later ...
50
+ # ... later ...
22
51
  kd2 = File.open("treefile") { |f| Kdtree.new(f) }
23
52
  ```
24
53
 
25
54
  ### Performance
26
55
 
27
- Kdtree is fast. How fast? Using a tree with 1 million points on my i5 2.8ghz:
56
+ Kdtree is fast. How fast? Using a tree with 1 million points on my M1:
28
57
 
29
58
  ```
30
- build 3.5s
31
- nearest point 0.000003s
32
- nearest 5 points 0.000004s
33
- nearest 50 points 0.000014s
34
- nearest 255 points 0.000063s
59
+ build (init) 0.96s
60
+ persist 0.000814s
61
+ read (init) 0.009236s
62
+
63
+ nearest point 0.000002s
64
+ nearest 5 points 0.000002s
65
+ nearest 50 points 0.000006s
66
+ nearest 255 points 0.000026s
35
67
  ```
36
68
 
37
69
  ### Limitations
38
70
 
39
- * No **editing** allowed! Once you construct a tree you&#8217;re stuck with it.
40
- * The tree is stored in **one big memory block**, 20 bytes per point. A tree with one million points will allocate a single 19mb block to store its nodes.
41
- * Persisted trees are **architecture dependent**, and may not work across different machines due to endian issues.
42
- * nearestk is limited to **255 results**
71
+ - No **editing** allowed! Once you construct a tree you're stuck with it.
72
+ - The tree is stored in **one big memory block**, 20 bytes per point. A tree with one million points will allocate a single 19mb block to store its nodes.
73
+ - Persisted trees are **architecture dependent**, and may not work across different machines due to endian issues.
74
+ - nearestk is limited to **255 results**
43
75
 
44
76
  ### Contributors
45
77
 
46
78
  Since this gem was originally released, several folks have contributed important patches:
47
79
 
48
- * @antifuchs (thread safety)
49
- * @evanphx (native cleanups, perf)
50
- * @ghazel (C89 compliance)
51
- * @mcerna (1.9 compat)
80
+ - @antifuchs (thread safety)
81
+ - @evanphx (native cleanups, perf)
82
+ - @ghazel (C89 compliance)
83
+ - @mcerna (1.9 compat)
52
84
 
53
85
  ### Changelog
54
86
 
55
- #### 0.3 (in progress, unreleased)
87
+ Note: This gem is stable, maintained and continues to work great with all modern versions of Ruby MRI. Our CI tests through Ruby 3.4. No need for new releases until something breaks!
88
+
89
+ #### 0.5 - May 2025
90
+
91
+ - justfile
92
+ - hygiene - updated deps, format/lint, modernize rakefile
93
+ - moved to ruby 3.x or higher, tested with ruby 3.4
94
+ - updated benchmark numbers (still real fast)
95
+
96
+ #### 0.4 - Mar 2017
97
+
98
+ - this is mostly housekeeping - test on more rubies, fix a few warnings
99
+
100
+ #### 0.3 - Oct 2012
56
101
 
57
- * Ruby 1.9.x compatibility (@mcerna and others)
58
- * renamed KDTree to the more idiomatic Kdtree
59
- * use IO methods directly instead of rooting around in rb_io
60
- * thread safe, no more statics (@antifuchs)
61
- * C90 compliance, no warnings (@ghazel)
62
- * native cleanups (@evanphx)
102
+ - Ruby 1.9.x compatibility (@mcerna and others)
103
+ - renamed KDTree to the more idiomatic Kdtree
104
+ - use IO methods directly instead of rooting around in rb_io
105
+ - thread safe, no more statics (@antifuchs)
106
+ - C90 compliance, no warnings (@ghazel)
107
+ - native cleanups (@evanphx)
63
108
 
64
109
  #### 0.2
65
110
 
66
111
  skipped this version to prevent confusion with other flavors of the gem
67
112
 
68
- #### 0.1
113
+ #### 0.1 - Jan 2010
69
114
 
70
- * Original release
115
+ - Original release
data/ext/kdtree/kdtree.c CHANGED
@@ -5,16 +5,14 @@
5
5
  //
6
6
 
7
7
  // the tree itself
8
- typedef struct kdtree_data
9
- {
8
+ typedef struct kdtree_data {
10
9
  int root;
11
10
  int len;
12
11
  struct kdtree_node *nodes;
13
12
  } kdtree_data;
14
13
 
15
14
  // a node in the tree
16
- typedef struct kdtree_node
17
- {
15
+ typedef struct kdtree_node {
18
16
  float x, y;
19
17
  int id;
20
18
  int left;
@@ -28,7 +26,7 @@ typedef struct kresult {
28
26
  } kresult;
29
27
 
30
28
  // helper macro for digging out our struct
31
- #define KDTREEP \
29
+ #define KDTREEP \
32
30
  struct kdtree_data *kdtreep; \
33
31
  Data_Get_Struct(kdtree, struct kdtree_data, kdtreep);
34
32
 
@@ -43,8 +41,10 @@ static VALUE kdtree_to_s(VALUE kdtree);
43
41
 
44
42
  // kdtree helpers
45
43
  static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth);
46
- static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth, int *n_index, float *n_dist);
47
- 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);
44
+ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth,
45
+ int *n_index, float *n_dist);
46
+ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float y, int k, int depth,
47
+ kresult *k_list, int *k_len, float *k_dist);
48
48
 
49
49
  // io helpers
50
50
  static void read_all(VALUE io, void *buf, int len);
@@ -59,16 +59,14 @@ static ID id_read, id_write, id_binmode;
59
59
  // implementation
60
60
  //
61
61
 
62
- static VALUE kdtree_alloc(VALUE klass)
63
- {
62
+ static VALUE kdtree_alloc(VALUE klass) {
64
63
  struct kdtree_data *kdtreep;
65
64
  VALUE obj = Data_Make_Struct(klass, struct kdtree_data, 0, kdtree_free, kdtreep);
66
65
  kdtreep->root = -1;
67
66
  return obj;
68
67
  }
69
68
 
70
- static void kdtree_free(struct kdtree_data *kdtreep)
71
- {
69
+ static void kdtree_free(struct kdtree_data *kdtreep) {
72
70
  if (kdtreep) {
73
71
  free(kdtreep->nodes);
74
72
  }
@@ -95,15 +93,14 @@ static void kdtree_free(struct kdtree_data *kdtreep)
95
93
  * kdtree. This makes it possible to build the tree in advance, persist it, and
96
94
  * start it up quickly later. See persist for more information.
97
95
  */
98
- static VALUE kdtree_initialize(VALUE kdtree, VALUE arg)
99
- {
96
+ static VALUE kdtree_initialize(VALUE kdtree, VALUE arg) {
100
97
  KDTREEP;
101
98
 
102
99
  if (TYPE(arg) == T_ARRAY) {
103
100
  // init from array of pints
104
101
  VALUE points = arg;
105
102
  int i;
106
- kdtreep->len = RARRAY_LEN(points);
103
+ kdtreep->len = (int)RARRAY_LEN(points);
107
104
  kdtreep->nodes = ALLOC_N(struct kdtree_node, kdtreep->len);
108
105
 
109
106
  for (i = 0; i < RARRAY_LEN(points); ++i) {
@@ -147,23 +144,20 @@ static VALUE kdtree_initialize(VALUE kdtree, VALUE arg)
147
144
  return kdtree;
148
145
  }
149
146
 
150
- static int comparex(const void *pa, const void *pb)
151
- {
152
- float a = ((const struct kdtree_node*)pa)->x;
153
- float b = ((const struct kdtree_node*)pb)->x;
147
+ static int comparex(const void *pa, const void *pb) {
148
+ float a = ((const struct kdtree_node *)pa)->x;
149
+ float b = ((const struct kdtree_node *)pb)->x;
154
150
  return (a < b) ? -1 : ((a > b) ? 1 : 0);
155
151
  }
156
152
 
157
- static int comparey(const void *pa, const void *pb)
158
- {
159
- float a = ((const struct kdtree_node*)pa)->y;
160
- float b = ((const struct kdtree_node*)pb)->y;
153
+ static int comparey(const void *pa, const void *pb) {
154
+ float a = ((const struct kdtree_node *)pa)->y;
155
+ float b = ((const struct kdtree_node *)pb)->y;
161
156
  return (a < b) ? -1 : ((a > b) ? 1 : 0);
162
157
  }
163
158
 
164
- static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth)
165
- {
166
- int(*compar)(const void *, const void *);
159
+ static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth) {
160
+ int (*compar)(const void *, const void *);
167
161
  struct kdtree_node *m;
168
162
  int median;
169
163
  if (max <= min) {
@@ -198,8 +192,7 @@ static int kdtree_build(struct kdtree_data *kdtreep, int min, int max, int depth
198
192
  * # which city is closest to Boston?
199
193
  * kd.nearest(42.4, -71.1) #=> 2
200
194
  */
201
- static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y)
202
- {
195
+ static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y) {
203
196
  int n_index;
204
197
  float n_dist;
205
198
  KDTREEP;
@@ -214,8 +207,8 @@ static VALUE kdtree_nearest(VALUE kdtree, VALUE x, VALUE y)
214
207
  return INT2NUM((kdtreep->nodes + n_index)->id);
215
208
  }
216
209
 
217
- static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth, int *n_index, float *n_dist)
218
- {
210
+ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y, int depth,
211
+ int *n_index, float *n_dist) {
219
212
  struct kdtree_node *n;
220
213
  float ad;
221
214
  int near, far;
@@ -234,11 +227,13 @@ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y
234
227
  //
235
228
 
236
229
  if (ad <= 0) {
237
- near = n->left; far = n->right;
230
+ near = n->left;
231
+ far = n->right;
238
232
  } else {
239
- near = n->right; far = n->left;
233
+ near = n->right;
234
+ far = n->left;
240
235
  }
241
- kdtree_nearest0(kdtreep, near, x, y, depth + 1, n_index, n_dist);
236
+ kdtree_nearest0(kdtreep, near, x, y, depth + 1, n_index, n_dist);
242
237
  if (ad * ad < *n_dist) {
243
238
  kdtree_nearest0(kdtreep, far, x, y, depth + 1, n_index, n_dist);
244
239
  }
@@ -278,11 +273,11 @@ static void kdtree_nearest0(struct kdtree_data *kdtreep, int i, float x, float y
278
273
  * kd = Kdtree.new(points)
279
274
  *
280
275
  * # which two cities are closest to San Francisco?
281
- * kd.nearest(34.1, -118.2) #=> [2, 1]
276
+ * kd.nearestk(34.1, -118.2, 2) #=> [2, 1]
282
277
  */
283
- static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k)
284
- {
285
- // note I leave an extra slot here at the end because of the way our binary insert works
278
+ static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k) {
279
+ // note I leave an extra slot here at the end because of the way our binary
280
+ // insert works
286
281
  kresult k_list[MAX_K + 1];
287
282
  int k_len = 0;
288
283
  float k_dist = INT_MAX;
@@ -296,7 +291,8 @@ static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k)
296
291
  } else if (ki > MAX_K) {
297
292
  ki = MAX_K;
298
293
  }
299
- kdtree_nearestk0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), ki, 0, k_list, &k_len, &k_dist);
294
+ kdtree_nearestk0(kdtreep, kdtreep->root, NUM2DBL(x), NUM2DBL(y), ki, 0, k_list, &k_len,
295
+ &k_dist);
300
296
 
301
297
  // convert result to ruby array
302
298
  ary = rb_ary_new();
@@ -306,8 +302,8 @@ static VALUE kdtree_nearestk(VALUE kdtree, VALUE x, VALUE y, VALUE k)
306
302
  return ary;
307
303
  }
308
304
 
309
- 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)
310
- {
305
+ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float y, int k, int depth,
306
+ kresult *k_list, int *k_len, float *k_dist) {
311
307
  struct kdtree_node *n;
312
308
  float ad;
313
309
  int near, far;
@@ -327,11 +323,13 @@ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float
327
323
  //
328
324
 
329
325
  if (ad <= 0) {
330
- near = n->left; far = n->right;
326
+ near = n->left;
327
+ far = n->right;
331
328
  } else {
332
- near = n->right; far = n->left;
329
+ near = n->right;
330
+ far = n->left;
333
331
  }
334
- kdtree_nearestk0(kdtreep, near, x, y, k, depth + 1, k_list, k_len, k_dist);
332
+ kdtree_nearestk0(kdtreep, near, x, y, k, depth + 1, k_list, k_len, k_dist);
335
333
  if (ad * ad < *k_dist) {
336
334
  kdtree_nearestk0(kdtreep, far, x, y, k, depth + 1, k_list, k_len, k_dist);
337
335
  }
@@ -404,9 +402,7 @@ static void kdtree_nearestk0(struct kdtree_data *kdtreep, int i, float x, float
404
402
  * # later, read the tree from disk
405
403
  * kd2 = File.open("treefile") { |f| Kdtree.new(f) }
406
404
  */
407
- static VALUE kdtree_persist(VALUE kdtree, VALUE io)
408
- {
409
- VALUE str;
405
+ static VALUE kdtree_persist(VALUE kdtree, VALUE io) {
410
406
  KDTREEP;
411
407
 
412
408
  if (!rb_respond_to(io, rb_intern("write"))) {
@@ -428,12 +424,11 @@ static VALUE kdtree_persist(VALUE kdtree, VALUE io)
428
424
  *
429
425
  * A string that tells you a bit about the tree.
430
426
  */
431
- static VALUE kdtree_to_s(VALUE kdtree)
432
- {
427
+ static VALUE kdtree_to_s(VALUE kdtree) {
433
428
  char buf[256];
434
429
  KDTREEP;
435
430
 
436
- sprintf(buf, "#<%s:%p nodes=%d>", rb_obj_classname(kdtree), (void*)kdtree, kdtreep->len);
431
+ sprintf(buf, "#<%s:%p nodes=%d>", rb_obj_classname(kdtree), (void *)kdtree, kdtreep->len);
437
432
  return rb_str_new(buf, strlen(buf));
438
433
  }
439
434
 
@@ -441,8 +436,7 @@ static VALUE kdtree_to_s(VALUE kdtree)
441
436
  // io helpers
442
437
  //
443
438
 
444
- static void read_all(VALUE io, void *buf, int len)
445
- {
439
+ static void read_all(VALUE io, void *buf, int len) {
446
440
  VALUE string = rb_funcall(io, id_read, 1, INT2NUM(len));
447
441
  if (NIL_P(string) || RSTRING_LEN(string) != len) {
448
442
  rb_raise(rb_eEOFError, "end of file reached");
@@ -450,8 +444,7 @@ static void read_all(VALUE io, void *buf, int len)
450
444
  memcpy(buf, RSTRING_PTR(string), len);
451
445
  }
452
446
 
453
- static void write_all(VALUE io, const void *buf, int len)
454
- {
447
+ static void write_all(VALUE io, const void *buf, int len) {
455
448
  rb_funcall(io, id_write, 1, rb_str_new(buf, len));
456
449
  }
457
450
 
@@ -481,14 +474,13 @@ static void write_all(VALUE io, const void *buf, int len)
481
474
  * # which city is closest to San Francisco?
482
475
  * kd.nearest(34.1, -118.2) #=> 2
483
476
  * # which two cities are closest to San Francisco?
484
- * kd.nearest(34.1, -118.2) #=> [2, 1]
477
+ * kd.nearestk(34.1, -118.2, 2) #=> [2, 1]
485
478
  *
486
479
  * For more information on kd trees, see:
487
480
  *
488
481
  * http://en.wikipedia.org/wiki/Kd-tree
489
482
  */
490
- void Init_kdtree()
491
- {
483
+ void Init_kdtree() {
492
484
  static VALUE clazz;
493
485
 
494
486
  clazz = rb_define_class("Kdtree", rb_cObject);
data/kdtree.gemspec CHANGED
@@ -1,22 +1,33 @@
1
1
  Gem::Specification.new do |s|
2
- s.name = "kdtree"
3
- s.version = "0.3"
2
+ s.name = "kdtree"
3
+ s.version = "0.5"
4
+ s.authors = ["Adam Doppelt"]
5
+ s.email = "amd@gurge.com"
4
6
 
5
- s.authors = ["Adam Doppelt"]
6
- s.email = ["amd@gurge.com"]
7
- s.homepage = "http://github.com/gurgeous/kdtree"
8
- s.summary = "Blazingly fast, native 2d kdtree."
9
- s.description = <<EOF
10
- A kdtree is a data structure that makes it possible to quickly solve
11
- the nearest neighbor problem. This is a native 2d kdtree suitable for
12
- production use with millions of points.
13
- EOF
7
+ s.summary = "Blazingly fast, native 2d kdtree."
8
+ s.homepage = "http://github.com/gurgeous/kdtree"
9
+ s.description = <<~EOF
10
+ A kdtree is a data structure that makes it possible to quickly solve
11
+ the nearest neighbor problem. This is a native 2d kdtree suitable for
12
+ production use with millions of points.
13
+ EOF
14
+ s.license = "MIT"
15
+ s.required_ruby_version = ">= 3.0.0"
16
+ s.metadata = {
17
+ "homepage_uri" => s.homepage,
18
+ "rubygems_mfa_required" => "true",
19
+ "source_code_uri" => s.homepage,
20
+ }
14
21
 
15
- s.rubyforge_project = "kdtree"
16
- s.add_development_dependency "rake-compiler"
22
+ s.files = %w[
23
+ ext/kdtree/extconf.rb
24
+ ext/kdtree/kdtree.c
25
+ kdtree.gemspec
26
+ lib/kdtree.rb
27
+ LICENSE
28
+ README.md
29
+ ]
17
30
 
18
- s.files = `git ls-files`.split("\n")
19
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
31
  s.extensions = ["ext/kdtree/extconf.rb"]
21
32
  s.require_paths = ["lib"]
22
33
  end
metadata CHANGED
@@ -1,85 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kdtree
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.3'
5
- prerelease:
4
+ version: '0.5'
6
5
  platform: ruby
7
6
  authors:
8
7
  - Adam Doppelt
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-10-17 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rake-compiler
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :development
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
- description: ! 'A kdtree is a data structure that makes it possible to quickly solve
31
-
11
+ date: 2025-05-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |
14
+ A kdtree is a data structure that makes it possible to quickly solve
32
15
  the nearest neighbor problem. This is a native 2d kdtree suitable for
33
-
34
16
  production use with millions of points.
35
-
36
- '
37
- email:
38
- - amd@gurge.com
17
+ email: amd@gurge.com
39
18
  executables: []
40
19
  extensions:
41
20
  - ext/kdtree/extconf.rb
42
21
  extra_rdoc_files: []
43
22
  files:
44
- - .gitignore
45
- - .travis.yml
46
- - Gemfile
47
23
  - LICENSE
48
24
  - README.md
49
- - Rakefile
50
25
  - ext/kdtree/extconf.rb
51
26
  - ext/kdtree/kdtree.c
52
27
  - kdtree.gemspec
53
28
  - lib/kdtree.rb
54
- - test/test_kdtree.rb
55
29
  homepage: http://github.com/gurgeous/kdtree
56
- licenses: []
57
- post_install_message:
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ homepage_uri: http://github.com/gurgeous/kdtree
34
+ rubygems_mfa_required: 'true'
35
+ source_code_uri: http://github.com/gurgeous/kdtree
36
+ post_install_message:
58
37
  rdoc_options: []
59
38
  require_paths:
60
39
  - lib
61
40
  required_ruby_version: !ruby/object:Gem::Requirement
62
- none: false
63
41
  requirements:
64
- - - ! '>='
42
+ - - ">="
65
43
  - !ruby/object:Gem::Version
66
- version: '0'
67
- segments:
68
- - 0
69
- hash: -3094601017742930682
44
+ version: 3.0.0
70
45
  required_rubygems_version: !ruby/object:Gem::Requirement
71
- none: false
72
46
  requirements:
73
- - - ! '>='
47
+ - - ">="
74
48
  - !ruby/object:Gem::Version
75
49
  version: '0'
76
- segments:
77
- - 0
78
- hash: -3094601017742930682
79
50
  requirements: []
80
- rubyforge_project: kdtree
81
- rubygems_version: 1.8.21
82
- signing_key:
83
- specification_version: 3
51
+ rubygems_version: 3.5.16
52
+ signing_key:
53
+ specification_version: 4
84
54
  summary: Blazingly fast, native 2d kdtree.
85
55
  test_files: []
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- *.gem
2
- Gemfile.lock
3
- lib/*.so
4
- tmp
data/.travis.yml DELETED
@@ -1,9 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.8.7
4
- - 1.9.2
5
- - 1.9.3
6
- - rbx-18mode
7
- - rbx-19mode
8
- # - ruby-head
9
- - ree
data/Gemfile DELETED
@@ -1,2 +0,0 @@
1
- source "http://rubygems.org"
2
- gemspec
data/Rakefile DELETED
@@ -1,41 +0,0 @@
1
- require "bundler/setup"
2
- require "rake/extensiontask"
3
- require "rake/testtask"
4
-
5
- # load the spec, we use it below
6
- spec = Gem::Specification.load("kdtree.gemspec")
7
-
8
- #
9
- # gem
10
- #
11
-
12
- task :build do
13
- system "gem build --quiet kdtree.gemspec"
14
- end
15
-
16
- task :install => :build do
17
- system "sudo gem install --quiet kdtree-#{spec.version}.gem"
18
- end
19
-
20
- task :release => :build do
21
- system "git tag -a #{spec.version} -m 'Tagging #{spec.version}'"
22
- system "git push --tags"
23
- system "gem push kdtree-#{spec.version}.gem"
24
- end
25
-
26
- #
27
- # rake-compiler
28
- #
29
-
30
- Rake::ExtensionTask.new("kdtree", spec)
31
-
32
-
33
- #
34
- # testing
35
- #
36
-
37
- Rake::TestTask.new(:test) do |test|
38
- test.libs << "test"
39
- end
40
- task :test => :compile
41
- task :default => :test
data/test/test_kdtree.rb DELETED
@@ -1,150 +0,0 @@
1
- require "benchmark"
2
- require "kdtree"
3
- require "tempfile"
4
- require "test/unit"
5
-
6
- #
7
- # create a tree
8
- #
9
-
10
- class KdtreeTest < Test::Unit::TestCase
11
- TMP = "#{Dir.tmpdir}/kdtree_test"
12
-
13
- def setup
14
- @points = (0...1000).map { |i| [rand_coord, rand_coord, i] }
15
- @kdtree = Kdtree.new(@points)
16
- end
17
-
18
- def teardown
19
- File.unlink(TMP) if File.exists?(TMP)
20
- end
21
-
22
- def test_nearest
23
- 100.times do
24
- pt = [rand_coord, rand_coord]
25
-
26
- # kdtree search
27
- id = @kdtree.nearest(pt[0], pt[1])
28
- kdpt = @points[id]
29
-
30
- # slow search
31
- sortpt = @points.sort_by { |i| distance(i, pt) }.first
32
-
33
- # assert
34
- kdd = distance(kdpt, pt)
35
- sortd = distance(sortpt, pt)
36
- assert((kdd - sortd).abs < 0.0000001, "kdtree didn't return the closest result")
37
- end
38
- end
39
-
40
- def test_nearestk
41
- 100.times do
42
- pt = [rand_coord, rand_coord]
43
-
44
- # kdtree search
45
- list = @kdtree.nearestk(pt[0], pt[1], 5)
46
- kdpt = @points[list.last]
47
-
48
- # slow search
49
- sortpt = @points.sort_by { |i| distance(i, pt) }[list.length - 1]
50
-
51
- # assert
52
- kdd = distance(kdpt, pt)
53
- sortd = distance(sortpt, pt)
54
- assert((kdd - sortd).abs < 0.0000001, "kdtree didn't return the closest result")
55
- end
56
- end
57
-
58
- def test_persist
59
- # write
60
- File.open(TMP, "w") { |f| @kdtree.persist(f) }
61
- # read
62
- kdtree2 = File.open(TMP, "r") { |f| Kdtree.new(f) }
63
-
64
- # now test some random points
65
- 100.times do
66
- pt = [rand_coord, rand_coord]
67
- id1 = @kdtree.nearest(*pt)
68
- id2 = kdtree2.nearest(*pt)
69
- assert(id1 == id2, "kdtree2 differed from kdtree")
70
- end
71
- end
72
-
73
- def test_bad_magic
74
- File.open(TMP, "w") { |f| f.puts "That ain't right" }
75
- assert_raise RuntimeError do
76
- File.open(TMP, "r") { |f| Kdtree.new(f) }
77
- end
78
- end
79
-
80
- def test_eof
81
- File.open(TMP, "w") { |f| @kdtree.persist(f) }
82
- bytes = File.read(TMP)
83
-
84
- [2, 10, 100].each do |len|
85
- File.open(TMP, "w") { |f| f.write(bytes[0, len]) }
86
- assert_raise EOFError do
87
- File.open(TMP, "r") { |f| Kdtree.new(f) }
88
- end
89
- end
90
- end
91
-
92
- def dont_test_speed
93
- sizes = [1, 100, 1000, 10000, 100000, 1000000]
94
- ks = [1, 5, 50, 255]
95
- sizes.each do |s|
96
- points = (0...s).map { |i| [rand_coord, rand_coord, i] }
97
-
98
- # build
99
- Benchmark.bm(17) do |bm|
100
- kdtree = nil
101
- bm.report "build" do
102
- kdtree = Kdtree.new(points)
103
- end
104
- bm.report "persist" do
105
- File.open(TMP, "w") { |f| kdtree.persist(f) }
106
- end
107
- bm.report "read" do
108
- File.open(TMP, "r") { |f| Kdtree.new(f) }
109
- end
110
-
111
- ks.each do |k|
112
- bm.report "100 queries (#{k})" do
113
- total = count = 0
114
- 100.times do
115
- tm = Time.now
116
- if k == 1
117
- kdtree.nearest(rand_coord, rand_coord)
118
- else
119
- kdtree.nearestk(rand_coord, rand_coord, k)
120
- end
121
- end
122
- end
123
- end
124
- end
125
- puts
126
- end
127
- end
128
-
129
- protected
130
-
131
- def distance(a, b)
132
- x, y = a[0] - b[0], a[1] - b[1]
133
- x * x + y * y
134
- end
135
-
136
- def rand_coord
137
- rand(0) * 10 - 5
138
- end
139
- end
140
-
141
- # running dont_test_speed on my i5 2.8ghz:
142
- #
143
- # user system total real
144
- # build 3.350000 0.020000 3.370000 ( 3.520528)
145
- # persist 0.150000 0.020000 0.170000 ( 0.301963)
146
- # read 0.280000 0.000000 0.280000 ( 0.432676)
147
- # 100 queries (1) 0.000000 0.000000 0.000000 ( 0.000319)
148
- # 100 queries (5) 0.000000 0.000000 0.000000 ( 0.000412)
149
- # 100 queries (50) 0.000000 0.000000 0.000000 ( 0.001417)
150
- # 100 queries (255) 0.000000 0.000000 0.000000 ( 0.006268)