Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

Quantity.java

Go to the documentation of this file.
00001 // file:    Quantity.java
00002 // author:  Robert Keller
00003 // purpose: Provide Unicalc Quantity class
00004 
00005 import OpenList;
00006 
00007 /**
00008  * Quantity is a class of objects representing Unicalc quantities.
00009  * Each quality is a numeric factor times a symbolic numerator divided
00010  * by a symbolic denominator.
00011  *
00012  * The equivalent rex definitions of various functions have been embedded
00013  * as comments.
00014  */
00015 
00016 public class Quantity
00017 {
00018 /**
00019  * the multiply operator in syntax trees returned by the parser
00020  */
00021  
00022 static final String multiplyOp = "*";
00023 
00024 
00025 /**
00026  * the divide operator in syntax trees returned by the parser
00027  */
00028  
00029 static final String divideOp   = "/";
00030 
00031 
00032 /**
00033  * the exponentiation operator in syntax trees returned by the parser
00034  */
00035  
00036 static final String powerOp    = "^";
00037 
00038 
00039 /** 
00040  * the empty list (for local convenience) 
00041  */
00042 
00043 static OpenList nil = OpenList.nil;
00044 
00045 
00046 /** 
00047  * the Quantity representing dimensionless 1, for convenience
00048  */
00049 
00050 final static Quantity one = new Quantity(1.0f, nil, nil);
00051 
00052 
00053 /** Factor is the numeric portion of the quantity. */
00054 
00055 private float Factor;
00056 
00057 
00058 /** Num is a list of units representing the numerator. */
00059 
00060 private OpenList Num;
00061 
00062 
00063 /** Denom is a list of units representing the denominator. */
00064 
00065 private OpenList Denom;
00066 
00067 
00068 /**
00069  * Construct a Quantity from a factor, numerator list, and denominator list.
00070  */
00071 
00072 Quantity(float Factor, OpenList Num, OpenList Denom)
00073   {
00074   this.Factor = Factor;
00075   this.Num    = Num;
00076   this.Denom  = Denom;
00077   }
00078 
00079 
00080 /** 
00081  * Return the Factor of this Quantity. 
00082  * @return the Factor of this Quantity
00083  */
00084 
00085 float getFactor()  { return Factor; }
00086 
00087 
00088 /** 
00089  * Return the numerator of this Quantity. 
00090  * @return the numerator of this Quantity
00091  */
00092 
00093 OpenList getNum()   { return Num; }
00094 
00095 
00096 /** 
00097  * Return the denominator of this Quantity. 
00098  * @return the denominator of this Quantity
00099  */
00100 
00101 OpenList getDenom() { return Denom; }
00102   
00103 
00104 /**
00105  * Construct a Quantity from a syntax tree represented using OpenList.
00106  * @param tree An Object representing a syntax tree, as returned by
00107  *        the Unicalc parser
00108  * @return a Quantity representing the value of the syntax tree
00109  */
00110 
00111 public static Quantity makeQuantity(Object tree)
00112   {
00113   // A tree consisting of a single numeric node becomes a quantity
00114   // with the numeric value as multiplier.
00115 
00116   if( tree instanceof Number )
00117     {
00118     return new Quantity(((Number)tree).floatValue(), nil, nil);
00119     }
00120 
00121   // A tree consisting of a single string becomes a quantity with
00122   // a that string in the numerator, an empty denominator, and 1 as
00123   // the multiplier.
00124 
00125   if( tree instanceof String )
00126     {
00127     return new Quantity(1.0f, list(tree), nil);
00128     }
00129 
00130   // A tree consisting of an OpenList represents a binary operator
00131   // joining two sub-trees.  Quantities are made from the sub-trees
00132   // and the Quantities are combined using the appropriate operators.
00133 
00134   if( tree instanceof OpenList )
00135     {
00136     OpenList treeAsList = (OpenList)tree;
00137 
00138     String operator = (String)treeAsList.first();
00139     Object leftSubtree = treeAsList.second();
00140     Object rightSubtree = treeAsList.third();
00141 
00142     if( operator.equals(multiplyOp) )           // multiply quantities
00143       {
00144       return multiply(makeQuantity(leftSubtree), makeQuantity(rightSubtree));
00145       }
00146 
00147     if( operator.equals(divideOp) )             // divide quantities
00148       {
00149       return divide(makeQuantity(leftSubtree), makeQuantity(rightSubtree));
00150       }
00151 
00152     if( operator.equals(powerOp) )              // raise to integer power
00153       {
00154       return raise(makeQuantity(leftSubtree), 
00155                    ((Integer)rightSubtree).intValue());
00156       }
00157     }
00158 
00159   return null;  // should not occur, but keeps compiler happy
00160   }
00161 
00162 
00163 /**
00164  * Raise a Quantity to an integer power.  
00165  * The power can be negative, positive, or 0.
00166  * @param quantity the Quantity to be raised to a power
00167  * @param power the power to which the Quantity is to be raised
00168  * @return the Quantity raised to the indicated power
00169  */
00170 
00171 static Quantity raise(Quantity quantity, int power)
00172   {
00173   if( power == 0 )
00174     return one;
00175 
00176   if( power > 0 )
00177     return multiply(quantity, raise(quantity, power-1));
00178   else
00179     return divide(quantity, raise(quantity, power+1));
00180   }
00181 
00182 
00183 /** 
00184  * local cons for convenience 
00185  */
00186 
00187 static OpenList cons(Object F, OpenList R) { return OpenList.cons(F, R); }
00188 
00189 
00190 /** 
00191  * local list of 1 argument, for convenience 
00192  */
00193 
00194 static OpenList list(Object X1) { return OpenList.list(X1); }
00195 
00196 
00197 /** 
00198  * local list of 2 arguments, for convenience 
00199  */
00200 
00201 static OpenList list(Object X1, Object X2) { return OpenList.list(X1, X2); }
00202 
00203 
00204 /** 
00205  * local list of 3 arguments, for convenience 
00206  */
00207 
00208 static OpenList list(Object X1, Object X2, Object X3) 
00209     { return OpenList.list(X1, X2, X3); }
00210 
00211 
00212 /**
00213  * simplify simplifies a quantity by cancelling any terms common to
00214  * numerator and denominotor.
00215  *<pre>
00216  * simplify([F, N, D]) => [F, cancelFrom(N, D), cancelFrom(D, N)];
00217  *</pre>
00218  */
00219 
00220 static Quantity simplify(Quantity Q)
00221   {
00222   return new Quantity(Q.getFactor(), 
00223                       cancelFrom(Q.getNum(),   Q.getDenom()), 
00224                       cancelFrom(Q.getDenom(), Q.getNum()));
00225   }
00226 
00227 /**
00228  * cancelFrom cancels from the first argument any units that are present
00229  * in the second argument.  It assumes that both lists are sorted and
00230  * guarantees that the result is similarly sorted.
00231  *
00232  *<pre>
00233  * cancelFrom([], M) => [];
00234  *
00235  *  cancelFrom(L, []) => L;
00236  *
00237  *  cancelFrom([A | L], [A | M]) => cancelFrom(L, M);
00238  *
00239  *  cancelFrom([A | L], [B | M]) => A < B ? [A | cancelFrom(L, [B | M])] 
00240  *                                        : cancelFrom([A | L], M);
00241  *</pre>
00242  * 
00243  * @param L the sorted list from which units are cancelled.
00244  * @param M the sorted list of units to be cancelled.
00245  * @return the sorted list containing the units in L with the units of M
00246  *         cancelled out
00247  */
00248 
00249 static OpenList cancelFrom(OpenList L, OpenList M)
00250   {
00251   if( L.isEmpty() ) return nil;
00252   if( M.isEmpty() ) return L;
00253 
00254   String A = (String)L.first();
00255   String B = (String)M.first();
00256 
00257   if( A.equals(B) ) return cancelFrom(L.rest(), M.rest());
00258 
00259   if( A.compareTo(B) < 0 ) return cons(A, cancelFrom(L.rest(), M));
00260                       else return cancelFrom(L, M.rest());
00261   }
00262 
00263 
00264 /** 
00265  * Multiply two Quantities, returning a simplified result.
00266  *
00267  *<pre>
00268  * multiply([M1, N1, D1], [M2, N2, D2]) => 
00269  *   simplify([M1*M2, merge(N1, N2), merge(D1, D2)]);
00270  *</pre>
00271  *
00272  * @param Q1 one of the Quantities to be multiplied
00273  * @param Q2 the other of the Quantities to be multiplied
00274  * @return the product of the two Quantities
00275  */  
00276 
00277 static Quantity multiply(Quantity Q1, Quantity Q2)
00278   {
00279   return simplify(new Quantity(Q1.getFactor() * Q2.getFactor(), 
00280                                OpenList.merge(Q1.getNum(), Q2.getNum()),
00281                                OpenList.merge(Q1.getDenom(), Q2.getDenom())));
00282   }
00283 
00284 
00285 /**
00286  * Divide first Quantity by Second Quantity, returning a simplified result.
00287  *
00288  *<pre>
00289  * divide([M1, N1, D1], [M2, N2, D2]) => 
00290  *   simplify([M1/M2, merge(N1, D2), merge(D1, N2)]);
00291  *</pre>
00292  *
00293  * @param Q1 the dividend Quantity
00294  * @param Q2 the divisor Quantity
00295  * @return the quotient of Q1 divided by Q2
00296  */
00297 
00298 static Quantity divide(Quantity Q1, Quantity Q2)
00299   {
00300   return simplify(new Quantity(Q1.getFactor() / Q2.getFactor(), 
00301                                OpenList.merge(Q1.getNum(), Q2.getDenom()),
00302                                OpenList.merge(Q1.getDenom(), Q2.getNum())));
00303   }
00304 
00305 
00306 /**
00307  * Invert the Quantity, that is, divide one by it.
00308  *
00309  * @param Q the Quantity to be inverted
00310  * @return the quotient of 1 divided by Q
00311  */
00312 
00313 static Quantity invert(Quantity Q)
00314   {
00315   return divide(one, Q);
00316   }
00317 
00318 
00319 /**
00320  * Normalize a Quantity, with respect to a Database, 
00321  * returning a simplified Quantity.
00322  *
00323  *<pre>
00324  * norm([M, N, D], DB) => 
00325  *   multiply([M, [], []], divide(normalizeAll(N, DB), normalizeAll(D, DB)));
00326  *</pre>
00327  *
00328  * @param Q the Quantity to be normalized
00329  * @param DB the conversion database
00330  * @return a normalized Quantity equivalent to Q
00331  */
00332 
00333 static Quantity norm(Quantity Q, OpenList DB)
00334   {
00335   return multiply(new Quantity(Q.getFactor(), nil, nil),
00336                   divide(normalizeAll(Q.getNum(), DB), 
00337                          normalizeAll(Q.getDenom(), DB)));
00338   }
00339 
00340 /**
00341  * Return the normalized producet of a list of Quantities.
00342  *
00343  *<pre>
00344  * normalizeAll(L, DB) = multiplyAll(map((X)=>normalizeUnit(X, DB), L));
00345  *</pre>
00346  *
00347  * @param L a list of units (strings)
00348  * @param DB the conversion database
00349  * @return a normalized Quantity representing the product of the units
00350  */
00351 
00352 static Quantity normalizeAll(OpenList L, OpenList DB)
00353   {
00354   return multiplyAll(L.map(new NormalizeUnit(DB)));
00355   }
00356 
00357 
00358 /**
00359  * Normalize a single unit with respect to a database.
00360  *
00361  *<pre>
00362  * normalizeUnit(Unit, DB) =
00363  *   Found = assoc(Unit, DB),
00364  *   Found == [] ? 
00365  *     [1., [Unit], []]             // If not found, create unit quantity.
00366  *   : norm(second(Found), DB);     // Otherwise normalize the right-hand side.
00367  *</pre>
00368  *
00369  * @param Unit the unit to be normalized
00370  * @param DB the conversion database
00371  * @return a normalized Quantity equivalent to Unit
00372  */
00373 
00374 static Quantity normalizeUnit(String Unit, OpenList DB)
00375   {
00376   OpenList Found = OpenList.assoc(Unit, DB);
00377 
00378   if( Found.isEmpty() )
00379     {
00380     return new Quantity(1.0f, OpenList.list(Unit), nil);
00381     }
00382   else
00383     {
00384     return norm((Quantity)(Found.second()), DB);
00385     }
00386   }
00387 
00388 
00389 /**
00390  * Multiply a list of Quantities together to get a single Quantity.
00391  *
00392  *<pre>
00393  * multiplyAll(L) = reduce(multiply, [1., [], []], L);
00394  *</pre>
00395  *
00396  * @param L a list of Quantities
00397  * @return the product of the Quantities in the argument list
00398  */
00399 
00400 static Quantity multiplyAll(OpenList L)
00401   {
00402   return (Quantity)L.reduce(new Multiply(), one);
00403   }
00404 
00405 
00406 /**
00407  * Convert the first Quantity to the Second, returning a normalized result
00408  * with respect to the database in the third argument.
00409  *
00410  *<pre>
00411  * convert(A, B, DB) = norm(divide(A, B), db);
00412  *</pre>
00413  *
00414  * @param A the Quantity from which conversion is to be done
00415  * @param B the Quantity to which conversion is to be done
00416  * @param DB the database used for normalization
00417  * @return the Quantity representing the conversion from A to B
00418  *         If the Quantities are inter-convertible, this will have
00419  *         empty numerator and denominator and the factor represents
00420  *         the conversion factor
00421  */
00422 
00423 static Quantity convert(Quantity A, Quantity B, OpenList DB)
00424   {
00425   return norm(divide(A, B), DB);
00426   }
00427 
00428 
00429 
00430 /**
00431  * Convert this Quantity to String (printable form).
00432  * @return String to which this Quantity is converted
00433  */
00434 
00435 public String toString()
00436   {
00437   if( Denom.isEmpty() )
00438     {
00439     if( Num.isEmpty() )
00440       {
00441       return "" + Factor;
00442       }
00443     else
00444       {
00445       return Factor + " " + Num.toString("", " ", "");
00446       }
00447     }
00448   else
00449     {
00450     if( Num.isEmpty() )
00451       {
00452       return Factor + "/" + Denom.toString("", " ", "");;
00453       }
00454     else
00455       {
00456       return Factor + " "
00457                  + Num.toString("", " ", "")
00458            + "/" + Denom.toString("", " ", "");
00459       }
00460     }
00461   }
00462 
00463 }

Generated at Wed Oct 9 23:52:14 2002 for Unicalc by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001