Make ConcurrentSet implement SortedSet
authorMaciej Piechotka <uzytkownik2@gmail.com>
Sun, 2 Dec 2012 10:58:09 +0000 (10:58 +0000)
committerMaciej Piechotka <uzytkownik2@gmail.com>
Tue, 25 Dec 2012 19:49:30 +0000 (20:49 +0100)
gee/concurrentset.vala
tests/testconcurrentset.vala
tests/testsortedset.vala

index 62d937d..854340f 100644 (file)
@@ -28,7 +28,7 @@
  * of structure during iteration is allowed. However the change may not be immidiatly
  * visible to other threads.
  */
-public class Gee.ConcurrentSet<G> : AbstractSet<G> {
+public class Gee.ConcurrentSet<G> : AbstractSortedSet<G> {
        public ConcurrentSet (owned CompareDataFunc<G>? compare_func = null) {
                if (compare_func == null) {
                        compare_func = Functions.get_compare_func_for (typeof (G));
@@ -63,10 +63,14 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                }
                uint32 rand_int = rnd->int_range (0, int32.MAX);
                uint8 height = 1 + (uint8)GLib.Bit.nth_lsf (~rand_int, -1);
-               Tower<G> prev = _head;
-               if (Tower.search<G> (_cmp, key, ref prev, null, height - 1)) {
+               TowerIter<G> prev = TowerIter<G>();
+               prev._iter[height - 1] = _head;
+               if (Tower.search<G> (_cmp, key, ref prev._iter[height - 1], null, height - 1)) {
                        return false;
                }
+               for (int i = height - 2; i >= 0; i--) {
+                       prev._iter[i] = prev._iter[height - 1];
+               }
                Tower<G>? result = Tower.insert<G> (_cmp, ref prev, key, height - 1);
                if (result != null) {
                        GLib.AtomicInt.inc (ref _size);
@@ -76,7 +80,11 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
 
        public override bool remove (G item) {
                HazardPointer.Context ctx = new HazardPointer.Context ();
-               bool result = Tower.remove_key<G> (_cmp, _head, item);
+               TowerIter<G> prev = TowerIter<G>();
+               for (int i = 0; i < _MAX_HEIGHT; i++) {
+                       prev._iter[i] = _head;
+               }
+               bool result = Tower.remove_key<G> (_cmp, ref prev, item);
                if (result) {
                        GLib.AtomicInt.dec_and_test (ref _size);
                }
@@ -91,6 +99,119 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                }
        }
 
+       public override G first () {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               Tower<G>? prev = null;
+               Tower<G> curr = _head;
+               if (Tower.proceed<G> (_cmp, ref prev, ref curr, 0)) {
+                       return curr._data;
+               } else {
+                       return null;
+               }
+       }
+
+       public override G last () {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               Tower<G>? prev = null;
+               Tower<G> curr = _head;
+               bool found = false;
+               for (int i = _MAX_HEIGHT; i >= 0; i--) {
+                       while (Tower.proceed<G> (_cmp, ref prev, ref curr, 0)) {
+                               found = true;
+                       }
+               }
+               if (!found) {
+                       return null;
+               }
+               return curr._data;
+       }
+
+       public override Gee.Iterator<G>? iterator_at (G element) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               TowerIter<G> prev = TowerIter<G> ();
+               TowerIter<G> curr;
+               for (int i = 0; i < _MAX_HEIGHT; i++) {
+                       prev._iter[i] = _head;
+               }
+               if (!Tower.search_from_bookmark<G> (_cmp, element, ref prev, out curr)) {
+                       return null;
+               }
+               return new Iterator<G>.point_at (this, ref prev, curr._iter[0]);
+       }
+
+       public override G? lower (G element) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               Tower<G> prev = _head;
+               Tower.search<G> (_cmp, element, ref prev);
+               if (prev == _head) {
+                       return null;
+               }
+               return prev._data;
+       }
+
+       public override G? higher (G element) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               Tower<G> prev = _head;
+               Tower<G>? next;
+               if (Tower.search<G> (_cmp, element, ref prev, out next)) {
+                       if (!Tower.proceed<G> (_cmp, ref prev, ref next, 0)) {
+                               return null;
+                       }
+               }
+               if (next == null) {
+                       return null;
+               }
+               return next._data;
+       }
+
+       public override G? floor (G element) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               Tower<G> prev = _head;
+               Tower<G>? next;
+               if (Tower.search<G> (_cmp, element, ref prev, out next)) {
+                       return next._data;
+               } else if (prev != _head) {
+                       return prev._data;
+               } else {
+                       return null;
+               }
+       }
+
+       public override G? ceil (G element) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               Tower<G> prev = _head;
+               Tower<G>? next;
+               Tower.search<G> (_cmp, element, ref prev, out next);
+               if (next == null) {
+                       return null;
+               }
+               return next._data;
+       }
+
+       public override SortedSet<G> head_set (G before) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               return new SubSet<G> (new Range<G>.head (this, before));
+       }
+
+       public override SortedSet<G> tail_set (G after) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               return new SubSet<G> (new Range<G>.tail (this, after));
+       }
+       public override SortedSet<G> sub_set (G from, G to) {
+               HazardPointer.Context ctx = new HazardPointer.Context ();
+               return new SubSet<G> (new Range<G> (this, from, to));
+       }
+
+       private unowned G max (G a, G b, out bool changed = null) {
+               changed = _cmp (a, b) < 0;
+               return changed ? b : a;
+       }
+
+       private unowned G min (G a, G b, out bool changed = null) {
+               changed = _cmp (a, b) > 0;
+               return changed ? b : a;
+       }
+
 #if DEBUG
        public void dump () {
                for (int i = _MAX_HEIGHT - 1; i >= 0; i--) {
@@ -103,6 +224,7 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                                        printed = true;
                                }
                                stderr.printf("    Node %p%s - %s\n", curr, state == State.NONE ? "" : state == State.MARKED ? " (MARKED)" : " (FLAGGED)", (string)curr._data);
+                               assert (curr._height > i);
                        }
                }
        }
@@ -124,34 +246,32 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                        assert (_curr != null);
                }
 
-               public Iterator.pointing (ConcurrentSet cset, Tower<G>[] prev, Tower<G> curr) {
-                       for (int i = 0; i < _MAX_HEIGHT; i++) {
-                               _prev[i] = prev[i];
-                       }
+               public Iterator.point_at (ConcurrentSet cset, ref TowerIter<G> prev, Tower<G> curr) {
                        _curr = curr;
                        _set = cset;
+                       _prev = prev;
                        assert (_curr != null);
                }
 
                public new bool foreach (ForallFunc<G> f) {
                        assert (_curr != null);
                        HazardPointer.Context ctx = new HazardPointer.Context ();
-                       if (_prev[0] != null && !_removed) {
+                       if (_prev._iter[0] != null && !_removed) {
                                if (!f (_curr._data)) {
                                        assert (_curr != null);
                                        return false;
                                }
                        }
-                       Tower<G> new_prev = _prev[0];
+                       Tower<G> new_prev = _prev._iter[0];
                        Tower<G>? new_curr = _curr;
                        while (Tower.proceed<G> (_set._cmp, ref new_prev, ref new_curr, 0)) {
                                assert (_curr != null);
                                if (!_removed) {
                                        //FIXME: Help mark/delete on the way
-                                       _prev[0] = new_prev;
-                                       int prev_height = GLib.AtomicInt.get(ref _prev[0]._height);
+                                       _prev._iter[0] = new_prev;
+                                       int prev_height = _prev._iter[0].get_height();
                                        for (int i = 1; i < prev_height; i++) {
-                                               _prev[i] = _prev[0];
+                                               _prev._iter[i] = _prev._iter[0];
                                        }
                                }
                                _curr = new_curr;
@@ -167,16 +287,16 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
 
                public bool next () {
                        HazardPointer.Context ctx = new HazardPointer.Context ();
-                       Tower<G> new_prev = _prev[0];
+                       Tower<G>? new_prev = _prev._iter[0];
                        Tower<G>? new_curr = _curr;
                        bool success = Tower.proceed<G> (_set._cmp, ref new_prev, ref new_curr, 0);
                        if (success) {
                                if (!_removed) {
                                        //FIXME: Help mark/delete on the way
-                                       _prev[0] = (owned)new_prev;
-                                       int prev_height = GLib.AtomicInt.get(ref _prev[0]._height);
+                                       _prev._iter[0] = (owned)new_prev;
+                                       int prev_height = _prev._iter[0].get_height();
                                        for (int i = 1; i < prev_height; i++) {
-                                               _prev[i] = _prev[0];
+                                               _prev._iter[i] = _prev._iter[0];
                                        }
                                }
                                _curr = (owned)new_curr;
@@ -188,7 +308,7 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
 
                public bool has_next () {
                        HazardPointer.Context ctx = new HazardPointer.Context ();
-                       Tower<G> prev = _prev[0];
+                       Tower<G> prev = _prev._iter[0];
                        Tower<G>? curr = _curr;
                        return Tower.proceed<G> (_set._cmp, ref prev, ref curr, 0);
                }
@@ -201,22 +321,746 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                public void remove () {
                        HazardPointer.Context ctx = new HazardPointer.Context ();
                        assert (valid);
-                       if (Tower.remove<G> (_set._cmp, _prev, _curr)) {
+                       if (Tower.remove<G> (_set._cmp, ref _prev, _curr)) {
                                AtomicInt.dec_and_test (ref _set._size);
                        }
                        _removed = true;
                }
 
-               public bool valid { get { return _prev[0] != null && !_removed; } }
+               public bool valid { get { return _prev._iter[0] != null && !_removed; } }
 
                public bool read_only { get { return true; } }
 
                private bool _removed = false;
                private ConcurrentSet<G> _set;
-               private Tower<G>? _prev[31 /*_MAX_HEIGHT*/];
+               private TowerIter<G> _prev;
                private Tower<G> _curr;
        }
 
+       private class SubSet<G> : AbstractSortedSet<G> {
+               public override int size {
+                       get {
+                               HazardPointer.Context ctx = new HazardPointer.Context ();
+                               Tower<G>? curr;
+                               Range.improve_bookmark<G> (_range, out curr);
+                               if (curr != null) {
+                                       int acc = 1;
+                                       Tower<G>? prev = HazardPointer.get_pointer<Tower<G>> (&_range._bookmark._iter[0]);
+                                       while (Range.proceed<G> (_range, ref prev, ref curr, 0)) {
+                                               acc++;
+                                       }
+                                       return acc;
+                               } else {
+                                       return 0;
+                               }
+                       }
+               }
+
+               public bool is_empty {
+                       get {
+                               HazardPointer.Context ctx = new HazardPointer.Context ();
+                               Tower<G>? curr;
+                               Range.improve_bookmark<G> (_range, out curr);
+                               return curr != null;
+                       }
+               }
+
+               public override bool read_only { get { return false; } }
+
+               public SubSet (Range<G> range) {
+                       _range = range;
+               }
+
+               public override Gee.Iterator<G> iterator () {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       return new SubIterator<G> (_range);
+               }
+
+               public override bool contains (G item) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       if (!Range.inside<G> (_range, item)) {
+                               return false;
+                       }
+                       TowerIter<G> prev;
+                       Range.improve_bookmark<G> (_range, null, out prev);
+                       return Tower.search_from_bookmark<G> (_range._set._cmp, item, ref prev);
+               }
+
+               public override bool add (G key) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       if (!Range.inside<G> (_range, key)) {
+                               return false;
+                       }
+                       TowerIter<G> prev;
+                       Range.improve_bookmark<G> (_range, null, out prev);
+                       Rand *rnd = ConcurrentSet.rand.get ();
+                       if (rnd == null) {
+                               rand.set (rnd = new Rand ());
+                       }
+                       uint32 rand_int = rnd->int_range (0, int32.MAX);
+                       uint8 height = 1 + (uint8)GLib.Bit.nth_lsf (~rand_int, -1);
+                       if (Tower.search_from_bookmark<G> (_range._set._cmp, key, ref prev, null, height - 1)) {
+                               return false;
+                       }
+                       for (int i = height - 2; i >= 0; i--) {
+                               prev._iter[i] = prev._iter[height - 1];
+                       }
+                       Tower<G>? result = Tower.insert<G> (_range._set._cmp, ref prev, key, height - 1);
+                       if (result != null) {
+                               GLib.AtomicInt.inc (ref _range._set._size);
+                       }
+                       return result != null;
+               }
+
+               public override bool remove (G key) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       if (!Range.inside<G> (_range, key)) {
+                               return false;
+                       }
+                       TowerIter<G> prev;
+                       Range.improve_bookmark<G> (_range, null, out prev);
+                       // FIXME: Use full bookmark
+                       bool result = Tower.remove_key<G> (_range._set._cmp, ref prev, key);
+                       if (result) {
+                               GLib.AtomicInt.dec_and_test (ref _range._set._size);
+                       }
+                       return result;
+               }
+
+               public override void clear () {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       TowerIter<G> prev;
+                       Tower<G>? first;
+                       Range.improve_bookmark<G> (_range, out first, out prev);
+                       while (first != null) {
+                               Tower.remove<G> (_range._set._cmp, ref prev, first);
+                               Range.improve_bookmark<G> (_range, out first, out prev);
+                       }
+               }
+
+               public override G? first () {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       Tower<G>? first;
+                       Range.improve_bookmark<G> (_range, out first);
+                       if (first == null) {
+                               return null;
+                       }
+                       return first._data;
+               }
+
+               public override G? last () {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       TowerIter<G> prev;
+                       Range.improve_bookmark<G> (_range, null, out prev);
+                       Tower<G>? curr = null;
+                       for (int i = _MAX_HEIGHT - 1; i >= 0; i--) {
+                               if (curr == null) {
+                                       curr = prev._iter[i].get_next ((uint8)i);
+                                       if (curr == null || !Range.inside<G> (_range, curr._data)) {
+                                               curr = null;
+                                               continue;
+                                       }
+                               }
+                               bool improved = false;
+                               while (Range.proceed<G> (_range, ref prev._iter[i], ref curr, (uint8)i)) {
+                                       improved = true;
+                               }
+                               if (improved && i > 0) {
+                                       prev._iter[i - 1] = prev._iter[i];
+                               }
+                       }
+                       if (curr == null) {
+                               return null;
+                       }
+                       return curr._data;
+               }
+
+               public override Gee.Iterator<G>? iterator_at (G element) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       if (!Range.inside<G> (_range, element)) {
+                               return null;
+                       }
+                       TowerIter<G> prev;
+                       TowerIter<G> next;
+                       Range.improve_bookmark<G> (_range, null, out prev);
+                       if (!Tower.search_from_bookmark<G> (_range._set._cmp, element, ref prev, out next)) {
+                               return null;
+                       }
+                       return new SubIterator<G>.point_at (_range, ref prev, next._iter[0]);
+               }
+
+               public override G? lower (G element) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       switch (Range.cmp<G> (_range, element)) {
+                       case Range.Position.BEFORE:
+                       case Range.Position.EMPTY:
+                               return null;
+                       case Range.Position.INSIDE:
+                               TowerIter<G> prev;
+                               Range.improve_bookmark<G> (_range, null, out prev);
+                               Tower.search_from_bookmark<G> (_range._set._cmp, element, ref prev);
+                               if (prev._iter[0] == _range._set._head || !Range.inside (_range, prev._iter[0]._data)) {
+                                       return null;
+                               }
+                               return prev._iter[0]._data;
+                       case Range.Position.AFTER:
+                               return last ();
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               public override G? higher (G element) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       switch (Range.cmp<G> (_range, element)) {
+                       case Range.Position.BEFORE:
+                               return first ();
+                       case Range.Position.INSIDE:
+                               TowerIter<G> prev;
+                               TowerIter<G> curr;
+                               Range.improve_bookmark<G> (_range, null, out prev);
+                               if (Tower.search_from_bookmark<G> (_range._set._cmp, element, ref prev, out curr)) {
+                                       if (!Tower.proceed<G> (_range._set._cmp, ref prev._iter[0], ref curr._iter[0], 0)) {
+                                               return null;
+                                       }
+                               }
+                               if (curr._iter[0] == null || !Range.inside(_range, curr._iter[0]._data)) {
+                                       return null;
+                               }
+                               return curr._iter[0]._data;
+                       case Range.Position.AFTER:
+                       case Range.Position.EMPTY:
+                               return null;
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               public override G? floor (G element) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       switch (Range.cmp<G> (_range, element)) {
+                       case Range.Position.BEFORE:
+                       case Range.Position.EMPTY:
+                               return null;
+                       case Range.Position.INSIDE:
+                               TowerIter<G> curr;
+                               TowerIter<G> prev;
+                               Range.improve_bookmark<G> (_range, null, out prev);
+                               if (!Tower.search_from_bookmark<G> (_range._set._cmp, element, ref prev, out curr)) {
+                                       curr._iter[0] = (owned)prev._iter[0];
+                               }
+                               if (curr._iter[0] == null || curr._iter[0].is_head () || !Range.inside(_range, curr._iter[0]._data)) {
+                                       return null;
+                               }
+                               return curr._iter[0]._data;
+                       case Range.Position.AFTER:
+                               return last ();
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               public override G? ceil (G element) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       switch (Range.cmp<G> (_range, element)) {
+                       case Range.Position.BEFORE:
+                               return first ();
+                       case Range.Position.INSIDE:
+                               TowerIter<G> curr;
+                               TowerIter<G> prev;
+                               Range.improve_bookmark<G> (_range, null, out prev);
+                               Tower.search_from_bookmark<G> (_range._set._cmp, element, ref prev, out curr);
+                               if (curr._iter[0] == null || !Range.inside(_range, curr._iter[0]._data)) {
+                                       return null;
+                               }
+                               return curr._iter[0]._data;
+                       case Range.Position.AFTER:
+                       case Range.Position.EMPTY:
+                               return null;
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               public override SortedSet<G> head_set (G before) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       return new SubSet<G> (Range.cut_tail (_range, before));
+               }
+
+               public override SortedSet<G> tail_set (G after) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       return new SubSet<G> (Range.cut_head (_range, after));
+               }
+
+               public override SortedSet<G> sub_set (G from, G to) {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       return new SubSet<G> (Range.cut (_range, from, to));
+               }
+
+               private Range<G> _range;
+       }
+
+       private class SubIterator<G> : Object, Traversable<G>, Gee.Iterator<G> {
+               public SubIterator (Range<G> range) {
+                       Range.improve_bookmark<G> (range);
+                       _range = range;
+               }
+
+               public SubIterator.point_at (Range<G> range, ref TowerIter<G> prev, owned Tower<G> curr) {
+                       Range.improve_bookmark<G> (range);
+                       _range = range;
+                       _prev = prev;
+                       _curr = curr;
+               }
+
+               public new bool foreach (ForallFunc<G> f) {
+                       assert (_curr != null);
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       if (!begin ()) {
+                               return true;
+                       }
+                       if (_prev._iter[0] != null && !_removed) {
+                               if (!f (_curr._data)) {
+                                       assert (_curr != null);
+                                       return false;
+                               }
+                       }
+                       Tower<G> new_prev = _prev._iter[0];
+                       Tower<G>? new_curr = _curr;
+                       while (Range.proceed<G> (_range, ref new_prev, ref new_curr, 0)) {
+                               assert (_curr != null);
+                               if (!f (_curr._data)) {
+                                       assert (_curr != null);
+                                       return false;
+                               }
+                               if (!_removed) {
+                                       //FIXME: Help mark/delete on the way
+                                       _prev._iter[0] = (owned)new_prev;
+                                       int prev_height = _prev._iter[0].get_height();
+                                       for (int i = 1; i < prev_height; i++) {
+                                               _prev._iter[i] = _prev._iter[0];
+                                       }
+                               }
+                               _curr = (owned)new_curr;
+                               _removed = false;
+                       }
+                       assert (_curr != null);
+                       return true;
+               }
+
+               public bool next () {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       if (_prev._iter[0] == null) {
+                               return begin ();
+                       } else {
+                               Tower<G> new_prev = _prev._iter[0];
+                               Tower<G> new_curr = _curr;
+                               if (Range.proceed<G> (_range, ref new_prev, ref new_curr, 0)) {
+                                       if (!_removed) {
+                                               //FIXME: Help mark/delete on the way
+                                               _prev._iter[0] = (owned)new_prev;
+                                               int prev_height = _prev._iter[0].get_height();
+                                               for (int i = 1; i < prev_height; i++) {
+                                                       _prev._iter[i] = _prev._iter[0];
+                                               }
+                                       }
+                                       _curr = (owned)new_curr;
+                                       _removed = false;
+                                       return true;
+                               } else {
+                                       return false;
+                               }
+                       }
+               }
+
+               public bool has_next () {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       if (_prev._iter[0] == null) {
+                               Tower<G> next;
+                               Range.improve_bookmark<G> (_range, out next);
+                               if (next != null && Range.beyond<G> (_range, next)) {
+                                       next = null;
+                               }
+                               return next != null;
+                       } else {
+                               Tower<G> new_prev = _prev._iter[0];
+                               Tower<G> new_curr = _curr;
+                               return Range.proceed<G> (_range, ref new_prev, ref new_curr, 0);
+                       }
+               }
+
+               public new G get () {
+                       assert (valid);
+                       return _curr._data;
+               }
+
+               public void remove () {
+                       HazardPointer.Context ctx = new HazardPointer.Context ();
+                       assert (valid);
+                       if (Tower.remove<G> (_range._set._cmp, ref _prev, _curr)) {
+                               AtomicInt.dec_and_test (ref _range._set._size);
+                       }
+                       _removed = true;
+               }
+
+               public bool valid {
+                       get {
+                               bool is_valid = _prev._iter[0] != null && !_removed;
+                               assert (!is_valid || _curr != null);
+                               return is_valid;
+                       }
+               }
+
+               public bool read_only { get { return false; } }
+
+               private bool begin () {
+                       if (_prev._iter[0] != null) {
+                               return true;
+                       }
+                       Range.improve_bookmark<G> (_range, out _curr, out _prev);
+                       if (_curr == null) {
+                               for (int i = 0; i < _MAX_HEIGHT; i++) {
+                                       _prev._iter[i] = null;
+                               }
+                       }
+                       return _curr != null;
+               }
+
+               private Range<G> _range;
+               private TowerIter<G> _prev;
+               private Tower<G>? _curr = null;
+               private bool _removed = false;
+       }
+
+       private class Range<G> {
+               public G? _start;
+               public G? _end;
+               public RangeType _type;
+               public TowerIter<G> _bookmark;
+               public ConcurrentSet<G> _set;
+
+               public Range (ConcurrentSet<G> cset, G start, G end) {
+                       _start = start;
+                       _end = end;
+                       if (cset._cmp(start, end) < 0) {
+                               _type = RangeType.BOUNDED;
+                               for (int i = 0; i < _MAX_HEIGHT; i++) {
+                                       _bookmark._iter[i] = cset._head;
+                               }
+                       } else {
+                               _type = RangeType.EMPTY;
+                       }
+                       _set = cset;
+               }
+
+               public Range.head (ConcurrentSet<G> cset, G end) {
+                       _end = end;
+                       _type = RangeType.HEAD;
+                       for (int i = 0; i < _MAX_HEIGHT; i++) {
+                               _bookmark._iter[i] = cset._head;
+                       }
+                       _set = cset;
+               }
+
+               public Range.tail (ConcurrentSet<G> cset, G start) {
+                       _start = start;
+                       _type = RangeType.TAIL;
+                       for (int i = 0; i < _MAX_HEIGHT; i++) {
+                               _bookmark._iter[i] = cset._head;
+                       }
+                       _set = cset;
+               }
+
+               public Range.empty (ConcurrentSet<G> cset) {
+                       _type = RangeType.EMPTY;
+                       _set = cset;
+               }
+
+               private void copy_bookmark (Range<G> range) {
+                       for (int i = 0; i < _MAX_HEIGHT; i++) {
+                               _bookmark._iter[i] = HazardPointer.get_pointer<Tower<G>> (&range._bookmark._iter[i]);
+                       }
+               }
+
+               public static Range<G> cut_head<G> (Range<G> from, G start) {
+                       Range<G> result = new Range<G>.empty (from._set);
+                       switch (from._type) {
+                       case RangeType.HEAD:
+                               if (from._set._cmp (start, from._end) < 0) {
+                                       result._start = start;
+                                       result._end = from._end;
+                                       result._type = RangeType.BOUNDED;
+                               } else {
+                                       result._type = RangeType.EMPTY;
+                               }
+                               break;
+                       case RangeType.TAIL:
+                               result._start = from._set.max (from._start, start);
+                               result._type = RangeType.TAIL;
+                               break;
+                       case RangeType.BOUNDED:
+                               if (from._set._cmp (from._start, start) < 0) {
+                                       result._start = from._set.max (from._start, start);
+                                       result._end = from._end;
+                                       result._type = RangeType.BOUNDED;
+                               } else {
+                                       result._type = RangeType.EMPTY;
+                               }
+                               break;
+                       case RangeType.EMPTY:
+                               result._type = RangeType.EMPTY;
+                               break;
+                       default:
+                               assert_not_reached ();
+                       }
+                       if (result._type != RangeType.EMPTY) {
+                               improve_bookmark<G> (from);
+                               result.copy_bookmark (from);
+                               improve_bookmark<G> (result);
+                       }
+                       return result;
+               }
+
+               public static Range<G> cut_tail<G> (Range<G> from, G end) {
+                       Range<G> result = new Range<G>.empty (from._set);
+                       switch (from._type) {
+                       case RangeType.HEAD:
+                               result._end = from._set.min (from._end, end);
+                               result._type = RangeType.HEAD;
+                               break;
+                       case RangeType.TAIL:
+                               if (from._set._cmp (from._start, end) < 0) {
+                                       result._start = from._start;
+                                       result._end = end;
+                                       result._type = RangeType.BOUNDED;
+                               } else {
+                                       result._type = RangeType.EMPTY;
+                               }
+                               break;
+                       case RangeType.BOUNDED:
+                               if (from._set._cmp (from._start, end) < 0) {
+                                       result._start = from._start;
+                                       result._end = from._set.min (from._end, end);
+                                       result._type = RangeType.BOUNDED;
+                               } else {
+                                       result._type = RangeType.EMPTY;
+                               }
+                               break;
+                       case RangeType.EMPTY:
+                               result._type = RangeType.EMPTY;
+                               break;
+                       default:
+                               assert_not_reached ();
+                       }
+                       if (result._type != RangeType.EMPTY) {
+                               improve_bookmark<G> (from);
+                               result.copy_bookmark (from);
+                               improve_bookmark<G> (result);
+                       }
+                       return result;
+               }
+
+               public static Range<G> cut<G> (Range<G> from, G start, G end) {
+                       Range<G> result = new Range<G>.empty (from._set);
+                       result._type = RangeType.BOUNDED;
+                       switch (from._type) {
+                       case RangeType.HEAD:
+                               end = from._set.min (from._end, end);
+                               break;
+                       case RangeType.TAIL:
+                               start = from._set.max (from._start, start);
+                               break;
+                       case RangeType.BOUNDED:
+                               start = from._set.max (from._start, start);
+                               end = from._set.min (from._end, end);
+                               break;
+                       case RangeType.EMPTY:
+                               result._type = RangeType.EMPTY;
+                               break;
+                       default:
+                               assert_not_reached ();
+                       }
+                       if (result._type != RangeType.EMPTY && from._set._cmp (start, end) < 0) {
+                               result._start = start;
+                               result._end = end;
+                               result._type = RangeType.BOUNDED;
+                       } else {
+                               result._type = RangeType.EMPTY;
+                       }
+                       if (result._type != RangeType.EMPTY) {
+                               improve_bookmark<G> (from);
+                               result.copy_bookmark (from);
+                               improve_bookmark<G> (result);
+                       }
+                       return result;
+               }
+
+               public static void improve_bookmark<G> (Range<G> range, out Tower<G>? out_curr = null, out TowerIter<G> prev = null) {
+                       prev = TowerIter<G>();
+                       switch (range._type) {
+                       case RangeType.HEAD:
+                               if (&out_curr != null) {
+                                       out_curr = HazardPointer.get_pointer<Tower<G>> (&range._bookmark._iter[0]);
+                                       if (&prev != null) {
+                                               prev._iter[0] = (owned)out_curr;
+                                               out_curr = prev._iter[0].get_next (0);
+                                       } else {
+                                               out_curr = out_curr.get_next (0);
+                                       }
+                               }
+                               if (&prev != null) {
+                                       for (int i = &out_curr != null ? 1 : 0; i < _MAX_HEIGHT; i++) {
+                                               prev._iter[i] = HazardPointer.get_pointer<Tower<G>> (&range._bookmark._iter[i]);
+                                       }
+                               }
+                               break;
+                       case RangeType.EMPTY:
+                               out_curr = null;
+                               break;
+                       case RangeType.TAIL:
+                       case RangeType.BOUNDED:
+                               Tower<G>? last_best = null;
+                               for (int i = _MAX_HEIGHT - 1; i >= 0; i--) {
+                                       Tower<G> curr = HazardPointer.get_pointer<Tower<G>> (&range._bookmark._iter[i]);
+                                       Tower<G> curr_old = curr;
+                                       assert (curr != null);
+                                       Tower.backtrace<G> (ref curr, (uint8)i);
+                                       if (last_best != null && last_best != curr && Tower.compare<G> (range._set._cmp, curr, last_best) < 0) {
+                                               curr = last_best;
+                                       }
+                                       if (curr != curr_old) {
+                                               if (!HazardPointer.compare_and_exchange_pointer<Tower<G>> (&range._bookmark._iter[i], curr_old, curr)) {
+                                                       curr = HazardPointer.get_pointer<Tower<G>> (&range._bookmark._iter[i]);
+                                               }
+                                       }
+                                       Tower<G> next = curr.get_next ((uint8)i);
+                                       if (&out_curr != null && i == 0) {
+                                               out_curr = next;
+                                       }
+                                       while (next != null && Tower.compare_data<G> (range._set._cmp, next, range._start) < 0) {
+                                               Tower.proceed<G> (range._set._cmp, ref curr, ref next, (uint8)i, true);
+                                               if (&curr != null && i == 0 && next != null) {
+                                                       out_curr = next;
+                                               }
+                                               if (Tower.compare_data<G> (range._set._cmp, curr, range._start) < 0) {
+                                                       if (!HazardPointer.compare_and_exchange_pointer<Tower<G>> (&range._bookmark._iter[i], curr_old, curr)) {
+                                                               curr = HazardPointer.get_pointer<Tower<G>> (&range._bookmark._iter[i]);
+                                                       }
+                                                       curr_old = curr;
+                                               } else {
+                                                       break;
+                                               }
+                                       }
+                                       if (&prev != null) {
+                                               prev._iter[i] = curr;
+                                       }
+                                       last_best = (owned)curr;
+                               }
+                               break;
+                       default:
+                               assert_not_reached ();
+                       }
+                       if (&out_curr != null && out_curr != null && Range.beyond<G> (range, out_curr)) {
+                               out_curr = null;
+                       }
+#if DEBUG
+                       stderr.printf("Bookmark after:\n");
+                       for (int i = _MAX_HEIGHT - 1; i >= 0; i--) {
+                               stderr.printf("    Level %d:\n", i);
+                               Tower<string>? t = HazardPointer.get_pointer<Tower<string>> (&range._bookmark._iter[i]);
+                               assert (t == null || t.get_height () > i);
+                               while (t != null) {
+                                       stderr.printf("      %s\n", t.is_head () ? "<<head>>" : t._data);
+                                       t = t.get_next ((uint8)i);
+                                       assert (t == null || t.get_height () > i);
+                               }
+                       }
+#endif
+               }
+
+               public static bool proceed<G> (Range<G> range, ref Tower<G>? prev, ref Tower<G> curr, uint8 level) {
+                       switch (range._type) {
+                       case RangeType.EMPTY:
+                               return false;
+                       case RangeType.TAIL:
+                               return Tower.proceed<G> (range._set._cmp, ref prev, ref curr, level);
+                       case RangeType.HEAD:
+                       case RangeType.BOUNDED:
+                               Tower<G>? tmp_prev = prev;
+                               Tower<G>? tmp_curr = curr;
+                               if (!Tower.proceed<G> (range._set._cmp, ref tmp_prev, ref tmp_curr, level)) {
+                                       return false;
+                               } else if (Tower.compare_data<G> (range._set._cmp, tmp_curr, range._end) >= 0) {
+                                       return false;
+                               } else {
+                                       prev = (owned)tmp_prev;
+                                       curr = (owned)tmp_curr;
+                                       return true;
+                               }
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               public static bool inside<G> (Range<G> range, G val) {
+                       switch (range._type) {
+                       case RangeType.HEAD:
+                               return range._set._cmp (val, range._end) < 0;
+                       case RangeType.TAIL:
+                               return range._set._cmp (val, range._start) >= 0;
+                       case RangeType.BOUNDED:
+                               return range._set._cmp (val, range._start) >= 0 && range._set._cmp (val, range._end) < 0;
+                       case RangeType.EMPTY:
+                               return false;
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               public static bool beyond<G> (Range<G> range, Tower<G> tw) {
+                       switch (range._type) {
+                       case RangeType.EMPTY:
+                               return true;
+                       case RangeType.TAIL:
+                               return false;
+                       case RangeType.HEAD:
+                       case RangeType.BOUNDED:
+                               return Tower.compare_data<G> (range._set._cmp, tw, range._end) >= 0;
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               public static int cmp<G> (Range<G> range, G val) {
+                       switch (range._type) {
+                       case RangeType.HEAD:
+                               return range._set._cmp (val, range._end) < 0 ? Position.INSIDE : Position.AFTER;
+                       case RangeType.TAIL:
+                               return range._set._cmp (val, range._start) >= 0 ? Position.INSIDE : Position.BEFORE;
+                       case RangeType.BOUNDED:
+                               return range._set._cmp (val, range._start) >= 0 ? (range._set._cmp (val, range._end) < 0 ? Position.INSIDE : Position.AFTER) : Position.BEFORE;
+                       case RangeType.EMPTY:
+                               return Position.EMPTY;
+                       default:
+                               assert_not_reached ();
+                       }
+               }
+
+               enum Position {
+                       BEFORE = -1,
+                       INSIDE = 0,
+                       AFTER = 1,
+                       EMPTY
+               }
+       }
+
+       public enum RangeType {
+               HEAD,
+               TAIL,
+               BOUNDED,
+               EMPTY
+       }
+
        private class Tower<G> {
                public inline Tower (G data, uint8 height) {
                        _nodes = new TowerNode<G>[height];
@@ -227,18 +1071,22 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
 
                public inline Tower.head () {
                        _nodes = new TowerNode<G>[_MAX_HEIGHT];
-                       _height = _MAX_HEIGHT;
+                       _height = -1;
+#if DEBUG
+                       _data = (G)"<<head>>";
+#endif
                }
 
                inline ~Tower () {
-                       for (uint i = 0; i < _height; i++) {
-                               HazardPointer<Tower<G>>.set_pointer (&_nodes[i]._succ, null);
-                               HazardPointer<Tower<G>>.set_pointer (&_nodes[i]._backlink, null);
+                       uint8 height = get_height();
+                       for (uint8 i = 0; i < height; i++) {
+                               set_succ (null, State.NONE, i);
+                               set_backlink (null, i);
                        }
                        _nodes = null;
                }
 
-               public static inline bool search<G> (CompareDataFunc<G>? cmp, G key, ref Tower<G> prev, out Tower<G>? next, uint8 to_level = 0, uint8 from_level = (uint8)_MAX_HEIGHT - 1) {
+               public static inline bool search<G> (CompareDataFunc<G>? cmp, G key, ref Tower<G> prev, out Tower<G>? next = null, uint8 to_level = 0, uint8 from_level = (uint8)_MAX_HEIGHT - 1) {
                        assert (from_level >= to_level);
                        bool res = false;
                        next = null;
@@ -248,25 +1096,47 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                        return res;
                }
 
+               public static inline bool search_from_bookmark<G> (CompareDataFunc<G>? cmp, G key, ref TowerIter<G> prev, out TowerIter<G> next = null, uint8 to_level = 0, uint8 from_level = (uint8)_MAX_HEIGHT - 1) {
+                       assert (from_level >= to_level);
+                       bool res = false;
+                       for (int i = from_level; i >= to_level; i--) {
+                               unowned Tower<G> tmp_prev = prev._iter[i]; // Should be treated as NULL-like value
+                               Tower<G> tmp_next;
+                               res = search_helper<G> (cmp, key, ref prev._iter[i], out tmp_next, (uint8)i);
+                               if (&next != null) {
+                                       next._iter[i] = (owned)tmp_next;
+                               }
+                               if (prev._iter[i] != tmp_prev) {
+                                       prev._iter[i] = prev._iter[i];
+                                       if (i > to_level && compare<G> (cmp, prev._iter[i - 1], prev._iter[i]) <= 0) {
+                                               prev._iter[i - 1] = prev._iter[i];
+                                       }
+                               }
+                       }
+                       return res;
+               }
+
                private static inline bool search_helper<G> (CompareDataFunc<G>? cmp, G key, ref Tower<G>? prev, out Tower<G>? next, uint8 level) {
                        next = prev.get_next (level);
-                       while (next != null && cmp(key, next._data) < 0 && proceed<G> (cmp, ref prev, ref next, level, true));
+                       while (next != null && compare_data<G> (cmp, next, key) < 0 && proceed<G> (cmp, ref prev, ref next, level, true));
                        return next != null && cmp(key, next._data) == 0;
                }
 
-               public static inline Tower<G>? insert<G> (CompareDataFunc<G>? cmp, ref Tower<G> prev, G key, uint8 chosen_level) {
+               public static inline Tower<G>? insert<G> (CompareDataFunc<G>? cmp, ref TowerIter<G> prev, G key, uint8 chosen_level) {
                        return insert_helper<G> (cmp, ref prev, key, chosen_level, chosen_level);
                }
 
-               private static inline Tower<G>? insert_helper<G> (CompareDataFunc<G>? cmp, ref Tower<G> prev, G key, uint8 chosen_level, uint8 level) {
+               private static inline Tower<G>? insert_helper<G> (CompareDataFunc<G>? cmp, ref TowerIter<G> prev, G key, uint8 chosen_level, uint8 level) {
                        Tower<G>? new_tower;
                        Tower<G>? next;
-                       if (search_helper (cmp, key, ref prev, out next, level)) {
+                       if (search_helper (cmp, key, ref prev._iter[level], out next, level)) {
                                return null;
                        }
                        if (level > 0) {
-                               Tower<G> prev_down = prev;
-                               new_tower = insert_helper<G> (cmp, ref prev_down, key, chosen_level, level - 1);
+                               if (compare<G> (cmp, prev._iter[level], prev._iter[level - 1]) >= 0) {
+                                       prev._iter[level - 1] = prev._iter[level];
+                               }
+                               new_tower = insert_helper<G> (cmp, ref prev, key, chosen_level, level - 1);
                        } else {
                                new_tower = new Tower<G> (key, chosen_level + 1);
                        }
@@ -275,53 +1145,52 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                        }
                        while (true) {
                                State prev_state;
-                               Tower<G>? prev_next = prev.get_succ (out prev_state, level);
+                               Tower<G>? prev_next = prev._iter[level].get_succ (out prev_state, level);
                                if (prev_state == State.FLAGGED) {
-                                       prev_next.help_flagged (prev, level);
+                                       prev_next.help_flagged (prev._iter[level], level);
                                } else {
                                        new_tower.set_succ (next, State.NONE, level);
-                                       bool result = prev.compare_and_exchange (next, State.NONE, new_tower, State.NONE, level);
+                                       bool result = prev._iter[level].compare_and_exchange (next, State.NONE, new_tower, State.NONE, level);
                                        if (result)
                                                break;
-                                       prev_next = prev.get_succ (out prev_state, level);
+                                       prev_next = prev._iter[level].get_succ (out prev_state, level);
                                        if (prev_state == State.FLAGGED) {
-                                               prev_next.help_flagged (prev, level);
+                                               prev_next.help_flagged (prev._iter[level], level);
                                        }
-                                       backtrace<G> (ref prev, level);
+                                       backtrace<G> (ref prev._iter[level], level);
                                }
-                               if (search_helper (cmp, key, ref prev, null, level)) {
+                               if (search_helper (cmp, key, ref prev._iter[level], null, level)) {
                                        return null;
                                }
                        }
                        GLib.AtomicInt.inc (ref new_tower._height);
                        if (new_tower.get_state (0) == State.MARKED) {
-                               remove_level (cmp, ref prev, new_tower, level);
+                               remove_level (cmp, ref prev._iter[level], new_tower, level);
                                return null;
                        }
                        return new_tower;
                }
 
-               public static inline bool remove_key<G> (CompareDataFunc<G>? cmp, Tower<G> arg_prev, G key, uint8 from_level = (uint8)_MAX_HEIGHT - 1) {
-                       Tower<G> prev[31 /*_MAX_HEIGHT*/];
-                       prev[from_level] = arg_prev;
+               public static inline bool remove_key<G> (CompareDataFunc<G>? cmp, ref TowerIter<G> prev, G key, uint8 from_level = (uint8)_MAX_HEIGHT - 1) {
                        for (int i = from_level; i >= 1; i--) {
                                Tower<G> next;
-                               search_helper<G> (cmp, key, ref prev[i], out next, (uint8)i);
-                               prev[i - 1] = prev[i];
+                               search_helper<G> (cmp, key, ref prev._iter[i], out next, (uint8)i);
+                               if (compare<G> (cmp, prev._iter[i - 1], prev._iter[i]) < 0) {
+                                       prev._iter[i - 1] = prev._iter[i];
+                               }
                        }
                        Tower<G>? curr;
-                       if (search_helper<G> (cmp, key, ref prev[0], out curr, 0)) {
-                               return remove<G> (cmp, prev, curr);
+                       if (search_helper<G> (cmp, key, ref prev._iter[0], out curr, 0)) {
+                               return remove<G> (cmp, ref prev, curr);
                        } else {
                                return false;
                        }
                }
 
-               public static inline bool remove<G> (CompareDataFunc<G>? cmp, Tower<G>[] prev, Tower<G> curr) {
-                       assert (prev.length >= AtomicInt.get (ref curr._height));
-                       bool removed = remove_level (cmp, ref prev[0], curr, 0);
-                       for (int i = 1; i < prev.length; i++) {
-                               remove_level (cmp, ref prev[i], curr, (uint8)i);
+               public static inline bool remove<G> (CompareDataFunc<G>? cmp, ref TowerIter<G> prev, Tower<G> curr) {
+                       bool removed = remove_level (cmp, ref prev._iter[0], curr, 0);
+                       for (int i = 1; i < _MAX_HEIGHT; i++) {
+                               remove_level (cmp, ref prev._iter[i], curr, (uint8)i);
                        }
                        return removed;
                }
@@ -339,7 +1208,7 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                        Tower<G> curr = arg_curr;
                        Tower<G>? next = curr.get_next (level);
                        if (next != null) {
-                               while (next.get_state (0) == State.MARKED) {
+                               while (next != null && next.get_state (0) == State.MARKED) {
                                        bool status;
                                        next.try_flag (cmp, ref curr, out status, level);
                                        if (status) {
@@ -446,6 +1315,27 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                        HazardPointer.compare_and_exchange_pointer<Tower<G>?> (&_nodes[level]._backlink, null, backlink);
                }
 
+               public inline int get_height () {
+                       int height = GLib.AtomicInt.get (ref _height);
+                       return height != -1 ? height : _MAX_HEIGHT;
+               }
+
+               public inline bool is_head () {
+                       int height = GLib.AtomicInt.get (ref _height);
+                       return height == -1;
+               }
+
+               public inline static int compare<G> (CompareDataFunc<G>? cmp, Tower<G> a, Tower<G> b) {
+                       bool ah = GLib.AtomicInt.get (ref a._height) == -1;
+                       bool bh = GLib.AtomicInt.get (ref b._height) == -1;
+                       return ah ? (bh ? 0 : -1) : (bh ? 1 : cmp(a._data, b._data));
+               }
+
+               public inline static int compare_data<G> (CompareDataFunc<G>? cmp, Tower<G> a, G b) {
+                       bool ah = GLib.AtomicInt.get (ref a._height) == -1;
+                       return ah ? -1 : cmp(a._data, b);
+               }
+
                [NoArrayLength]
                public TowerNode<G>[] _nodes;
                public G _data;
@@ -457,6 +1347,12 @@ public class Gee.ConcurrentSet<G> : AbstractSet<G> {
                public Tower<G> *_backlink;
        }
 
+       private struct TowerIter<G> {
+               [NoArrayLength]
+               public Tower<G>? _iter[31 /*_MAX_HEIGHT*/];
+       }
+
+
        private enum State {
                NONE = 0,
                MARKED = 1,
index 39fcfcf..0b23c4e 100644 (file)
@@ -22,9 +22,9 @@
 
 using Gee;
 
-public class ConcurrentSetTests : SetTests {
+public class ConcurrentSetTests : SortedSetTests {
        public ConcurrentSetTests () {
-               base ("ConcurrentSet");
+               base ("ConcurrentSet", false);
        }
 
        public override void set_up () {
index fdd6e44..00c9bd2 100644 (file)
@@ -24,8 +24,9 @@ using GLib;
 using Gee;
 
 public abstract class SortedSetTests : SetTests {
-       public SortedSetTests (string name) {
+       public SortedSetTests (string name, bool strict = true) {
                base (name);
+               this.strict = strict;
                add_test ("[SortedSet] first", test_first);
                add_test ("[SortedSet] last", test_last);
                add_test ("[SortedSet] ordering", test_ordering);
@@ -34,10 +35,10 @@ public abstract class SortedSetTests : SetTests {
                add_test ("[SortedSet] higher", test_higher);
                add_test ("[SortedSet] floor", test_floor);
                add_test ("[SortedSet] ceil", test_ceil);
-               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.HEAD).get_suite ());
-               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.TAIL).get_suite ());
-               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.SUB).get_suite ());
-               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.EMPTY).get_suite ());
+               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.HEAD, strict).get_suite ());
+               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.TAIL, strict).get_suite ());
+               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.SUB, strict).get_suite ());
+               get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.EMPTY, strict).get_suite ());
        }
 
        public void test_ordering () {
@@ -90,12 +91,14 @@ public abstract class SortedSetTests : SetTests {
        public void test_first () {
                var test_set = test_collection as SortedSet<string>;
 
-               if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
-                                      TestTrapFlags.SILENCE_STDERR)) {
-                       test_set.first ();
-                       Posix.exit (0);
+               if (strict) {
+                       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
+                                              TestTrapFlags.SILENCE_STDERR)) {
+                               test_set.first ();
+                               Posix.exit (0);
+                       }
+                       Test.trap_assert_failed ();
                }
-               Test.trap_assert_failed ();
 
                assert (test_set.add ("one"));
                assert (test_set.add ("two"));
@@ -110,12 +113,14 @@ public abstract class SortedSetTests : SetTests {
        public void test_last () {
                var test_set = test_collection as SortedSet<string>;
 
-               if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
-                                      TestTrapFlags.SILENCE_STDERR)) {
-                       test_set.last ();
-                       Posix.exit (0);
+               if (strict) {
+                       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
+                                              TestTrapFlags.SILENCE_STDERR)) {
+                               test_set.last ();
+                               Posix.exit (0);
+                       }
+                       Test.trap_assert_failed ();
                }
-               Test.trap_assert_failed ();
 
                assert (test_set.add ("one"));
                assert (test_set.add ("two"));
@@ -267,10 +272,11 @@ public abstract class SortedSetTests : SetTests {
                }
                private Type type;
 
-               public SubSetTests (SortedSetTests test, Type type) {
+               public SubSetTests (SortedSetTests test, Type type, bool strict) {
                        base ("%s Subset".printf (type.to_string ()));
                        this.test = test;
                        this.type = type;
+                       this.strict = strict;
                        add_test ("[Collection] size", test_size);
                        add_test ("[Collection] contains", test_contains);
                        add_test ("[Collection] add", test_add);
@@ -580,18 +586,20 @@ public abstract class SortedSetTests : SetTests {
                                assert (subset.last () == "six");
                                break;
                        case Type.EMPTY:
-                               if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
-                                                      TestTrapFlags.SILENCE_STDERR)) {
-                                       subset.first ();
-                                       Posix.exit (0);
+                               if (strict) {
+                                       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
+                                                              TestTrapFlags.SILENCE_STDERR)) {
+                                               subset.first ();
+                                               Posix.exit (0);
+                                       }
+                                       Test.trap_assert_failed ();
+                                       if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
+                                                              TestTrapFlags.SILENCE_STDERR)) {
+                                               subset.last ();
+                                               Posix.exit (0);
+                                       }
+                                       Test.trap_assert_failed ();
                                }
-                               Test.trap_assert_failed ();
-                               if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT |
-                                                      TestTrapFlags.SILENCE_STDERR)) {
-                                       subset.last ();
-                                       Posix.exit (0);
-                               }
-                               Test.trap_assert_failed ();
                                break;
                        default:
                                assert_not_reached ();
@@ -842,6 +850,10 @@ public abstract class SortedSetTests : SetTests {
                                assert_not_reached ();
                        }
                }
+
+               private bool strict;
        }
+
+       private bool strict;
 }