com.github.krukow.clj_lang.PersistentHATTrie Maven / Gradle / Ivy
/**
* Copyright (c) Karl Krukow. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package com.github.krukow.clj_lang;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
/*
A persistent rendition of Nikolas Askitis' HAT Trie
Uses path copying for persistence
Any errors are my own
*/
@SuppressWarnings({"rawtypes","unchecked"})
public class PersistentHATTrie extends APersistentTrie implements IObj {
private static final long serialVersionUID = -7068824281866890730L;
final IPersistentMap meta;
final HATTrieNode root;
final int count;
public static final PersistentHATTrie EMPTY = new PersistentHATTrie(null, null, 0);
public PersistentHATTrie(HATTrieNode root, IPersistentMap meta, int count) {
this.root = root;
this.meta = meta;
this.count = count;
}
public IPersistentMap meta() {
return meta;
}
private static interface HATTrieNode {
HATTrieNode add(String s, int i, T t);
T get(String s, int j);
Iterator> nodeIt(String prefix);
}
private static interface ToStringWithPrefix {
String toStringWithPrefix(String prefix);
}
private static final class AccessNode implements HATTrieNode,ToStringWithPrefix {
private final HATTrieNode children[];
private final T emptyPtr;
public AccessNode(HATTrieNode[] children, T emptyPtr) {
this.children = children;
this.emptyPtr = emptyPtr;
}
public String toString() {
return toStringWithPrefix("");
}
public String toStringWithPrefix(String prefix) {
StringBuilder sb = new StringBuilder();
String nestedPrefix = prefix+" ";
sb.append(prefix);
sb.append("(access-node\n").append(nestedPrefix);
for (int i=0;i ").append(((ToStringWithPrefix) node)
.toStringWithPrefix(nestedPrefix)).append(";\n").append(nestedPrefix);
}
}
if (emptyPtr != null) {
sb.append("\n").append(prefix).append("**");
}
sb.append(prefix).append(")");
return sb.toString();
}
public HATTrieNode add(String s, int i, T t) {
int length = s.length();
if (i < length) {
char ichar = s.charAt(i);
HATTrieNode hatTrieNode = children[ichar];
if (hatTrieNode != null) {
HATTrieNode newNode = hatTrieNode.add(s, i+1, t);
if (newNode == hatTrieNode) {
return this;
}
HATTrieNode[] newArr = new HATTrieNode[children.length];
System.arraycopy(children, 0, newArr, 0, children.length);
newArr[ichar] = newNode;
return new AccessNode(newArr, emptyPtr);
}
ContainerNode c = new ContainerNode(PersistentTreeMap.EMPTY.assoc(s.substring(i+1), t));
HATTrieNode[] newArr = new HATTrieNode[children.length];
System.arraycopy(children, 0, newArr, 0, children.length);
newArr[ichar] = c;
return new AccessNode(newArr, emptyPtr);
}
if (i == length && emptyPtr == null) {
return new AccessNode(children, s);
}
return this;
}
public T get(String s, int i) {
if (i == s.length()) {
return emptyPtr;
}
HATTrieNode c = children[s.charAt(i)];
if (c == null) {
return null;
}
return c.get(s, i+1);
}
private static final class AccessNodeIterator implements Iterator> {
private final HATTrieNode children[];
private final T emptyPtr;
private int index = -1;
private final String prefix;
Iterator> current = null;
AccessNodeIterator(AccessNode node, String prefix) {
children = node.children;
emptyPtr = node.emptyPtr;
this.prefix = prefix;
moveCurIfNeeded();
}
private void moveCurIfNeeded() {
if (index == -1){
if (emptyPtr == null) {
index = 0;
} else {
return;
}
}
if (current != null && current.hasNext()) return;
while (index < children.length && children[index] == null) {index += 1;};
if (index == children.length) { current = null;}
else {
String prefix = new StringBuilder(this.prefix).append((char) index).toString();
current = children[index++].nodeIt(prefix);
}
}
public boolean hasNext() {
if (index == -1 && emptyPtr != null) {
return true;
}
while (current != null && !current.hasNext()) {
moveCurIfNeeded();
}
return current != null && current.hasNext();
}
@Override
public MapEntry next() {
if (index == -1 && emptyPtr != null) {
index = 0;
moveCurIfNeeded();
return new MapEntry(prefix, emptyPtr);
}
return current.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
@Override
public Iterator> nodeIt(String prefix) {
return new AccessNodeIterator(this,prefix);
}
}
private static final class ContainerNode implements HATTrieNode,ToStringWithPrefix {
private PersistentTreeMap strings;
public ContainerNode(PersistentTreeMap strings) {
this.strings = strings;
}
public String toString() {
return strings.toString();
}
public HATTrieNode add(String s, int i, T t) {
String ss = s.substring(i);
T et = strings.get(ss);
if (Util.equiv(et, t)) {
return this;
}
if (shouldBurst()) {
return burst(s,i,t);
}
return new ContainerNode(this.strings.assoc(s.substring(i),t));
}
public T get(String s, int i) {
return strings.get(s.substring(i));
}
private HATTrieNode burst(String s, int i, T t) {
HATTrieNode[] children = new HATTrieNode[256];
T empty = s.length() == i ? t : null;
for (Iterator> iterator = strings.iterator(); iterator.hasNext();) {
Entry next = iterator.next();
String old = next.getKey();
T value = next.getValue();
if (empty == null && "".equals(old)) {//can only happen once
empty = value;
} else {
char f = old.charAt(0);
children[f] = addToNode(children[f],old,value,1);
}
}
if (empty != t) {//i < s.length()
char f = s.charAt(i);
children[f] = addToNode(children[f],s,t,i+1);
}
return new AccessNode(children, empty);
}
private static final HATTrieNode addToNode(HATTrieNode hatTrieNode, String s, T v, int i) {
if (hatTrieNode == null) {
return new ContainerNode(PersistentTreeMap.EMPTY.assoc(s.substring(i),v));
} else {
return hatTrieNode.add(s, i,v);
}
}
private boolean shouldBurst() {
return strings.count() == 4;
}
@Override
public Iterator> nodeIt(final String prefix) {
return new Iterator>() {
Iterator> it = strings.iterator();
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Map.Entry next() {
Entry next = it.next();
return new MapEntry(prefix+next.getKey(), next.getValue());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public String toStringWithPrefix(String prefix) {
return prefix+toString();
}
}
@Override
public T getMember(String s) {
if (root == null || s == null) return null;
return root.get(s,0);
}
@Override
public IPersistentTrie addMember(String s, T t) {
if (root == null) {
return new PersistentHATTrie(new ContainerNode(PersistentTreeMap.EMPTY.assoc(s, t)),null,1);
}
HATTrieNode newRoot = root.add(s, 0,t);
if (root == newRoot) {
return this;
}
return new PersistentHATTrie(newRoot,meta,count+1);
}
@Override
public IPersistentSet disjoin(Object key) {
throw new UnsupportedOperationException();
}
@Override
public boolean contains(Object key) {
return (key instanceof String) && getMember((String) key) != null;
}
@Override
public Boolean get(Object key) {
return contains(key);
}
@Override
public int count() {
return count;
}
@Override
public IPersistentCollection cons(Object o) {
if (!(o instanceof Map.Entry)) {
throw new IllegalArgumentException("Only adding strings is supported");
}
Map.Entry e = (Entry) o;
return (IPersistentCollection) this.addMember(e.getKey(),e.getValue());
}
@Override
public IPersistentCollection empty() {
return EMPTY;
}
@Override
public Iterator> iterator() {
return root != null ? root.nodeIt("") : new EmptyIterator();
}
@Override
public ISeq seq() {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
@Override
public boolean removeAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public IObj withMeta(IPersistentMap meta) {
return new PersistentHATTrie(root,meta,count);
}
@Override
public String toString() {
if (root == null) {return "{}";}
return root.toString();
}
@Override
public boolean add(Entry e) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection extends Entry> c) {
throw new UnsupportedOperationException();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy