001    package ps1;
002    
003    /**
004     * <b>RatTerm</b> is an immutable representation of a term in a single-variable
005     * polynomial expression. The term has the form C*x^E where C is a rational
006     * number and E is an integer.
007     * <p>
008     * 
009     * A RatTerm, t, can be notated by the pair (C . E), where C is the coefficient
010     * of t, and E is the exponent of t.
011     * <p>
012     * 
013     * The zero RatTerm, (0 . 0), is the only RatTerm that may have a zero
014     * coefficient. For example, (0 . 7) is an invalid RatTerm and an attempt to
015     * construct such a RatTerm (through the constructor or arithmetic operations on
016     * existing RatTerms) will return the semantically equivalent RatTerm (0 . 0).
017     * For example, (1 . 7) + (-1 . 7) = (0 . 0).
018     * <p>
019     * 
020     * (0 . 0), (1 . 0), (1 . 1), (1 . 3), (3/4 . 17), (7/2 . -1), and (NaN . 74)
021     * are all valid RatTerms, corresponding to the polynomial terms "0", "1", "x",
022     * "x^3", "3/4*x^17", "7/2*x^-1" and "NaN*x^74", respectively.
023     */
024    // See RatNum's documentation for a definition of "immutable".
025    public final class RatTerm {
026    
027        /** Coefficient of this term. */
028        private final RatNum coeff;
029    
030        /** Exponent of this term. */
031        private final int expt;
032    
033        // Abstraction Function:
034        // For a given RatTerm t, "coefficient of t" is synonymous with
035        // t.coeff, and, likewise, "exponent of t" is synonymous with t.expt.
036        // All RatTerms with a zero coefficient are represented by the
037        // zero RatTerm, z, which has zero for its coefficient AND exponent.
038        //
039        // Representation Invariant:
040        // coeff != null
041        // coeff.equals(RatNum.ZERO) ==> expt == 0
042    
043        /** A constant holding a Not-a-Number (NaN) value of type RatTerm */
044        public static final RatTerm NaN = new RatTerm(RatNum.NaN, 0);
045    
046        /** A constant holding a zero value of type RatNum */
047        public static final RatTerm ZERO = new RatTerm(RatNum.ZERO, 0);
048    
049        /**
050         * @requires c != null
051         * @effects Constructs a new RatTerm t, with t.coeff = c, and if
052         *          c.equals(RatNum.ZERO), then t.expt = 0, otherwise t.expt = e
053         */
054        public RatTerm(RatNum c, int e) {
055            if (c.equals(RatNum.ZERO)) {
056                // If coefficient is zero, must set exponent to zero.
057                coeff = RatNum.ZERO;
058                expt = 0;
059            } else {
060                coeff = c;
061                expt = e;
062            }
063            checkRep();
064        }
065    
066        /**
067         * Gets the coefficient of this RatTerm.
068         * 
069         * @return the coefficient of this RatTerm.
070         */
071        public RatNum getCoeff() {
072            // TODO: Fill in this method, then remove the RuntimeException
073            throw new RuntimeException("RatTerm->getCoeff() unimplemented!");
074        }
075    
076        /**
077         * Gets the exponent of this RatTerm.
078         * 
079         * @return the exponent of this RatTerm.
080         */
081        public int getExpt() {
082            // TODO: Fill in this method, then remove the RuntimeException
083            throw new RuntimeException("RatTerm->getExpt() unimplemented!");
084        }
085    
086        /**
087         * Returns true if this RatTerm is not-a-number.
088         * 
089         * @return true if and only if this has NaN as a coefficient.
090         */
091        public boolean isNaN() {
092            // TODO: Fill in this method, then remove the RuntimeException
093            throw new RuntimeException("RatTerm->isNaN() unimplemented!");
094        }
095    
096        /**
097         * Returns true if this RatTerm is equal to 0.
098         * 
099         * @return true if and only if this has zero as a coefficient.
100         */
101        public boolean isZero() {
102            // TODO: Fill in this method, then remove the RuntimeException
103            throw new RuntimeException("RatTerm->isZero() unimplemented!");
104        }
105    
106        /**
107         * Returns the value of this RatTerm, evaluated at d.
108         * 
109         * @return the value of this polynomial when evaluated at 'd'. For example,
110         *         "3*x^2" evaluated at 2 is 12. if (this.isNaN() == true), return
111         *         Double.NaN
112         */
113        public double eval(double d) {
114            // TODO: Fill in this method, then remove the RuntimeException
115            // Hint: You may find java.lang.Math's pow() method useful.
116            throw new RuntimeException("RatTerm->eval() unimplemented!");
117        }
118    
119        /**
120         * Negation operation.
121         * 
122         * @return a RatTerm equals to (-this). If this is NaN, then returns NaN.
123         */
124        public RatTerm negate() {
125            // TODO: Fill in this method, then remove the RuntimeException
126            throw new RuntimeException("RatTerm->negate() unimplemented!");
127        }
128    
129        /**
130         * Addition operation.
131         * 
132         * @requires (arg != null) && ((this.expt == arg.expt) || (this.isZero() ||
133         *           arg.isZero() || this.isNaN() || arg.isNaN())).
134         * @return a RatTerm equals to (this + arg). If either argument is NaN, then
135         *         returns NaN.
136         * @throws IllegalArgumentException
137         *             if (this.expt != arg.expt) and neither argument is zero or
138         *             NaN.
139         */
140        public RatTerm add(RatTerm arg) {
141            // TODO: Fill in this method, then remove the RuntimeException
142            throw new RuntimeException("RatTerm->add() unimplemented!");
143        }
144    
145        /**
146         * Subtraction operation.
147         * 
148         * @requires (arg != null) && ((this.expt == arg.expt) || (this.isZero() ||
149         *           arg.isZero() || this.isNaN() || arg.isNaN())).
150         * @return a RatTerm equals to (this - arg). If either argument is NaN, then
151         *         returns NaN.
152         * @throws IllegalArgumentException
153         *             if (this.expt != arg.expt) and neither argument is zero or
154         *             NaN.
155         */
156        public RatTerm sub(RatTerm arg) {
157            // TODO: Fill in this method, then remove the RuntimeException
158            throw new RuntimeException("RatTerm->sub() unimplemented!");
159        }
160    
161        /**
162         * Multiplication operation.
163         * 
164         * @requires arg != null
165         * @return a RatTerm equals to (this * arg). If either argument is NaN, then
166         *         returns NaN.
167         */
168        public RatTerm mul(RatTerm arg) {
169            // TODO: Fill in this method, then remove the RuntimeException
170            throw new RuntimeException("RatTerm->mul() unimplemented!");
171        }
172    
173        /**
174         * Division operation.
175         * 
176         * @requires arg != null
177         * @return a RatTerm equals to (this / arg). If arg is zero, or if either
178         *         argument is NaN, then returns NaN.
179         */
180        public RatTerm div(RatTerm arg) {
181            // TODO: Fill in this method, then remove the RuntimeException
182            throw new RuntimeException("RatTerm->div() unimplemented!");
183        }
184    
185        /**
186         * Return the derivative of this RatTerm.
187         * 
188         * @return a RatTerm that, q, such that q = dy/dx, where this == y. In other
189         *         words, q is the derivative of this. If this.isNaN(), then return
190         *         some q such that q.isNaN()
191         *         <p>
192         *         Given a term, a*x^b, the derivative of the term is: (a*b)*x^(b-1)
193         *         for b > 0 and 0 for b == 0 (Do not worry about the case when b <
194         *         0. The caller of this function, RatPoly, contains a rep.
195         *         invariant stating that b is never less than 0.)
196         */
197        public RatTerm differentiate() {
198            // TODO: Fill in this method, then remove the RuntimeException
199            throw new RuntimeException("RatTerm->differentiate() unimplemented!");
200        }
201    
202        /**
203         * Returns the antiderivative of this RatTerm.
204         * 
205         * @return a RatTerm, q, such that dq/dx = this where the constant of
206         *         intergration is assumed to be 0. In other words, q is the
207         *         antiderivative of this. If this.isNaN(), then return some q such
208         *         that q.isNaN()
209         *         <p>
210         *         Given a term, a*x^b, (where b >= 0) the antiderivative of the
211         *         term is: a/(b+1)*x^(b+1) (Do not worry about the case when b < 0.
212         *         The caller of this function, RatPoly, contains a rep. invariant
213         *         stating that b is never less than 0.)
214         */
215        public RatTerm antiDifferentiate() {
216            // TODO: Fill in this method, then remove the RuntimeException
217            throw new RuntimeException(
218                    "RatTerm->antiDifferentiate() unimplemented!");
219        }
220    
221        private static final RatNum ONE = new RatNum(1);
222    
223        /**
224         * Returns a string representation of this RatTerm.
225         * 
226         * @return A String representation of the expression represented by this.
227         *         <p>
228         *         There is no whitespace in the returned string.
229         *         <p>
230         *         If the term is itself zero, the returned string will just be "0".
231         *         <p>
232         *         If this.isNaN(), then the returned string will be just "NaN"
233         *         <p>
234         * 
235         * The string for a non-zero, non-NaN RatTerm is in the form "C*x^E" where C
236         * is a valid string representation of a RatNum (see {@link ps1.RatNum}'s
237         * toString method) and E is an integer. UNLESS: (1) the exponent E is zero,
238         * in which case T takes the form "C" (2) the exponent E is one, in which
239         * case T takes the form "C*x" (3) the coefficient C is one, in which case T
240         * takes the form "x^E" or "x" (if E is one) or "1" (if E is zero).
241         * <p>
242         * Valid example outputs include "3/2*x^2", "-1/2", "0", and "NaN".
243         */
244        @Override
245        public String toString() {
246            if (this.isNaN()) {
247                return "NaN";
248            }
249            StringBuilder output = new StringBuilder();
250            RatNum c = coeff;
251            int e = expt;
252            if (c.isNegative()) {
253                output.append("-");
254                c = c.negate();
255            }
256            if (c.equals(ONE) && e == 1) {
257                output.append("x");
258            } else if (e == 0) {
259                output.append(c.toString());
260            } else if (c.equals(ONE)) {
261                output.append("x^" + e);
262            } else if (e == 1) {
263                output.append(c.toString() + "*x");
264            } else {
265                output.append(c.toString() + "*x^" + e);
266            }
267            return output.toString();
268        }
269    
270        /**
271         * Builds a new RatTerm, given a descriptive String.
272         * 
273         * @requires 'termStr' is an instance of a string with no spaces that
274         *           expresses a RatTerm in the form defined in the toString()
275         *           method.
276         *           <p>
277         * 
278         * Valid inputs include "0", "x", and "-5/3*x^3", and "NaN".
279         * 
280         * @return a RatTerm t such that t.toString() = termStr
281         */
282        public static RatTerm valueOf(String termStr) {
283    
284            if (termStr.equals("NaN")) {
285                return NaN;
286            }
287    
288            // Term is: "R" or "R*x" or "R*x^N" or "x^N" or "x",
289            // where R is a rational num and N is an integer.
290    
291            // First we parse the coefficient
292            int multIndex = termStr.indexOf("*");
293            RatNum coeff = null;
294            if (multIndex == -1) {
295                // "R" or "x^N" or "x"
296                int xIndex = termStr.indexOf("x");
297                if (xIndex == -1) {
298                    // "R"
299                    coeff = RatNum.valueOf(termStr);
300                } else {
301                    int negIndex = termStr.indexOf("-");
302                    // "x^N" or "x" ==> coeff = 1
303                    if (negIndex == -1) {
304                        coeff = new RatNum(1);
305                    }
306                    // "-x^N" or "-x" ==> coeff = -1
307                    else if (negIndex == 0) {
308                        coeff = new RatNum(-1);
309                    } else {
310                        throw new RuntimeException(
311                                "Minus sign, '-', not allowed in the middle of input string: "
312                                        + termStr);
313                    }
314                }
315            } else {
316                // "R*x" or "R*x^N"
317                coeff = RatNum.valueOf(termStr.substring(0, multIndex));
318            }
319    
320            // Second we parse the exponent
321            int powIndex = termStr.indexOf("^");
322            int expt;
323            if (powIndex == -1) {
324                // "R" or "R*x" or "x"
325                int xIndex = termStr.indexOf("x");
326                if (xIndex == -1) {
327                    // "R"
328                    expt = 0;
329                } else {
330                    // "R*x" or "x"
331                    expt = 1;
332                }
333            } else {
334                // "R*x^N" or "x^N"
335                expt = Integer.parseInt(termStr.substring(powIndex + 1));
336            }
337            return new RatTerm(coeff, expt);
338        }
339    
340        /**
341         * Standard hashCode function.
342         * 
343         * @return an int that all objects equal to this will also.
344         */
345        @Override
346        public int hashCode() {
347            if (this.isNaN()) {
348                return 0;
349            }
350            return coeff.hashCode() * 7 + expt * 43;
351        }
352    
353        /**
354         * Standard equality operation.
355         * 
356         * @return true iff 'obj' is an instance of a RatTerm and 'this' and 'obj'
357         *         represent the same RatTerm. Note that all NaN RatTerms are equal.
358         */
359        @Override
360        public boolean equals(Object obj) {
361            if (obj instanceof RatTerm) {
362                RatTerm rt = (RatTerm) obj;
363                if (this.isNaN() && rt.isNaN()) {
364                    return true;
365                } else {
366                    return this.expt == rt.expt && this.coeff.equals(rt.coeff);
367                }
368            } else {
369                return false;
370            }
371        }
372    
373        /**
374         * Checks that the representation invariant holds (if any).
375         */
376        // Throws a RuntimeException if the rep invariant is violated.
377        private void checkRep() throws RuntimeException {
378            if (coeff == null) {
379                throw new RuntimeException("coeff == null");
380            }
381            if (coeff.equals(RatNum.ZERO) && expt != 0) {
382                throw new RuntimeException("coeff is zero while expt == " + expt);
383            }
384        }
385    }