diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2019-07-02 18:05:22 -0400 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2019-07-02 18:05:22 -0400 |
| commit | 843329de434bb334d90927c4d22345373a388530 (patch) | |
| tree | b0ad1f764bd29ff43841e1095a5b58194c20cb37 /src/main/java/bjc/esodata | |
| parent | ac36f171a3cebb0993cc28548635e3f654f8e325 (diff) | |
Rename package root
The package root is now bjc, not io.github.bculkin2442.
Diffstat (limited to 'src/main/java/bjc/esodata')
| -rw-r--r-- | src/main/java/bjc/esodata/AbbrevMap.java | 207 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/DefaultList.java | 116 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/Directory.java | 107 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/DoubleSided.java | 24 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/DoubleTape.java | 189 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/MapSet.java | 178 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/PushdownMap.java | 158 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/QueueStack.java | 89 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/SimpleDirectory.java | 95 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/SimpleStack.java | 87 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/SingleTape.java | 212 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/SpaghettiStack.java | 102 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/Stack.java | 456 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/Tape.java | 135 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/ThresholdSet.java | 238 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/UnifiedDirectory.java | 105 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/todos.txt | 3 |
17 files changed, 2501 insertions, 0 deletions
diff --git a/src/main/java/bjc/esodata/AbbrevMap.java b/src/main/java/bjc/esodata/AbbrevMap.java new file mode 100644 index 0000000..048577d --- /dev/null +++ b/src/main/java/bjc/esodata/AbbrevMap.java @@ -0,0 +1,207 @@ +package bjc.esodata; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import bjc.funcdata.FunctionalMap; +import bjc.funcdata.IMap; + +// /** +// * Represents a mapping from a set of strings to a mapping of all unambiguous +// * prefixes of their respective strings. +// * +// * This works the same as Ruby's Abbrev module. +// * +// * @author EVE +// */ +// public class AbbrevMap { +// /* All of the words we have abbreviations for. */ +// private final Set<String> wrds; + +// /* Maps abbreviations to their strings. */ +// private IMap<String, String> abbrevMap; + +// /* Counts how many times we've seen a substring. */ +// private Set<String> seen; + +// /* Maps ambiguous abbreviations to the strings they could be. */ +// private SetMultimap<String, String> ambMap; + +// /** +// * Create a new abbreviation map. +// * +// * @param words +// * The initial set of words to put in the map. +// */ +// public AbbrevMap(final String... words) { +// wrds = new HashSet<>(Arrays.asList(words)); + +// recalculate(); +// } + +// /** +// * Recalculate all the abbreviations in this map. +// * +// * This may be needed after certain operations to ensure that all of the +// * results are correct. +// */ +// public void recalculate() { +// abbrevMap = new FunctionalMap<>(); + +// ambMap = HashMultimap.create(); + +// seen = new HashSet<>(); + +// for(final String word : wrds) { +// intAddWord(word); +// } +// } + +// /** +// * Adds words to the abbreviation map. +// * +// * @param words +// * The words to add to the abbreviation map. +// */ +// public void addWords(final String... words) { +// wrds.addAll(Arrays.asList(words)); + +// for(final String word : words) { +// intAddWord(word); +// } +// } + +// /* Actually add abbreviations of a word. */ +// private void intAddWord(final String word) { +// /* A word always abbreviates to itself. */ +// abbrevMap.put(word, word); + +// /* Skip blank words. */ +// if(word.equals("")) return; + +// /* Handle each possible abbreviation. */ +// for(int i = word.length(); i > 0; i--) { +// final String subword = word.substring(0, i); + +// if(seen.contains(subword)) { +// /* +// * Remove a mapping if its ambiguous and not a +// * whole word. +// */ +// if(abbrevMap.containsKey(subword) && !wrds.contains(subword)) { +// final String oldword = abbrevMap.remove(subword); + +// ambMap.put(subword, oldword); +// ambMap.put(subword, word); +// } else if(!wrds.contains(subword)) { +// ambMap.put(subword, word); +// } +// } else { +// seen.add(subword); + +// abbrevMap.put(subword, word); +// } +// } +// } + +// /** +// * Removes words from the abbreviation map. +// * +// * NOTE: There may be inconsistent behavior after removing a word from +// * the map. Use {@link AbbrevMap#recalculate()} to fix it if it occurs. +// * +// * @param words +// * The words to remove. +// */ +// public void removeWords(final String... words) { +// wrds.removeAll(Arrays.asList(words)); + +// for(final String word : words) { +// intRemoveWord(word); +// } +// } + +// /* Actually remove a word. */ +// private void intRemoveWord(final String word) { +// /* Skip blank words. */ +// if(word.equals("")) return; + +// /* Handle each possible abbreviation. */ +// for(int i = word.length(); i > 0; i--) { +// final String subword = word.substring(0, i); + +// if(abbrevMap.containsKey(subword)) { +// abbrevMap.remove(subword); +// } else { +// ambMap.remove(subword, word); + +// final Set<String> possWords = ambMap.get(subword); + +// if(possWords.size() == 0) { +// seen.remove(subword); +// } else if(possWords.size() == 1) { +// /* +// * An abbreviation went from ambiguous +// * to non-ambiguous. +// */ +// final String newWord = possWords.iterator().next(); + +// abbrevMap.put(subword, newWord); +// ambMap.remove(subword, newWord); +// } +// } +// } +// } + +// /** +// * Convert an abbreviation into all the strings it could abbreviate +// * into. +// * +// * @param abbrev +// * The abbreviation to convert. +// * +// * @return All the expansions for the provided abbreviation. +// */ +// public String[] deabbrev(final String abbrev) { +// if(abbrevMap.containsKey(abbrev)) { +// return new String[] { +// abbrevMap.get(abbrev) +// }; +// } + +// return ambMap.get(abbrev).toArray(new String[0]); +// } + +// @Override +// public int hashCode() { +// final int prime = 31; + +// int result = 1; +// result = prime * result + (wrds == null ? 0 : wrds.hashCode()); + +// return result; +// } + +// @Override +// public boolean equals(final Object obj) { +// if(this == obj) return true; +// if(obj == null) return false; +// if(!(obj instanceof AbbrevMap)) return false; + +// final AbbrevMap other = (AbbrevMap) obj; + +// if(wrds == null) { +// if(other.wrds != null) return false; +// } else if(!wrds.equals(other.wrds)) return false; + +// return true; +// } + +// @Override +// public String toString() { +// final String fmt = "AbbrevMap [wrds=%s, abbrevMap=%s, seen=%s, ambMap=%s]"; + +// return String.format(fmt, wrds, abbrevMap, seen, ambMap); +// } +// } diff --git a/src/main/java/bjc/esodata/DefaultList.java b/src/main/java/bjc/esodata/DefaultList.java new file mode 100644 index 0000000..e4b2806 --- /dev/null +++ b/src/main/java/bjc/esodata/DefaultList.java @@ -0,0 +1,116 @@ +package bjc.esodata; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; + +/** + * A list that has a default value that out-of-bounds accesses return. + * + * @author Ben Culkin + * @param <ValueType> + * The type of the values contained in the list. + */ +public class DefaultList<ValueType> extends AbstractList<ValueType> { + /* + * @NOTE 9/17/18 + * + * A possible feature to add would be the ability to set a 'default + * index', so that the default value is 'whatever is at that list index' + * + * Of course, this would cause an exception if that index was out of + * bounds, but what are you going to do? + */ + + private ValueType defVal; + + private List<ValueType> backing; + + /** + * Create a new DefaultList. + */ + public DefaultList() { + this(new ArrayList<>(), null); + } + + /** + * Create a new DefaultList, with a set default value. + * + * @param defVal + * The default value for the list. + */ + public DefaultList(ValueType defVal) { + this(new ArrayList<>(), defVal); + } + + /** + * Create a new DefaultList, with a specific backing list. + * + * @param backer + * The backing list to use. + * + */ + public DefaultList(List<ValueType> backer) { + this(backer, null); + } + + /** + * Create a new DefaultList, with a set default value. + * + * @param backer + * The backing list to use. + * + * @param defVal + * The default value for the list. + */ + public DefaultList(List<ValueType> backer, ValueType defVal) { + this.defVal = defVal; + + this.backing = backer; + } + + /** + * Get the default value. + * + * @return The default value. + */ + public ValueType getDefault() { + return defVal; + } + + /** + * Set the default value. + * + * @param defVal The default value. + */ + public void setDefault(ValueType defVal) { + this.defVal = defVal; + } + + @Override + public ValueType get(int idx) { + if (idx < 0 || idx >= backing.size()) return defVal; + + return backing.get(idx); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public ValueType set(int idx, ValueType val) { + return backing.set(idx, val); + } + + @Override + public void add(int idx, ValueType val) { + backing.add(idx, val); + } + + @Override + public ValueType remove(int idx) { + return backing.remove(idx); + } +} diff --git a/src/main/java/bjc/esodata/Directory.java b/src/main/java/bjc/esodata/Directory.java new file mode 100644 index 0000000..4147e17 --- /dev/null +++ b/src/main/java/bjc/esodata/Directory.java @@ -0,0 +1,107 @@ +package bjc.esodata; + +/** + * Represents a hierarchical map. + * + * What's useful about this is that you can hand sub-directories to people and + * be able to ensure that they can't write outside of it. + * + * @param <K> + * The key type of the map. + * @param <V> + * The value type of the map. + */ +public interface Directory<K, V> { + /** + * Retrieves a given sub-directory. + * + * @param key + * The key to retrieve the sub-directory for. + * + * @return The sub-directory under that name. + * + * @throws IllegalArgumentException + * If the given sub-directory doesn't exist. + */ + Directory<K, V> getSubdirectory(K key); + + /** + * Check if a given sub-directory exists. + * + * @param key + * The key to look for the sub-directory under. + * + * @return Whether or not a sub-directory of that name exists. + */ + boolean hasSubdirectory(K key); + + /** + * Insert a sub-directory into the dictionary. + * + * @param key + * The name of the new sub-directory + * @param value + * The sub-directory to insert + * + * @return The old sub-directory attached to this key, or null if such a + * sub-directory didn't exist + */ + Directory<K, V> putSubdirectory(K key, Directory<K, V> value); + + /** + * Create a new sub-directory. + * + * Will fail if a sub-directory of that name already exists. + * + * @param key + * The name of the new sub-directory. + * + * @return The new sub-directory, or null if one by that name already + * exists. + */ + default Directory<K, V> newSubdirectory(final K key) { + if(hasSubdirectory(key)) return null; + + final Directory<K, V> dir = new SimpleDirectory<>(); + + putSubdirectory(key, dir); + + return dir; + } + + /** + * Check if the directory contains a data-item under the given key. + * + * @param key + * The key to check for. + * + * @return Whether or not there is a data item for the given key. + */ + boolean containsKey(K key); + + /** + * Retrieve a given data-item from the directory. + * + * @param key + * The key to retrieve data for. + * + * @return The value for the given key. + * + * @throws IllegalArgumentException + * If no value exists for the given key. + */ + V getKey(K key); + + /** + * Insert a data-item into the directory. + * + * @param key + * The key to insert into. + * + * @param val + * The value to insert. + * + * @return The old value of key, or null if such a value didn't exist. + */ + V putKey(K key, V val); +} diff --git a/src/main/java/bjc/esodata/DoubleSided.java b/src/main/java/bjc/esodata/DoubleSided.java new file mode 100644 index 0000000..6ecbdcf --- /dev/null +++ b/src/main/java/bjc/esodata/DoubleSided.java @@ -0,0 +1,24 @@ +package bjc.esodata; + +/** + * Interface for a double-sided object. + * + * @author bjculkin + * + */ +public interface DoubleSided { + /** + * Flips the object. + * + * The active side becomes inactive, and the inactive side becomes + * active. + */ + void flip(); + + /** + * Check which side of the object is active; + * + * @return True if the front side is active, false otherwise. + */ + boolean currentSide(); +} diff --git a/src/main/java/bjc/esodata/DoubleTape.java b/src/main/java/bjc/esodata/DoubleTape.java new file mode 100644 index 0000000..c36fcff --- /dev/null +++ b/src/main/java/bjc/esodata/DoubleTape.java @@ -0,0 +1,189 @@ +package bjc.esodata; + +/** + * Double-sided tape is essentially two tapes stuck together with a shared + * cursor. + * + * The main way a double-sided tape differs is that it can be flipped, allowing + * access to another set of data. + * + * However, there is only one cursor, and the position of the cursor on one side + * is the inverse of the position on the other side. + * + * When one side is extended, a null will be inserted into the inactive side + * regardless of the auto-extension policy of the tape. The policy will still be + * respected for the active side. + * + * All operations that refer to the tape refer to the currently active side of + * the tape, except for flip. + * + * Flip refers to the entire tape for 'obvious' reasons. + * + * @param <T> + * The element type of the tape. + * + * @author bjculkin + */ +public class DoubleTape<T> implements Tape<T>, DoubleSided { + private boolean frontActive; + + /* The front-side of the tape. */ + private Tape<T> front; + /* The back-side of the tape. */ + private Tape<T> back; + + /** Create a new empty double-sided tape that doesn't autoextend. */ + public DoubleTape() { + this(false); + } + + /** + * Create a new empty double-sided tape that follows the specified + * auto-extension policy. + * + * @param autoExtnd + * Whether or not to auto-extend the tape to the right w/ nulls. + */ + public DoubleTape(final boolean autoExtnd) { + front = new SingleTape<>(autoExtnd); + back = new SingleTape<>(autoExtnd); + + frontActive = true; + } + + @Override + public T item() { + return front.item(); + } + + @Override + public void item(final T itm) { + front.item(itm); + } + + @Override + public int size() { + return front.size(); + } + + @Override + public int position() { + return front.position(); + } + + @Override + public void insertBefore(final T itm) { + front.insertBefore(itm); + back.insertAfter(null); + } + + @Override + public void insertAfter(final T itm) { + front.insertAfter(itm); + back.insertBefore(itm); + } + + @Override + public T remove() { + back.remove(); + + return front.remove(); + } + + @Override + public void first() { + front.first(); + back.last(); + } + + @Override + public void last() { + front.last(); + back.first(); + } + + @Override + public boolean left() { + return left(1); + } + + @Override + public boolean left(final int amt) { + final boolean succ = front.left(amt); + + if(succ) { + back.right(amt); + } + + return succ; + } + + @Override + public boolean right() { + return right(1); + } + + @Override + public boolean right(final int amt) { + final boolean succ = front.right(amt); + + if(succ) { + back.left(amt); + } + + return succ; + } + + public boolean seekTo(int tgtPos) { + return front.seekTo(tgtPos); + } + + @Override + public void flip() { + final Tape<T> tmp = front; + + front = back; + + back = tmp; + + frontActive = !frontActive; + } + + @Override + public boolean currentSide() { + return frontActive; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (back == null ? 0 : back.hashCode()); + result = prime * result + (front == null ? 0 : front.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof DoubleTape<?>)) return false; + + final DoubleTape<?> other = (DoubleTape<?>) obj; + + if(back == null) { + if(other.back != null) return false; + } else if(!back.equals(other.back)) return false; + + if(front == null) { + if(other.front != null) return false; + } else if(!front.equals(other.front)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("DoubleTape [front=%s, back=%s]", front, back); + } +} diff --git a/src/main/java/bjc/esodata/MapSet.java b/src/main/java/bjc/esodata/MapSet.java new file mode 100644 index 0000000..cbb5d34 --- /dev/null +++ b/src/main/java/bjc/esodata/MapSet.java @@ -0,0 +1,178 @@ +package bjc.esodata; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * A string-keyed set of maps. + * + * @author bjculkin + * + * @param <KeyType> + * The key type of the maps. + * @param <ValueType> + * The value type of the maps. + */ +public class MapSet<KeyType, ValueType> extends AbstractMap<KeyType, ValueType> { + private Map<String, Map<KeyType, ValueType>> backing; + + private Map<KeyType, ValueType> currentMap = null; + + /** + * Create a new set of maps. + */ + public MapSet() { + backing = new HashMap<>(); + } + + /** + * Create a new set of maps, with the specified set of maps. + * + * @param back + * The set of maps to use. + */ + public MapSet(Map<String, Map<KeyType, ValueType>> back) { + backing = back; + } + + /** + * Add a keyed map. + * + * @param key + * The key for the map. + * @param map + * The map itself. + */ + public void addMap(String key, Map<KeyType, ValueType> map) { + backing.put(key, map); + } + + /** + * Clear out the contents of the set + */ + public void clearMap() { + currentMap = null; + + backing.clear(); + } + + /** + * Check if there is a map attached to the specified key. + * + * @param key + * The key to look for. + * @return Whether or not there is anything attached to the key. + */ + public boolean containsMap(String key) { + return backing.containsKey(key); + } + + /** + * Get the map attached to a specified key. + * + * @param key + * The key to look for. + * @return The map attached to the key. + */ + public Map<KeyType, ValueType> getMap(String key) { + return backing.get(key); + } + + /** + * Get all of the backing entries. + * + * @return The backing entries. + */ + public Set<Map.Entry<String, Map<KeyType, ValueType>>> getMapEntries() { + return backing.entrySet(); + } + + /** + * Get all of the keys. + * + * @return The keys currently in use. + */ + public Set<String> getMapKeys() { + return backing.keySet(); + } + + /** + * Get all of the keyed maps. + * + * @return The keyed maps. + */ + public Collection<Map<KeyType, ValueType>> getMapValues() { + return backing.values(); + } + + /** + * Set the current map. + * + * @param key + * The key to use as the current map. + * @return False if there is no map attached to the key, true otherwise. + */ + public boolean setMap(String key) { + if (!backing.containsKey(key)) return false; + + currentMap = backing.get(key); + + return true; + } + + /** + * Sets the current map, or creates a new one if there isn't one + * attached to that key. + * + * @param key + * The key to use as the current map. + */ + public void setCreateMap(String key) { + if (!backing.containsKey(key)) { + currentMap = new HashMap<>(); + + backing.put(key, currentMap); + + return; + } + + currentMap = backing.get(key); + } + + /** + * Set the current map, or bind a map to it. + * + * @param key + * The key to set or bind. + * @param map + * The map to bind to the key if it isn't present. + */ + public void setPutMap(String key, Map<KeyType, ValueType> map) { + if (!backing.containsKey(key)) { + currentMap = map; + + backing.put(key, map); + + return; + } + + currentMap = backing.get(key); + } + + @Override + public Set<Map.Entry<KeyType, ValueType>> entrySet() { + if (currentMap == null) throw new NullPointerException("Current map is not set"); + + return currentMap.entrySet(); + } + + @Override + public ValueType put(KeyType key, ValueType value) { + if (currentMap == null) throw new NullPointerException("Current map is not set"); + + return currentMap.put(key, value); + } +} diff --git a/src/main/java/bjc/esodata/PushdownMap.java b/src/main/java/bjc/esodata/PushdownMap.java new file mode 100644 index 0000000..5db5f05 --- /dev/null +++ b/src/main/java/bjc/esodata/PushdownMap.java @@ -0,0 +1,158 @@ +package bjc.esodata; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +import bjc.funcdata.FunctionalMap; +import bjc.funcdata.IList; +import bjc.funcdata.IMap; + +/** + * A variant of a map where inserting a duplicate key shadows the existing value + * instead of replacing it. + * + * This could be useful for things like variable scopes. + * + * @author EVE + * + * @param <KeyType> + * The key of the map. + * + * @param <ValueType> + * The values in the map. + */ +public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { + /* Our backing storage. */ + private final IMap<KeyType, Stack<ValueType>> backing; + + /** Create a new empty stack-based map. */ + public PushdownMap() { + backing = new FunctionalMap<>(); + } + + /** Create a new empty stack-based map using the specified backing. */ + private PushdownMap(final IMap<KeyType, Stack<ValueType>> back) { + backing = back; + } + + @Override + public void clear() { + backing.clear(); + } + + @Override + public boolean containsKey(final KeyType key) { + return backing.containsKey(key); + } + + @Override + public IMap<KeyType, ValueType> extend() { + return new PushdownMap<>(backing.extend()); + } + + @Override + public void forEach(final BiConsumer<KeyType, ValueType> action) { + backing.forEach((key, stk) -> action.accept(key, stk.top())); + } + + @Override + public void forEachKey(final Consumer<KeyType> action) { + backing.forEachKey(action); + } + + @Override + public void forEachValue(final Consumer<ValueType> action) { + backing.forEachValue(stk -> action.accept(stk.top())); + } + + @Override + public ValueType get(final KeyType key) { + return backing.get(key).top(); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public IList<KeyType> keyList() { + return backing.keyList(); + } + + @Override + public <V2> IMap<KeyType, V2> transform(final Function<ValueType, V2> transformer) { + /* + * @NOTE Can and should we support this? More to the point, + * maybe this should be a map sub-type that does what it needs + * to? + */ + throw new UnsupportedOperationException("Cannot transform pushdown maps."); + } + + @Override + public ValueType put(final KeyType key, final ValueType val) { + if(backing.containsKey(key)) { + final Stack<ValueType> stk = backing.get(key); + + final ValueType vl = stk.top(); + + stk.push(val); + + return vl; + } + + final Stack<ValueType> stk = new SimpleStack<>(); + + stk.push(val); + + return null; + } + + @Override + public ValueType remove(final KeyType key) { + final Stack<ValueType> stk = backing.get(key); + + if(stk.size() > 1) { + return stk.pop(); + } + + return backing.remove(key).top(); + } + + @Override + public IList<ValueType> valueList() { + return backing.valueList().map(Stack::top); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof PushdownMap<?, ?>)) return false; + + final PushdownMap<?, ?> other = (PushdownMap<?, ?>) obj; + + if(backing == null) { + if(other.backing != null) return false; + } else if(!backing.equals(other.backing)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("PushdownMap [backing=%s]", backing); + } +} diff --git a/src/main/java/bjc/esodata/QueueStack.java b/src/main/java/bjc/esodata/QueueStack.java new file mode 100644 index 0000000..49476f0 --- /dev/null +++ b/src/main/java/bjc/esodata/QueueStack.java @@ -0,0 +1,89 @@ +package bjc.esodata; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * A FIFO implementation of a stack. + * + * Basically, a stack that actually acts like a queue. + * + * @param <T> + * The datatype stored in the stack. + * + * @author Ben Culkin + */ +public class QueueStack<T> extends Stack<T> { + /* Our backing queue. */ + private final Deque<T> backing; + + /** Create a new empty stack queue. */ + public QueueStack() { + backing = new LinkedList<>(); + } + + @Override + public void push(final T elm) { + backing.add(elm); + } + + @Override + public T pop() { + if(backing.isEmpty()) throw new StackUnderflowException(); + + return backing.remove(); + } + + @Override + public T top() { + if(backing.isEmpty()) throw new StackUnderflowException(); + + return backing.peek(); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public boolean empty() { + return backing.size() == 0; + } + + @SuppressWarnings("unchecked") + @Override + public T[] toArray() { + return (T[]) backing.toArray(); + } + + @Override + public String toString() { + return String.format("QueueStack [backing=%s]", backing); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof QueueStack<?>)) return false; + + final QueueStack<?> other = (QueueStack<?>) obj; + + if(backing == null) { + if(other.backing != null) return false; + } else if(!backing.equals(other.backing)) return false; + + return true; + } +} diff --git a/src/main/java/bjc/esodata/SimpleDirectory.java b/src/main/java/bjc/esodata/SimpleDirectory.java new file mode 100644 index 0000000..8ac19cf --- /dev/null +++ b/src/main/java/bjc/esodata/SimpleDirectory.java @@ -0,0 +1,95 @@ +package bjc.esodata; + +import bjc.funcdata.FunctionalMap; +import bjc.funcdata.IMap; + +/** + * Simple implementation of {@link Directory}. + * + * Has a split namespace for data and children. + * + * @author EVE + * + * @param <K> + * The key type of the directory. + * + * @param <V> + * The value type of the directory. + */ +public class SimpleDirectory<K, V> implements Directory<K, V> { + /* Our sub-directories. */ + private final IMap<K, Directory<K, V>> children; + /* Our data. */ + private final IMap<K, V> data; + + /** Create a new directory. */ + public SimpleDirectory() { + children = new FunctionalMap<>(); + data = new FunctionalMap<>(); + } + + @Override + public Directory<K, V> getSubdirectory(final K key) { + return children.get(key); + } + + @Override + public boolean hasSubdirectory(final K key) { + return children.containsKey(key); + } + + @Override + public Directory<K, V> putSubdirectory(final K key, final Directory<K, V> val) { + return children.put(key, val); + } + + @Override + public boolean containsKey(final K key) { + return data.containsKey(key); + } + + @Override + public V getKey(final K key) { + return data.get(key); + } + + @Override + public V putKey(final K key, final V val) { + return data.put(key, val); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (children == null ? 0 : children.hashCode()); + result = prime * result + (data == null ? 0 : data.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof SimpleDirectory<?, ?>)) return false; + + final SimpleDirectory<?, ?> other = (SimpleDirectory<?, ?>) obj; + + if(children == null) { + if(other.children != null) return false; + } else if(!children.equals(other.children)) return false; + + if(data == null) { + if(other.data != null) return false; + } else if(!data.equals(other.data)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SimpleDirectory [children=%s, data=%s]", children, data); + } +} diff --git a/src/main/java/bjc/esodata/SimpleStack.java b/src/main/java/bjc/esodata/SimpleStack.java new file mode 100644 index 0000000..41b217d --- /dev/null +++ b/src/main/java/bjc/esodata/SimpleStack.java @@ -0,0 +1,87 @@ +package bjc.esodata; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * Simple implementation of a stack. + * + * @param <T> + * The datatype stored in the stack. + * + * @author Ben Culkin + */ +public class SimpleStack<T> extends Stack<T> { + /* Our backing stack. */ + private final Deque<T> backing; + + /** Create a new empty stack. */ + public SimpleStack() { + backing = new LinkedList<>(); + } + + @Override + public void push(final T elm) { + backing.push(elm); + } + + @Override + public T pop() { + if(backing.isEmpty()) throw new StackUnderflowException(); + + return backing.pop(); + } + + @Override + public T top() { + if(backing.isEmpty()) throw new StackUnderflowException(); + + return backing.peek(); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public boolean empty() { + return backing.size() == 0; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray() { + return (T[]) backing.toArray(); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof SimpleStack<?>)) return false; + + final SimpleStack<?> other = (SimpleStack<?>) obj; + + if(backing == null) { + if(other.backing != null) return false; + } else if(!backing.equals(other.backing)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SimpleStack [backing=%s]", backing); + } +} diff --git a/src/main/java/bjc/esodata/SingleTape.java b/src/main/java/bjc/esodata/SingleTape.java new file mode 100644 index 0000000..28dca2a --- /dev/null +++ b/src/main/java/bjc/esodata/SingleTape.java @@ -0,0 +1,212 @@ +package bjc.esodata; + +import java.util.ArrayList; + +/** + * A tape is a one-dimensional array that can only be accessed in one position + * at a time. + * + * A tape is essentially a 1D array with a cursor attached to it, and you can + * only affect elements at that cursor. The size of the array is theoretically + * unbounded to the right, but in practice bounded by available memory. + * + * You can choose whether or not you want the tape to automatically extend + * itself to the right with null elements by specifying its auto-extension + * policy. + * + * @param <T> + * The element type of the tape. + * + * @author bjculkin + */ +public class SingleTape<T> implements Tape<T> { + /* Our backing store. */ + protected ArrayList<T> backing; + /* Our position in the list. */ + protected int pos; + /* Whether to auto-extend the list on the left with nulls. */ + protected boolean autoExtend; + + /** + * Create a new tape with the specified contents that doesn't + * autoextend. + * + * @param vals + * The values to put on the tape. + */ + @SafeVarargs + public SingleTape(T... vals) { + autoExtend = false; + + backing = new ArrayList<>(vals.length); + + for(T val : vals) { + backing.add(val); + } + } + + /** Create a new empty tape that doesn't autoextend. */ + public SingleTape() { + this(false); + } + + /** + * Create a new tape with values taken from an iterable. + * + * @param itr + * The iterable to get values from. + */ + public SingleTape(Iterable<T> itr) { + this(false); + + for(T itm : itr) { + backing.add(itm); + } + } + + /** + * Create a new empty tape that follows the specified auto-extension + * policy. + * + * @param autoExtnd + * Whether or not to auto-extend the tape to the right w/ nulls. + */ + public SingleTape(final boolean autoExtnd) { + autoExtend = autoExtnd; + + backing = new ArrayList<>(); + } + + @Override + public T item() { + if (pos < 0 || pos >= backing.size()) return null; + + return backing.get(pos); + } + + @Override + public void item(final T itm) { + backing.set(pos, itm); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public int position() { + return pos; + } + + @Override + public void insertBefore(final T itm) { + backing.add(pos, itm); + } + + @Override + public void insertAfter(final T itm) { + if(pos == backing.size() - 1) { + backing.add(itm); + } else { + backing.add(pos + 1, itm); + } + } + + @Override + public T remove() { + final T res = backing.remove(pos); + if(pos != 0) { + pos -= 1; + } + return res; + } + + @Override + public void first() { + pos = 0; + } + + @Override + public void last() { + pos = backing.size() - 1; + } + + @Override + public boolean left() { + return left(1); + } + + @Override + public boolean left(final int amt) { + if(pos - amt < 0) return false; + + pos -= amt; + return true; + } + + @Override + public boolean right() { + return right(1); + } + + @Override + public boolean right(final int amt) { + if(pos + amt > backing.size()) { + if(autoExtend) { + while(pos + amt >= backing.size() - 1) { + backing.add(null); + } + } else return false; + } + + pos += amt; + return true; + } + + public boolean seekTo(int tgtPos) { + if(tgtPos < 0) + return false; + + if(tgtPos >= backing.size() - 1) + if(autoExtend) + while(tgtPos >= backing.size() - 1) + backing.add(null); + else + return false; + + pos = tgtPos; + + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof SingleTape<?>)) return false; + + final SingleTape<?> other = (SingleTape<?>) obj; + + if(backing == null) { + if(other.backing != null) return false; + } else if(!backing.equals(other.backing)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SingleTape [backing=%s, pos=%s, autoExtend=%s]", backing, pos, autoExtend); + } +} diff --git a/src/main/java/bjc/esodata/SpaghettiStack.java b/src/main/java/bjc/esodata/SpaghettiStack.java new file mode 100644 index 0000000..9280c28 --- /dev/null +++ b/src/main/java/bjc/esodata/SpaghettiStack.java @@ -0,0 +1,102 @@ +package bjc.esodata; + +import java.util.Arrays; +import java.util.stream.Stream; + +/** + * Implements a spaghetti stack, which is a stack that is branched off of a + * parent stack. + * + * @param <T> + * The datatype stored in the stack. + * + * @author Ben Culkin + */ +class SpaghettiStack<T> extends Stack<T> { + /* Our backing stack. */ + private final Stack<T> backing; + /* The stack we branched off of. */ + private final Stack<T> parent; + + /** + * Create a new empty spaghetti stack, off of the specified parent. + * + * @param par + * The parent stack + */ + public SpaghettiStack(final Stack<T> par) { + backing = new SimpleStack<>(); + + parent = par; + } + + @Override + public void push(final T elm) { + backing.push(elm); + } + + @Override + public T pop() { + if(backing.empty()) return parent.pop(); + + return backing.pop(); + } + + @Override + public T top() { + if(backing.empty()) return parent.top(); + + return backing.top(); + } + + @Override + public int size() { + return parent.size() + backing.size(); + } + + @Override + public boolean empty() { + return backing.empty() && parent.empty(); + } + + @SuppressWarnings("unchecked") + @Override + public T[] toArray() { + return (T[]) Stream.concat(Arrays.stream(parent.toArray()), Arrays.stream(backing.toArray())).toArray(); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + result = prime * result + (parent == null ? 0 : parent.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof SpaghettiStack<?>)) return false; + + final SpaghettiStack<?> other = (SpaghettiStack<?>) obj; + + if(backing == null) { + if(other.backing != null) return false; + } else if(!backing.equals(other.backing)) return false; + + if(parent == null) { + if(other.parent != null) return false; + } else if(!parent.equals(other.parent)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SpaghettiStack [backing=%s, parent=%s]", backing, parent); + } +} diff --git a/src/main/java/bjc/esodata/Stack.java b/src/main/java/bjc/esodata/Stack.java new file mode 100644 index 0000000..13480a3 --- /dev/null +++ b/src/main/java/bjc/esodata/Stack.java @@ -0,0 +1,456 @@ +package bjc.esodata; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/* + * @TODO 10/11/17 Ben Culkin :StackCombinators + * Implement more combinators for the stack. + */ +/** + * A stack, with support for forth/factor style stack combinators. + * + * <p> + * <h2>Stack underflow</h2> + * <p> + * NOTE: In general, using any operation that attempts to remove more data from + * the stack than exists will cause a {@link StackUnderflowException} to be + * thrown. Check the size of the stack if you want to avoid this. + * <p> + * </p> + * + * @param <T> + * The datatype stored in the stack. + * + * @author Ben Culkin + */ +public abstract class Stack<T> { + /** + * The exception thrown when attempting to access an element from the + * stack that isn't there. + * + * @author EVE + */ + public static class StackUnderflowException extends RuntimeException { + /* The ID of the exception */ + private static final long serialVersionUID = 1423867176204571539L; + } + + /** + * Push an element onto the stack. + * + * @param elm + * The element to insert. + */ + public abstract void push(T elm); + + /** + * Pop an element off of the stack. + * + * @return The element on top of the stack. + */ + public abstract T pop(); + + /** + * Retrieve the top element of this stack without removing it from the + * stack. + * + * @return The top element of this stack. + */ + public abstract T top(); + + /** + * Get the number of elements in the stack. + * + * @return the number of elements in the stack. + */ + public abstract int size(); + + /** + * Check if the stack is empty. + * + * @return Whether or not the stack is empty. + */ + public abstract boolean empty(); + + /** + * Create a spaghetti stack branching off of this one. + * + * @return A spaghetti stack with this stack as a parent. + */ + public Stack<T> spaghettify() { + return new SpaghettiStack<>(this); + } + + /* + * Basic combinators + */ + + /** + * Drop n items from the stack. + * + * @param n + * The number of items to drop. + */ + public void drop(final int n) { + for(int i = 0; i < n; i++) { + pop(); + } + } + + /** Drop one item from the stack. */ + public void drop() { + drop(1); + } + + /** + * Delete n items below the current one. + * + * @param n + * The number of items below the top to delete. + */ + public void nip(final int n) { + final T elm = pop(); + + drop(n); + + push(elm); + } + + /** Delete the second element in the stack. */ + public void nip() { + nip(1); + } + + /** + * Replicate the top n items of the stack m times. + * + * @param n + * The number of items to duplicate. + * + * @param m + * The number of times to duplicate items. + */ + public void multidup(final int n, final int m) { + final List<T> lst = new ArrayList<>(n); + + for(int i = n; i > 0; i--) { + lst.set(i - 1, pop()); + } + + for(int i = 0; i < m; i++) { + for(final T elm : lst) { + push(elm); + } + } + } + + /** + * Duplicate the top n items of the stack. + * + * @param n + * The number of items to duplicate. + */ + public void dup(final int n) { + multidup(n, 2); + } + + /** Duplicate the top item on the stack. */ + public void dup() { + dup(1); + } + + /** + * Replicate the n elements below the top one m times. + * + * @param n + * The number of items to duplicate. + * + * @param m + * The number of times to duplicate items. + */ + public void multiover(final int n, final int m) { + final List<T> lst = new ArrayList<>(n); + + final T elm = pop(); + + for(int i = n; i > 0; i--) { + lst.set(i - 1, pop()); + } + + for(final T nelm : lst) { + push(nelm); + } + push(elm); + + for(int i = 1; i < m; i++) { + for(final T nelm : lst) { + push(nelm); + } + } + } + + /** + * Duplicate the n elements below the top one. + * + * @param n + * The number of items to duplicate. + */ + public void over(final int n) { + multiover(n, 2); + } + + /** Duplicate the second item in the stack. */ + public void over() { + over(1); + } + + /** Duplicate the third item in the stack. */ + public void pick() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(x); + push(y); + push(z); + push(x); + } + + /** Swap the top two items on the stack. */ + public void swap() { + final T y = pop(); + final T x = pop(); + + push(y); + push(x); + } + + /** Duplicate the second item below the first item. */ + public void deepdup() { + final T y = pop(); + final T x = pop(); + + push(x); + push(x); + push(y); + } + + /** Swap the second and third items in the stack. */ + public void deepswap() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(y); + push(x); + push(z); + } + + /** Rotate the top three items on the stack */ + public void rot() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(y); + push(z); + push(x); + } + + /** Inversely rotate the top three items on the stack */ + public void invrot() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(z); + push(x); + push(y); + } + + /* + * :StackCombinators Add a general rotate/roll operator. + */ + + /* + * Dataflow Combinators + */ + + /** + * Hides the top n elements on the stack from an action. + * + * @param n + * The number of elements to hide. + * + * @param action + * The action to hide the elements from + */ + public void dip(final int n, final Consumer<Stack<T>> action) { + final List<T> elms = new ArrayList<>(n); + + for(int i = n; i > 0; i--) { + elms.set(i - 1, pop()); + } + + action.accept(this); + + for(final T elm : elms) { + push(elm); + } + } + + /** + * Hide the top element of the stack from an action. + * + * @param action + * The action to hide the top from + */ + public void dip(final Consumer<Stack<T>> action) { + dip(1, action); + } + + /** + * Copy the top n elements on the stack, replacing them once an action + * is done. + * + * @param n + * The number of elements to copy. + * + * @param action + * The action to execute. + */ + public void keep(final int n, final Consumer<Stack<T>> action) { + /* + * @NOTE Is this correct? + */ + dup(n); + dip(n, action); + } + + /** + * Apply all the actions in a list to the top n elements of the stack. + * + * @param n + * The number of elements to give to cons. + * + * @param actions + * The actions to execute. + */ + public void multicleave(final int n, final List<Consumer<Stack<T>>> actions) { + final List<T> elms = new ArrayList<>(n); + + for(int i = n; i > 0; i--) { + elms.set(i - 1, pop()); + } + + for(final Consumer<Stack<T>> action : actions) { + for(final T elm : elms) { + push(elm); + } + + action.accept(this); + } + } + + /** + * Apply all the actions in a list to the top element of the stack. + * + * @param actions + * The actions to execute. + */ + public void cleave(final List<Consumer<Stack<T>>> actions) { + multicleave(1, actions); + } + + /** + * Apply every action in a list of actions to n arguments. + * + * @param n + * The number of parameters each action takes. + * + * @param actions + * The actions to execute. + */ + public void multispread(final int n, final List<Consumer<Stack<T>>> actions) { + final List<List<T>> nelms = new ArrayList<>(actions.size()); + + for(int i = actions.size(); i > 0; i--) { + final List<T> elms = new ArrayList<>(n); + + for(int j = n; j > 0; j--) { + elms.set(j, pop()); + } + + nelms.set(i, elms); + } + + int i = 0; + for(final List<T> elms : nelms) { + for(final T elm : elms) { + push(elm); + } + + actions.get(i).accept(this); + i += 1; + } + } + + /** + * Apply the actions in a list of actions to corresponding elements from + * the stack. + * + * @param conses + * The actions to execute. + */ + public void spread(final List<Consumer<Stack<T>>> conses) { + multispread(1, conses); + } + + /** + * Apply an action to the first m groups of n arguments. + * + * @param n + * The number of arguments cons takes. + * + * @param m + * The number of time to call cons. + * + * @param action + * The action to execute. + */ + public void multiapply(final int n, final int m, final Consumer<Stack<T>> action) { + final List<Consumer<Stack<T>>> actions = new ArrayList<>(m); + + for(int i = 0; i < m; i++) { + actions.add(action); + } + + multispread(n, actions); + } + + /** + * Apply an action n times to the corresponding elements in the stack. + * + * @param n + * The number of times to execute cons. + * + * @param action + * The action to execute. + */ + public void apply(final int n, final Consumer<Stack<T>> action) { + multiapply(1, n, action); + } + + /* + * Misc. functions + */ + + /** + * Get an array representing this stack. + * + * @return The stack as an array. + */ + public abstract T[] toArray(); +} diff --git a/src/main/java/bjc/esodata/Tape.java b/src/main/java/bjc/esodata/Tape.java new file mode 100644 index 0000000..080e216 --- /dev/null +++ b/src/main/java/bjc/esodata/Tape.java @@ -0,0 +1,135 @@ +package bjc.esodata; + +/** + * Interface for something that acts like a tape. + * + * A tape is essentially a 1D array with a cursor attached to it, and you can + * only affect elements at that cursor. The size of the array is theoretically + * unbounded to the right, but in practice bounded by available memory. + * + * @param <T> + * The element type of the tape. + * + * @author bjculkin + */ +public interface Tape<T> { + /** + * Get the item the tape is currently on. + * + * @return The item the tape is on. + */ + T item(); + + /** + * Set the item the tape is currently on. + * + * @param itm + * The new value for the tape item. + */ + void item(T itm); + + /** + * Get the current number of elements in the tape. + * + * @return The current number of elements in the tape. + */ + int size(); + + /** + * Get the position of the current item. + * + * @return The position of the current item. + */ + int position(); + + /** + * Insert an element before the current item. + * + * @param itm + * The item to add. + */ + void insertBefore(T itm); + + /** + * Insert an element after the current item. + * + * @param itm + * The item to insert. + */ + void insertAfter(T itm); + + /** + * Remove the current element. + * + * Also moves the cursor back one step if possible to maintain relative + * position. + * + * @return The removed item. + */ + T remove(); + + /** Move the cursor to the left-most position. */ + void first(); + + /** Move the cursor to the right-most position. */ + void last(); + + /** + * Move the cursor one space left. + * + * The cursor can't go past zero. + * + * @return True if the cursor was moved left. + */ + boolean left(); + + /** + * Move the cursor the specified amount left. + * + * The cursor can't go past zero. Attempts to move the cursor by amounts + * that would exceed zero don't move the cursor at all. + * + * @param amt + * The amount to attempt to move the cursor left. + * + * @return True if the cursor was moved left. + */ + boolean left(int amt); + + /** + * Move the cursor one space right. + * + * @return Whether the cursor was moved right. + */ + boolean right(); + + /** + * Move the cursor the specified amount right. + * + * @param amt + * The amount to move the cursor right by. + * + * @return Whether the cursor was moved right. + */ + boolean right(int amt); + + /** + * Seek to an absolute position on the tape. + * + * @param pos + * The position to seek to. + * @return Whether or not the tape successfully seeked to that position. + */ + boolean seekTo(int pos); + + /** + * Check if this tape is at its end. + * + * Equivalent to checking if position() == size(). + * + * @return Whether or not the tape is at its end. + */ + default boolean atEnd() { + return position() == size(); + } +} diff --git a/src/main/java/bjc/esodata/ThresholdSet.java b/src/main/java/bjc/esodata/ThresholdSet.java new file mode 100644 index 0000000..b6f677e --- /dev/null +++ b/src/main/java/bjc/esodata/ThresholdSet.java @@ -0,0 +1,238 @@ +package bjc.esodata; + +import java.util.*; + +/** + * Represents a counted set, that overflows to a map. + * + * More specifically, this is a set/map combo type. + * + * Initially, when you add an item, it will go into the set. Attempting to add a duplicate item to + * that set will cause the entry to be removed from the set, and added to the map, which will count + * the number of times that particular item has been added to the set. If you remove enough copies + * of that item to put it back down to 1 copy, that copy will be removed from the map, and readded + * to the set. + * + * The iterator that this type gives by default is an iterator over all of the values in the set, + * not including any of those in the map. + * + * @param KeyType The value being counted. + * + * @author Ben Culkin + */ +public class ThresholdSet<KeyType> { + // View of this class as a java.util.Set + private class SetView extends AbstractSet<KeyType> { + /* + * This is technically not a valid implementation of add, because it does not guarantee that + * the set will contain key after it returns (as a matter of fact, attempting to add the + * component might actually cause it to be removed from the collection). + */ + public boolean add(KeyType key) { + // Qualified-this; allows us to reference the 'this' of our enclosing type. + int ret = ThresholdSet.this.add(key); + + // No change to set contents + if (ret > 2) return false; + + return true; + } + + public boolean remove(Object o) { + // Will throw a ClassCastException if you give us something bad. + KeyType k = (KeyType)o; + + int ret = ThresholdSet.this.remove(k); + + // We removed the element. + if (ret == 0) return true; + + return false; + } + + public boolean contains(Object o) { + // Will throw a ClassCastException if you give us something bad. + KeyType k = (KeyType)o; + + int ret = ThresholdSet.this.remove(k); + + // The object is set-visible + if (ret == 1) return true; + + return false; + } + + public int size() { + return ThresholdSet.this.setSize(); + } + + public Iterator<KeyType> iterator() { + return ThresholdSet.this.setIterator(); + } + } + + private Set<KeyType> keySet; + // @TODO :CountMap Ben Culkin 6/19/2019 + // Replace this with a CountSet or some equivalent concept, whenever that gets written + private Map<KeyType, Integer> keyMap; + + /** + * Create a new empty threshold set. + */ + public ThresholdSet() { + keySet = new HashSet<>(); + keyMap = new HashMap<>(); + } + + /** + * Add multiple keys at once to the map. + * + * @param keys + * The keys to add. + * @return An array containing the results of adding the keys. + */ + public int[] addAll(KeyType... keys) { + int[] ret = new int[keys.length]; + + for (int i = 0; i < keys.length; i++) { + ret[i] = add(keys[i]); + } + + return ret; + } + + /** + * Add a key to the collection. + * + * @param key + * The key to add to the collection. + * @return The number of times that key now exists in the collection. Should always be < 0. + */ + public int add(KeyType key) { + if (keySet.contains(key)) { + // Promote to counted item + keySet.remove(key); + + keyMap.put(key, 2); + + return 2; + } else if (keyMap.containsKey(key)) { + // Increment count + int cnt = keyMap.get(key) + 1; + + keyMap.put(key, cnt); + + return cnt; + } else { + // New key + keySet.add(key); + + return 1; + } + } + + /** + * Remove a bunch of keys from the collection. + * + * @param keys + * The keys to remove from the collection. + * + * @return The results from removing the keys. + */ + public int[] removeAll(KeyType... keys) { + int[] ret = new int[keys.length]; + + for (int i = 0; i < keys.length; i++) { + ret[i] = remove(keys[i]); + } + + return ret; + } + + /** + * Remove a key from the collection. + * + * @param key + * The key to remove from the collection. + * + * @return The number of times that key now exists in the collection. Returns -1 if that key + * wasn't in the collection beforehand. + */ + public int remove(KeyType key) { + if (keySet.contains(key)) { + // No more occurances + keySet.remove(key); + + return 0; + } else if (keyMap.containsKey(key)) { + // Decrement count + int cnt = keyMap.get(key) - 1; + + if (cnt == 1) { + // Move key to set + keyMap.remove(key); + + keySet.add(key); + + return 1; + } else { + keyMap.put(key, cnt); + + return cnt; + } + } else { + // We don't know about that key + return -1; + } + } + + /** + * Get the number of times the set contains a set of given keys. + * + * @param key + * The keys to look for. + * + * @return The containment counts for each key. + */ + public int[] containsAll(KeyType... keys) { + int[] ret = new int[keys.length]; + + for (int i = 0; i < keys.length; i++) { + ret[i] = contains(keys[i]); + } + + return ret; + } + + /** + * Get the number of times the set contains a given key. + * + * @param key + * The key to look for. + * + * @return The number of times the key occurs; -1 if it doesn't occur. + */ + public int contains(KeyType key) { + if (keySet.contains(key)) return 1; + if (!keyMap.containsKey(key)) return -1; + + return keyMap.get(key); + } + + /** + * Get a view of this collection as a java.util.Set. + * + * @return A view of the collection as a set. + */ + public Set<KeyType> setView() { + return new SetView(); + } + + int setSize() { + return keySet.size(); + } + + Iterator<KeyType> setIterator() { + return keySet.iterator(); + } +} diff --git a/src/main/java/bjc/esodata/UnifiedDirectory.java b/src/main/java/bjc/esodata/UnifiedDirectory.java new file mode 100644 index 0000000..dec940f --- /dev/null +++ b/src/main/java/bjc/esodata/UnifiedDirectory.java @@ -0,0 +1,105 @@ +package bjc.esodata; + +import bjc.funcdata.FunctionalMap; +import bjc.funcdata.IMap; + +/** + * Simple implementation of {@link Directory}. + * + * Has a unified namespace for data and children. + * + * @author EVE + * + * @param <K> + * The key type of the directory. + * + * @param <V> + * The value type of the directory. + */ +public class UnifiedDirectory<K, V> implements Directory<K, V> { + /* Our directory children. */ + private final IMap<K, Directory<K, V>> children; + /* Our data children. */ + private final IMap<K, V> data; + + /** Create a new directory. */ + public UnifiedDirectory() { + children = new FunctionalMap<>(); + data = new FunctionalMap<>(); + } + + @Override + public Directory<K, V> getSubdirectory(final K key) { + return children.get(key); + } + + @Override + public boolean hasSubdirectory(final K key) { + return children.containsKey(key); + } + + @Override + public Directory<K, V> putSubdirectory(final K key, final Directory<K, V> val) { + if(data.containsKey(key)) { + final String msg = String.format("Key %s is already used for data", key); + + throw new IllegalArgumentException(msg); + } + + return children.put(key, val); + } + + @Override + public boolean containsKey(final K key) { + return data.containsKey(key); + } + + @Override + public V getKey(final K key) { + return data.get(key); + } + + @Override + public V putKey(final K key, final V val) { + if(children.containsKey(key)) { + final String msg = String.format("Key %s is already used for sub-directories.", key); + + throw new IllegalArgumentException(msg); + } + + return data.put(key, val); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (children == null ? 0 : children.hashCode()); + result = prime * result + (data == null ? 0 : data.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof UnifiedDirectory<?, ?>)) return false; + + final UnifiedDirectory<?, ?> other = (UnifiedDirectory<?, ?>) obj; + + if(children == null) { + if(other.children != null) return false; + } else if(!children.equals(other.children)) return false; + + if(data == null) { + if(other.data != null) return false; + } else if(!data.equals(other.data)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("UnifiedDirectory [children=%s, data=%s]", children, data); + } +} diff --git a/src/main/java/bjc/esodata/todos.txt b/src/main/java/bjc/esodata/todos.txt new file mode 100644 index 0000000..28af9b7 --- /dev/null +++ b/src/main/java/bjc/esodata/todos.txt @@ -0,0 +1,3 @@ +@TODO 10/11/17 Ben Culkin :CursorHands + Add cursored list/tree data structures with the ability to pick/put the + current node. |
