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}