com.phoenixst.plexus.examples.CompleteTree.java Source code

Java tutorial

Introduction

Here is the source code for com.phoenixst.plexus.examples.CompleteTree.java

Source

/*
 *  $Id: CompleteTree.java,v 1.32 2006/06/20 01:09:29 rconner Exp $
 *
 *  Copyright (C) 1994-2006 by Phoenix Software Technologists,
 *  Inc. and others.  All rights reserved.
 *
 *  THIS PROGRAM AND DOCUMENTATION IS PROVIDED UNDER THE TERMS OF THE
 *  COMMON PUBLIC LICENSE ("AGREEMENT") WHICH ACCOMPANIES IT.  ANY
 *  USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
 *  RECIPIENT'S ACCEPTANCE OF THE AGREEMENT.
 *
 *  The license text can also be found at
 *    http://opensource.org/licenses/cpl.php
 */

package com.phoenixst.plexus.examples;

import java.io.*;
import java.util.*;

import org.apache.commons.collections.iterators.EmptyIterator;

import com.phoenixst.plexus.*;

/**
 *  A <code>Graph</code> which is a complete tree.
 *
 *  @version    $Revision: 1.32 $
 *  @author     Ray A. Conner
 *
 *  @since      1.0
 */
public class CompleteTree extends AbstractIntegerNodeGraph {

    private static final long serialVersionUID = 2L;

    /*
     *  A complete tree of height H with K children per internal node
     *  has:
     *
     *    (K^H - 1) / (K - 1)     internal nodes
     *    (K^H)                   leaf nodes
     *    (K^(H+1) - 1) / (K - 1) total nodes
     *
     *  The numInternalNodes field is calculated, and used as a
     *  dividing point in node indices.  Node 0 is the root, nodes 1
     *  through (numInternalNodes-1) are the rest of the internal
     *  nodes, and all nodes >= numInternalNodes are leaves.
     *
     *  For a given node of index I, other related nodes (if
     *  appropriate) are:
     *
     *     parent index = (I - 1) / K
     *     child indices = (I * K) + 1 through (I * K) + K
     */

    /**
     *  The height.
     *
     *  @serial
     */
    private final int height;

    /**
     *  The number of children for each internal node.
     *
     *  @serial
     */
    private final int numChildren;

    /**
     *  The number of internal nodes.
     */
    private transient int numInternalNodes;

    ////////////////////////////////////////
    // Constructor
    ////////////////////////////////////////

    /**
     *  Creates a new <code>CompleteTree</code>.  A tree of height 0
     *  is just a single root node (numChildren is ignored in this
     *  case).
     */
    public CompleteTree(int height, int numChildren) {
        super(getNodeSize(height, numChildren));
        this.height = height;
        this.numChildren = numChildren;
        if (height < 0) {
            throw new IllegalArgumentException("A CompleteTree must have height at least 0: " + height);
        }
        if (numChildren < 1) {
            throw new IllegalArgumentException(
                    "A CompleteTree must have at least 1 child per internal node: " + numChildren);
        }

        if (numChildren == 1) {
            numInternalNodes = height;
        } else {
            int numNodes = 1;
            for (int i = 0; i < height; i++) {
                numNodes *= numChildren;
            }
            numInternalNodes = (numNodes - 1) / (numChildren - 1);
        }
    }

    private static int getNodeSize(int height, int numChildren) {
        if (numChildren == 1) {
            return height + 1;
        }
        int numNodes = 1;
        for (int i = 0; i < height; i++) {
            numNodes *= numChildren;
        }
        return (numNodes * numChildren - 1) / (numChildren - 1);
    }

    ////////////////////////////////////////
    // Serialization
    ////////////////////////////////////////

    private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
        in.defaultReadObject();
        if (height < 0) {
            throw new InvalidObjectException("A CompleteTree must have height at least 0: " + height);
        }
        if (numChildren < 1) {
            throw new InvalidObjectException(
                    "A CompleteTree must have at least 1 child per internal node: " + numChildren);
        }

        int numNodes = 1;
        if (numChildren == 1) {
            numInternalNodes = height;
            numNodes = height + 1;
        } else {
            numNodes = 1;
            for (int i = 0; i < height; i++) {
                numNodes *= numChildren;
            }
            numInternalNodes = (numNodes - 1) / (numChildren - 1);
            numNodes = (numNodes * numChildren - 1) / (numChildren - 1);
        }

        if (numNodes != getNodeSize()) {
            throw new InvalidObjectException("Calculated number of nodes does not match.");
        }
    }

    ////////////////////////////////////////
    // CompleteTree Methods
    ////////////////////////////////////////

    /**
     *  Gets the height of this <code>CompleteTree</code>.
     */
    public final int getHeight() {
        return height;
    }

    /**
     *  Gets the number of children of each internal node in this
     *  <code>CompleteTree</code>.
     */
    public final int getNumChildren() {
        return numChildren;
    }

    ////////////////////////////////////////
    // Graph methods
    ////////////////////////////////////////

    /**
     *  Returns the degree of <code>node</code>, defined as the number
     *  of edges incident on <code>node</code>.
     */
    public int degree(Object node) {
        int nodeIndex = checkNode(node);
        if (nodeIndex == 0) {
            return (height == 0) ? 0 : numChildren;
        } else if (nodeIndex < numInternalNodes) {
            return numChildren + 1;
        } else {
            return 1;
        }
    }

    ////////////////////////////////////////
    // Other methods
    ////////////////////////////////////////

    protected Graph.Edge createEdge(int tailIndex, int headIndex) {
        if (tailIndex < headIndex) {
            if (tailIndex == (headIndex - 1) / numChildren) {
                return new EdgeImpl(tailIndex, headIndex, true);
            }
        } else if (tailIndex > headIndex) {
            if (headIndex == (tailIndex - 1) / numChildren) {
                return new EdgeImpl(headIndex, tailIndex, true);
            }
        }
        return null;
    }

    protected Collection createEdgeCollection() {
        return new EdgeCollection();
    }

    protected Traverser createTraverser(int nodeIndex) {
        if (height == 0) {
            return GraphUtils.EMPTY_TRAVERSER;
        } else if (nodeIndex == 0) {
            return new RootTraverser();
        } else if (nodeIndex < numInternalNodes) {
            return new InternalTraverser(nodeIndex);
        } else {
            int tailIndex = (nodeIndex - 1) / numChildren;
            return GraphUtils.singletonTraverser(this, new Integer(tailIndex), createEdge(tailIndex, nodeIndex));
        }
    }

    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append("Complete Tree( ");
        s.append(height);
        s.append(", ");
        s.append(numChildren);
        s.append(" )");
        return s.toString();
    }

    ////////////////////////////////////////
    // Private classes
    ////////////////////////////////////////

    private class EdgeCollection extends AbstractIntegerEdgeCollection {
        EdgeCollection() {
            super();
        }

        public int size() {
            return getNodeSize() - 1;
        }

        public Iterator iterator() {
            return getHeight() == 0 ? EmptyIterator.INSTANCE : new EdgeIterator();
        }
    }

    /**
     *
     */
    private class EdgeIterator implements Iterator {
        private int headIndex = 0;

        EdgeIterator() {
            super();
        }

        public boolean hasNext() {
            return headIndex < getNodeSize() - 1;
        }

        public Object next() {
            headIndex++;
            if (headIndex >= getNodeSize()) {
                throw new NoSuchElementException();
            }
            return createEdge((headIndex - 1) / getNumChildren(), headIndex);
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    /**
     *
     */
    private class RootTraverser implements Traverser {
        /*
         *  We don't have to check for height==0 here, since the empty
         *  case is caught in the adjacentTraverser() method.
         */

        private int headIndex = 0;

        RootTraverser() {
            super();
        }

        public boolean hasNext() {
            return headIndex < getNumChildren();
        }

        public Object next() {
            if (headIndex >= getNumChildren()) {
                throw new NoSuchElementException();
            }
            headIndex++;
            return new Integer(headIndex);
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        public Graph.Edge getEdge() {
            if (headIndex == 0 || headIndex > getNumChildren()) {
                throw new IllegalStateException();
            }
            return createEdge(0, headIndex);
        }

        public void removeEdge() {
            throw new UnsupportedOperationException();
        }
    }

    /**
     *
     */
    private class InternalTraverser implements Traverser {
        /*
         *  When i is -1, we haven't started yet
         *  When i is 0, traverse the parent edge
         *  When i is 1->#children, traverse child edges
         */

        private final int nodeIndex;
        private final int parentIndex;
        private final int baseChildIndex;
        private int i = -1;

        InternalTraverser(int nodeIndex) {
            super();
            this.nodeIndex = nodeIndex;
            parentIndex = (nodeIndex - 1) / getNumChildren();
            baseChildIndex = nodeIndex * getNumChildren();
        }

        public boolean hasNext() {
            return i < getNumChildren();
        }

        public Object next() {
            if (i >= getNumChildren()) {
                throw new NoSuchElementException();
            }
            i++;
            return (i == 0) ? new Integer(parentIndex) : new Integer(baseChildIndex + i);
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

        public Graph.Edge getEdge() {
            if (i == -1 || i > getNumChildren()) {
                throw new IllegalStateException();
            }
            return (i == 0) ? createEdge(parentIndex, nodeIndex) : createEdge(nodeIndex, baseChildIndex + i);
        }

        public void removeEdge() {
            throw new UnsupportedOperationException();
        }
    }

}