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.
@@ -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
  }