/** * Performs a depth first search on the internal hierarchy model * * @param parent the parent internal node of the current internal node * @param root the current internal node * @param connectingEdge the internal edge connecting the internal node and the parent internal * node, if any * @param visitor the visitor pattern to be called for each node * @param seen a set of all nodes seen by this dfs a set of all of the ancestor node of the * current node * @param layer the layer on the dfs tree ( not the same as the model ranks ) */ public void dfs( mxGraphHierarchyNode parent, mxGraphHierarchyNode root, mxGraphHierarchyEdge connectingEdge, CellVisitor visitor, Set<mxGraphHierarchyNode> seen, int layer) { if (root != null) { if (!seen.contains(root)) { visitor.visit(parent, root, connectingEdge, layer, 0); seen.add(root); // Copy the connects as source list so that visitors // can change the original for edge direction inversions final Object[] outgoingEdges = root.connectsAsSource.toArray(); for (int i = 0; i < outgoingEdges.length; i++) { mxGraphHierarchyEdge internalEdge = (mxGraphHierarchyEdge) outgoingEdges[i]; mxGraphHierarchyNode targetNode = internalEdge.target; // Root check is O(|roots|) dfs(root, targetNode, internalEdge, visitor, seen, layer + 1); } } else { // Use the int field to indicate this node has been seen visitor.visit(parent, root, connectingEdge, layer, 1); } } }
/** * Visit a row of the data matrix. * * @param mat Data matrix * @param row Row to visit * @param visitor Visitor function */ protected void visitRow(double[][] mat, int row, int mode, CellVisitor visitor) { boolean rselected = BitsUtil.get(rows, row); final double[] rowdata = mat[row]; for (int cpos = 0, clpos = 0; clpos < cols.length; ++clpos) { long clong = cols[clpos]; // Fast skip blocks of 64 masked values. if (mode == CellVisitor.SELECTED && clong == 0L) { cpos += Long.SIZE; continue; } if (mode == CellVisitor.NOT_SELECTED && clong == -1L) { cpos += Long.SIZE; continue; } for (int j = 0; j < Long.SIZE && cpos < colM.length; ++j, ++cpos, clong >>>= 1) { boolean cselected = ((clong & 1L) == 1L); if (mode == CellVisitor.SELECTED && !cselected) { continue; } if (mode == CellVisitor.NOT_SELECTED && cselected) { continue; } boolean stop = visitor.visit(rowdata[cpos], row, cpos, rselected, cselected); if (stop) { return; } } } }
/** * Visit a column of the matrix. * * @param mat Data matrix * @param col Column to visit * @param mode Operation mode * @param visitor Visitor function */ protected void visitColumn(double[][] mat, int col, int mode, CellVisitor visitor) { boolean cselected = BitsUtil.get(cols, col); // For efficiency, we manually iterate over the rows and column bitmasks. // This saves repeated shifting needed by the manual bit access. for (int rpos = 0, rlpos = 0; rlpos < rows.length; ++rlpos) { long rlong = rows[rlpos]; // Fast skip blocks of 64 masked values. if (mode == CellVisitor.SELECTED && rlong == 0L) { rpos += Long.SIZE; continue; } if (mode == CellVisitor.NOT_SELECTED && rlong == -1L) { rpos += Long.SIZE; continue; } for (int i = 0; i < Long.SIZE && rpos < rowM.length; ++i, ++rpos, rlong >>>= 1) { boolean rselected = ((rlong & 1L) == 1L); if (mode == CellVisitor.SELECTED && !rselected) { continue; } if (mode == CellVisitor.NOT_SELECTED && rselected) { continue; } boolean stop = visitor.visit(mat[rpos][col], rpos, col, rselected, cselected); if (stop) { return; } } } }
/** * Performs a depth first search on the internal hierarchy model. This dfs extends the default * version by keeping track of cells ancestors, but it should be only used when necessary because * of it can be computationally intensive for deep searches. * * @param parent the parent internal node of the current internal node * @param root the current internal node * @param connectingEdge the internal edge connecting the internal node and the parent internal * node, if any * @param visitor the visitor pattern to be called for each node * @param seen a set of all nodes seen by this dfs * @param ancestors the parent hash code * @param childHash the new hash code for this node * @param layer the layer on the dfs tree ( not the same as the model ranks ) */ public void dfs( mxGraphHierarchyNode parent, mxGraphHierarchyNode root, mxGraphHierarchyEdge connectingEdge, CellVisitor visitor, Set<mxGraphHierarchyNode> seen, int[] ancestors, int childHash, int layer) { // Explanation of custom hash set. Previously, the ancestors variable // was passed through the dfs as a HashSet. The ancestors were copied // into a new HashSet and when the new child was processed it was also // added to the set. If the current node was in its ancestor list it // meant there is a cycle in the graph and this information is passed // to the visitor.visit() in the seen parameter. The HashSet clone was // very expensive on CPU so a custom hash was developed using primitive // types. temp[] couldn't be used so hashCode[] was added to each node. // Each new child adds another int to the array, copying the prefix // from its parent. Child of the same parent add different ints (the // limit is therefore 2^32 children per parent...). If a node has a // child with the hashCode already set then the child code is compared // to the same portion of the current nodes array. If they match there // is a loop. // Note that the basic mechanism would only allow for 1 use of this // functionality, so the root nodes have two ints. The second int is // incremented through each node root and the first is incremented // through each run of the dfs algorithm (therefore the dfs is not // thread safe). The hash code of each node is set if not already set, // or if the first int does not match that of the current run. if (root != null) { if (parent != null) { // Form this nodes hash code if necessary, that is, if the // hashCode variable has not been initialized or if the // start of the parent hash code does not equal the start of // this nodes hash code, indicating the code was set on a // previous run of this dfs. if (root.hashCode == null || root.hashCode[0] != parent.hashCode[0]) { int hashCodeLength = parent.hashCode.length + 1; root.hashCode = new int[hashCodeLength]; System.arraycopy(parent.hashCode, 0, root.hashCode, 0, parent.hashCode.length); root.hashCode[hashCodeLength - 1] = childHash; } } if (!seen.contains(root)) { visitor.visit(parent, root, connectingEdge, layer, 0); seen.add(root); // Copy the connects as source list so that visitors // can change the original for edge direction inversions final Object[] outgoingEdges = root.connectsAsSource.toArray(); for (int i = 0; i < outgoingEdges.length; i++) { mxGraphHierarchyEdge internalEdge = (mxGraphHierarchyEdge) outgoingEdges[i]; mxGraphHierarchyNode targetNode = internalEdge.target; // Root check is O(|roots|) dfs(root, targetNode, internalEdge, visitor, seen, root.hashCode, i, layer + 1); } } else { // Use the int field to indicate this node has been seen visitor.visit(parent, root, connectingEdge, layer, 1); } } }