import java.awt.*; /* class Quads defines a class of representations of functions. Generally, the function is first set as an infix string in InfixFunction. Then appropriate call to the function ConvertToQuads will first convert that string to an equivalent Postfix string and, then, to a set of quads of the form: Operator Operand1 Operand2 Result where Operator is either a capital letter representing a function (for example, C means Cosine) and Operand1, Operand2, and Result are array indexes into the array Constants that contains the current values. As an example, suppose our function is 2.0*x/7.5. Then 2.0 is stored in Constants[0], 7.5 is stored in Constants[1], and the quads become: * 0 23 50 / 50 1 51 = -1 -1 51 Later an evaluation function when asked to Evaluate(x) would put x in Constants[23] and do the following: 1. Constants[0]*Constants[23]->Constants[50] (2*x -> Constants[50]) 2. Constants[50]/Constants[1]->Constants[51] (2*x)/7.5->Constants[51] 3. = says the result is in Constants[51] Thus, quite fast evaluation of the function becomes possible. These "quads" are a standard method of representing intermediate results in compilers. This works fast enough for our needs. Thus, this class is basically to convert InfixFunctions to a good evaluatable form. */ public class Quads { private double temp; // Used for computations throughout. private int Length; /* Temporary variable containing the length of each transformation as it occurs. */ private char [] CurrentCharacter; /* a private array used to convert from characters to strings. */ public boolean IsActive; /* IsActive true means that this function is ready to be used. The infix function was properly converted to quads. */ public boolean Linear; /* When Linear is true, this function is meant to be plotted with line segments and not 'dots'. */ public Color QuadColor; // The plotting color for this function public String QuadColorString; // The string representing that color. public boolean ConversionError; // True when a conversion error occurs public boolean EvaluationError; // True when an evaluation error occurs public char [] Operator; // The array of operators for the quads public int [] Operand1; /* The array of first operands for the quads */ public int [] Operand2; /* the array of second operands for the quads */ public int [] Result; // The array of results for the quads. public double [] Constants; /* The array of Constants used by the evaluator. Constants[4] is e, Constants[15] is PI and Constants[24] is ln(10). In evaluation, the variable, x is stored in Constants[23] and Constants[0]-Constants[3], Constants[5]-Constants[14], and Constants[16]-Constants[22] are used to score constant values that are part of the Infix function. In addition, if ever necessary, Constants[25]- Constants[49] could also be used. Constants[50]-Constants[99] are used for temporary storage of results used in the quads. */ public String InfixFunction; /* The String containing the original, infix representation of the function. */ public String PostfixFunction; /* The String containing the converted Postfix representation of the function. */ public double Xmin,Xmax; /* The "domain" of the function. Xmin and Xmax define the interval where this function is defined. */ public boolean IntegrateIt; /* If IntegrateIt is true, then the plotting function will try to draw an area of integration for the function. */ public double LeftLimit,RightLimit; /* These define the area of integration between x=LeftLimit and x=RightLimit. */ public boolean PlotDerivative; /* When on, the derivative of this function will be plotted in addition to whatever else is plotted (including, of course, this function. */ public boolean PlotIntegral; /* When on, the integral of this function will be plotted in addition to whatever else is plotted (including, of course, this function). */ // Constructor Quads() sets the various variables to good initial values. public Quads() { Length=0; Operator=new char[100]; Operator[0]='='; Operand1=new int[100]; Operand1[0]=-1; Operand2=new int[100]; Operand2[0]=-1; Result=new int[100]; Result[0]=23; Constants=new double[100]; CurrentCharacter=new char[1]; Constants['e'-'a']=2.718281828459; // Constants[4]=e Constants['p'-'a']=3.14159265359; // Constants[15]=PI Constants['y'-'a']=2.302585092994; // Constants[24]=ln(10) IsActive=false; PlotDerivative=false; Linear=false; PlotIntegral=false; QuadColor=Color.red; QuadColorString="red"; Xmin=-100.0; Xmax=100.0; InfixFunction="x"; PostfixFunction="x"; LeftLimit=-1.0; RightLimit=+1.0; } public double Simpson(double LeftLimit, double RightLimit, int NumberOfIntervals) { if(NumberOfIntervals<=1) { EvaluationError=true; return(0.0); } else { int ActualNumberOfIntervals=(NumberOfIntervals/2); double dx=(RightLimit-LeftLimit)/2.0/ActualNumberOfIntervals; double sum=Evaluate(LeftLimit)+Evaluate(RightLimit)+ 4*Evaluate(RightLimit-dx); for(int i=1;i<2*(ActualNumberOfIntervals-1);i=i+2) { sum=sum+4*Evaluate(LeftLimit+i*dx)+2*Evaluate(LeftLimit+(i+1)*dx); } return(sum*dx/3.0); } } /* IsRegularOperator returns true if the character is one of our operators +,-,*,/,^ (for exponentiation) and ~ (for unary minus). */ boolean IsRegularOperator(char Operator) { return ((Operator=='+') | (Operator=='-') | (Operator=='*') | (Operator=='/') | (Operator=='^') | (Operator=='~') | (Operator=='@') ); } /* IsOperator returns true if the character is a regular operator or if it is a representation of the absolute value sign (| or [) or if it is a ( or if it is a capital letter which represents a function. */ boolean IsOperator(char Operator) { return (IsRegularOperator(Operator) || (Operator=='(') || (Operator=='[') || (Operator=='|') || ( (Operator<='Z') & (Operator>='A') ) ); } /* Sometimes you want to take a power of a real number to an integer power. In such a case, I use function MyPower which actually uses RecursivePower to compute the result. MyPower assumes that the power is a POSITIVE integer (not 0). x can be any double precision number. */ double RecursivePower(double x,int power) { if(power==1) return x; else { double temp=RecursivePower(x,power/2); if((power % 2) == 0) return (temp*temp); else return (x*temp*temp); } } /* MyPower first checks to see if we are taking 0 to a power <= 0. If so, we have an EvaluationError. If not, if power is 0, it returns 1. If power is less than 0 it returns 1/RecursivePower(x,-power). If x=0.0 it returns 0.0. In all other cases, it simply returns RecursivePower(x,power). */ double MyPower(double x,int power) { if ((power == 0) && (x==0.0)) { EvaluationError=true; return (0.0); } else if (power==0) return (1.0); else if (power<0) return (1.0/RecursivePower(x,-power)); else if (x==0.0) return 0.0; else return RecursivePower(x,power); } /* Converting from infix to postfix requires that we know the (relative) priority of all operators. Note that ( and [ are 0, + and - are 1, * and / are 2, ~ is 3, ^ is 4, and all others (meaning functions) are 5. (Here [ represents the beginning of an expression whose absolute value will be taken. Also, ~ represents unary - instead of subtraction which is represented by -.) */ int OperatorPriority(char Operator) { switch (Operator) { case '(': case '[': return(0); case '+': case '-': return(1); case '*': case '/': return(2); case '~': return(3); case '^': return(4); default : return(5); } } /* We convert from Infix to Postfix in several stages. The first stage is to go through the string and remove all spaces. Then go through and if a '-' comes at the beginning or after a ( , change it to a ~ (a unary -). At the same time. if a numeric value comes up, convert it to a double and store it at consecutive spots in the Constants array, starting with 0. Also, in the output string, use lower case letters. Thus, the string -2.7 3* C os(x /2.4 +7. 3) would be converted to ~a*Cos(x/b+c) and Constants[0] would have value 2.73 (corresponding to a in the output expression), Constants[1] would have value 2.4 (corresponding to b in the output expression), and Constants[2] would have value 7.3 corresponding to c in the output expression). This is all this function does. It returns the string with the correspondence you see here. Notice that this function is only called in function MakeQuads. */ private String NoSpaceUnaryMinusNumbersToLetters(String temp) { String atemp=new String(temp); char [] OutFunction=new char[200]; char [] NoSpaceFunction=new char[200]; int index=0; int i=0; double dtemp; char c; int NewLength; ConversionError=false; for(i=0;i0) & (OutFunction[index-1]=='^')) { OutFunction[index-1]='@'; } else if((index>2) & (OutFunction[index-1]=='~') & (OutFunction[index-2]=='(') & (OutFunction[index-3]=='^') & (c==')')) { OutFunction[index-3]='@'; } } Constants[CurrentCharacter[0]-'a']=dtemp; OutFunction[index]=CurrentCharacter[0]; index++; CurrentCharacter[0]++; if((CurrentCharacter[0]=='e') | (CurrentCharacter[0]=='p')) { CurrentCharacter[0]++; } if(CurrentCharacter[0]=='x') { CurrentCharacter[0]++; CurrentCharacter[0]++; } } else { OutFunction[index]=c; index++; i++; if(i A (ArcTangent) Acos <-> B (ArcCosine) Cos <-> C (Cosine) Asin <-> D (ArcSine) Atanh <-> E (ArcTanHyperbolic) Acosh <-> F (ArcCosHyperbolic) Asinh <-> G (ArcSinHyperbolic) Ceil <-> H (Ceiling function) Csc <-> I (CoSecant) Cot <-> J (CoTangent) Cosh <-> K (CosHyperbolic) Ln <-> L (Natural Logarithm) Cbrt <-> M (Cube Root) Sec <-> N (Secant) Sinh <-> O (SineHyperbolic) Sqrt <-> P (Square Root) Tanh <-> Q (TangentHyperbolic) Log <-> R (Log base 10) Sin <-> S (Sine) Tan <-> T (Tangent) After converting these functions, it then checks to see of two operators are right next to each other or if two operators are next to each other. If so, ConversionError becomes true, indicating a conversion error. Notice that entering sin instead of Sin will AUTOMATICALLY generate an error. Only Sin is the Sine function. Also, the function converts matching pairs of | signs to [ ] pairs to ease the final conversion to postfix. Thus the input string ~|~a+b*Cos|c*x-d|+Acosh(f*x)| gets converted to ~[~a+b*C[c*x-d]+F(f*x)] This is the final stage before infix->postfix conversion. */ public String FunctionsToLetters(String temp) { String atemp=new String(temp); char [] InFunction=new char[100]; int NewLength=Length; int i=0; int index=0; char c=temp.charAt(i); while((i'Z')) { // If it ain't a capital letter, it doesn't start a function InFunction[index]=temp.charAt(i); index++; i++; if(i='a') & (InFunction[i]<='z') & (InFunction[i+1]>='a') & (InFunction[i+1]<='z')) { // Whoops! Two variables next to each other ConversionError=true; atemp=new String( "Error: Function has two variables or constants next to each other"); } else if(IsRegularOperator(InFunction[i]) & IsRegularOperator(InFunction[i+1])) { // Whoops! Two operators next to each other ConversionError=true; atemp=new String( "Error: Function has two operators (+,-,*,/, or ^) next to each other"); } } // Fix up the | signs for the InfixToPostfix routine if(InFunction[0]=='|') { InFunction[0]='['; } for(i=1;i=0) && (OperatorPriority(OperatorStack[StackPointer]) >= OperatorPriority(c))) { OutString[index]=OperatorStack[StackPointer]; index++; StackPointer--; } StackPointer++; // Then push the ^ onto the stack. OperatorStack[StackPointer]=c; } else { // Left-to-right operator. Pop all operators with priority // strictly greater than this operator onto the output string. while((StackPointer>=0) && (OperatorPriority(OperatorStack[StackPointer]) > OperatorPriority(c))) { OutString[index]=OperatorStack[StackPointer]; index++; StackPointer--; } StackPointer++; // Now push this operator onto the output string. OperatorStack[StackPointer]=c; } } else { if(c==')') { // With ) we pop operators off the stack until we run into a // [ (an ERROR), the stack becomes empty (an ERROR), or we // run into a matching ( while((StackPointer>=0) && (OperatorStack[StackPointer]!='(') && (OperatorStack[StackPointer]!='[')) { OutString[index]=OperatorStack[StackPointer]; index++; StackPointer--; } if(StackPointer<0) { // ERROR - no matching parentheses found atemp=new String ( "Error: Unmatched Parentheses in expression"); ConversionError=true; } else if(OperatorStack[StackPointer]=='[') { // ERROR - improperly mixed | and ( in the infix function atemp=new String ( "Error: Interleaved | and () in expression"); ConversionError=true; } else { // Pop and discard that matching ( StackPointer--; } } else if(c==']') { // With ] we pop operators off the stack until we run into a // ( (an ERROR), the stack becomes empty (an ERROR), or we // run into a matching [ while((StackPointer>=0) && (OperatorStack[StackPointer]!='(') && (OperatorStack[StackPointer]!='[')) { OutString[index]=OperatorStack[StackPointer]; index++; StackPointer--; } if(StackPointer<0) { // ERROR - no matching [ found atemp=new String ( "Error: Unmatched Parentheses in expression"); ConversionError=true; } else if(OperatorStack[StackPointer]=='(') { // ERROR - improperly mixed || and () in the infix function atemp=new String ( "Error: Interleaved | and () in expression"); ConversionError=true; } else { // Pop and discard that matching ( and add a | so that the // absolute value function will form a quad. StackPointer--; OutString[index]='|'; index++; } } else if(c==' ') { } else { // Variables are just copied to the output OutString[index]=c; index++; } } i++; } // end of while loop that goes through the whole infix expression while((StackPointer>=0) & !ConversionError) { // Pops off remaining operators if((OperatorStack[StackPointer]=='|') || (OperatorStack[StackPointer]=='[')) { ConversionError=true; atemp=new String("Error: Unmatched | in expression"); } else if(OperatorStack[StackPointer]=='(') { ConversionError=true; atemp=new String("Error: More ( than ) in expression"); } else { OutString[index]=OperatorStack[StackPointer]; StackPointer--; index++; } } // end while */ if(!ConversionError) { // Get set to return the string converted from the array of // operators and operands from the proper postfix expression atemp=new String(OutString); } Length=index; return atemp; // Either a proper postfix or an error message. } /* To convert from postfix to quads, use a stack to hold either variables (lower case letters from a to z) or temporary variables (letters from A to Z). The algorithm is: Scan the postfix string from left to right. If the character is an operator then if the operator requires one operand, pop the operand of the stack. (Empty stack is an ERROR). Generate the quad: operator OperandFromStack -1 TemporaryVariable and, then, push TemporaryVariable onto the stack. If the character is an operator that requires two operands pop operand2 from the stack then pop operand1 from the stack. Generate the quad operator operand1 operand2 TemporaryVariable and, then, push TemporaryVariable onto the stack. If the character is not an operator, push it onto the stack. After scanning the string, if there is anything left on the stack, it is a conversion error. */ String ConvertToQuads(String temp) { int index=0; int i=0; char TempSpot='A'; // TempSpot is the name of the next available temporary // variable. It goes to the next variable when used. char [] OperandStack=new char[100]; int StackPointer=-1; char TempChar; char c; String atemp=new String(temp); while((i='A'))) { // Unary operators and functions (also unary) if(StackPointer>=0) { if((OperandStack[StackPointer]>='a') & (OperandStack[StackPointer]<='y')) { Operand1[index]= OperandStack[StackPointer]-'a'; Operand2[index]=-1; } else { Operand1[index]= (OperandStack[StackPointer]-'A')+50; Operand2[index]=-1; } Result[index]=(TempSpot-'A')+50; OperandStack[StackPointer]=TempSpot; TempSpot++; index++; } else { ConversionError=true; atemp=new String ( "Error: Bad Function - too few constants or variables"); } } else { // Binary operators - get two operands from the stack. // Operand1 is one position further down the stack than // Operand2. if(StackPointer>=1) { if((OperandStack[StackPointer-1]>='a') & (OperandStack[StackPointer-1]<='z')) { Operand1[index]=OperandStack[StackPointer-1]-'a'; } else { Operand1[index]= (OperandStack[StackPointer-1]-'A')+50; } if((OperandStack[StackPointer]>='a') & (OperandStack[StackPointer]<='y')) { Operand2[index]= OperandStack[StackPointer]-'a'; } else { Operand2[index]= (OperandStack[StackPointer]-'A')+50; } Result[index]=(TempSpot-'A')+50; StackPointer--; OperandStack[StackPointer]=TempSpot; TempSpot++; index++; } else { ConversionError=true; atemp=new String ( "Error: Bad Function - too few constants or variables"); } } } else { // Push those variables onto the stack StackPointer++; OperandStack[StackPointer]=c; } i++; } // Generate that last quad which is always of the form = -1 -1 number // where number is the index where the last value computed was stored. if(Length==1) Result[index]=(temp.charAt(0)-'a'); // Constant function else Result[index]=(OperandStack[0]-'A')+50; Operand1[index]=-1; Operand2[index]=-1; Operator[index]='='; Length=index; if(StackPointer>0) { // Whoops. More than that last storage location on the stack. ConversionError=true; atemp=new String( "Error: Bad function - too many variables and constants"); } else if ((StackPointer<0) & !ConversionError) { // Whoops! Nothing on the stack. ConversionError=true; atemp=new String("Error: Bad function - too many operators"); } // Otherwise, that last result is the only thing on the stack. return atemp; } /* Evaluate works as follows: First, set Constants[23]=the input value x Second, run through the quads until the operator is '=', executing each quad as you go. Return the value at Constants[Result[i]] where i is the number of the quad that has that = in it. */ public double Evaluate(double x) { int i=0; Constants[23]=x; EvaluationError=false; while ((Operator[i]!='=') & !EvaluationError) { switch (Operator[i]) { // Each case is a different operator or function. case '+':Constants[Result[i]]=Constants[Operand1[i]]+ Constants[Operand2[i]]; break; case '-':Constants[Result[i]]=Constants[Operand1[i]]- Constants[Operand2[i]]; break; case '*':Constants[Result[i]]=Constants[Operand1[i]]* Constants[Operand2[i]]; break; case '/':if(Constants[Operand2[i]]==0.0) EvaluationError=true; else Constants[Result[i]]=Constants[Operand1[i]]/ Constants[Operand2[i]]; break; case '~':Constants[Result[i]]=-Constants[Operand1[i]]; break; case '^':if(Constants[Operand2[i]]* Math.log(Math.abs(Constants[Operand1[i]]))<=706.0) { if(Constants[Operand1[i]]<0.0) EvaluationError=true; else Constants[Result[i]]=Math.pow(Constants[Operand1[i]], Constants[Operand2[i]]); } else EvaluationError=true; break; case '@':if(Constants[Operand2[i]]* Math.log(Math.abs(Constants[Operand1[i]]))<=706.0) Constants[Result[i]]=MyPower(Constants[Operand1[i]], (int) Constants[Operand2[i]]); else EvaluationError=true; break; case '|':// Absolute Value | Constants[Result[i]]=Math.abs(Constants[Operand1[i]]); break; case 'A':// ArcTan (Atan) Constants[Result[i]]=Math.atan(Constants[Operand1[i]]); break; case 'B':// ArcCos (Acos) if(Math.abs(Constants[Operand1[i]])>1.0) EvaluationError=true; else Constants[Result[i]]=Math.acos(Constants[Operand1[i]]); break; case 'C':// Cos Constants[Result[i]]=Math.cos(Constants[Operand1[i]]); break; case 'D':// ArcSin (Asin) if(Math.abs(Constants[Operand1[i]])>1.0) EvaluationError=true; else Constants[Result[i]]=Math.asin(Constants[Operand1[i]]); break; case 'E':// ArcTanh (Atanh) if(Math.abs(Constants[Operand1[i]])>=1.0) EvaluationError=true; else Constants[Result[i]]= 0.5*Math.log((1.0+Constants[Operand1[i]])/ (1.0-Constants[Operand1[i]])); break; case 'F':// ArcCosh (Acosh) if(Constants[Operand1[i]]<1.0) EvaluationError=true; else Constants[Result[i]]=Math.log(Constants[Operand1[i]]+ Math.sqrt(Constants[Operand1[i]]* Constants[Operand1[i]]-1.0) ); break; case 'G':// ArcSinh (Asinh) if(Constants[Operand1[i]]<0.0) Constants[Result[i]]=-Math.log(-Constants[Operand1[i]]+ Math.sqrt(Constants[Operand1[i]]* Constants[Operand1[i]]+1.0)); else Constants[Result[i]]=Math.log(Constants[Operand1[i]]+ Math.sqrt(Constants[Operand1[i]]* Constants[Operand1[i]]+1.0)); break; case 'H':// Ceiling Function (Ceil) Constants[Result[i]]=Math.ceil(Constants[Operand1[i]]); break; case 'I':// CoSecant (Csc) if(Math.sin(Constants[Operand1[i]])==0.0) EvaluationError=true; else Constants[Result[i]]=1.0/Math.sin(Constants[Operand1[i]]); break; case 'J':// CoTangent (Cot) if(Math.sin(Constants[Operand1[i]])==0.0) EvaluationError=true; else Constants[Result[i]]=Math.cos(Constants[Operand1[i]])/ Math.sin(Constants[Operand1[i]]); break; case 'K':// Cosh if(Math.abs(Constants[Operand1[i]])<=706.0) { temp=Math.pow(Constants[4],Constants[Operand1[i]]); // e^x Constants[Result[i]]=(temp+1.0/temp)/2.0; } else EvaluationError=true; break; case 'L':// Natural Logarithm (Ln) if(Constants[Operand1[i]]<=0.0) EvaluationError=true; else Constants[Result[i]]=Math.log(Constants[Operand1[i]]); break; case 'M':// Cube Root (Cbrt) Constants[Result[i]]=Math.pow(Math.abs(Constants[Operand1[i]]), 0.33333333333333); if(Constants[Operand1[i]]<0.0) Constants[Result[i]]=-Constants[Result[i]]; break; case 'N':// Sec if(Math.cos(Constants[Operand1[i]])==0.0) EvaluationError=true; else Constants[Result[i]]=1.0/Math.cos(Constants[Operand1[i]]); break; case 'O':// Sinh if(Math.abs(Constants[Operand1[i]])<=706.0) { temp=Math.pow(Constants[4],Constants[Operand1[i]]); // e^x Constants[Result[i]]=(temp-1.0/temp)/2.0; } else EvaluationError=true; break; case 'P':// Square Root (Sqrt) if(Constants[Operand1[i]]<0.0) EvaluationError=true; else Constants[Result[i]]=Math.sqrt(Constants[Operand1[i]]); break; case 'Q':// Tanh if(Math.abs(Constants[Operand1[i]])<=706.0) { temp=Math.pow(Constants[4],2.0*Constants[Operand1[i]]); //e^2x Constants[Result[i]]=(temp-1.0)/(temp+1.0); } else EvaluationError=true; break; case 'R':// Log base 10 (Log) if(Constants[Operand1[i]]<0.0) EvaluationError=true; else Constants[Result[i]]=Math.log(Constants[Operand1[i]])/ Constants[24]; // Ln(x)/Ln(10) break; case 'S':// Sin Constants[Result[i]]=Math.sin(Constants[Operand1[i]]); break; case 'T':// Tangent (Tan) if(Math.cos(Constants[Operand1[i]])==0.0) EvaluationError=true; else Constants[Result[i]]=Math.tan(Constants[Operand1[i]]); break; default: EvaluationError=true; } // End of switch i++; } // End of while if(EvaluationError) return(0.0); else return(Constants[Result[i]]); } // MakeQuads just goes through the various functions above to produce the // Quads and Postfix expression from the input Infix expression. void MakeQuads() { // Get rid of spaces, change unary minuses to ~ and // change numbers to letters representing the numbers. String temp=new String(NoSpaceUnaryMinusNumbersToLetters(InfixFunction)); // Translate functions into single letters and set up // the output to check to see if it is right. temp=FunctionsToLetters(temp); // Translate to Postfix from Infix temp=InfixToPostfix(temp); // Save the Postfix Length in the value of variable x for a check Constants['x'-'a']=Length; // Convert the expression into Quads for later computation ConvertToQuads(temp); PostfixFunction=temp; if(!ConversionError) IsActive=true; } }