package DPJRuntime.Framework;
import extra166y.*;
import java.util.ArrayList;
import java.lang.reflect.Array;
import DPJRuntime.Framework.TreeOps.*;
/**
* <p>class DisjointTree</p>
*
* <p>A linear tree supporting safe recursive parallel operations.
* Supports several generic parallel operations that can be
* specialized via hook methods. Details TBA.
* T<TR>: type of data in tree nodes
* RR : region in which root node of the tree lives.
**/
public final class DisjointTree<type T<region TR>, region Cont>
implements DisjointContainer
{
private Node<T, Cont> root in Cont;
enum NodeType { LEAFNODE, INNERNODE };
public DisjointTree() pure { this.root = null; }
public Node<T, Cont> getRoot() reads Cont { return root; }
private static class Node<type T, region DR> {
protected final NodeType nodeType in DR;
T data in DR;
public Node(T data, NodeType nt) pure {
this.data = data;
this.nodeType = nt;
}
public void setData(T data) writes DR { this.data = data; }
public T getData() reads DR { return this.data; }
public NodeType getNodeType() reads DR { return this.nodeType; }
}
private static class DPJInnerNode<type T<region TR>, region DR>
extends Node<T, DR> {
public final ArrayList<Node<T, DR>> children;
// You must know the #children (arity) when creating an inner node.
public DPJInnerNode(T data, int arity) pure {
super(data, NodeType.INNERNODE);
children = new ArrayList<Node<T, DR>>(arity);
for (int i = 0; i < arity; i++) // needed to allow set(i, V) to work
children.add(null);
}
// Get the number of children of this node
public int arity() reads DR { return children.size(); }
// Get a single child from the array of children.
public Node<T, DR> get(int i) reads DR {
return children.get(i);
}
// Insert a single child into the array of children.
// Only to be used inside the framework to ensure linearity.
public void set(int i, Node<T, DR> node) writes Cont {
children.set(i, node);
}
}
private static class DPJLeafNode<type T<region TR>, region DR>
extends Node<T, DR> {
public DPJLeafNode(T data) pure {
super(data, NodeType.LEAFNODE);
}
}
/**
* @method buildTree: Build a tree of a fixed arity at all inner nodes.
* @param things : The objects to insert as leaves of the tree.
* @param arity : The fixed arity of all inner nodes in the tree.
* @param expander : The logic to choose where to insert each object.
* @return
*/
public <effect E | effect E # writes Cont effect E>
void buildTree(final DisjointArray<T, Cont> things,
final int arity,
final TreeOps.NodeExpander<T, effect E> expander)
writes Cont effect E
{
Node<T, Cont> root = null;
Node<T, Cont> lastNode1 = null, lastNode2 = null;
for (int i=0; i < things.size(); ++i) {
T next = things.get(i);
root = this.<effect E>buildTreeHelper(next, arity, expander,
root, lastNode2, -1);
lastNode2 = lastNode1;
lastNode1 = root;
}
this.root = root;
}
private <effect E> Node<T, Cont>
buildTreeHelper(final T thing,
final int arity,
final TreeOps.NodeExpander<T, effect E> expander,
final Node<T, Cont> curNode,
final Node<T, Cont> parentNode,
final int indexOfCurNodeInParent)
writes Cont effect E
{
//--------------------------------------------------------------------
// 1. If null node, insert immediately as new leaf node. Return it.
// 2. If leaf node: Must be non-empty:
// (a) Create new InnerNode; this is tree-to-return.
// (b) Ask factory for a *fresh* object to go in that node, if any.
// (c) Ask which slot to use for old node; insert it in that slot.
// 3. Else inner node:
// (a) This node is tree-to-return.
// (b) Ask factory for a *fresh* object to go in that node, if any.
// 4. Ask which slot to use for new object
// 5. Recursively build new subtree with updated box.
// NOTE: Thing may collide again, but that is the right behavior!
// Collisions will continue until tree is expanded enough.
// 6. Insert new subtree in slot computed in step 4.
// 7. Return tree-to-return.
//--------------------------------------------------------------------
// 1. If null node, insert immediately as new leaf node and return it.
if (curNode == null) {
DPJLeafNode newNode = new DPJLeafNode(thing);
System.out.println("New Node [1]: " + newNode + "; obj =" + thing);
return newNode;
}
// 2. If leaf node: Must be non-empty:
// (a) Create new InnerNode; this is node-to-return.
// (b) Ask factory for a *fresh* object to go in that node, if any.
// (c) Ask which slot to use for old node; insert it in that slot.
//
DPJInnerNode<T, Cont> treeToReturn = null;
Node<T, Cont> newParent = null;
if (curNode.nodeType == NodeType.LEAFNODE) {
assert(curNode.getData() != null);
T newObject = expander.<region TR>nodeFactory(curNode.getData(),
parentNode==null? null: parentNode.getData(),
indexOfCurNodeInParent, thing);
final DPJInnerNode<T, Cont> newInner =
new DPJInnerNode<T, Cont>(newObject, arity);
final int ci = expander.slotToExpand(newObject,
parentNode==null? null : parentNode.getData(),
curNode.getData());
assert(0 <= ci && ci < arity);
newInner.set(ci, curNode);
treeToReturn = newInner;
newParent = parentNode;
}
else {
// 3. Else inner node:
// (a) This node is node-to-return.
// (b) Ask factory for a *fresh* object to go in that node, if any.
assert (curNode.nodeType == NodeType.INNERNODE);
T newObject = expander.<region TR>nodeFactory(curNode.getData(),
parentNode==null? null : parentNode.getData(),
indexOfCurNodeInParent, thing);
curNode.setData(newObject);
treeToReturn = (DPJInnerNode<T, Cont>) curNode;
newParent = parentNode;
}
// 4. Ask which slot to use for new object
final int cj = expander.slotToExpand(treeToReturn.getData(),
newParent==null? null : newParent.getData(),
thing);
assert(0 <= cj && cj < arity);
// 5. Recursively call self to build new subtree at child cj,
// inserting 'thing' in that subtree.
final Node<T, Cont> newTree =
buildTreeHelper(thing, arity, expander, treeToReturn.get(cj), treeToReturn, cj);
// 6. Insert new subtree in slot computed in step 4.
treeToReturn.set(cj, newTree);
// 7. Return tree-to-return.
return treeToReturn;
}
/**
* Entry point for subtree visitor pattern to traverse a subtree
* in postorder and apply a given operation to each node,
* passing in the results of the children as a vector Tresult[].
*/
public static class PostOrderTraversal<type T<region TR>,
type Tresult<region Rresult>, region Cont> {
public <effect E | effect E # reads Cont writes TR:*>
Tresult<Rresult>
parallelPostorder(Node<T, Cont> subtree,
SubtreeVisitorPostOrder<T, Tresult, effect E> visitor)
reads Cont writes TR:*, Rresult:* effect E
{
if (subtree == null)
return null;
// recursively visit all the children of subtree
ArrayList<Tresult<Rresult>> childResults = null;
if (subtree.nodeType == NodeType.INNERNODE) {
// recursively visit the children in parallel
DPJInnerNode<T, Cont> inner = (DPJInnerNode<T, Cont>) subtree;
childResults = new ArrayList<Tresult<Rresult>>(inner.arity());
// This is a hack: initialize the slots sequentially.
for (int i = 0; i < inner.arity(); ++i)
childResults.add(null);
foreach (int i in 0, inner.arity()) {
childResults.set(i,
parallelPostorder(inner.get(i), visitor));
}
}
// Now, visit current node, passing in children's result.
// Return the result of this postorder visit.
Tresult<Rresult> result =
visitor.<region TR, Rresult>
visit(subtree.getData(), childResults);
return result;
}
}
}