/*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package sh.isaac.api.tree;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.mahout.math.list.IntArrayList;
import org.apache.mahout.math.set.OpenIntHashSet;
import sh.isaac.api.collections.ConceptSequenceSet;
import sh.isaac.api.collections.SequenceSet;
//~--- classes ----------------------------------------------------------------
/**
* The Class TreeNodeVisitData.
*
* @author kec
*/
public class TreeNodeVisitData {
/** The visit started. */
private SequenceSet<?> visitStarted = new SequenceSet<>();
/** The visit ended. */
private SequenceSet<?> visitEnded = new SequenceSet<>();
/** The leaf nodes. */
private SequenceSet<?> leafNodes = new SequenceSet<>();
/** The max depth. */
private int maxDepth = 0;
/** The time. */
private int time = 0;
/** The nodes visited. */
private int nodesVisited = 0;
/** The distance list. */
protected final IntArrayList distanceList;
/** The discovery time list. */
protected final IntArrayList discoveryTimeList;
/** The finish time list. */
protected final IntArrayList finishTimeList;
/** The predecessor sequence list. */
protected final IntArrayList predecessorSequenceList;
/** The sibling group sequence list. */
protected final IntArrayList siblingGroupSequenceList;
/** The concepts referenced at node or above. */
private OpenIntHashSet[] conceptsReferencedAtNodeOrAbove;
/** The graph size. */
private final int graphSize;
//~--- constructors --------------------------------------------------------
/**
* Instantiates a new tree node visit data.
*
* @param graphSize the graph size
*/
public TreeNodeVisitData(int graphSize) {
this.graphSize = graphSize;
this.visitStarted = new SequenceSet<>();
this.visitEnded = new SequenceSet<>();
this.leafNodes = new SequenceSet<>();
this.distanceList = new IntArrayList(new int[graphSize]);
this.discoveryTimeList = new IntArrayList(new int[graphSize]);
this.finishTimeList = new IntArrayList(new int[graphSize]);
this.siblingGroupSequenceList = new IntArrayList(new int[graphSize]);
this.predecessorSequenceList = new IntArrayList(new int[graphSize]);
this.predecessorSequenceList.fillFromToWith(0, graphSize - 1, -1);
}
//~--- methods -------------------------------------------------------------
/**
* End node visit.
*
* @param nodeSequence the node sequence
*/
public void endNodeVisit(int nodeSequence) {
setNodeStatus(nodeSequence, NodeStatus.FINISHED);
setFinishTime(nodeSequence, this.time++);
}
/**
* Start node visit.
*
* @param nodeSequence the node sequence
* @param depth the depth
*/
public void startNodeVisit(int nodeSequence, int depth) {
setNodeStatus(nodeSequence, NodeStatus.PROCESSING);
setDiscoveryTime(nodeSequence, this.time++);
setDistance(nodeSequence, depth);
this.nodesVisited++;
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the concepts referenced at node or above.
*
* @param nodeSequence the node sequence
* @return the concepts referenced at node or above
*/
public OpenIntHashSet getConceptsReferencedAtNodeOrAbove(int nodeSequence) {
if (nodeSequence >= 0) {
// lazy creation to save memory since not all tree traversals want to
// use this capability.
if (this.conceptsReferencedAtNodeOrAbove == null) {
this.conceptsReferencedAtNodeOrAbove = new OpenIntHashSet[this.graphSize];
}
if (this.conceptsReferencedAtNodeOrAbove[nodeSequence] == null) {
this.conceptsReferencedAtNodeOrAbove[nodeSequence] = new OpenIntHashSet(this.graphSize);
}
return this.conceptsReferencedAtNodeOrAbove[nodeSequence];
}
return new OpenIntHashSet(this.graphSize);
}
//~--- set methods ---------------------------------------------------------
/**
* Set concepts referenced at node or above.
*
* @param nodeSequence the node sequence
* @param conceptSet the concept set
*/
public void setConceptsReferencedAtNodeOrAbove(int nodeSequence, ConceptSequenceSet conceptSet) {
if (nodeSequence >= 0) {
// lazy creation to save memory since not all tree traversals want to
// use this capability.
if (this.conceptsReferencedAtNodeOrAbove == null) {
this.conceptsReferencedAtNodeOrAbove = new OpenIntHashSet[this.graphSize];
}
this.conceptsReferencedAtNodeOrAbove[nodeSequence] = conceptSet.asOpenIntHashSet();
}
}
/**
* Set concepts referenced at node or above.
*
* @param nodeSequence the node sequence
* @param conceptSet the concept set
*/
public void setConceptsReferencedAtNodeOrAbove(int nodeSequence, OpenIntHashSet conceptSet) {
if (nodeSequence >= 0) {
// lazy creation to save memory since not all tree traversals want to
// use this capability.
if (this.conceptsReferencedAtNodeOrAbove == null) {
this.conceptsReferencedAtNodeOrAbove = new OpenIntHashSet[this.graphSize];
}
this.conceptsReferencedAtNodeOrAbove[nodeSequence] = conceptSet;
}
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the discovery time.
*
* @param sequence the sequence
* @return the discovery time
*/
public int getDiscoveryTime(int sequence) {
return this.discoveryTimeList.getQuick(sequence);
}
//~--- set methods ---------------------------------------------------------
/**
* Set discovery time.
*
* @param sequence the sequence
* @param discoveryTime the discovery time
*/
private void setDiscoveryTime(int sequence, int discoveryTime) {
if (sequence >= this.discoveryTimeList.size()) {
this.discoveryTimeList.setSize(sequence + 1);
}
this.discoveryTimeList.set(sequence, discoveryTime);
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the distance.
*
* @param sequence the sequence
* @return the distance
*/
public int getDistance(int sequence) {
return this.distanceList.getQuick(sequence);
}
//~--- set methods ---------------------------------------------------------
/**
* Set distance.
*
* @param sequence the sequence
* @param distance the distance
*/
public void setDistance(int sequence, int distance) {
if (sequence >= this.distanceList.size()) {
this.distanceList.setSize(sequence + 1);
}
this.distanceList.set(sequence, distance);
this.maxDepth = Math.max(this.maxDepth, distance);
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the finish time.
*
* @param sequence the sequence
* @return the finish time
*/
public int getFinishTime(int sequence) {
return this.finishTimeList.getQuick(sequence);
}
//~--- set methods ---------------------------------------------------------
/**
* Set finish time.
*
* @param sequence the sequence
* @param finishTime the finish time
*/
private void setFinishTime(int sequence, int finishTime) {
if (sequence >= this.finishTimeList.size()) {
this.finishTimeList.setSize(sequence + 1);
}
this.finishTimeList.set(sequence, finishTime);
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the graph size.
*
* @return the graph size
*/
public int getGraphSize() {
return this.graphSize;
}
/**
* Gets the intermediate nodes.
*
* @return the intermediate nodes
*/
public SequenceSet<?> getIntermediateNodes() {
final SequenceSet intermediateNodes = new SequenceSet<>();
intermediateNodes.or(this.visitEnded);
intermediateNodes.andNot(this.leafNodes);
return intermediateNodes;
}
//~--- set methods ---------------------------------------------------------
/**
* Sets the leaf node.
*
* @param sequence the new leaf node
*/
public void setLeafNode(int sequence) {
this.leafNodes.add(sequence);
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the leaf nodes.
*
* @return the leaf nodes
*/
public SequenceSet<?> getLeafNodes() {
return this.leafNodes;
}
/**
* Gets the max depth.
*
* @return the max depth
*/
public int getMaxDepth() {
return this.maxDepth;
}
/**
* Gets the node ids for depth.
*
* @param depth the depth
* @return the node ids for depth
*/
public SequenceSet<?> getNodeIdsForDepth(int depth) {
final SequenceSet<?> nodeIdsForDepth = new SequenceSet<>();
for (int i = 0; i < this.distanceList.size(); i++) {
if (this.distanceList.get(i) == depth) {
nodeIdsForDepth.add(i);
}
}
return nodeIdsForDepth;
}
/**
* Gets the node status.
*
* @param nodeSequence the node sequence
* @return the node status
*/
public NodeStatus getNodeStatus(int nodeSequence) {
if (!this.visitStarted.contains(nodeSequence)) {
return NodeStatus.UNDISCOVERED;
}
if (this.visitEnded.contains(nodeSequence)) {
return NodeStatus.FINISHED;
}
return NodeStatus.PROCESSING;
}
//~--- set methods ---------------------------------------------------------
/**
* Set node status.
*
* @param nodeSequence the node sequence
* @param nodeStatus the node status
*/
public void setNodeStatus(int nodeSequence, NodeStatus nodeStatus) {
switch (nodeStatus) {
case FINISHED:
this.visitEnded.add(nodeSequence);
break;
case PROCESSING:
this.visitStarted.add(nodeSequence);
break;
case UNDISCOVERED:
throw new UnsupportedOperationException("Can't reset to undiscovered");
default:
throw new UnsupportedOperationException("no support for: " + nodeStatus);
}
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the nodes visited.
*
* @return the nodes visited
*/
public int getNodesVisited() {
return this.nodesVisited;
}
/**
* Gets the predecessor sequence.
*
* @param sequence the sequence
* @return the predecessor sequence
*/
public int getPredecessorSequence(int sequence) {
return this.predecessorSequenceList.getQuick(sequence);
}
//~--- set methods ---------------------------------------------------------
/**
* Set predecessor sequence.
*
* @param sequence the sequence
* @param predecessorSequence the predecessor sequence
*/
public void setPredecessorSequence(int sequence, int predecessorSequence) {
if (sequence >= this.predecessorSequenceList.size()) {
this.predecessorSequenceList.setSize(sequence + 1);
}
this.predecessorSequenceList.set(sequence, predecessorSequence);
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the sibling group for sequence.
*
* @param sequence the sequence
* @return the sibling group for sequence
*/
public int getSiblingGroupForSequence(int sequence) {
return this.siblingGroupSequenceList.get(sequence);
}
//~--- set methods ---------------------------------------------------------
/**
* Set sibling group for sequence.
*
* @param sequence the sequence
* @param value the value
*/
public void setSiblingGroupForSequence(int sequence, int value) {
this.siblingGroupSequenceList.set(sequence, value);
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the time.
*
* @return the time
*/
public int getTime() {
return this.time;
}
}