rbtree 0.3.0 → 0.4.0
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 +7 -0
- data/LICENSE +1 -1
- data/MANIFEST +0 -1
- data/README +45 -68
- data/dict.c +60 -84
- data/dict.h +27 -3
- data/extconf.rb +10 -1
- data/rbtree.c +724 -437
- data/test.rb +311 -182
- metadata +37 -41
- data/ChangeLog +0 -505
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b2c9450418c5e8acbcf6dfe359cbd7f5495558ea
|
4
|
+
data.tar.gz: 3cc9df43495129924dc06ef7571309dc604b99d9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aba578f329b5ceb9dd07de643671d97ff16110bab46b1fbceec28424ec10a69898129f137bcfa382412f6d8580a31008fbb309e7e3098d50821b12881826937d
|
7
|
+
data.tar.gz: 6cbef7d7032c04e4a2183c4346418ebe2220de66309b5e3d41c275eee9e091a542a8c51ebd81948e8fa39a5aeb71ad81b2ae1fe81c9f4e6b88f619836a82500d
|
data/LICENSE
CHANGED
data/MANIFEST
CHANGED
data/README
CHANGED
@@ -1,42 +1,37 @@
|
|
1
|
-
=begin
|
2
|
-
|
3
1
|
= Ruby/RBTree
|
4
2
|
|
5
|
-
|
6
|
-
Red-Black Tree. The elements of RBTree are ordered and its interface
|
7
|
-
is the almost same as Hash, so simply you can consider RBTree sorted
|
8
|
-
Hash.
|
3
|
+
== Description
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
Hash is unordered the data structure is more effective than
|
15
|
-
Red-Black Tree as an associative collection.
|
5
|
+
A RBTree is a sorted associative collection that is implemented with a
|
6
|
+
Red-Black Tree. It maps keys to values like a Hash, but maintains its
|
7
|
+
elements in ascending key order. The interface is the almost identical
|
8
|
+
to that of Hash.
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
A Red-Black Tree is a kind of binary tree that automatically balances
|
11
|
+
by itself when a node is inserted or deleted. Lookup, insertion, and
|
12
|
+
deletion are performed in O(log N) time in the expected and worst
|
13
|
+
cases. On the other hand, the average case complexity of
|
14
|
+
lookup/insertion/deletion on a Hash is constant time. A Hash is
|
15
|
+
usually faster than a RBTree where ordering is unimportant.
|
23
16
|
|
24
|
-
The
|
25
|
-
|
17
|
+
The elements of a RBTree are sorted using the <=> method of their keys
|
18
|
+
or a Proc set by the readjust method. It means all keys should be
|
19
|
+
comparable with each other or a Proc that takes two keys should return
|
20
|
+
a negative integer, 0, or a positive integer as the first argument is
|
21
|
+
less than, equal to, or greater than the second one.
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
* shift, pop
|
30
|
-
* reverse_each
|
23
|
+
RBTree also provides a few additional methods to take advantage of the
|
24
|
+
ordering:
|
31
25
|
|
32
|
-
|
33
|
-
|
26
|
+
* lower_bound, upper_bound, bound
|
27
|
+
* first, last
|
28
|
+
* shift, pop
|
29
|
+
* reverse_each
|
34
30
|
|
35
|
-
|
31
|
+
== Example
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
duplications of keys but MultiRBTree does.
|
33
|
+
A RBTree cannot contain duplicate keys. Use MultiRBTree that is the
|
34
|
+
parent class of RBTree to store duplicate keys.
|
40
35
|
|
41
36
|
require "rbtree"
|
42
37
|
|
@@ -46,61 +41,43 @@ duplications of keys but MultiRBTree does.
|
|
46
41
|
rbtree.each do |k, v|
|
47
42
|
p [k, v]
|
48
43
|
end # => ["a", 20] ["b", 30] ["c", 10]
|
49
|
-
|
44
|
+
|
50
45
|
mrbtree = MultiRBTree["c", 10, "a", 20, "e", 30, "a", 40]
|
51
46
|
p mrbtree.lower_bound("b") # => ["c", 10]
|
52
47
|
mrbtree.bound("a", "d") do |k, v|
|
53
48
|
p [k, v]
|
54
49
|
end # => ["a", 20] ["a", 40] ["c", 10]
|
55
50
|
|
56
|
-
|
51
|
+
Note: a RBTree cannot be modified during iteration.
|
57
52
|
|
58
|
-
|
53
|
+
== Installation
|
59
54
|
|
60
|
-
|
55
|
+
Run the following command.
|
61
56
|
|
62
57
|
$ sudo gem install rbtree
|
63
58
|
|
64
|
-
|
65
|
-
|
66
|
-
* ((<"Ruby/RBTree 0.3.0"|URL:rbtree-0.3.0.tar.gz>))
|
67
|
-
|
68
|
-
and then
|
69
|
-
|
70
|
-
$ tar xzf rbtree-x.x.x.tar.gz
|
71
|
-
$ cd rbtree-x.x.x.tar.gz
|
72
|
-
$ ruby extconf.rb
|
73
|
-
$ make
|
74
|
-
$ sudo make site-install
|
75
|
-
|
76
|
-
== Test
|
59
|
+
== Changes
|
60
|
+
=== 0.4.0
|
77
61
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
62
|
+
* Fixed build failure with Ruby 2.0.0.
|
63
|
+
* \#bound now returns an enumerator if no block is given.
|
64
|
+
* \#select now returns a new MultiRBTree / RBTree.
|
65
|
+
* Fixed a bug where \#reject could return nil.
|
66
|
+
* \#to_s is now equivalent to \#inspect.
|
67
|
+
* \#each now passes a two elements array as an argument to the given block.
|
68
|
+
* Added new methods: \#default_proc=, \#flatten, \#keep_if, \#key, \#select! and \#to_h.
|
85
69
|
|
86
70
|
== License
|
87
71
|
|
88
|
-
MIT License. Copyright (c) 2002-
|
72
|
+
MIT License. Copyright (c) 2002-2013 OZAWA Takuma.
|
89
73
|
|
90
74
|
dict.c and dict.h are modified copies that are originally in Kazlib
|
91
|
-
written by Kaz Kylheku.
|
92
|
-
and dict.h for the
|
93
|
-
|
94
|
-
|
95
|
-
== Support
|
96
|
-
|
97
|
-
Bug fixes, suggestions and other feedbacks are welcomed. Please mail
|
98
|
-
me at burningdowntheopera at yahoo dot co dot jp.
|
75
|
+
1.20 written by Kaz Kylheku. Its license is similar to the MIT
|
76
|
+
license. See dict.c and dict.h for the details. The web page of Kazlib
|
77
|
+
is at http://www.kylheku.com/~kaz/kazlib.html.
|
99
78
|
|
100
|
-
==
|
79
|
+
== Contact
|
101
80
|
|
102
|
-
|
103
|
-
* ((<RubyForge: rbtree: Project Info|URL:http://rubyforge.org/projects/rbtree/>))
|
104
|
-
* ((<URL:http://www.geocities.co.jp/SiliconValley-PaloAlto/3388/rbtree/README.html>))
|
81
|
+
Run the following command.
|
105
82
|
|
106
|
-
|
83
|
+
$ ruby -e "puts 'YnVybmluZ2Rvd250aGVvcGVyYUB5YWhvby5jby5qcA==\n'.unpack('m')"
|
data/dict.c
CHANGED
@@ -25,10 +25,9 @@
|
|
25
25
|
#include <stdlib.h>
|
26
26
|
#include <stddef.h>
|
27
27
|
#include <assert.h>
|
28
|
+
#define DICT_IMPLEMENTATION
|
28
29
|
#include "dict.h"
|
29
30
|
|
30
|
-
#include <ruby.h>
|
31
|
-
|
32
31
|
#ifdef KAZLIB_RCSID
|
33
32
|
static const char rcsid[] = "$Id: dict.c,v 1.15 2005/10/06 05:16:35 kuma Exp $";
|
34
33
|
#endif
|
@@ -66,8 +65,6 @@ static const char rcsid[] = "$Id: dict.c,v 1.15 2005/10/06 05:16:35 kuma Exp $";
|
|
66
65
|
#define dict_nil(D) (&(D)->nilnode)
|
67
66
|
#define DICT_DEPTH_MAX 64
|
68
67
|
|
69
|
-
#define COMPARE(dict, key1, key2) dict->compare(key1, key2, dict->context)
|
70
|
-
|
71
68
|
static dnode_t *dnode_alloc(void *context);
|
72
69
|
static void dnode_free(dnode_t *node, void *context);
|
73
70
|
|
@@ -158,17 +155,17 @@ static int verify_bintree(dict_t *dict)
|
|
158
155
|
first = dict_first(dict);
|
159
156
|
|
160
157
|
if (dict->dupes) {
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
158
|
+
while (first && (next = dict_next(dict, first))) {
|
159
|
+
if (dict->compare(first->key, next->key, dict->context) > 0)
|
160
|
+
return 0;
|
161
|
+
first = next;
|
162
|
+
}
|
166
163
|
} else {
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
164
|
+
while (first && (next = dict_next(dict, first))) {
|
165
|
+
if (dict->compare(first->key, next->key, dict->context) >= 0)
|
166
|
+
return 0;
|
167
|
+
first = next;
|
168
|
+
}
|
172
169
|
}
|
173
170
|
return 1;
|
174
171
|
}
|
@@ -251,8 +248,8 @@ static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
|
|
251
248
|
|
252
249
|
dict_t *dict_create(dict_comp_t comp)
|
253
250
|
{
|
254
|
-
dict_t*
|
255
|
-
|
251
|
+
dict_t *new = malloc(sizeof *new);
|
252
|
+
|
256
253
|
if (new) {
|
257
254
|
new->compare = comp;
|
258
255
|
new->allocnode = dnode_alloc;
|
@@ -263,7 +260,7 @@ dict_t *dict_create(dict_comp_t comp)
|
|
263
260
|
new->nilnode.right = &new->nilnode;
|
264
261
|
new->nilnode.parent = &new->nilnode;
|
265
262
|
new->nilnode.color = dnode_black;
|
266
|
-
|
263
|
+
new->dupes = 0;
|
267
264
|
}
|
268
265
|
return new;
|
269
266
|
}
|
@@ -291,7 +288,7 @@ void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
|
|
291
288
|
void dict_destroy(dict_t *dict)
|
292
289
|
{
|
293
290
|
assert (dict_isempty(dict));
|
294
|
-
|
291
|
+
free(dict);
|
295
292
|
}
|
296
293
|
|
297
294
|
/*
|
@@ -424,9 +421,6 @@ int dict_similar(const dict_t *left, const dict_t *right)
|
|
424
421
|
if (left->context != right->context)
|
425
422
|
return 0;
|
426
423
|
|
427
|
-
/* if (left->dupes != right->dupes) */
|
428
|
-
/* return 0; */
|
429
|
-
|
430
424
|
return 1;
|
431
425
|
}
|
432
426
|
|
@@ -447,24 +441,24 @@ dnode_t *dict_lookup(dict_t *dict, const void *key)
|
|
447
441
|
/* simple binary search adapted for trees that contain duplicate keys */
|
448
442
|
|
449
443
|
while (root != nil) {
|
450
|
-
result =
|
444
|
+
result = dict->compare(key, root->key, dict->context);
|
451
445
|
if (result < 0)
|
452
446
|
root = root->left;
|
453
447
|
else if (result > 0)
|
454
448
|
root = root->right;
|
455
449
|
else {
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
450
|
+
if (!dict->dupes) { /* no duplicates, return match */
|
451
|
+
return root;
|
452
|
+
} else { /* could be dupes, find leftmost one */
|
453
|
+
do {
|
454
|
+
saved = root;
|
455
|
+
root = root->left;
|
456
|
+
while (root != nil && dict->compare(key, root->key, dict->context))
|
457
|
+
root = root->right;
|
458
|
+
} while (root != nil);
|
459
|
+
return saved;
|
460
|
+
}
|
461
|
+
}
|
468
462
|
}
|
469
463
|
|
470
464
|
return NULL;
|
@@ -482,21 +476,21 @@ dnode_t *dict_lower_bound(dict_t *dict, const void *key)
|
|
482
476
|
dnode_t *tentative = 0;
|
483
477
|
|
484
478
|
while (root != nil) {
|
485
|
-
int result =
|
486
|
-
|
479
|
+
int result = dict->compare(key, root->key, dict->context);
|
480
|
+
|
487
481
|
if (result > 0) {
|
488
482
|
root = root->right;
|
489
483
|
} else if (result < 0) {
|
490
484
|
tentative = root;
|
491
485
|
root = root->left;
|
492
486
|
} else {
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
487
|
+
if (!dict->dupes) {
|
488
|
+
return root;
|
489
|
+
} else {
|
490
|
+
tentative = root;
|
491
|
+
root = root->left;
|
492
|
+
}
|
493
|
+
}
|
500
494
|
}
|
501
495
|
|
502
496
|
return tentative;
|
@@ -514,7 +508,7 @@ dnode_t *dict_upper_bound(dict_t *dict, const void *key)
|
|
514
508
|
dnode_t *tentative = 0;
|
515
509
|
|
516
510
|
while (root != nil) {
|
517
|
-
int result =
|
511
|
+
int result = dict->compare(key, root->key, dict->context);
|
518
512
|
|
519
513
|
if (result < 0) {
|
520
514
|
root = root->left;
|
@@ -522,13 +516,13 @@ dnode_t *dict_upper_bound(dict_t *dict, const void *key)
|
|
522
516
|
tentative = root;
|
523
517
|
root = root->right;
|
524
518
|
} else {
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
519
|
+
if (!dict->dupes) {
|
520
|
+
return root;
|
521
|
+
} else {
|
522
|
+
tentative = root;
|
523
|
+
root = root->right;
|
524
|
+
}
|
525
|
+
}
|
532
526
|
}
|
533
527
|
|
534
528
|
return tentative;
|
@@ -555,12 +549,10 @@ int dict_insert(dict_t *dict, dnode_t *node, const void *key)
|
|
555
549
|
assert (!dnode_is_in_a_dict(node));
|
556
550
|
|
557
551
|
/* basic binary tree insert */
|
558
|
-
|
552
|
+
|
559
553
|
while (where != nil) {
|
560
554
|
parent = where;
|
561
|
-
result =
|
562
|
-
/* trap attempts at duplicate key insertion unless it's explicitly allowed */
|
563
|
-
|
555
|
+
result = dict->compare(key, where->key, dict->context);
|
564
556
|
if (!dict->dupes && result == 0) {
|
565
557
|
where->data = node->data;
|
566
558
|
return 0;
|
@@ -928,6 +920,13 @@ void dict_allow_dupes(dict_t *dict)
|
|
928
920
|
dict->dupes = 1;
|
929
921
|
}
|
930
922
|
|
923
|
+
#undef dict_count
|
924
|
+
#undef dict_isempty
|
925
|
+
#undef dict_isfull
|
926
|
+
#undef dnode_get
|
927
|
+
#undef dnode_put
|
928
|
+
#undef dnode_getkey
|
929
|
+
|
931
930
|
dictcount_t dict_count(dict_t *dict)
|
932
931
|
{
|
933
932
|
return dict->nodecount;
|
@@ -1036,16 +1035,16 @@ void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
|
|
1036
1035
|
{
|
1037
1036
|
dict_t *dict = load->dictptr;
|
1038
1037
|
dnode_t *nil = &load->nilnode;
|
1039
|
-
|
1038
|
+
|
1040
1039
|
assert (!dnode_is_in_a_dict(newnode));
|
1041
1040
|
assert (dict->nodecount < DICTCOUNT_T_MAX);
|
1042
1041
|
|
1043
1042
|
#ifndef NDEBUG
|
1044
1043
|
if (dict->nodecount > 0) {
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1044
|
+
if (dict->dupes)
|
1045
|
+
assert (dict->compare(nil->left->key, key, dict->context) <= 0);
|
1046
|
+
else
|
1047
|
+
assert (dict->compare(nil->left->key, key, dict->context) < 0);
|
1049
1048
|
}
|
1050
1049
|
#endif
|
1051
1050
|
|
@@ -1152,7 +1151,7 @@ void dict_merge(dict_t *dest, dict_t *source)
|
|
1152
1151
|
|
1153
1152
|
for (;;) {
|
1154
1153
|
if (leftnode != NULL && rightnode != NULL) {
|
1155
|
-
if (
|
1154
|
+
if (dest->compare(leftnode->key, rightnode->key, dest->context) < 0)
|
1156
1155
|
goto copyleft;
|
1157
1156
|
else
|
1158
1157
|
goto copyright;
|
@@ -1191,26 +1190,3 @@ void dict_merge(dict_t *dest, dict_t *source)
|
|
1191
1190
|
dict_clear(source);
|
1192
1191
|
dict_load_end(&load);
|
1193
1192
|
}
|
1194
|
-
|
1195
|
-
int dict_equal(dict_t* dict1, dict_t* dict2,
|
1196
|
-
dict_value_eql_t value_eql)
|
1197
|
-
{
|
1198
|
-
dnode_t* node1;
|
1199
|
-
dnode_t* node2;
|
1200
|
-
|
1201
|
-
if (dict_count(dict1) != dict_count(dict2))
|
1202
|
-
return 0;
|
1203
|
-
if (!dict_similar(dict1, dict2))
|
1204
|
-
return 0;
|
1205
|
-
|
1206
|
-
for (node1 = dict_first(dict1), node2 = dict_first(dict2);
|
1207
|
-
node1 != NULL && node2 != NULL;
|
1208
|
-
node1 = dict_next(dict1, node1), node2 = dict_next(dict2, node2)) {
|
1209
|
-
|
1210
|
-
if (COMPARE(dict1, node1->key, node2->key) != 0)
|
1211
|
-
return 0;
|
1212
|
-
if (!value_eql(node1->data, node2->data))
|
1213
|
-
return 0;
|
1214
|
-
}
|
1215
|
-
return 1;
|
1216
|
-
}
|
data/dict.h
CHANGED
@@ -26,6 +26,9 @@
|
|
26
26
|
#define DICT_H
|
27
27
|
|
28
28
|
#include <limits.h>
|
29
|
+
#ifdef KAZLIB_SIDEEFFECT_DEBUG
|
30
|
+
#include "sfx.h"
|
31
|
+
#endif
|
29
32
|
|
30
33
|
/*
|
31
34
|
* Blurb for inclusion into C++ translation units
|
@@ -45,21 +48,24 @@ typedef unsigned long dictcount_t;
|
|
45
48
|
typedef enum { dnode_red, dnode_black } dnode_color_t;
|
46
49
|
|
47
50
|
typedef struct dnode_t {
|
51
|
+
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
48
52
|
struct dnode_t *dict_left;
|
49
53
|
struct dnode_t *dict_right;
|
50
54
|
struct dnode_t *dict_parent;
|
51
55
|
dnode_color_t dict_color;
|
52
56
|
const void *dict_key;
|
53
57
|
void *dict_data;
|
58
|
+
#else
|
59
|
+
int dict_dummy;
|
60
|
+
#endif
|
54
61
|
} dnode_t;
|
55
62
|
|
56
63
|
typedef int (*dict_comp_t)(const void *, const void *, void *);
|
57
64
|
typedef dnode_t *(*dnode_alloc_t)(void *);
|
58
65
|
typedef void (*dnode_free_t)(dnode_t *, void *);
|
59
66
|
|
60
|
-
typedef int (*dict_value_eql_t)(const void *, const void *);
|
61
|
-
|
62
67
|
typedef struct dict_t {
|
68
|
+
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
63
69
|
dnode_t dict_nilnode;
|
64
70
|
dictcount_t dict_nodecount;
|
65
71
|
dict_comp_t dict_compare;
|
@@ -67,13 +73,20 @@ typedef struct dict_t {
|
|
67
73
|
dnode_free_t dict_freenode;
|
68
74
|
void *dict_context;
|
69
75
|
int dict_dupes;
|
76
|
+
#else
|
77
|
+
int dict_dummmy;
|
78
|
+
#endif
|
70
79
|
} dict_t;
|
71
80
|
|
72
81
|
typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
|
73
82
|
|
74
83
|
typedef struct dict_load_t {
|
84
|
+
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
75
85
|
dict_t *dict_dictptr;
|
76
86
|
dnode_t dict_nilnode;
|
87
|
+
#else
|
88
|
+
int dict_dummmy;
|
89
|
+
#endif
|
77
90
|
} dict_load_t;
|
78
91
|
|
79
92
|
extern dict_t *dict_create(dict_comp_t);
|
@@ -114,7 +127,18 @@ extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
|
|
114
127
|
extern void dict_load_end(dict_load_t *);
|
115
128
|
extern void dict_merge(dict_t *, dict_t *);
|
116
129
|
|
117
|
-
|
130
|
+
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
131
|
+
#ifdef KAZLIB_SIDEEFFECT_DEBUG
|
132
|
+
#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
|
133
|
+
#else
|
134
|
+
#define dict_isfull(D) ((D)->dict_nodecount == DICTCOUNT_T_MAX)
|
135
|
+
#endif
|
136
|
+
#define dict_count(D) ((D)->dict_nodecount)
|
137
|
+
#define dict_isempty(D) ((D)->dict_nodecount == 0)
|
138
|
+
#define dnode_get(N) ((N)->dict_data)
|
139
|
+
#define dnode_getkey(N) ((N)->dict_key)
|
140
|
+
#define dnode_put(N, X) ((N)->dict_data = (X))
|
141
|
+
#endif
|
118
142
|
|
119
143
|
#ifdef __cplusplus
|
120
144
|
}
|