001 package edu.nrao.sss.math; 002 003 import java.io.Serializable; 004 005 import edu.nrao.sss.util.StringUtil; 006 007 /** 008 * A single term in a polynomial equation of the form 009 * <tt>f(x)=c<sub>0</sub> + c<sub>1</sub>x + c<sub>2</sub>x<sup>2</sup> 010 * + ... + c<sub>n</sub>x<sup>n</sup></tt>. 011 * <p> 012 * <b>Version Info:</b> 013 * <table style="margin-left:2em"> 014 * <tr><td>$Revision: 2151 $</td> 015 * <tr><td>$Date: 2009-04-03 10:26:17 -0600 (Fri, 03 Apr 2009) $</td> 016 * <tr><td>$Author: dharland $ (last person to modify)</td> 017 * </table></p> 018 * 019 * @author David M. Harland 020 * @since 2006-03-24 021 */ 022 public class PolynomialTerm 023 implements Cloneable, Comparable<PolynomialTerm>, Serializable 024 { 025 private static final long serialVersionUID = 1L; 026 027 private double coefficient; 028 private int exponent; 029 030 /** Creates a new term with a coefficient of 0.0 and an exponent of 0. */ 031 public PolynomialTerm() 032 { 033 this(0.0, 0); 034 } 035 036 /** Creates a new term with the given coefficient and exponent. */ 037 public PolynomialTerm(double coefficient, int exponent) 038 { 039 this.coefficient = coefficient; 040 this.exponent = exponent; 041 } 042 043 /** 044 * Sets both the coefficient and exponent of this term. 045 * 046 * @param newCoefficient the new coefficient of this term. 047 * @param newExponent the new exponent of this term. 048 */ 049 public void set(double newCoefficient, int newExponent) 050 { 051 setCoefficient(newCoefficient); 052 setExponent(newExponent); 053 } 054 055 /** 056 * Returns a new term based on {@code termString}. 057 * See {@link #parse(String)} for the expected format of 058 * {@code termString}. 059 * 060 * @param termString a string that will be used to set this term. 061 * 062 * @throws IllegalArgumentException if {@code termString} is not in 063 * the expected form. 064 */ 065 public void set(String termString) 066 { 067 PolynomialTerm.parse(termString, this); 068 } 069 070 /** 071 * Sets the coefficient of this term to {@code newCoefficient}. 072 * 073 * @param newCoefficient the new coefficient of this term. 074 * 075 * @see #getCoefficient() 076 */ 077 public void setCoefficient(double newCoefficient) 078 { 079 coefficient = newCoefficient; 080 } 081 082 /** 083 * Returns the coefficient of this term. For term 084 * <tt>c<sub>i</sub>x<sup>i</sup></tt>, <tt>c<sub>i</sub></tt> 085 * is the coefficient. 086 * 087 * @return the coefficient of this term. 088 */ 089 public double getCoefficient() 090 { 091 return coefficient; 092 } 093 094 /** 095 * Sets the exponent of this term to {@code newExponent}. 096 * 097 * @param newExponent the new exponent of this term. 098 * 099 * @see #getExponent() 100 */ 101 public void setExponent(int newExponent) 102 { 103 exponent = newExponent; 104 } 105 106 /** 107 * Returns the exponent of this term. For term 108 * <tt>c<sub>i</sub>x<sup>i</sup></tt>, {@code i} 109 * is the exponent. 110 * 111 * @return the exponent of this term. 112 */ 113 public int getExponent() 114 { 115 return exponent; 116 } 117 118 /** 119 * Returns a term that is a derivative of this term. That is, 120 * for term <tt>c<sub>i</sub>x<sup>i</sup></tt>, the returned 121 * term has a coefficient of <tt>i * c<sub>i</sub></tt> and an 122 * exponent of of <tt>x<sup>i-1</sup></tt>. 123 * <p> 124 * For the special case where the exponent is zero, the returned 125 * term will have a coefficient of {@code 0.0} and an exponent of 126 * {@code 0}.</p> 127 * 128 * @return a term that is the mathematical derivative of this one. 129 */ 130 public PolynomialTerm createDerivativeTerm() 131 { 132 //We need to return "0.0". We do this by setting the coefficient 133 //to zero and choosing, somewhat arbitrarily, an exponent of zero. 134 if (exponent == 0) 135 return new PolynomialTerm(0.0, 0); 136 137 //Normal logic 138 return new PolynomialTerm(coefficient * exponent, exponent - 1); 139 } 140 141 /** 142 * Returns the value of this term calculated for the given number. 143 * That is the result, r, is calculated as: 144 * <pre> 145 * r = getCoefficient() * (number ^ getExponent()) 146 * </pre> 147 * 148 * @param number the value of the independent variable of this term. 149 * For term <tt>c<sub>i</sub>x<sup>i</sup></tt>, {@code x} 150 * is the independent variable. 151 152 * @return getCoefficient() * (number ^ getExponent()). 153 */ 154 public double calculateFor(double number) 155 { 156 double answer; 157 158 switch (exponent) 159 { 160 case -1: 161 answer = coefficient / number; 162 break; 163 case 0: 164 answer = coefficient; 165 break; 166 case 1: 167 answer = coefficient * number; 168 break; 169 default: 170 answer = coefficient * Math.pow(number, exponent); 171 } 172 173 return answer; 174 } 175 176 /** 177 * Adds {@code otherTerm} to this one, if and only if its 178 * exponent is the same as this term's. If the exponents 179 * are not identical, this method does nothing. 180 * 181 * @param otherTerm the term to be added to this one. 182 * 183 * @return the new value of this term's coefficient. If 184 * {@code otherTerm} does not have the same exponent 185 * as this term, the value returned will be the 186 * coefficient of this term before this method was 187 * called. 188 */ 189 public double add(PolynomialTerm otherTerm) 190 { 191 if (otherTerm.getExponent() == this.getExponent()) 192 setCoefficient(getCoefficient() + otherTerm.getCoefficient()); 193 194 return getCoefficient(); 195 } 196 197 /** 198 * Returns a new term based on {@code termString}. 199 * <p> 200 * The format of {@code termString} is [+-]#.#a^[+-]#, 201 * where<br/> 202 * '#' is any numeric character repeated one or more times,<br/> 203 * 'a' any alpha character [a-zA-Z] repeated one or more times,<br/> 204 * '^' is the literal caret character, and<br/> 205 * '[+-]'indicates that a '+' character, a '-' character, or neither 206 * is permitted before the coefficient and the exponent.</p> 207 * <p> 208 * Examples of typical terms are: <tt>12.34x^56, +12.34x^-56,</tt> and 209 * <tt>-12.34x^+56</tt>. 210 * Constant terms, such as <tt>42</tt> or <tt>867.5309</tt> are also legal, 211 * as are terms that have an implied exponent of one, such as 212 * <tt>12.34x</tt>.</p> 213 * <p> 214 * <b><u>Special Cases</u></b><br/> 215 * A {@code termString} of <i>null</i> or <tt>""</tt> (the empty 216 * string) will <i>not</i> result in an {@code IllegalArgumentException}, 217 * but will instead return a term whose coefficient and exponent are 218 * both zero.</p> 219 * 220 * @param termString a string that will be converted into a polynomial term. 221 * 222 * @throws IllegalArgumentException if {@code termString} is not in 223 * the expected form. 224 */ 225 public static PolynomialTerm parse(String termString) 226 { 227 PolynomialTerm newTerm = new PolynomialTerm(); 228 229 if ((termString != null) && !termString.equals("")) 230 PolynomialTerm.parse(termString, newTerm); 231 232 return newTerm; 233 } 234 235 private static PolynomialTerm parse(String termString, PolynomialTerm term) 236 { 237 String errMsg = null; 238 239 //Any string of consecutive alpha characters is interpreted as 240 //the independent variable. 241 String[] pieces = termString.split("[a-zA-Z]+", -1); 242 243 //Normally get two pieces: ##.### and ^##. However, 244 //if term is #.###x, the second piece will be the empty string. 245 if (pieces.length == 2) 246 { 247 try 248 { 249 //First piece should be coefficient; s/b parsable as Double -- 250 //unless it is an implied "1" 251 double coef = 252 (pieces[0].equals("")) ? 1.0 : Double.parseDouble(pieces[0]); 253 254 if (pieces[1].length() == 0) //Implied exponent of 1 255 { 256 term.set(coef, 1); 257 } 258 else if (pieces[1].charAt(0) == '^') 259 { 260 if (pieces[1].charAt(1) == '+') //Integer can't parse '+'! 261 term.set(coef, Integer.parseInt(pieces[1].substring(2))); 262 else 263 term.set(coef, Integer.parseInt(pieces[1].substring(1))); 264 } 265 else 266 { 267 errMsg = "Did not find caret ('^') in proper location."; 268 } 269 } 270 catch (NumberFormatException ex) 271 { 272 errMsg = ex.getMessage(); 273 } 274 } 275 //If we don't have the normal two-piece split, perhaps we 276 //have a constant w/out the independent variable. 277 else 278 { 279 //See if the entire parameter string is a real number. 280 try 281 { 282 term.set(Double.parseDouble(termString), 0); 283 } 284 catch (NumberFormatException ex) 285 { 286 errMsg = "Expected coefficient-variable-^-exponent or naked constant."; 287 } 288 } 289 290 if (errMsg != null) 291 throw new IllegalArgumentException("Problem parsing " + termString + 292 ": " + errMsg); 293 return term; 294 } 295 296 /** Returns an polynomial term that is equal to this one. */ 297 public PolynomialTerm clone() 298 { 299 //Since this class has only primitive attributes, 300 //the clone in Object is all we need. 301 try 302 { 303 return (PolynomialTerm)super.clone(); 304 } 305 catch (CloneNotSupportedException ex) 306 { 307 //We'll never get here, but just in case... 308 throw new RuntimeException(ex); 309 } 310 } 311 312 /** Returns <i>true</i> if {@code o} is equal to this term. */ 313 public boolean equals(Object o) 314 { 315 //Quick exit if o is this 316 if (o == this) 317 return true; 318 319 //Quick exit if o is null 320 if (o == null) 321 return false; 322 323 //Quick exit if classes are different 324 if (!o.getClass().equals(this.getClass())) 325 return false; 326 327 PolynomialTerm otherTerm = (PolynomialTerm)o; 328 329 return (otherTerm.coefficient == this.coefficient) && 330 (otherTerm.exponent == this.exponent); 331 } 332 333 /** Returns a hash code value for this term. */ 334 public int hashCode() 335 { 336 return (int)calculateFor(10.0); 337 } 338 339 /** Compares this term with the {@code otherTerm} for order. */ 340 public int compareTo(PolynomialTerm otherTerm) 341 { 342 //Compare exponents first; smaller exponents come first 343 int answer = this.exponent - otherTerm.exponent; 344 if (answer != 0) 345 return answer; 346 347 //Exponents were equal. Compare coeffiecients; smaller ones come first 348 return (int)(this.coefficient - otherTerm.coefficient); 349 } 350 351 /** 352 * Returns a string of form #.#t^#. For example, the following are 353 * all possible return values: 354 * <ul> 355 * <li>12.345t^67</li> 356 * <li>8t^0</li> 357 * <li>0.123456t^789</li> 358 * </ul> 359 */ 360 @Override 361 public String toString() 362 { 363 return toString('t'); 364 } 365 366 /** 367 * Returns a string of form #.#variableName^#. For example, the following are 368 * all possible return values if <tt>variableName</tt> is 'x': 369 * <ul> 370 * <li>12.345x^67</li> 371 * <li>8x^0</li> 372 * <li>0.123456x^789</li> 373 * </ul> 374 * 375 * @param variableName 376 * the character to use as the independent variable. 377 * 378 * @return 379 * a text representation of this polynomial term. 380 */ 381 public String toString(char variableName) 382 { 383 StringBuilder buff = new StringBuilder(); 384 385 buff.append(StringUtil.getInstance().formatNoScientificNotation(coefficient)); 386 buff.append(variableName).append('^').append(exponent); 387 388 return buff.toString(); 389 } 390 391 //This is here for quick & dirty testing 392 /* 393 public static void main(String[] args) 394 { 395 PolynomialTerm term; 396 for (String arg : args) 397 { 398 System.out.print("Input: " + arg); 399 term = PolynomialTerm.parse(arg); 400 String termText = term.toString(); 401 System.out.println("; Output: " + termText); 402 System.out.println(" Parse output into new term: " + PolynomialTerm.parse(termText)); 403 System.out.println(); 404 } 405 406 term = PolynomialTerm.parse(""); 407 System.out.println("Empty string gives: " + term); 408 409 term = PolynomialTerm.parse(null); 410 System.out.println("null gives: " + term); 411 } 412 */ 413 }