001package hw3;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.StringTokenizer;
006
007/**
008 * <b>RatPoly</b> represents an immutable single-variate polynomial expression.
009 * RatPolys are sums of RatTerms with non-negative exponents.
010 * <p>
011 *
012 * Examples of RatPolys include "0", "x-10", and "x^3-2*x^2+5/3*x+3", and "NaN".
013 */
014// See RatNum's documentation for a definition of "immutable".
015public final class RatPoly {
016
017    /** Holds all the RatTerms in this RatPoly */
018    private final List<RatTerm> terms;
019
020    // Definitions:
021    // For a RatPoly p, let C(p,i) be "p.terms.get(i).getCoeff()" and
022    // E(p,i) be "p.terms.get(i).getExpt()"
023    // length(p) be "p.terms.size()"
024    // (These are helper functions that will make it easier for us
025    // to write the remainder of the specifications. They are not
026    // executable code; they just represent complex expressions in a
027    // concise manner, so that we can stress the important parts of
028    // other expressions in the spec rather than get bogged down in
029    // the details of how we extract the coefficient for the 2nd term
030    // or the exponent for the 5th term. So when you see C(p,i),
031    // think "coefficient for the ith term in p".)
032    //
033    // Abstraction Function:
034    // RatPoly, p, represents the polynomial equal to the sum of the
035    // RatTerms contained in 'terms':
036    // sum (0 <= i < length(p)): p.terms.get(i)
037    // If there are no terms, then the RatPoly represents the zero
038    // polynomial.
039    //
040    // Representation Invariant for every RatPoly p:
041    // terms != null &&
042    // forall i such that (0 <= i < length(p)), C(p,i) != 0 &&
043    // forall i such that (0 <= i < length(p)), E(p,i) >= 0 &&
044    // forall i such that (0 <= i < length(p) - 1), E(p,i) > E(p, i+1)
045    // In other words:
046    // * The terms field always points to some usable object.
047    // * No term in a RatPoly has a zero coefficient.
048    // * No term in a RatPoly has a negative exponent.
049    // * The terms in a RatPoly are sorted in descending exponent order.
050    // (It is implied that 'terms' does not contain any null elements by the
051    // above
052    // invariant.)
053
054    /** A constant holding a Not-a-Number (NaN) value of type RatPoly */
055    public static final RatPoly NaN = new RatPoly(RatTerm.NaN);
056
057    /** A constant holding a zero value of type RatPoly */
058    public static final RatPoly ZERO = new RatPoly();
059
060    /**
061     * @effects Constructs a new Poly, "0".
062     */
063    public RatPoly() {
064        terms = new ArrayList<RatTerm>();
065        checkRep();
066    }
067
068    /**
069     * @param rt The single term which the new RatPoly equals.
070     * @requires rt.getExpt() >= 0
071     * @effects Constructs a new Poly equal to "rt". If rt.getCoeff() is zero,
072     *          constructs a "0" polynomial.
073     */
074    public RatPoly(RatTerm rt) {
075        // TODO: Fill in this method, then remove the RuntimeException
076        throw new RuntimeException("RatPoly constructor is not yet implemented");
077    }
078
079    /**
080     * @param c The constant in the term which the new RatPoly equals.
081     * @param e The exponent in the term which the new RatPoly equals.
082     * @requires e >= 0
083     * @effects Constructs a new Poly equal to "c*x^e". If c is zero, constructs
084     *          a "0" polynomial.
085     */
086    public RatPoly(int c, int e) {
087        // TODO: Fill in this method, then remove the RuntimeException
088        throw new RuntimeException("RatPoly constructor is not yet implemented");
089    }
090
091    /**
092     * @param rt A list of terms to be contained in the new RatPoly.
093     * @requires 'rt' satisfies clauses given in rep. invariant
094     * @effects Constructs a new Poly using 'rt' as part of the representation.
095     *          The method does not make a copy of 'rt'.
096     */
097    private RatPoly(List<RatTerm> rt) {
098        terms = rt;
099        // The spec tells us that we don't need to make a copy of 'rt'
100        checkRep();
101    }
102
103    /**
104     * Returns the degree of this RatPoly.
105     *
106     * @requires !this.isNaN()
107     * @return the largest exponent with a non-zero coefficient, or 0 if this is
108     *         "0".
109     */
110    public int degree() {
111        // TODO: Fill in this method, then remove the RuntimeException
112        throw new RuntimeException("RatPoly->degree() is not yet implemented");
113    }
114
115    /**
116     * Gets the RatTerm associated with degree 'deg'
117     *
118     * @param deg The degree for which to find the corresponding RatTerm.
119     * @requires !this.isNaN()
120     * @return the RatTerm of degree 'deg'. If there is no term of degree 'deg'
121     *         in this poly, then returns the zero RatTerm.
122     */
123    public RatTerm getTerm(int deg) {
124        // TODO: Fill in this method, then remove the RuntimeException
125        throw new RuntimeException("RatPoly->getTerm() is not yet implemented");
126    }
127
128    /**
129     * Returns true if this RatPoly is not-a-number.
130     *
131     * @return true if and only if this has some coefficient = "NaN".
132     */
133    public boolean isNaN() {
134        // TODO: Fill in this method, then remove the RuntimeException
135        throw new RuntimeException("RatPoly->isNaN() is not yet implemented");
136    }
137
138    /**
139     * Scales coefficients within 'lst' by 'scalar' (helper procedure).
140     *
141     * @param lst The RatTerms to be scaled.
142     * @param scalar the value by which to scale coefficients in lst.
143     * @requires lst, scalar != null
144     * @modifies lst
145     * @effects Forall i s.t. 0 <= i < lst.size(), if lst.get(i) = (C . E) then
146     *          lst_post.get(i) = (C*scalar . E)
147     * @see hw3.RatTerm regarding (C . E) notation
148     */
149    private static void scaleCoeff(List<RatTerm> lst, RatNum scalar) {
150        // TODO: Fill in this method, then remove the RuntimeException
151        throw new RuntimeException("RatPoly->scaleCoeff() is not yet implemented");
152    }
153
154    /**
155     * Increments exponents within 'lst' by 'degree' (helper procedure).
156     *
157     * @param lst The RatTerms whose exponents are to be incremented.
158     * @param degree the value by which to increment exponents in lst.
159     * @requires lst != null
160     * @modifies lst
161     * @effects Forall i s.t. 0 <= i < lst.size(), if (C . E) = lst.get(i) then
162     *          lst_post.get(i) = (C . E+degree)
163     * @see hw3.RatTerm regarding (C . E) notation
164     */
165    private static void incremExpt(List<RatTerm> lst, int degree) {
166        // TODO: Fill in this method, then remove the RuntimeException
167        throw new RuntimeException("RatPoly->incremExpt() is not yet implemented");
168    }
169
170    /**
171     * Helper procedure: Inserts a term into a sorted sequence of terms,
172     * preserving the sorted nature of the sequence. If a term with the given
173     * degree already exists, adds their coefficients.
174     *
175     * Definitions: Let a "Sorted List<RatTerm>" be a List<RatTerm> V such
176     * that [1] V is sorted in descending exponent order && [2] there are no two
177     * RatTerms with the same exponent in V && [3] there is no RatTerm in V with
178     * a coefficient equal to zero
179     *
180     * For a Sorted List<RatTerm> V and integer e, let cofind(V, e) be either
181     * the coefficient for a RatTerm rt in V whose exponent is e, or zero if
182     * there does not exist any such RatTerm in V. (This is like the coeff
183     * function of RatPoly.) We will write sorted(lst) to denote that lst is a
184     * Sorted List<RatTerm>, as defined above.
185     *
186     * @param lst The list into which newTerm should be inserted.
187     * @param newTerm The term to be inserted into the list.
188     * @requires lst != null && sorted(lst)
189     * @modifies lst
190     * @effects sorted(lst_post) && (cofind(lst_post,newTerm.getExpt()) =
191     *          cofind(lst,newTerm.getExpt()) + newTerm.getCoeff())
192     */
193    private static void sortedInsert(List<RatTerm> lst, RatTerm newTerm) {
194        // TODO: Fill in this method, then remove the RuntimeException
195        throw new RuntimeException("RatPoly->sortedInsert() is not yet implemented");
196    }
197
198    /**
199     * Return the additive inverse of this RatPoly.
200     *
201     * @return a RatPoly equal to "0 - this"; if this.isNaN(), returns some r
202     *         such that r.isNaN()
203     */
204    public RatPoly negate() {
205        // TODO: Fill in this method, then remove the RuntimeException
206        throw new RuntimeException("RatPoly->negate() is not yet implemented");
207    }
208
209    /**
210     * Addition operation.
211     *
212     * @param p The other value to be added.
213     * @requires p != null
214     * @return a RatPoly, r, such that r = "this + p"; if this.isNaN() or
215     *         p.isNaN(), returns some r such that r.isNaN()
216     */
217    public RatPoly add(RatPoly p) {
218        // TODO: Fill in this method, then remove the RuntimeException
219        throw new RuntimeException("RatPoly->add() is not yet implemented");
220    }
221
222    /**
223     * Subtraction operation.
224     *
225     * @param p The value to be subtracted.
226     * @requires p != null
227     * @return a RatPoly, r, such that r = "this - p"; if this.isNaN() or
228     *         p.isNaN(), returns some r such that r.isNaN()
229     */
230    public RatPoly sub(RatPoly p) {
231        // TODO: Fill in this method, then remove the RuntimeException
232        throw new RuntimeException("RatPoly->sub() is not yet implemented");
233    }
234
235    /**
236     * Multiplication operation.
237     *
238     * @param p The other value to be multiplied.
239     * @requires p != null
240     * @return a RatPoly, r, such that r = "this * p"; if this.isNaN() or
241     *         p.isNaN(), returns some r such that r.isNaN()
242     */
243    public RatPoly mul(RatPoly p) {
244        // TODO: Fill in this method, then remove the RuntimeException
245        throw new RuntimeException("RatPoly->mul() is not yet implemented");
246    }
247
248    /**
249     * Division operation (truncating).
250     *
251     * @param p The divisor.
252     * @requires p != null
253     * @return a RatPoly, q, such that q = "this / p"; if p = 0 or this.isNaN()
254     *         or p.isNaN(), returns some q such that q.isNaN()
255     *         <p>
256     *
257     * Division of polynomials is defined as follows: Given two polynomials u
258     * and v, with v != "0", we can divide u by v to obtain a quotient
259     * polynomial q and a remainder polynomial r satisfying the condition u = "q *
260     * v + r", where the degree of r is strictly less than the degree of v, the
261     * degree of q is no greater than the degree of u, and r and q have no
262     * negative exponents.
263     * <p>
264     *
265     * For the purposes of this class, the operation "u / v" returns q as
266     * defined above.
267     * <p>
268     *
269     * The following are examples of div's behavior: "x^3-2*x+3" / "3*x^2" =
270     * "1/3*x" (with r = "-2*x+3"). "x^2+2*x+15 / 2*x^3" = "0" (with r =
271     * "x^2+2*x+15"). "x^3+x-1 / x+1 = x^2-x+2 (with r = "-3").
272     * <p>
273     *
274     * Note that this truncating behavior is similar to the behavior of integer
275     * division on computers.
276     */
277    public RatPoly div(RatPoly p) {
278        // TODO: Fill in this method, then remove the RuntimeException
279        throw new RuntimeException("RatPoly->div() is not yet implemented");
280    }
281
282    /**
283     * Return the derivative of this RatPoly.
284     *
285     * @return a RatPoly, q, such that q = dy/dx, where this == y. In other
286     *         words, q is the derivative of this. If this.isNaN(), then return
287     *         some q such that q.isNaN()
288     *
289     * <p>
290     * The derivative of a polynomial is the sum of the derivative of each term.
291     */
292    public RatPoly differentiate() {
293        // TODO: Fill in this method, then remove the RuntimeException
294        throw new RuntimeException("RatPoly->differentiate() is not yet implemented");
295    }
296
297    /**
298     * Returns the antiderivative of this RatPoly.
299     *
300     * @param integrationConstant The constant of integration to use when
301     *  computating the antiderivative.
302     * @requires integrationConstant != null
303     * @return a RatPoly, q, such that dq/dx = this and the constant of
304     *         integration is "integrationConstant" In other words, q is the
305     *         antiderivative of this. If this.isNaN() or
306     *         integrationConstant.isNaN(), then return some q such that
307     *         q.isNaN()
308     *
309     * <p>
310     * The antiderivative of a polynomial is the sum of the antiderivative of
311     * each term plus some constant.
312     */
313    public RatPoly antiDifferentiate(RatNum integrationConstant) {
314        // TODO: Fill in this method, then remove the RuntimeException
315        throw new RuntimeException(
316                "RatPoly->antiDifferentiate() unimplemented!");
317    }
318
319    /**
320     * Returns the integral of this RatPoly, integrated from lowerBound to
321     * upperBound.
322     *
323     * <p>
324     * The Fundamental Theorem of Calculus states that the definite integral of
325     * f(x) with bounds a to b is F(b) - F(a) where dF/dx = f(x) NOTE: Remember
326     * that the lowerBound can be higher than the upperBound.
327     *
328     * @param lowerBound The lower bound of integration.
329     * @param upperBound The upper bound of integration.
330     * @return a double that is the definite integral of this with bounds of
331     *         integration between lowerBound and upperBound. If this.isNaN(),
332     *         or either lowerBound or upperBound is Double.NaN, return
333     *         Double.NaN.
334     */
335    public double integrate(double lowerBound, double upperBound) {
336        // TODO: Fill in this method, then remove the RuntimeException
337        throw new RuntimeException("RatPoly->integrate() is not yet implemented");
338    }
339
340    /**
341     * Returns the value of this RatPoly, evaluated at d.
342     *
343     * @param d The value at which to evaluate this polynomial.
344     * @return the value of this polynomial when evaluated at 'd'. For example,
345     *         "x+2" evaluated at 3 is 5, and "x^2-x" evaluated at 3 is 6. if
346     *         (this.isNaN() == true), return Double.NaN
347     */
348    public double eval(double d) {
349        // TODO: Fill in this method, then remove the RuntimeException
350        throw new RuntimeException("RatPoly->eval() is not yet implemented");
351    }
352
353    /**
354     * Returns a string representation of this RatPoly.
355     *
356     * @return A String representation of the expression represented by this,
357     *         with the terms sorted in order of degree from highest to lowest.
358     *         <p>
359     *         There is no whitespace in the returned string.
360     *         <p>
361     *         If the polynomial is itself zero, the returned string will just
362     *         be "0".
363     *         <p>
364     *         If this.isNaN(), then the returned string will be just "NaN"
365     *         <p>
366     *         The string for a non-zero, non-NaN poly is in the form
367     *         "(-)T(+|-)T(+|-)...", where "(-)" refers to a possible minus
368     *         sign, if needed, and "(+|-)" refer to either a plus or minus
369     *         sign, as needed. For each term, T takes the form "C*x^E" or "C*x"
370     *         where C > 0, UNLESS: (1) the exponent E is zero, in which case T
371     *         takes the form "C", or (2) the coefficient C is one, in which
372     *         case T takes the form "x^E" or "x". In cases were both (1) and
373     *         (2) apply, (1) is used.
374     *         <p>
375     *         Valid example outputs include "x^17-3/2*x^2+1", "-x+1", "-1/2",
376     *         and "0".
377     *         <p>
378     */
379    @Override
380    public String toString() {
381        if (terms.size() == 0) {
382            return "0";
383        }
384        if (isNaN()) {
385            return "NaN";
386        }
387        StringBuilder output = new StringBuilder();
388        boolean isFirst = true;
389        for (RatTerm rt : terms) {
390            if (isFirst) {
391                isFirst = false;
392                output.append(rt.toString());
393            } else {
394                if (rt.getCoeff().isNegative()) {
395                    output.append(rt.toString());
396                } else {
397                    output.append("+" + rt.toString());
398                }
399            }
400        }
401        return output.toString();
402    }
403
404    /**
405     * Builds a new RatPoly, given a descriptive String.
406     *
407     * @param polyStr A string of the format described in the @requires clause.
408     * @requires 'polyStr' is an instance of a string with no spaces that
409     *           expresses a poly in the form defined in the toString() method.
410     *           <p>
411     *
412     * Valid inputs include "0", "x-10", and "x^3-2*x^2+5/3*x+3", and "NaN".
413     *
414     * @return a RatPoly p such that p.toString() = polyStr
415     */
416    public static RatPoly valueOf(String polyStr) {
417
418        List<RatTerm> parsedTerms = new ArrayList<RatTerm>();
419
420        // First we decompose the polyStr into its component terms;
421        // third arg orders "+" and "-" to be returned as tokens.
422        StringTokenizer termStrings = new StringTokenizer(polyStr, "+-", true);
423
424        boolean nextTermIsNegative = false;
425        while (termStrings.hasMoreTokens()) {
426            String termToken = termStrings.nextToken();
427
428            if (termToken.equals("-")) {
429                nextTermIsNegative = true;
430            } else if (termToken.equals("+")) {
431                nextTermIsNegative = false;
432            } else {
433                // Not "+" or "-"; must be a term
434                RatTerm term = RatTerm.valueOf(termToken);
435
436                // at this point, coeff and expt are initialized.
437                // Need to fix coeff if it was preceeded by a '-'
438                if (nextTermIsNegative) {
439                    term = term.negate();
440                }
441
442                // accumulate terms of polynomial in 'parsedTerms'
443                sortedInsert(parsedTerms, term);
444            }
445        }
446        return new RatPoly(parsedTerms);
447    }
448
449    /**
450     * Standard hashCode function.
451     *
452     * @return an int that all objects equal to this will also return.
453     */
454    @Override
455    public int hashCode() {
456        // all instances that are NaN must return the same hashcode;
457        if (this.isNaN()) {
458            return 0;
459        }
460        return terms.hashCode();
461    }
462
463    /**
464     * Standard equality operation.
465     *
466     * @param obj The object to be compared for equality.
467     * @return true if and only if 'obj' is an instance of a RatPoly and 'this'
468     *         and 'obj' represent the same rational polynomial. Note that all
469     *         NaN RatPolys are equal.
470     */
471    @Override
472    public boolean equals(/*@Nullable*/ Object obj) {
473        if (obj instanceof RatPoly) {
474            RatPoly rp = (RatPoly) obj;
475
476            // special case: check if both are NaN
477            if (this.isNaN() && rp.isNaN()) {
478                return true;
479            } else {
480                return terms.equals(rp.terms);
481            }
482        } else {
483            return false;
484        }
485    }
486
487    /**
488     * Checks that the representation invariant holds (if any).
489     */
490    // Throws a RuntimeException if the rep invariant is violated.
491    private void checkRep() throws RuntimeException {
492        if (terms == null) {
493            throw new RuntimeException("terms == null");
494        }
495        for (int i = 0; i < terms.size(); i++) {
496            if (terms.get(i).getCoeff().equals(new RatNum(0))) {
497                throw new RuntimeException("zero coefficient");
498            }
499            if (terms.get(i).getExpt() < 0) {
500                throw new RuntimeException("negative exponent");
501            }
502            if (i < terms.size() - 1) {
503                if (terms.get(i + 1).getExpt() >= terms.get(i).getExpt()) {
504                    throw new RuntimeException("terms out of order");
505                }
506            }
507        }
508    }
509}