use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method removeHavingAcquiredWriteLock.
public boolean removeHavingAcquiredWriteLock(SearchResult searchResult, boolean recurse) {
SearchResult.Classification classification = searchResult.classification;
switch(classification) {
case EXACT_MATCH:
Node found = searchResult.found;
Node parent = searchResult.parentNode;
Object v = found.getValue();
if (!recurse && ((v == null) || (v == VoidValue.SINGLETON))) {
// No need to remove it...
return false;
}
List<X> reinsertions = new FasterList<>(0);
if (v != null && v != VoidValue.SINGLETON) {
X xv = (X) v;
boolean removed = tryRemove(xv);
if (!recurse) {
if (!removed)
// remove was disabled for this entry
return false;
} else {
if (!removed) {
// continue removing below then reinsert afterward
reinsertions.add(xv);
}
}
}
// Proceed with deleting the node...
FasterList<Node> childEdges = found.getOutgoingEdges();
int numChildren = childEdges.size();
if (numChildren > 0) {
if (!recurse) {
if (numChildren > 1) {
// This node has more than one child, so if we delete the value from this node, we still need
// to leave a similar node in place to act as the split between the child edges.
// Just delete the value associated with this node.
// -> Clone this node without its value, preserving its child nodes...
@SuppressWarnings("NullableProblems") Node cloned = createNode(found.getIncomingEdge(), null, found.getOutgoingEdges(), false);
// Re-add the replacement node to the parent...
parent.updateOutgoingEdge(cloned);
} else if (numChildren == 1) {
// Node has one child edge.
// Create a new node which is the concatenation of the edges from this node and its child,
// and which has the outgoing edges of the child and the value from the child.
Node child = childEdges.get(0);
AbstractBytes concatenatedEdges = concatenate(found.getIncomingEdge(), child.getIncomingEdge());
Node mergedNode = createNode(concatenatedEdges, child.getValue(), child.getOutgoingEdges(), false);
// Re-add the merged node to the parent...
parent.updateOutgoingEdge(mergedNode);
}
} else {
// collect all values from the subtree, call onRemove for them. then proceed below with removal of this node and its value
forEach(found, (k, f) -> {
boolean removed = tryRemove(f);
if (!removed) {
reinsertions.add(f);
}
});
numChildren = 0;
}
}
if (numChildren == 0) {
if (reinsertions.size() == 1) {
// in this case make no further changes
return false;
}
// Node has no children. Delete this node from its parent,
// which involves re-creating the parent rather than simply updating its child edge
// (this is why we need parentNodesParent).
// However if this would leave the parent with only one remaining child edge,
// and the parent itself has no value (is a split node), and the parent is not the root node
// (a special case which we never merge), then we also need to merge the parent with its
// remaining child.
FasterList<Node> currentEdgesFromParent = parent.getOutgoingEdges();
// Create a list of the outgoing edges of the parent which will remain
// if we remove this child...
int cen = currentEdgesFromParent.size();
FasterList<Node> newEdgesOfParent = new FasterList<>(0, new Node[cen]);
boolean differs = false;
for (int i = 0; i < cen; i++) {
Node node = currentEdgesFromParent.get(i);
if (node != found) {
newEdgesOfParent.add(node);
} else {
differs = true;
}
}
if (!differs)
// re-use original
newEdgesOfParent = currentEdgesFromParent;
// Note the parent might actually be the root node (which we should never merge)...
boolean parentIsRoot = (parent == root);
Node newParent;
if (newEdgesOfParent.size() == 1 && parent.getValue() == null && !parentIsRoot) {
// Parent is a non-root split node with only one remaining child, which can now be merged.
Node parentsRemainingChild = newEdgesOfParent.get(0);
// Merge the parent with its only remaining child...
AbstractBytes concatenatedEdges = concatenate(parent.getIncomingEdge(), parentsRemainingChild.getIncomingEdge());
newParent = createNode(concatenatedEdges, parentsRemainingChild.getValue(), parentsRemainingChild.getOutgoingEdges(), parentIsRoot);
} else {
// Parent is a node which either has a value of its own, has more than one remaining
// child, or is actually the root node (we never merge the root node).
// Create new parent node which is the same as is currently just without the edge to the
// node being deleted...
newParent = createNode(parent.getIncomingEdge(), parent.getValue(), newEdgesOfParent, parentIsRoot);
}
// Re-add the parent node to its parent...
if (parentIsRoot) {
// Replace the root node...
this.root = newParent;
} else {
// Re-add the parent node to its parent...
searchResult.parentNodesParent.updateOutgoingEdge(newParent);
}
}
reinsertions.forEach(this::put);
return true;
default:
return false;
}
}
use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method getValuesForClosestKeys.
/**
* {@inheritDoc}
*/
public Iterable<X> getValuesForClosestKeys(AbstractBytes candidate) {
acquireReadLockIfNecessary();
try {
SearchResult searchResult = searchTree(candidate);
SearchResult.Classification classification = searchResult.classification;
switch(classification) {
case EXACT_MATCH:
return getDescendantValues(candidate, searchResult.found);
case KEY_ENDS_MID_EDGE:
// Append the remaining characters of the edge to the key.
// For example if we searched for CO, but first matching node was COFFEE,
// the key associated with the first node should be COFFEE...
AbstractBytes edgeSuffix = getSuffix(searchResult.found.getIncomingEdge(), searchResult.charsMatchedInNodeFound);
candidate = concatenate(candidate, edgeSuffix);
return getDescendantValues(candidate, searchResult.found);
case INCOMPLETE_MATCH_TO_MIDDLE_OF_EDGE:
{
// Example: if we searched for CX, but deepest matching node was CO,
// the results should include node CO and its descendants...
AbstractBytes keyOfParentNode = getPrefix(candidate, searchResult.charsMatched - searchResult.charsMatchedInNodeFound);
AbstractBytes keyOfNodeFound = concatenate(keyOfParentNode, searchResult.found.getIncomingEdge());
return getDescendantValues(keyOfNodeFound, searchResult.found);
}
case INCOMPLETE_MATCH_TO_END_OF_EDGE:
if (searchResult.charsMatched == 0) {
// Closest match is the root node, we don't consider this a match for anything...
break;
}
// Example: if we searched for COFFEE, but deepest matching node was CO,
// the results should include node CO and its descendants...
AbstractBytes keyOfNodeFound = getPrefix(candidate, searchResult.charsMatched);
return getDescendantValues(keyOfNodeFound, searchResult.found);
}
return Collections.emptySet();
} finally {
releaseReadLockIfNecessary();
}
}
use of jcog.data.byt.AbstractBytes in project narchy by automenta.
the class MyConcurrentRadixTree method getKeyValuePairsForKeysStartingWith.
/**
* {@inheritDoc}
*/
public Iterable<Pair<AbstractBytes, X>> getKeyValuePairsForKeysStartingWith(AbstractBytes prefix) {
acquireReadLockIfNecessary();
try {
SearchResult searchResult = searchTree(prefix);
SearchResult.Classification classification = searchResult.classification;
Node f = searchResult.found;
switch(classification) {
case EXACT_MATCH:
return getDescendantKeyValuePairs(prefix, f);
case KEY_ENDS_MID_EDGE:
// Append the remaining characters of the edge to the key.
// For example if we searched for CO, but first matching node was COFFEE,
// the key associated with the first node should be COFFEE...
AbstractBytes edgeSuffix = getSuffix(f.getIncomingEdge(), searchResult.charsMatchedInNodeFound);
prefix = concatenate(prefix, edgeSuffix);
return getDescendantKeyValuePairs(prefix, f);
default:
// Incomplete match means key is not a prefix of any node...
return Collections.emptySet();
}
} finally {
releaseReadLockIfNecessary();
}
}
Aggregations