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