/**
   * 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);
      }
    }
  }
Example #2
0
 /**
  * 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;
       }
     }
   }
 }
Example #3
0
 /**
  * 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);
      }
    }
  }