rbtree-jruby 0.1.0 → 0.2.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.
- data/Gemfile.lock +1 -1
- data/README.md +11 -0
- data/VERSION +1 -1
- data/benchmark/bm.rb +26 -0
- data/benchmark/bm1.rb +30 -0
- data/benchmark/result.png +0 -0
- data/benchmark/result.txt +114 -0
- data/java/src/rbtree/ext/MultiRBTree.java +1170 -1080
- data/lib/rbtree/ext/multi_r_b_tree.jar +0 -0
- metadata +6 -8
- data/java/src/rbtree/ext/Color.java +0 -6
- data/java/src/rbtree/ext/NilNode.java +0 -24
- data/java/src/rbtree/ext/Node.java +0 -98
- data/lib/rbtree/ext/multi_red_black_tree.jar +0 -0
- data/lib/rbtree/ext/rbtree.jar +0 -0
- data/lib/rbtree/ext/red_black_tree.jar +0 -0
@@ -34,1150 +34,1240 @@ import java.util.List;
|
|
34
34
|
|
35
35
|
@JRubyClass(name = "MultiRBTree")
|
36
36
|
public class MultiRBTree extends RubyObject {
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
rbtree = (MultiRBTree) klass.allocate();
|
100
|
-
rbtree.update(context, (MultiRBTree) args[0], Block.NULL_BLOCK);
|
101
|
-
return rbtree;
|
102
|
-
}
|
103
|
-
IRubyObject tmp = TypeConverter.convertToTypeWithCheck(args[0], runtime.getHash(), "to_hash");
|
104
|
-
if (!tmp.isNil()) {
|
105
|
-
rbtree = (MultiRBTree) klass.allocate();
|
106
|
-
RubyHash hash = (RubyHash) tmp;
|
107
|
-
hash.visitAll(new RubyHash.Visitor() {
|
108
|
-
@Override
|
109
|
-
public void visit(IRubyObject key, IRubyObject val) {
|
110
|
-
rbtree.internalPut(context, key, val, false);
|
111
|
-
}
|
112
|
-
});
|
113
|
-
return rbtree;
|
114
|
-
}
|
115
|
-
tmp = TypeConverter.convertToTypeWithCheck(args[0], runtime.getArray(), "to_ary");
|
116
|
-
if (!tmp.isNil()) {
|
117
|
-
rbtree = (MultiRBTree) klass.allocate();
|
118
|
-
RubyArray arr = (RubyArray) tmp;
|
119
|
-
for (int i = 0, j = arr.getLength(); i < j; i++) {
|
120
|
-
IRubyObject v = TypeConverter.convertToTypeWithCheck(arr.entry(i), runtime.getArray(), "to_ary");
|
121
|
-
if (v.isNil()) continue;
|
122
|
-
IRubyObject key = runtime.getNil();
|
123
|
-
IRubyObject val = runtime.getNil();
|
124
|
-
switch(((RubyArray) v).getLength()) {
|
125
|
-
case 2:
|
126
|
-
val = ((RubyArray) v).entry(1);
|
127
|
-
case 1:
|
128
|
-
key = ((RubyArray) v).entry(0);
|
129
|
-
rbtree.internalPut(context, key, val, false);
|
130
|
-
}
|
131
|
-
}
|
132
|
-
return rbtree;
|
133
|
-
}
|
134
|
-
}
|
135
|
-
if (args.length % 2 != 0) throw runtime.newArgumentError("odd number of arguments");
|
37
|
+
private Node root = NilNode.getInstance();
|
38
|
+
private int size = 0;
|
39
|
+
private static final int PROCDEFAULT_HASH_F = 1 << 10;
|
40
|
+
private static final int DEFAULT_INSPECT_STR_SIZE = 20;
|
41
|
+
private IRubyObject ifNone;
|
42
|
+
private RubyProc cmpProc;
|
43
|
+
private boolean dupes;
|
44
|
+
|
45
|
+
public static RubyClass createMultiRBTreeClass(Ruby runtime) {
|
46
|
+
RubyClass rbtreeClass = runtime.defineClass("MultiRBTree", runtime.getObject(), RBTREE_ALLOCATOR);
|
47
|
+
rbtreeClass.setReifiedClass(MultiRBTree.class);
|
48
|
+
rbtreeClass.includeModule(runtime.getEnumerable());
|
49
|
+
rbtreeClass.setMarshal(RBTREE_MARSHAL);
|
50
|
+
rbtreeClass.defineAnnotatedMethods(MultiRBTree.class);
|
51
|
+
return rbtreeClass;
|
52
|
+
}
|
53
|
+
|
54
|
+
private static final ObjectAllocator RBTREE_ALLOCATOR = new ObjectAllocator() {
|
55
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
56
|
+
return new MultiRBTree(runtime, klazz);
|
57
|
+
}
|
58
|
+
};
|
59
|
+
|
60
|
+
public MultiRBTree(final Ruby ruby, RubyClass rubyClass) {
|
61
|
+
super(ruby, rubyClass);
|
62
|
+
this.dupes = getMetaClass().getRealClass().getName().equals("MultiRBTree");
|
63
|
+
this.ifNone = ruby.getNil();
|
64
|
+
}
|
65
|
+
|
66
|
+
@JRubyMethod(name = "initialize", optional = 1)
|
67
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) {
|
68
|
+
if (block.isGiven()) {
|
69
|
+
if (args.length > 0) raiseArgumeneError();
|
70
|
+
this.ifNone = getRuntime().newProc(Block.Type.PROC, block);
|
71
|
+
flags |= PROCDEFAULT_HASH_F;
|
72
|
+
} else {
|
73
|
+
Arity.checkArgumentCount(getRuntime(), args, 0, 1);
|
74
|
+
if (args.length == 1) this.ifNone = args[0];
|
75
|
+
}
|
76
|
+
return this;
|
77
|
+
}
|
78
|
+
|
79
|
+
private void raiseArgumeneError() {
|
80
|
+
throw getRuntime().newArgumentError("wrong number arguments");
|
81
|
+
}
|
82
|
+
|
83
|
+
@JRubyMethod(name = "clear")
|
84
|
+
public IRubyObject init() {
|
85
|
+
this.root = NilNode.getInstance();
|
86
|
+
this.size = 0;
|
87
|
+
return this;
|
88
|
+
}
|
89
|
+
|
90
|
+
@JRubyMethod(name = "[]", rest = true, meta = true)
|
91
|
+
public static IRubyObject create(final ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
|
92
|
+
RubyClass klass = (RubyClass) recv;
|
93
|
+
Ruby runtime = context.getRuntime();
|
94
|
+
final MultiRBTree rbtree;
|
95
|
+
if (args.length == 1) {
|
96
|
+
if (klass.getName().equals("RBTree") && args[0].getMetaClass().getRealClass().getName().equals("MultiRBTree"))
|
97
|
+
throw runtime.newTypeError("cannot convert MultiRBTree to RBTree");
|
98
|
+
if (args[0] instanceof MultiRBTree) {
|
136
99
|
rbtree = (MultiRBTree) klass.allocate();
|
137
|
-
|
138
|
-
rbtree.internalPut(context, args[i], args[i+1], false);
|
139
|
-
}
|
100
|
+
rbtree.update(context, (MultiRBTree) args[0], Block.NULL_BLOCK);
|
140
101
|
return rbtree;
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
fastASetCheckString(context, key, val);
|
152
|
-
return val;
|
153
|
-
}
|
154
|
-
@JRubyMethod(name = "[]=", required = 2, compat = RUBY1_9)
|
155
|
-
public IRubyObject op_aset19(ThreadContext context, IRubyObject key, IRubyObject val) {
|
156
|
-
fastASetCheckString19(context, key, val);
|
157
|
-
return val;
|
158
|
-
}
|
159
|
-
|
160
|
-
public final void fastASetCheckString(ThreadContext context, IRubyObject key, IRubyObject value) {
|
161
|
-
if (key instanceof RubyString) {
|
162
|
-
op_asetForString(context, (RubyString) key, value);
|
163
|
-
} else {
|
164
|
-
internalPut(context, key, value);
|
165
|
-
}
|
166
|
-
}
|
167
|
-
|
168
|
-
public final void fastASetCheckString19(ThreadContext context, IRubyObject key, IRubyObject value) {
|
169
|
-
if (key.getMetaClass().getRealClass() == context.runtime.getString()) {
|
170
|
-
op_asetForString(context, (RubyString) key, value);
|
171
|
-
} else {
|
172
|
-
internalPut(context, key, value);
|
173
|
-
}
|
174
|
-
}
|
175
|
-
|
176
|
-
protected void op_asetForString(ThreadContext context, RubyString key, IRubyObject value) {
|
177
|
-
Node node;
|
178
|
-
if (!dupes && (node = internalGet(context, (RubyObject) key)) != null) {
|
179
|
-
node.setValue(value);
|
180
|
-
} else {
|
181
|
-
checkIterating();
|
182
|
-
if (!key.isFrozen()) {
|
183
|
-
key = key.strDup(context.runtime);
|
184
|
-
key.setFrozen(true);
|
185
|
-
}
|
186
|
-
internalPut(context, key, value, false);
|
187
|
-
}
|
188
|
-
}
|
189
|
-
|
190
|
-
@JRubyMethod(name = "fetch", required = 1, optional = 1)
|
191
|
-
public IRubyObject rbtree_fetch(ThreadContext context, IRubyObject[] args, Block block) {
|
192
|
-
if (block.isGiven() && args.length == 2) {
|
193
|
-
getRuntime().getWarnings().warn("block supersedes default value argument");
|
194
|
-
}
|
195
|
-
Node node = internalGet(context, (RubyObject) args[0]);
|
196
|
-
if (node != null)
|
197
|
-
return node.getValue();
|
198
|
-
if (block.isGiven())
|
199
|
-
return block.yield(context, args[0]);
|
200
|
-
if (args.length == 1)
|
201
|
-
throw getRuntime().newIndexError("key not found");
|
202
|
-
return args[1];
|
203
|
-
}
|
204
|
-
|
205
|
-
@JRubyMethod(name = "index")
|
206
|
-
public IRubyObject rbtree_index(final ThreadContext context, final IRubyObject value) {
|
207
|
-
try {
|
208
|
-
iteratorVisitAll(new Visitor() {
|
209
|
-
public void visit(IRubyObject key, IRubyObject val) {
|
210
|
-
if (value.eql(val)) {
|
211
|
-
throw new FoundKey(key);
|
212
|
-
}
|
213
|
-
}
|
214
|
-
});
|
215
|
-
return null;
|
216
|
-
} catch (FoundKey found) {
|
217
|
-
return found.key;
|
218
|
-
}
|
219
|
-
}
|
220
|
-
|
221
|
-
private void checkCompatible(Ruby runtime, IRubyObject other) {
|
222
|
-
if (!(other instanceof MultiRBTree))
|
223
|
-
throw runtime.newTypeError(String.format("wrong argument type %s (expected %s)", other.getMetaClass().getRealClass().getName(), "MultiRBTree"));
|
224
|
-
if (getMetaClass().getRealClass().getName().equals("RBTree") && other.getMetaClass().getRealClass().getName().equals("MultiRBTree"))
|
225
|
-
throw runtime.newTypeError("cannot convert MultiRBTree to RBTree");
|
226
|
-
}
|
227
|
-
|
228
|
-
@JRubyMethod(name = {"update", "merge!"})
|
229
|
-
public IRubyObject update(ThreadContext context, IRubyObject other, Block block) {
|
230
|
-
Ruby runtime = getRuntime();
|
231
|
-
checkCompatible(runtime, other);
|
232
|
-
MultiRBTree otherTree = (MultiRBTree) other;
|
233
|
-
for (Node node = otherTree.minimum(); !node.isNull(); node = otherTree.successor(node)) {
|
234
|
-
if (block.isGiven()) {
|
235
|
-
op_aset(context, node.getKey(), block.yieldSpecific(context, node.getKey(), op_aref(context, node.getKey()), node.getValue()));
|
236
|
-
} else {
|
237
|
-
op_aset(context, node.getKey(), node.getValue());
|
238
|
-
}
|
239
|
-
}
|
240
|
-
return this;
|
241
|
-
}
|
242
|
-
|
243
|
-
@JRubyMethod
|
244
|
-
public IRubyObject merge(final ThreadContext context, final IRubyObject other) {
|
245
|
-
Ruby runtime = getRuntime();
|
246
|
-
// TODO should allow RubyHash
|
247
|
-
if (!(other instanceof MultiRBTree)) {
|
248
|
-
runtime.newArgumentError(String.format("wrong argument type %s (expected %s)", other.getMetaClass().getRealClass().getName(), "MultiRBTree"));
|
249
|
-
}
|
250
|
-
|
251
|
-
MultiRBTree result = (MultiRBTree) dup();
|
252
|
-
MultiRBTree otherTree = (MultiRBTree) other;
|
253
|
-
for (Node node = otherTree.minimum(); !node.isNull(); node = otherTree.successor(node)) {
|
254
|
-
result.op_aset(context, node.getKey(), node.getValue());
|
255
|
-
}
|
256
|
-
return result;
|
257
|
-
}
|
258
|
-
|
259
|
-
@JRubyMethod(name = {"has_key?", "key?", "include?", "member?"})
|
260
|
-
public IRubyObject has_key_p(final ThreadContext context, IRubyObject key) {
|
261
|
-
return internalGet(context, (RubyObject) key) == null ? getRuntime().getFalse() : getRuntime().getTrue();
|
262
|
-
}
|
263
|
-
|
264
|
-
private boolean hasValue(final ThreadContext context, final IRubyObject value) {
|
265
|
-
try {
|
266
|
-
visitAll(new Visitor() {
|
267
|
-
public void visit(IRubyObject key, IRubyObject val) {
|
268
|
-
if (equalInternal(context, val, value)) throw FOUND;
|
269
|
-
}
|
270
|
-
});
|
271
|
-
return false;
|
272
|
-
} catch (Found found) {
|
273
|
-
return true;
|
274
|
-
}
|
275
|
-
}
|
276
|
-
|
277
|
-
@JRubyMethod(name = {"has_value?", "value?"})
|
278
|
-
public IRubyObject has_value_p(final ThreadContext context, final IRubyObject value) {
|
279
|
-
return getRuntime().newBoolean(hasValue(context, value));
|
280
|
-
}
|
281
|
-
|
282
|
-
@JRubyMethod(name = "keys")
|
283
|
-
public IRubyObject keys() {
|
284
|
-
final RubyArray keys = getRuntime().newArray(size);
|
285
|
-
visitAll(new Visitor() {
|
286
|
-
public void visit(IRubyObject key, IRubyObject val) {
|
287
|
-
keys.append(key);
|
288
|
-
}
|
289
|
-
});
|
290
|
-
return keys;
|
291
|
-
}
|
292
|
-
|
293
|
-
@JRubyMethod(name = "values")
|
294
|
-
public IRubyObject values() {
|
295
|
-
final RubyArray values = getRuntime().newArray(size);
|
296
|
-
visitAll(new Visitor() {
|
297
|
-
public void visit(IRubyObject key, IRubyObject val) {
|
298
|
-
values.append(val);
|
299
|
-
}
|
300
|
-
});
|
301
|
-
return values;
|
302
|
-
}
|
303
|
-
|
304
|
-
@JRubyMethod(name = "to_hash")
|
305
|
-
public IRubyObject to_hash() {
|
306
|
-
Ruby runtime = getRuntime();
|
307
|
-
if (getMetaClass().getRealClass().getName().equals("MultiRBTree"))
|
308
|
-
throw runtime.newTypeError("cannot convert MultiRBTree to Hash");
|
309
|
-
final RubyHash hash = new RubyHash(runtime, runtime.getHash());
|
310
|
-
hash.default_value_set(ifNone);
|
311
|
-
hash.setFlag(flags, true);
|
312
|
-
visitAll(new Visitor() {
|
313
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
314
|
-
hash.fastASet(key, value);
|
315
|
-
}
|
102
|
+
}
|
103
|
+
IRubyObject tmp = TypeConverter.convertToTypeWithCheck(args[0], runtime.getHash(), "to_hash");
|
104
|
+
if (!tmp.isNil()) {
|
105
|
+
rbtree = (MultiRBTree) klass.allocate();
|
106
|
+
RubyHash hash = (RubyHash) tmp;
|
107
|
+
hash.visitAll(new RubyHash.Visitor() {
|
108
|
+
@Override
|
109
|
+
public void visit(IRubyObject key, IRubyObject val) {
|
110
|
+
rbtree.internalPut(context, key, val, false);
|
111
|
+
}
|
316
112
|
});
|
317
|
-
return
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
} else if (args[0].isNil()) {
|
336
|
-
cmpfunc = null;
|
337
|
-
} else {
|
338
|
-
throw getRuntime().newTypeError(String.format("wrong argument type %s (expected %s)", args[0].getMetaClass().getRealClass().getName(), "Proc"));
|
339
|
-
}
|
340
|
-
}
|
341
|
-
MultiRBTree self = (MultiRBTree) this.dup();
|
342
|
-
try {
|
343
|
-
replaceInternal(context, self, cmpfunc);
|
344
|
-
} catch (RaiseException e) {
|
345
|
-
replaceInternal(context, self, oldProc);
|
346
|
-
throw e;
|
347
|
-
}
|
348
|
-
return this;
|
349
|
-
}
|
350
|
-
|
351
|
-
@JRubyMethod(name = "default=")
|
352
|
-
public IRubyObject setDefaultVal(ThreadContext context, IRubyObject defaultValue) {
|
353
|
-
ifNone = defaultValue;
|
354
|
-
flags &= ~PROCDEFAULT_HASH_F;
|
355
|
-
|
356
|
-
return ifNone;
|
357
|
-
}
|
358
|
-
|
359
|
-
@JRubyMethod(name = "default")
|
360
|
-
public IRubyObject default_value_get(ThreadContext context) {
|
361
|
-
if ((flags & PROCDEFAULT_HASH_F) != 0) {
|
362
|
-
return getRuntime().getNil();
|
363
|
-
}
|
364
|
-
return ifNone;
|
365
|
-
}
|
366
|
-
@JRubyMethod(name = "default")
|
367
|
-
public IRubyObject default_value_get(ThreadContext context, IRubyObject arg) {
|
368
|
-
if ((flags & PROCDEFAULT_HASH_F) != 0) {
|
369
|
-
return RuntimeHelpers.invoke(context, ifNone, "call", this, arg);
|
113
|
+
return rbtree;
|
114
|
+
}
|
115
|
+
tmp = TypeConverter.convertToTypeWithCheck(args[0], runtime.getArray(), "to_ary");
|
116
|
+
if (!tmp.isNil()) {
|
117
|
+
rbtree = (MultiRBTree) klass.allocate();
|
118
|
+
RubyArray arr = (RubyArray) tmp;
|
119
|
+
for (int i = 0, j = arr.getLength(); i < j; i++) {
|
120
|
+
IRubyObject v = TypeConverter.convertToTypeWithCheck(arr.entry(i), runtime.getArray(), "to_ary");
|
121
|
+
if (v.isNil()) continue;
|
122
|
+
IRubyObject key = runtime.getNil();
|
123
|
+
IRubyObject val = runtime.getNil();
|
124
|
+
switch (((RubyArray) v).getLength()) {
|
125
|
+
case 2:
|
126
|
+
val = ((RubyArray) v).entry(1);
|
127
|
+
case 1:
|
128
|
+
key = ((RubyArray) v).entry(0);
|
129
|
+
rbtree.internalPut(context, key, val, false);
|
130
|
+
}
|
370
131
|
}
|
371
|
-
return
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
132
|
+
return rbtree;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
if (args.length % 2 != 0) throw runtime.newArgumentError("odd number of arguments");
|
136
|
+
rbtree = (MultiRBTree) klass.allocate();
|
137
|
+
for (int i = 0; i < args.length; i += 2) {
|
138
|
+
rbtree.internalPut(context, args[i], args[i + 1], false);
|
139
|
+
}
|
140
|
+
return rbtree;
|
141
|
+
}
|
142
|
+
|
143
|
+
@JRubyMethod(name = "[]", required = 1)
|
144
|
+
public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
|
145
|
+
Node node = internalGet(context, (RubyObject) key);
|
146
|
+
return node == null ? callMethod(context, "default", key) : node.value;
|
147
|
+
}
|
148
|
+
|
149
|
+
@JRubyMethod(name = "[]=", required = 2, compat = RUBY1_8)
|
150
|
+
public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject val) {
|
151
|
+
fastASetCheckString(context, key, val);
|
152
|
+
return val;
|
153
|
+
}
|
154
|
+
|
155
|
+
@JRubyMethod(name = "[]=", required = 2, compat = RUBY1_9)
|
156
|
+
public IRubyObject op_aset19(ThreadContext context, IRubyObject key, IRubyObject val) {
|
157
|
+
fastASetCheckString19(context, key, val);
|
158
|
+
return val;
|
159
|
+
}
|
160
|
+
|
161
|
+
public final void fastASetCheckString(ThreadContext context, IRubyObject key, IRubyObject value) {
|
162
|
+
if (key instanceof RubyString) {
|
163
|
+
op_asetForString(context, (RubyString) key, value);
|
164
|
+
} else {
|
165
|
+
internalPut(context, key, value);
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
public final void fastASetCheckString19(ThreadContext context, IRubyObject key, IRubyObject value) {
|
170
|
+
if (key.getMetaClass().getRealClass() == context.runtime.getString()) {
|
171
|
+
op_asetForString(context, (RubyString) key, value);
|
172
|
+
} else {
|
173
|
+
internalPut(context, key, value);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
protected void op_asetForString(ThreadContext context, RubyString key, IRubyObject value) {
|
178
|
+
Node node;
|
179
|
+
if (!dupes && (node = internalGet(context, (RubyObject) key)) != null) {
|
180
|
+
node.setValue(value);
|
181
|
+
} else {
|
182
|
+
checkIterating();
|
183
|
+
if (!key.isFrozen()) {
|
184
|
+
key = key.strDup(context.runtime);
|
185
|
+
key.setFrozen(true);
|
186
|
+
}
|
187
|
+
internalPut(context, key, value, false);
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
@JRubyMethod(name = "fetch", required = 1, optional = 1)
|
192
|
+
public IRubyObject rbtree_fetch(ThreadContext context, IRubyObject[] args, Block block) {
|
193
|
+
if (block.isGiven() && args.length == 2) {
|
194
|
+
getRuntime().getWarnings().warn("block supersedes default value argument");
|
195
|
+
}
|
196
|
+
Node node = internalGet(context, (RubyObject) args[0]);
|
197
|
+
if (node != null)
|
198
|
+
return node.value;
|
199
|
+
if (block.isGiven())
|
200
|
+
return block.yield(context, args[0]);
|
201
|
+
if (args.length == 1)
|
202
|
+
throw getRuntime().newIndexError("key not found");
|
203
|
+
return args[1];
|
204
|
+
}
|
205
|
+
|
206
|
+
@JRubyMethod(name = "index")
|
207
|
+
public IRubyObject rbtree_index(final ThreadContext context, final IRubyObject value) {
|
208
|
+
try {
|
209
|
+
iteratorVisitAll(new Visitor() {
|
210
|
+
public void visit(IRubyObject key, IRubyObject val) {
|
211
|
+
if (value.eql(val)) {
|
212
|
+
throw new FoundKey(key);
|
213
|
+
}
|
395
214
|
}
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
215
|
+
});
|
216
|
+
return null;
|
217
|
+
} catch (FoundKey found) {
|
218
|
+
return found.key;
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
private void checkCompatible(Ruby runtime, IRubyObject other) {
|
223
|
+
if (!(other instanceof MultiRBTree))
|
224
|
+
throw runtime.newTypeError(String.format("wrong argument type %s (expected %s)", other.getMetaClass().getRealClass().getName(), "MultiRBTree"));
|
225
|
+
if (getMetaClass().getRealClass().getName().equals("RBTree") && other.getMetaClass().getRealClass().getName().equals("MultiRBTree"))
|
226
|
+
throw runtime.newTypeError("cannot convert MultiRBTree to RBTree");
|
227
|
+
}
|
228
|
+
|
229
|
+
@JRubyMethod(name = {"update", "merge!"})
|
230
|
+
public IRubyObject update(ThreadContext context, IRubyObject other, Block block) {
|
231
|
+
Ruby runtime = getRuntime();
|
232
|
+
checkCompatible(runtime, other);
|
233
|
+
MultiRBTree otherTree = (MultiRBTree) other;
|
234
|
+
for (Node node = otherTree.minimum(); !node.isNull(); node = otherTree.successor(node)) {
|
235
|
+
if (block.isGiven()) {
|
236
|
+
op_aset(context, node.key, block.yieldSpecific(context, node.key, op_aref(context, node.key), node.value));
|
237
|
+
} else {
|
238
|
+
op_aset(context, node.key, node.value);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
return this;
|
242
|
+
}
|
243
|
+
|
244
|
+
@JRubyMethod
|
245
|
+
public IRubyObject merge(final ThreadContext context, final IRubyObject other) {
|
246
|
+
Ruby runtime = getRuntime();
|
247
|
+
// TODO should allow RubyHash
|
248
|
+
if (!(other instanceof MultiRBTree)) {
|
249
|
+
runtime.newArgumentError(String.format("wrong argument type %s (expected %s)", other.getMetaClass().getRealClass().getName(), "MultiRBTree"));
|
250
|
+
}
|
251
|
+
|
252
|
+
MultiRBTree result = (MultiRBTree) dup();
|
253
|
+
MultiRBTree otherTree = (MultiRBTree) other;
|
254
|
+
for (Node node = otherTree.minimum(); !node.isNull(); node = otherTree.successor(node)) {
|
255
|
+
result.op_aset(context, node.key, node.value);
|
256
|
+
}
|
257
|
+
return result;
|
258
|
+
}
|
259
|
+
|
260
|
+
@JRubyMethod(name = {"has_key?", "key?", "include?", "member?"})
|
261
|
+
public IRubyObject has_key_p(final ThreadContext context, IRubyObject key) {
|
262
|
+
return internalGet(context, (RubyObject) key) == null ? getRuntime().getFalse() : getRuntime().getTrue();
|
263
|
+
}
|
264
|
+
|
265
|
+
private boolean hasValue(final ThreadContext context, final IRubyObject value) {
|
266
|
+
try {
|
267
|
+
visitAll(new Visitor() {
|
268
|
+
public void visit(IRubyObject key, IRubyObject val) {
|
269
|
+
if (equalInternal(context, val, value)) throw FOUND;
|
433
270
|
}
|
434
|
-
|
271
|
+
});
|
272
|
+
return false;
|
273
|
+
} catch (Found found) {
|
274
|
+
return true;
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
@JRubyMethod(name = {"has_value?", "value?"})
|
279
|
+
public IRubyObject has_value_p(final ThreadContext context, final IRubyObject value) {
|
280
|
+
return getRuntime().newBoolean(hasValue(context, value));
|
281
|
+
}
|
282
|
+
|
283
|
+
@JRubyMethod(name = "keys")
|
284
|
+
public IRubyObject keys() {
|
285
|
+
final RubyArray keys = getRuntime().newArray(size);
|
286
|
+
visitAll(new Visitor() {
|
287
|
+
public void visit(IRubyObject key, IRubyObject val) {
|
288
|
+
keys.append(key);
|
289
|
+
}
|
290
|
+
});
|
291
|
+
return keys;
|
292
|
+
}
|
293
|
+
|
294
|
+
@JRubyMethod(name = "values")
|
295
|
+
public IRubyObject values() {
|
296
|
+
final RubyArray values = getRuntime().newArray(size);
|
297
|
+
visitAll(new Visitor() {
|
298
|
+
public void visit(IRubyObject key, IRubyObject val) {
|
299
|
+
values.append(val);
|
300
|
+
}
|
301
|
+
});
|
302
|
+
return values;
|
303
|
+
}
|
304
|
+
|
305
|
+
@JRubyMethod(name = "to_hash")
|
306
|
+
public IRubyObject to_hash() {
|
307
|
+
Ruby runtime = getRuntime();
|
308
|
+
if (getMetaClass().getRealClass().getName().equals("MultiRBTree"))
|
309
|
+
throw runtime.newTypeError("cannot convert MultiRBTree to Hash");
|
310
|
+
final RubyHash hash = new RubyHash(runtime, runtime.getHash());
|
311
|
+
hash.default_value_set(ifNone);
|
312
|
+
hash.setFlag(flags, true);
|
313
|
+
visitAll(new Visitor() {
|
314
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
315
|
+
hash.fastASet(key, value);
|
316
|
+
}
|
317
|
+
});
|
318
|
+
return hash;
|
319
|
+
}
|
320
|
+
|
321
|
+
@JRubyMethod
|
322
|
+
public IRubyObject to_rbtree() {
|
323
|
+
return this;
|
324
|
+
}
|
325
|
+
|
326
|
+
@JRubyMethod(name = "readjust", optional = 1)
|
327
|
+
public IRubyObject readjust(ThreadContext context, IRubyObject[] args, Block block) {
|
328
|
+
RubyProc oldProc = cmpProc;
|
329
|
+
RubyProc cmpfunc = null;
|
330
|
+
if (block.isGiven()) {
|
331
|
+
if (args.length > 0) raiseArgumeneError();
|
332
|
+
cmpfunc = getRuntime().newProc(Block.Type.PROC, block);
|
333
|
+
} else if (args.length == 1) {
|
334
|
+
if (args[0] instanceof RubyProc) {
|
335
|
+
cmpfunc = (RubyProc) args[0];
|
336
|
+
} else if (args[0].isNil()) {
|
337
|
+
cmpfunc = null;
|
338
|
+
} else {
|
339
|
+
throw getRuntime().newTypeError(String.format("wrong argument type %s (expected %s)", args[0].getMetaClass().getRealClass().getName(), "Proc"));
|
340
|
+
}
|
341
|
+
}
|
342
|
+
MultiRBTree self = (MultiRBTree) this.dup();
|
343
|
+
try {
|
344
|
+
replaceInternal(context, self, cmpfunc);
|
345
|
+
} catch (RaiseException e) {
|
346
|
+
replaceInternal(context, self, oldProc);
|
347
|
+
throw e;
|
348
|
+
}
|
349
|
+
return this;
|
350
|
+
}
|
351
|
+
|
352
|
+
@JRubyMethod(name = "default=")
|
353
|
+
public IRubyObject setDefaultVal(ThreadContext context, IRubyObject defaultValue) {
|
354
|
+
ifNone = defaultValue;
|
355
|
+
flags &= ~PROCDEFAULT_HASH_F;
|
356
|
+
|
357
|
+
return ifNone;
|
358
|
+
}
|
359
|
+
|
360
|
+
@JRubyMethod(name = "default")
|
361
|
+
public IRubyObject default_value_get(ThreadContext context) {
|
362
|
+
if ((flags & PROCDEFAULT_HASH_F) != 0) {
|
363
|
+
return getRuntime().getNil();
|
364
|
+
}
|
365
|
+
return ifNone;
|
366
|
+
}
|
367
|
+
|
368
|
+
@JRubyMethod(name = "default")
|
369
|
+
public IRubyObject default_value_get(ThreadContext context, IRubyObject arg) {
|
370
|
+
if ((flags & PROCDEFAULT_HASH_F) != 0) {
|
371
|
+
return RuntimeHelpers.invoke(context, ifNone, "call", this, arg);
|
372
|
+
}
|
373
|
+
return ifNone;
|
374
|
+
}
|
375
|
+
|
376
|
+
@JRubyMethod(name = "default_proc")
|
377
|
+
public IRubyObject getDefaultProc() {
|
378
|
+
return (flags & PROCDEFAULT_HASH_F) != 0 ? ifNone : getRuntime().getNil();
|
379
|
+
}
|
380
|
+
|
381
|
+
@JRubyMethod(name = "cmp_proc")
|
382
|
+
public IRubyObject getCmpProc() {
|
383
|
+
return this.cmpProc;
|
384
|
+
}
|
385
|
+
|
386
|
+
public MultiRBTree internalPut(ThreadContext context, IRubyObject key, IRubyObject value) {
|
387
|
+
return internalPut(context, key, value, true);
|
388
|
+
}
|
389
|
+
|
390
|
+
public MultiRBTree internalPut(ThreadContext context, IRubyObject key, IRubyObject value, boolean checkExisting) {
|
391
|
+
if (!dupes && checkExisting) {
|
392
|
+
Node node = internalGet(context, (RubyObject) key);
|
393
|
+
if (node != null) {
|
394
|
+
node.setValue(value);
|
435
395
|
return this;
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
396
|
+
}
|
397
|
+
}
|
398
|
+
|
399
|
+
Node x = new Node((RubyObject) key, value);
|
400
|
+
internalPutHelper(context, x);
|
401
|
+
while (x != this.root && x.parent.isRed()) {
|
402
|
+
if (x.parent == x.parent.parent.left) {
|
403
|
+
Node y = x.parent.parent.right;
|
404
|
+
if (!y.isNull() && y.isRed()) {
|
405
|
+
x.parent.setBlack();
|
406
|
+
y.setBlack();
|
407
|
+
x.parent.parent.setRed();
|
408
|
+
x = x.parent.parent;
|
444
409
|
} else {
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
410
|
+
if (x.isRight()) {
|
411
|
+
x = x.parent;
|
412
|
+
leftRotate(x);
|
413
|
+
}
|
414
|
+
x.parent.setBlack();
|
415
|
+
x.parent.parent.setRed();
|
416
|
+
rightRotate(x.parent.parent);
|
450
417
|
}
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
418
|
+
} else {
|
419
|
+
Node y = x.parent.parent.left;
|
420
|
+
if (!y.isNull() && y.isRed()) {
|
421
|
+
x.parent.setBlack();
|
422
|
+
y.setBlack();
|
423
|
+
x.parent.parent.setRed();
|
424
|
+
x = x.parent.parent;
|
425
|
+
} else {
|
426
|
+
if (x.isLeft()) {
|
427
|
+
x = x.parent;
|
428
|
+
rightRotate(x);
|
429
|
+
}
|
430
|
+
x.parent.setBlack();
|
431
|
+
x.parent.parent.setRed();
|
432
|
+
leftRotate(x.parent.parent);
|
464
433
|
}
|
465
|
-
|
466
|
-
}
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
434
|
+
}
|
435
|
+
}
|
436
|
+
root.setBlack();
|
437
|
+
return this;
|
438
|
+
}
|
439
|
+
|
440
|
+
public IRubyObject internalDelete(ThreadContext context, Node z) {
|
441
|
+
Node y = (z.left.isNull() || z.right.isNull()) ? z : successor(z);
|
442
|
+
Node x = y.left.isNull() ? y.right : y.left;
|
443
|
+
x.parent = y.parent;
|
444
|
+
if (y.parent.isNull()) {
|
445
|
+
this.root = x;
|
446
|
+
} else {
|
447
|
+
if (y.isLeft()) {
|
448
|
+
y.parent.left = x;
|
449
|
+
} else {
|
450
|
+
y.parent.right = x;
|
451
|
+
}
|
452
|
+
}
|
453
|
+
if (y != z) {
|
454
|
+
z.setKey(y.key);
|
455
|
+
z.setValue(y.value);
|
456
|
+
}
|
457
|
+
if (y.isBlack()) deleteFixup(x);
|
458
|
+
this.size -= 1;
|
459
|
+
|
460
|
+
return newArray(y);
|
461
|
+
}
|
462
|
+
|
463
|
+
private Node minimum() {
|
464
|
+
if (this.root.isNull()) {
|
465
|
+
return this.root;
|
466
|
+
}
|
467
|
+
return minimum(this.root);
|
468
|
+
}
|
469
|
+
|
470
|
+
private Node minimum(Node x) {
|
471
|
+
while (!x.left.isNull()) {
|
472
|
+
x = x.left;
|
473
|
+
}
|
474
|
+
return x;
|
475
|
+
}
|
476
|
+
|
477
|
+
private Node maximum() {
|
478
|
+
if (this.root.isNull()) {
|
479
|
+
return this.root;
|
480
|
+
}
|
481
|
+
return maximum(this.root);
|
482
|
+
}
|
483
|
+
|
484
|
+
private Node maximum(Node x) {
|
485
|
+
while (!x.right.isNull())
|
486
|
+
x = x.right;
|
487
|
+
return x;
|
488
|
+
}
|
489
|
+
|
490
|
+
// this is wrong, it cannot grant walk all nodes..
|
491
|
+
private Node successor(Node x) {
|
492
|
+
if (!x.right.isNull()) return minimum(x.right);
|
493
|
+
Node y = x.parent;
|
494
|
+
while (!y.isNull() && x == y.right) {
|
495
|
+
x = y;
|
496
|
+
y = y.parent;
|
497
|
+
}
|
498
|
+
return y;
|
499
|
+
}
|
500
|
+
|
501
|
+
private Node predecessor(Node x) {
|
502
|
+
if (!x.left.isNull()) return maximum(x.left);
|
503
|
+
Node y = x.parent;
|
504
|
+
while (!y.isNull() && x == y.left) {
|
505
|
+
x = y;
|
506
|
+
y = y.parent;
|
507
|
+
}
|
508
|
+
return y;
|
509
|
+
}
|
510
|
+
|
511
|
+
@JRubyMethod(name = {"each_pair", "each"})
|
512
|
+
public IRubyObject rbtree_each(final ThreadContext context, final Block block) {
|
513
|
+
return block.isGiven() ? eachCommon(context, block)
|
514
|
+
: enumeratorize(getRuntime(), this, "each");
|
515
|
+
}
|
516
|
+
|
517
|
+
@JRubyMethod
|
518
|
+
public IRubyObject each_key(final ThreadContext context, final Block block) {
|
519
|
+
return block.isGiven() ? each_keyCommon(context, block) : enumeratorize(context.runtime, this, "each_key");
|
520
|
+
}
|
521
|
+
|
522
|
+
public MultiRBTree each_keyCommon(final ThreadContext context, final Block block) {
|
523
|
+
iteratorVisitAll(new Visitor() {
|
524
|
+
public void visit(IRubyObject key, IRubyObject val) {
|
525
|
+
block.yield(context, key);
|
526
|
+
}
|
527
|
+
});
|
528
|
+
return this;
|
529
|
+
}
|
530
|
+
|
531
|
+
@JRubyMethod
|
532
|
+
public IRubyObject each_value(final ThreadContext context, final Block block) {
|
533
|
+
return block.isGiven() ? each_valueCommon(context, block) : enumeratorize(context.runtime, this, "each_value");
|
534
|
+
}
|
535
|
+
|
536
|
+
public MultiRBTree each_valueCommon(final ThreadContext context, final Block block) {
|
537
|
+
iteratorVisitAll(new Visitor() {
|
538
|
+
public void visit(IRubyObject key, IRubyObject val) {
|
539
|
+
block.yield(context, val);
|
540
|
+
}
|
541
|
+
});
|
542
|
+
return this;
|
543
|
+
}
|
544
|
+
|
545
|
+
public IRubyObject eachCommon(final ThreadContext context, final Block block) {
|
546
|
+
iteratorVisitAll(new Visitor() {
|
547
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
548
|
+
block.yieldSpecific(context, key, value);
|
549
|
+
}
|
550
|
+
});
|
551
|
+
return this;
|
552
|
+
}
|
553
|
+
|
554
|
+
@JRubyMethod
|
555
|
+
public IRubyObject shift(ThreadContext context) {
|
556
|
+
return nodeOrDefault(context, minimum(), true);
|
557
|
+
}
|
558
|
+
|
559
|
+
@JRubyMethod
|
560
|
+
public IRubyObject pop(ThreadContext context) {
|
561
|
+
return nodeOrDefault(context, maximum(), true);
|
562
|
+
}
|
563
|
+
|
564
|
+
@JRubyMethod
|
565
|
+
public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
|
566
|
+
Node node = lower_boundInternal(context, key);
|
567
|
+
if (node.isNull()) {
|
568
|
+
if (block.isGiven()) {
|
569
|
+
return block.yield(context, key);
|
570
|
+
}
|
571
|
+
return getRuntime().getNil();
|
572
|
+
}
|
573
|
+
internalDelete(context, node);
|
574
|
+
return node.value;
|
575
|
+
}
|
576
|
+
|
577
|
+
@JRubyMethod
|
578
|
+
public IRubyObject delete_if(final ThreadContext context, final Block block) {
|
579
|
+
return block.isGiven() ? delete_ifInternal(context, block) : enumeratorize(context.runtime, this, "delete_if");
|
580
|
+
}
|
581
|
+
|
582
|
+
private IRubyObject delete_ifInternal(final ThreadContext context, final Block block) {
|
583
|
+
List<Node> nodeList = new ArrayList<Node>();
|
584
|
+
try {
|
585
|
+
iteratorEntry();
|
586
|
+
for (Node x = minimum(); !x.isNull(); x = successor(x)) {
|
587
|
+
if (block.yieldSpecific(context, x.key, x.value).isTrue()) {
|
588
|
+
nodeList.add(x);
|
471
589
|
}
|
590
|
+
}
|
591
|
+
// delete backward
|
592
|
+
for (int i = nodeList.size() - 1; i >= 0; i--) {
|
593
|
+
internalDelete(context, nodeList.get(i));
|
594
|
+
}
|
595
|
+
} finally {
|
596
|
+
iteratorExit();
|
597
|
+
}
|
598
|
+
return this;
|
599
|
+
}
|
600
|
+
|
601
|
+
@JRubyMethod(name = "reject!")
|
602
|
+
public IRubyObject reject_bang(final ThreadContext context, final Block block) {
|
603
|
+
return block.isGiven() ? reject_bangInternal(context, block) : enumeratorize(context.runtime, this, "reject!");
|
604
|
+
}
|
605
|
+
|
606
|
+
private IRubyObject reject_bangInternal(ThreadContext context, Block block) {
|
607
|
+
int n = size;
|
608
|
+
delete_if(context, block);
|
609
|
+
if (n == size) return getRuntime().getNil();
|
610
|
+
return this;
|
611
|
+
}
|
612
|
+
|
613
|
+
@JRubyMethod
|
614
|
+
public IRubyObject reject(final ThreadContext context, final Block block) {
|
615
|
+
return block.isGiven() ? rejectInternal(context, block) : enumeratorize(context.runtime, this, "reject");
|
616
|
+
}
|
617
|
+
|
618
|
+
private IRubyObject rejectInternal(ThreadContext context, Block block) {
|
619
|
+
return ((MultiRBTree) dup()).reject_bangInternal(context, block);
|
620
|
+
}
|
621
|
+
|
622
|
+
@JRubyMethod
|
623
|
+
public IRubyObject select(final ThreadContext context, final Block block) {
|
624
|
+
final Ruby runtime = getRuntime();
|
625
|
+
if (!block.isGiven()) return enumeratorize(runtime, this, "select");
|
626
|
+
|
627
|
+
final MultiRBTree rbtree = (MultiRBTree) getMetaClass().getRealClass().allocate();
|
628
|
+
iteratorVisitAll(new Visitor() {
|
629
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
630
|
+
if (block.yieldSpecific(context, key, value).isTrue())
|
631
|
+
rbtree.internalPut(context, key, value);
|
632
|
+
}
|
633
|
+
});
|
634
|
+
return rbtree;
|
635
|
+
}
|
636
|
+
|
637
|
+
@JRubyMethod
|
638
|
+
public RubyArray to_a() {
|
639
|
+
final Ruby runtime = getRuntime();
|
640
|
+
final RubyArray result = runtime.newArray(size);
|
641
|
+
iteratorVisitAll(new Visitor() {
|
642
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
643
|
+
result.append(runtime.newArray(key, value));
|
644
|
+
}
|
645
|
+
});
|
646
|
+
return result;
|
647
|
+
}
|
648
|
+
|
649
|
+
@JRubyMethod
|
650
|
+
public IRubyObject flatten(final ThreadContext context, final Block block) {
|
651
|
+
RubyArray arg = to_a();
|
652
|
+
arg.callMethod(context, "flatten!", RubyFixnum.one(context.runtime));
|
653
|
+
return arg;
|
654
|
+
}
|
655
|
+
|
656
|
+
@JRubyMethod(name = "values_at", rest = true)
|
657
|
+
public IRubyObject values_at(final ThreadContext context, IRubyObject[] args) {
|
658
|
+
RubyArray result = RubyArray.newArray(context.runtime, args.length);
|
659
|
+
for (int i = 0; i < args.length; i++) {
|
660
|
+
result.append(op_aref(context, args[i]));
|
661
|
+
}
|
662
|
+
return result;
|
663
|
+
}
|
664
|
+
|
665
|
+
@JRubyMethod
|
666
|
+
public IRubyObject invert(final ThreadContext context) {
|
667
|
+
final MultiRBTree rbtree = (MultiRBTree) getMetaClass().getRealClass().allocate();
|
668
|
+
iteratorVisitAll(new Visitor() {
|
669
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
670
|
+
rbtree.internalPut(context, value, key);
|
671
|
+
}
|
672
|
+
});
|
673
|
+
return rbtree;
|
674
|
+
}
|
675
|
+
|
676
|
+
private IRubyObject nodeOrDefault(ThreadContext context, Node node) {
|
677
|
+
return nodeOrDefault(context, node, false);
|
678
|
+
}
|
679
|
+
|
680
|
+
private IRubyObject nodeOrDefault(ThreadContext context, Node node, boolean deleteNode) {
|
681
|
+
if (node.isNull()) {
|
682
|
+
if (this.ifNone == null)
|
683
|
+
return getRuntime().getNil();
|
684
|
+
if ((flags & PROCDEFAULT_HASH_F) != 0)
|
685
|
+
return RuntimeHelpers.invoke(context, ifNone, "call", this, getRuntime().getNil());
|
686
|
+
return ifNone;
|
687
|
+
}
|
688
|
+
if (deleteNode) {
|
689
|
+
internalDelete(context, node);
|
690
|
+
}
|
691
|
+
return newArray(node);
|
692
|
+
}
|
693
|
+
|
694
|
+
@JRubyMethod(name = {"reverse_inorder_walk", "reverse_each"})
|
695
|
+
public IRubyObject reverse_each(final ThreadContext context, final Block block) {
|
696
|
+
return block.isGiven() ? reverse_eachInternal(context, block) : enumeratorize(context.runtime, this, "reverse_each");
|
697
|
+
}
|
698
|
+
|
699
|
+
private IRubyObject reverse_eachInternal(final ThreadContext context, final Block block) {
|
700
|
+
iteratorReverseVisitAll(new Visitor() {
|
701
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
702
|
+
block.yieldSpecific(context, key, value);
|
703
|
+
}
|
704
|
+
});
|
705
|
+
return this;
|
706
|
+
}
|
707
|
+
|
708
|
+
public Node internalGet(ThreadContext context, RubyObject key) {
|
709
|
+
Node x = this.root;
|
710
|
+
while (!x.isNull()) {
|
711
|
+
int ret = compare(context, key, x.key);
|
712
|
+
if (ret > 0) {
|
713
|
+
x = x.right;
|
714
|
+
} else if (ret < 0) {
|
715
|
+
x = x.left;
|
716
|
+
} else {
|
472
717
|
return x;
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
718
|
+
}
|
719
|
+
}
|
720
|
+
return null;
|
721
|
+
}
|
722
|
+
|
723
|
+
@JRubyMethod(name = "empty?")
|
724
|
+
public IRubyObject empty_p(ThreadContext context) {
|
725
|
+
return getRuntime().newBoolean(size == 0);
|
726
|
+
}
|
727
|
+
|
728
|
+
@JRubyMethod(name = "black_height")
|
729
|
+
public IRubyObject blackHeight() {
|
730
|
+
Node x = this.root;
|
731
|
+
int height = 0;
|
732
|
+
while (!x.isNull()) {
|
733
|
+
x = x.left;
|
734
|
+
if (x.isNull() || x.isBlack()) height += 1;
|
735
|
+
}
|
736
|
+
return RubyFixnum.newFixnum(getRuntime(), height);
|
737
|
+
}
|
738
|
+
|
739
|
+
private void leftRotate(Node x) {
|
740
|
+
Node y = x.right;
|
741
|
+
x.right = y.left;
|
742
|
+
|
743
|
+
if (!y.left.isNull()) {
|
744
|
+
y.left.parent = x;
|
745
|
+
}
|
746
|
+
y.parent = x.parent;
|
747
|
+
if (x.parent.isNull()) {
|
748
|
+
this.root = y;
|
749
|
+
} else {
|
750
|
+
if (x.isLeft()) {
|
751
|
+
x.parent.left = y;
|
752
|
+
} else
|
753
|
+
x.parent.right = y;
|
754
|
+
}
|
755
|
+
y.left = x;
|
756
|
+
x.parent = y;
|
757
|
+
}
|
758
|
+
|
759
|
+
private void rightRotate(Node x) {
|
760
|
+
Node y = x.left;
|
761
|
+
x.left = y.right;
|
762
|
+
if (!y.right.isNull()) {
|
763
|
+
y.right.parent = x;
|
764
|
+
}
|
765
|
+
y.parent = x.parent;
|
766
|
+
if (x.parent.isNull()) {
|
767
|
+
this.root = y;
|
768
|
+
} else {
|
769
|
+
if (x.isLeft()) {
|
770
|
+
x.parent.left = y;
|
771
|
+
} else {
|
772
|
+
x.parent.right = y;
|
773
|
+
}
|
774
|
+
}
|
775
|
+
y.right = x;
|
776
|
+
x.parent = y;
|
777
|
+
}
|
778
|
+
|
779
|
+
private int compare(ThreadContext context, Node a, Node b) {
|
780
|
+
return compare(context, a.key, b.key);
|
781
|
+
}
|
782
|
+
|
783
|
+
private int compare(ThreadContext context, RubyObject a, RubyObject b) {
|
784
|
+
if (context == null || cmpProc == null)
|
785
|
+
return a.compareTo(b);
|
786
|
+
return (int) cmpProc.call(context, new IRubyObject[]{a, b}).convertToInteger().getLongValue();
|
787
|
+
}
|
788
|
+
|
789
|
+
private void internalPutHelper(ThreadContext context, Node z) {
|
790
|
+
Node y = NilNode.getInstance();
|
791
|
+
Node x = this.root;
|
792
|
+
while (!x.isNull()) {
|
793
|
+
y = x;
|
794
|
+
x = compare(context, z, x) < 0 ? x.left : x.right;
|
795
|
+
}
|
796
|
+
z.parent = y;
|
797
|
+
if (y.isNull()) {
|
798
|
+
this.root = z;
|
799
|
+
} else {
|
800
|
+
if (compare(context, z, y) < 0) {
|
801
|
+
y.left = z;
|
802
|
+
} else {
|
803
|
+
y.right = z;
|
804
|
+
}
|
805
|
+
}
|
806
|
+
this.size += 1;
|
807
|
+
}
|
808
|
+
|
809
|
+
private void deleteFixup(Node x) {
|
810
|
+
while (x != this.root && x.isBlack()) {
|
811
|
+
if (x.isLeft()) {
|
812
|
+
Node w = x.parent.right;
|
813
|
+
if (w.isRed()) {
|
814
|
+
w.setBlack();
|
815
|
+
x.parent.setRed();
|
816
|
+
leftRotate(x.parent);
|
817
|
+
w = x.parent.right;
|
478
818
|
}
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
y = y.getParent();
|
819
|
+
if (w.left.isBlack() && w.right.isBlack()) {
|
820
|
+
w.setRed();
|
821
|
+
x = x.parent;
|
822
|
+
} else {
|
823
|
+
if (w.right.isBlack()) {
|
824
|
+
w.left.setBlack();
|
825
|
+
w.setRed();
|
826
|
+
rightRotate(w);
|
827
|
+
w = x.parent.right;
|
828
|
+
}
|
829
|
+
w.color = x.parent.color;
|
830
|
+
x.parent.setBlack();
|
831
|
+
w.right.setBlack();
|
832
|
+
leftRotate(x.parent);
|
833
|
+
x = this.root;
|
495
834
|
}
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
x = y;
|
504
|
-
y = y.getParent();
|
835
|
+
} else {
|
836
|
+
Node w = x.parent.left;
|
837
|
+
if (w.isRed()) {
|
838
|
+
w.setBlack();
|
839
|
+
x.parent.setRed();
|
840
|
+
rightRotate(x.parent);
|
841
|
+
w = x.parent.left;
|
505
842
|
}
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
iteratorVisitAll(new Visitor() {
|
522
|
-
public void visit(IRubyObject key, IRubyObject val) {
|
523
|
-
block.yield(context, key);
|
524
|
-
}
|
525
|
-
});
|
526
|
-
return this;
|
527
|
-
}
|
528
|
-
|
529
|
-
@JRubyMethod
|
530
|
-
public IRubyObject each_value(final ThreadContext context, final Block block) {
|
531
|
-
return block.isGiven() ? each_valueCommon(context, block) : enumeratorize(context.runtime, this, "each_value");
|
532
|
-
}
|
533
|
-
|
534
|
-
public MultiRBTree each_valueCommon(final ThreadContext context, final Block block) {
|
535
|
-
iteratorVisitAll(new Visitor() {
|
536
|
-
public void visit(IRubyObject key, IRubyObject val) {
|
537
|
-
block.yield(context, val);
|
538
|
-
}
|
539
|
-
});
|
540
|
-
return this;
|
541
|
-
}
|
542
|
-
|
543
|
-
public IRubyObject eachCommon(final ThreadContext context, final Block block) {
|
544
|
-
iteratorVisitAll(new Visitor() {
|
545
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
546
|
-
block.yieldSpecific(context, key, value);
|
547
|
-
}
|
548
|
-
});
|
549
|
-
return this;
|
550
|
-
}
|
551
|
-
|
552
|
-
@JRubyMethod
|
553
|
-
public IRubyObject shift(ThreadContext context) {
|
554
|
-
return nodeOrDefault(context, minimum(), true);
|
555
|
-
}
|
556
|
-
|
557
|
-
@JRubyMethod
|
558
|
-
public IRubyObject pop(ThreadContext context) {
|
559
|
-
return nodeOrDefault(context, maximum(), true);
|
560
|
-
}
|
561
|
-
|
562
|
-
@JRubyMethod
|
563
|
-
public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
|
564
|
-
Node node = lower_boundInternal(context, key);
|
565
|
-
if (node.isNull()) {
|
566
|
-
if (block.isGiven()) {
|
567
|
-
return block.yield(context, key);
|
568
|
-
}
|
569
|
-
return getRuntime().getNil();
|
843
|
+
if (w.right.isBlack() && w.left.isBlack()) {
|
844
|
+
w.setRed();
|
845
|
+
x = x.parent;
|
846
|
+
} else {
|
847
|
+
if (w.left.isBlack()) {
|
848
|
+
w.right.setBlack();
|
849
|
+
w.setRed();
|
850
|
+
rightRotate(w);
|
851
|
+
w = x.parent.left;
|
852
|
+
}
|
853
|
+
w.color = x.parent.color;
|
854
|
+
x.parent.setBlack();
|
855
|
+
w.left.setBlack();
|
856
|
+
rightRotate(x.parent);
|
857
|
+
x = this.root;
|
570
858
|
}
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
859
|
+
}
|
860
|
+
}
|
861
|
+
x.setBlack();
|
862
|
+
}
|
863
|
+
|
864
|
+
@JRubyMethod(name = "size")
|
865
|
+
public IRubyObject getSize() {
|
866
|
+
return getRuntime().newFixnum(this.size);
|
867
|
+
}
|
868
|
+
|
869
|
+
@JRubyMethod
|
870
|
+
public IRubyObject last(ThreadContext context) {
|
871
|
+
return nodeOrDefault(context, maximum());
|
872
|
+
}
|
873
|
+
|
874
|
+
@JRubyMethod
|
875
|
+
public IRubyObject first(ThreadContext context) {
|
876
|
+
return nodeOrDefault(context, minimum());
|
877
|
+
}
|
878
|
+
|
879
|
+
public Node lower_boundInternal(ThreadContext context, IRubyObject key) {
|
880
|
+
Ruby runtime = getRuntime();
|
881
|
+
Node node = this.root;
|
882
|
+
Node tentative = NilNode.getInstance();
|
883
|
+
while (!node.isNull()) {
|
884
|
+
int result = compare(context, (RubyObject) key, node.key);
|
885
|
+
if (result > 0) {
|
886
|
+
node = node.right;
|
887
|
+
} else if (result < 0) {
|
888
|
+
tentative = node;
|
889
|
+
node = node.left;
|
890
|
+
} else {
|
891
|
+
if (!dupes) {
|
892
|
+
return node;
|
893
|
+
} else {
|
894
|
+
tentative = node;
|
895
|
+
node = node.left;
|
595
896
|
}
|
596
|
-
|
597
|
-
}
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
final MultiRBTree rbtree = (MultiRBTree) getMetaClass().getRealClass().allocate();
|
626
|
-
iteratorVisitAll(new Visitor() {
|
627
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
628
|
-
if (block.yieldSpecific(context, key, value).isTrue())
|
629
|
-
rbtree.internalPut(context, key, value);
|
630
|
-
}
|
631
|
-
});
|
632
|
-
return rbtree;
|
633
|
-
}
|
634
|
-
|
635
|
-
@JRubyMethod
|
636
|
-
public RubyArray to_a() {
|
637
|
-
final Ruby runtime = getRuntime();
|
638
|
-
final RubyArray result = runtime.newArray(size);
|
639
|
-
iteratorVisitAll(new Visitor() {
|
640
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
641
|
-
result.append(runtime.newArray(key, value));
|
642
|
-
}
|
643
|
-
});
|
644
|
-
return result;
|
645
|
-
}
|
646
|
-
|
647
|
-
@JRubyMethod
|
648
|
-
public IRubyObject flatten(final ThreadContext context, final Block block) {
|
649
|
-
RubyArray arg = to_a();
|
650
|
-
arg.callMethod(context, "flatten!", RubyFixnum.one(context.runtime));
|
651
|
-
return arg;
|
652
|
-
}
|
653
|
-
|
654
|
-
@JRubyMethod(name = "values_at", rest = true)
|
655
|
-
public IRubyObject values_at(final ThreadContext context, IRubyObject[] args) {
|
656
|
-
RubyArray result = RubyArray.newArray(context.runtime, args.length);
|
657
|
-
for (int i = 0; i < args.length; i++) {
|
658
|
-
result.append(op_aref(context, args[i]));
|
897
|
+
}
|
898
|
+
}
|
899
|
+
return tentative;
|
900
|
+
}
|
901
|
+
|
902
|
+
@JRubyMethod
|
903
|
+
public IRubyObject lower_bound(ThreadContext context, IRubyObject key) {
|
904
|
+
Node node = lower_boundInternal(context, key);
|
905
|
+
return node.isNull() ? context.runtime.getNil() : newArray(node);
|
906
|
+
}
|
907
|
+
|
908
|
+
public Node upper_boundInternal(ThreadContext context, IRubyObject key) {
|
909
|
+
Ruby runtime = getRuntime();
|
910
|
+
Node node = this.root;
|
911
|
+
Node tentative = NilNode.getInstance();
|
912
|
+
while (!node.isNull()) {
|
913
|
+
int result = compare(context, (RubyObject) key, node.key);
|
914
|
+
if (result < 0) {
|
915
|
+
node = node.left;
|
916
|
+
} else if (result > 0) {
|
917
|
+
tentative = node;
|
918
|
+
node = node.right;
|
919
|
+
} else {
|
920
|
+
if (!dupes) {
|
921
|
+
return node;
|
922
|
+
} else { // if there are duplicates, go to the far right
|
923
|
+
tentative = node;
|
924
|
+
node = node.right;
|
659
925
|
}
|
660
|
-
|
661
|
-
}
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
926
|
+
}
|
927
|
+
}
|
928
|
+
return tentative;
|
929
|
+
}
|
930
|
+
|
931
|
+
@JRubyMethod
|
932
|
+
public IRubyObject upper_bound(ThreadContext context, IRubyObject key) {
|
933
|
+
Node node = upper_boundInternal(context, key);
|
934
|
+
return node.isNull() ? context.runtime.getNil() : newArray(node);
|
935
|
+
}
|
936
|
+
|
937
|
+
@JRubyMethod(name = "bound", required = 1, optional = 1)
|
938
|
+
public IRubyObject bound(final ThreadContext context, final IRubyObject[] bounds, final Block block) {
|
939
|
+
final Ruby runtime = getRuntime();
|
940
|
+
final RubyArray ret = runtime.newArray();
|
941
|
+
iteratorVisitAll(new Visitor() {
|
942
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
943
|
+
if (((RubyObject) key).compareTo((RubyObject) bounds[0]) == 0
|
944
|
+
|| bounds.length == 2 && ((RubyObject) key).compareTo((RubyObject) bounds[0]) >= 0
|
945
|
+
&& ((RubyObject) key).compareTo((RubyObject) bounds[1]) <= 0) {
|
946
|
+
if (block.isGiven()) {
|
947
|
+
block.yieldSpecific(context, key, value);
|
948
|
+
} else {
|
949
|
+
ret.add(runtime.newArray(key, value));
|
950
|
+
}
|
685
951
|
}
|
686
|
-
|
687
|
-
|
952
|
+
}
|
953
|
+
});
|
954
|
+
return ret;
|
955
|
+
}
|
956
|
+
|
957
|
+
private RubyArray newArray(Node node) {
|
958
|
+
return getRuntime().newArray(node.key, node.value);
|
959
|
+
}
|
960
|
+
|
961
|
+
@JRubyMethod(name = {"replace", "initialize_copy"})
|
962
|
+
public IRubyObject replace(final ThreadContext context, IRubyObject other) {
|
963
|
+
checkCompatible(context.runtime, other);
|
964
|
+
MultiRBTree otherTree = (MultiRBTree) other;
|
965
|
+
return replaceInternal(context, otherTree, otherTree.cmpProc);
|
966
|
+
}
|
967
|
+
|
968
|
+
private IRubyObject replaceInternal(final ThreadContext context, MultiRBTree otherTree, RubyProc cmpfunc) {
|
969
|
+
init();
|
970
|
+
if (this == otherTree) return this;
|
971
|
+
this.ifNone = otherTree.ifNone;
|
972
|
+
this.flags = otherTree.flags;
|
973
|
+
this.cmpProc = cmpfunc;
|
974
|
+
otherTree.visitAll(new Visitor() {
|
975
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
976
|
+
internalPut(context, key, value);
|
977
|
+
}
|
978
|
+
});
|
979
|
+
return this;
|
980
|
+
}
|
981
|
+
|
982
|
+
@JRubyMethod(name = "==")
|
983
|
+
public IRubyObject op_equal(IRubyObject other) {
|
984
|
+
Ruby runtime = getRuntime();
|
985
|
+
if (this == other)
|
986
|
+
return runtime.getTrue();
|
987
|
+
if (!(other instanceof MultiRBTree))
|
988
|
+
return runtime.getFalse();
|
989
|
+
return this.dict_eq((MultiRBTree) other) ? runtime.getTrue() : runtime.getFalse();
|
990
|
+
}
|
991
|
+
|
992
|
+
private boolean dict_eq(MultiRBTree other) {
|
993
|
+
if (this.size != other.size || !similar(other))
|
994
|
+
return false;
|
995
|
+
for (Node node1 = minimum(), node2 = other.minimum();
|
996
|
+
!node1.isNull() && !node2.isNull();
|
997
|
+
node1 = successor(node1), node2 = other.successor(node2)) {
|
998
|
+
if (!node1.key.eql(node2.key) || !node1.value.eql(node2.value))
|
999
|
+
return false;
|
1000
|
+
}
|
1001
|
+
return true;
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
private boolean similar(MultiRBTree other) {
|
1005
|
+
return this.cmpProc == other.cmpProc;
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
private byte comma_breakable(ThreadContext context, IRubyObject pp) {
|
1009
|
+
return (byte) ',';
|
1010
|
+
}
|
1011
|
+
|
1012
|
+
private IRubyObject inspectMultiRBTree(final ThreadContext context, final IRubyObject pp) {
|
1013
|
+
final RubyString str = RubyString.newStringLight(context.runtime, DEFAULT_INSPECT_STR_SIZE);
|
1014
|
+
str.cat(new byte[]{'#', '<'}).cat(getMetaClass().getRealClass().getName().getBytes()).cat(": {".getBytes());
|
1015
|
+
final boolean[] firstEntry = new boolean[1];
|
1016
|
+
|
1017
|
+
firstEntry[0] = true;
|
1018
|
+
final boolean is19 = context.runtime.is1_9();
|
1019
|
+
visitAll(new Visitor() {
|
1020
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
1021
|
+
if (!firstEntry[0]) str.cat(comma_breakable(context, pp)).cat((byte) ' ');
|
1022
|
+
|
1023
|
+
RubyString inspectedKey = inspect(context, key);
|
1024
|
+
RubyString inspectedValue = inspect(context, value);
|
1025
|
+
|
1026
|
+
if (is19) {
|
1027
|
+
str.cat19(inspectedKey);
|
1028
|
+
str.cat((byte) '=').cat((byte) '>');
|
1029
|
+
str.cat19(inspectedValue);
|
1030
|
+
} else {
|
1031
|
+
str.cat(inspectedKey);
|
1032
|
+
str.cat((byte) '=').cat((byte) '>');
|
1033
|
+
str.cat(inspectedValue);
|
688
1034
|
}
|
689
|
-
return newArray(node);
|
690
|
-
}
|
691
1035
|
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
}
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
1036
|
+
firstEntry[0] = false;
|
1037
|
+
}
|
1038
|
+
});
|
1039
|
+
str.cat((byte) '}').cat(comma_breakable(context, pp)).cat(" default=".getBytes());
|
1040
|
+
if (ifNone == null) {
|
1041
|
+
str.cat("nil".getBytes());
|
1042
|
+
} else {
|
1043
|
+
str.cat(inspect(context, ifNone));
|
1044
|
+
}
|
1045
|
+
str.cat(comma_breakable(context, pp)).cat(" cmp_proc=".getBytes());
|
1046
|
+
if (cmpProc == null) {
|
1047
|
+
str.cat("nil".getBytes());
|
1048
|
+
} else {
|
1049
|
+
str.cat(inspect(context, cmpProc));
|
1050
|
+
}
|
1051
|
+
str.cat((byte) '>');
|
1052
|
+
return str;
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
@JRubyMethod(name = "inspect")
|
1056
|
+
public IRubyObject inspect(ThreadContext context) {
|
1057
|
+
if (getRuntime().isInspecting(this)) return getRuntime().newString("#<RBTree: ...>");
|
1058
|
+
|
1059
|
+
try {
|
1060
|
+
getRuntime().registerInspecting(this);
|
1061
|
+
return inspectMultiRBTree(context, null);
|
1062
|
+
} finally {
|
1063
|
+
getRuntime().unregisterInspecting(this);
|
1064
|
+
}
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
@JRubyMethod(name = "to_s")
|
1068
|
+
public IRubyObject to_s(ThreadContext context) {
|
1069
|
+
Ruby runtime = context.runtime;
|
1070
|
+
if (runtime.isInspecting(this)) return runtime.newString("{...}");
|
1071
|
+
try {
|
1072
|
+
runtime.registerInspecting(this);
|
1073
|
+
return to_a().to_s();
|
1074
|
+
} finally {
|
1075
|
+
runtime.unregisterInspecting(this);
|
1076
|
+
}
|
1077
|
+
}
|
1078
|
+
|
1079
|
+
private AtomicInteger iteratorCount = new AtomicInteger(0);
|
1080
|
+
|
1081
|
+
private void iteratorEntry() {
|
1082
|
+
iteratorCount.incrementAndGet();
|
1083
|
+
}
|
1084
|
+
|
1085
|
+
private void iteratorExit() {
|
1086
|
+
iteratorCount.decrementAndGet();
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
private void checkIterating() {
|
1090
|
+
if (iteratorCount.get() > 0) {
|
1091
|
+
throw getRuntime().newRuntimeError("can't add a new key into RBTree during iteration");
|
1092
|
+
}
|
1093
|
+
}
|
1094
|
+
|
1095
|
+
private void visitAll(Visitor visitor) {
|
1096
|
+
for (Node x = minimum(); !x.isNull(); x = successor(x)) {
|
1097
|
+
visitor.visit(x.key, x.value);
|
1098
|
+
}
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
public void iteratorReverseVisitAll(Visitor visitor) {
|
1102
|
+
try {
|
1103
|
+
iteratorEntry();
|
1104
|
+
for (Node x = maximum(); !x.isNull(); x = predecessor(x)) {
|
1105
|
+
visitor.visit(x.key, x.value);
|
1106
|
+
}
|
1107
|
+
} finally {
|
1108
|
+
iteratorExit();
|
1109
|
+
}
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
public void iteratorVisitAll(Visitor visitor) {
|
1113
|
+
try {
|
1114
|
+
iteratorEntry();
|
1115
|
+
for (Node x = minimum(); !x.isNull(); x = successor(x)) {
|
1116
|
+
visitor.visit(x.key, x.value);
|
1117
|
+
}
|
1118
|
+
} finally {
|
1119
|
+
iteratorExit();
|
1120
|
+
}
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
private static class VisitorIOException extends RuntimeException {
|
1124
|
+
VisitorIOException(Throwable cause) {
|
1125
|
+
super(cause);
|
1126
|
+
}
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
private static final ObjectMarshal RBTREE_MARSHAL = new ObjectMarshal() {
|
1130
|
+
public void marshalTo(Ruby runtime, final Object obj, RubyClass recv, final MarshalStream output) throws IOException {
|
1131
|
+
MultiRBTree rbtree = (MultiRBTree) obj;
|
1132
|
+
if (rbtree.size == 0) throw runtime.newArgumentError("cannot dump empty tree");
|
1133
|
+
if (rbtree.cmpProc != null) throw runtime.newArgumentError("cannot dump rbtree with compare proc");
|
1134
|
+
output.registerLinkTarget(rbtree);
|
1135
|
+
output.writeInt(rbtree.size);
|
1136
|
+
try {
|
1137
|
+
rbtree.visitAll(new Visitor() {
|
1138
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
1139
|
+
try {
|
1140
|
+
output.dumpObject(key);
|
1141
|
+
output.dumpObject(value);
|
1142
|
+
} catch (IOException e) {
|
1143
|
+
throw new VisitorIOException(e);
|
701
1144
|
}
|
1145
|
+
}
|
702
1146
|
});
|
703
|
-
|
1147
|
+
} catch (VisitorIOException e) {
|
1148
|
+
throw (IOException) e.getCause();
|
1149
|
+
}
|
704
1150
|
}
|
705
1151
|
|
706
|
-
public
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
} else {
|
715
|
-
return x;
|
716
|
-
}
|
717
|
-
}
|
718
|
-
return null;
|
1152
|
+
public Object unmarshalFrom(Ruby runtime, RubyClass type, UnmarshalStream input) throws IOException {
|
1153
|
+
MultiRBTree result = (MultiRBTree) type.allocate();
|
1154
|
+
input.registerLinkTarget(result);
|
1155
|
+
int size = input.unmarshalInt();
|
1156
|
+
for (int i = 0; i < size; i++) {
|
1157
|
+
result.internalPut(runtime.getCurrentContext(), input.unmarshalObject(), input.unmarshalObject());
|
1158
|
+
}
|
1159
|
+
return result;
|
719
1160
|
}
|
1161
|
+
};
|
720
1162
|
|
721
|
-
|
722
|
-
public
|
723
|
-
|
724
|
-
}
|
1163
|
+
private static abstract class Visitor {
|
1164
|
+
public abstract void visit(IRubyObject key, IRubyObject value);
|
1165
|
+
}
|
725
1166
|
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
while (!x.isNull()) {
|
731
|
-
x = x.getLeft();
|
732
|
-
if (x.isNull() || x.isBlack()) height += 1;
|
733
|
-
}
|
734
|
-
return RubyFixnum.newFixnum(getRuntime(), height);
|
1167
|
+
private static class Found extends RuntimeException {
|
1168
|
+
@Override
|
1169
|
+
public synchronized Throwable fillInStackTrace() {
|
1170
|
+
return null;
|
735
1171
|
}
|
1172
|
+
}
|
736
1173
|
|
737
|
-
|
738
|
-
Node y = x.getRight();
|
739
|
-
x.setRight(y.getLeft());
|
1174
|
+
private static final Found FOUND = new Found();
|
740
1175
|
|
741
|
-
|
742
|
-
|
743
|
-
}
|
744
|
-
y.setParent(x.getParent());
|
745
|
-
if (x.getParent().isNull()) {
|
746
|
-
this.root = y;
|
747
|
-
} else {
|
748
|
-
if (x == x.getParent().getLeft()) {
|
749
|
-
x.getParent().setLeft(y);
|
750
|
-
} else {
|
751
|
-
x.getParent().setRight(y);
|
752
|
-
}
|
753
|
-
}
|
754
|
-
y.setLeft(x);
|
755
|
-
x.setParent(y);
|
756
|
-
}
|
1176
|
+
private static class FoundKey extends Found {
|
1177
|
+
public final IRubyObject key;
|
757
1178
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
if (! y.getRight().isNull()) {
|
762
|
-
y.getRight().setParent(x);
|
763
|
-
}
|
764
|
-
y.setParent(x.getParent());
|
765
|
-
if (x.getParent().isNull()) {
|
766
|
-
this.root = y;
|
767
|
-
} else {
|
768
|
-
if (x == x.getParent().getLeft()) {
|
769
|
-
x.getParent().setLeft(y);
|
770
|
-
} else {
|
771
|
-
x.getParent().setRight(y);
|
772
|
-
}
|
773
|
-
}
|
774
|
-
y.setRight(x);
|
775
|
-
x.setParent(y);
|
776
|
-
}
|
777
|
-
private int compare(ThreadContext context, Node a, Node b) {
|
778
|
-
return compare(context, a.getKey(), b.getKey());
|
1179
|
+
FoundKey(IRubyObject key) {
|
1180
|
+
super();
|
1181
|
+
this.key = key;
|
779
1182
|
}
|
1183
|
+
}
|
780
1184
|
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
1185
|
+
private static class Node {
|
1186
|
+
protected Color color;
|
1187
|
+
protected RubyObject key;
|
1188
|
+
protected IRubyObject value;
|
1189
|
+
protected Node left;
|
1190
|
+
protected Node right;
|
1191
|
+
protected Node parent;
|
786
1192
|
|
787
|
-
|
788
|
-
Node y = NilNode.getInstance();
|
789
|
-
Node x = this.root;
|
790
|
-
while(!x.isNull()) {
|
791
|
-
y = x;
|
792
|
-
x = compare(context, z, x) < 0 ? x.getLeft() : x.getRight();
|
793
|
-
}
|
794
|
-
z.setParent(y);
|
795
|
-
if (y.isNull()) {
|
796
|
-
this.root = z;
|
797
|
-
} else {
|
798
|
-
if (compare(context, z, y) < 0) {
|
799
|
-
y.setLeft(z);
|
800
|
-
} else {
|
801
|
-
y.setRight(z);
|
802
|
-
}
|
803
|
-
}
|
804
|
-
this.size += 1;
|
805
|
-
}
|
806
|
-
|
807
|
-
private void deleteFixup(Node x) {
|
808
|
-
while (x != this.root && x.isBlack()) {
|
809
|
-
if (x.isLeft()) {
|
810
|
-
Node w = x.getParent().getRight();
|
811
|
-
if (w.isRed()) {
|
812
|
-
w.setBlack();
|
813
|
-
x.getParent().setRed();
|
814
|
-
leftRotate(x.getParent());
|
815
|
-
w = x.getParent().getRight();
|
816
|
-
}
|
817
|
-
if (w.getLeft().isBlack() && w.getRight().isBlack()) {
|
818
|
-
w.setRed();
|
819
|
-
x = x.getParent();
|
820
|
-
} else {
|
821
|
-
if (w.getRight().isBlack()) {
|
822
|
-
w.getLeft().setBlack();
|
823
|
-
w.setRed();
|
824
|
-
rightRotate(w);
|
825
|
-
w = x.getParent().getRight();
|
826
|
-
}
|
827
|
-
w.setColor(x.getParent().getColor());
|
828
|
-
x.getParent().setBlack();
|
829
|
-
w.getRight().setBlack();
|
830
|
-
leftRotate(x.getParent());
|
831
|
-
x = this.root;
|
832
|
-
}
|
833
|
-
} else {
|
834
|
-
Node w = x.getParent().getLeft();
|
835
|
-
if (w.isRed()) {
|
836
|
-
w.setBlack();
|
837
|
-
x.getParent().setRed();
|
838
|
-
rightRotate(x.getParent());
|
839
|
-
w = x.getParent().getLeft();
|
840
|
-
}
|
841
|
-
if (w.getRight().isBlack() && w.getLeft().isBlack()) {
|
842
|
-
w.setRed();
|
843
|
-
x = x.getParent();
|
844
|
-
} else {
|
845
|
-
if (w.getLeft().isBlack()) {
|
846
|
-
w.getRight().setBlack();
|
847
|
-
w.setRed();
|
848
|
-
rightRotate(w);
|
849
|
-
w = x.getParent().getLeft();
|
850
|
-
}
|
851
|
-
w.setColor(x.getParent().getColor());
|
852
|
-
x.getParent().setBlack();
|
853
|
-
w.getLeft().setBlack();
|
854
|
-
rightRotate(x.getParent());
|
855
|
-
x = this.root;
|
856
|
-
}
|
857
|
-
}
|
858
|
-
}
|
859
|
-
x.setBlack();
|
860
|
-
}
|
861
|
-
|
862
|
-
@JRubyMethod(name = "size")
|
863
|
-
public IRubyObject getSize() {
|
864
|
-
return getRuntime().newFixnum(this.size);
|
865
|
-
}
|
866
|
-
|
867
|
-
@JRubyMethod
|
868
|
-
public IRubyObject last(ThreadContext context) {
|
869
|
-
return nodeOrDefault(context, maximum());
|
870
|
-
}
|
871
|
-
|
872
|
-
@JRubyMethod
|
873
|
-
public IRubyObject first(ThreadContext context) {
|
874
|
-
return nodeOrDefault(context, minimum());
|
875
|
-
}
|
876
|
-
|
877
|
-
public Node lower_boundInternal(ThreadContext context, IRubyObject key) {
|
878
|
-
Ruby runtime = getRuntime();
|
879
|
-
Node node = this.root;
|
880
|
-
Node tentative = NilNode.getInstance();
|
881
|
-
while (!node.isNull()) {
|
882
|
-
int result = compare(context, (RubyObject) key, node.getKey());
|
883
|
-
if (result > 0) {
|
884
|
-
node = node.getRight();
|
885
|
-
} else if (result < 0) {
|
886
|
-
tentative = node;
|
887
|
-
node = node.getLeft();
|
888
|
-
} else {
|
889
|
-
if (!dupes) {
|
890
|
-
return node;
|
891
|
-
} else {
|
892
|
-
tentative = node;
|
893
|
-
node = node.getLeft();
|
894
|
-
}
|
895
|
-
}
|
896
|
-
}
|
897
|
-
return tentative;
|
898
|
-
}
|
899
|
-
|
900
|
-
@JRubyMethod
|
901
|
-
public IRubyObject lower_bound(ThreadContext context, IRubyObject key) {
|
902
|
-
Node node = lower_boundInternal(context, key);
|
903
|
-
return node.isNull() ? context.runtime.getNil() : newArray(node);
|
904
|
-
}
|
905
|
-
|
906
|
-
public Node upper_boundInternal(ThreadContext context, IRubyObject key) {
|
907
|
-
Ruby runtime = getRuntime();
|
908
|
-
Node node = this.root;
|
909
|
-
Node tentative = NilNode.getInstance();
|
910
|
-
while (!node.isNull()) {
|
911
|
-
int result = compare(context, (RubyObject) key, node.getKey());
|
912
|
-
if (result < 0) {
|
913
|
-
node = node.getLeft();
|
914
|
-
} else if (result > 0) {
|
915
|
-
tentative = node;
|
916
|
-
node = node.getRight();
|
917
|
-
} else {
|
918
|
-
if (!dupes) {
|
919
|
-
return node;
|
920
|
-
} else { // if there are duplicates, go to the far right
|
921
|
-
tentative = node;
|
922
|
-
node = node.getRight();
|
923
|
-
}
|
924
|
-
}
|
925
|
-
}
|
926
|
-
return tentative;
|
927
|
-
}
|
928
|
-
|
929
|
-
@JRubyMethod
|
930
|
-
public IRubyObject upper_bound(ThreadContext context, IRubyObject key) {
|
931
|
-
Node node = upper_boundInternal(context, key);
|
932
|
-
return node.isNull() ? context.runtime.getNil() : newArray(node);
|
933
|
-
}
|
934
|
-
|
935
|
-
@JRubyMethod(name = "bound", required = 1, optional = 1)
|
936
|
-
public IRubyObject bound(final ThreadContext context, final IRubyObject[] bounds, final Block block) {
|
937
|
-
final Ruby runtime = getRuntime();
|
938
|
-
final RubyArray ret = runtime.newArray();
|
939
|
-
iteratorVisitAll(new Visitor() {
|
940
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
941
|
-
if (((RubyObject) key).compareTo((RubyObject) bounds[0]) == 0
|
942
|
-
|| bounds.length == 2 && ((RubyObject) key).compareTo((RubyObject) bounds[0]) >= 0
|
943
|
-
&& ((RubyObject) key).compareTo((RubyObject) bounds[1]) <= 0) {
|
944
|
-
if (block.isGiven()) {
|
945
|
-
block.yieldSpecific(context, key, value);
|
946
|
-
} else {
|
947
|
-
ret.add(runtime.newArray(key, value));
|
948
|
-
}
|
949
|
-
}
|
950
|
-
}
|
951
|
-
});
|
952
|
-
return ret;
|
1193
|
+
protected Node() {
|
953
1194
|
}
|
954
1195
|
|
955
|
-
|
956
|
-
|
1196
|
+
protected Node(IRubyObject key, IRubyObject value) {
|
1197
|
+
this(key, value, Color.RED);
|
957
1198
|
}
|
958
1199
|
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
1200
|
+
protected Node(IRubyObject key, IRubyObject value, Color color) {
|
1201
|
+
this.key = (RubyObject) key;
|
1202
|
+
this.value = value;
|
1203
|
+
this.color = color;
|
1204
|
+
this.left = this.right = this.parent = NilNode.getInstance();
|
964
1205
|
}
|
965
1206
|
|
966
|
-
|
967
|
-
|
968
|
-
if (this == otherTree) return this;
|
969
|
-
this.ifNone = otherTree.ifNone;
|
970
|
-
this.flags = otherTree.flags;
|
971
|
-
this.cmpProc = cmpfunc;
|
972
|
-
otherTree.visitAll(new Visitor() {
|
973
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
974
|
-
internalPut(context, key, value);
|
975
|
-
}
|
976
|
-
});
|
977
|
-
return this;
|
1207
|
+
public boolean isRed() {
|
1208
|
+
return this.color == Color.RED;
|
978
1209
|
}
|
979
1210
|
|
980
|
-
|
981
|
-
|
982
|
-
Ruby runtime = getRuntime();
|
983
|
-
if (this == other)
|
984
|
-
return runtime.getTrue();
|
985
|
-
if (!(other instanceof MultiRBTree))
|
986
|
-
return runtime.getFalse();
|
987
|
-
return this.dict_eq((MultiRBTree) other) ? runtime.getTrue() : runtime.getFalse();
|
988
|
-
}
|
989
|
-
|
990
|
-
private boolean dict_eq(MultiRBTree other) {
|
991
|
-
if (this.size != other.size || ! similar(other))
|
992
|
-
return false;
|
993
|
-
for (Node node1 = minimum(), node2 = other.minimum();
|
994
|
-
!node1.isNull() && !node2.isNull();
|
995
|
-
node1 = successor(node1), node2 = other.successor(node2))
|
996
|
-
{
|
997
|
-
if (!node1.key.eql(node2.key) || !node1.value.eql(node2.value))
|
998
|
-
return false;
|
999
|
-
}
|
1000
|
-
return true;
|
1211
|
+
public boolean isBlack() {
|
1212
|
+
return !isRed();
|
1001
1213
|
}
|
1002
1214
|
|
1003
|
-
|
1004
|
-
|
1215
|
+
public Node getGrandParent() {
|
1216
|
+
return this.parent.parent;
|
1005
1217
|
}
|
1006
1218
|
|
1007
|
-
|
1008
|
-
|
1219
|
+
public void setKey(IRubyObject key) {
|
1220
|
+
this.key = (RubyObject) key;
|
1009
1221
|
}
|
1010
1222
|
|
1011
|
-
|
1012
|
-
|
1013
|
-
str.cat(new byte[]{'#', '<'}).cat(getMetaClass().getRealClass().getName().getBytes()).cat(": {".getBytes());
|
1014
|
-
final boolean[] firstEntry = new boolean[1];
|
1015
|
-
|
1016
|
-
firstEntry[0] = true;
|
1017
|
-
final boolean is19 = context.runtime.is1_9();
|
1018
|
-
visitAll(new Visitor() {
|
1019
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
1020
|
-
//if (!firstEntry[0]) str.cat((byte)',').cat((byte)' ');
|
1021
|
-
if (!firstEntry[0]) str.cat(comma_breakable(context, pp)).cat((byte)' ');
|
1022
|
-
|
1023
|
-
RubyString inspectedKey = inspect(context, key);
|
1024
|
-
RubyString inspectedValue = inspect(context, value);
|
1025
|
-
|
1026
|
-
if (is19) {
|
1027
|
-
str.cat19(inspectedKey);
|
1028
|
-
str.cat((byte)'=').cat((byte)'>');
|
1029
|
-
str.cat19(inspectedValue);
|
1030
|
-
} else {
|
1031
|
-
str.cat(inspectedKey);
|
1032
|
-
str.cat((byte)'=').cat((byte)'>');
|
1033
|
-
str.cat(inspectedValue);
|
1034
|
-
}
|
1035
|
-
|
1036
|
-
firstEntry[0] = false;
|
1037
|
-
}
|
1038
|
-
});
|
1039
|
-
str.cat((byte) '}').cat(comma_breakable(context, pp)).cat(" default=".getBytes());
|
1040
|
-
if (ifNone == null) {
|
1041
|
-
str.cat("nil".getBytes());
|
1042
|
-
} else {
|
1043
|
-
str.cat(inspect(context, ifNone));
|
1044
|
-
}
|
1045
|
-
str.cat(comma_breakable(context, pp)).cat(" cmp_proc=".getBytes());
|
1046
|
-
if (cmpProc == null) {
|
1047
|
-
str.cat("nil".getBytes());
|
1048
|
-
} else {
|
1049
|
-
str.cat(inspect(context, cmpProc));
|
1050
|
-
}
|
1051
|
-
str.cat((byte)'>');
|
1052
|
-
return str;
|
1223
|
+
public void setValue(IRubyObject val) {
|
1224
|
+
this.value = val;
|
1053
1225
|
}
|
1054
1226
|
|
1055
|
-
|
1056
|
-
|
1057
|
-
if (getRuntime().isInspecting(this)) return getRuntime().newString("#<RBTree: ...>");
|
1058
|
-
|
1059
|
-
try {
|
1060
|
-
getRuntime().registerInspecting(this);
|
1061
|
-
return inspectMultiRBTree(context, null);
|
1062
|
-
} finally {
|
1063
|
-
getRuntime().unregisterInspecting(this);
|
1064
|
-
}
|
1227
|
+
public void setBlack() {
|
1228
|
+
this.color = Color.BLACK;
|
1065
1229
|
}
|
1066
1230
|
|
1067
|
-
|
1068
|
-
|
1069
|
-
Ruby runtime = context.runtime;
|
1070
|
-
if (runtime.isInspecting(this)) return runtime.newString("{...}");
|
1071
|
-
try {
|
1072
|
-
runtime.registerInspecting(this);
|
1073
|
-
return to_a().to_s();
|
1074
|
-
} finally {
|
1075
|
-
runtime.unregisterInspecting(this);
|
1076
|
-
}
|
1231
|
+
public void setRed() {
|
1232
|
+
this.color = Color.RED;
|
1077
1233
|
}
|
1078
1234
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
iteratorCount.incrementAndGet();
|
1082
|
-
}
|
1083
|
-
private void iteratorExit() {
|
1084
|
-
iteratorCount.decrementAndGet();
|
1235
|
+
public boolean isNull() {
|
1236
|
+
return false;
|
1085
1237
|
}
|
1086
1238
|
|
1087
|
-
|
1088
|
-
|
1089
|
-
throw getRuntime().newRuntimeError("can't add a new key into RBTree during iteration");
|
1090
|
-
}
|
1239
|
+
public boolean isLeft() {
|
1240
|
+
return this == this.parent.left;
|
1091
1241
|
}
|
1092
1242
|
|
1093
|
-
|
1094
|
-
|
1095
|
-
visitor.visit(x.getKey(), x.getValue());
|
1096
|
-
}
|
1243
|
+
public boolean isRight() {
|
1244
|
+
return this == this.parent.right;
|
1097
1245
|
}
|
1098
1246
|
|
1099
|
-
public
|
1100
|
-
|
1101
|
-
iteratorEntry();
|
1102
|
-
for (Node x = maximum(); !x.isNull(); x = predecessor(x)) {
|
1103
|
-
visitor.visit(x.getKey(), x.getValue());
|
1104
|
-
}
|
1105
|
-
} finally {
|
1106
|
-
iteratorExit();
|
1107
|
-
}
|
1247
|
+
public Node getSibling() {
|
1248
|
+
return isLeft() ? this.parent.right : this.parent.left;
|
1108
1249
|
}
|
1250
|
+
}
|
1109
1251
|
|
1110
|
-
public void iteratorVisitAll(Visitor visitor){
|
1111
|
-
try {
|
1112
|
-
iteratorEntry();
|
1113
|
-
for (Node x = minimum(); !x.isNull(); x = successor(x)) {
|
1114
|
-
visitor.visit(x.getKey(), x.getValue());
|
1115
|
-
}
|
1116
|
-
} finally {
|
1117
|
-
iteratorExit();
|
1118
|
-
}
|
1119
|
-
}
|
1120
1252
|
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
private static final ObjectMarshal RBTREE_MARSHAL = new ObjectMarshal() {
|
1128
|
-
public void marshalTo(Ruby runtime, final Object obj, RubyClass recv, final MarshalStream output) throws IOException {
|
1129
|
-
MultiRBTree rbtree = (MultiRBTree) obj;
|
1130
|
-
if (rbtree.size == 0) throw runtime.newArgumentError("cannot dump empty tree");
|
1131
|
-
if (rbtree.cmpProc != null) throw runtime.newArgumentError("cannot dump rbtree with compare proc");
|
1132
|
-
output.registerLinkTarget(rbtree);
|
1133
|
-
output.writeInt(rbtree.size);
|
1134
|
-
try {
|
1135
|
-
rbtree.visitAll(new Visitor() {
|
1136
|
-
public void visit(IRubyObject key, IRubyObject value) {
|
1137
|
-
try {
|
1138
|
-
output.dumpObject(key);
|
1139
|
-
output.dumpObject(value);
|
1140
|
-
} catch (IOException e) {
|
1141
|
-
throw new VisitorIOException(e);
|
1142
|
-
}
|
1143
|
-
}
|
1144
|
-
});
|
1145
|
-
} catch (VisitorIOException e) {
|
1146
|
-
throw (IOException) e.getCause();
|
1147
|
-
}
|
1148
|
-
//if (!rbtree.ifNone != null) output.dumpObject(rbtree.ifNone);
|
1149
|
-
}
|
1253
|
+
private static class NilNode extends Node {
|
1254
|
+
private static NilNode nil = null;
|
1255
|
+
private NilNode() {
|
1256
|
+
this.color = Color.BLACK;
|
1257
|
+
this.left = this.right = this.parent = null;
|
1258
|
+
}
|
1150
1259
|
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
}
|
1158
|
-
//if (defaultValue) result.default_value_set(input.unmarshalObject());
|
1159
|
-
return result;
|
1160
|
-
}
|
1161
|
-
};
|
1260
|
+
public static NilNode getInstance() {
|
1261
|
+
if (nil == null) {
|
1262
|
+
nil = new NilNode();
|
1263
|
+
}
|
1264
|
+
return nil;
|
1265
|
+
}
|
1162
1266
|
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
public synchronized Throwable fillInStackTrace() {
|
1170
|
-
return null;
|
1171
|
-
}
|
1172
|
-
}
|
1173
|
-
|
1174
|
-
private static final Found FOUND = new Found();
|
1175
|
-
|
1176
|
-
private static class FoundKey extends Found {
|
1177
|
-
public final IRubyObject key;
|
1178
|
-
FoundKey(IRubyObject key) {
|
1179
|
-
super();
|
1180
|
-
this.key = key;
|
1181
|
-
}
|
1182
|
-
}
|
1267
|
+
@Override
|
1268
|
+
public boolean isNull() {
|
1269
|
+
return true;
|
1270
|
+
}
|
1271
|
+
}
|
1272
|
+
private enum Color { RED, BLACK }
|
1183
1273
|
}
|