/**
   * DOCUMENT ME!
   *
   * @param node DOCUMENT ME!
   * @return DOCUMENT ME!
   */
  public final boolean nodeRemove(final int node) {
    final IntEnumerator edges = edgesAdjacent(node, true, true, true);

    if (edges == null) {
      return false;
    }

    m_stack.empty();

    while (edges.numRemaining() > 0) m_stack.push(edges.nextInt());

    while (m_stack.size() > 0) edgeRemove(m_stack.pop());

    final Node n = m_nodes.getNodeAtIndex(node);

    if (n.prevNode != null) {
      n.prevNode.nextNode = n.nextNode;
    } else {
      m_firstNode = n.nextNode;
    }

    if (n.nextNode != null) {
      n.nextNode.prevNode = n.prevNode;
    }

    m_nodes.setNodeAtIndex(null, node);
    n.prevNode = null;
    n.firstOutEdge = null;
    n.firstInEdge = null;
    m_nodeDepot.recycleNode(n);
    m_nodeCount--;

    return true;
  }
  private void addWordRec(
      NodeArray children, final String word, final int depth, final int frequency) {

    final int wordLength = word.length();
    final char c = word.charAt(depth);
    // Does children have the current character?
    final int childrenLength = children.length;
    Node childNode = null;
    boolean found = false;
    for (int i = 0; i < childrenLength; i++) {
      childNode = children.data[i];
      if (childNode.code == c) {
        found = true;
        break;
      }
    }
    if (!found) {
      childNode = new Node();
      childNode.code = c;
      children.add(childNode);
    }
    if (wordLength == depth + 1) {
      // Terminate this word
      childNode.terminal = true;
      childNode.frequency += frequency; // If there are multiple similar words
      return;
    }
    if (childNode.children == null) {
      childNode.children = new NodeArray();
    }
    addWordRec(childNode.children, word, depth + 1, frequency);
  }
  /**
   * DOCUMENT ME!
   *
   * @param node DOCUMENT ME!
   * @return DOCUMENT ME!
   */
  public final boolean nodeExists(final int node) {
    if ((node < 0) || (node == Integer.MAX_VALUE)) {
      return false;
    }

    return m_nodes.getNodeAtIndex(node) != null;
  }
  /**
   * DOCUMENT ME!
   *
   * @param edge DOCUMENT ME!
   * @return DOCUMENT ME!
   */
  public final boolean edgeRemove(final int edge) {
    if ((edge < 0) || (edge == Integer.MAX_VALUE)) {
      return false;
    }

    final Edge e = m_edges.getEdgeAtIndex(edge);

    if (e == null) {
      return false;
    }

    final Node source = m_nodes.getNodeAtIndex(e.sourceNode);
    final Node target = m_nodes.getNodeAtIndex(e.targetNode);

    if (e.prevOutEdge != null) {
      e.prevOutEdge.nextOutEdge = e.nextOutEdge;
    } else {
      source.firstOutEdge = e.nextOutEdge;
    }

    if (e.nextOutEdge != null) {
      e.nextOutEdge.prevOutEdge = e.prevOutEdge;
    }

    if (e.prevInEdge != null) {
      e.prevInEdge.nextInEdge = e.nextInEdge;
    } else {
      target.firstInEdge = e.nextInEdge;
    }

    if (e.nextInEdge != null) {
      e.nextInEdge.prevInEdge = e.prevInEdge;
    }

    if (e.directed) {
      source.outDegree--;
      target.inDegree--;
    } else {
      source.undDegree--;
      target.undDegree--;
    }

    if (source == target) { // Self-edge.

      if (e.directed) {
        source.selfEdges--;
      } else {
        source.undDegree++;
      }
    }

    m_edges.setEdgeAtIndex(null, edge);
    e.prevOutEdge = null;
    e.nextInEdge = null;
    e.prevInEdge = null;
    m_edgeDepot.recycleEdge(e);
    m_edgeCount--;

    return true;
  }
  /**
   * DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  public final int nodeCreate() {
    final Node n = m_nodeDepot.getNode();
    final int returnThis;

    if (n.nodeId < 0) {
      returnThis = (n.nodeId = ++m_maxNode);
    } else {
      returnThis = n.nodeId;
    }

    m_nodes.setNodeAtIndex(n, returnThis);
    m_nodeCount++;
    n.nextNode = m_firstNode;

    if (m_firstNode != null) {
      m_firstNode.prevNode = n;
    }

    m_firstNode = n;
    n.outDegree = 0;
    n.inDegree = 0;
    n.undDegree = 0;
    n.selfEdges = 0;

    return returnThis;
  }
  /**
   * DOCUMENT ME!
   *
   * @param node DOCUMENT ME!
   * @param outgoing DOCUMENT ME!
   * @param incoming DOCUMENT ME!
   * @param undirected DOCUMENT ME!
   * @return DOCUMENT ME!
   */
  public final IntEnumerator edgesAdjacent(
      final int node, final boolean outgoing, final boolean incoming, final boolean undirected) {
    if ((node < 0) || (node == Integer.MAX_VALUE)) {
      return null;
    }

    final Node n = m_nodes.getNodeAtIndex(node);

    if (n == null) {
      return null;
    }

    final Edge[] edgeLists;

    if (undirected || (outgoing && incoming)) {
      edgeLists = new Edge[] {n.firstOutEdge, n.firstInEdge};
    } else if (outgoing) { // Cannot also be incoming.
      edgeLists = new Edge[] {n.firstOutEdge, null};
    } else if (incoming) { // Cannot also be outgoing.
      edgeLists = new Edge[] {null, n.firstInEdge};
    } else { // All boolean input parameters are false.
      edgeLists = new Edge[] {null, null};
    }

    int tentativeEdgeCount = 0;

    if (outgoing) {
      tentativeEdgeCount += n.outDegree;
    }

    if (incoming) {
      tentativeEdgeCount += n.inDegree;
    }

    if (undirected) {
      tentativeEdgeCount += n.undDegree;
    }

    if (outgoing && incoming) {
      tentativeEdgeCount -= n.selfEdges;
    }

    return new EdgesAdjacentEnumerator(
        tentativeEdgeCount, edgeLists, undirected, incoming, outgoing);
  }
  /**
   * DOCUMENT ME!
   *
   * @param in DOCUMENT ME!
   */
  public final void readExternal(final java.io.ObjectInput in) throws java.io.IOException {
    m_nodeCount = in.readInt();
    m_maxNode = in.readInt();
    m_edgeCount = in.readInt();
    m_maxEdge = in.readInt();

    { // m_nodeDepot.
      Node currNode = m_nodeDepot.m_head;

      while (true) {
        final int id = in.readInt();

        if (id < 0) {
          break;
        }

        currNode.nextNode = new Node();
        currNode = currNode.nextNode;
        currNode.nodeId = id;
      }
    }

    { // m_edgeDepot.
      Edge currEdge = m_edgeDepot.m_head;

      while (true) {
        final int id = in.readInt();

        if (id < 0) {
          break;
        }

        currEdge.nextOutEdge = new Edge();
        currEdge = currEdge.nextOutEdge;
        currEdge.edgeId = id;
      }
    }

    { // m_edges.
      final int arrLen = in.readInt();
      final Edge[] arr = (m_edges.m_edgeArr = new Edge[arrLen]);

      for (int i = 0; i < arrLen; i++) {
        final int source = in.readInt();

        if (source < 0) {
          continue;
        }

        final Edge edge = (arr[i] = new Edge());
        edge.edgeId = i;
        edge.sourceNode = source;
        edge.targetNode = in.readInt();
        edge.directed = in.readBoolean();
      }

      for (int i = 0; i < arrLen; i++) {
        final Edge edge = arr[i];

        if (edge == null) {
          continue;
        }

        final int nextOutEdge = in.readInt();
        final int prevOutEdge = in.readInt();
        final int nextInEdge = in.readInt();
        final int prevInEdge = in.readInt();

        if (nextOutEdge >= 0) {
          edge.nextOutEdge = arr[nextOutEdge];
        }

        if (prevOutEdge >= 0) {
          edge.prevOutEdge = arr[prevOutEdge];
        }

        if (nextInEdge >= 0) {
          edge.nextInEdge = arr[nextInEdge];
        }

        if (prevInEdge >= 0) {
          edge.prevInEdge = arr[prevInEdge];
        }
      }
    }

    { // m_nodes.
      final int arrLen = in.readInt();
      final Node[] arr = (m_nodes.m_nodeArr = new Node[arrLen]);

      for (int i = 0; i < arrLen; i++) {
        final int outDeg = in.readInt();

        if (outDeg < 0) {
          continue;
        }

        final Node node = (arr[i] = new Node());
        node.nodeId = i;
        node.outDegree = outDeg;
        node.inDegree = in.readInt();
        node.undDegree = in.readInt();
        node.selfEdges = in.readInt();
      }

      final Edge[] edgeArr = m_edges.m_edgeArr;

      for (int i = 0; i < arrLen; i++) {
        final Node node = arr[i];

        if (node == null) {
          continue;
        }

        final int nextNode = in.readInt();
        final int prevNode = in.readInt();
        final int firstOutEdge = in.readInt();
        final int firstInEdge = in.readInt();

        if (nextNode >= 0) {
          node.nextNode = arr[nextNode];
        }

        if (prevNode >= 0) {
          node.prevNode = arr[prevNode];
        }

        if (firstOutEdge >= 0) {
          node.firstOutEdge = edgeArr[firstOutEdge];
        }

        if (firstInEdge >= 0) {
          node.firstInEdge = edgeArr[firstInEdge];
        }
      }
    }

    { // m_firstNode.
      final int firstNode = in.readInt();

      if (firstNode >= 0) {
        m_firstNode = m_nodes.m_nodeArr[firstNode];
      }
    }
  }
  /**
   * DOCUMENT ME!
   *
   * @param sourceNode DOCUMENT ME!
   * @param targetNode DOCUMENT ME!
   * @param directed DOCUMENT ME!
   * @return DOCUMENT ME!
   */
  public final int edgeCreate(final int sourceNode, final int targetNode, final boolean directed) {
    if ((sourceNode < 0) || (sourceNode == Integer.MAX_VALUE)) {
      return -1;
    }

    final Node source = m_nodes.getNodeAtIndex(sourceNode);

    if ((targetNode < 0) || (targetNode == Integer.MAX_VALUE)) {
      return -1;
    }

    final Node target = m_nodes.getNodeAtIndex(targetNode);

    if ((source == null) || (target == null)) {
      return -1;
    }

    final Edge e = m_edgeDepot.getEdge();
    final int returnThis;

    if (e.edgeId < 0) {
      returnThis = (e.edgeId = ++m_maxEdge);
    } else {
      returnThis = e.edgeId;
    }

    m_edges.setEdgeAtIndex(e, returnThis);
    m_edgeCount++;

    if (directed) {
      source.outDegree++;
      target.inDegree++;
    } else {
      source.undDegree++;
      target.undDegree++;
    }

    if (source == target) { // Self-edge.

      if (directed) {
        source.selfEdges++;
      } else {
        source.undDegree--;
      }
    }

    e.nextOutEdge = source.firstOutEdge;

    if (source.firstOutEdge != null) {
      source.firstOutEdge.prevOutEdge = e;
    }

    source.firstOutEdge = e;
    e.nextInEdge = target.firstInEdge;

    if (target.firstInEdge != null) {
      target.firstInEdge.prevInEdge = e;
    }

    target.firstInEdge = e;
    e.directed = directed;
    e.sourceNode = sourceNode;
    e.targetNode = targetNode;

    return returnThis;
  }