From b7946c9580d4b5f43913ebbb0c8446263dff7552 Mon Sep 17 00:00:00 2001 From: "Kirill A. Korinsky" Date: Sat, 15 Apr 2023 23:12:31 +0200 Subject: [PATCH] Introduced `MovableBidirectionalIterator` This is a memory optimization for the case when a user required to modify iterator by moving it to a desire position. This works the same way as `iteraotr(fromElement)` but doesn't create a new iterator that decreases memory footprint at an algorithms which makes a lot of jumps. So, user simple calls `move(fromElement)`. It also contains `rewind()` which moves iterator to the first position. --- drv/AVLTreeMap.drv | 63 ++++++++--- drv/AVLTreeSet.drv | 56 +++++++++- drv/AbstractSortedMap.drv | 22 ++-- drv/AbstractSortedSet.drv | 5 +- drv/Iterators.drv | 54 ++++++++++ drv/ListIterator.drv | 21 +++- drv/MovableBidirectionalIterable.drv | 65 +++++++++++ drv/MovableBidirectionalIterator.drv | 66 ++++++++++++ drv/OpenHashMap.drv | 4 +- drv/RBTreeMap.drv | 64 ++++++++--- drv/RBTreeSet.drv | 56 +++++++++- drv/SortedMap.drv | 6 +- drv/SortedMaps.drv | 15 ++- drv/SortedSet.drv | 29 ++--- drv/SortedSets.drv | 18 ++-- gencsource.sh | 2 + makefile | 10 ++ .../MovableBidirectionalIterator.java | 65 +++++++++++ .../fastutil/ints/Int2IntAVLTreeMapTest.java | 102 ++++++++++++++++++ .../fastutil/ints/Int2IntRBTreeMapTest.java | 102 ++++++++++++++++++ .../dsi/fastutil/ints/IntAVLTreeSetTest.java | 74 +++++++++++++ .../dsi/fastutil/ints/IntRBTreeSetTest.java | 74 +++++++++++++ 22 files changed, 894 insertions(+), 79 deletions(-) create mode 100644 drv/MovableBidirectionalIterable.drv create mode 100644 drv/MovableBidirectionalIterator.drv create mode 100644 src/it/unimi/dsi/fastutil/MovableBidirectionalIterator.java create mode 100644 test/it/unimi/dsi/fastutil/ints/Int2IntAVLTreeMapTest.java create mode 100644 test/it/unimi/dsi/fastutil/ints/Int2IntRBTreeMapTest.java diff --git a/drv/AVLTreeMap.drv b/drv/AVLTreeMap.drv index 1a9ab33c..eb337f5c 100644 --- a/drv/AVLTreeMap.drv +++ b/drv/AVLTreeMap.drv @@ -18,7 +18,7 @@ package PACKAGE; #if ! KEYS_REFERENCE import it.unimi.dsi.fastutil.objects.AbstractObjectSortedSet; -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; import it.unimi.dsi.fastutil.objects.ObjectListIterator; import it.unimi.dsi.fastutil.objects.ObjectSortedSet; #endif @@ -1185,6 +1185,23 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL while(i-- != 0 && hasPrevious()) previousEntry(); return n - i - 1; } + + public void moveKey(final KEY_GENERIC_TYPE fromElement) { + if ((next = locateKey(fromElement)) != null) { + if (compare(next.key, fromElement) <= 0) { + prev = next; + next = next.next(); + } + else prev = next.prev(); + } + } + + public void rewindKey() { + prev = null; + next = firstEntry; + curr = null; + index = 0; + } } @@ -1193,7 +1210,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL *

This class can iterate in both directions on a threaded tree. */ - private class EntryIterator extends TreeIterator implements ObjectListIterator { + private class EntryIterator extends TreeIterator implements ObjectMovableBidirectionalIterator, ObjectListIterator { EntryIterator() {} EntryIterator(final KEY_GENERIC_TYPE k) { @@ -1211,6 +1228,12 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL @Override public void add(MAP.Entry KEY_VALUE_GENERIC ok) { throw new UnsupportedOperationException(); } + + @Override + public void move(final MAP.Entry KEY_VALUE_GENERIC fromElement) { moveKey(fromElement.ENTRY_GET_KEY()); } + + @Override + public void rewind() { rewindKey(); } } @@ -1227,10 +1250,10 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL public Comparator comparator() { return comparator; } @Override - public ObjectBidirectionalIterator iterator() { return new EntryIterator(); } + public ObjectMovableBidirectionalIterator iterator() { return new EntryIterator(); } @Override - public ObjectBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { return new EntryIterator(from.ENTRY_GET_KEY()); } + public ObjectMovableBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { return new EntryIterator(from.ENTRY_GET_KEY()); } @Override SUPPRESS_WARNINGS_KEY_UNCHECKED @@ -1297,21 +1320,27 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL * simply override the {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly * their type-specific counterparts) so that they return keys instead of entries. */ - private final class KeyIterator extends TreeIterator implements KEY_LIST_ITERATOR KEY_GENERIC { + private final class KeyIterator extends TreeIterator implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC, KEY_LIST_ITERATOR KEY_GENERIC { public KeyIterator() {} public KeyIterator(final KEY_GENERIC_TYPE k) { super(k); } @Override public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; } @Override public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { moveKey(fromElement); } + + @Override + public void rewind() { rewindKey(); } } /** A keyset implementation using a more direct implementation for iterators. */ private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet { @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeyIterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeyIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new KeyIterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new KeyIterator(from); } } /** Returns a type-specific sorted set view of the keys contained in this map. @@ -1443,9 +1472,9 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL public ObjectSortedSet ENTRYSET() { if (entries == null) entries = new AbstractObjectSortedSet() { @Override - public ObjectBidirectionalIterator iterator() { return new SubmapEntryIterator(); } + public ObjectMovableBidirectionalIterator iterator() { return new SubmapEntryIterator(); } @Override - public ObjectBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { return new SubmapEntryIterator(from.ENTRY_GET_KEY()); } + public ObjectMovableBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { return new SubmapEntryIterator(from.ENTRY_GET_KEY()); } @Override public Comparator comparator() { return AVL_TREE_MAP.this.ENTRYSET().comparator(); } @Override @@ -1504,9 +1533,9 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet { @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubmapKeyIterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubmapKeyIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubmapKeyIterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubmapKeyIterator(from); } } @Override @@ -1708,7 +1737,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL } } - private class SubmapEntryIterator extends SubmapIterator implements ObjectListIterator { + private class SubmapEntryIterator extends SubmapIterator implements ObjectMovableBidirectionalIterator, ObjectListIterator { SubmapEntryIterator() {} SubmapEntryIterator(final KEY_GENERIC_TYPE k) { super(k); } @@ -1716,6 +1745,10 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL public MAP.Entry KEY_VALUE_GENERIC next() { return nextEntry(); } @Override public MAP.Entry KEY_VALUE_GENERIC previous() { return previousEntry(); } + @Override + public void move(final MAP.Entry KEY_VALUE_GENERIC from) { moveKey(from.ENTRY_GET_KEY()); } + @Override + public void rewind() { rewindKey(); } } @@ -1727,7 +1760,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL * type-specific counterparts) so that they return keys instead of * entries. */ - private final class SubmapKeyIterator extends SubmapIterator implements KEY_LIST_ITERATOR KEY_GENERIC { + private final class SubmapKeyIterator extends SubmapIterator implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC, KEY_LIST_ITERATOR KEY_GENERIC { public SubmapKeyIterator() { super(); } public SubmapKeyIterator(KEY_GENERIC_TYPE from) { super(from); } @@ -1735,6 +1768,10 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VAL public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; } @Override public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; } + @Override + public void move(final KEY_GENERIC_TYPE from) { moveKey(from); } + @Override + public void rewind() { rewindKey(); } }; /** An iterator on a subrange of values. diff --git a/drv/AVLTreeSet.drv b/drv/AVLTreeSet.drv index c51ecce0..29dcddbe 100644 --- a/drv/AVLTreeSet.drv +++ b/drv/AVLTreeSet.drv @@ -1055,7 +1055,7 @@ public class AVL_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC im *

This class can iterate in both directions on a threaded tree. */ - private class SetIterator implements KEY_LIST_ITERATOR KEY_GENERIC { + private class SetIterator implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC, KEY_LIST_ITERATOR KEY_GENERIC { /** The entry that will be returned by the next call to {@link java.util.ListIterator#previous()} (or {@code null} if no previous entry exists). */ Entry KEY_GENERIC prev; /** The entry that will be returned by the next call to {@link java.util.ListIterator#next()} (or {@code null} if no next entry exists). */ @@ -1127,13 +1127,32 @@ public class AVL_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC im AVL_TREE_SET.this.remove(curr.key); curr = null; } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { + if ((next = locateKey(fromElement)) != null) { + if (compare(next.key, fromElement) <= 0) { + prev = next; + next = next.next(); + } + else prev = next.prev(); + } + } + + @Override + public void rewind() { + prev = null; + next = firstEntry; + curr = null; + index = 0; + } } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SetIterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new SetIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SetIterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SetIterator(from); } @Override public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { return actualComparator; } @@ -1241,10 +1260,10 @@ public class AVL_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC im public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { return actualComparator; } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubsetIterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubsetIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubsetIterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubsetIterator(from); } @Override public SORTED_SET KEY_GENERIC headSet(final KEY_GENERIC_TYPE to) { @@ -1360,6 +1379,33 @@ public class AVL_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC im next = next.next(); if (! top && next != null && AVL_TREE_SET.this.compare(next.key, to) >= 0) next = null; } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { + next = firstEntry(); + + if (next != null) { + if (! bottom && compare(fromElement, next.key) < 0) prev = null; + else if (! top && compare(fromElement, (prev = lastEntry()).key) >= 0) next = null; + else { + next = locateKey(fromElement); + + if (compare(next.key, fromElement) <= 0) { + prev = next; + next = next.next(); + } + else prev = next.prev(); + } + } + } + + @Override + public void rewind() { + prev = null; + next = firstEntry(); + curr = null; + index = 0; + } } } diff --git a/drv/AbstractSortedMap.drv b/drv/AbstractSortedMap.drv index fd32e371..7bf849aa 100644 --- a/drv/AbstractSortedMap.drv +++ b/drv/AbstractSortedMap.drv @@ -26,7 +26,7 @@ import VALUE_PACKAGE.VALUE_ITERATOR; #if KEYS_REFERENCE import java.util.Comparator; #else -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; #endif /** An abstract class providing basic methods for sorted maps implementing a type-specific interface. */ @@ -84,10 +84,10 @@ public abstract class ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP public SORTED_SET KEY_GENERIC subSet(final KEY_GENERIC_TYPE from, final KEY_GENERIC_TYPE to) { return subMap(from, to).keySet(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new KeySetIterator KEY_VALUE_GENERIC_DIAMOND(ENTRYSET().iterator(new BasicEntry KEY_VALUE_GENERIC_DIAMOND(from, VALUE_NULL))); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new KeySetIterator KEY_VALUE_GENERIC_DIAMOND(ENTRYSET().iterator(new BasicEntry KEY_VALUE_GENERIC_DIAMOND(from, VALUE_NULL))); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeySetIterator KEY_VALUE_GENERIC_DIAMOND(SORTED_MAPS.fastIterator(ABSTRACT_SORTED_MAP.this)); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeySetIterator KEY_VALUE_GENERIC_DIAMOND(SORTED_MAPS.fastIterator(ABSTRACT_SORTED_MAP.this)); } } /** A wrapper exhibiting a map iterator as an iterator on keys. @@ -96,10 +96,10 @@ public abstract class ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP * class using the corresponding iterator on entries. */ - protected static class KeySetIterator KEY_VALUE_GENERIC implements KEY_BIDI_ITERATOR KEY_GENERIC { - protected final ObjectBidirectionalIterator i; + protected static class KeySetIterator KEY_VALUE_GENERIC implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC { + protected final ObjectMovableBidirectionalIterator i; - public KeySetIterator(ObjectBidirectionalIterator i) { + public KeySetIterator(ObjectMovableBidirectionalIterator i) { this.i = i; } @@ -114,6 +114,12 @@ public abstract class ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP @Override public boolean hasPrevious() { return i.hasPrevious(); } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { i.move(new BasicEntry KEY_VALUE_GENERIC_DIAMOND(fromElement, VALUE_NULL)); } + + @Override + public void rewind() { i.rewind(); } } /** {@inheritDoc} @@ -154,9 +160,9 @@ public abstract class ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP */ protected static class ValuesIterator KEY_VALUE_GENERIC implements VALUE_ITERATOR VALUE_GENERIC { - protected final ObjectBidirectionalIterator i; + protected final ObjectMovableBidirectionalIterator i; - public ValuesIterator(ObjectBidirectionalIterator i) { + public ValuesIterator(ObjectMovableBidirectionalIterator i) { this.i = i; } diff --git a/drv/AbstractSortedSet.drv b/drv/AbstractSortedSet.drv index 8d9eb40a..ac51e9e7 100644 --- a/drv/AbstractSortedSet.drv +++ b/drv/AbstractSortedSet.drv @@ -24,5 +24,8 @@ public abstract class ABSTRACT_SORTED_SET KEY_GENERIC extends ABSTRACT_SET KEY_G protected ABSTRACT_SORTED_SET() {} @Override - public abstract KEY_BIDI_ITERATOR KEY_GENERIC iterator(); + public abstract KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE fromElement); + + @Override + public abstract KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(); } diff --git a/drv/Iterators.drv b/drv/Iterators.drv index 56d3392e..6ab9c2cf 100644 --- a/drv/Iterators.drv +++ b/drv/Iterators.drv @@ -1414,6 +1414,60 @@ public final class ITERATORS { public static KEY_GENERIC KEY_BIDI_ITERATOR KEY_GENERIC unmodifiable(final KEY_BIDI_ITERATOR KEY_EXTENDS_GENERIC i) { return new UnmodifiableBidirectionalIterator KEY_GENERIC_DIAMOND(i); } + /** An unmodifiable wrapper class for bidirectional iterators. */ + + public static class UnmodifiableMovableBidirectionalIterator KEY_GENERIC implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC { + protected final KEY_MOVE_BIDI_ITERATOR KEY_EXTENDS_GENERIC i; + + public UnmodifiableMovableBidirectionalIterator(final KEY_MOVE_BIDI_ITERATOR KEY_EXTENDS_GENERIC i) { + this.i = i; + } + + @Override + public boolean hasNext() { return i.hasNext(); } + + @Override + public boolean hasPrevious() { return i.hasPrevious(); } + + @Override + public KEY_GENERIC_TYPE NEXT_KEY() { return i.NEXT_KEY(); } + + @Override + public KEY_GENERIC_TYPE PREV_KEY() { return i.PREV_KEY(); } + +#if KEYS_PRIMITIVE + @Override + public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + i.forEachRemaining(action); + } +#endif + + DEPRECATED_IF_KEYS_PRIMITIVE + @Override + public void forEachRemaining(final Consumer action) { + i.forEachRemaining(action); + } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { + ((KEY_MOVE_BIDI_ITERATOR KEY_GENERIC)i).move(fromElement); + } + + @Override + public void rewind() { + i.rewind(); + } + } + + + /** Returns an unmodifiable movable bidirectional iterator backed by the specified bidirectional iterator. + * + * @param i the bidirectional iterator to be wrapped in an unmodifiable bidirectional iterator. + * @return an unmodifiable view of the specified bidirectional iterator. + */ + public static KEY_GENERIC KEY_MOVE_BIDI_ITERATOR KEY_GENERIC unmodifiable(final KEY_MOVE_BIDI_ITERATOR KEY_EXTENDS_GENERIC i) { return new UnmodifiableMovableBidirectionalIterator KEY_GENERIC_DIAMOND(i); } + + /** An unmodifiable wrapper class for list iterators. */ public static class UnmodifiableListIterator KEY_GENERIC implements KEY_LIST_ITERATOR KEY_GENERIC { diff --git a/drv/ListIterator.drv b/drv/ListIterator.drv index d9304608..15bfb213 100644 --- a/drv/ListIterator.drv +++ b/drv/ListIterator.drv @@ -30,7 +30,7 @@ import java.util.ListIterator; * @see it.unimi.dsi.fastutil.BidirectionalIterator */ -public interface KEY_LIST_ITERATOR KEY_GENERIC extends KEY_BIDI_ITERATOR KEY_GENERIC, ListIterator { +public interface KEY_LIST_ITERATOR KEY_GENERIC extends KEY_MOVE_BIDI_ITERATOR KEY_GENERIC, ListIterator { /** * Replaces the last element returned by {@link #next} or @@ -89,13 +89,28 @@ public interface KEY_LIST_ITERATOR KEY_GENERIC extends KEY_BIDI_ITERATOR KEY_GEN * @deprecated Please use the corresponding type-specific method instead. */ @Deprecated @Override - default KEY_GENERIC_CLASS next() { return KEY_BIDI_ITERATOR.super.next(); } + default KEY_GENERIC_CLASS next() { return KEY_MOVE_BIDI_ITERATOR.super.next(); } /** {@inheritDoc} * @deprecated Please use the corresponding type-specific method instead. */ @Deprecated @Override - default KEY_GENERIC_CLASS previous() { return KEY_BIDI_ITERATOR.super.previous(); } + default KEY_GENERIC_CLASS previous() { return KEY_MOVE_BIDI_ITERATOR.super.previous(); } #endif + /** + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @see it.unimi.dsi.fastutil.MovableBidirectionalIterator#move() + */ + + @Override + default void move(final KEY_GENERIC_TYPE fromElement) { throw new UnsupportedOperationException(); } + + /** + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @see it.unimi.dsi.fastutil.MovableBidirectionalIterator#rewind() + */ + + @Override + default void rewind() { throw new UnsupportedOperationException(); } } diff --git a/drv/MovableBidirectionalIterable.drv b/drv/MovableBidirectionalIterable.drv new file mode 100644 index 00000000..79648d51 --- /dev/null +++ b/drv/MovableBidirectionalIterable.drv @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2002-2023 Sebastiano Vigna + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package PACKAGE; + +/** A type-specific {@link Iterable} that further strengthens the specification of {@link it.unimi.dsi.fastutil.BidirectionalIterator#iterator()}. + */ +public interface KEY_MOVE_BIDI_ITERABLE KEY_GENERIC extends KEY_BIDI_ITERABLE KEY_GENERIC { + + /** Returns a type-specific {@link it.unimi.dsi.fastutil.MovableBidirectionalIterator} + * on the elements in this set, starting from a given element of the domain (optional operation). + * + *

This method returns a type-specific movable bidirectional iterator with given + * starting point. The starting point is any element comparable to the + * elements of this set (even if it does not actually belong to the + * set). The next element of the returned iterator is the least element of + * the set that is greater than the starting point (if there are no + * elements greater than the starting point, {@link + * it.unimi.dsi.fastutil.MovableBidirectionalIterator#hasNext() hasNext()} will return + * {@code false}). The previous element of the returned iterator is + * the greatest element of the set that is smaller than or equal to the + * starting point (if there are no elements smaller than or equal to the + * starting point, {@link it.unimi.dsi.fastutil.BidirectionalIterator#hasPrevious() + * hasPrevious()} will return {@code false}). + * + *

Note that passing the last element of the set as starting point and + * calling {@link it.unimi.dsi.fastutil.MovableBidirectionalIterator#previous() previous()} + * you can traverse the entire set in reverse order. + * + * @param fromElement an element to start from. + * @return a movable bidirectional iterator on the element in this set, starting at the given element. + * @throws UnsupportedOperationException if this set does not support iterators with a starting point. + */ + + KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE fromElement); + + /** Returns a type-specific {@link it.unimi.dsi.fastutil.MovableBidirectionalIterator} + * on the elements in this set. + * + *

This method returns a parameterised movable bidirectional iterator. The iterator + * can be moreover safely cast to a type-specific iterator. + * + * @apiNote This specification strengthens the one given in the corresponding type-specific + * {@link Collection}. + * + * @return a movable bidirectional iterator on the element in this set. + */ + + @Override + KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(); +} diff --git a/drv/MovableBidirectionalIterator.drv b/drv/MovableBidirectionalIterator.drv new file mode 100644 index 00000000..536b00ca --- /dev/null +++ b/drv/MovableBidirectionalIterator.drv @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2002-2023 Sebastiano Vigna + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package PACKAGE; + +import it.unimi.dsi.fastutil.MovableBidirectionalIterator; +#if KEYS_PRIMITIVE +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; +#endif + +/** A type-specific movable bidirectional iterator; provides an additional method to avoid + * (re)creation iterator then it need to be moved forward or backwards. + * + * @see MovableBidirectionalIterator + */ + +#if KEYS_PRIMITIVE +public interface KEY_MOVE_BIDI_ITERATOR KEY_GENERIC extends KEY_BIDI_ITERATOR KEY_GENERIC, ObjectMovableBidirectionalIterator { +#else +public interface KEY_MOVE_BIDI_ITERATOR KEY_GENERIC extends KEY_BIDI_ITERATOR KEY_GENERIC, MovableBidirectionalIterator { +#endif + +#if KEYS_PRIMITIVE + + /** {@inheritDoc} + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + default void move(final KEY_GENERIC_CLASS fromElement) { move((KEY_GENERIC_TYPE) fromElement); } + + /** Moves back or forward to the elements in the collection, + * starting from a given element of the domain. + * + *

This method moves an iterator to the specified starting point. + * The starting point is any element comparable to the elements of the collection + * (even if it does not actually belong to the collection). + * The next element of the returned iterator is the least element of + * the collection that is greater than the starting point (if there are no + * elements greater than the starting point, {@link + * it.unimi.dsi.fastutil.BidirectionalIterator#hasNext() hasNext()} will return + * {@code false}). The previous element of the returned iterator is + * the greatest element of the collection that is smaller than or equal to the + * starting point (if there are no elements smaller than or equal to the + * starting point, {@link it.unimi.dsi.fastutil.BidirectionalIterator#hasPrevious() + * hasPrevious()} will return {@code false}). + * + * @param fromElement an element to start from. + * @throws UnsupportedOperationException if the collection does not support move at iterator. + */ + void move(final KEY_GENERIC_TYPE fromElement); +#endif +} diff --git a/drv/OpenHashMap.drv b/drv/OpenHashMap.drv index b06c4acf..8e7034b1 100644 --- a/drv/OpenHashMap.drv +++ b/drv/OpenHashMap.drv @@ -61,7 +61,7 @@ import it.unimi.dsi.fastutil.objects.ObjectIterator; #if ! KEYS_REFERENCE import it.unimi.dsi.fastutil.objects.AbstractObjectSortedSet; import it.unimi.dsi.fastutil.objects.ObjectListIterator; -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; import it.unimi.dsi.fastutil.objects.ObjectSpliterator; import it.unimi.dsi.fastutil.objects.ObjectSpliterators; import it.unimi.dsi.fastutil.objects.ObjectSortedSet; @@ -2123,7 +2123,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE private static final int SPLITERATOR_CHARACTERISTICS = ObjectSpliterators.SET_SPLITERATOR_CHARACTERISTICS | java.util.Spliterator.ORDERED; @Override - public ObjectBidirectionalIterator iterator() { return new EntryIterator(); } + public ObjectMovableBidirectionalIterator iterator() { return new EntryIterator(); } /** {@inheritDoc} * diff --git a/drv/RBTreeMap.drv b/drv/RBTreeMap.drv index 4f0a1b65..f3227ce7 100644 --- a/drv/RBTreeMap.drv +++ b/drv/RBTreeMap.drv @@ -19,7 +19,7 @@ package PACKAGE; #if ! KEYS_REFERENCE import it.unimi.dsi.fastutil.objects.AbstractObjectSortedSet; -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; import it.unimi.dsi.fastutil.objects.ObjectListIterator; import it.unimi.dsi.fastutil.objects.ObjectSortedSet; #endif @@ -1116,6 +1116,23 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE while(i-- != 0 && hasPrevious()) previousEntry(); return n - i - 1; } + + public void moveKey(final KEY_GENERIC_TYPE fromElement) { + if ((next = locateKey(fromElement)) != null) { + if (compare(next.key, fromElement) <= 0) { + prev = next; + next = next.next(); + } + else prev = next.prev(); + } + } + + public void rewindKey() { + prev = null; + next = firstEntry; + curr = null; + index = 0; + } } @@ -1124,7 +1141,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE *

This class can iterate in both directions on a threaded tree. */ - private class EntryIterator extends TreeIterator implements ObjectListIterator { + private class EntryIterator extends TreeIterator implements ObjectMovableBidirectionalIterator, ObjectListIterator { EntryIterator() {} EntryIterator(final KEY_GENERIC_TYPE k) { @@ -1135,6 +1152,12 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE public MAP.Entry KEY_VALUE_GENERIC next() { return nextEntry(); } @Override public MAP.Entry KEY_VALUE_GENERIC previous() { return previousEntry(); } + + @Override + public void move(final MAP.Entry KEY_VALUE_GENERIC fromElement) { moveKey(fromElement.ENTRY_GET_KEY()); } + + @Override + public void rewind() { rewindKey(); } } @@ -1152,10 +1175,10 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE public Comparator comparator() { return comparator; } @Override - public ObjectBidirectionalIterator iterator() { return new EntryIterator(); } + public ObjectMovableBidirectionalIterator iterator() { return new EntryIterator(); } @Override - public ObjectBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { return new EntryIterator(from.ENTRY_GET_KEY()); } + public ObjectMovableBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { return new EntryIterator(from.ENTRY_GET_KEY()); } @Override SUPPRESS_WARNINGS_KEY_UNCHECKED @@ -1222,7 +1245,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE * simply override the {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly * their type-specific counterparts) so that they return keys instead of entries. */ - private final class KeyIterator extends TreeIterator implements KEY_LIST_ITERATOR KEY_GENERIC { + private final class KeyIterator extends TreeIterator implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC, KEY_LIST_ITERATOR KEY_GENERIC { public KeyIterator() {} public KeyIterator(final KEY_GENERIC_TYPE k) { super(k); } @@ -1231,15 +1254,21 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE @Override public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { moveKey(fromElement); } + + @Override + public void rewind() { rewindKey(); } }; /** A keyset implementation using a more direct implementation for iterators. */ private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet { @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeyIterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeyIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new KeyIterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new KeyIterator(from); } } /** Returns a type-specific sorted set view of the keys contained in this map. @@ -1372,12 +1401,12 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE public ObjectSortedSet ENTRYSET() { if (entries == null) entries = new AbstractObjectSortedSet() { @Override - public ObjectBidirectionalIterator iterator() { + public ObjectMovableBidirectionalIterator iterator() { return new SubmapEntryIterator(); } @Override - public ObjectBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { + public ObjectMovableBidirectionalIterator iterator(final MAP.Entry KEY_VALUE_GENERIC from) { return new SubmapEntryIterator(from.ENTRY_GET_KEY()); } @@ -1449,9 +1478,9 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet { @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubmapKeyIterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubmapKeyIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubmapKeyIterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubmapKeyIterator(from); } } @Override @@ -1656,7 +1685,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE } } - private class SubmapEntryIterator extends SubmapIterator implements ObjectListIterator { + private class SubmapEntryIterator extends SubmapIterator implements ObjectMovableBidirectionalIterator, ObjectListIterator { SubmapEntryIterator() {} SubmapEntryIterator(final KEY_GENERIC_TYPE k) { @@ -1667,6 +1696,10 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE public MAP.Entry KEY_VALUE_GENERIC next() { return nextEntry(); } @Override public MAP.Entry KEY_VALUE_GENERIC previous() { return previousEntry(); } + @Override + public void move(final MAP.Entry KEY_VALUE_GENERIC from) { moveKey(from.ENTRY_GET_KEY()); } + @Override + public void rewind() { rewindKey(); } } @@ -1678,7 +1711,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE * type-specific counterparts) so that they return keys instead of * entries. */ - private final class SubmapKeyIterator extends SubmapIterator implements KEY_LIST_ITERATOR KEY_GENERIC { + private final class SubmapKeyIterator extends SubmapIterator implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC, KEY_LIST_ITERATOR KEY_GENERIC { public SubmapKeyIterator() { super(); } public SubmapKeyIterator(KEY_GENERIC_TYPE from) { super(from); } @@ -1686,6 +1719,11 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; } @Override public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; } + @Override + public void move(final KEY_GENERIC_TYPE from) { moveKey(from); } + + @Override + public void rewind() { rewindKey(); } }; /** An iterator on a subrange of values. diff --git a/drv/RBTreeSet.drv b/drv/RBTreeSet.drv index fc2380d3..e2fe9829 100644 --- a/drv/RBTreeSet.drv +++ b/drv/RBTreeSet.drv @@ -985,7 +985,7 @@ public class RB_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC imp *

This class can iterate in both directions on a threaded tree. */ - private class SetIterator implements KEY_LIST_ITERATOR KEY_GENERIC { + private class SetIterator implements KEY_MOVE_BIDI_ITERATOR KEY_GENERIC, KEY_LIST_ITERATOR KEY_GENERIC { /** The entry that will be returned by the next call to {@link java.util.ListIterator#previous()} (or {@code null} if no previous entry exists). */ Entry KEY_GENERIC prev; /** The entry that will be returned by the next call to {@link java.util.ListIterator#next()} (or {@code null} if no next entry exists). */ @@ -1055,13 +1055,32 @@ public class RB_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC imp RB_TREE_SET.this.remove(curr.key); curr = null; } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { + if ((next = locateKey(fromElement)) != null) { + if (compare(next.key, fromElement) <= 0) { + prev = next; + next = next.next(); + } + else prev = next.prev(); + } + } + + @Override + public void rewind() { + prev = null; + next = firstEntry; + curr = null; + index = 0; + } } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SetIterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new SetIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SetIterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SetIterator(from); } @Override public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { return actualComparator; } @@ -1174,12 +1193,12 @@ public class RB_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC imp } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubsetIterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SubsetIterator(from); } @@ -1299,6 +1318,33 @@ public class RB_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC imp next = next.next(); if (! top && next != null && RB_TREE_SET.this.compare(next.key, to) >= 0) next = null; } + + @Override + public void move(final KEY_GENERIC_TYPE fromElement) { + next = firstEntry(); + + if (next != null) { + if (! bottom && compare(fromElement, next.key) < 0) prev = null; + else if (! top && compare(fromElement, (prev = lastEntry()).key) >= 0) next = null; + else { + next = locateKey(fromElement); + + if (compare(next.key, fromElement) <= 0) { + prev = next; + next = next.next(); + } + else prev = next.prev(); + } + } + } + + @Override + public void rewind() { + prev = null; + next = firstEntry(); + curr = null; + index = 0; + } } } diff --git a/drv/SortedMap.drv b/drv/SortedMap.drv index b3a8d043..7ed58094 100644 --- a/drv/SortedMap.drv +++ b/drv/SortedMap.drv @@ -28,7 +28,7 @@ import java.util.SortedMap; import java.util.Comparator; #else import it.unimi.dsi.fastutil.objects.ObjectSortedSet; -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; #endif /** A type-specific {@link SortedMap}; provides some additional methods that use polymorphism to avoid (un)boxing. @@ -149,7 +149,7 @@ public interface SORTED_MAP KEY_VALUE_GENERIC extends MAP KEY_VALUE_GENERIC, Sor /** {@inheritDoc} */ @Override - ObjectBidirectionalIterator fastIterator(); + ObjectMovableBidirectionalIterator fastIterator(); /** Returns a fast iterator over this entry set, starting from a given element of the domain (optional operation); @@ -158,7 +158,7 @@ public interface SORTED_MAP KEY_VALUE_GENERIC extends MAP KEY_VALUE_GENERIC, Sor * @param from an element to start from. * @return a fast iterator over this sorted entry set starting at {@code from}; the iterator might return always the same entry object, suitably mutated. */ - ObjectBidirectionalIterator fastIterator(MAP.Entry KEY_VALUE_GENERIC from); + ObjectMovableBidirectionalIterator fastIterator(MAP.Entry KEY_VALUE_GENERIC from); } #if KEYS_PRIMITIVE || VALUES_PRIMITIVE diff --git a/drv/SortedMaps.drv b/drv/SortedMaps.drv index bb24eb54..0176ddb9 100644 --- a/drv/SortedMaps.drv +++ b/drv/SortedMaps.drv @@ -18,8 +18,8 @@ package PACKAGE; #if ! KEYS_REFERENCE -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterable; -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterable; +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; import it.unimi.dsi.fastutil.objects.ObjectSortedSet; import it.unimi.dsi.fastutil.objects.ObjectSortedSets; #endif @@ -55,7 +55,7 @@ public final class SORTED_MAPS { * @since 8.0.0 */ SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED - public static KEY_VALUE_GENERIC ObjectBidirectionalIterator fastIterator(SORTED_MAP KEY_VALUE_GENERIC map) { + public static KEY_VALUE_GENERIC ObjectMovableBidirectionalIterator fastIterator(SORTED_MAP KEY_VALUE_GENERIC map) { final ObjectSortedSet entries = map.ENTRYSET(); return entries instanceof SORTED_MAP.FastSortedEntrySet ? ((SORTED_MAP.FastSortedEntrySet KEY_VALUE_GENERIC) entries).fastIterator() : entries.iterator(); } @@ -66,9 +66,14 @@ public final class SORTED_MAPS { * @since 8.0.0 */ SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED - public static KEY_VALUE_GENERIC ObjectBidirectionalIterable fastIterable(SORTED_MAP KEY_VALUE_GENERIC map) { + public static KEY_VALUE_GENERIC ObjectMovableBidirectionalIterable fastIterable(SORTED_MAP KEY_VALUE_GENERIC map) { final ObjectSortedSet entries = map.ENTRYSET(); - return entries instanceof SORTED_MAP.FastSortedEntrySet ? ((SORTED_MAP.FastSortedEntrySet KEY_VALUE_GENERIC)entries)::fastIterator : entries; + return entries instanceof SORTED_MAP.FastSortedEntrySet ? new ObjectMovableBidirectionalIterable() { + @Override + public ObjectMovableBidirectionalIterator iterator() { return ((SORTED_MAP.FastSortedEntrySet KEY_VALUE_GENERIC)entries).fastIterator(); } + @Override + public ObjectMovableBidirectionalIterator iterator(MAP.Entry KEY_VALUE_GENERIC from) { return ((SORTED_MAP.FastSortedEntrySet KEY_VALUE_GENERIC)entries).fastIterator(from); } + } : entries; } diff --git a/drv/SortedSet.drv b/drv/SortedSet.drv index 6e8d6bd8..c4378df9 100644 --- a/drv/SortedSet.drv +++ b/drv/SortedSet.drv @@ -33,18 +33,18 @@ import static it.unimi.dsi.fastutil.Size64.sizeOf; */ -public interface SORTED_SET KEY_GENERIC extends SET KEY_GENERIC, SortedSet, KEY_BIDI_ITERABLE KEY_GENERIC { +public interface SORTED_SET KEY_GENERIC extends SET KEY_GENERIC, SortedSet, KEY_MOVE_BIDI_ITERABLE KEY_GENERIC { - /** Returns a type-specific {@link it.unimi.dsi.fastutil.BidirectionalIterator} on the elements in - * this set, starting from a given element of the domain (optional operation). + /** Returns a type-specific {@link it.unimi.dsi.fastutil.MovableBidirectionalIterator} + * on the elements in this set, starting from a given element of the domain (optional operation). * - *

This method returns a type-specific bidirectional iterator with given + *

This method returns a type-specific movable bidirectional iterator with given * starting point. The starting point is any element comparable to the * elements of this set (even if it does not actually belong to the * set). The next element of the returned iterator is the least element of * the set that is greater than the starting point (if there are no * elements greater than the starting point, {@link - * it.unimi.dsi.fastutil.BidirectionalIterator#hasNext() hasNext()} will return + * it.unimi.dsi.fastutil.MovableBidirectionalIterator#hasNext() hasNext()} will return * {@code false}). The previous element of the returned iterator is * the greatest element of the set that is smaller than or equal to the * starting point (if there are no elements smaller than or equal to the @@ -52,30 +52,31 @@ public interface SORTED_SET KEY_GENERIC extends SET KEY_GENERIC, SortedSetNote that passing the last element of the set as starting point and - * calling {@link it.unimi.dsi.fastutil.BidirectionalIterator#previous() previous()} you can traverse the - * entire set in reverse order. + * calling {@link it.unimi.dsi.fastutil.MovableBidirectionalIterator#previous() previous()} + * you can traverse the entire set in reverse order. * * @param fromElement an element to start from. - * @return a bidirectional iterator on the element in this set, starting at the given element. + * @return a movable bidirectional iterator on the element in this set, starting at the given element. * @throws UnsupportedOperationException if this set does not support iterators with a starting point. */ - KEY_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE fromElement); + @Override + KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE fromElement); - /** Returns a type-specific {@link it.unimi.dsi.fastutil.BidirectionalIterator} on the elements in - * this set. + /** Returns a type-specific {@link it.unimi.dsi.fastutil.MovableBidirectionalIterator} + * on the elements in this set. * - *

This method returns a parameterised bidirectional iterator. The iterator + *

This method returns a parameterised movable bidirectional iterator. The iterator * can be moreover safely cast to a type-specific iterator. * * @apiNote This specification strengthens the one given in the corresponding type-specific * {@link Collection}. * - * @return a bidirectional iterator on the element in this set. + * @return a movable bidirectional iterator on the element in this set. */ @Override - KEY_BIDI_ITERATOR KEY_GENERIC iterator(); + KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(); /** * Returns a type-specific spliterator on the elements of this sorted-set. diff --git a/drv/SortedSets.drv b/drv/SortedSets.drv index 565e0788..e6efc547 100644 --- a/drv/SortedSets.drv +++ b/drv/SortedSets.drv @@ -50,7 +50,11 @@ public final class SORTED_SETS { @Override SUPPRESS_WARNINGS_KEY_UNCHECKED - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE from) { return ITERATORS.EMPTY_ITERATOR; } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return ITERATORS.EMPTY_ITERATOR; } + + @Override + SUPPRESS_WARNINGS_KEY_UNCHECKED + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE from) { return ITERATORS.EMPTY_ITERATOR; } @Override SUPPRESS_WARNINGS_KEY_UNCHECKED @@ -158,8 +162,8 @@ public final class SORTED_SETS { } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE from) { - KEY_BIDI_ITERATOR KEY_GENERIC i = iterator(); + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE from) { + KEY_MOVE_BIDI_ITERATOR KEY_GENERIC i = iterator(); if (compare(element, from) <= 0) i.NEXT_KEY(); return i; } @@ -308,10 +312,10 @@ public final class SORTED_SETS { public SORTED_SET KEY_GENERIC tailSet(final KEY_GENERIC_TYPE from) { return new SynchronizedSortedSet KEY_GENERIC_DIAMOND(sortedSet.tailSet(from), sync); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return sortedSet.iterator(); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return sortedSet.iterator(); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return sortedSet.iterator(from); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return sortedSet.iterator(from); } @Override public KEY_GENERIC_TYPE FIRST() { synchronized(sync) { return sortedSet.FIRST(); } } @@ -401,10 +405,10 @@ public final class SORTED_SETS { public SORTED_SET KEY_GENERIC tailSet(final KEY_GENERIC_TYPE from) { return new UnmodifiableSortedSet KEY_GENERIC_DIAMOND(sortedSet.tailSet(from)); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return ITERATORS.unmodifiable(sortedSet.iterator()); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator() { return ITERATORS.unmodifiable(sortedSet.iterator()); } @Override - public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return ITERATORS.unmodifiable(sortedSet.iterator(from)); } + public KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return ITERATORS.unmodifiable(sortedSet.iterator(from)); } @Override public KEY_GENERIC_TYPE FIRST() { return sortedSet.FIRST(); } diff --git a/gencsource.sh b/gencsource.sh index b7b5f26e..0348bfd8 100755 --- a/gencsource.sh +++ b/gencsource.sh @@ -380,6 +380,8 @@ fi)\ "#define KEY_WIDENED_SPLITERATOR ${TYPE_CAP[$wk]}Spliterator\n"\ "#define KEY_BIDI_ITERATOR ${TYPE_CAP2[$k]}BidirectionalIterator\n"\ "#define KEY_BIDI_ITERABLE ${TYPE_CAP2[$k]}BidirectionalIterable\n"\ +"#define KEY_MOVE_BIDI_ITERATOR ${TYPE_CAP2[$k]}MovableBidirectionalIterator\n"\ +"#define KEY_MOVE_BIDI_ITERABLE ${TYPE_CAP2[$k]}MovableBidirectionalIterable\n"\ "#define KEY_LIST_ITERATOR ${TYPE_CAP2[$k]}ListIterator\n"\ "#define KEY_BIG_LIST_ITERATOR ${TYPE_CAP2[$k]}BigListIterator\n"\ "#define STD_KEY_ITERATOR ${TYPE_STD[$k]}Iterator\n"\ diff --git a/makefile b/makefile index 9239a3ee..94496a8d 100644 --- a/makefile +++ b/makefile @@ -142,6 +142,11 @@ $(BIDIRECTIONAL_ITERABLES): drv/BidirectionalIterable.drv; ./gencsource.sh $< $@ CSOURCES += $(BIDIRECTIONAL_ITERABLES) +MOVABLE_BIDIRECTIONAL_ITERABLES := $(foreach k,$(TYPE_NOREF), $(GEN_SRCDIR)/$(PKG_PATH)/$(PACKAGE_$(k))/$(k)MovableBidirectionalIterable.c) +$(MOVABLE_BIDIRECTIONAL_ITERABLES): drv/MovableBidirectionalIterable.drv; ./gencsource.sh $< $@ >$@ + +CSOURCES += $(MOVABLE_BIDIRECTIONAL_ITERABLES) + COLLECTIONS := $(foreach k,$(TYPE), $(GEN_SRCDIR)/$(PKG_PATH)/$(PACKAGE_$(k))/$(k)Collection.c) $(COLLECTIONS): drv/Collection.drv; ./gencsource.sh $< $@ >$@ @@ -237,6 +242,11 @@ $(BIDIRECTIONAL_ITERATORS): drv/BidirectionalIterator.drv; ./gencsource.sh $< $@ CSOURCES += $(BIDIRECTIONAL_ITERATORS) +MOVABLE_BIDIRECTIONAL_ITERATORS := $(foreach k,$(TYPE_NOREF), $(GEN_SRCDIR)/$(PKG_PATH)/$(PACKAGE_$(k))/$(k)MovableBidirectionalIterator.c) +$(MOVABLE_BIDIRECTIONAL_ITERATORS): drv/MovableBidirectionalIterator.drv; ./gencsource.sh $< $@ >$@ + +CSOURCES += $(MOVABLE_BIDIRECTIONAL_ITERATORS) + LIST_ITERATORS := $(foreach k,$(TYPE_NOREF), $(GEN_SRCDIR)/$(PKG_PATH)/$(PACKAGE_$(k))/$(k)ListIterator.c) $(LIST_ITERATORS): drv/ListIterator.drv; ./gencsource.sh $< $@ >$@ diff --git a/src/it/unimi/dsi/fastutil/MovableBidirectionalIterator.java b/src/it/unimi/dsi/fastutil/MovableBidirectionalIterator.java new file mode 100644 index 00000000..f3fef0b6 --- /dev/null +++ b/src/it/unimi/dsi/fastutil/MovableBidirectionalIterator.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2002-2023 Sebastiano Vigna + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package it.unimi.dsi.fastutil; + +import java.util.Iterator; +import java.util.ListIterator; + +/** A bidirectional {@link Iterator}. + * + *

This kind of iterator is essentially a {@link ListIterator} that + * does not support {@link ListIterator#previousIndex()} and {@link + * ListIterator#nextIndex()}. It is useful for those maps that can easily + * provide bidirectional iteration, but provide no index. + * + *

Note that iterators returned by {@code fastutil} classes are more + * specific, and support skipping. This class serves the purpose of organising + * in a cleaner way the relationships between various iterators. + * + * @see Iterator + * @see ListIterator + */ + +public interface MovableBidirectionalIterator extends BidirectionalIterator { + + /** Moves back or forward to the elements in the collection, + * starting from a given element of the domain. + * + *

This method moves an iterator to the specified starting point. + * The starting point is any element comparable to the elements of the collection + * (even if it does not actually belong to the collection). + * The next element of the returned iterator is the least element of + * the collection that is greater than the starting point (if there are no + * elements greater than the starting point, {@link + * it.unimi.dsi.fastutil.BidirectionalIterator#hasNext() hasNext()} will return + * {@code false}). The previous element of the returned iterator is + * the greatest element of the collection that is smaller than or equal to the + * starting point (if there are no elements smaller than or equal to the + * starting point, {@link it.unimi.dsi.fastutil.BidirectionalIterator#hasPrevious() + * hasPrevious()} will return {@code false}). + * + * @param fromElement an element to start from. + * @throws UnsupportedOperationException if the collection does not support move at iterator. + */ + void move(final K fromElement); + + /** Moves iterator to the first element of the collection. + * + * @throws UnsupportedOperationException if the collection does not support move at iterator. + */ + void rewind(); +} diff --git a/test/it/unimi/dsi/fastutil/ints/Int2IntAVLTreeMapTest.java b/test/it/unimi/dsi/fastutil/ints/Int2IntAVLTreeMapTest.java new file mode 100644 index 00000000..7135f81e --- /dev/null +++ b/test/it/unimi/dsi/fastutil/ints/Int2IntAVLTreeMapTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017-2022 Sebastiano Vigna + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package it.unimi.dsi.fastutil.ints; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; + +public class Int2IntAVLTreeMapTest { + + @Test + public void testIteratorMove() { + final Int2IntSortedMap m = new Int2IntAVLTreeMap(); + for (int i = 0; i < 100; i += 3) { + m.put(i, i); + } + + final IntMovableBidirectionalIterator mapIt = m.keySet().iterator(50); + + assertTrue(mapIt.hasNext()); + assertEquals(51, mapIt.nextInt()); + + mapIt.move(50); + assertTrue(mapIt.hasPrevious()); + assertEquals(48, mapIt.previousInt()); + + mapIt.move(-1); + assertTrue(mapIt.hasNext()); + assertFalse(mapIt.hasPrevious()); + assertEquals(0, mapIt.nextInt()); + + mapIt.move(100); + assertFalse(mapIt.hasNext()); + assertTrue(mapIt.hasPrevious()); + assertEquals(99, mapIt.previousInt()); + + final ObjectMovableBidirectionalIterator mapEntryIt = m.int2IntEntrySet().iterator(new AbstractInt2IntMap.BasicEntry(50, 0)); + + assertTrue(mapEntryIt.hasNext()); + assertEquals(51, mapEntryIt.next().getIntKey()); + + mapEntryIt.move(new AbstractInt2IntMap.BasicEntry(50, 0)); + assertTrue(mapEntryIt.hasPrevious()); + assertEquals(48, mapEntryIt.previous().getIntKey()); + + mapEntryIt.move(new AbstractInt2IntMap.BasicEntry(-1, 0)); + assertTrue(mapEntryIt.hasNext()); + assertFalse(mapEntryIt.hasPrevious()); + assertEquals(0, mapEntryIt.next().getIntKey()); + + mapEntryIt.move(new AbstractInt2IntMap.BasicEntry(100, 0)); + assertFalse(mapEntryIt.hasNext()); + assertTrue(mapEntryIt.hasPrevious()); + assertEquals(99, mapEntryIt.previous().getIntKey()); + } + + @Test + public void testIteratorRewind() { + final Int2IntSortedMap m = new Int2IntAVLTreeMap(); + for (int i = 0; i < 100; i += 3) { + m.put(i, i); + } + + final IntMovableBidirectionalIterator mapIt = m.keySet().iterator(50); + + assertTrue(mapIt.hasNext()); + assertEquals(51, mapIt.nextInt()); + + mapIt.rewind(); + assertTrue(mapIt.hasNext()); + assertFalse(mapIt.hasPrevious()); + assertEquals(0, mapIt.nextInt()); + + final ObjectMovableBidirectionalIterator mapEntryIt = m.int2IntEntrySet().iterator(new AbstractInt2IntMap.BasicEntry(50, 0)); + + assertTrue(mapEntryIt.hasNext()); + assertEquals(51, mapEntryIt.next().getIntKey()); + + mapEntryIt.rewind(); + assertTrue(mapEntryIt.hasNext()); + assertFalse(mapEntryIt.hasPrevious()); + assertEquals(0, mapEntryIt.next().getIntKey()); + } +} diff --git a/test/it/unimi/dsi/fastutil/ints/Int2IntRBTreeMapTest.java b/test/it/unimi/dsi/fastutil/ints/Int2IntRBTreeMapTest.java new file mode 100644 index 00000000..944af8d5 --- /dev/null +++ b/test/it/unimi/dsi/fastutil/ints/Int2IntRBTreeMapTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017-2022 Sebastiano Vigna + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package it.unimi.dsi.fastutil.ints; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import it.unimi.dsi.fastutil.objects.ObjectMovableBidirectionalIterator; + +public class Int2IntRBTreeMapTest { + + @Test + public void testIteratorMove() { + final Int2IntSortedMap m = new Int2IntRBTreeMap(); + for (int i = 0; i < 100; i += 3) { + m.put(i, i); + } + + final IntMovableBidirectionalIterator mapKeyIt = m.keySet().iterator(50); + + assertTrue(mapKeyIt.hasNext()); + assertEquals(51, mapKeyIt.nextInt()); + + mapKeyIt.move(50); + assertTrue(mapKeyIt.hasPrevious()); + assertEquals(48, mapKeyIt.previousInt()); + + mapKeyIt.move(-1); + assertTrue(mapKeyIt.hasNext()); + assertFalse(mapKeyIt.hasPrevious()); + assertEquals(0, mapKeyIt.nextInt()); + + mapKeyIt.move(100); + assertFalse(mapKeyIt.hasNext()); + assertTrue(mapKeyIt.hasPrevious()); + assertEquals(99, mapKeyIt.previousInt()); + + final ObjectMovableBidirectionalIterator mapEntryIt = m.int2IntEntrySet().iterator(new AbstractInt2IntMap.BasicEntry(50, 0)); + + assertTrue(mapEntryIt.hasNext()); + assertEquals(51, mapEntryIt.next().getIntKey()); + + mapEntryIt.move(new AbstractInt2IntMap.BasicEntry(50, 0)); + assertTrue(mapEntryIt.hasPrevious()); + assertEquals(48, mapEntryIt.previous().getIntKey()); + + mapEntryIt.move(new AbstractInt2IntMap.BasicEntry(-1, 0)); + assertTrue(mapEntryIt.hasNext()); + assertFalse(mapEntryIt.hasPrevious()); + assertEquals(0, mapEntryIt.next().getIntKey()); + + mapEntryIt.move(new AbstractInt2IntMap.BasicEntry(100, 0)); + assertFalse(mapEntryIt.hasNext()); + assertTrue(mapEntryIt.hasPrevious()); + assertEquals(99, mapEntryIt.previous().getIntKey()); + } + + @Test + public void testIteratorRewind() { + final Int2IntSortedMap m = new Int2IntRBTreeMap(); + for (int i = 0; i < 100; i += 3) { + m.put(i, i); + } + + final IntMovableBidirectionalIterator mapIt = m.keySet().iterator(50); + + assertTrue(mapIt.hasNext()); + assertEquals(51, mapIt.nextInt()); + + mapIt.rewind(); + assertTrue(mapIt.hasNext()); + assertFalse(mapIt.hasPrevious()); + assertEquals(0, mapIt.nextInt()); + + final ObjectMovableBidirectionalIterator mapEntryIt = m.int2IntEntrySet().iterator(new AbstractInt2IntMap.BasicEntry(50, 0)); + + assertTrue(mapEntryIt.hasNext()); + assertEquals(51, mapEntryIt.next().getIntKey()); + + mapEntryIt.rewind(); + assertTrue(mapEntryIt.hasNext()); + assertFalse(mapEntryIt.hasPrevious()); + assertEquals(0, mapEntryIt.next().getIntKey()); + } +} diff --git a/test/it/unimi/dsi/fastutil/ints/IntAVLTreeSetTest.java b/test/it/unimi/dsi/fastutil/ints/IntAVLTreeSetTest.java index fd9ae055..dab5544c 100644 --- a/test/it/unimi/dsi/fastutil/ints/IntAVLTreeSetTest.java +++ b/test/it/unimi/dsi/fastutil/ints/IntAVLTreeSetTest.java @@ -41,4 +41,78 @@ public void testGet() { public void testLegacyMainMethodTests() throws Exception { MainRunner.callMainIfExists(IntAVLTreeSet.class, "test", /*num=*/"20", /*seed=*/"423429"); } + + @Test + public void testIteratorMove() { + final IntSortedSet s = new IntAVLTreeSet(); + for (int i = 0; i < 100; i += 3) { + s.add(i); + } + + final IntMovableBidirectionalIterator it = s.iterator(50); + + assertTrue(it.hasNext()); + assertEquals(51, it.nextInt()); + + it.move(50); + assertTrue(it.hasPrevious()); + assertEquals(48, it.previousInt()); + + it.move(-1); + assertTrue(it.hasNext()); + assertFalse(it.hasPrevious()); + assertEquals(0, it.nextInt()); + + it.move(100); + assertFalse(it.hasNext()); + assertTrue(it.hasPrevious()); + assertEquals(99, it.previousInt()); + + final IntMovableBidirectionalIterator subIt = s.subSet(10, 90).iterator(50); + + assertTrue(subIt.hasNext()); + assertEquals(51, subIt.nextInt()); + + subIt.move(50); + assertTrue(subIt.hasPrevious()); + assertEquals(48, subIt.previousInt()); + + subIt.move(-1); + assertTrue(subIt.hasNext()); + assertFalse(subIt.hasPrevious()); + assertEquals(12, subIt.nextInt()); + + subIt.move(90); + assertFalse(subIt.hasNext()); + assertTrue(subIt.hasPrevious()); + assertEquals(87, subIt.previousInt()); + } + + @Test + public void testIteratorRewind() { + final IntSortedSet s = new IntAVLTreeSet(); + for (int i = 0; i < 100; i += 3) { + s.add(i); + } + + final IntMovableBidirectionalIterator it = s.iterator(50); + + assertTrue(it.hasNext()); + assertEquals(51, it.nextInt()); + + it.rewind(); + assertTrue(it.hasNext()); + assertFalse(it.hasPrevious()); + assertEquals(0, it.nextInt()); + + final IntMovableBidirectionalIterator subIt = s.subSet(10, 90).iterator(50); + + assertTrue(subIt.hasNext()); + assertEquals(51, subIt.nextInt()); + + subIt.rewind(); + assertTrue(subIt.hasNext()); + assertFalse(subIt.hasPrevious()); + assertEquals(12, subIt.nextInt()); + } } diff --git a/test/it/unimi/dsi/fastutil/ints/IntRBTreeSetTest.java b/test/it/unimi/dsi/fastutil/ints/IntRBTreeSetTest.java index e151c447..6e1fdf2d 100644 --- a/test/it/unimi/dsi/fastutil/ints/IntRBTreeSetTest.java +++ b/test/it/unimi/dsi/fastutil/ints/IntRBTreeSetTest.java @@ -42,4 +42,78 @@ public void testAddAndGet() { public void testLegacyMainMethodTests() throws Exception { MainRunner.callMainIfExists(IntRBTreeSet.class, "test", /*num=*/"20", /*seed=*/"423429"); } + + @Test + public void testIteratorMove() { + final IntSortedSet s = new IntRBTreeSet(); + for (int i = 0; i < 100; i += 3) { + s.add(i); + } + + final IntMovableBidirectionalIterator it = s.iterator(50); + + assertTrue(it.hasNext()); + assertEquals(51, it.nextInt()); + + it.move(50); + assertTrue(it.hasPrevious()); + assertEquals(48, it.previousInt()); + + it.move(-1); + assertTrue(it.hasNext()); + assertFalse(it.hasPrevious()); + assertEquals(0, it.nextInt()); + + it.move(100); + assertFalse(it.hasNext()); + assertTrue(it.hasPrevious()); + assertEquals(99, it.previousInt()); + + final IntMovableBidirectionalIterator subIt = s.subSet(10, 90).iterator(50); + + assertTrue(subIt.hasNext()); + assertEquals(51, subIt.nextInt()); + + subIt.move(50); + assertTrue(subIt.hasPrevious()); + assertEquals(48, subIt.previousInt()); + + subIt.move(-1); + assertTrue(subIt.hasNext()); + assertFalse(subIt.hasPrevious()); + assertEquals(12, subIt.nextInt()); + + subIt.move(90); + assertFalse(subIt.hasNext()); + assertTrue(subIt.hasPrevious()); + assertEquals(87, subIt.previousInt()); + } + + @Test + public void testIteratorRewind() { + final IntSortedSet s = new IntAVLTreeSet(); + for (int i = 0; i < 100; i += 3) { + s.add(i); + } + + final IntMovableBidirectionalIterator it = s.iterator(50); + + assertTrue(it.hasNext()); + assertEquals(51, it.nextInt()); + + it.rewind(); + assertTrue(it.hasNext()); + assertFalse(it.hasPrevious()); + assertEquals(0, it.nextInt()); + + final IntMovableBidirectionalIterator subIt = s.subSet(10, 90).iterator(50); + + assertTrue(subIt.hasNext()); + assertEquals(51, subIt.nextInt()); + + subIt.rewind(); + assertTrue(subIt.hasNext()); + assertFalse(subIt.hasPrevious()); + assertEquals(12, subIt.nextInt()); + } }