001    package ps3.graph;
002    
003    import java.util.*;
004    
005    /**
006     * A WeightedNodePath characterizes a path of WeightedNodes.  The cost
007     * for a path is the sum of the costs of the WeightedNodes it contains.<p>
008     *
009     * A WeightedNodePath is immutable. A new WeightedNodePath is returned through
010     * the extend path operation. <p>
011     *
012     * Specfields inherited from Path:
013     *
014     * @specfield  cost :     double       // cost of traversing this path.
015     * @specfield  elements : sequence     // sequence of nodes in this path.
016     *
017     **/
018    
019    public class WeightedNodePath implements Path<WeightedNode, WeightedNodePath> {
020    
021        // The representation invariant describes what invariants hold for
022        // the concrete representation.
023        //
024        // RepInvariant:
025        //  RI(c) =
026        //   (c.node != null) &&
027        //   (c.path == null) ==> (c.cost == c.node.cost) &&
028        //   (c.path != null) ==> (c.cost == c.node.cost + c.path.cost)
029        //
030    
031        //
032        // Abstraction Function:
033        //
034        //  The abstract state is given in terms of the specfields of the
035        //  Path interface, namely the cost and elements of a path.
036        //
037        //  The AF uses two helper functions, which map a concrete state
038        //  'c' to the abstract state.
039        //
040        //  AF(c) = < wnpcost(c), wnpelms(c) >
041        //  (Maps c to wnp cost and wnp elements abstract fields recursively)
042        //
043        //    wnpcost(c) = c.cost
044        //    wnpelms(c) = / [c.node]                   if (c.path == null)
045        //                 \ wnpelms(c.path) + [c.node] if (c.path != null)
046        //
047        //  (Note that [c.node] appears at the *end* not the *start* of the
048        //   path sequence.)
049        //
050        //  To make the AF(c) clearer, we could also write the following:
051        //  AF(c) = < cost, elements >  where
052        //   cost     = c.cost
053        //   elements = [c.node]                     if c.path == null
054        //              [c.node] + c.path.elements   if c.path != null
055        //
056    
057        /** The WeightedNode at the end of the path. */
058        private final WeightedNode node;
059        /** A WeightedNodePath which, when extended with 'node' at the end,
060         *  is equal to this.  May be null iff this path has only 1 element. */
061        private final /*@Nullable*/ WeightedNodePath path;
062        /** The cost of this WeightedNodePath. */
063        private final int cost;
064    
065    
066        /**
067         * Constructs a WeightedNodePath containing one node.
068         *
069         * @requires node != null
070         * @effects Creates a new WeightedNodePath which originates at
071         * <code>node</code>.
072         **/
073        public WeightedNodePath(WeightedNode node) {
074            this(node, null);
075        }
076    
077        /**
078         * @requires node != null
079         * @effects Creates a new WeightedNodePath 'res' such that
080         * res.elements = path.elements + [ node ]
081         **/
082        private WeightedNodePath(WeightedNode node,
083                    /*@Nullable*/ WeightedNodePath path) {
084            if (node == null) {
085                throw new IllegalArgumentException();
086            }
087            this.node = node;
088            this.path = path;
089            if (path != null) {
090                this.cost = node.cost + path.cost;
091            } else {
092                this.cost = node.cost;
093            }
094        }
095    
096    
097        // Specified by Path interface.
098        public WeightedNodePath extend(WeightedNode node) {
099            return new WeightedNodePath(node, this);
100        }
101    
102        // Specified by Path interface.
103        public double cost() {
104            return cost;
105        }
106    
107        // Specified by Path interface (which extends Iterable)
108        public Iterator<WeightedNode> iterator() {
109            // reverse the linked list, so that elements are returned in order
110            // from start to end of the path.
111            List<WeightedNode> accumulator = new LinkedList<WeightedNode>();
112            for (WeightedNodePath cur = this; cur!=null; cur = cur.path) {
113                accumulator.add(0, cur.end());
114            }
115            return accumulator.iterator();
116        }
117    
118        /**
119         * @return a string representation of this.
120         **/
121        public String toString() {
122            StringBuilder sb = new StringBuilder();
123            sb.append("[WeightedNodePath: ");
124            boolean first=true;
125            for (WeightedNode wn : this) {
126                if (first) first = false;
127                else sb.append(", ");
128                sb.append(wn);
129            }
130            sb.append("]");
131            return sb.toString();
132        }
133    
134        /**
135         * @return true iff o is a WeightedNodePath and o.elements is the
136         * same sequence as this.elements
137         **/
138        @Override
139        public boolean equals(/*@Nullable*/ Object o) {
140            if (!(o instanceof WeightedNodePath))
141                return false;
142            return this.equals((WeightedNodePath) o);
143        }
144    
145        /**
146         * @return true iff wnp.elements is the same sequence as this.elements
147         **/
148        public boolean equals(WeightedNodePath wnp) {
149            return (wnp != null) &&
150                this.node.equals(wnp.node) &&
151                (this.path == null ? wnp.path==null : this.path.equals(wnp.path));
152        }
153    
154        /**
155         * @return a valid hashcode for this.
156         **/
157        public int hashCode() {
158            return node.hashCode() + (this.path==null ? 0 : 13 * path.hashCode());
159        }
160    
161    
162        /**
163         * Compares the cost of this path to the given path.
164         * @return the value 0 if the cost of this path is equal to the
165         *         cost of the given path; a value less than 0 if this.cost is
166         *         less than p.cost; and a value greater than 0 if this.cost
167         *         is greater than p.cost.
168         * @see java.lang.Comparable#compareTo
169         */
170        public int compareTo(Path<?,?> p) {
171            return Double.compare(this.cost(), p.cost());
172        }
173    
174        /**
175         * Return the end of this path
176         */
177        public WeightedNode end() {
178            return node;
179        }
180    }