Parcourir la source

Ordered colls get removeAt and alter methods

These are also tested with an extension of the OpenJDK tests. I think
`alter()` may be especially useful in the future.
Tommy Ettinger il y a 7 ans
Parent
commit
964611af10

+ 49 - 1
README.md

@@ -5,6 +5,23 @@ SquidLib is used for Wyrm, Epigon, Attack the Geth, Assault Fish, [Dungeon Merce
 
 [![Join the slow-paced chat at https://gitter.im/SquidPony/SquidLib](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SquidPony/SquidLib)
 
+Documentation:
+---
+Jars of javadocs are distributed with each release via Maven Central, and with the current latest via JitPack. You can
+get the docs and source of the latest version, 3.0.0-b6, in two parts for each; squidlib-util (the core of the library,
+and also the largest part) has its
+[docs here](http://search.maven.org/remotecontent?filepath=com/squidpony/squidlib-util/3.0.0-b6/squidlib-util-3.0.0-b6-javadoc.jar),
+and [source here](http://search.maven.org/remotecontent?filepath=com/squidpony/squidlib-util/3.0.0-b6/squidlib-util-3.0.0-b6-sources.jar),
+while squidlib (the display part of the library, named the way it is because depending on squidlib should also pull in
+squidlib-util to make it a "one-stop shop" dependency) has its
+[docs here](http://search.maven.org/remotecontent?filepath=com/squidpony/squidlib/3.0.0-b6/squidlib-3.0.0-b6-javadoc.jar),
+and [source here](http://search.maven.org/remotecontent?filepath=com/squidpony/squidlib/3.0.0-b6/squidlib-3.0.0-b6-sources.jar).
+You can browse the javadocs of the most recent commit
+[here for squidlib-util](https://jitpack.io/com/github/SquidPony/SquidLib/squidlib-util/-SNAPSHOT/javadoc/) and
+[here for squidlib](https://jitpack.io/com/github/SquidPony/SquidLib/squidlib/-SNAPSHOT/javadoc/), though JitPack will often
+need to rebuild the docs if there was a recent commit, which may take (according to their FAQ) up to 15 minutes; refresh
+if it isn't loading or looks wrong.
+
 Current Features:
 --
 ###Ease Of Use
@@ -199,7 +216,31 @@ Current Features:
   - Improvements to LanguageCipher as well, allowing library users to choose how much to cache, or to make it so a single fake word virtually-never can be generated as the translation for multiple source words
   - LinesPanel is simpler, allows line wrapping, and a choice of vertical direction for line layout
   - Over 2000 icons from http://game-icons.net added to the optional assets download, with a distance field effect applied so they can be used along with "stretchable" fonts, and all this put in a texture atlas for efficient lookup
-  - Various other small improvements, like RNG and RandomnessSource allowing all their variants to be copied with `copy()`, GapShuffler being added (used in Thesaurus to ensure the same word doesn't show up twice in rapid succession, which repeated loops through sequences shuffled with `RNG.shuffle()` can't guarantee), and Maker being added to help construction of cumbersome objects, like a LinkedHashMap, with one method call
+  - Various other small improvements, like RNG and RandomnessSource allowing all their variants to be copied with `copy()`, GapShuffler being added (used in Thesaurus
+    to ensure the same word doesn't show up twice in rapid succession, which repeated loops through sequences shuffled with `RNG.shuffle()` can't guarantee), and Maker
+    being added to help construction of cumbersome objects, like a LinkedHashMap, with one method call
+- In the upcoming next release, but not yet in a beta release (only in the master revision):
+  - Some breaking changes, though they should be easy to make un-broken in your code.
+    - The JRE's LinkedHashMap and LinkedHashSet were used heavily in SquidLib (mainly to ensure RNG seeds keep the same behavior across Java versions and platforms).
+    - These have been replaced with OrderedMap and OrderedSet, but the OrderedX classes have a superset of the LinkedHashX classes, so replacing is as simple as a find and
+      replace that swaps "LinkedHash" for "Ordered" and including squidlib-util's OrderedMap and/or OrderedSet classes (libGDX has unrelated classes with those names).
+    - But why? Well, OrderedMap and OrderedSet allow the ordering to be altered, so they can be shuffled, plus elements can be random-accessed by index in constant time,
+      and a useful feature is being able to generate a random ordering with `RNG.randomOrdering()` and use it with `reorder()` on multiple OrderedMap or OrderedSet
+      collections of the same size, producing the same "shuffle" for each one.
+      - Some other features are being added to the Ordered collections as they are needed, but they already do everything a LinkedHashMap or LinkedHashSet does (except
+        access-ordering instead of insertion-ordering in LinkedHashMaps, which is not commonly needed), and pass essentially the same tests the LinkedHashX classes do.
+  - Hopefully-significant speedups to region encoding!
+    - CoordPacker is nice to use, mostly, but if used too often it can get slow, especially on larger maps. The need for a better way of handling small amounts of
+      encoded regions quickly without needing tightly controlled memory usage (the initial reason CoordPacker was made) has led to GreasedRegion.
+    - GreasedRegion is an un-compressed bitset-like region encoding that already implements equivalents to much of CoordPacker's API but can be *hundreds of times faster*.
+    - It's a little less lean on memory usage, hence the "fat-filled" name, but it's also like greased lightning as to speed.
+    - It still hasn't been integrated into the rest of SquidLib, so it isn't clear how much speedup can be gained, but it should really help Placement and SectionDungeonGenerator.
+  - More FakeLanguageGen improvements; now you can generate purely random languages.
+  - DetailedMimic is like MimicFill, and is a port of [SynTex](https://github.com/mxgmn/SynTex) but can handle color or
+    char information, with the former useful for texture generation and the latter for map style imitation.
+  - Improvements to the long-neglected Bresenham line drawing class, letting 2D lines use 2D Coords (and pool them accordingly, helping performance).
+  - Some extra support for DijkstraMap, including checks for out-of-map points in more places and the ability to configure what makes diagonals pass-able.
+  - Several bugfixes, including to one annoying 9-month-old bug with a specific FOV type.
 - But, 3.0.0's final release will be major, and so should be expected to *break* API backwards compatibility
   - Any minor releases after 3.0.0 and before 4.0.0 should be expected to *keep* API backwards compatibility, unless a feature is broken or unusable
   - The most significant change in 3.0.0 will be the removal of the Swing-based rendering and full transition to the similar, but much faster and more responsive, libGDX renderer
@@ -212,6 +253,13 @@ Current Features:
   - If you use SquidLib's latest version as of April 12, the assets have been moved out of the `squidlib` jar, making the download size smaller, but a freshly-updated SquidSetup has all the latest assets
     and will put them in the correct assets folder (as before) without duplicating them.
     - If you don't use SquidSetup, you can download any assets you need from the assets/ folder of this GitHub repo, or get all the assets in a .zip or .tar.gz file from SquidSetup's release page for the [latest SquidSetup](https://github.com/tommyettinger/SquidSetup/releases/tag/v3.0.0-LATEST).
+  - [This commit on June 19, 2016](https://github.com/SquidPony/SquidLib/commit/22c770b37b3635c6beacadda6ef71e07c9a55a8e)
+    changed usages of LinkedHashMap and LinkedHashSet to OrderedMap and OrderedSet in the squidpony.squidmath package.
+    If you use SquidLib versions before that commit, then update to after that commit (after beta 6), you should expect
+    some find/replace needed throughout your code. The APIs for LinkedHashSet and OrderedSet are identical except
+    for the additions OrderedSet makes, and almost the same is true for LinkedHashMap (no possible access-ordering like
+    in LinkedHashMap; only insertion-ordering or user-specified-ordering are possible in OrderedMap, though the last
+    isn't possible in LinkedHashMap).
 
 Download
 --

+ 116 - 9
squidlib-util/src/main/java/squidpony/squidmath/OrderedMap.java

@@ -388,11 +388,7 @@ public class OrderedMap<K, V> implements SortedMap<K, V>, java.io.Serializable,
         value[pos] = v;
         if (size == 0) {
             first = last = pos;
-            // Special case of SET_UPPER_LOWER( link[ pos ], -1, -1 );
-            //link[pos] = -1L;
         } else {
-            //link[last] ^= ((link[last] ^ (pos & 0xFFFFFFFFL)) & 0xFFFFFFFFL);
-            //link[pos] = ((last & 0xFFFFFFFFL) << 32) | (-1 & 0xFFFFFFFFL);
             last = pos;
         }
         order.add(pos);
@@ -426,7 +422,6 @@ public class OrderedMap<K, V> implements SortedMap<K, V>, java.io.Serializable,
                 if (((curr = key[pos]) == null)) {
                     key[last] = (null);
                     value[last] = null;
-                    order.removeValue(last);
                     return;
                 }
                 slot = (HashCommon.mix(hasher.hash(curr)))
@@ -808,8 +803,8 @@ public class OrderedMap<K, V> implements SortedMap<K, V>, java.io.Serializable,
                 && ((value[n]) == null ? v == null : (value[n]).equals(v)))
             return true;
         for (int i = n; i-- != 0;)
-            if (!((key[i]) == null)
-                    && ((value[i]) == null ? v == null : (value[i]).equals(v)))
+            if (key[i] != null
+                    && (value[i] == null ? v == null : value[i].equals(v)))
                 return true;
         return false;
     }
@@ -2376,10 +2371,10 @@ public class OrderedMap<K, V> implements SortedMap<K, V>, java.io.Serializable,
         int pos;
         final K[] key = this.key;
         if (idx < 0 || idx >= order.size)
-            return containsNullKey ? value[n] : defRetValue;
+            return defRetValue;
         // The starting point.
         if ((key[pos = order.get(idx)]) == null)
-            return defRetValue;
+            return containsNullKey ? value[n] : defRetValue;
         return value[pos];
     }
     /**
@@ -2407,6 +2402,24 @@ public class OrderedMap<K, V> implements SortedMap<K, V>, java.io.Serializable,
         return new MapEntry(order.get(idx));
     }
 
+    /**
+     * Removes the key and value at the given index in the iteration order in not-exactly constant time (though it still
+     * should be efficient).
+     * @param idx the index in the iteration order of the key and value to remove
+     * @return the value removed, if there was anything removed, or the default return value otherwise (often null)
+     */
+    public V removeAt(final int idx) {
+
+        if (idx < 0 || idx >= order.size)
+            return defRetValue;
+        int pos = order.get(idx);
+        if (key[pos] == null) {
+            if (containsNullKey)
+                return removeNullEntry();
+            return defRetValue;
+        }
+        return removeEntry(pos);
+    }
     /**
      * Gets a random value from this OrderedMap in constant time, using the given RNG to generate a random number.
      * @param rng used to generate a random index for a value
@@ -2475,5 +2488,99 @@ public class OrderedMap<K, V> implements SortedMap<K, V>, java.io.Serializable,
         last = order.peek();
         return this;
     }
+    private V alterEntry(final int pos, final K replacement) {
+        key[pos] = null;
+
+        final V[] value = this.value;
+        V v = value[pos];
+        value[pos] = null;
+
+        int rep;
+        if ((replacement == null)) {
+            if (containsNullKey)
+                return v;
+            rep = n;
+            containsNullKey = true;
+        } else {
+            K curr;
+            final K[] key = this.key;
+
+            // The starting point.
+            if (!((curr = key[rep = HashCommon.mix(hasher.hash(replacement)) & mask]) == null)) {
+                if (curr.equals(replacement))
+                    return v;
+                while (!((curr = key[rep = (rep + 1) & mask]) == null))
+                    if (curr.equals(replacement))
+                        return v;
+            }
+            key[rep] = replacement;
+            value[rep] = v;
+        }
+        fixPointers(pos, rep);
+        return v;
+    }
+    private V alterNullEntry(final K replacement) {
+        containsNullKey = false;
+        key[n] = null;
+        final V[] value = this.value;
+        V v = value[n];
+        value[n] = null;
+
+        int rep;
+        if (replacement == null) {
+            rep = n;
+            containsNullKey = true;
+        } else {
+            K curr;
+            final K[] key = this.key;
+            // The starting point.
+            if ((curr = key[rep = HashCommon.mix(hasher.hash(replacement)) & mask]) != null) {
+                if (curr.equals(replacement))
+                    return v;
+                while ((curr = key[rep = (rep + 1) & mask]) != null)
+                    if (curr.equals(replacement))
+                        return v;
+            }
+            key[rep] = replacement;
+            value[rep] = v;
+
+        }
+        fixPointers(n, rep);
+        return v;
+    }
+
+    /**
+     * Swaps a key, original, for another key, replacement, while keeping replacement at the same point in the iteration
+     * order as original and keeping it associated with the same value (which also keeps its iteration index).
+     * @param original the key to find and swap out
+     * @param replacement the key to replace original with
+     * @return the value associated with original before, and replacement now
+     */
+    public V alter(final K original, final K replacement) {
+        if (original == null) {
+            if (containsNullKey) {
+                return alterNullEntry(replacement);
+            }
+            else
+                return put(replacement, null);
+        }
+        else if(original.equals(replacement))
+            return get(original);
+        K curr;
+        final K[] key = this.key;
+        int pos;
+        // The starting point.
+        if ((curr = key[pos = HashCommon.mix(hasher.hash(original)) & mask]) == null)
+            return put(replacement, null);
+        if (original.equals(curr))
+            return alterEntry(pos, replacement);
+        while (true) {
+            if (((curr = key[pos = (pos + 1) & mask]) == null))
+                return put(replacement, null);
+            if (original.equals(curr))
+                return alterEntry(pos, replacement);
+        }
+    }
+
 
 }

+ 98 - 6
squidlib-util/src/main/java/squidpony/squidmath/OrderedSet.java

@@ -419,11 +419,7 @@ public class OrderedSet<K> implements SortedSet<K>, java.io.Serializable, Clonea
         }
         if (size == 0) {
             first = last = pos;
-            // Special case of SET_UPPER_LOWER( link[ pos ], -1, -1 );
-            //link[pos] = -1L;
         } else {
-            //link[last] ^= ((link[last] ^ (pos & 0xFFFFFFFFL)) & 0xFFFFFFFFL);
-            //link[pos] = ((last & 0xFFFFFFFFL) << 32) | (-1 & 0xFFFFFFFFL);
             last = pos;
         }
         order.add(pos);
@@ -498,7 +494,6 @@ public class OrderedSet<K> implements SortedSet<K>, java.io.Serializable, Clonea
             for (;;) {
                 if (((curr = key[pos]) == null)) {
                     key[last] = (null);
-                    order.removeValue(last);
                     return;
                 }
                 slot = HashCommon.mix(hasher.hash(curr))
@@ -1701,13 +1696,30 @@ public class OrderedSet<K> implements SortedSet<K>, java.io.Serializable, Clonea
      * @return the key at the index, if the index is valid, otherwise null
      */
     public K getAt(final int idx) {
-        final K[] key = this.key;
         if (idx < 0 || idx >= order.size)
             return null;
+        final K[] key = this.key;
         // The starting point.
         return key[order.get(idx)];
     }
 
+    /**
+     * Removes the item at the given index in the iteration order in not-exactly constant time (though it still should
+     * be efficient).
+     * @param idx the index in the iteration order of the item to remove
+     * @return true if this Set was changed as a result of this call, or false if nothing changed.
+     */
+    public boolean removeAt(final int idx) {
+        if (idx < 0 || idx >= order.size)
+            throw new NoSuchElementException();
+        int pos = order.get(idx);
+        if (key[pos] == null) {
+            if (containsNull)
+                return removeNullEntry();
+            return false;
+        }
+        return removeEntry(pos);
+    }
     /**
      * Gets a random value from this OrderedSet in constant time, using the given RNG to generate a random number.
      * @param rng used to generate a random index for a value
@@ -1755,4 +1767,84 @@ public class OrderedSet<K> implements SortedSet<K>, java.io.Serializable, Clonea
         last = order.peek();
         return this;
     }
+    private boolean alterEntry(final int pos, final K replacement) {
+        int rep;
+        if ((replacement == null)) {
+            if (containsNull)
+                return false;
+            rep = n;
+            containsNull = true;
+        } else {
+            K curr;
+            final K[] key = this.key;
+            key[pos] = null;
+            // The starting point.
+            if (!((curr = key[rep = HashCommon.mix(hasher.hash(replacement)) & mask]) == null)) {
+                if (curr.equals(replacement))
+                    return false;
+                while (!((curr = key[rep = (rep + 1) & mask]) == null))
+                    if (curr.equals(replacement))
+                        return false;
+            }
+            key[rep] = replacement;
+        }
+        fixPointers(pos, rep);
+        return true;
+    }
+    private boolean alterNullEntry(final K replacement) {
+        containsNull = false;
+        key[n] = null;
+        int rep;
+        if (replacement == null) {
+            rep = n;
+            containsNull = true;
+        } else {
+            K curr;
+            final K[] key = this.key;
+            // The starting point.
+            if (!((curr = key[rep = HashCommon.mix(hasher.hash(replacement)) & mask]) == null)) {
+                if (curr.equals(replacement))
+                    return false;
+                while (!((curr = key[rep = (rep + 1) & mask]) == null))
+                    if (curr.equals(replacement))
+                        return false;
+            }
+            key[rep] = replacement;
+        }
+        fixPointers(n, rep);
+        return true;
+    }
+
+    /**
+     * Changes a K, original, to another, replacement, while keeping replacement at the same point in the ordering.
+     * @param original a K value that will be removed from this Set if present, and its iteration index remembered
+     * @param replacement another K value that will replace original at the remembered index
+     * @return true if the Set changed, or false if it didn't (such as if the two arguments are equal, or replacement was already in the Set but original was not)
+     */
+    public boolean alter(K original, K replacement)
+    {
+        if (original == null) {
+            if (containsNull) {
+                return replacement != null && alterNullEntry(replacement);
+            }
+            else
+                return add(replacement);
+        }
+        else if(original.equals(replacement))
+            return false;
+        K curr;
+        final K[] key = this.key;
+        int pos;
+        // The starting point.
+        if ((curr = key[pos = HashCommon.mix(hasher.hash(original)) & mask]) == null)
+            return add(replacement);
+        if (original.equals(curr))
+            return alterEntry(pos, replacement);
+        while (true) {
+            if (((curr = key[pos = (pos + 1) & mask]) == null))
+                return add(replacement);
+            if (original.equals(curr))
+                return alterEntry(pos, replacement);
+        }
+    }
 }

+ 45 - 6
squidlib-util/src/test/java/openjdk/java/util/OrderedMapTest.java

@@ -37,8 +37,9 @@ import java.util.*;
 
 public class OrderedMapTest {
     static Random rnd = new Random(666);
-    static Object nil = new Integer(0);
+    static Object nil = 0;
 
+    @SuppressWarnings("unchecked")
     public static void main(String[] args)  throws Exception {
         int numItr =  200;
         int mapSize = 2500;
@@ -51,7 +52,7 @@ public class OrderedMapTest {
             for (int j=0; j<mapSize; j++) {
                 Object newHead;
                 do {
-                    newHead = new Integer(rnd.nextInt());
+                    newHead = rnd.nextInt();
                 } while (m.containsKey(newHead));
                 m.put(newHead, head);
                 head = newHead;
@@ -82,14 +83,52 @@ public class OrderedMapTest {
                 throw new Exception("Map nonempty after removing all links.");
             if (j != mapSize)
                 throw new Exception("Linked list size not as expected.");
+
+            //head = rnd.nextInt(mapSize*4);
+            OrderedMap weird1 = new OrderedMap();
+            for (int s=0; s<mapSize; s++) {
+                Object newHead;
+                do {
+                    newHead = rnd.nextInt(mapSize*4);
+                } while (weird1.containsKey(newHead));
+                weird1.put(newHead, head);
+                head = newHead;
+            }
+            weird1.alter(weird1.firstKey(), mapSize*4);
+            OrderedMap weird2 = new OrderedMap();
+            for (int s=0; s<mapSize; s++) {
+                Object newHead;
+                do {
+                    newHead = rnd.nextInt(mapSize*4);
+                } while (weird2.containsKey(newHead));
+                weird2.put(newHead, head);
+                head = newHead;
+            }
+            weird2.alter(weird2.firstKey(), mapSize*4);
+
+
+            if(!(weird1.containsKey(weird2.firstKey()) && weird2.containsKey(weird1.firstKey())))
+                throw new Exception("Weird ordered maps don't share a first key");
+            weird1.put(mapSize * 4, mapSize * 5 + rnd.nextInt(mapSize));
+            weird2.put(mapSize * 4, mapSize * 5 + rnd.nextInt(mapSize));
+            weird1.reorder(1, 0);
+            weird2.reorder(1, 0);
+            if(!(weird1.keyAt(1).equals(weird2.keyAt(1)) && weird2.keyAt(1).equals(weird1.keyAt(1))))
+                throw new Exception("Weird ordered maps don't share a reordered key");
+            Object weirdVal1 = weird1.removeAt(1);
+            Object weirdVal2 = weird2.removeAt(1);
+            if(weird1.containsKey(mapSize*4) || weird2.containsKey(mapSize*4))
+                throw new Exception("Weird ordered maps didn't respond correctly to removeAt(), keys");
+            if(weird1.containsValue(weirdVal1) || weird2.containsValue(weirdVal2))
+                throw new Exception("Weird ordered maps didn't respond correctly to removeAt(), values");
         }
 
         Map m = new OrderedMap();
         for (int i=0; i<mapSize; i++)
-            if (m.put(new Integer(i), new Integer(2*i)) != null)
+            if (m.put(i, 2 * i) != null)
                 throw new Exception("put returns non-null value erroneously.");
         for (int i=0; i<2*mapSize; i++)
-            if (m.containsValue(new Integer(i)) != (i%2==0))
+            if (m.containsValue(i) != (i%2==0))
                 throw new Exception("contains value "+i);
         if (m.put(nil, nil) == null)
             throw new Exception("put returns a null value erroneously.");
@@ -144,7 +183,7 @@ public class OrderedMapTest {
         m = new OrderedMap();
         List l = new ArrayList(mapSize);
         for (int i=0; i<mapSize; i++) {
-            Integer x = new Integer(i);
+            Integer x = i;
             m.put(x, x);
             l.add(x);
         }
@@ -165,7 +204,7 @@ public class OrderedMapTest {
         if (!new ArrayList(m.keySet()).equals(l))
             throw new Exception("Insert order not preserved after reinsert.");
 
-        m2 = (Map) ((OrderedMap)m).clone();
+        m2 = ((OrderedMap)m).clone();
         if (!m.equals(m2))
             throw new Exception("Insert-order Map != clone.");
 

+ 17 - 1
squidlib-util/src/test/java/openjdk/java/util/OrderedSetTest.java

@@ -38,6 +38,7 @@ import java.util.*;
 public class OrderedSetTest {
     static Random rnd = new Random(666);
 
+    @SuppressWarnings("unchecked")
     public static void main(String[] args) throws Exception {
         int numItr =  200;
         int setSize = 2500;
@@ -54,6 +55,8 @@ public class OrderedSetTest {
             Set diff1 = clone(s1); diff1.removeAll(s2);
             Set diff2 = clone(s2); diff2.removeAll(s1);
             Set union = clone(s1); union.addAll(s2);
+            OrderedSet weird1 = new OrderedSet(s1); weird1.alter(weird1.first(), setSize);
+            OrderedSet weird2 = new OrderedSet(s2); weird2.alter(weird2.first(), setSize);
 
             if (diff1.removeAll(diff2))
                 throw new Exception("Set algebra identity 2 failed");
@@ -97,10 +100,22 @@ public class OrderedSetTest {
             s1.clear();
             if (!s1.isEmpty())
                 throw new Exception("Set nonempty after clear.");
+            if(!(weird1.contains(weird2.first()) && weird2.contains(weird1.first())))
+                throw new Exception("Weird ordered sets don't share a first element");
+            weird1.reorder(1, 0);
+            weird2.reorder(1, 0);
+            if(!(weird1.getAt(1).equals(weird2.getAt(1)) && weird2.getAt(1).equals(weird1.getAt(1))))
+                throw new Exception("Weird ordered sets don't share a reordered element");
+            weird1.removeAt(1);
+            weird2.removeAt(1);
+            if(weird1.contains(setSize) || weird2.contains(setSize))
+                throw new Exception("Weird ordered sets didn't respond correctly to removeAt()");
+
         }
         System.err.println("Success.");
     }
 
+    @SuppressWarnings("unchecked")
     static Set clone(Set s) throws Exception {
         Set clone;
         int method = rnd.nextInt(3);
@@ -137,10 +152,11 @@ public class OrderedSetTest {
         return result;
     }
 
+    @SuppressWarnings("unchecked")
     static void AddRandoms(Set s, int n) throws Exception {
         for (int i=0; i<n; i++) {
             int r = rnd.nextInt() % n;
-            Integer e = new Integer(r < 0 ? -r : r);
+            Integer e = r < 0 ? -r : r;
 
             int preSize = s.size();
             boolean prePresent = s.contains(e);