rbtree-jruby 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,1150 +34,1240 @@ import java.util.List;
34
34
 
35
35
  @JRubyClass(name = "MultiRBTree")
36
36
  public class MultiRBTree extends RubyObject {
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) {
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
- for (int i = 0; i < args.length; i += 2) {
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
- @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.getValue();
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
- @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 hash;
318
- }
319
-
320
- @JRubyMethod
321
- public IRubyObject to_rbtree() {
322
- return this;
323
- }
324
-
325
- @JRubyMethod(name = "readjust", optional = 1)
326
- public IRubyObject readjust(ThreadContext context, IRubyObject[] args, Block block) {
327
- RubyProc oldProc = cmpProc;
328
- RubyProc cmpfunc = null;
329
- if (block.isGiven()) {
330
- if (args.length > 0) raiseArgumeneError();
331
- cmpfunc = getRuntime().newProc(Block.Type.PROC, block);
332
- } else if (args.length == 1) {
333
- if (args[0] instanceof RubyProc) {
334
- cmpfunc = (RubyProc) args[0];
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 ifNone;
372
- }
373
-
374
- @JRubyMethod(name = "default_proc")
375
- public IRubyObject getDefaultProc() {
376
- return (flags & PROCDEFAULT_HASH_F) != 0 ? ifNone : getRuntime().getNil();
377
- }
378
-
379
- @JRubyMethod(name = "cmp_proc")
380
- public IRubyObject getCmpProc() {
381
- return this.cmpProc;
382
- }
383
-
384
- public MultiRBTree internalPut(ThreadContext context, IRubyObject key, IRubyObject value) {
385
- return internalPut(context, key, value, true);
386
- }
387
-
388
- public MultiRBTree internalPut(ThreadContext context, IRubyObject key, IRubyObject value, boolean checkExisting) {
389
- if (!dupes && checkExisting) {
390
- Node node = internalGet(context, (RubyObject) key);
391
- if (node != null) {
392
- node.setValue(value);
393
- return this;
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
- Node x = new Node((RubyObject) key, value);
398
- internalPutHelper(context, x);
399
- while (x != this.root && x.getParent().getColor() == Color.RED){
400
- if (x.getParent() == x.getParent().getParent().getLeft()) {
401
- Node y = x.getParent().getParent().getRight();
402
- if (!y.isNull() && y.color == Color.RED) {
403
- x.getParent().setColor(Color.BLACK);
404
- y.setColor(Color.BLACK);
405
- x.getParent().getParent().setColor(Color.RED);
406
- x = x.getParent().getParent();
407
- } else {
408
- if (x == x.getParent().getRight()) {
409
- x = x.getParent();
410
- leftRotate(x);
411
- }
412
- x.getParent().setColor(Color.BLACK);
413
- x.getParent().getParent().setColor(Color.RED);
414
- rightRotate(x.getParent().getParent());
415
- }
416
- } else {
417
- Node y = x.getParent().getParent().getLeft();
418
- if (!y.isNull() && y.isRed()) {
419
- x.getParent().setBlack();
420
- y.setBlack();
421
- x.getParent().getParent().setRed();
422
- x = x.getParent().getParent();
423
- } else {
424
- if (x == x.getParent().getLeft()) {
425
- x = x.getParent();
426
- rightRotate(x);
427
- }
428
- x.getParent().setBlack();
429
- x.getParent().getParent().setRed();
430
- leftRotate(x.getParent().getParent());
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
- root.setBlack();
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
- public IRubyObject internalDelete(ThreadContext context, Node z) {
439
- Node y = (z.getLeft().isNull() || z.getRight().isNull()) ? z : successor(z);
440
- Node x = y.getLeft().isNull() ? y.getRight() : y.getLeft();
441
- x.setParent(y.getParent());
442
- if (y.getParent().isNull()) {
443
- this.root = x;
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
- if (y == y.getParent().getLeft()) {
446
- y.getParent().setLeft(x);
447
- } else {
448
- y.getParent().setRight(x);
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
- if (y != z) {
452
- z.setKey(y.getKey());
453
- z.setValue(y.getValue());
454
- }
455
- if (y.isBlack()) deleteFixup(x);
456
- this.size -= 1;
457
-
458
- return newArray(y);
459
- }
460
-
461
- private Node minimum() {
462
- if (this.root.isNull()) {
463
- return this.root;
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
- return minimum(this.root);
466
- }
467
-
468
- private Node minimum(Node x) {
469
- while (!x.getLeft().isNull()) {
470
- x = x.getLeft();
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
- private Node maximum() {
476
- if (this.root.isNull()) {
477
- return this.root;
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
- return maximum(this.root);
480
- }
481
-
482
- private Node maximum(Node x) {
483
- while (!x.getRight().isNull())
484
- x = x.getRight();
485
- return x;
486
- }
487
-
488
- // this is wrong, it cannot grant walk all nodes..
489
- private Node successor(Node x) {
490
- if (!x.getRight().isNull()) return minimum(x.getRight());
491
- Node y = x.getParent();
492
- while (!y.isNull() && x == y.getRight()) {
493
- x = y;
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
- return y;
497
- }
498
-
499
- private Node predecessor(Node x) {
500
- if (!x.getLeft().isNull()) return maximum(x.getLeft());
501
- Node y = x.getParent();
502
- while (!y.isNull() && x == y.getLeft()) {
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
- return y;
507
- }
508
-
509
- @JRubyMethod(name = {"each_pair", "each"})
510
- public IRubyObject rbtree_each(final ThreadContext context, final Block block) {
511
- return block.isGiven() ? eachCommon(context, block)
512
- : enumeratorize(getRuntime(), this, "each");
513
- }
514
-
515
- @JRubyMethod
516
- public IRubyObject each_key(final ThreadContext context, final Block block) {
517
- return block.isGiven() ? each_keyCommon(context, block) : enumeratorize(context.runtime, this, "each_key");
518
- }
519
-
520
- public MultiRBTree each_keyCommon(final ThreadContext context, final Block block) {
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
- internalDelete(context, node);
572
- return node.getValue();
573
- }
574
-
575
- @JRubyMethod
576
- public IRubyObject delete_if(final ThreadContext context, final Block block) {
577
- return block.isGiven() ? delete_ifInternal(context, block) : enumeratorize(context.runtime, this, "delete_if");
578
- }
579
-
580
- private IRubyObject delete_ifInternal(final ThreadContext context, final Block block) {
581
- List<Node> nodeList = new ArrayList<Node>();
582
- try {
583
- iteratorEntry();
584
- for (Node x = minimum(); !x.isNull(); x = successor(x)) {
585
- if (block.yieldSpecific(context, x.getKey(), x.getValue()).isTrue()) {
586
- nodeList.add(x);
587
- }
588
- }
589
- // delete backward
590
- for (int i = nodeList.size() - 1; i >= 0; i--) {
591
- internalDelete(context, nodeList.get(i));
592
- }
593
- } finally {
594
- iteratorExit();
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
- return this;
597
- }
598
-
599
- @JRubyMethod(name = "reject!")
600
- public IRubyObject reject_bang(final ThreadContext context, final Block block) {
601
- return block.isGiven() ? reject_bangInternal(context, block) : enumeratorize(context.runtime, this, "reject!");
602
- }
603
-
604
- private IRubyObject reject_bangInternal(ThreadContext context, Block block) {
605
- int n = size;
606
- delete_if(context, block);
607
- if (n == size) return getRuntime().getNil();
608
- return this;
609
- }
610
-
611
- @JRubyMethod
612
- public IRubyObject reject(final ThreadContext context, final Block block) {
613
- return block.isGiven() ? rejectInternal(context, block) : enumeratorize(context.runtime, this, "reject");
614
- }
615
-
616
- private IRubyObject rejectInternal(ThreadContext context, Block block) {
617
- return ((MultiRBTree) dup()).reject_bangInternal(context, block);
618
- }
619
-
620
- @JRubyMethod
621
- public IRubyObject select(final ThreadContext context, final Block block) {
622
- final Ruby runtime = getRuntime();
623
- if (!block.isGiven()) return enumeratorize(runtime, this, "select");
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
- return result;
661
- }
662
-
663
- @JRubyMethod
664
- public IRubyObject invert(final ThreadContext context) {
665
- final MultiRBTree rbtree = (MultiRBTree) getMetaClass().getRealClass().allocate();
666
- iteratorVisitAll(new Visitor() {
667
- public void visit(IRubyObject key, IRubyObject value) {
668
- rbtree.internalPut(context, value, key);
669
- }
670
- });
671
- return rbtree;
672
- }
673
-
674
- private IRubyObject nodeOrDefault(ThreadContext context, Node node) {
675
- return nodeOrDefault(context, node, false);
676
- }
677
-
678
- private IRubyObject nodeOrDefault(ThreadContext context, Node node, boolean deleteNode) {
679
- if (node.isNull()) {
680
- if (this.ifNone == null)
681
- return getRuntime().getNil();
682
- if ((flags & PROCDEFAULT_HASH_F) != 0)
683
- return RuntimeHelpers.invoke(context, ifNone, "call", this, getRuntime().getNil());
684
- return ifNone;
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
- if (deleteNode) {
687
- internalDelete(context, node);
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
- @JRubyMethod(name = {"reverse_inorder_walk", "reverse_each"})
693
- public IRubyObject reverse_each(final ThreadContext context, final Block block) {
694
- return block.isGiven() ? reverse_eachInternal(context, block) : enumeratorize(context.runtime, this, "reverse_each");
695
- }
696
-
697
- private IRubyObject reverse_eachInternal(final ThreadContext context, final Block block) {
698
- iteratorReverseVisitAll(new Visitor() {
699
- public void visit(IRubyObject key, IRubyObject value) {
700
- block.yieldSpecific(context, key, value);
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
- return this;
1147
+ } catch (VisitorIOException e) {
1148
+ throw (IOException) e.getCause();
1149
+ }
704
1150
  }
705
1151
 
706
- public Node internalGet(ThreadContext context, RubyObject key) {
707
- Node x = this.root;
708
- while (!x.isNull()) {
709
- int ret = compare(context, key, x.getKey());
710
- if (ret > 0) {
711
- x = x.getRight();
712
- } else if (ret < 0) {
713
- x = x.getLeft();
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
- @JRubyMethod(name = "empty?")
722
- public IRubyObject empty_p(ThreadContext context) {
723
- return getRuntime().newBoolean(size == 0);
724
- }
1163
+ private static abstract class Visitor {
1164
+ public abstract void visit(IRubyObject key, IRubyObject value);
1165
+ }
725
1166
 
726
- @JRubyMethod(name = "black_height")
727
- public IRubyObject blackHeight() {
728
- Node x = this.root;
729
- int height = 0;
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
- private void leftRotate(Node x) {
738
- Node y = x.getRight();
739
- x.setRight(y.getLeft());
1174
+ private static final Found FOUND = new Found();
740
1175
 
741
- if (!y.getLeft().isNull()) {
742
- y.getLeft().setParent(x);
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
- private void rightRotate(Node x) {
759
- Node y = x.getLeft();
760
- x.setLeft(y.getRight());
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
- private int compare(ThreadContext context, RubyObject a, RubyObject b) {
782
- if (context == null || cmpProc == null)
783
- return a.compareTo(b);
784
- return (int) cmpProc.call(context, new IRubyObject[]{a, b}).convertToInteger().getLongValue();
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
- private void internalPutHelper(ThreadContext context, Node z) {
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
- private RubyArray newArray(Node node) {
956
- return getRuntime().newArray(node.getKey(), node.getValue());
1196
+ protected Node(IRubyObject key, IRubyObject value) {
1197
+ this(key, value, Color.RED);
957
1198
  }
958
1199
 
959
- @JRubyMethod(name = {"replace", "initialize_copy"})
960
- public IRubyObject replace(final ThreadContext context, IRubyObject other) {
961
- checkCompatible(context.runtime, other);
962
- MultiRBTree otherTree = (MultiRBTree) other;
963
- return replaceInternal(context, otherTree, otherTree.cmpProc);
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
- private IRubyObject replaceInternal(final ThreadContext context, MultiRBTree otherTree, RubyProc cmpfunc) {
967
- init();
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
- @JRubyMethod(name = "==")
981
- public IRubyObject op_equal(IRubyObject other) {
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
- private boolean similar(MultiRBTree other) {
1004
- return this.cmpProc == other.cmpProc;
1215
+ public Node getGrandParent() {
1216
+ return this.parent.parent;
1005
1217
  }
1006
1218
 
1007
- private byte comma_breakable(ThreadContext context, IRubyObject pp) {
1008
- return (byte) ',';
1219
+ public void setKey(IRubyObject key) {
1220
+ this.key = (RubyObject) key;
1009
1221
  }
1010
1222
 
1011
- private IRubyObject inspectMultiRBTree(final ThreadContext context, final IRubyObject pp) {
1012
- final RubyString str = RubyString.newStringLight(context.runtime, DEFAULT_INSPECT_STR_SIZE);
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
- @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
- }
1227
+ public void setBlack() {
1228
+ this.color = Color.BLACK;
1065
1229
  }
1066
1230
 
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
- }
1231
+ public void setRed() {
1232
+ this.color = Color.RED;
1077
1233
  }
1078
1234
 
1079
- private AtomicInteger iteratorCount = new AtomicInteger(0);
1080
- private void iteratorEntry() {
1081
- iteratorCount.incrementAndGet();
1082
- }
1083
- private void iteratorExit() {
1084
- iteratorCount.decrementAndGet();
1235
+ public boolean isNull() {
1236
+ return false;
1085
1237
  }
1086
1238
 
1087
- private void checkIterating() {
1088
- if (iteratorCount.get() > 0) {
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
- private void visitAll(Visitor visitor) {
1094
- for (Node x = minimum(); !x.isNull(); x = successor(x)) {
1095
- visitor.visit(x.getKey(), x.getValue());
1096
- }
1243
+ public boolean isRight() {
1244
+ return this == this.parent.right;
1097
1245
  }
1098
1246
 
1099
- public void iteratorReverseVisitAll(Visitor visitor){
1100
- try {
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
- private static class VisitorIOException extends RuntimeException {
1122
- VisitorIOException(Throwable cause) {
1123
- super(cause);
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
- public Object unmarshalFrom(Ruby runtime, RubyClass type, UnmarshalStream input) throws IOException {
1152
- MultiRBTree result = (MultiRBTree) type.allocate();
1153
- input.registerLinkTarget(result);
1154
- int size = input.unmarshalInt();
1155
- for (int i = 0; i < size; i++) {
1156
- result.internalPut(runtime.getCurrentContext(), input.unmarshalObject(), input.unmarshalObject());
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
- private static abstract class Visitor {
1164
- public abstract void visit(IRubyObject key, IRubyObject value);
1165
- }
1166
-
1167
- private static class Found extends RuntimeException {
1168
- @Override
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
  }