001package hw3;
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".
025public 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 RatTerm */
047    public static final RatTerm ZERO = new RatTerm(RatNum.ZERO, 0);
048
049    /** A constant holding a one value of type RatTerm */
050    private static final RatNum ONE = new RatNum(1);
051
052    /**
053     * @param c the coefficient of the RatTerm to be constructed.
054     * @param e the exponent of the RatTerm to be constructed.
055     * @requires c != null
056     * @effects Constructs a new RatTerm t, with t.coeff = c, and if
057     *          c.equals(RatNum.ZERO), then t.expt = 0, otherwise t.expt = e
058     */
059    public RatTerm(RatNum c, int e) {
060        // TODO: Fill in this method, then remove the RuntimeException
061        throw new RuntimeException("RatTerm->constructor is not yet implemented");
062    }
063
064    /**
065     * Gets the coefficient of this RatTerm.
066     *
067     * @return the coefficient of this RatTerm.
068     */
069    public RatNum getCoeff() {
070        // TODO: Fill in this method, then remove the RuntimeException
071        throw new RuntimeException("RatTerm->getCoeff() is not yet implemented");
072    }
073
074    /**
075     * Gets the exponent of this RatTerm.
076     *
077     * @return the exponent of this RatTerm.
078     */
079    public int getExpt() {
080        // TODO: Fill in this method, then remove the RuntimeException
081        throw new RuntimeException("RatTerm->getExpt() is not yet implemented");
082    }
083
084    /**
085     * Returns true if this RatTerm is not-a-number.
086     *
087     * @return true if and only if this has NaN as a coefficient.
088     */
089    public boolean isNaN() {
090        // TODO: Fill in this method, then remove the RuntimeException
091        throw new RuntimeException("RatTerm->isNaN() is not yet implemented");
092    }
093
094    /**
095     * Returns true if this RatTerm is equal to 0.
096     *
097     * @return true if and only if this has zero as a coefficient.
098     */
099    public boolean isZero() {
100        // TODO: Fill in this method, then remove the RuntimeException
101        throw new RuntimeException("RatTerm->isZero() is not yet implemented");
102    }
103
104    /**
105     * Returns the value of this RatTerm, evaluated at d.
106     *
107     * @param d The value at which to evaluate this term.
108     * @return the value of this polynomial when evaluated at 'd'. For example,
109     *         "3*x^2" evaluated at 2 is 12. if (this.isNaN() == true), return
110     *         Double.NaN
111     */
112    public double eval(double d) {
113        // TODO: Fill in this method, then remove the RuntimeException
114        // Hint: You may find java.lang.Math's pow() method useful.
115        throw new RuntimeException("RatTerm->eval() is not yet implemented");
116    }
117
118    /**
119     * Negation operation.
120     *
121     * @return a RatTerm equals to (-this). If this is NaN, then returns NaN.
122     */
123    public RatTerm negate() {
124        // TODO: Fill in this method, then remove the RuntimeException
125        throw new RuntimeException("RatTerm->negate() is not yet implemented");
126    }
127
128    /**
129     * Addition operation.
130     *
131     * @param p The other value to be added.
132     * @requires arg != null
133     * @return a RatTerm equals to (this + arg). If either argument is NaN, then
134     *         returns NaN.
135     * @throws IllegalArgumentException
136     *             if (this.expt != arg.expt) and neither argument is zero or
137     *             NaN.
138     */
139    public RatTerm add(RatTerm p) {
140        // TODO: Fill in this method, then remove the RuntimeException
141        throw new RuntimeException("RatTerm->add() is not yet implemented");
142    }
143
144    /**
145     * Subtraction operation.
146     *
147     * @param p The value to be subtracted.
148     * @requires arg != null
149     * @return a RatTerm equals to (this - arg). If either argument is NaN, then
150     *         returns NaN.
151     * @throws IllegalArgumentException
152     *             if (this.expt != arg.expt) and neither argument is zero or
153     *             NaN.
154     */
155    public RatTerm sub(RatTerm p) {
156        // TODO: Fill in this method, then remove the RuntimeException
157        throw new RuntimeException("RatTerm->sub() is not yet implemented");
158    }
159
160    /**
161     * Multiplication operation.
162     *
163     * @param p The other value to be multiplied.
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 p) {
169        // TODO: Fill in this method, then remove the RuntimeException
170        throw new RuntimeException("RatTerm->mul() is not yet implemented");
171    }
172
173    /**
174     * Division operation.
175     *
176     * @param p The divisor.
177     * @requires arg != null
178     * @return a RatTerm equals to (this / arg). If arg is zero, or if either
179     *         argument is NaN, then returns NaN.
180     */
181    public RatTerm div(RatTerm p) {
182        // TODO: Fill in this method, then remove the RuntimeException
183        throw new RuntimeException("RatTerm->div() is not yet implemented");
184    }
185
186    /**
187     * Return the derivative of this RatTerm.
188     *
189     * @return a RatTerm that, q, such that q = dy/dx, where this == y. In other
190     *         words, q is the derivative of this. If this.isNaN(), then return
191     *         some q such that q.isNaN()
192     *         <p>
193     *         Given a term, a*x^b, the derivative of the term is: (a*b)*x^(b-1)
194     *         for b > 0 and 0 for b == 0 (Do not worry about the case when b <
195     *         0. The caller of this function, RatPoly, contains a rep.
196     *         invariant stating that b is never less than 0.)
197     */
198    public RatTerm differentiate() {
199        // TODO: Fill in this method, then remove the RuntimeException
200        throw new RuntimeException("RatTerm->differentiate() is not yet implemented");
201    }
202
203    /**
204     * Returns the antiderivative of this RatTerm.
205     *
206     * @return a RatTerm, q, such that dq/dx = this where the constant of
207     *         intergration is assumed to be 0. In other words, q is the
208     *         antiderivative of this. If this.isNaN(), then return some q such
209     *         that q.isNaN()
210     *         <p>
211     *         Given a term, a*x^b, (where b >= 0) the antiderivative of the
212     *         term is: a/(b+1)*x^(b+1) (Do not worry about the case when b < 0.
213     *         The caller of this function, RatPoly, contains a rep. invariant
214     *         stating that b is never less than 0.)
215     */
216    public RatTerm antiDifferentiate() {
217        // TODO: Fill in this method, then remove the RuntimeException
218        throw new RuntimeException(
219                "RatTerm->antiDifferentiate() unimplemented!");
220    }
221
222    /**
223     * Returns a string representation of this RatTerm.
224     *
225     * @return A String representation of the expression represented by this.
226     *         <p>
227     *         There is no whitespace in the returned string.
228     *         <p>
229     *         If the term is itself zero, the returned string will just be "0".
230     *         <p>
231     *         If this.isNaN(), then the returned string will be just "NaN"
232     *         <p>
233     *
234     * The string for a non-zero, non-NaN RatTerm is in the form "C*x^E" where C
235     * is a valid string representation of a RatNum (see {@link hw3.RatNum}'s
236     * toString method) and E is an integer. UNLESS: (1) the exponent E is zero,
237     * in which case T takes the form "C" (2) the exponent E is one, in which
238     * case T takes the form "C*x" (3) the coefficient C is one, in which case T
239     * takes the form "x^E" or "x" (if E is one) or "1" (if E is zero).
240     * <p>
241     * Valid example outputs include "3/2*x^2", "-1/2", "0", and "NaN".
242     */
243    @Override
244    public String toString() {
245        if (this.isNaN()) {
246            return "NaN";
247        }
248        StringBuilder output = new StringBuilder();
249        RatNum c = coeff;
250        int e = expt;
251        if (c.isNegative()) {
252            output.append("-");
253            c = c.negate();
254        }
255        if (c.equals(ONE) && e == 1) {
256            output.append("x");
257        } else if (e == 0) {
258            output.append(c.toString());
259        } else if (c.equals(ONE)) {
260            output.append("x^" + e);
261        } else if (e == 1) {
262            output.append(c.toString() + "*x");
263        } else {
264            output.append(c.toString() + "*x^" + e);
265        }
266        return output.toString();
267    }
268
269    /**
270     * Builds a new RatTerm, given a descriptive String.
271     *
272     * @param termStr A string of the format described in the @requires clause.
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     * @param obj The object to be compared for equality.
357     * @return true iff 'obj' is an instance of a RatTerm and 'this' and 'obj'
358     *         represent the same RatTerm. Note that all NaN RatTerms are equal.
359     */
360    @Override
361    public boolean equals(/*@Nullable*/ Object obj) {
362        if (obj instanceof RatTerm) {
363            RatTerm rt = (RatTerm) obj;
364            if (this.isNaN() && rt.isNaN()) {
365                return true;
366            } else {
367                return this.expt == rt.expt && this.coeff.equals(rt.coeff);
368            }
369        } else {
370            return false;
371        }
372    }
373
374    /**
375     * Checks that the representation invariant holds (if any).
376     * Throws an exception if the rep invariant is violated.
377     */
378    private void checkRep() throws RuntimeException {
379       // assert coeff != null: "coeff == null";
380       // assert !(coeff.equals(RatNum.ZERO) && expt != 0): "coeff is zero while expt == " + expt;
381        
382        if (coeff == null) {
383            throw new RuntimeException("coeff == null");
384        }
385        if (coeff.equals(RatNum.ZERO) && expt != 0) {
386            throw new RuntimeException("coeff is zero while expt == " + expt);
387        }
388    }
389}