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