SIMPLIFYING NUMERICAL EXPRESSIONS By Richard Andrew Mealing A DISSERTATION Submitted to The University of Liverpool in partial fulfilment of the requirements for the degree of MASTER OF SCIENCE 13/09/2010 i ABSTRACT SIMPLIFYING NUMERICAL EXPRESSIONS By Richard Andrew Mealing This dissertation presents a computer algebra system focusing primarily on numerical simplification and secondly on algebraic simplification. It provides functionality absent in similar online publicly and freely available systems. Examples include surd reduction, monomial and binomial root rationalisation and products of bases to the same power. It uses a readily accessible recursive representation based on nested lists making it easily extensible. Moreover, the system shows simplification and formatting steps and is open source unlike the main alternatives. This makes it applicable as demonstration software and a learning aid for Mathematical and Computer Science disciplines. The project used iterative and incremental cyclic software development methodologies with rapid prototyping and object oriented design. User feedback was essential throughout the process and was constantly sought and drawn on to improve usability. An intended consequence of this is to alter public opinion of computer algebra systems. Historically their target audience was individuals fluent in advanced mathematics due to demand from artificial intelligence and theoretical physics research. It intends to provide a bridge to those requiring a computer algebra system without formal mathematical training or the resources to acquire a proprietary system. Although anyone can use the system, its target audience is people with at least a knowledge of standard arithmetic operations and will probably be most useful for students. The system is composed of three main components, the web page, which provides the user interface and embeds the other two components, the JavaScript, which formats a user input infix expression and prepares it for simplification and the Java applets, which draw an expression tree and perform the simplification. Parsing a user input infix expression involves tokenization and conversion into a postfix expression using an extended form of the Shunting Yard Algorithm that can handle functions with variable numbers of arguments. The system can render and return simplification steps as Mathematical Markup Language, which is an emerging standard for displaying mathematical expressions and currently under development by the World Wide Web Consortium. The system is modular and can have any individual component used separately for independent interest. ii DECLARATION I hereby certify that this dissertation constitutes my own product, that where the language of others is set forth, quotation marks so indicate, and that appropriate credit is given where I have used the language, ideas, expressions, or writings of another. I declare that the dissertation describes original work that has not previously been presented for the award of any other degree of any institution. Signed, Richard Mealing iii ACKNOWLEDGEMENTS I would like to acknowledge and thank those who have provided advice, guidance, and support throughout this project: Dr Paul W. Goldberg (Supervisor) whose help and encouragement made this project possible. Dr Piotr Krysta, (Second Supervisor), Dr Martin Gairing (Assessor) and Dr Russell Martin (Alternative Assessor) for their constructive feedback. Dr Leszek Gasieniec for answering my queries. Miss Judith Lewa and Mrs Janet Lowry for saving me time by scanning and e-mailing feedback and providing administrative assistance. Users who engaged with the system for providing information valuable to debugging and redesign. My family and friends for their tolerance and understanding by putting up with the many hours I dedicated to this project over the previous months. Lastly, to anyone else who supported me in any other respect throughout this project. iv TABLE OF CONTENTS Page ABSTRACT ........................................................................................ ii DECLARATION ..................................................................................iii ACKNOWLEDGEMENTS .................................................................. iv LIST OF TABLES ............................................................................... x LIST OF FIGURES ............................................................................ xi ABBREVIATIONS ..............................................................................xii 1. INTRODUCTION ......................................................................... 1 1.1 Scope ............................................................................................................ 1 1.2 Problem statement ........................................................................................ 1 1.2.1 Original project proposal .................................................................................................... 1 1.2.2 Original aims derived from the first project proposal: ........................................................ 1 1.2.3 Interpreted aims derived from expanding on the original aims: ......................................... 1 1.2.4 Extended aims: .................................................................................................................. 2 1.2.4.1 Derived from further discussion: ................................................................................. 2 1.2.4.2 Derived from further analysis: ..................................................................................... 2 1.3 Approach ....................................................................................................... 2 1.4 Solution and justification ................................................................................ 3 1.4.1 JavaScript functions to: ...................................................................................................... 3 1.4.2 Java functions to: ............................................................................................................... 3 1.5 Outline ........................................................................................................... 3 2. BACKGROUND .......................................................................... 4 2.1 Tokenization .................................................................................................. 4 2.2 Notations and operator precedence .............................................................. 4 2.3 Infix to postfix ................................................................................................ 4 2.4 Expression representation ............................................................................. 5 2.5 Existing online tools....................................................................................... 5 2.5.1 Principal Nth Root Calculator and Reducer ....................................................................... 5 2.5.2 Expression Calculator ........................................................................................................ 5 2.5.3 Algebra Simplifier and Math Solver that SHOWS WORK ................................................. 5 2.5.4 Google's built-in calculator function ................................................................................... 6 3. DESIGN ...................................................................................... 7 3.1 Components and structure ............................................................................ 7 3.2 Data structures and algorithms.................................................................... 10 v 3.3 Evaluation ................................................................................................... 17 3.3.1 Criteria ............................................................................................................................. 17 3.3.2 Assessment: .................................................................................................................... 17 3.4 Interface description .................................................................................... 17 4. REALISATION .......................................................................... 18 4.1 Implementation ............................................................................................ 18 4.1.1 Software ........................................................................................................................... 18 4.1.2 Libraries ........................................................................................................................... 18 4.2 Deployment ................................................................................................. 18 4.2.1 Packaging ........................................................................................................................ 18 4.2.2 Compression .................................................................................................................... 18 4.2.3 Browser Compatibility ...................................................................................................... 18 4.3 Testing ........................................................................................................ 19 4.3.1 Final formatted expression .............................................................................................. 19 4.3.2 Postfix token array expression ......................................................................................... 20 4.3.3 Final simplified expression ............................................................................................... 20 5. EVALUATION ........................................................................... 23 6. PROFESSIONAL ISSUES ........................................................ 26 6.1 Code of conduct .......................................................................................... 26 6.2 Code of good practice ................................................................................. 26 7. CONCLUSIONS ........................................................................ 27 7.1 Main findings and learning points ................................................................ 27 7.2 Future development .................................................................................... 27 7.2.1 Reliability proof ................................................................................................................ 27 7.2.2 Number limit ..................................................................................................................... 27 7.2.3 Equation solving............................................................................................................... 27 7.2.4 Animated processing ....................................................................................................... 27 7.2.5 Custom themes ................................................................................................................ 27 7.2.6 Language conversion ...................................................................................................... 27 7.2.7 Java and JavaScript alternative ....................................................................................... 28 7.2.8 Simplification analysis ...................................................................................................... 28 8. REFERENCES.......................................................................... 29 9. APPENDICES ........................................................................... 33 9.1 Project log ................................................................................................... 33 9.2 Project plan ................................................................................................. 34 9.3 Additional background information .............................................................. 35 9.3.1 Computer algebra systems .............................................................................................. 35 9.3.2 CAS algorithms ................................................................................................................ 35 vi 9.3.3 Regular expressions ........................................................................................................ 35 9.3.4 Irrational numbers ............................................................................................................ 35 9.3.5 Operator properties .......................................................................................................... 36 9.3.6 Basic algebra ................................................................................................................... 36 9.3.7 Factorization .................................................................................................................... 37 9.3.8 JavaScript ........................................................................................................................ 37 9.4 Additional figure explanations...................................................................... 37 9.4.1 Figure 4 ............................................................................................................................ 37 9.4.2 Figure 7 ............................................................................................................................ 38 9.4.3 Figure 8 ............................................................................................................................ 38 9.5 Interface designs ......................................................................................... 38 9.6 Interface screenshots and user feedback .................................................... 41 9.7 User guide ................................................................................................... 45 9.8 Problems and solutions ............................................................................... 47 9.8.1 Applet browser compatibility ............................................................................................ 47 9.8.2 Response time ................................................................................................................. 48 9.8.2.1 Pack200 compression ............................................................................................... 48 9.8.2.2 Minification ................................................................................................................ 48 9.8.2.3 File combination ........................................................................................................ 48 9.8.3 Applet loading speed ....................................................................................................... 49 9.8.4 Checking Java support .................................................................................................... 49 9.8.5 Character escaping and replacing ................................................................................... 49 9.8.6 Theme switcher................................................................................................................ 50 9.9 Simplification example ................................................................................. 50 9.10 Relationship to the British Computer Society Code of Conduct................. 52 9.10.1 The public interest ......................................................................................................... 52 9.10.2 Duty to the relevant authority ......................................................................................... 52 9.10.3 Duty to the profession .................................................................................................... 53 9.10.4 Professional competence and integrity .......................................................................... 53 9.11 Relationship to the British Computer Society Code of Practice ................. 54 9.11.1 Maintaining technical competence ................................................................................ 54 9.11.2 Adhere to regulations ..................................................................................................... 54 9.11.3 Act professionally as a specialist ................................................................................... 54 9.11.4 Use appropriate methods and tools ............................................................................... 54 9.11.5 Manage your workload efficiently .................................................................................. 54 9.11.6 Participate maturely ....................................................................................................... 55 9.11.7 Promote good practices within the organisation ............................................................ 55 9.11.8 Represent the profession to the public .......................................................................... 55 9.11.9 Project management ...................................................................................................... 55 9.11.10 Security management .................................................................................................. 55 vii 9.11.11 System building ........................................................................................................... 56 9.11.12 Quality management .................................................................................................... 56 9.11.13 Research ...................................................................................................................... 56 9.12 Pseudocode .............................................................................................. 56 9.12.1 Java pseudocode ........................................................................................................... 56 9.12.1.1 ExpressionTree ....................................................................................................... 56 9.12.1.2 ExpressionTreeNode .............................................................................................. 57 9.12.1.3 ES_symja ................................................................................................................ 58 9.12.1.4 Log .......................................................................................................................... 59 9.12.1.5 CombineBases ........................................................................................................ 59 9.12.1.6 Rationalise............................................................................................................... 60 9.12.1.7 RationaliseBinomial ................................................................................................. 60 9.12.1.8 RationaliseMonomial ............................................................................................... 61 9.12.1.9 ReduceRoot ............................................................................................................ 61 9.12.1.10 RemoveAllHead .................................................................................................... 62 9.12.2 JavaScript pseudocode ................................................................................................. 62 9.12.2.1 ES Declaration ........................................................................................................ 62 9.12.2.2 ES Utilities ............................................................................................................... 62 9.12.2.3 ES Operators........................................................................................................... 64 9.12.2.4 ES Functions ........................................................................................................... 65 9.12.2.5 ES Variables............................................................................................................ 68 9.12.2.6 ES Argument Separators ........................................................................................ 68 9.12.2.7 ES Format ............................................................................................................... 68 9.12.2.8 ES Error ................................................................................................................... 71 9.12.2.9 ES Tokenize ............................................................................................................ 74 9.12.2.10 ES Shunting Yard .................................................................................................. 75 9.12.2.11 ES Process............................................................................................................ 76 9.12.2.12 ES Main ................................................................................................................. 81 9.13 Source code .............................................................................................. 85 9.13.1 XHTML/CSS source code .............................................................................................. 85 9.13.1.1 ExpressionSimplifierBeautified.xhtml ...................................................................... 85 9.13.1.2 ExpressionSimplifier.css ......................................................................................... 91 9.13.2 JavaScript source code ................................................................................................. 95 9.13.2.1 ES Declaration.js ..................................................................................................... 95 9.13.2.2 ES Utilities.js ........................................................................................................... 95 9.13.2.3 ES Operators.js ....................................................................................................... 99 9.13.2.4 ES Functions.js ..................................................................................................... 101 9.13.2.5 ES Variables.js ...................................................................................................... 107 9.13.2.6 ES Argument Separators.js ................................................................................... 107 9.13.2.7 ES Format.js.......................................................................................................... 108 9.13.2.8 ES Error.js ............................................................................................................. 114 viii 9.13.2.9 ES Tokenize.js ...................................................................................................... 119 9.13.2.10 ES Shunting Yard.js ............................................................................................ 121 9.13.2.11 ES Process.js ...................................................................................................... 123 9.13.2.12 ES Main.js ........................................................................................................... 133 9.13.3 Java source code ......................................................................................................... 150 9.13.3.1 ExpressionTree.java ............................................................................................. 150 9.13.3.2 ExpressionTreeNode.java ..................................................................................... 156 9.13.3.3 ES_symja.java ...................................................................................................... 159 9.13.3.4 ES_symja.php ....................................................................................................... 162 9.13.3.5 ES_symja.java (server side) ................................................................................. 162 9.13.3.6 Log.java ................................................................................................................. 165 9.13.3.7 CombineBases.java .............................................................................................. 166 9.13.3.8 Rationalise.java ..................................................................................................... 169 9.13.3.9 RationaliseBinomial.java ....................................................................................... 170 9.13.3.10 RationaliseMonomial.java ................................................................................... 171 9.13.3.11 ReduceRoot.java ................................................................................................. 173 9.13.3.12 RemoveAllHead.java ........................................................................................... 174 IMPORTANT NOTES: The system is available (assuming the CD/DVD drive is D) at: 1. file:///D:/7. Source Code/ExpressionSimplifierBeautified.xhtml 2. file:///D:/7. Source Code/minified/ExpressionSimplifier.xhtml The following web site may become inactive but the system may be accessible at http://www.csc.liv.ac.uk/~m9ram/ExpressionSimplifier.xhtml. The author recommends running the system on Microsoft Windows with the latest versions of Firefox http://www.mozilla-europe.org/en/firefox/ and the Java Runtime Environment http://www.java.com/en/. ix LIST OF TABLES Page Table 1: System compatibility .......................................................................................................... 19 Table 2: Final formatted expression tests ........................................................................................ 19 Table 3: Postfix token array expression tests .................................................................................. 20 Table 4: Final simplified expression tests ........................................................................................ 22 Table 5: Aim evaluation ................................................................................................................... 25 Table 6: Project log .......................................................................................................................... 33 Table 7: User guide operations........................................................................................................ 45 Table 8: User guide functions .......................................................................................................... 46 Table 9: Infix token array to postfix token array example ................................................................ 51 Table 10: Postfix token array subexpression formation and simplification example ....................... 51 x LIST OF FIGURES Page Figure 1: JavaScript files, functions and fields .................................................................................. 8 Figure 2: Java applet classes ............................................................................................................ 9 Figure 3: Use case diagram ............................................................................................................ 10 Figure 4: JavaScript sequence diagram .......................................................................................... 11 Figure 5: ExpressionTree applet sequence diagram (1) ................................................................. 12 Figure 6: ExpressionTree applet sequence diagram (2) ................................................................. 13 Figure 7: ES_symja (expression simplifier) applet sequence diagram ........................................... 14 Figure 8: System state diagram ...................................................................................................... 15 Figure 9: Atomic math object hierarchy ........................................................................................... 16 Figure 10: Function extensions ....................................................................................................... 16 Figure 11: Project plan .................................................................................................................... 34 Figure 12: Interface design 1 .......................................................................................................... 38 Figure 13: Interface design 2 .......................................................................................................... 39 Figure 14: Interface design 3 .......................................................................................................... 39 Figure 15: Interface design 4 .......................................................................................................... 40 Figure 16: Interface design 5 .......................................................................................................... 40 Figure 17: Interface design 6 (final) ................................................................................................ 41 Figure 18: Interface implementation 1 ............................................................................................. 41 Figure 19: Interface implementation 2 ............................................................................................. 42 Figure 20: Interface implementation 3 ............................................................................................. 42 Figure 21: Interface implementation 4 ............................................................................................. 43 Figure 22: Interface implementation 5 ............................................................................................. 43 Figure 23: Interface implementation 6 (final) ................................................................................... 44 Figure 24: Graph showing the number of seconds for each of 20 users to read the user guide and simplify an expression ...................................................................................................................... 44 xi ABBREVIATIONS MathML Mathematical Markup Language API Application Programming Interface W3C World Wide Web Consortium LGPL Lesser General Public License AST Abstract Syntax Tree CAS Computer Algebra System BCS British Computing Society RegEx(p) Regular Expressions RPN Reverse Polish Notation CRE (Macsyma) Canonical Rational Expression HCF Highest Common Factor GCD/F Greatest Common Divisor/Factor ECMA European Computer Manufacturers Association AJAX Asynchronous JavaScript and XML (X)HTML (Extensible) HyperText Markup Language CSS Cascading Style Sheet SDK Software Development Kit JRE Java Runtime Environment GNU GNU is Not Unix PHP PHP HyperText Preprocessor TCP Transmission Control Protocol GIF Graphics Interchange Format SVG Scalable Vector Graphics xii “Computer Algebra Systems are NOT the Devil but the new MESSIAH that will take us out of the current utterly trivial phase of human-made mathematics into the much deeper semi-trivial computer-generated phase of future mathematics...” ~ Doron Zeilberger xiii 1. INTRODUCTION This chapter briefly overviews the project including its scope, the problem addressed and its development, and the delivered solution with its justification based on originality and effectiveness. 1.1 Scope Computer algebra systems have been developed since the 1960s [1 p. 5] becoming integral in many fields especially those requiring accurate processing of large numbers of algebraic equations and so are particularly useful for research mathematicians, scientists and engineers. They have a wide variety of functionality including many forms of symbolic manipulation, arbitraryprecision numeric operations, graphing, etc. Despite the vast number of CASs available, only a handful are open source and thus free to use with even fewer being usable online [2]. This limits CAS public use with more advanced features requiring either proprietary software or the non-trivial setup of open source software. This project‟s objective is to produce a free and open source CAS accessible online with functionality unavailable in similar systems. In particular, allowing the manipulation of irrational expressions including surd reduction, decimal number rationalisation, and rationalisation of fractions with monomial surds or binomial expressions with square roots as denominators. While the primary interest is in numerical simplification, algebraic expressions are also considered. Moreover, to ensure the system is applicable as demonstration software a systematic simplification process is used. Transformations required for this process are applicable to other problems, for example, other CASs could use the extended Shunting Yard algorithm implementation. Also a combination of JavaScript and Java programming languages (the former being standardized [3] and both being high-level and quite popular [4]) coupled with API documentation improve the applicability of the system as demonstration software. 1.2 Problem statement 1.2.1 Original project proposal “Inspired by web sites such as: http://www.mathwarehouse.com/arithmetic/principal-nth-rootcalculator.php we aim to make a more advanced online tool (perhaps using javascript) that simplifies expressions that are constructed from the standard arithmetic operators as well as square roots, cube roots etc. Issues to be addressed include representing these expressions in such a way that they can be manipulated and simplified, parsing a user's input, and displaying the results.” [5] 1.2.2 Original aims derived from the first project proposal: 1. (Essential) Simplification must support standard arithmetic operators as well as square and cube roots. 2. (Essential) Parsing user input must produce expressions represented in a way allowing manipulation and simplification. 3. (Essential) Users must have their results displayed. 1.2.3 Interpreted aims derived from expanding on the original aims: 1. (Essential) Formatting must make a user input expression string given in infix notation processable. 2. (Essential) Tokenizing a formatted expression string must produce an infix token array. 3. (Essential) Converting an infix token array into a postfix token array is necessary. 4. (Essential) Converting a postfix token array into a series of subexpressions forming simplification steps is required. 5. (Essential) Converting between notations specifically infix and postfix must be reversible. 1 6. (Essential) Formatting, converting and simplifying a valid input expression must produce subexpressions that together have, and a final simplified expression that alone has the same mathematical meaning. 7. (Essential) A final simplified infix expression string with simplification steps must be output. 8. (Optional) A final formatted infix expression string with formatting steps should be output. 9. (Optional) Outputting postfix and expression tree expression string equivalents of the formatted expression. 10. (Conditional) Each simplification step should be clear in itself and in how it relates to the overall expression. 1.2.4 Extended aims: 1.2.4.1 Derived from further discussion: 1. (Essential) The system must be applicable as demonstration software. 2. (Essential) Rationalising fractions with monomial and binomial denominators must remove surds and square roots respectively in them producing equivalent fractions. 3. (Optional) Multiplying bases raised to the same exponent should produce the multiplication of those bases raised to that exponent. 1.2.4.2 Derived from further analysis: 1. (Essential) The simplification process must take a reasonable amount of time based on input expression complexity. 2. (Essential) New users fluent in reading and understanding English with a basic knowledge of elementary arithmetic must be able, at least partly, to use the web site‟s interface within 5 minutes after it has loaded. 3. (Optional) The logarithm of a number to any base should be equal to the base x logarithm of that number divided by the base x logarithm of that base, where x is some positive real number. 4. (Optional) Reducing a reducible surd should give an irreducible surd multiplied by a numerical factor. 5. (Optional) Rationalising a finite or repeating decimal number should produce an equivalent fractional number. 6. (Conditional) The web site should have a practical loading time based on users‟ connection speeds. Essential aims required completion for the project to be successful. Optional aims were not required for project success but do support it. Conditional aims are optional and subjective. 1.3 Approach The XHTML web page serves the interface, embeds the other two components, and provides the user guide and World Wide Web Consortium (W3C) validation and Lesser General Public License (LGPL) links. The JavaScript functions control the interface, utilise the Java applets, pass results between components, transform input into a processable form, and show systematic manipulations. The Java methods convert into, draw, provide expressions for, and simplify expression trees/ASTs. Many free online systems limit input to a specific function, for example, the “Principal Nth Root Calculator and Reducer” allows the user to enter a surd root and number to be reduced and evaluated in two input text boxes but does not support further processing [6]. However, in this project users can input any infix expression using standard script notation into the system and have it attempt simplification. Both manual and computational tests (via third party software) check the equivalence between transformations. The system can attempt to make invalid input expressions valid through formatting, but they may end up with different meanings. 2 1.4 Solution and justification To summarise, the project completed successfully with all aims listed in section 1.2 achieved producing a web page supporting the simplification of numerical expressions with a given set of functions and operators including addition, subtraction, multiplication, division, and/or exponentiation. The system‟s original aspects include the following: 1.4.1 JavaScript functions to: Automatically detect and correct errors in user input infix expression strings. Tokenize a user input infix expression string into an infix token/object array. Convert an infix token array into a postfix token array based on the extended Shunting Yard algorithm described by Robin Sheat [7]. Decompose a postfix token array into a series of subexpressions. Convert a postfix subexpression into an infix subexpression. Output formatting steps in standard script notation. Output simplification steps in standard script and Mathematical Markup Language (MathML) [8] notations. 1.4.2 Java functions to: Rationalise a fraction with either a monomial surd or a binomial expression with (a) square root(s) as its denominator. Multiply bases to the power of the same exponent to produce the multiplication of those bases raised to that exponent. Reduce a numerical surd to give an irreducible surd multiplied by a numerical factor. Draw an expression tree and provide an expression for that tree given a series of postfix token string and operand count integer pairs. The operand count refers to the number of operands a postfix token requires. All functions/methods are open source and thus applicable for demonstration purposes such as learning aids. However, there were weaknesses identified which are as follows: firstly, there is no formal proof to guarantee successful simplification of all valid input expressions although all tests have been successful. Secondly, the system struggles with expressions involving very large numbers. Finally, Google Chrome 5, Safari 5, and Opera 10.6 only have partial support for MathML; later versions will likely fully support it since it is an emerging W3C standard. Solutions to these weaknesses would enhance the system but are not required for its success and form the basis for some future developments discussed in section 7. 1.5 Outline This work provides a background/introduction into CAS with information on representing and manipulating expressions. Section 2 explains concepts the project required and overviews examples of free online CASs. Section 3 provides full design details. Section 4 discusses the system implementation, deployment and testing. Section 5 evaluates the system with respect to all project aims. Section 6 talks about the project relationship to the British Computing Society (BCS) code of conduct and practice. Section 7 gives the main findings and possible future developments. Section 8 has references and section 9 provides appendices. 3 2. BACKGROUND This chapter looks at key concepts relevant to the project and an overview of a selection of free online CAS. For additional background information, please refer to section 9.3. 2.1 Tokenization Before simplifying, an input expression it must be decomposed into meaningful elements/tokens. Automatic tokenization could involve inputting the expression using buttons with each corresponding to a token like with handheld calculators. However, with a sequence of text characters (a string) these need splitting into tokens. A lexical analyzer/scanner or lexer often exists as individual functions called by a parser or another function and performs this process known as lexical analysis. Regular expressions or lexemes usually specify sets of characters composing each token. Tokenization then takes a character input stream and attempts lexeme matching to form tokens. Token formation can form a separate lexical analysis evaluation stage where the token type is set as the lexeme type and the token value is instantiated to the matched characters. Generally, lexers only generate tokens leaving parsers to handle them. Moreover, lexers are finite state machines reading characters one-by-one into finite memory. Upon matching a lexeme, a state transition takes place with actions to replace currently recognized characters in memory with the corresponding token representation and to move back to the initial state. The maximal munch or longest match rule says keep adding characters until no lexemes match and then backtrack by one character and return the matching lexeme. If this leaves multiple matching lexemes then the lexer must choose between them, skip the recognized characters, or return an error. If there is no matching lexeme then the lexer probably encountered a character that does not match any so it must either skip it or return an error. 2.2 Notations and operator precedence Infix notation is the common arithmetic and logical formula notation placing operators between operands (e.g. 1+3). This contrasts with prefix notation putting operators before operands (e.g. + 1 3) and postfix notation using operators after operands (e.g. 1 3 +). Prefix and postfix notations are also called polish and reverse polish notations (RPN) respectively referring to the nationality of Jan Łukasiewicz who invented prefix notation in the 1920s [9]. RPN was put forward by Burks, Warren and Wright in 1954 [10]. Infix notation leads to ambiguities when two operators surround an operand (e.g. 1+3*4) because one has to decide which operator binds more closely and evaluates first. This can be determined using commonly accepted and unambiguous rules often called precedence rules eliciting the operation order. According to Stroud, for an infix expression you would process brackets, powers, multiplications/divisions, and additions/subtractions in that order. Thus, brackets are useful for surrounding parts of an expression that require evaluation priority where the evaluation of nested brackets occurs from the innermost to the outermost. For each one you would work across the expression left to right searching for occurrences and trying to process them [11 p. 11, 43]. Powers or exponents can also express roots in that the nth root of an operand equals putting that operand to the power of 1/n. The main advantage of RPN is that precedence rules and brackets are unnecessary because it expresses operations in the correct evaluation order but operands require a separator to distinguish between them. 2.3 Infix to postfix Edsger Dijkstra invented the Shunting Yard algorithm, named so as its operations resemble a railroad-shunting yard, it parses mathematical equations represented in infix notation and outputs the equivalent RPN or AST. It temporarily uses a stack to hold operators until they are ready to be output as well as input and output strings. Since it reads and prints each token once and pushes and pops each function, operator, or parenthesis once, there are a constant number of operations executed per token. Thus, the algorithm has linear time complexity (i.e. its running time is O(n) or linearly dependent on input size) [12]. Although the classic algorithm can handle functions the number of arguments must be predefined and so one extension, described by Robin Sheat, allows variable numbers of arguments [7], it works using four stacks. The output stack has numbers and variables pushed directly onto it. When pushing an operator with left/right associativity onto the 4 operator stack the algorithm pops any operators of equal-or-higher/higher precedence respectively and pushes them onto the output stack. The operator stack also has function identifiers pushed onto it, another stack holds an integer for each function representing the operand that function is waiting for to be output or has currently had output. Whenever encountering an argument separator, such as a comma, within a function its value on this stack is incremented. The final stack holds a Boolean value for each function currently on the operator stack, which is true if the operand it is up to has been output. When encountering a closing bracket belonging to a function, the algorithm pops that function and pushes it onto the output stack whilst removing any values associated with it on other stacks. 2.4 Expression representation To deal with expressions such as polynomials effectively requires a data structure more dynamical than a standard string representation. One option is a dense representation, which stores all the coefficients of a univariate polynomial in an array. However, this is inefficient for polynomials with high degrees, for example would have an array of 1001 coefficients even though only two coefficients are necessary. Another option is to use sparse representation where a univariate polynomial is represented using pairs of corresponding exponents and coefficients for each polynomial term. So for example – would be represented as the list ((1000 2)(0 -2)). Using both representations in algorithms adding two polynomials with n terms, would yield a time complexity linearly dependent on the number of terms (i.e. O(n)). A third option uses recursive representation with exponent ordering (e.g. alphabetical) to represent multivariate polynomials. The variable chosen first is the main variable of the polynomial and coefficients of powers of the main variable will be polynomials in other variables. This means a list of triples represent polynomials. So for example – would be represented as the list ((x 4 ((y 5 1) (y 1 4))). Macsyma Canonical Rational Expression (CRE) is a fourth option based on recursive representation except more succinct, for example – would be represented as ((x 4 (y 2 (z 1 2) 0 – 3) 1 - 8).1). Finally recursive representation in Reduce would represent – as (((x.4).1) ((x.2).2) ((x.1).-3).12). Overall recursive representation seems the best option because it is conceptually simple, has lower memory overhead than dense representation and is more general than sparse representation. 2.5 Existing online tools 2.5.1 Principal Nth Root Calculator and Reducer This calculator uses Flash and works out the reduced form of a user input nth root of another user input number by taking out the largest factor of the number equal to the root to some whole power and multiplying the root by that power with evaluation calculating the original root. It has a basic coloured user interface, which only allows users to enter numbers. [6] 2.5.2 Expression Calculator This calculator primarily supports combining like terms and expanding brackets for multivariate polynomials. It has one input text box for the user to enter an expression requiring the use of the carrot symbol “^” to represent exponents and a single “simplify” button. Exponents must be positive integers and only be used on variables. Unsupported operators include division, square root, radicals, and fractions. It explains each simplification step and has a simple user interface with clear instructions. [13] 2.5.3 Algebra Simplifier and Math Solver that SHOWS WORK This calculator supports everything that the expression calculator supports as well as negative and fractional exponents on any term and division but it does not support root rationalisation. Like the expression calculator, it combines one input text box and simplify button. Another improvement is that the simplification explanations are more detailed and it is even able to produce a cartoon animation for the process. [14] 5 2.5.4 Google's built-in calculator function This calculator supports numerical computation and a variety of functions but does not allow algebraic simplification because using letters or words as variables would interfere with Google‟s search engine. Similarly, to previous calculators it has a simple user interface with a single input text box and one search button. [15] 6 3. DESIGN This chapter provides full design details based on agile/lean/rapid development methodologies, the spiral model and oriented design. These have allowed for rapid cyclic development with an interlaced design and implementation developed synchronously producing a series of prototypes refined throughout iterations. 3.1 Components and structure The system has three main components: the web page which provides the user interface and embeds the other two components, the JavaScript which controls the interface, invokes the applets, formats a user input infix expression and prepares it for simplification (see figure 1), and the Java applets which draw an expression tree and perform simplifications (see figure 2). The intention is for one human user to use the web page (see figure 3) although multiple users could request it simultaneously with each instance executed in an independent environment (i.e. within each user‟s browser). With a 28Kb/s (kilobits per second) dial-up modem downloading the expression simplifier applet, which is approximately 851KB (kilobytes), would take (4 minutes and 3 seconds). Thus whilst the expression simplifier applet is loading requests sent to it are redirected to a server side invocation using AJAX [16] saving time. 7 Figure 1: JavaScript files, functions and fields 8 init initialises components and overrides the paintComponent method of panel to draw the expression tree. addPostfixToken pushes a postfix token and its argument count onto the postfixTokens stack and clearPostfixTokens removes all elements on the stack. buildRootTree constructs the expression tree using tokens on the postfixToken stack. getExpression returns the expression tree expression. isReady returns true indicating the applet is accessible. ExpressionTreeNode sets the content of the node and initialises its children. getContent returns the content of the node. addChild/removeChild and addChildAt/removeChildAt add/remove node children to/from the current node‟s children vector returned using getChildren. getTreeContent returns an expression tree expression String assuming the current node is the root of the tree. init adds extension classes to the system namespace, initialises symbols and calls a JavaScript function informing the web page it is loaded. simplify simplifies an input infix expression within an input amount of time in milliseconds or returns the input. getMathMl returns the MathML equivalent of an input infix expression. isReady returns true indicating the applet is accessible. Figure 2: Java applet classes 9 Figure 3: Use case diagram <<extends>> indicates optional use cases possibly not ran in their respective base use cases <<includes>> indicates included use case behaviour is inserted into base use case behaviour 3.2 Data structures and algorithms An input infix expression string is formatted by various JavaScript functions through its regular expression engine and the XRegExp [17] library. Tokenization then transforms the formatted string into an array of JavaScript object tokens namely numbers, variables, operators, functions, and argument separators. The extended Shunting Yard algorithm converts this infix token array into a postfix token array. The expression tree applet uses this postfix token array to draw an expression tree, and the JavaScript further uses it to form subexpressions (see figures 4, 5 and 6). Using postfix notation allows easy construction of the expression tree through recursion using the following method: method buildPostfixTree() argumentCount = pop last value off postfixTokens stack and cast it as an integer node = pop last value off postfixTokens stack, use it to create a new ExpressionTreeNode for (1 to argumentCount) add a child node to node by recursively calling this method return node end method 10 Additional explanation in section 9.4.1 Figure 4: JavaScript sequence diagram 11 This represents the JavaScript that is relevant to the system but not relevant to these lifelines because other entities could invoke these methods such as humans through command line arguments or other programs. This represents a JPanel that is responsible for drawing the expression tree when the display is refreshed (see figure 6). Figure 5: ExpressionTree applet sequence diagram (1) 12 The paintComponent, paintTree and paintNode methods do not return values instead they operate on a graphics object reference propagated through them. Figure 6: ExpressionTree applet sequence diagram (2) Converting each subexpression into infix notation and passing it to the expression simplifier applet as a string allows further parsing and simplification the result of which becomes an operand for a subsequent operation or function and forms a simplification step (see figure 7). The system runs continuously until the user closes the web page (e.g. by navigating away) and the simplification process starts when the user either directly or indirectly presses the simplify button (see figure 8). 13 Additional explanation in section 9.4.2 Figure 7: ES_symja (expression simplifier) applet sequence diagram 14 Additional explanation in section 9.4.3 Figure 8: System state diagram Atomic math objects include numbers that are integers, floating point, fractional and complex as well as patterns, strings and symbols. Although the intention was not to deal with complex numbers they are partially supported by allowing access to the complex number i ( ), also patterns and Strings are only used for internal representation and processing. Each atomic math object has its own class deriving from a common expression ancestor object (see figure 9). Abstract syntax trees (ASTs) derived from lists represent operations and functions with a symbol placed at the first index or head of the AST to act as an identifier with arguments in following indices which may be atomic or ASTs forming nested lists. For example, the system would convert ( into . Function definitions derive from a common interface making the system readily extensible with new functionality. Each function class has at least a (possibly inherited empty) constructor and an evaluate method which takes an AST as its only argument, it may also implement a setUp() method which takes one or more symbols as arguments to be applied to the input like HoldFirst which would prevent the first argument being evaluated (see figure 10). 15 Figure 9: Atomic math object hierarchy Figure 10: Function extensions Functions to reduce and rationalise monomial surds and to rationalise binomial expressions with square roots for fraction denominators require functions to find logarithms to any base and to rationalise decimal numbers. Monomial and binomial denominators with surds represented as and respectively can be rationalised by multiplying them by and respectively where, x, y, a, b are numbers (y may also be a square root), is a rational number and or another integer. Reducing a monomial surd represented as would involve taking out the largest factor of x that is equal to n to some integer power, calculating the upper limit of this involves taking the base n logarithm of x. The goal would be to reduce the surd into an irreducible form where . If input exponents are decimal numbers, it is necessary to convert them into rational fractional numbers for rationalisation. Multiplying the decimal number by the smallest power of 10 that makes it an integer and then creating a fraction where this integer is the numerator and the power of 10 is the denominator accomplishes this. The fraction may be reduced by dividing numerator and denominator by their greatest common divisor using for example the Euclidean algorithm [18]. An additional function multiplies bases with the same exponent such that becomes . Another function takes an AST and removes all unary function heads of a given type with the recursive method below: method removeAllHead(head, functionList) for (all arguments in functionList) if (the current argument is an AST) then set the current argument at this position to the result of recursively calling this method with head and the current argument as parameters end for loop if (functionList is an AST with head as its first argument) then return its second argument else return functionList end method 16 3.3 Evaluation 3.3.1 Criteria The evaluation criteria match the aims in section 1.2. 3.3.2 Assessment: Ensuring there is equivalence between valid inputted, formatted, converted, and simplified expressions through manual and computational comparisons using the system and third party software. Timing a sample of random new users to ensure they can use the system within 5 minutes after it has loaded. Calculating system loading time for the 2009 average UK broadband speed of 3.6Mbit/s (megabits per second) [19] and checking it falls below 3 minutes. 3.4 Interface description The interface uses jQuery [20] and jQuery UI plug-ins [21] to allow dynamic component modification and provide a consistent style and theme. Vertically central components are draggable and resizable and the user input text box has an autocomplete drop down menu for function suggestions. The user can choose MathML to render the simplification steps. Conversion from standard script notation to MathML is accomplished either using JavaScript ASCII2MathML [22] functions or the expression simplifier applet toMathML method based on JEuclid [23]. 17 4. REALISATION This chapter gives implementation, deployment, and testing details. Specific examples of problems encountered and solutions devised are in section 9.8. 4.1 Implementation 4.1.1 Software Netbeans IDE [24] and Eclipse [25] were used in combination for Java programming whilst Adobe Dreamweaver [26] was used for JavaScript programming with RegexBuddy [27] supporting regular expression formation. Additionally JSLint by Douglas Crockford [28] was used to test JavaScript code quality and Javascript unpacker and beautifier by Einar Lielmanis [29] helped ensure it was understandable and formatted nicely. Finally XHTML correctness was tested using the W3C Validator [30]. 4.1.2 Libraries For JavaScript the jQuery [20] and jQuery UI [21] libraries were used for interface improvements, the ASCII2MathML library [22] was used to convert infix expression strings into MathML, the XRegExp library was used to escape certain characters in regular expressions and PluginDetect was used to detect Java support. For Java the Matheclipse [31] and symja [32] libraries were used to provide atomic math object implementations and methods supporting String to AST transformations and manipulations. In turn the latter library uses The Apache Commons Mathematics Library [33], The Java Algebra System (JAS) [34] and JEuclid [23]. 4.2 Deployment 4.2.1 Packaging Using the Fat Jar Eclipse Plug-In to package the Eclipse java-projects allowed them to be output as single JAR files. Fatjar automatically includes referenced classes and/or JARs meaning no classpath needs to be set and no additional JARs need deploying. The deployment of both the expression simplifier and expression tree applets made use of it but it was particularly useful for the expression simplifier applet as it used multiple external libraries some of which were imported and updated regularly from subversion repositories (SVN). [35] 4.2.2 Compression The expression simplifier applet was the largest component with a size of 4.58MB (megabytes). Using a tool called Pack200 for compression reduced the size by approximately 73% to 1.24MB. The high compression ratio was due to the JAR file having a large Java class file density which Pack200 works most efficiently on. Low fidelity mode gained a further 31% reduction from 1.24MB to 0.85MB or an overall reduction of 81% from 4.58 MB to 0.85MB. The other components were minified using JS Minifier by Frank Marcia [36] for JavaScript, CSS Compressor & Minifier by Streamlined Fusion [37] for stylesheets and HTML Minifier by William Peavy [38] for XHTML minified. Moreover stylesheets were combined manually and JavaScript files were combined using a Java application called JsCombine [39] reducing the number of HTTP requests and improving response time. 4.2.3 Browser Compatibility Java applets can be embedded into web pages using applet or object tags however the applet tag has been deprecated by the W3C [40]. Therefore the system was made compatible with browsers officially supporting the object tag namely Internet Explorer 8, Firefox 3.6.8, Google Chrome 5, Safari 5 and Opera 10.60 [41]. The following table indicates the level of compatibility achieved with these browsers. 18 Browser Version XHTML jQuery JavaScript Java Internet Explorer Firefox Opera Google Chrome Safari 8 Yes Yes Yes Yes Pack200 Compression Yes MathML 3.6.8 10.60 5 Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes, with MathPlayer Yes Partial Partial 5 Yes Yes Yes Yes Table 1: System compatibility Yes Partial A temporary solution to partial MathML support altered simplification steps to include their equivalent script notation, which also made copying them easier. The web page has a consistent “look and feel” across all the browsers except for minor browser specific customizations. Additionally a theme switcher was added allowing users to select from a variety of pre-made themes, all of which are available for download from the JQuery-UI website [21]. There are some accessibility advantages when using Internet Explorer 8 with MathPlayer [42] including the ability to zoom in on MathML expressions and have them spoken. However since users must install this tool manually Firefox is the recommended browser for using the system as it is seemingly the only one to natively fully support MathML. Minor changes to the web page include changing the GPL image to the LGPL image and converting it into a link to the GNU website allowing users to access the license readily. In addition, solely for Internet Explorer 8, placing a link to download MathPlayer in the bottom right (after the validation logos) allowing users to easily download and install it. 4.3 Testing 4.3.1 Final formatted expression Run 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. Input Expected Result Actual Result x++x x+x x+x x+-x x-x x-x x+-+-+-+-+x x+x x+x x--++---x x-x x-x 5*(x+x 5*(x+x) 5*(x+x) x+x)*5 (x+x)*5 (x+x)*5 (x+(x+(x (x+(x+x)) (x+(x+x)) x)-x)-x) ((x-x)-x) ((x-x)-x) ((5+6)(())))*((5/7(() (5+6)*(5/7) (5+6)*(5/7) ((x+x)) (x+x) (x+x) Sin(((x+x))) Sin((x+x)) Sin((x+x)) ((((1+2)))+3)) ((1+2)+3) ((1+2)+3) 1+2() 1+2 1+2 (((()))+3) 3 3 £$%3#5&& 35 35 35-635-6 35-6 x-*x x*x x*x x+x^ x+x x+x ^x-x x-x x-x 3 + 5 / 2 3+5/2 3+5/2 xxxxx x*x*x*x*x x*x*x*x*x xTimes(xx,xx)x x*Times(x*x,x*x)*x x*Times(x*x,x*x)*x Sin(60)xsin(60) Sin(60)*x*sin(60) Sin(60)*x*sin(60) sin(60)sin(60)sin(60) sin(60)*sin(60)*sin(60) sin(60)*sin(60)*sin(60) xsin(sin(sin(60)))x x*sin(sin(sin(60)))*x x*sin(sin(sin(60)))*x Table 2: Final formatted expression tests 19 4.3.2 Postfix token array expression Run 1. 2. 3. 4. Expected Result Actual Result 32^32^345-+6* 345-+6* 342*15-23^^/+ 342*15-23^^/+ 12 1 3 / 4 2 ^ * + 12 1 3 / 4 2 ^ * + random ceil + random ceil + 5. maxn(4+5,1+2^2,(8+6)*10) 4 5 + 1 2 2 ^ + 8 6 + 4 5 + 1 2 2 ^ + 8 6 + 10 * maxn 10 * maxn Table 3: Postfix token array expression tests 4.3.3 Final simplified expression Run 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. Input -3^2 (3+(4-5))*6 3+4*2/(1-5)^2^3 12+1/3*4^2+ceil(random()) Input 31+11 -25+93 43-79 -98-37 2*8 -2*-8 -7*6 9*-3 6/3 -6/-3 -4/10 1/-3 2^2 2^-2 -3^3 -3^-3 sin(60) Sin(x) asin(0.4) ArcSin(x) cos(60) Cos(x) acos(0.4) ArcCos(x) tan(60) Tan(x) atan(0.4) ArcTan(x) exp(2) ceil(4.2) floor(4.8) abs(-9) abs(9) roundTo(0.015,2) power(4,2) Power(x,x) times(4,2) Times(x,x) divide(4,2) Divide(x^3,x^2) plus(4,2) Expected result 42 68 -36 -135 16 16 -42 -27 2 2 -2/5 -1/3 4 1/4 -27 -1/27 -0.304810621 Sin(x) 0.411516846 ArcSin(x) -0.95241298 Cos(x) 1.15927948 ArcCos(x) 0.320040389 Tan(x) 0.380506377 ArcTan(x) 7.3890561 5 4 9 9 0.02 16 x^x 8 x^2 2 x 6 20 Actual result 42 68 -36 -135 16 16 -42 -27 2 2 -2/5 -1/3 4 1/4 -27 -1/27 -0.3048106211022167 Sin(x) 0.41151684606748806 ArcSin(x) -0.9524129804151563 Cos(x) 1.1592794807274085 ArcCos(x) 0.320040389379563 Tan(x) 0.3805063771123649 ArcTan(x) 7.38905609893065 5 4 9 9 0.02 16 x^x 8 x^2 2 x 6 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. Plus(x,x) minus(2,2) Minus(5*x,x) nroot(4,625) Nroot(4,2) randomBetween(1,10,2) maxn(1,10,100) minn(1,10,100) avg(1,10,100) Expand((x+1)*(x+2)) Expand((2-3^(1/2))*(1+2*3^(1/2))) Factor(x^2+3*x+2) Hold(2+2) x+x -x+x x-x -x-x x*x -x*-x -x*x x*-x x/x -x/-x -x/x x/-x x^y -x^y x^-y -x^-y x^0 -x^0 log(100) log(0) log(-1) log(x) logn(10,100) logn(10,0) logn(10,-1) logn(-1,10) logn(x,y) Log(10,100) Log(10,0) Log(10,-1) Log(-1,10) Log(x,y) 32^(1/4) -64^(1/4) 540^(1/2) 75^(1/2)-27^(1/2) 12^(1/2)*8^(1/2)*98^(1/2) 2^(1/2)+1-2*3^(1/2)+4*2^(1/2)-3 Rationalize(0.5) Rationalize(-0.125) Rationalise(x^-0.5) Rationalise(x^0.125) RationaliseMonomial(2^(-1/2)) 2*x 0 4*x 5 2^(1/4) N/A 100 1 37 x^2+3*x+2 3*3^(1/2)-4 (x+1)*(x+2) 2+2 2*x 0 0 -2*x x^2 x^2 -x^2 -x^2 1 1 -1 -1 x^y -x^y x^-y -x^-y 1 -1 2 -Infinity NaN NaN 2 -Infinity NaN NaN NaN 2.0 -Infinity NaN NaN NaN 2*2^(1/4) (-2)*4^(1/4) 6*15^(1/2) 2*3^(1/2) 56*3^(1/2) -2*3^(1/2)+5*2^(1/2)-2 1/2 -1/8 x^(-1/2) Rationalise(x^0.125) 1/2*2^(1/2) 21 2*x 0 4*x 5 2^(1/4) 1.44 100 1 37 x^2+3*x+2 3*3^(1/2)-4 (x+1)*(x+2) 2+2 2*x 0 0 (-2)*x x^2 x^2 (-1)*x^2 (-1)*x^2 1 1 -1 -1 x^y (-1)*x^y x^((-1)*y) (-1)*x^((-1)*y) 1 -1 2 -Infinity NaN NaN 2 -Infinity NaN NaN NaN 2.0 -Infinity NaN NaN NaN 3*7^(1/2) (-2)*4^(1/4) 6*15^(1/2) 2*3^(1/2) 56*3^(1/2) -2*3^(1/2)+5*2^(1/2)-2 1/2 -1/8 x^(-1/2) x^(1/8) 1/2*2^(1/2) 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. RationaliseMonomial(x^(-1/4)) 2/3*RationaliseMonomial(2^(-1/2)) RationaliseBinomial((5^(1/2)+6)^-1) RationaliseBinomial((x^(1/2)-y)^-1) Expand((2+3^(1/2))*RationaliseBinomial((1+2^(1/2))^1)) CombineBases(Hold(5^8)*Hold(6^8)) CombineBases(x^8*y^8) CombineBases(-9^z*1^z*8^z) TrigToExp(Sin(x)) Sin(x)^2+Cos(x)^2 E^(I*Pii(1)) TrigToExp(ArcSin(x)) TrigToExp(Cos(x)) TrigToExp(ArcCos(x)) x^(3/4)*x^(-1) 1/3*2^(1/2) (-1/31)*(5^(1/2)-6) (-y-x^(1/2))*(y^2-x)^(-1) 2^(1/2)*3^(1/2)3^(1/2)+2*2^(1/2)-2 30^8 (x*y)^8 (-72)^z -I*1/2*E^(I*x)+I*1/2*E^(-I*x) 1 -1 -I*Log((-x^2+1)^(1/2)+I*x) 1/2*E^(I*x)+1/2*E^(-I*x) I*Log((x^2+1)^(1/2)+I*x)+1/2*Pi I*(-E^(I*x)+E^(I*x))*(E^(I*x)+E^(-I*x))^(-1) I*1/2*Log(I*x+1)+I*1/2*Log(I*x+1) 5 (3, -3) Table 4: Final simplified expression tests 112. TrigToExp(Tan(x)) 113. TrigToExp(ArcTan(x)) 114. 115. Roots(2*x-10) Roots(x^2-9) 22 x^(3/4)*x^(-1) 1/3*2^(1/2) (-1/31)*(5^(1/2)-6) (-y-x^(1/2))*(y^2-x)^(-1) 2^(1/2)*3^(1/2)3^(1/2)+2*2^(1/2)-2 30^8 (x*y)^8 (-72)^z -I*1/2*E^(I*x)+I*1/2*E^(-I*x) 1 -1 -I*Log((-x^2+1)^(1/2)+I*x) 1/2*E^(I*x)+1/2*E^(-I*x) I*Log((x^2+1)^(1/2)+I*x)+1/2*Pi I*(-E^(I*x)+E^(I*x))*(E^(I*x)+E^(-I*x))^(-1) I*1/2*Log(I*x+1)+I*1/2*Log(I*x+1) (5) (3, -3) 5. EVALUATION This chapter gives a critical appreciation of the project outlining its strengths and weaknesses through discussion of all aims in section 1.2 in the following table. Aim 1.2.2 1 Achieved Yes 1.2.2 2 Yes 1.2.2 3 Yes 1.2.3 1 Yes 1.2.3 2 Yes 1.2.3 3 Yes 1.2.3 4 Yes 1.2.3 5 Yes How Tokenization, transformations, and simplification recognize standard arithmetic operators including exponentiation, multiplication, division, addition, and subtraction. Additionally they recognize parentheses and the difference between unary and binary minus. Exponentiation can represent square and cube roots and there are some functions for performing roots such as Nroot. The system parses user input into processable infix expression strings equivalent to the original input given there are no significant input errors. It tokenizes the final formatted expression into an infix token array for easy manipulation. Decomposing a postfix token array, obtained through converting this infix token array, forms subexpressions, which become simplification steps and are passed to the expression simplifier applet, which parses them into ASTs, for simplification. The system relays each formatting and simplification step back to the user using standard script notation. The user can also choose MathML rendering of simplification steps depending on browser compatibility. Moreover, formatting steps will highlight detected errors to show how each step is proceeding. Simplification steps will highlight where each subexpression occurs in the overall expression with before, simplification and after steps. Each simplification is numbered and has a title for its operation or function. By passing the user input infix expression string through a variety of functions, most of which make use of JavaScript‟s regular expression engine, it becomes processable. Although the formatted string will likely return a result, it may not be as expected if the original form had too many errors. However, since formatting steps highlight errors the user should have enough information (as well as by reading the user guide) to correct them to produce the right result. By marking the beginning and end of function names and by replacing unary minus with an underscore character tokenizing the formatted string into an infix token array is successful. Function marking and token recognition, similarly to formatting, use regular expressions and so it is important to escape characters where necessary. Each token has its own object and these include Number (integers or floating-point numbers), Variable (single letters a-zA-Z), Operator ((, ), ^, _, *, /, +, -), AFunction and ArgumentSeparator (,). Representing tokens as objects makes them easily recognizable and allows the definition of token specific fields and functions. By implementing the extended Shunting yard algorithm as described by Robin Sheat [7] a function was made which can convert an infix token array into a postfix token array including the ability to handle functions with varying numbers of arguments. Since postfix expressions do not have parentheses and evaluation only requires processing them from left to right, subexpressions can be more easily formed from them (as opposed to infix expressions). This involves pushing operands onto an operand queue and taking enough off upon encountering a function or operator to form an operation. To show subexpression positions within the overall expression the previously converted postfix expression needed conversion back into an infix expression so it could be output with subexpressions highlighted within it. Forming all subexpressions without simplifying 23 1.2.3 6 Yes (so far) 1.2.3 7 Yes 1.2.3 8 Yes 1.2.3 9 Yes 1.2.3 10 Yes (subjectively) 1.2.4.1 1 Yes 1.2.4.1 2 Yes 1.2.4.1 3 Yes 1.2.4.2 1 Yes 1.2.4.2 2 Yes them along the way accomplished this. All tests indicate mathematical equivalence between validly input, formatted, and simplified expressions (including simplification steps). This requires most of the system to operate correctly and since there are infinite test cases, a rigorous mathematical proof may be the only way to provide confidence in the system‟s reliability. System modularity also helps and is why there are more outputting stages than necessary. In addition, it is why the XHTML, JavaScript, and Java components have functionality split between and within them. The text box representing the simplification result has its value set in standard script notation as the final subexpression (the overall simplification). The formatted expression text box has a value set using standard script notation as the final formatting step, which is the final formatted expression. The postfix and expression tree expressions generated from converting the postfix token array to a string and by reading the expression tree left to right top to bottom respectively are set as the values of their respective text boxes. This aim is conditional because its success depends on user experience, in particular mathematical and computer skills. However, feedback suggests users see simplification steps as related to the overall expression. Moreover, an objective attempt towards this goal modified simplification steps to include each subexpression position within the overall larger expression. A high level of public access is available since the system has an online web page as its user interface compatible with all major web browsers. Moreover since the system is licensed under the LGPL [43] users can apply the XHTML, CSS, JavaScript and/or Java source code to their own purposes (e.g. learning material). The user guide contains links to the source code. Two Java methods namely RationaliseMonomial and RationaliseBinomial successfully achieve the required functionality. Creating an additional Hold function prevented further simplification. For example, Hold(2+2) prevents the simplification to 4. These methods are intended to be called with minimal input for example instead of RationaliseMonomial(5*x^-0.5) the user would be expected to specify 5*RationaliseMonomial(x^-0.5). CombineBases successfully achieves the required functionality and is intended to be called with minimal input for example instead of CombineBases(2^x*3^x*4^x) the user would be expected to specify CombineBases(CombineBases(2^x*3^x)*4^x) although it does have a limited ability to handle larger expressions. Using AST representation, it can easily identify the powers and separate their bases and exponents for manipulation. Most simplifications take between less than a second and a few seconds however, this time increases with input complexity. In particular, if expressions resulting in extraordinarily large numbers are used the simplification may not complete. Prohibiting users from entering very large numbers would not be a suitable solution as firstly, expressions simplifying to them could still be input and secondly it limits the system, which could have access to increased computing power in the future. However, a possible future extension could allow users to specify a number limit or base one on the detection of hardware/software capabilities. According to feedback, users could use the web site within 5 minutes after it had loaded due to an easily accessible user guide, an intuitive web site layout and clear formatting and simplification steps. 24 1.2.4.2 3 Yes 1.2.4.2 4 Yes 1.2.4.2 5 Yes 1.2.4.2 6 Yes A JavaScript logn function and a Java Logn method successfully achieve the required functionality. Both options are available to provide redundancy and to show different approaches for demonstration purposes. Base x in this case is 10 as JavaScript and Java have built in functions to perform logarithms to the base 10. A ReduceRoot Java method successfully achieves the required functionality. It does this by firstly performing the logarithm of the base of the surd to a base equal to its root. Secondly, it divides the base of the root by integers below this value raised to the inverse of the root. A division resulting in an integer value means the dividend is a factor and thus is factorable. For example take 32^(1/4) (the 4th root of 32), Log(4,32) = 2.5, floor(2.5) = 2, 2^(1/1/4) = 2^4 = 16, 32/16 = 2 thus since 2 is an integer a factor has been found so 32^(1/4) = (16*2)^(1/4) = 16^(1/4)*2^(1/4) = 2*2^(1/4). An approximateFraction JavaScript function and a Rationalise Java method successfully achieve the required functionality. The former does this by firstly calculating an approximate value by dividing the decimal by the inverse of a number less than or equal to a given maximum denominator. Subtracting the approximate value divided by the current denominator from the decimal number gives the error. The returned fraction has a numerator equal to the given decimal number divided by the inverse of the denominator that had the lowest error, rounded to the nearest integer, and a denominator equal to the best denominator. For example if we called approximateFraction(0.5, 2, 0) its first approximate value would be 0.5/(1/1) = 0.5 and its first error would be 0.5 - (1/1) = 0.5. On the second iteration its approximate value would be 0.5/(1/2) = 1 with an error of 0.5 - (1/2) = 0. Since its best error is less than or equal to the given error a value can be returned which is 0.5/(1/2) + “/” + 2 = “1/2”. A combination of packaging and compression has made the total size of all web page components about 800KB. Considering the average broadband speed for 2009 is 3.6 megabits per second (Mbit/s) [19] which equates to 460KB/s then the web page would download in under 2 seconds. The applet would add on about another 851KB but the web page can function even if the applet has not finished downloading via AJAX requests. Table 5: Aim evaluation In summary, all project aims were successful although the degree of success with subjective aims is user dependent and there are two shortcomings which somewhat limit success. The first is that there is no formal proof to guarantee all valid input expressions are processable. The second is that input expressions and/or subexpressions with or leading to very large numbers may cause the system to hang and/or crash. 25 6. PROFESSIONAL ISSUES For a full breakdown of relationships to the British Computer Society (BCS) codes of conduct and good practice please see sections 9.10 and 9.11 respectively. 6.1 Code of conduct This project complies with the codes of conduct of the BCS and The University of Liverpool Computer Science Department as well as relevant legislation and licensing. It has been open and honest respecting the rights and practices of relevant authorities and third parties providing them information where necessary. The author‟s skills in handling and combining a wide variety of technologies have improved and constant monitoring of the research area ensured the incorporation of useful new developments. 6.2 Code of good practice Monitoring of publications and software developments within the subject area ensured the maintenance of the author‟s technical competence. Moreover, the author ensured the relevant authorities and third parties knew his ability level before proceeding allowing assessment of any consequences. Scaling the workload to the author‟s ability and ensuring all required resources were gathered helped complete the project on time. Project applications are clear, the main being to serve as demonstration software, and there are no pitfalls in having attempted or completed the project. Finally, an evaluation of similar tools is available in section 2.5 and the project itself is fully publicly accessible. 26 7. CONCLUSIONS 7.1 Main findings and learning points This project has discovered how to combine a variety of technologies and libraries to implement new simplification functions/methods. Learning points derive mainly from completing all aims in section 1.2 successfully and from solutions to problems encountered (discussed in section 9.8). From these the author has not only increased expertise in the management and development of CAS and CAS algorithms but also in utilizing and merging current and emerging technologies. 7.2 Future development 7.2.1 Reliability proof Currently there is no guarantee all valid input expressions are correctly processed. Although all testing so far is successful, there are infinite test cases. Thus, a rigorous mathematical proof is the only formal way to prove the system reliability through deductive reasoning rather than empirical and/or inductive arguments. 7.2.2 Number limit Ideally, the system would reject expressions resulting in unprocessable simplifications. The processable number sizes depend primarily on user hardware and software capabilities. Therefore implementing a way to limit number sizes input either directly or through simplifications would help prevent the system hanging giving an improved user experience. 7.2.3 Equation solving A natural extension to simplification is equation solving. Although the system supports finding roots of univariate polynomials, it does not support equation input. For example, to solve 2*x = 10 for x the user would have to use the expression in a form equal to 0 (as 2*x - 10) and give it as input to the Roots Java method (Roots(2*x-10)). 7.2.4 Animated processing In addition to displaying simplification and formatting steps, an animation of these steps in progress would be beneficial allowing users to grasp their meaning more easily. This could be possible by displaying a GIF image within each log with frames equal to steps within their logs. The ASCII2MathML JavaScript library has support for outputting SVG images [22] and therefore could aid in converting a series of expressions into a GIF. 7.2.5 Custom themes Giving users the ability to create personalised themes and applicable to the system with for example, the JQuery UI ThemeRoller, would make the system more accessible to people for example who are colour blind, and more appealing improving user experience. This feature may not be too difficult to implement since the JQuery UI ThemeRoller is already able dynamically to produce CSS from external queries. [44] 7.2.6 Language conversion Translating the system into other languages would make it more accessible. Since the user guide would be the only major system component to translate the task would be relatively easy. Programmatic conversion into other popular languages such as C, PHP, and Perl would make the system more useful for demonstration purposes. 27 7.2.7 Java and JavaScript alternative The expression simplifier applet has an alternative server side implementation to handle AJAX requests should it fail. Adding an alternative for the expression tree applet would allow access to users lacking Java. Moreover, a PHP version of jQuery could give access to users lacking JavaScript. The jQuery PHP library developed by Anton Shevchuk [45] could be used. Alternatively, Java Web Start, a framework for starting Java applications directly from the Internet using a web browser [46], could almost fully replace the web site and JavaScript. 7.2.8 Simplification analysis Miller defines simplification as “The act of reducing complexity” [47] and Flake describes complexity as “An ill-defined term that means many things to many people. Complex things are neither random nor regular, but hover somewhere in between. Intuitively, complexity is a measure of how interesting something is” [48]. Since simplification is subjective, it may be beneficial to research the most widely accepted forms and allow users to choose forms they prefer. 28 8. REFERENCES 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. K.O. Geddes, S.R. Czapor, and G. Labahn, Algorithms for computer algebra. Kluwer Academic Publishers. 1992. 585. T. Daly. Axiom Computer Algebra System. Tim Daly. 2010. [cited 1st September 2010]; Available from: http://axiom-developer.org/axiom-website/rosetta.html. e. INTERNATIONAL, Standard ECMA-262, in ECMAScript Language Specification. 2009, ecma INTERNATIONAL. TIOBE Software: The Coding Standards Company. 2010 TIOBE Software BV. 2010. [cited 2nd September 2010]; Available from: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html. P. Goldberg. Simplifying numerical expressions. The University of Liverpool. 2010. [cited 7th June 2010]; Available from: https://www2.csc.liv.ac.uk/~leszek/COMP702/proj/paulg3.html. www.mathwarehouse.com. Principal Nth Root Calculator and Reducer. 2010. [cited 12th June 2010]; Available from: http://www.mathwarehouse.com/arithmetic/principal-nth-rootcalculator.php. R. Sheat. Extension to the „Shunting Yard‟ algorithm to allow variable numbers of arguments to functions. 2008. [cited 12th June 2010]; Available from: http://www.kallisti.net.nz/blog/2008/02/extension-to-the-shunting-yard-algorithm-to-allowvariable-numbers-of-arguments-to-functions/. W3C Math Home. W3C. 2010. [cited 24th June 2010]; Available from: http://www.w3.org/Math/. J. Łukasiewicz, Uwagi o aksjomacie Nicoda i 'dedukcji uogólniającej "Remarks on Nicod's Axiom and the "Generalizing Deduction"". Księga pamiątkowa Polskiego Towarzystwa Filozoficznego, 1931. A.W. Burks, D.W. Warren, and J.B. Wright, An Analysis of a Logical Machine Using Parenthesis-Free Notation. Mathematical Tables and Other Aids to Computation, 1954. 8(46): p. 53-57. K.A. Stroud and D.J. Booth, Engineering mathematics. 5th ed / ed. Palgrave: Basingstoke. 2001. xxviii, 1236p. E. Dijkstra, An ALGOL 60 Translator for the X1, in ALGOL Bulletin Supplement nr. 10 ALGOL-60 Translation. 1961, Stichting Mathematisch Centrum: Amsterdam. [email protected]. Expression Calculator. 2010. [cited 12th June 2010]; Available from: http://www.algebrahelp.com/calculators/expression/oops/. I. Chudov. Algebra Simplifier and Math Solver that SHOWS WORK. 2010. [cited 12th June 2010]; Available from: http://www.algebra.com/services/rendering/simplifier.mpl. Google. Calculator. Google. 2010. [cited 13th June 2010]; Available from: http://www.google.co.uk/intl/en/help/features.html#calculator. AJAX Tutorial. W3Schools. 2010. [cited 16th July 2010]; Available from: http://www.w3schools.com/Ajax/Default.Asp. S. Levithan. XRegExp. 2010. [cited 14th July 2010]; Available from: http://xregexp.com/. Euclid, Ji he yuan ben. Di 2 ban. ed. Shanxi ke xue ji shu chu ban she: Xi'an. 2003. [12], 2, 15, 2, 673, 3 p. Ofcom. Ofcom reveals UK's average broadband speed. Ofcom. 2009. [cited 22nd June 2010]; Available from: http://www.ofcom.org.uk/media/features/brspeeds. jQuery is a new kind of JavaScript library. The jQuery Project. 2010. [cited 30th June 2010]; Available from: http://jquery.com/. jQuery user interface. The jQuery Project and the jQuery UI Team. 2010. [cited 30th June 2010]; Available from: http://jqueryui.com/. Peter Jipsen. ASCII2MathML : Home Page. S.A. Miedema. 2010. Updated 14th July 2010 [cited 14th July 2010]; Available from: http://www.asciimathml.com/dredging/default.asp?id=1&mnu=1. About JEuclid. The JEuclid project. 2010. [cited 23rd June 2010]; Available from: http://jeuclid.sourceforge.net/index.html. NetBeans IDE 6.9. Oracle Corporation and/or its affiliates. 2010. [cited 22nd June 2010]; Available from: http://netbeans.org/. 29 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. Explore the Eclipse universe. The Eclipse Foundation. 2010. [cited 22nd June 2010]; Available from: http://www.eclipse.org/. ADOBE DREAMWEAVER CS5. Adobe Systems Incorporated. 2010. [cited 22nd June 2010]; Available from: http://www.adobe.com/products/dreamweaver/. J. Goyvaerts. Learn, Create, Understand, Test, Use and Save Regular Expressions with RegexBuddy™. Just Great Software Co. Ltd. 2010. [cited 22nd June 2010]; Available from: http://www.regexbuddy.com/. D. Crockford. JSLint The JavaScript Code Quality Tool. Douglas Crockford. 2010. [cited 22nd June 2010]; Available from: http://www.jslint.com/. E. Lielmanis. Online javascript beautifier. 2010. [cited 22nd July 2010]; Available from: http://jsbeautifier.org/. W3C. W3C Markup Validation Service. 2010. [cited 22nd June 2010]; Available from: http://validator.w3.org/. A. Kramer. Matheclipse | Google Groups. Google. 2010. [cited 13th September 2010]; Available from: http://groups.google.com/group/matheclipse. A. Kramer. symja - Project Hosting on Google Code. Google. 2010. [cited 13th September 2010]; Available from: http://code.google.com/p/symja/. Math - Commons-Math: The Apache Commons Mathematics Library. The Apache Software Foundation. 2010. [cited 13th September 2010]; Available from: http://commons.apache.org/math/. H. Kredel. Java Algebra System (JAS). Heinz Kredel. 2010. [cited 13th September 2010]; Available from: http://krum.rz.uni-mannheim.de/jas/. Fat Jar Eclipse Plug-In. Geeknet, Inc. [cited 5th July 2010]; Available from: http://fjep.sourceforge.net/#about. F. Marcia. JS Minifier. Franck Marcia. 2010. [cited 22nd July 2010]; Available from: http://fmarcia.info/jsmin/test.html. CSS Compressor, Free CSS Compressor and CSS Compression Tool. Streamlined Fusion. 2009. [cited 22nd July 2010]; Available from: http://www.minifycss.com/csscompressor/. W. Peavy. HTML Minifier. 2010. [cited 22nd July 2010]; Available from: http://willpeavy.com/minifier/. JavaScript Source Joiner/Combiner | Code Central. thecodecentral.com 2008. [cited 21st July 2010]; Available from: http://thecodecentral.com/2008/03/18/javascript-sourcejoinercombiner. Objects, Images, and Applets in HTML documents. W3C. 2010. [cited 29th July 2010]; Available from: http://www.w3.org/TR/REC-html40/struct/objects.html. HTML object tag. W3Schools. 2010. [cited 29th July 2010]; Available from: http://www.w3schools.com/TAGS/tag_object.asp. MathPlayer. Design Science. 2010. [cited 29th July 2010]; Available from: http://www.dessci.com/en/products/mathplayer/. GNU Lesser General Public License - GNU Project - Free Software Foundation (FSF). The Free Software Foundation. 2007. [cited 16th August 2010]; Available from: http://www.gnu.org/licenses/lgpl.html. jQuery UI - ThemeRoller. The jQuery Project and the jQuery UI Team. 2010. [cited 17th August 2010]; Available from: http://jqueryui.com/themeroller/. A. Shevchuk. jQuery PHP library. Anton Shevchuk. 2010. [cited 17th August 2010]; Available from: http://jquery.hohli.com/. Java SE Desktop Technologies - Java Web Start Technology. 2010. [cited 3rd September 2010]; Available from: http://www.oracle.com/technetwork/java/javase/tech/index-jsp136112.html. G.A. Miller. WordNet Search - 3.0. Princeton University. [cited 13th July 2010]; Available from: http://wordnetweb.princeton.edu/perl/webwn?s=simplification. G.W. Flake. The Computational Beauty of Nature. 2002. [cited 17th July 2010]; Available from: http://mitpress.mit.edu/books/FLAOH/cbnhtml/glossary-C.html. M. Veltman, SCHOONSHIP. CERN, 1967. C. Engelman, MATHLAB: a program for on-line machine assistance in symbolic computations, in Proceedings of the November 30--December 1, 1965, fall joint computer conference, part II: computers: their impact on society. 1965, ACM: Las Vegas, Nevada. p. 117-126. 30 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. C. Moler. The Origins of MATLAB. The MathWorks, Inc. 2004. [cited 11th June 2010]; Available from: http://www.mathworks.com/company/newsletters/news_notes/clevescorner/dec04.html. Google. Science > Math > Algebra > Software, Categories and Web Pages in Google PageRank order. 2010. [cited 12th June 2010]; Available from: http://directory.google.com/Top/Science/Math/Algebra/Software/. R. Nelson. Hewlett-Packard Calculator Firsts. Hewlett-Packard Development Company, L.P. 2010. [cited 11th June 2010]; Available from: http://h20331.www2.hp.com/Hpsub/cache/392617-0-0-225-121.html. T. Instruments. Products. 2010. [cited 11th June 2010]; Available from: http://education.ti.com/educationportal/sites/US/productHome/us_product.html. R.H. Risch, The Problem of Integration in Finite Terms. Mathematical Tables and Other Aids to Computation, 1969. 139: p. 167-189. D. Knuth and P. Bendix, Simple word problems in universal algebras. Computational Problems in Abstract Algebra, 1970: p. 263–297. B. Buchberger, Theoretical Basis for the Reduction of Polynomials to Canonical Forms. ACM SIGSAM Bull, 1976. 10(3): p. 19-29. J. R. William Gosper, Decision procedure for indefinite hypergeometric summation. Proc. Natl. Acad. Sci. USA, 1978. 75(1): p. 40-42. J. Pollard, Monte Carlo methods for index computation mod p. Mathematics of Computation, 1978. 32. D.G. Cantor and H. Zassenhaus, A New Algorithm for Factoring Polynomials Over Finite Fields. Mathematics of Computation, 1981. 36: p. 587-592. J.C. Faugère, A new efficient algorithm for computing Gröbner bases (F4). Journal of Pure and Applied Algebra (Elsevier Science), 1999. 139(1): p. 61-88. S.C. Kleene, Representation of Events in Nerve Nets and Finite Automata. Automata Studies, 1956: p. 3-42. E.W. Weisstein. Irrational Number. MathWorld--A Wolfram Web Resource. 2010. [cited 7th June 2010]; Available from: http://mathworld.wolfram.com/IrrationalNumber.html. E.W. Weisstein. Euler Formula. MathWorld--A Wolfram Web Resource. 2010. [cited 7th June 2010]; Available from: http://mathworld.wolfram.com/EulerFormula.html. J. Sondow and E.W. Weisstein. e. MathWorld--A Wolfram Web Resource. 2010. [cited 8th June 2010]; Available from: http://mathworld.wolfram.com/e.html. G. Chrystal, Introduction to algebra, for the use of secondary schools and technical colleges: London,. 1898. R.E. Crandall and C. Pomerance, Prime numbers : a computational perspective. SpringerVerlag: New York ; London. 2001. xv., 545p. D. Crockford. The Little JavaScripter. [cited 8th June 2010]; Available from: http://www.crockford.com/javascript/little.html. M. Andreessen. INNOVATORS OF THE NET: BRENDAN EICH AND JAVASCRIPT. 1998. [cited 8th June 2010]; Available from: http://web.archive.org/web/20080208124612/http://wp.netscape.com/comprod/columns/tec hvision/innovators_be.html. P. Krill. JavaScript creator ponders past, future Mozilla's Brendan Eich describes JavaScript's history, the upcoming upgrade, and disagreements with Microsoft. [cited 8th June 2010]; Available from: http://www.infoworld.com/d/developer-world/javascript-creatorponders-past-future-704. TIOBE Programming Community Index for June 2010. 2010. [cited 8th June 2010]; Available from: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html. R. Green. Applet : Java Glossary. Canadian Mind Products. 2010. [cited 16th August 2010]; Available from: http://mindprod.com/jgloss/applet.html#OBJECT. The MathML Interface. W3C. 2010. [cited 16th August 2010]; Available from: http://www.w3.org/TR/MathML2/chapter7.html#interf.embed. XHTML Modularization 2.0 - XHTML Applet Module -. W3C. 2009. [cited 16th August 2010]; Available from: http://www.w3.org/MarkUp/2009/ED-xhtml-modularization220090123/mod-applet.html. Objects, Images, and Applets in HTML documents. W3C. 2010. [cited 16th August 2010]; Available from: http://www.w3.org/TR/REC-html40/struct/objects.html#adef-classid. 31 76. 77. 78. 79. 80. 81. 82. R. Dissanayake. Java - Javascript interaction. Raditha Dissanayake. 2010. [cited 16th August 2010]; Available from: http://www.raditha.com/java/javascript.php. Pack200 and Compression. Sun Microsystems, Inc. [cited 5th July 2010]; Available from: http://java.sun.com/j2se/1.5.0/docs/guide/deployment/deployment-guide/pack200.html. Best Practices for Speeding Up Your Web Site. Yahoo! Inc. 2010. [cited 21st July 2010]; Available from: http://developer.yahoo.com/performance/rules.html. Special Applet Attributes. Oracle. 2010. [cited 16th August 2010]; Available from: http://download.oracle.com/javase/6/docs/technotes/guides/plugin/developer_guide/specia l_attributes.html. E. Gerds. Plugin Detection. Eric Gerds. 2010. [cited 16th August 2010]; Available from: http://www.pinlady.net/PluginDetect/. S. Levithan. XRegExp: API. Steven Levithan. 2010. [cited 16th August 2010]; Available from: http://xregexp.com/api/. jQuery UI - Documentation: UI/Theming/ThemeSwitcher. The jQuery Project and the jQuery UI Team. 2010. [cited 16th August 2010]; Available from: http://jqueryui.com/docs/Theming/ThemeSwitcher. 32 9. APPENDICES 9.1 Project log Date 21/03/10 22/03/10 26/03/10 22/04/10 24/04/10 13/06/10 14/06/10 17/06/10 21/06/10 29/06/10 11/07/10 27/07/10 02/08/10 09/08/10 23/08/10 31/08/10 Activity The author sent his initial e-mail of interest regarding the project to Dr Paul W. Goldberg (supervisor). Dr Paul W. Goldberg replied to the author‟s initial interest establishing first communication. The author released v0.2 (later renumbered to v0.02) using interface implementation 1. The first meeting took place between the author and Dr Paul W. Goldberg who accepted the author onto the project and outlined it with some additional requirements. The author released v0.3 (later renumbered to v0.03) using interface implementation 2. The author released v0.4 (later renumbered to v0.04) using interface implementation 3. The second meeting took place between the author and Dr Paul W. Goldberg with discussion on topics including the literary review, regular expressions, and simplification representations. The author released v0.07 (later renumbered to v0.05) using interface implementation 4. The third meeting took place between the author and Dr Paul W. Goldberg focussing on a range of topics, mainly the specification, further correspondence was mainly via e-mail due to travel distance and time. The author submitted two copies of the specification to the student office in the Ashton Building. Dr Martin Gairing (assessor) and Dr Piotr Krysta (second assessor) carried out assessment of these. The author released v0.09 using interface implementation 5. The author submitted the design report electronically, via e-mail, to Dr Russell Martin (temporary assessor), Dr Martin Gairing, and Dr Piotr Krysta. The design presentation took place in the Ashton Building room 310 with the author presenting to Dr Russell Martin and Dr Piotr Krysta. In addition, the author gave the assessors physical copies of the presentation slides and design report. The author released v0.12 using (the final) interface implementation 6. The author submitted the final report electronically, via e-mail, to Dr Martin Gairing and Dr Piotr Krysta. The final presentation and demonstration took place in the Ashton Building room 310 with the author presenting to Dr Martin Gairing and Dr Piotr Krysta. In addition, the author gave the assessors physical copies of the presentation slides and final report. Table 6: Project log The author regularly kept in contact with Dr Paul W. Goldberg on usually at least a weekly basis to provide project updates and gather feedback. 33 9.2 Project plan Figure 11: Project plan 34 9.3 Additional background information 9.3.1 Computer algebra systems CASs arose in the 1960s due to demand from artificial intelligence and theoretical physics research. SAINT, a LISP program for symbolic automatic integration, is perhaps the earliest example made in 1961 by James Slagle at M.I.T [1 p. 5]. Schoonship (Dutch for “clean ship”) is another early example of a program developed to perform symbolic mathematics, it was made in 1963 by Nobel Prize winner Martin Veltman specifically for high energy physics [49]. Other examples include MATHLAB created in 1964 by Carl Engelman [50] and MATLAB made in the late 1970s by Cleve Moler [51]. The first popular systems were arguably muMATH, Reduce, Derive (based on muMATH) and Macsyma whereas today Mathematica, Maple and Sage (a free alternative) seem to dominate [52]. Hewlett-Packard‟s HP-28 series handheld calculators introduced in 1987 were the first to support CAS with features including algebraic rearrangement, differentiation, limited symbolic integration, Taylor series construction and an algebraic equation solver [53]. They have since been superseded by for example Texas Instruments‟ TI-92 calculator in 1995 boasting an advanced CAS based on Derive and further the TI-89 series in 2007 with the TI-Nspire CAS [54]. 9.3.2 CAS algorithms Many CAS algorithms exist including: Risch 1969 algorithm for indefinite integration calculus operations [55]. Knuth–Bendix completion 1970 algorithm which transforms a set of equations over terms into a confluent term rewriting system [56]. Buchberger's 1976 algorithm to turn a generator set for a polynomial ideal into a Gröbner basis according to some monomial order [57]. Gosper's 1977 algorithm for finding sums of hypergeometric terms that are themselves hypergeometric terms [58]. Pollard's kangaroo/lambda 1978 algorithm which solves the discrete logarithm problem [59]. Cantor–Zassenhaus 1981 algorithm to factor polynomials over finite fields [60]. Faugère F4 1999 algorithm to compute the Gröbner basis of an ideal of a multivariate polynomial ring [61] and many more. Although this project has not required algorithms as advanced as these, they are useful to mention to give an impression of the current state and complexity of CAS. 9.3.3 Regular expressions Regular expressions (sometimes abbreviated as regex or regexp) can be concise and flexible character, word, or pattern matching mechanisms for text strings. A formal language is required to express them in a way that allows correct interpretation by a regular expression processor, which is either a parser generator or a text examiner to identify specification matches. Their purpose is to describe sets of strings more concisely than would be expected by enumerating the set. Almost all formalisms support key concepts like Boolean choices, grouping, quantification, and character classes. They originated from automata theory and formal language theory in theoretical computer science and are said to have been invented by Stephen Cole Kleene [62]. The standard quantifiers in regular expressions are usually greedy meaning they match as much as possible. If there is a mismatch, they backtrack giving back as few characters as needed to satisfy the regular expression. One extension is to allow lazy quantifiers (also known as non-greedy, reluctant, minimal, or ungreedy) which attempt to find a minimal match. Different quantifiers can produce different regular expressions that perform the same match, choosing the most suitable is an issue of readability and efficiency. 9.3.4 Irrational numbers One argument is that simplifying a numerical expression is the same as evaluating it. However there will be a loss of precision if the result is an irrational number which Weisstein and Eric define as a number with infinite non-periodic decimal expansions which is inexpressible as a fraction of two integers p / q [63]. The reason for the loss of precision is that a computer cannot calculate an infinite number of decimal expansions and even a large number of decimal expansions require rounding for the user to be able to read them. Perhaps the most famous irrational numbers are , e and . In fact any numbers of the form are irrational unless n is the mth power of an integer. 35 Moreover numbers of the form are irrational if m and n are integers and one has a prime factor which the other lacks [63]. Also trigonometric functions produce irrational numbers for many rational inputs because, expressing them using Euler‟s formula [64], we can see the arguments are used as exponents to the irrational number e. The most concise, precise and arguably simple way to represent expressions evaluating to irrational numbers may be leaving them unevaluated. Simplification of could involve testing if the result is an integer and replacing the root with it only if it is. Proving a number is irrational can become complex and may be unsolvable. For example it is unknown whether or not or is irrational [65]. Therefore, the only solution may be to limit the maximum number of decimal expansions so if a result exceeds this number it should not replace the expression. 9.3.5 Operator properties Commutativity, associativity, and distributivity are three important concepts in processing an expression. An operator is commutative if the order of its operands does not affect the evaluation. Thus, addition and multiplication are commutative operators whereas subtraction, division, and exponentiation are not. An operator is associative if the order of evaluation for two or more occurrences of that operator in a row does not affect the result. Again, this makes addition and multiplication associative operators whereas subtraction, division, and exponentiation are not. Finally, a binary operator is distributive over another binary operator if applying the first operator to an operand and the result of applying the second operator to two operands is the same as applying the second operator to the results of the first operator applied to both the second operator‟s operands. This means multiplication is distributed over addition and subtraction from both left and right (e.g. ) whereas division is distributed over addition and subtraction from just the right (e.g. – ) [11 p. 78-79]. 9.3.6 Basic algebra Expanding simplification to include algebraic expressions requires the rules of algebra and is necessary to leave expressions unevaluated. Collecting like terms (i.e. terms with the same variables) involves performing addition/subtraction between their coefficients, for example . Factorization shows this, that is to say and in this case, factorization is simply making use of the distributivity of multiplication over addition. Similar terms are terms that have some of the same variables that are suitable for factorizing out. In most circumstances a simplified form will be the factorized form, however in some cases brackets may need to be expanded beforehand to find the most suitable factors. Expanding brackets involves multiplying everything between one pair of brackets with everything between the other pair of brackets. Evaluating exponents may result in irrational numbers and/or extremely large or small numbers and if this is the case, it is essential simplification can manipulate exponents of bases without evaluation. Multiplying/dividing like terms to different exponents is equivalent to the term to the sum/difference of the exponents respectively. Moreover, a term to an exponent bracketed and put to another exponent is the same as that term to the multiplication of the exponents. The main algebraic rules for handling fractions are as follows: Multiplying one fraction by another is equivalent to forming a new fraction whose numerator and denominator is the result of multiplying the numerators and denominators of the operand fractions respectively. Dividing one fraction by another is the same as multiplying the first fraction by the reciprocal of the second. Adding/subtracting fractions requires a common denominator, multiplying the numerator and denominator of each fraction by the denominator of the other fraction obtains the common denominator. Another common simplification step is root rationalisation by eliminating roots/surds/radicals in the denominator of a fraction. Removing one root in the denominator can be accomplished by multiplying top and bottom by the root a number of times one less than its degree. Rationalising the addition or difference of two square roots or a variable and a square root involves multiplying numerator and denominator by the conjugate of the denominator. Finally rationalisation can be extended to all algebraic numbers and functions as an application of norm forms [66 p. 189-199]. 36 9.3.7 Factorization The simplest form of factorization is the extraction of highest common factors (HCFs) and when extended to polynomials it becomes the largest polynomial that divides all polynomials evenly. This will be a polynomial containing all the variables to their highest degrees, which appear in all polynomial terms. The Euclidean algorithm (named after Euclid the Greek mathematician who first described it [18]) is an efficient method for computing the greatest common divisor/factor (GCD/F) or HCF of two natural numbers. Although the algorithm is quite efficient as Gabriel Lamé proved in 1844 showing it never requires more steps than 5 times the number of digits (base 10), methods exist for improving its efficiency. The extended Euclidean algorithm can find the integers x and y in Bézout‟s identity and is a method for computing continued fractions efficiently. Calculating a GCD is also an essential step in many integer factorization algorithms such as Pollard‟s rho, Shor‟s, Dixon‟s factorization method, the Lenstra elliptic curve factorization and the continued fraction factorization [67]. The Gröbner basis generalizes the Euclidean algorithm and is a particular kind of generating subset of an ideal I in a polynomial ring R. The ideal I is defined as a special subset of a ring and a ring is defined as an algebraic structure consisting of a set together with two binary operations (usually called addition and multiplication) which combine two elements to form a third element [57]. 9.3.8 JavaScript JavaScript is a dynamic, weakly typed, prototype based, object oriented and functional scripting language implementing the ECMAScript language standard. It boasts first-class functions, supports closures and higher order functions with many more features [3]. Primarily used client side through web browsers it enhances user interaction allowing dynamic website content. Its design was based on several languages, most notably the Self and Scheme programming languages [68], similar to Java but with increased accessibility for less experienced programmers [69]. Originally named Mocha, later renamed to LiveScript and, finally JavaScript under the development of Brendan Eich (Netscape) [70]. Also in November 1996 Netscape submitted JavaScript to ECMA International attempting to have it recognized as an industry standard which resulted successfully in a standardized version named ECMAScript [3]. JavaScript is now one of the most popular programming languages on the web with a recent resurgence through the introduction of Asynchronous JavaScript and XML (AJAX) [71]. One of its most powerful and controversial features is its eval function which can execute string statements at runtime as JavaScript leading to possible code injection vulnerabilities. Another is passing an indefinite number of parameters to a function, which could prove valuable for defining functions that take variable numbers of arguments. Moreover, it supports concise and powerful regular expressions in a similar way to Perl, which are extremely useful for pattern matching in all steps of a simplification process. Arguably, the most powerful aspect is its ability to interact with the document object model (DOM) of HyperText Markup Language (HTML) web pages as it allows dynamic manipulation of their presentation. Overall JavaScript was very useful for the simplification process as it could easily format and present results back to the user. 9.4 Additional figure explanations 9.4.1 Figure 4 This diagram shows the most significant events occurring within the JavaScript in a typical system run. Simplifying the diagram meant excluding methods to display formatting and simplification steps and the final simplified result as well as the ES Declaration.js, ES Utilities.js, and ES Error.js files. The top column to the diagram shows the main JavaScript file calling methods in ES Format.js to format the input infix expression eventually returning infixExpression9, which is the formatted result, parsed as MathML. infixExpression8 is passed to the shunting_yard method in ES Shunting Yard.js which then passes it to the ES tokenize.js tokenize method. This method returns an array of tokens each initialised by calling the Number constructor or one from any of the ES Operators.js, ES Functions.js, ES Argument Separators and ES Variables.js files. Calling constructors multiple times in any order is possible. 37 9.4.2 Figure 7 The simplify method passes the given parameters to the evaluate method which schedules a Timer to run a TimerTask to interrupt its thread after the given number of milliseconds (parsed as an integer). The given infix expression String is converted into an IExpr and the EvalEngine evaluate method is called on it to attempt simplification. Either a result returns before the thread interrupts or it returns null. If the returned IExpr is not null the simplify method converts it into a String and returns it otherwise it returns the original input infix expression String. The getMathML method takes an infix expression String, converts it into an IExpr, and passes it with a StringBufferWriter object to the EvalUtilities toMathML method. Returning a result requires calling the StringBufferWriter toString method as it receives the resulting data. 9.4.3 Figure 8 This diagram omits detailed formatting actions with corresponding checks as well as conversion from standard mathematical script notation into MathML (see figure 4) and displaying of the simplification result and simplification steps for simplicity. Although actions execute sequentially and automatically the system is unlike a flow chart since most are event driven requiring triggers from external components. 9.5 Interface designs Figure 12: Interface design 1 38 Figure 13: Interface design 2 Figure 14: Interface design 3 39 Figure 15: Interface design 4 Figure 16: Interface design 5 40 Figure 17: Interface design 6 (final) 9.6 Interface screenshots and user feedback Figure 18: Interface implementation 1 41 Figure 19: Interface implementation 2 User feedback: Listing the major problems and changes is not helpful. The interface is quite bland, a style and colour would improve its appearance. It would be nice to see formatting and simplification steps. Figure 20: Interface implementation 3 42 User feedback: Plain text steps are not visually appealing. There should be more information about the author and licensing. It would be helpful to have all components resizable especially the expression tree. Figure 21: Interface implementation 4 User feedback: There should be a user guide to list and explain the available functions. It should give a link to MathPlayer for Internet Explorer 8 users. I don‟t like the colours, could you make a way to change them? Figure 22: Interface implementation 5 43 Figure 23: Interface implementation 6 (final) Red line: Maximum time users should take to be able to use the system. Blue line: The time each user took to read the user guide and simplify their first expression. Average completion time 3:19s. Figure 24: Graph showing the number of seconds for each of 20 users to read the user guide and simplify an expression 44 9.7 User guide To simplify a mathematical expression: 1. Enter the expression into the "Input Expression" text box using infix script notation. o Infix expressions have operators placed between their operands in the standard way (e.g. 2+2). o Script means exponentiation/power, multiplication, division, addition and subtraction are represented by ^, *, /, + and - (see the operation table for examples). 2. Press the enter key inside the "Input Expression" text box or click the "Simplify" button to begin simplification. Please note: Expressions with one or more floating-point numbers will be evaluated otherwise they will be simplified. The only supported operations and functions are those displayed in the operation and function tables. In addition, their representation must be exactly as shown. π (pi) can be obtained using pii() or pii(1) for a numeric form and Pii() or Pii(1) for an expression form. e can be obtained using exp() or exp(1) for a numeric form and Exp(), Exp(1) or E for an expression form. i (imaginary number) can be obtained using Im(), Im(1) or I for an expression form. Not a Number (NaN) results if a mathematical error occurs during simplification for example logn(10,0) will result in NaN. Operation x^y x*y x/y x+y x-y Input two expressions x and y Returns x to the power of y x multiplied by y x divided by y x added to y y subtracted from x Table 7: User guide operations Function random() Input none sin(n) asin(n) cos(n) acos(n) tan(n) atan(n) exp(n) pii(n) ceil(n) a number n floor(n) abs(n) log(n) round(n) Rationalize(n) logn(n1,n2), Log(n1,n2) max(n1,n2) min(n1,n2) roundTo(n1,n2) two numbers n1 and n2 45 Returns a random number greater than or equal to 0 but less than 1 sine of n arcsine of n cosine of n arccosine of n tangent of n arctangent of n e to the power of n pi to the power of n n rounded up to the nearest integer n rounded down to the nearest integer absolute value of n base 10 logarithm of n the nearest integer to n a rational expression equal to n logarithm to base n1 of n2 the maximum of n1 and n2 the minimum of n1 and n2 n1 rounded to n2 decimal power(n1,n2) times(n1,n2) divide(n1,n2) plus(n1,n2) minus(n1,n2) nroot(n1,n2) randomBetween(n1,n2,n3) maxn(n1,n2,…,nN) minn(n1,n2,…,nN) avg(n1,n2,…,nN) Sin(x) ArcSin(x) Cos(x) ArcCos(x) Tan(x) ArcTan(x) Pii(x) Exp(x) Im(x) three numbers n1, n2 and n3 where n3 is an integer and n1 < n2 numbers n1 to nN an expression x Numeric(x) Hold(x) Expand(x) Factor(x) Power(x,y) Times(x,y) Divide(x,y) Plus(x,y) Minus(x,y) Nroot(x,y) CombineBases(x) two expressions x and y an expression x expecting two or more powers with the same exponent multiplied together Rationalise(x) an expression x expecting a power with a decimal exponent RationaliseMonomial(x) an expression x expecting a monomial surd as a fraction denominator an expression x expecting a binomial expression with a square root as a fraction denominator an expression x expecting an unevaluated trigonometric function (which must start with an uppercase letter) an expression x expecting a univariate polynomial function Table 8: User guide functions RationaliseBinomial(x) TrigToExp(x) Roots(x) 46 places n1 to the power of n2 n1 multiplied by n2 n1 divided by n2 n1 added to n2 n2 subtracted from n1 n1 root of n2 a random number between n1 and n2 inclusive with n3 decimal places maximum of n1 to nN minimum of n1 to nN arithmetic mean of n1 to nN sine of x arcsine of x cosine of x arccosine of x tangent of x arctangent of x pi to the power of x e to the power of x I (imaginary number) to the power of x evaluates the given expression if possible even if it doesn‟t have decimal values the same expression x prevented from being evaluated an expanded expression a factorized expression x to the power of y x multiplied by y x divided by y x added to y y subtracted from x x root of y x with the multiplication of two or more bases to the same exponent (only the first power found is attempted to be combined) x with the discovered power‟s decimal exponent replaced with a fractional exponent x with a rationalised fraction the exponential representation of x the root(s) of x 9.8 Problems and solutions 9.8.1 Applet browser compatibility Typically, a Java Virtual Machine (JVM) runs Java applet bytecode in a web browser, these applets may behave differently depending on their parameters. Applets can be placed on web pages using either the depreciated applet element or the recommended object element [40]. Additionally a non-standard embed element can be used for Mozilla family browsers. However, despite the object element being the officially recommended tag, as of 2010 support for it is not yet fully cross browser compatible and thus oracle continues to recommend the applet tag for older browsers [40]. For this reason deprecating the applet tag has been criticised [72]. MathML is recommended to be embedded in XHTML rather than HTML since they share a common XML framework [73] and for XHTML to validate the object tag must be used [74]. Thus, to allow the object tag to be used a process of trial and error ensued eventually becoming successful in the latest versions of all major browsers supporting the object tag. Success was possible through a combination of object element attributes, object tag parameters and Internet Explorer conditional statements, the code used is as follows: <!--[if !IE]><!--> <object id="ExpressionTreeApplet" width="100%" height="100%" type="application/x-java-applet" classid="java:ExpressionTree" archive="ES_ExpressionTree.jar" codebase="classes/applet"> <param name="java_arguments" value="-Djnlp.packEnabled=true" /> <param name="codebase_lookup" value="false" /> <param name="archive" value="ES_ExpressionTree.jar" /> <param name="codebase" value="classes/applet" /> <param name="java_codebase" value="classes/applet" /> <param name="code" value="ExpressionTree.class" /> <param name="mayscript" value="true" /> <param name="scriptable" value="true" /> <param name="wmode" value="transparent" /> </object> <!--<![endif]--> <!--[if IE]> <object id="ExpressionTreeApplet" width="100%" height="100%" classid="clsid:8AD9C840-044E11D1-B3E9-00805F499D93"> <param name="java_arguments" value="-Djnlp.packEnabled=true" /> <param name="codebase_lookup" value="false" /> <param name="archive" value="ES_ExpressionTree.jar" /> <param name="codebase" value="classes/applet" /> <param name="code" value="ExpressionTree.class" /> <param name="mayscript" value="true" /> <param name="scriptable" value="true" /> <param name="wmode" value="transparent" /> </object> <![endif]--> This shows the object XHTML code for the expression tree applet, which is identical to the expression simplifier applet except width, height, and file name. There are only two differences between the code for Internet Explorer 8 (bottom) and the other browsers (top). The first is the classid attribute which is “used to specify the location of an object's implementation via a URI” [75]. The cryptic looking "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" for Internet Explorer 8 instructs the browser to use the latest Java Plug-In ActiveX object GUID. The second difference is the java_codebase parameter and is specifically intended for Safari as some versions use it instead of the codebase parameter. Moreover, Safari requires the code parameter to use class files with the “.class” extension included. The mayscript and scriptable parameters allow the JavaScript and applets to call each other‟s methods respectively [76]. The Internet Explorer conditional statements are only recognised by Internet Explorer but this is what is needed, they have a lower client side impact compared to using scripts and can be used across browsers. 47 9.8.2 Response time 9.8.2.1 Pack200 compression Pack200 operates in high fidelity (Hi-Fi) mode by default using the following size reduction techniques: Co-locating, merging and sorting class file constant-pool data Removing unnecessary class attributes Storing internal data structures Using variable and delta length encoding Optimizing secondary compression through coding type variation In this mode, it also retains all JAR entry and class attributes usually adding to the packaged file size. Low fidelity (Lo-Fi) mode includes all Hi-Fi reduction techniques as well as: Removing individual modification time entries resulting in one modification time transmitted per pack file segment. Removing deflation hints, which are the compression states of individual entries (a minor downside to this is when recomposing the JAR it will contain “stored” entries consuming more disk space). Removing debugging attributes like Line Numbers and Source File. The following windows command to pack200.exe (located within the JRE/JDK bin folder) would compress the input file to produce the output file in Lo-Fi mode. “pack200 --modification-time=latest --deflate-hint="false" --strip-debug output.jar.pack.gz input.jar” Hosting and transmitting the compressed file over the network requires the receiving application to decompress and restore it. It works by the requesting application, such as a web browser, sending an Accept-Encoding field with its HTTP request whose value is set to “pack200-gzip” which tells the server it can handle this compression format. The server then searches for the requested JAR file with an additional “.pack.gz” extension and sends a HTTP response including the located file and a Content-Encoding field set to “pack200-gzip” which can be inspected by the receiving application to apply the correct decompression. It can also optionally set a Content-Type field to application/Java-archive. HTTP compression was implemented in compliance with RFC 2616 and included in Java Plug-in and Java Web Start as of Software Development Kit (SDK)/Java Runtime Environment (JRE) version 5.0 [77]. 9.8.2.2 Minification Minification strips source code of all unnecessary characters without changing its functionality. Unnecessary characters usually comprise white space (including new line characters) and any comments (block delimiters) used to add understanding and readability to code but which are not essential for its execution. Minified source is particularly useful for interpreted languages deployed and transmitted over the Internet like JavaScript as it reduces the data volume transferred. Minification also conceals logic behind code, which can help prevent tampering and deter reverse engineering (security through obscurity). 9.8.2.3 File combination About 80% of end-user response time is spent on the front-end mostly downloading all web page components including images, stylesheets, JavaScripts, applets, etc. Reducing the number of components in turn reduces the number of HTTP requests required to render the page, which is one of the largest overheads. One way to accomplish this is by combining all JavaScript files into a single JavaScript file, and similarly combining all stylesheets into a single stylesheet. This is especially challenging when scripts and stylesheets are drawn from multiple sources however making this part of the release process improves response time [78]. JsCombine combines 48 JavaScript files by taking two arguments, the first is the path to a text file that contains the absolute paths of all JavaScript files to combine, and the second specifies the combined output file name. The following windows command to JsCombine would combine all JavaScript files with absolute paths listed in FilePathList.txt to produce combined.js. “java -jar JsCombine.jar FilePathList.txt combined.js” 9.8.3 Applet loading speed By default when the applet classloader requires a class or resource it will search both in the applet JAR files and in the codebase. However, even if the files are found in the applet JAR files, the codebase will still be checked to compare versions ensuring files are up to date. Worse still, for each file checked a separate Transmission Control Protocol (TCP) connection is made to the server [79] which causes a significant overhead especially if a large number of files are checked. Testing also revealed Firefox 3.6.8 would intermittently close its JVM possibly due to the large number of connections established. Since the applets have all the needed classes and resources stored within their JAR files codebase lookup is unnecessary and so adding the following parameter (also shown above) solved the problem: <param name="codebase_lookup" value="false" /> 9.8.4 Checking Java support Java detection can be broadly based on either applet or non-applet methods. The former do not require the Java Plug-In to be active and include searching through the navigator.mimeTypes and/or navigator.plugins arrays for a Java mimetype and/or plugin and trying to instantiate certain ActiveX objects associated with Java. The latter rely on instantiating Java applet(s) in the browser and then querying those applet(s) to get information. The non-applet methods are comparably faster whereas the applet methods are more reliable but slower. The problem is choosing between these methods is highly reliant on the browser and so to solve this problem PluginDetect by Eric Gerds was used which is supported by all major browsers. It tries both “on the fly” (OTF) and “not on the fly” (NOTF) methods. The difference between them is that OTF methods are synchronous (no JavaScript can be executed between detection initiation and completion) whereas NOTF are asynchronous (JavaScript can be executed in between). [80] 9.8.5 Character escaping and replacing It was realized early in the project that tokenization required a distinction between unary and binary minus due to them having one and two operands respectively and because they have different precedence. The precedence of unary minus was chosen to reflect the view that -x is shorthand for -1*x or (0-1)*x also equivalent to (0-x) and thus was placed below the precedence of exponentiation but above multiplication/division. Unary and binary minus could not share the same minus character (i.e. “-”) for tokenization and so unary minus was represented by an underscore character (i.e. “_”). This meant finding and replacing unary minus occurrences with an underscore character before tokenization was necessary, using regular expressions in the formatting stage accomplished this. In particular unary minus occurs whenever an operand is not on the left hand side of a minus character. One downside of this alternative representation is any output to the user involving it needed replacing back with a minus character to avoid mixed representations. Other characters needing replacement before outputting results to the user included quotes used for example to represent expressions as strings that should not require simplification. However for MathML some function names require quotes to be inserted around them to prevent some of their characters being interpreted as mathematical symbols such as Pii(1) being interpreted as πi(1). Since user input can be composed of any character, it was vital to escape all input characters and convert back any safe characters. This was accomplished using the built in JavaScript escape function. Additionally the XRegExp JavaScript library escape function was used in some regular expressions since it can escape characters including [, ], , (, ), -, *, +, ?, ., \, ^, $, |, ,, #, and whitespace [81]. 49 9.8.6 Theme switcher Although JQuery UI has a theme switcher component [82] it is not XHTML compatible specifically due to some HTML entities not being escaped in the URLs linking to the theme stylesheets. Escaping all entities in the links in a copy of the JavaScript used by the themeswitcher fixed this. Moreover, changing all external links to local links avoided having to rely on external web sites and the method used to update the stylesheet was simplified. 9.9 Simplification example If we assume a user enters an infix expression string and the system formats it into the following formatted expression string: “12+1/3*4^2+ceil(-logn(3.5,101))*x” It will then tokenize this expression by searching from the start of the string using a regular expression for each token. It removes matches from the string, converts them into tokens, and pushes them onto an infix token array. By removing each match, the regular expressions are always searching from the beginning of the string. The regular expressions search sequentially in that if one does not find a match then the next one begins searching. The system performs object match searching in the following order: 1. 2. 3. 4. 5. Number Operator AFunction Variable ArgumentSeparator Before tokenization can occur function names must be marked to separate the characters within them from variables. Surrounding them with hash (#) symbols accomplishes this giving: 12+1/3*4^2+#ceil#(-#logn#(3.5,101))*x Tokenizing this expression would yield the following infix token array: [12, +, 1, /, 3, *, 4, ^, 2, +, ceil, (, _, logn, (, 3.5, ,, 101, ), ), *, x] The system would convert this into a postfix token array using the extended Shunting Yard algorithm described earlier in the following steps shown in the table below: Step Operator Stack Argument count stack Operand passed stack Output 1. [] [] [] [] 2. [] [] [] [12] 3. [+] [] [] [12] 4. [+] [] [] [12, 1] 5. [+, /] [] [] [12, 1] 6. [+, /] [] [] [12, 1, 3] 7. [+, *] [] [] [12, 1, 3, /] 8. [+, *] [] [] [12, 1, 3, /, 4] 9. [+, *, ^] [] [] [12, 1, 3, /, 4] 10. [+, *, ^] [] [] [12, 1, 3, /, 4, 2] 11. [+] [] [] [12, 1, 3, /, 4, 2, ^, *, +] 12. [+, ceil] [0] [false] [12, 1, 3, /, 4, 2, ^, *, +] 50 13. [+, ceil, (] [0] [false] [12, 1, 3, /, 4, 2, ^, *, +] 14. [+, ceil, (, _] [0] [false] [12, 1, 3, /, 4, 2, ^, *, +] 15. [+, ceil, (, _, logn] [0, 0] [true, false] [12, 1, 3, /, 4, 2, ^, *, +] 16. [+, ceil, (, _, logn, (] [0, 0] [true, false] [12, 1, 3, /, 4, 2, ^, *, +] 17. [+, ceil, (, _, logn, (] [0, 0] [true, true] [12, 1, 3, /, 4, 2, ^, *, +, 3.5] 18. [+, ceil, (, _, logn, (] [0,1] [true, false] [12, 1, 3, /, 4, 2, ^, *, +, 3.5] 19. [+, ceil, (, _, logn, (] [0,1] [true, true] [12, 1, 3, /, 4, 2, ^, *, +, 3.5, 101] 20. [+, ceil, (, _] [0] [true] [12, 1, 3, /, 4, 2, ^, *, +, 3.5, 101, logn] 21. [+] [] [] [12, 1, 3, /, 4, 2, ^, *, +, 3.5, 101, logn, _, ceil] 22. [+, *] [] [] [12, 1, 3, /, 4, 2, ^, *, +, 3.5, 101, logn, _, ceil] 23. [+, *] [] [] [12, 1, 3, /, 4, 2, ^, *, +, 3.5, 101, logn, _, ceil, x] 24. [] [] [] [12, 1, 3, /, 4, 2, ^, *, +, 3.5, 101, logn, _, ceil, x, *, +] Giving the final postfix token array expression as: [12, 1, 3, /, 4, 2, ^, *, +, 3.5, 101, logn, _, ceil, x, *, +] Table 9: Infix token array to postfix token array example The system would decompose this into a series of subexpressions by taking a number of operands immediately prior to each operator or function required to form an operation. Finally, it would attempt to simplify these operations with each forming a simplification step where possible. The following table shows the results of this process: Step 1. Postfix subexpression ..., 1, 3, /,... Infix subexpression 1/3 Abstract Syntax Tree Rational[1 , 3] Simplification N/A 2. ..., 4, 2, ^,... 4^2 Power[4 , 2] 16 3. 4. 5. 6. 7. 8. 9. ..., 1, 3, /,16, *,... ..., 12, 16, 3, /, +,... ..., 3.5, 101, logn,... 3.68..., _ ..., 3.5, 3.68..., ceil,... ..., 3, _, x, *,... ..., 3, _, x, *, 52, 3, /, +,... (1 / 3) * 16 12 + (16 / 3) logn(3.5, 101) _3.68... ceil(_3.68...) _3 * x (52 / 3) + (_3 * x) Times[Rational[1, 3], 16] Plus[12, Rational[16, 3]] N/A N/A N/A Times[_3, x] Plus[Rational[52, 3], Times[_3, x]] Rational[16, 3] Rational[52, 3] 3.68... N/A _3 N/A N/A Table 10: Postfix token array subexpression formation and simplification example Consequently, this would yield the final simplified result as: -3*x + 52/3 51 9.10 Relationship to the British Computer Society Code of Conduct 9.10.1 The public interest 1. “In your professional role you shall have regard for the public health, safety and environment.” There is no health, safety, and/or environmental issues related to this project. 2. “You shall have regard to the legitimate rights of third parties.” They define a third party as “professional colleagues, or possibly competitors, or members of „the public‟ who might be affected by an IT System without their being directly aware of its existence.” The system cannot affect anyone who is unaware of its existence. 3. “You shall ensure that within your professional field/s you have knowledge and understanding of relevant legislation, regulations and standards, and that you comply with such requirements.” The author is fully aware of and in compliance with The University of Liverpool Computer Science Department code of conduct and relevant legislation including The Data Protection Act of 1998 and The Computer Misuse Act of 1990. The author has also abided by the relevant licenses for any external materials used including the GNU Lesser Public License and the MIT License or the GNU General Public License. 4. “You shall conduct your professional activities without discrimination against clients or colleagues.” The basis for client/user selection to test the system was on their prior knowledge of the system, mathematical background, interest in the project and accessibility to both the system and me. Anyone who wished to test the system was welcome to such that there has been absolutely no discrimination based on race, colour, ethnic origin, gender, sexual orientation, age and/or disability. The author has had no colleagues in this project except counting his supervisor whose advice and guidance has helped the development. The project is open to anyone interested and endorses equal access to IT benefits for all groups in society. It attempts to avoid social exclusion through a user-friendly interface with clear instructions and guidance. It is also applicable as an IT and mathematical learning tool and attempts to increase user knowledge in these disciplines. 5. “You shall reject and shall not make any offer of bribery or inducement.” The author has neither accepted a bribe from nor offered a bribe to anyone involved in this project and all participants will testify to support this statement. 9.10.2 Duty to the relevant authority 6. “You shall carry out work or study with due care and diligence in accordance with the relevant authority‟s requirements, and the interests of system users. If your professional judgement is overruled, you shall indicate the likely risks and consequences.” The relevant authority has reviewed the system at regular intervals and milestones to ensure it meets their requirements. There have been no disputes nor has there been overruling of the author‟s judgement during the project. 7. “You shall avoid any situation that may give rise to a conflict of interest between you and your relevant authority. You shall make full and immediate disclosure to them if any conflict is likely to occur or be seen by a third party as likely to occur. You shall endeavour to complete work undertaken on time to budget and shall advise the relevant authority as soon as practicable if any overrun is foreseen.” No conflict of interest has arisen during the project and all its essential deliverables are complete to time and budget. 52 8. “You shall not disclose or authorise to be disclosed, or use for personal gain or to benefit a third party, confidential information except with the permission of your relevant authority, or at the direction of a court of law.” This project did not involve confidential information. 9. “You shall not misrepresent or withhold information on the performance of products, systems or services, or take advantage of the lack of relevant knowledge or inexperience of others.” The system and its functionality has been fully documented during its development including the design, implementation and testing stages and a user guide has been provided ensuring all relevant information is disclosed honestly, openly and accessibly to the relevant authorities and users. 9.10.3 Duty to the profession 10. “You shall uphold the reputation and good standing of the BCS in particular, and the profession in general, and shall seek to improve professional standards through participation in their development, use and enforcement.” One of the requirements met by the system is that it is applicable as a demo. This has resulted in it promoting public understanding of information systems (in particular CAS) as well as basic arithmetic and algebra alongside some advanced mathematical concepts. The documentation produced also reflects and supports this. 11. “You shall act with integrity in your relationships with all members of the BCS and with members of other professions with whom you work in a professional capacity.” The author‟s supervisor is the only person he has worked with in a professional manner and they maintain a good working relationship. 12. “You shall have due regard for the possible consequences of your statements on others. You shall not make any public statement in your professional capacity unless you are properly qualified and, where appropriate, authorised to do so. You shall not purport to represent the BCS unless authorised to do so.” The author has not made nor has he been in a position to make comments on others, public statements in a professional capacity and/or claims to represent the BCS. 13. “You shall notify the Society if convicted of a criminal offence or upon becoming bankrupt or disqualified as Company Director.” The author has no criminal offenses nor has he become bankrupt nor is he a company director. 9.10.4 Professional competence and integrity 14. “You shall seek to upgrade your professional knowledge and skill, and shall maintain awareness of technological developments, procedures and standards which are relevant to your field, and encourage your subordinates to do likewise.” Due to the project, the author has developed his professional knowledge and skill in many areas including for example developing with Eclipse, using SVN repositories and handling MathML. He has maintained his awareness of technological developments, procedures, and standards for example by consistently comparing the system to other CAS and using XHTML and MathML for the web page. 15. “You shall not claim any level of competence that you do not possess. You shall only offer to do work or provide a service that is within your professional competence.” 53 The author has not made any claims of competence that he does not possess and has made clear to colleagues such as his supervisor any knowledge expected for the project, which is/was, beyond his ability. If researching unknown knowledge was not possible, then the other solution was to search for alternative knowledge. 16. “In addition to this Code of Conduct, you shall observe whatever clauses you regard as relevant from the BCS Code of Good Practice and any other relevant standards, and you shall encourage your colleagues to do likewise.” The following section will discuss clauses relevant to this project from the BCS Code of Good Practice. There are no other relevant standards requiring discussion. In addition, the project encourages the following of standards by linking to information about them wherever possible. 17. “You shall accept professional responsibility for your work and for the work of colleagues who are defined in a given context as working under your supervision.” No one is working under the author‟s supervision and he accepts professional responsibility for his work. 9.11 Relationship to the British Computer Society Code of Practice 9.11.1 Maintaining technical competence The author has constantly sought to improve his IT skills through conferring with academic experts such as his supervisor, acquiring relevant literature through public libraries, and searching other information sources such as the World Wide Web (WWW). He has also realised information from the WWW may not be reliable and so made efforts to ensure the validity of this information. The author has kept up to date with technological advances for example by using the latest versions of software for development tools. 9.11.2 Adhere to regulations Choosing development methods and technologies has been the author‟s responsibility and so there have been no organisation standards to follow in this respect. However, he has attempted to pursue and keep up to date with standards relevant to the project like the W3C XHTML and MathML standards. The author has kept up to date with and fully obliged to the substance and content of legal and regulatory frameworks applying to his work. 9.11.3 Act professionally as a specialist The author has maintained and improved his knowledge through the encouragement of his supervisor and the conduct of this project by reading relevant literature (books, articles, papers, etc) and through meeting and maintaining contact with his supervisor. Comparisons between the system and other CAS and different development tools has led to an evaluation of their potential benefit and recommended use. 9.11.4 Use appropriate methods and tools The relevant authority reviewed the specification and design stages, which planned, described, and explained methodologies in detail. Testing and ranking tools relevant to these methodologies in advance allowed selection and regular updating of the most appropriate. 9.11.5 Manage your workload efficiently 54 A project plan submitted with the specification and reviewed by the relevant authority set a timeframe to layout the workload evenly. However, the author endeavoured to keep the project well ahead of schedule to deal early with potential setbacks laid out in the risk assessment. The gathering of all necessary resources (mainly information) was successful for the project allowing its completion on time and to budget. 9.11.6 Participate maturely Seeking and welcoming constructive criticism from the author‟s supervisor and user feedback has allowed constant improvement in the system. A good working relationship exists with both the author‟s supervisor and system users. 9.11.7 Promote good practices within the organisation The intention is to increase IT awareness throughout the organisation as a project consequence since it is applicable as a demo. Mainly the specification but also the design, and testing phases look at potential project hazards, failures, and risks. The author has sought improvement, involvement and participation in best practices at all levels through thorough research and by engaging with his supervisor. 9.11.8 Represent the profession to the public Key project aspects involve contributing to public education both so the system is understandable and accessible and for improving the understanding of the relevant disciplines. The intention is for the produced web page to have a user-friendly interface and this could amplify user trust in the Internet. 9.11.9 Project management Although all essential project deliverables were successful, it was unclear, more so near the beginning, whether all of them were achievable. Expressing these uncertainties in the risk assessment and documenting them in the design, implementation, and testing phases, allowed not only the author but also his supervisor, and users to stay informed about whether the project was likely to meet its requirements. The specification gave a common programme structure understanding. Setting precise deliverables, such as the user being able to use the interface within 5 minutes given they can read and understand English, was meant to allow quantitative and transparent reporting based on objective measures particularly for the testing phase. Meetings with the author‟s supervisor ensured objectives underpinning project requirements were successful in scope, issues, constraints, and risks. Deliverables documented and defined these objectives also explaining how to measure them. By seeking out and using other libraries, specifically the Symja library, the author has benefitted by learning lessons from it. Adding features incrementally as separate modules where possible mitigated serious risks. The evaluation has attempted to honestly summarise the project mistakes and good fortunes and lay out possible future developments. 9.11.10 Security management No personal information is stored on the system (other than the author‟s name and e-mail) which means data protection is not a concern. Moreover, since the system is executed client side through JavaScript and applets the server has a shield from modification of these components. 55 The University of Liverpool hosts the system and provides a strong layer of security to protect against passive and/or active attacks also tools used like JSLint for JavaScript [28] improve code security. 9.11.11 System building Only evaluated and validated software languages and tools helped program the system. The University of Liverpool ensures the hardware hosting the system is protected and well maintained. 9.11.12 Quality management Deliverables were broken down into three categories namely essential, optional, and conditional. The system had to have essential deliverables and could have had optional deliverables whereas the appropriateness of conditional deliverables could not be determined, as they were dependent on unknown circumstances. This enabled the clear and concise expression of system quality applicable for regular reviews. 9.11.13 Research The direction of research was primarily at forming a solution to the project aims but also in providing benefits to either system users or developers. The literary review carried out an investigation and analysis of research by other people and organisations in topics related to the project. 9.12 Pseudocode 9.12.1 Java pseudocode 9.12.1.1 ExpressionTree method init() initialises some components overrides panel's paintComponent method o if (root node is initialised) then paint expression tree and resize screen/scrollbars end override try to call a JavaScript method to inform the web page the applet is loaded end method method addPostfixToken(postfixToken, postfixArgumentCount) push postifxToken onto postfixTokens push postfixArgumentCount onto postfixTokens end method method clearPostfixTokens() clear postfixTokens end method method buildRootTree() clear abbreviatedContent root = buldPostfixTree() expression = call root‟s getTreeContent method set the scrollbars to focus on the middle of the screen end method method getExpression() return expression 56 end method method checkContentWidth(content) contentWidth = get content string width if (contentWidth > NODE_SIZE (the size (width) of a tree node)) then o contentIndex = find first (if any) occurrence of content in abbreviatedContent o if (contentIndex != -1) then content = “F” + contentIndex o else add content to the end of abbreviatedContent content = “F” + abbreviatedContent size – 1 o end else end if return content end method method drawAbbreviatedContent(g, xPos, yPos) for (all the elements in abbreviatedContent) o draw “F” + the element index + “ = ” + the element at xPos and yPos (x and y coordinates) on g (graphics object) o yPos = yPos + line height end for loop return yPos end method method buildPostfixTree() argumentCount = pop postfixTokens last value and cast as an integer node = pop postfixTokens last value and create a new ExpressionTreeNode using it for (1 to argumentCount) add a child node to node created by recursively calling this method return node end method method paintNode(g, content, xPos, yPos) paint a circular node onto g with its centre at xPos and yPos paint content in the circle‟s centre end method method paintTree(g, root, xPos, yPos) parents, positions, children = empty vectors add root to parents paint root onto g with its centre at xPos and yPos while (true) o for (all parents) add the parent‟s children to children o if (children has no elements) then break o for (all children) draw each child equally spaced and connected to its parent add each child‟s children to parents o end for loop o remove old parents end while loop end method method isReady() return “true” end method 9.12.1.2 ExpressionTreeNode 57 method ExpressionTreeNode(aContent) content = aContent children = empty vector end method method getContent() return content end method method addChild(etn) add etn to children end method method addChildAt(etn, index) add etn to children at index end method method removeChild(etn) remove etn from children end method method removeChildAt(index) remove child from children at index end method method getChildren() return children end method method getTreeContent(depthContent, depth) if (depthContent size – 1 < depth) then add content to end of depthContent else add a space + content at depth in depthContent if (children size > 0) then o for (all children) add child getTreeContent to depthContent at depth + 1 end if return depthContent end method 9.12.1.3 ES_symja method init() add Symja library extensions to system namespace initialise the MathEclipse expression creation factory try to call a JavaScript method to inform the web page the applet is loaded end method method simplify(anExpression, aSimplifyMilliSeconds) result = call the evaluate method with the given parameters if (result != null) then return result cast as a string else return anExpression end method method evaluate(anExpression, simplifyMilliSeconds) schedule the main thread to be interrupted by another newly created thread after simplifyMilliSeconds try to return the result of calling the EvalEngine evaluate method on anExpression cast as an IExpr and return null if the evaluation is interrupted end method 58 method IExprToString(iexpr) stringBufferWriter = create a new StringBufferWriter call the OutputFormFactory convert method on stringBufferWriter and iexpr return stringBufferWriter cast as a string end method method StringToIExpr(anExpression) return the result of calling the EvalEngine parse method on anExpression end method method getMathML(anExpression) stringBufferWriter = create a new StringBufferWriter call the EvalUtilities toMathML method on the result of calling StringToIExpr on anExpression and stringBufferWriter return stringBufferWriter cast as a string end method method isReady() return “true” end method 9.12.1.4 Log method setUp(symbol) set symbol attribute to NUMERICFUNCTION end method method evaluate(functionList) if (functionList size == 3) then o if (the second and third arguments are numbers) then convert them to double floating point numbers return the logarithm to the base 10 of the converted third argument divided by the logarithm to the base 10 of the converted second argument o end if end if return null end method 9.12.1.5 CombineBases method setUp(symbol) set symbol attribute to HOLDFIRST end method method evaluate(functionList) if (functionList size == 2) then o if (functionList only contains powers and multiplications) then noPowers = functionList second argument powers = get all powers from functionList if (more than one power was found) then for (all powers found) o replace the power in noPowers with constant number 1 end for loop combineBases = noPowers multiplied by the base of the first power in powers for (all powers found starting from the first) 59 if (the previous power‟s exponent is equal to this power‟s exponent) then combineBases = combineBases multiplied by this power‟s base o end if o else return null end for loop return combineBases o o end if end if return null end method end if method getAllHead(head, functionList, results) for (all functionList elements) o if (the element is the head) then add the element to results o else if (the element is an AST) then call this method recursively passing head, the element and results as arguments end for loop return results end method method onlyContains(heads, functionList) for (all functionList elements) o if (the element is an AST) then if (heads does not contain the AST head) then return false if (calling this method recursively passing heads and the AST as arguments returns false) then return false o end if end for loop return true end method 9.12.1.6 Rationalise method setUp(symbol) set symbol attribute to HOLDFIRST end method method evaluate(functionList) if (functionList size == 2) then o if (second argument is a power) then power = second argument if (power‟s second argument can be rationalised) then if (power‟s first argument can be rationalised) then try to reduce the power and return it else replace power‟s second argument with its rationalised form and return it end if o end if end if return null end method 9.12.1.7 RationaliseBinomial method setUp(symbol) 60 set symbol attribute to HOLDFIRST end method method evaluate(functionList) if (functionList size == 2) then o if (second argument is a power) then power = second argument if (power‟s first argument is a plus and power‟s second argument is -1) then rewrite power with a positive exponent as a fraction and multiply numerator and denominator by the conjugate of power‟s denominator and return the result end if o end if end if return null end method 9.12.1.8 RationaliseMonomial method setUp(symbol) set symbol attribute to HOLDFIRST end method method evaluate(functionList) if (functionList size == 2) then o if (second argument is a power) then power = second argument o else if (second argument is a multiplication) then times = second argument if (times first argument is a power) then power = times first argument factor = times second argument end if else if (times second argument is a power) then factor = times first argument power = times second argument end else if o end else if o if (power != null) then if (power‟s exponent can be rationalised and is negative) then rewrite power with a positive exponent as a fraction and multiply numerator and denominator by a factor removing the non integer power from the denominator and return the result o end if end if return null end method 9.12.1.9 ReduceRoot method setUp(symbol) set symbol attribute to HOLDFIRST end method method evaluate(functionList) if (functionList size == 2) then o if (second argument is a power) then power = second argument 61 o end if end if return null end method if (power‟s first and second arguments are numbers) then if (power evaluated is a rational number) then return the evaluated power else if (power‟s second argument can be rationalised) then return the result of taking out the largest factor of power‟s first argument which equals the inverse of power‟s second argument to some integer power end if 9.12.1.10 RemoveAllHead method setUp(symbol) set symbol attribute to HOLDALL end method method evaluate(functionList) if (functionList size == 3) then o if (second argument is a symbol and third argument is an AST) then return the result of calling removeAllHead with the second and third arguments as parameters end if return null end method method removeAllHead(head, functionList) for (all elements in functionList) o if (the element is an AST) then set this element as the result of recursively calling this method with head and the element as parameters end for loop if (functionList is an AST with head as its first argument) then return its second argument else return functionList end method 9.12.2 JavaScript pseudocode 9.12.2.1 ES Declaration Global validCharacters = "()a-zA-Z0-9.,^*/+-", possibleClosingBrackets = "\\]\\}7\\>" possibleOpeningBrackets = 9.12.2.2 ES Utilities function importScript(url) tag = new DOM “script” element set tag “type” attribute to "text/javascript" set tag “src” attribute to url add tag to DOM “body” element end function function replaceBetween(string, startIndex, stopIndex, replacement) if (startIndex < 0) then startIndex = 0 if (stopIndex > string length) then stopIndex = string length string = replace string characters from startIndex to stopIndex with replacement return string 62 "\\[\\{\\<", end function function makeGlobal(re) if (re global attribute is false) then o reString = re characters after first “/” but before final “/” o reParams = “g” + re characters after final “/” o re = new RegExp using reString and reParams end if return re end function function countMatches(string, re) matches = an array of re matches against string if (matches != null) then return matches length else return 0 end function function countMatchesBetween(string, startIndex, stopIndex, re) string = string characters between startIndex and stopIndex return countMatches(string, re) end function function findClosingBracket(string, index) if (index < 0) then index = 0 if (index >= string length -1 or string character at index != “(“) then return -1 unmatchedBracketCount = 1 while (unmatchedBracketCount != 0) o index = index + 1 o if (index >= string length) then return -1 o else if (string character at index == “)”) then unmatchedBracketCount = unmatchedBracketCount – 1 o else if (string character at index == “(”) then unmatchedBracketCount = unmatchedBracketCount + 1 end while loop return index end function function findOpeningBracket(string, index) if (index <= 0 or string character at index != “)“) then return -1 if (index > string length - 1) then index = string length - 1 unmatchedBracketCount = -1 while (unmatchedBracketCount != 0) o index = index - 1 o if (index < 0) then return -1 o else if (string character at index == “)”) then unmatchedBracketCount = unmatchedBracketCount – 1 o else if (string character at index == “(”) then unmatchedBracketCount = unmatchedBracketCount + 1 end while loop return index end function function surroundString(string, regExp, mark, lookBehind) match = re match against string if (match != null and lookBehind != null) then o while (match != null and lookBehind == characters before match) insert mark at the start and end of string match = re match against string 63 o end if else o end while loop while (match != null) insert mark at the start and end of string match = re match against string o end while loop end else return string end function function getType(argument) type = type of argument if (type == object) then return its constructor name else return type end function function countDecimals(number, decimalSeparator) if (string contains decimalSeparator) then return the number of characters after its occurrence else return 0 end function function printStringIndexes(string) for (all string characters) result = string character index number + a space result = result + a newline character for (all string characters) o result = result + string character o for (the length of the character index number) result = result + a space end for loop return result end function function sortNumber(x, y) return x - y end function function removeTags() string = the first argument with characters escaped where necessary if (more than 1 argument is given) then o for (all tags specified in the second argument) remove them from string end if else remove all tags from string return string end function 9.12.2.3 ES Operators Global operators = an array of supported Operator objects function Operator(name, precedence, associativity, argumentCount) this object‟s name = name this object‟s precedence = precedence this object‟s associativity = associativity this object‟s argumentCount = argumentCount end function function getOperator(string) 64 for (all operators) o if (operator name == string) then return operator end for loop return null end function function getOperatorLongName(string) if (string == “^”) then return "Exponentiation" else if (string == “_”) then return "Unary minus" else if (string == “*”) then return "Multiplication" else if (string == “/”) then return "Division" else if (string == “+”) then return "Addition" else if (string == “-”) then return "Subtraction" else return null end function function evaluateOperation(myArguments) operator = myArguments last element operandOne = myArguments first element operandTwo = myArguments second element if (operator name == “^”) then return operandOne to the power of operandTwo else if (operator name == “_”) then return -operandOne else if (operator name == “*”) then return operandOne multiplied by operandTwo else if (operator name == “/”) then return operandOne divided by operandTwo else if (operator name == “+”) then return operandOne plus operandTwo else if (operator name == “-”) then return operandOne minus operandTwo else return null end function 9.12.2.4 ES Functions Global functions = an array of supported function names function AFunction(name, argumentCount) this object‟s name = name this object‟s argumentCount = argumentCount end function function isFunction(string, functionList) for (all functions in functionList) o if (string == the function) then return true end for loop return false end function function evaluateFunction(arguments) call the function whose name is at the end of arguments end function function sin(number) return the sine of the number end function function asin(number) return the arcsine of the number end function 65 function cos(number) return the cosine of the number end function function acos(number) return the arccosine of the number end function function tan(number) return the tangent of the number end function function atan(number) return the arctangent of the number end function function exp() if (an argument is given) then return e to the power of that argument else return e end function function pii() if (an argument is given) then return pi to the power of that argument else return pi end function function random() return a random number end function function logn() return the log to base 10 of the second argument divided by the log to base 10 of the first argument end function function maxn() result = first argument for (the rest of the arguments) o result = max(result, argument) end for loop return result end function function minn() result = first argument for (the rest of the arguments) o result = min(result, argument) end for loop return result end function function roundTo() return (first argument * 10 ^ second argument) rounded to the nearest integer / (10 ^ second argument) end function function power() return first argument to the power of the second argument 66 end function function times() return first argument multiplied by the second argument end function function divide() return first argument divided by the second argument end function function plus() return first argument added to the second argument end function function minus() return second argument subtracted from the first argument end function function nroot() return second argument to the power of (1/first argument) end function function ceil(number) return the nearest integer greater than number end function function floor(number) return the nearest integer less than number end function function avg() result = 0 for (all the arguments) o result = result + argument end for loop result = result / arguments length return result end function function abs(number) return the absolute (positive) value of number end function function randomBetween() randomNumber = first argument + (random() * (second argument - first argument)) if (third argument type == undefined) then return randomNumber rounded else return randomNumber to third argument decimal places end function function approximateFraction() approximate, error, best, bestError = 0 for (1 to second argument) o approximate = first argument / (1 / denominator) rounded to the nearest integer o error = (first argument - (approximate / denominator)) o if (denominator == 1) then best = denominator bestError = error o end if 67 o if (abs(error) < abs(bestError)) then best = denominator bestError = error o end if end for loop if (bestError > third argument) then return null else return first argument / (1 / denominator) rounded to the nearest integer + “/” + best end function 9.12.2.5 ES Variables function Variable(name) this object‟s name = name end function 9.12.2.6 ES Argument Separators function ArgumentSeparator(name) this object‟s name = name end function 9.12.2.7 ES Format function replaceBrackets(string) replace all possibleOpeningBrackets characters in string with “(“ replace all possibleClosingBrackets characters in string with “)“ end function function insertMultiplication(string) re = a letter and (letter or number) next to each other match, nextOpenBracket = null do o match = re match against string o if (match != null) then nextOpenBracket = first “(“ within or after the match if (nextOpen Bracket != -1 or the match is not part of a function) then insert a multiplication between the characters in the match else set re to search from after nextOpenBracket o end if while (match != null) end do while loop return string end function function removeInvalidCharacters(string) re = all invalid characters remove all re matches in string end function function closeUnmatchedParentheses(string) re = “(“ or “)” match = null do o match = re match against string o if (match != null) if (match == “(“) parenthesesCount = the number of “(” – the number of “)” starting from and including the found “(” 68 end if else while (parenthesesCount != 0) o string = string + “)” o parenthesesCount = parenthesesCount – 1 end while loop parenthesesCount = the number of “(” – the number of “)” starting from the beginning of the string and ending at and including the found “)” while (parenthesesCount != 0) o string = “(” + string o parenthesesCount = parenthesesCount + 1 end while loop end else o end if while (match != null) end do while loop return string end function function removeEmptyParentheses(string) re = “()” match = null do o match = re match against string o if (match != null) then for (all functions) if (the empty brackets are part of a function) then break else if (the empty brackets are not part of the last function) then remove them from string end for loop o end if while (match != null) end do while loop return string end function function removeUnnecessaryParentheses(string) re1 = a pair of parentheses without any inner parentheses re2 = a pair of parentheses immediately around “~” and “#” characters re3 = the start of the string or any character except a letter or number followed by parentheses surrounding 0 or more letters or numbers match1, match2, match3 = null do o match1 = re1 match against string o if (match1 != null) then remove the surrounding parentheses and surround the string with “~” and “#” match2 = re2 match against string if (match2 != null) then if (the character immediately before the opening parenthesis is not a letter) then remove the “~” and “#” characters end if o end if while (match1 != null) end do while loop replace “~” and “#” characters in string with “(” and “)” respectively 69 do o match3 = re3 match against string o if (match3 != null) then remove the parentheses while (match3 != null) end do while loop return string end function function replaceCombinedOperators(string) re = any combination of a “+” and a “-” do o match = re match against string o if (match != null) then if (the operators are the same) then replace them with a “+” else replace them with a “-” o end if while (match != null) end do while loop return string end function function removeUnmatchedUnaryOperators(string) re = a unary operator preceding anything other than an operand do o match = re match against string o if (match != null) then remove it from string while (match != null) end do while loop return string end function function removeUnmatchedBinaryOperators(string) re = a binary operator preceding anything other than an operand do o match = re match against string o if (match != null) then remove it from string while (match != null) end do while loop return string end function function replaceUnaryMinus(string) re = a unary minus do o match = re match against string o if (match != null) then replace it with an underscore character “_” to differ from binary minus while (match != null) end do while loop return string end function function escapeFunctionNames(string, fucntionList) for (all functions in functionList) o re = the current function o do match = re match against string 70 if (match != null) then surround the function name with double quotes o while (match != null) o end do while loop end for loop return string end function function looksLikeFunction(string, startIndex) re = 1 or more letters between the start and end of the string string = string substring from startIndex to the end of the string leftParenthesisIndex = first index of “(” in string if (leftParenthesisIndex != -1) then o string = string substring from 0 to leftParenthesisIndex o if (string matches re) then return true end if return false end function function fixMathMl(string) string = change standalone MathML string so it can be embedded return string end function 9.12.2.8 ES Error function findReplaceableBrackets(string) re = any character in possibleOpeningBrackets and possibleClosingBrackets replaceableBracketIndexes = an empty array do o match = re match against string o if (match != null) then push match index in string onto replaceableBracketIndexes while (match != null) end do while loop return replaceableBracketIndexes end function function findInvalidCharacters(string) re = all invalid characters invalidCharacterIndexes = an empty array do o match = re match against string o if (match != null) then push the character index onto invalidCharacterIndexes while (match != null) end do while loop return invalidCharacterIndexes end function function findUnmatchedParentheses(string) re = innermost parentheses (no parentheses within them) unmatchedParenthesesIndexes = an empty array do o match = re match against string o if (match != null) then replace the parentheses with a dummy character while (match != null) end do while loop re = “(” or “)” do 71 o o match = re match against string if (match != null) then unmatchedParenthesesIndexes while (match != null) end do while loop return unmatchedParenthesesIndexes end function push the character index onto function findEmptyParentheses(string) re = “(” followed by 0 or more hashes “#” followed by “)” emptyParenthesesIndexes = an empty array do o match = re match against string o if (match != null) then o push the parentheses indexes onto the emptyParenthesesIndexes array while (match != null) end do while loop return emptyParenthesesIndexes end function function findUnnecessaryParentheses(string) re1 = a pair of parentheses without any inner parentheses re2 = a pair of parentheses immediately around “~” and “#” characters re3 = the start of the string or any character except a letter or number followed by parentheses surrounding 0 or more letters or numbers match1, match2, match3 = null unnecessaryParenthesesIndexes = an empty array do o match1 = re1 match against string o if (match1 != null) then remove the surrounding parentheses and surround the string with “~” and “#” match2 = re2 match against string if (match2 != null) then if (the character immediately before the opening parenthesis is not a letter) then push the parentheses indexes onto unnecessaryParenthesesIndexes and replace the “~” and “#” characters with “@” characters end if o end if while (match1 != null) end do while loop do o match3 = re3 match against string o if (match3 != null) then add any remaining indexes to unnecessaryParenthesesIndexes while (match3 != null) end do while loop return unnecessaryParenthesesIndexes end function function findCombinedOperators(string) re = any combination of a “+” and a “-” combinedOperatorsIndexes = an empty array do o match = re match against string o if (match != null) then 72 for (all the match characters) push combinedOperatorsIndexes replace the characters with hash symbols “#” the character onto o end if while (match != null) end do while loop return combinedOperatorsIndexes end function function findUnmatchedUnaryOperators(string) re = a unary operator preceding anything other than an operand unmatchedUnaryOperatorsIndexes = an empty array do o match = re match against string o if (match != null) then push the character unmatchedUnaryOperatorsIndexes while (match != null) end do while loop return unmatchedUnaryOperatorsIndexes end function index onto function findUnmatchedBinaryOperators(string) re = a binary operator preceding anything other than an operand unmatchedBinaryOperatorsIndexes = an empty array do o match = re match against string o if (match != null) then push the character unmatchedBinaryOperatorsIndexes while (match != null) end do while loop return unmatchedBinaryOperatorsIndexes end function index onto function findInsertedMultiplications(string) re = a letter and (letter or number) next to each other insertedMultiplicationIndexes = an empty array do o match = re match against string o if (match != null) then nextOpenBracket = first “(“ within or after the match if (nextOpen Bracket != -1 or the match is not part of a function) then insert a multiplication between the characters in the match push the index of the inserted character onto insertedMultiplicationIndexes end if else set re to search from after nextOpenBracket o end if while (match != null) end do while loop return insertedMultiplicationIndexes end function function showInputErrors(string, errorIndexArray, color) html = “” if (errorIndexArray length > 0) then o inputErrorIndex = shift an element off the array o for (all the characters in the string) 73 if (inputErrorIndex == character index) then html = html + the character inside a “span” DOM element with its “style” attribute set to have a color which is the given color inputErrorIndex = shift an element off the array end if else html = html + the character end for loop o end if return html end function 9.12.2.9 ES Tokenize function markFunctions(string, functionList, mark, temporaryMark) if (mark == temporaryMark) then return null for (all function names in the functionList) surround function name occurrences in string with temporaryMark if they are not part of a larger function name startIndex = index of the first temporary mark occurrence in string while (startIndex != -1) o find the matching right parenthesis for the left parentheses after temporaryMark o place mark just after right parenthesis o replace temporaryMark at start of function name with mark o remove temporaryMark immediately after function name o startIndex = index of the first temporary mark occurrence in string end while loop return string end function function getFunctionName(string) return string except the first character (assumed to be a marker) end function function getFunctionArgumentCount(string) get everything between the outermost parentheses remove all parentheses and their contents return the number of argument separator characters + 1 end function function tokenize(string) string = markFunctions(string, functions, “$”, “#”) tokens = empty array while (string length > 0) o switch (reCount) o case 0: o re = a number o break o case 1: re = an operator o break o case 2: re = a function o break o case 3: re = a variable o break o case 4: re = an argument separator o break o end switch o match = re match against string o if (match != null) then switch (reCount) 74 case 0: token = match cast as a number break case 1: token = match cast as an operator break case 2: token = match cast as a function break case 3: token = match cast as a variable break case 4: token = match cast as an argument separator break end switch push token onto tokens remove match from string reCount = 0 o end if o else reCount = reCount + 1 end while loop return tokens end function 9.12.2.10 ES Shunting Yard function shunting_yard(string) tokens = tokenize(string) stack, wereValues, argCount, output = empty arrays for (all tokens) o tokenType = getType(token) o if (tokenType == a number or tokenType == a variable) then push token onto output if (wereValues length > 0) then pop wereValues last value push true onto wereValues end if o end if o else if (tokenType == a function) then push token onto stack push 0 onto argCount if (wereValues length > 0) then pop wereValues last value push true onto wereValues end if push false onto wereValues o end if o else if (tokenType == an argument separator) then while (last element of stack is not a left parenthesis) last = pop stack last value push last onto output end while loop w = pop wereValues last value if (w == true) then a = pop argCount last value a=a+1 push a onto argCount 75 end if push false onto wereValues o end if o else if (tokenType == an operator except parentheses) then while (there is an operator, o2, at the end of stack and either the token is left-associative and its precedence is less than or equal to that of o2 or the token is right-associative and its precedence is less than that of o2) last = pop stack last value push last onto output end while loop push token onto stack o end if o else if (tokenType == an operator and it is a left parenthesis) then push token onto stack o else if (tokenType == an operator and it is a right parenthesis) then while (the last element of stack is not a left parenthesis) last = pop stack last value push last onto output end while loop pop the left parenthesis from stack if (the last element of stack is a function) then f = pop stack last value a = pop argCount last value w = pop wereValues last value if (w == true) then a = a + 1 set f argumentCount = a push f onto output end if o end if end for loop for (all remaining stack elements) o last = pop stack last value o push last onto output end for loop return output end function 9.12.2.11 ES Process Global noEval = false, subCount function getXMLHttpRequest() if (an XMLHttpRequest object is available) then return it else return a substitution ActiveX object end function function executeJava() xmlhttp = getXMLHttpRequest() open a request to a server side script using xmlhttp set xmlhttp‟s request header use xmlhttp to send the first 3 arguments (method, expression string, time allowed) to a server side invocation of expression simplifier return the response end function function subexpressionString() result = null operator = the last argument 76 operandOne = the first argument operandTwo = the second argument operatorType = getType(operator) if (operatorType == a function) then o result = operator name + “(” o for (all arguments except the last one) if (it is the second to last argument) then result = result + the second to last argument else result = result + argument + “,” o end for loop o result = result + “)” end if else o if (the operator is unary minus) then result = unary minus concatenated with operandOne o else if (operandOne contains an operator) then if (operandTwo contains an operator) then result = “(” + operandOne + “)” + operator name + “(” + operandTwo + “)” else result = “(” + operandOne + “)” + operator name + operandTwo o end else if o else if (operandTwo contains an operator) then result = operandOne + operator name + “(” + operandTwo + “)” else result = operandOne + operator name + operandTwo o end else end else subCount = subCount + 1 return a span DOM element whose id is equal to subCount and content equals result end function function postfixToInfixExpression(postfixTokens) subCount = 0 operandQueue = an empty array for (all postfixTokens) o tokenType = getType(token) o if (tokenType == an operator or tokenType == a function) then argumentArray = an empty array for (all token arguments) unshift an operand popped from operandQueue onto argumentArray push token onto argumentArray push the result of calling subexpressionString(argumentArray) onto operandQueue o end if o else push the current token onto operandQueue end for loop return pop operandQueue last value and cast as a string end function function simplifySubExpression(string) if (the expression simplifier applet is loaded) then string = the result of calling its simplify method with string and a calculation time limit as parameters else string = the result of calling executeJava with “simplify”, string and a calculation time limit as parameters return string end function function argumentTest() result, simplification, resultString = null 77 operator = the last argument operandOne = the first argument operandTwo = the second argument operatorType = getType(operator) if (operatorType == a function) then o simplification = operator name o result = operator name + “(” o for (all arguments except the last one) if (the argument is a string) then get rid of any double quotes if (it is the second to last argument) then result = result + the second to last argument else result = result + argument + “,” o end for loop o result = result + “)” o resultString = evaluateFunction(arguments) o pop the last value end if else o simplification = getOperatorLongName(operator name) o if (the operator is unary minus) then result = unary minus concatenated with operandOne o else if (operandOne contains an operator) then if (operandTwo contains an operator) then result = “(” + operandOne + “)” + operator name + “(” + operandTwo + “)” else result = “(” + operandOne + “)” + operator name + operandTwo o end else if o else if (operandTwo contains an operator) then result = operandOne + operator name + “(” + operandTwo + “)” else result = operandOne + operator name + operandTwo o end else o if (getType(result) == string) then if (!noEval) then resultString = simplifySubExpression(result) else resultString = result o end if end else call addSubExpression(simplification, result, resultString) return result end function function getLastOperandPosition(position) token = the token at position in postfixTokens tokenType = getType(token) if (tokenType == an operator or function) then o argumentCount = token argument count o while (argumentCount != 0) position = position - 1 tokenType = getType(token at position in postfixTokens) if (tokenType == an operator or function) then argumentCount = (argumentCount - 1) + the argumentCount of the token at position in postfixTokens else argumentCount = argumentCount - 1 o end while loop end if return position end function function markAllHold(postfixTokens) 78 for (all postfixTokens) o tokenType = getType(token) o if (tokenType == a function and its name is Hold) then lastOperandPosition = getLastOperandPosition(the current token) insert double quotes in postfixTokens around current token o end if end for loop end function function evaluatePostfixExpression(postfixTokens) markAllHold(postfixTokens) operandQueue = an empty array for (all postfixTokens) o tokenType = getType(token) o if (tokenType == an operator or tokenType == a function) then argumentArray = an empty array for (all token arguments) unshift an operand popped from operandQueue onto argumentArray push the token onto argumentArray result = argumentTest(argumentArray) if (result does not contain “NaN”) then push result onto operandQueue else break o end if o else if (tokenType == a string) then noEval = !noEval o else push the current token onto operandQueue end for loop return pop operandQueue last value and cast as a string end function function Hold(string) return string surrounded by quotes end function function Expand(string) if (string contains a “(” character) then o expand = simplifySubExpression("Expand[" + string + "]") o if (the simplification succeeded) then string = expand end if return string end function function Factor(string) factor = simplifySubExpression("Factor[" + string + "]") if (the simplification succeeded) then string = factor return string end function function Rationalize(string) rationalized = simplifySubExpression("Rationalize[" + string + "]") if (the simplification succeeded) then string = rationalized return string end function function CombineBases(string) combinedBases = simplifySubExpression("CombineBases[" + string + "]") if (the simplification succeeded) then string = combinedBases return string end function 79 function Rationalise(string) rationalised = simplifySubExpression("Rationalise[" + string + "]") if (the simplification succeeded) then string = rationalised return string end function function RationaliseMonomial(string) rationalMonomial = simplifySubExpression("RationaliseMonomial[" + string + "]") if (the simplification succeeded) then string = rationalMonomial return string end function function RationaliseBinomial(string) rationalBinomial = simplifySubExpression("RationaliseBinomial[" + string + "]") if (the simplification succeeded) then string = rationalBinomial return string end function function Log(string) logarithm = simplifySubExpression("Log[" + string + "]") if (the simplification succeeded) then string = logarithm else string = “NaN” return string end function function Sin(string) return simplifySubExpression("Sin[" + string + "]") end function function ArcSin(string) return simplifySubExpression("ArcSin[" + string + "]") end function function Cos(string) return simplifySubExpression("Cos[" + string + "]") end function function ArcCos(string) return simplifySubExpression("ArcCos[" + string + "]") end function function Tan(string) return simplifySubExpression("Tan[" + string + "]") end function function ArcTan(string) return simplifySubExpression("ArcTan[" + string + "]") end function function Numeric(string) return simplifySubExpression("N[" + string + "]") end function function Power(string) return simplifySubExpression(first argument + “^” + second argument) end function 80 function Times(string) return simplifySubExpression(first argument + “*” + second argument) end function function Divide(string) return simplifySubExpression(first argument + “/” + second argument) end function function Plus(string) return simplifySubExpression(first argument + “+” + second argument) end function function Minus(string) return simplifySubExpression(first argument + “-” + second argument) end function function Nroot(string) return simplifySubExpression(first argument + “^(1/” + second argument + “)”) end function function TrigToExp(string) conversion = simplifySubExpression("TrigToExp[" + string + "]") if (the simplification succeeded) then string = TrigToExp return string end function function Pii(string) if (an argument is given) then return simplifySubExpression("Pi^” + string) else return “Pi” end function function Exp(string) if (an argument is given) then return simplifySubExpression("E^” + string) else return “E” end function function Im(string) if (an argument is given) then return simplifySubExpression("I^” + string) else return “I” end function function Roots(string) roots = simplifySubExpression("Roots[" + string + "]") if (the simplification succeeded) then string = roots return string end function 9.12.2.12 ES Main Global postfixTokens, subExpressionCount, displayCount, ES_symjaAppletLoaded = false, ExpressionTreeAppletLoaded = false, menuOpen = false, normalColor = “blue”, highlightColor = "green", errorColor = "red" function customEscape(String) string = escape(string) return string with some escaped characters replaced with their unescaped versions end function 81 function format(string) clear the format log output string (the input expression) to the format log newString = replaceBrackets(string) if (newString != string) then output it to the format log newString = removeInvalidCharacters(string) if (newString != string) then output it to the format log newString = closeUnmatchedParentheses(string) if (newString != string) then output it to the format log newString = removeEmptyParentheses(string) if (newString != string) then output it to the format log newString = replaceCombinedOperators(string) if (newString != string) then output it to the format log newString = removeUnmatchedUnaryOperators(string) if (newString != string) then output it to the format log newString = removeUnmatchedBinaryOperators(string) if (newString != string) then output it to the format log newString = removeUnnecessaryParentheses(string) if (newString != string) then output it to the format log newString = insertMultiplication(string) if (newString != string) then output it to the format log string = replaceUnaryMinus(string) output string to the format log return string end function function drawPostfixExpressionTree(postfixTokens) call the expression tree applet‟s clearPostfixTokens method argumentCount = 0 for (all tokens in postfixTokens) o if (getType(token) == an operator or function) then argumentCount = token.argumentCount o else argumentCount = 0 o call the expression tree applet‟s addPostfixToken method passing the token and argumentCount as parameters o call the expression tree applet‟s buildRootTree method o call the expression tree applet‟s repaint method end for loop end function function displayFormat(string) return the string in a displayable format by replacing characters with standard representations end function function rgb2hex(string) rgb = match each color in the rgb representation function hex(x) o return (“0” + parseInt(x,10) cast as a string to base 16) padded with 0s if needed end function return “#” + hex(rgb first match) + hex(rgb second match) + hex(rgb third match) end function function changeSimplifyInnerHtml() if (the MathML checkbox is ticked) change the simplify log to use MathML notation else change it to use standard script notation end function 82 function addSubExpression(simplification, input, output) input = displayFormat(input cast as a string) output = displayFormat(output cast as a string) if (input != output) then output the following into the simplifyText div box o the simplification o the full infix expression with the input highlighted o input + “=” + output o the full infix expression with the output replacing the input and highlighted o repeat the above but output it as MathML into the simplifyMathML div box o displayCount = displayCount + 1 changeSimplifyInnerHtml() subExpressionCount = subExpressionCount + 1 end function function doSimplify() clear the simplify log and all associated components subExpressionCount = 1 displayCount = 1 inputExpression = get the user input infix expression from the input text box inputExpression = format(inputExpression) output inputExpression to the formatted expression text box postfixTokens = shunting_yard(inputExpression) output postfixTokens to the postfix expression text box drawPostfixExpressionTree(postfixTokens) expressionTreeString = call the expression tree applet‟s getExpression method output evaluatePostfixExpression(postfixTokens) to the result text box end function function getMatchingFunctions(string, functions) matchingFunctions = an empty array for (all functions) o if (string matches the function name start) then push it onto matchingFunctions end for loop return matchingFunctions end function function getLastDelimeterIndex(string) if (an operator can be found in string reversed) then return (string length – 1) – the first occurrence index else return the first occurrence index end function function extractLast(string) lastDelimeterIndex = getLastDelimeterIndex(term) if (lastDelimeterIndex != -1) then return string substring from lastDelimeterIndex + 1 to end else return string end function function closeUserGuide() set the user guide to have a hidden display end function function findIntersectors(targetSelector, intersectorsSelector) intersectors = an empty array for (all the elements in intersectorsSelector) o if (the current element overlaps targetSelector) then add it to intersectors 83 end for loop return intersectors end function function checkExpressionTreeAppletOverlap(elementToCheck, elementsIntersecting) intersectors = findIntersectors(elementToCheck, elementsIntersecting) for (all the elements in intersectors) o if (the current element z-index is greater than the expression tree applet‟s z-index) then hide the expression tree applet o return end for loop show the expression tree applet end function function ExpressionTreeLoaded () ES_symjaAppletLoaded = true end function function ES_symjaLoaded() ExpressionTreeAppletLoaded = true end function function showElements() hide the loading image show all hidden components end function function waitForExpressionTreeApplet() try to check if the expression tree applet is loaded and if it is call showElements if (an exception occurs) then try calling this method again in half a second end function function bodyInit() make all central components draggable and resizeable and set most of their initial sizes make the input button be clicked when enter is pressed in the input text box setup the autocomplete to have a minimum length of 1 and to suggest function names after each operator setup the user guide dataTables to be able to display subsets of records make clicking the help button display the user guide enable the theme switcher add the bottom left text (name, e-mail and last modification time) end function function checkJavaSupport() if (the Java version is at minimum 1.6 and the object tag is supported and the Java 2 PlugIn is being used) then display the loading image and call bodyInit() else display an error message end function function checkJavaScriptSupport() remove an error message end function $(document).ready(function () { checkJavaScriptSupport() checkJavaSupport() }) 84 9.13 Source code 9.13.1 XHTML/CSS source code 9.13.1.1 ExpressionSimplifierBeautified.xhtml 1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" 3 "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> 4 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> 5 <head> 6 <!--<meta http-equiv="Pragma" content="no-cache" /> 7 <meta http-equiv="Expires" content="-1" />--> 8 <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" /> 9 <meta http-equiv="content-script-type" content="text/javascript" /> 10 <meta http-equiv="content-style-type" content="text/css" /> 11 <meta http-equiv="accept-encoding" content="pack200-gzip,gzip" /> 12 <meta http-equiv="X-UA-Compatible" content="IE=8" /> 13 <title>Expression Simplifier</title> 14 <link rel="stylesheet" type="text/css" href="css/base.css" /> 15 <link rel="stylesheet" type="text/css" href="css/ExpressionSimplifier.css" /> 16 <script type="text/javascript" src="js/jQuery/jquery-1.4.2.min.js"></script> 17 <script type="text/javascript" src="js/jQuery/jquery-ui-1.8.4.custom.min.js"></script> 18 <script type="text/javascript" src="js/Expression Simplifier/ES Declaration.js"></script> 19 <script type="text/javascript" src="js/Expression Simplifier/ES Utilities.js"></script> 20 <script type="text/javascript" src="js/Expression Simplifier/ES Operators.js"></script> 21 <script type="text/javascript" src="js/Expression Simplifier/ES Functions.js"></script> 22 <script type="text/javascript" src="js/Expression Simplifier/ES Variables.js"></script> 23 <script type="text/javascript" src="js/Expression Simplifier/ES Argument Separators.js"></script> 24 <script type="text/javascript" src="js/Expression Simplifier/ES Format.js"></script> 25 <script type="text/javascript" src="js/Expression Simplifier/ES Error.js"></script> 26 <script type="text/javascript" src="js/Expression Simplifier/ES Tokenize.js"></script> 27 <script type="text/javascript" src="js/Expression Simplifier/ES Shunting Yard.js"></script> 28 <script type="text/javascript" src="js/Expression Simplifier/ES Process.js"></script> 29 <script type="text/javascript" src="js/ASCII2MathML/ASCII2MathML-Evaluate.js"></script> 30 <script type="text/javascript" src="js/ASCII2MathML/ASCII2MathML-Core.js"></script> 31 <script type="text/javascript" src="js/ASCII2MathML/ASCII2MathML-FallBack.js"></script> 32 <script type="text/javascript" src="js/ASCII2MathML/ASCII2MathML-Extensions.js"></script> 33 <script type="text/javascript" src="js/xregexp.js"></script> 34 <script type="text/javascript" src="js/Expression Simplifier/ES Main.js"></script> 35 <script type="text/javascript" src="js/jQuery/jquery.dataTables.min.js"></script> 36 <script type="text/javascript" src="js/jQuery/themeswitchertool.js"></script> 37 <script type="text/javascript" src="js/PluginDetect.js"></script> 38 <!--<script type="text/javascript" src="js/jQuery/jquery.alphanumeric.pack.js"></script>--> 39 </head> 40 <body> 41 42 <div id="header" class="ui-widget-header hidden"> 43 <h1>Expression Simplifier</h1> 44 <div id="switcherWrapper"> 45 <div id="switcher"> 46 </div> 47 </div> 48 </div> 49 50 <div> 51 <a href="" onclick="return false;"><img id="help" class="ui-widget-header hidden" src="images/help.png" alt="User Guide" width="64" height="64 85 "/></a> 52 </div> 53 54 <div id="TextBoxes" class="ui-widget-content hidden"> 55 <p class="ui-widget-header"><span>Input Expression</span></p> 56 <input type="text" id="input" value="" /><br /><br /> 57 <p class="ui-widget-header"><span>Formatted Expression</span></p> 58 <input type="text" id="formattedInput" readonly="readonly" /><br /><br /> 59 <p class="ui-widget-header"><span>Postfix Expression</span></p> 60 <input type="text" id="postfixExpression" readonly="readonly" /><br /><br /> 61 <p class="ui-widget-header"><span>Expression Tree Expression</span></p> 62 <input type="text" id="expressionTreeExpression" readonly="readonly" /><br /><br /> 63 <p class="ui-widget-header"><span>Simplification Result</span></p> 64 <input type="text" id="result" readonly="readonly" /><br /><br /> 65 <div id="buttonWrapper"><input type="button" id="calculate" value="Simplify" onclick="doSimplify();" /></div> 66 </div> 67 68 <div id="UserGuide" class="ui-widget-content hidden"> 69 <div id="UserGuideHeader" class="ui-widget-header"> 70 <h3><span>User Guide</span></h3> 71 <a href="#"><span id="closeButton" class="ui-icon ui-icon-closethick" onclick="closeUserGuide();"></span></a> 72 </div> 73 <div id="guide"> 74 <h4 class="ui-widget-header"><span>Use</span></h4> 75 <p>To simplify a mathematical expression:</p> 76 <ol> 77 <li> 78 Enter the expression into the "Input Expression" text box using infix script notation. 79 <ul> 80 <li>Infix means operators are placed between their operands in the standard way (e.g. 2+2).</li> 81 <li>Script means exponentiation/power, multiplication, division, addition and subtraction are represented by ^, *, /, + and - (see the operation tabl e for examples).</li> 82 </ul> 83 </li> 84 <li>Press the enter key inside the "Input Expression" text box or click the "Simplify" button to begin the simplification process.</li> 85 </ol> 86 <p>Please note:</p> 87 <ul> 88 <li>Expressions with one or more floating point numbers will be evaluated otherwise they will be simplified.</li> 89 <li>Only the operations and functions displayed in the operation and function tables are supported and must be represented exactly as shown.</l i> 90 <li>Π (pi) can be obtained using pii() or pii(1) for a numeric form and Pii() or Pii(1) for an expression form.</li> 91 <li>e can be obtained using exp() or exp(1) for a numeric form and Exp(), Exp(1) or E for an expression form.</li> 92 <li>i (imaginary number) can be obtained using Im(), Im(1) or I for an expression form.</li> 93 <li>Not A Number (NaN) (or any other word) results if a mathematical error occurs during simplification for example logn(10,0) will result in NaN. </li> 94 </ul> 95 <h4 class="ui-widget-header"><span>Operations</span></h4> 96 <table id="operations" border="1"> 86 97 <thead> 98 <tr><th>Operation</th><th>Input</th><th>Returns</th></tr> 99 </thead> 100 <tbody> 101 <tr><td>x^y</td><td>two expressions x and y</td><td>x to the power of y</td></tr> 102 <tr><td>x*y</td><td>two expressions x and y</td><td>x multiplied by y</td></tr> 103 <tr><td>x/y</td><td>two expressions x and y</td><td>x divided by y</td></tr> 104 <tr><td>x+y</td><td>two expressions x and y</td><td>x added to y</td></tr> 105 <tr><td>x-y</td><td>two expressions x and y</td><td>y subtracted from x</td></tr> 106 </tbody> 107 </table> 108 <h4 class="ui-widget-header"><span>Functions</span></h4> 109 <table id="functions" border="1"> 110 <thead> 111 <tr><th>Function</th><th>Input</th><th>Returns</th></tr> 112 </thead> 113 <tbody> 114 <tr><td>random()</td><td>none</td><td>a random number greater than or equal to 0 but less than 1</td></tr> 115 <tr><td>sin(n)</td><td>a number n</td><td>sine of n</td></tr> 116 <tr><td>asin(n)</td><td>a number n</td><td>arcsine of n</td></tr> 117 <tr><td>cos(n)</td><td>a number n</td><td>cosine of n</td></tr> 118 <tr><td>acos(n)</td><td>a number n</td><td>arccosine of n</td></tr> 119 <tr><td>tan(n)</td><td>a number n</td><td>tangent of n</td></tr> 120 <tr><td>atan(n)</td><td>a number n</td><td>arctangent of n</td></tr> 121 <tr><td>exp(n)</td><td>a number n</td><td>e to the power of n</td></tr> 122 <tr><td>pii(n)</td><td>a number n</td><td>pi to the power of n</td></tr> 123 <tr><td>ceil(n)</td><td>a number n</td><td>n rounded up to the nearest integer</td></tr> 124 <tr><td>floor(n)</td><td>a number n</td><td>n rounded down to the nearest integer</td></tr> 125 <tr><td>abs(n)</td><td>a number n</td><td>absolute (positive) value of n</td></tr> 126 <tr><td>log(n)</td><td>a number n</td><td>base 10 logarithm of n</td></tr> 127 <tr><td>round(n)</td><td>a number n</td><td>the nearest integer to n</td></tr> 128 <tr><td>Rationalize(n)</td><td>a number n</td><td>a rational expression equal to n</td></tr> 129 <tr><td>logn(n1,n2), Log(n1,n2)</td><td>two numbers n1 and n2</td><td>logarithm to base n1 of n2</td></tr> 130 <tr><td>max(n1,n2)</td><td>two numbers n1 and n2</td><td>the maximum of n1 and n2</td></tr> 131 <tr><td>min(n1,n2)</td><td>two numbers n1 and n2</td><td>the minimum of n1 and n2</td></tr> 132 <tr><td>roundTo(n1,n2)</td><td>two numbers n1 and n2</td><td>n1 rounded to n2 decimal places</td></tr> 133 <tr><td>power(n1,n2)</td><td>two numbers n1 and n2</td><td>n1 to the power of n2</td></tr> 134 <tr><td>times(n1,n2)</td><td>two numbers n1 and n2</td><td>n1 multiplied by n2</td></tr> 135 <tr><td>divide(n1,n2)</td><td>two numbers n1 and n2</td><td>n1 divided by n2</td></tr> 136 <tr><td>plus(n1,n2)</td><td>two numbers n1 and n2</td><td>n1 added to n2</td></tr> 137 <tr><td>minus(n1,n2)</td><td>two numbers n1 and n2</td><td>n2 subtracted from n1</td></tr> 138 <tr><td>nroot(n1,n2)</td><td>two numbers n1 and n2</td><td>n1 root of n2</td></tr> 139 <tr><td>randomBetween(n1,n2,n3)</td><td>three numbers n1, n2 and n3 where n3 is an integer and n1 < n2</td><td>a random number between n1 and n2 inclusive with n3 decimal places</td></tr> 140 <tr><td>maxn(n1,n2,…,nN)</td><td>numbers n1 to nN</td><td>maximum of n1 to nN</td></tr> 141 <tr><td>minn(n1,n2,…,nN)</td><td>numbers n1 to nN</td><td>minimum of n1 to nN</td></tr> 87 142 <tr><td>avg(n1,n2,…,nN)</td><td>numbers n1 to nN</td><td>arithmetic mean of n1 to nN</td></tr> 143 <tr><td>Sin(x)</td><td>an expression x</td><td>sine of x</td></tr> 144 <tr><td>ArcSin(x)</td><td>an expression x</td><td>arcsine of x</td></tr> 145 <tr><td>Cos(x)</td><td>an expression x</td><td>cosine of x</td></tr> 146 <tr><td>ArcCos(x)</td><td>an expression x</td><td>arccosine of x</td></tr> 147 <tr><td>Tan(x)</td><td>an expression x</td><td>tangent of x</td></tr> 148 <tr><td>ArcTan(x)</td><td>an expression x</td><td>arctangent of x</td></tr> 149 <tr><td>Pii(x)</td><td>an expression x</td><td>pi to the power of x</td></tr> 150 <tr><td>Exp(x)</td><td>an expression x</td><td>e to the power of x</td></tr> 151 <tr><td>Im(x)</td><td>an expression x</td><td>i (imaginary number) to the power of x</td></tr> 152 <tr><td>Numeric(x)</td><td>an expression x</td><td>evaluates the given expression if possible even if it doesn't have decimal values</td></tr> 153 <tr><td>Hold(x)</td><td>an expression x</td><td>the same expression but prevented from being evaluated</td></tr> 154 <tr><td>Expand(x)</td><td>an expression x</td><td>an expanded expression</td></tr> 155 <tr><td>Factor(x)</td><td>an expression x</td><td>a factorized expression</td></tr> 156 <tr><td>Power(x,y)</td><td>two expressions x and y</td><td>x to the power of y</td></tr> 157 <tr><td>Times(x,y)</td><td>two expressions x and y</td><td>x multiplied by y</td></tr> 158 <tr><td>Divide(x,y)</td><td>two expressions x and y</td><td>x divided by y</td></tr> 159 <tr><td>Plus(x,y)</td><td>two expressions x and y</td><td>x added to y</td></tr> 160 <tr><td>Minus(x,y)</td><td>two expressions x and y</td><td>y subtracted from x</td></tr> 161 <tr><td>Nroot(x,y)</td><td>two expressions x and y</td><td>x root of y</td></tr> 162 <tr><td>CombineBases(x)</td><td>an expression x expecting two or more powers with the same exponent multiplied together</td><td>x with th e multiplication of two or more bases to the same exponent (only the first power found is attempted to be combined)</td></tr> 163 <tr><td>Rationalise(x)</td><td>an expression x expecting a power with a decimal exponent</td><td>a power with a fractional exponent</td></tr > 164 <tr><td>RationaliseMonomial(x)</td><td>an expression x expecting a monomial surd as a fraction denominator</td><td>a rationalized fraction</ td></tr> 165 <tr><td>RationaliseBinomial(x)</td><td>an expression x expecting a binomial expression with a square root as a fraction denominator</td><td> a rationalized fraction</td></tr> 166 <tr><td>TrigToExp(x)</td><td>an expression x expecting an unevaluated trigonometric function (which must start with an uppercase letter)</td> <td>the exponential representation of x</td></tr> 167 <tr><td>Roots(x)</td><td>an expression x expecting a univariate polynomial function</td><td>the root(s) of x</td></tr> 168 </tbody> 169 </table> 170 <h4 class="ui-widget-header"><span>Source</span></h4> 171 <ul> 172 <li><a href="ExpressionSimplifierBeautified.txt">Extensible Hypertext Markup Language (XHTML) (as text)</a></li> 173 <li><a href="css">Cascading Style Sheets (CSS)</a></li> 174 <li><a href="js/">JavaScript (JS)</a></li> 175 <li><a href="java/">Java</a></li> 176 </ul> 177 </div> 178 </div> 179 180 <div id="FormatLog" class="ui-widget-content hidden"> 181 <h3 class="ui-widget-header"><span>Format Log</span></h3> 182 <div id="format"> 183 </div> 88 184 </div> 185 186 <div id="SimplifyLog" class="ui-widget-content hidden"> 187 <div id="SimplifyLogHeader" class="ui-widget-header"> 188 <h3><span>Simplify Log</span></h3> 189 <span id="useMathML">MathML<input type="checkbox" id="mathMLCheckbox" onclick="changeSimplifyInnerHtml();"/></span> 190 </div> 191 <div id="simplifyText" class="simplify nodisplay"> 192 </div> 193 <div id="simplifyMathML" class="simplify nodisplay"> 194 </div> 195 <div id="currentExpression" class="nodisplay"> 196 </div> 197 </div> 198 199 <div id="ExpressionTree" class="ui-widget-content hidden"> 200 <h3 class="ui-widget-header"><span>Expression Tree</span></h3> 201 <div id="ExpressionTreeAppletWrapper"> 202 <!--[if !IE]><!--> 203 <object id="ExpressionTreeApplet" width="100%" height="100%" type="application/x-javaapplet" classid="java:ExpressionTree" archive="ES_E xpressionTree.jar" codebase="classes/applet"> 204 <param name="java_arguments" value="-Djnlp.packEnabled=true" /> 205 <param name="codebase_lookup" value="false" /> 206 <param name="archive" value="ES_ExpressionTree.jar" /> 207 <param name="codebase" value="classes/applet" /> 208 <param name="java_codebase" value="classes/applet" /> 209 <param name="code" value="ExpressionTree.class" /> 210 <param name="mayscript" value="true" /> 211 <param name="scriptable" value="true" /> 212 <param name="wmode" value="transparent" /> 213 </object> 214 <!--<![endif]--> 215 <!--[if IE]> 216 <object id="ExpressionTreeApplet" width="100%" height="100%" classid="clsid:8AD9C840044E-11D1-B3E9-00805F499D93"> 217 <param name="java_arguments" value="-Djnlp.packEnabled=true" /> 218 <param name="codebase_lookup" value="false" /> 219 <param name="archive" value="ES_ExpressionTree.jar" /> 220 <param name="codebase" value="classes/applet" /> 221 <param name="code" value="ExpressionTree.class" /> 222 <param name="mayscript" value="true" /> 223 <param name="scriptable" value="true" /> 224 <param name="wmode" value="transparent" /> 225 </object> 226 <![endif]--> 227 </div> 228 </div> 229 230 <div id="ExpressionSimplifier"> 231 <!--[if !IE]><!--> 232 <object id="ES_symja_Applet" width="0" height="0" type="application/x-java-applet" classid="java:ES_symja" archive="ES_symja.jar" codebase ="classes/applet"> 233 <param name="java_arguments" value="-Djnlp.packEnabled=true" /> 234 <param name="codebase_lookup" value="false" /> 235 <param name="cache_archive" value="ES_symja.jar" /> 236 <param name="archive" value="ES_symja.jar" /> 89 237 <param name="codebase" value="classes/applet" /> 238 <param name="java_codebase" value="classes/applet" /> 239 <param name="code" value="ES_symja.class" /> 240 <param name="mayscript" value="true" /> 241 <param name="scriptable" value="true" /> 242 </object> 243 <!--<![endif]--> 244 <!--[if IE]> 245 <object id="ES_symja_Applet" width="0" height="0" classid="clsid:8AD9C840-044E-11D1B3E9-00805F499D93"> 246 <param name="java_arguments" value="-Djnlp.packEnabled=true" /> 247 <param name="codebase_lookup" value="false" /> 248 <param name="cache_archive" value="ES_symja.jar" /> 249 <param name="archive" value="ES_symja.jar" /> 250 <param name="codebase" value="classes/applet" /> 251 <param name="code" value="ES_symja.class" /> 252 <param name="mayscript" value="true" /> 253 <param name="scriptable" value="true" /> 254 </object> 255 <![endif]--> 256 </div> 257 258 <div id="bottomLeft" class="ui-widget-header hidden"> 259 <a href="http://www.gnu.org/licenses/lgpl.html"><img class="noBorder" src="images/lgplv388x31.png" alt="Lesser General Public License Versi on 3" width="88" height="31" /></a> 260 </div> 261 262 <div id="bottomRight" class="ui-widget-header hidden"> 263 <a href="http://validator.w3.org/check?uri=referer"> 264 <img class="noBorder" src="images/valid-xhtml11-blue.png" alt="Valid XHTML 1.1" width="88" height="31" /> 265 <img class="noBorder" src="images/valid-mathml20-blue.png" alt="Valid MathML 2.0" width="88" height="31" /> 266 </a> 267 <!--[if IE]> 268 <a href="http://www.dessci.com/getmp"><img src="images/MathPlayerDownload.gif" border="0" alt="Download MathPlayer"></a> 269 <![endif]--> 270 </div> 271 272 <div id="loader" class="hidden"><img src="images/loader.gif" alt="Loading, please be patient." width="200" height="200" /></div> 273 274 <div id="javaScriptError" class="ui-widget"> 275 <div class="ui-state-error ui-corner-all"> 276 <p><span class="ui-icon ui-icon-alert"></span> 277 <strong>Alert:</strong> JavaScript is required for this website. <br /> 278 Please revisit the page after enabling JavaScript and/or downloading and installing a browser which supports it.</p> 279 </div> 280 </div> 281 282 <div id="javaError" class="ui-widget hidden"> 283 <div class="ui-state-error ui-corner-all"> 284 <p><span class="ui-icon ui-icon-alert"></span> 285 <strong>Alert:</strong> Java Runtime Environment (JRE) Version 1.6 or higher is required for this website and was not detected.<br />Please re visit the page after downloading and installing the latest JRE version from:</p> 90 286 <a href="http://www.java.com/en/download/index.jsp">http://www.java.com/en/download/index.jsp</a > 287 </div> 288 </div> 289 290 <!--<script type="text/javascript"> 291 /*<![CDATA[*/ 292 var newImg = document.createElement("img"); 293 newImg.setAttribute("src", 'image.jpg?state=' + new Date().getTime()); 294 newImg.setAttribute("visibility", "hidden"); 295 document.body.appendChild(newImg); 296 /*]]>*/ 297 </script>--> 298 299 </body> 300 <!--<head> 301 <meta http-equiv="Pragma" content="no-cache" /> 302 <meta http-equiv="Expires" content="-1" /> 303 </head>--> 304 </html> 9.13.1.2 ExpressionSimplifier.css 1* 2{ 3 font-family:Arial, Helvetica, sans-serif; 4} 5 6 #header 7{ 8 position:absolute; 9 width:90%; 10 left:1%; 11 } 12 13 #switcherWrapper 14 { 15 position:relative; 16 } 17 18 #switcher 19 { 20 position:absolute; 21 right:0; 22 top:0; 23 } 24 25 #help 26 { 27 position:absolute; 28 right:1%; 29 } 30 31 #UserGuide 32 { 33 position:absolute; 34 width:30%; 35 height:58%; 91 36 z-index:5; 37 } 38 39 #guide 40 { 41 overflow:auto; 42 height:90%; 43 width:100%; 44 } 45 46 #UserGuideHeader 47 { 48 white-space:nowrap; 49 } 50 51 #TextBoxes 52 { 53 position:absolute; 54 top:15%; 55 left:1%; 56 white-space:nowrap; 57 z-index:1; 58 } 59 60 #buttonWrapper 61 { 62 position:relative; 63 } 64 65 #calculate 66 { 67 position:absolute; 68 right:0; 69 bottom:0; 70 } 71 72 #ExpressionTree 73 { 74 position:absolute; 75 top:15%; 76 right:1%; 77 white-space:nowrap; 78 z-index:4; 79 } 80 81 #ExpressionTreeApplet 82 { 83 width:0%; 84 height:0%; 85 visibility:visible; 86 } 87 88 #FormatLog 89 { 90 position:absolute; 91 top:15%; 92 left:26.5%; 93 white-space:nowrap; 94 z-index:2; 92 95 } 96 97 #SimplifyLog 98 { 99 position:absolute; 100 top:52%; 101 left:26.5%; 102 white-space:nowrap; 103 z-index:3; 104 } 105 106 #format, .simplify 107 { 108 overflow:auto; 109 white-space:normal; 110 } 111 112 #UserGuideHeader, #SimplifyLogHeader 113 { 114 position:relative; 115 } 116 117 #closeButton, #useMathML 118 { 119 position:absolute; 120 top:0; 121 right:0; 122 } 123 124 #bottomLeft 125 { 126 position:absolute; 127 bottom:1%; 128 left:1%; 129 } 130 131 #bottomRight 132 { 133 position:absolute; 134 bottom:1%; 135 right:1%; 136 } 137 138 /*a:link 139 { 140 color:#66F; 141 } 142 143 a:visited 144 { 145 color:#60C; 146 } 147 148 a:hover 149 { 150 color:#0CF; 151 }*/ 152 153 p,h3,h1 93 154 { 155 margin-top:0; 156 margin-bottom:0; 157 } 158 159 .hidden 160 { 161 visibility:hidden; 162 } 163 164 .nodisplay, #operations_wrapper div, .dataTables_wrapper table + div 165 { 166 display:none; 167 } 168 169 .noBorder 170 { 171 border:none; 172 } 173 174 #me, input, .ui-menu .ui-menu-item a 175 { 176 font-size:.75em; 177 } 178 179 .ui-menu .ui-menu-item a 180 { 181 line-height:1.0; 182 } 183 184 .dataTables_wrapper 185 { 186 font-size:.88em; 187 } 188 189 .ui-resizable-helper 190 { 191 border: 2px dotted #00F; 192 } 193 194 #javaScriptError 195 { 196 position:absolute; 197 bottom:50%; 198 left:50%; 199 width:470px; 200 margin-left:-235px; 201 } 202 203 #javaError 204 { 205 position:absolute; 206 bottom:50%; 207 left:50%; 208 width:1000px; 209 margin-left:-500px; 210 } 211 212 .ui-state-error 94 213 { 214 padding: 0pt 0.7em; 215 text-align:center; 216 } 217 218 .ui-icon-alert 219 { 220 float: left; 221 margin-right: 0.3em; 222 } 9.13.2 JavaScript source code 9.13.2.1 ES Declaration.js 1 "use strict"; 2 3 var validCharacters = "()a-zA-Z0-9.,^*/+\-", 4 //Valid characters recognised by the system 5 possibleOpeningBrackets = "\\[\\{\\<", 6 //Opening brackets recognised by the system 7 possibleClosingBrackets = "\\]\\}\\>"; //Closing brackets recognised by the system 9.13.2.2 ES Utilities.js 1 "use strict"; 2 3 //Creates a new (Java)Script element in the body of the document whose source is the given URL 4 function importScript(url) 5{ 6 var tag = document.createElement("script"); 7 tag.type = "text/javascript"; 8 tag.src = url; 9 document.body.appendChild(tag); 10 } 11 12 //Returns the given String with the characters from the given start index up to but not including the given stop index replaced by the given replace ment String 13 function replaceBetween(string, startIndex, stopIndex, replacement) 14 { 15 if (startIndex < 0) 16 { 17 startIndex = 0; 18 } 19 if (stopIndex > string.length) 20 { 21 stopIndex = string.length; 22 } 23 return string.substring(0, startIndex) + replacement + string.substring(stopIndex); 24 } 25 26 //Returns the given regular expression with the global parameter set to true if it was originally false 27 function makeGlobal(re) 28 { 29 if (re.global === false) 30 { 95 31 var reString = re.toString(), 32 firstSlash = reString.indexOf("/"), 33 lastSlash = reString.lastIndexOf("/"), 34 reParams = reString.substring(lastSlash + 1); 35 reString = re.toString().substring(firstSlash + 1, lastSlash); 36 re = new RegExp(reString, "g" + reParams); 37 } 38 return re; 39 } 40 41 //Returns an integer representing the number of times a given regular expression matches the given String 42 function countMatches(string, re) 43 { 44 re.lastIndex = 0; 45 var m = string.match(re); 46 if (m !== null) 47 { 48 return m.length; 49 } 50 else 51 { 52 return 0; 53 } 54 } 55 56 //Returns an integer representing the number of times a given regular expression matches the given String from the given start index up to but not including the given stop index 57 function countMatchesBetween(string, startIndex, stopIndex, re) 58 { 59 re.lastIndex = 0; 60 string = string. substring (startIndex, stopIndex); 61 return countMatches(string, re); 62 } 63 64 //Returns the index of the closing bracket matching the opening bracket assumed to be at the given index 65 function findClosingBracket(string, index) 66 { 67 if (index < 0) 68 { 69 index = 0; 70 } 71 if (index >= string.length - 1 || string.charAt(index) !== "(") 72 { 73 return -1; 74 } 75 var unmatchedBracketCount = 1; 76 while (unmatchedBracketCount !== 0) 77 { 78 index = index + 1; 79 if (index >= string.length) 80 { 81 return -1; 82 } 83 else if (string.charAt(index) === ")") 84 { 85 unmatchedBracketCount = unmatchedBracketCount - 1; 96 86 } 87 else if (string.charAt(index) === "(") 88 { 89 unmatchedBracketCount = unmatchedBracketCount + 1; 90 } 91 } 92 return index; 93 } 94 95 //Returns the index of the opening bracket matching the closing bracket assumed to be at the given index 96 function findOpeningBracket(string, index) 97 { 98 if (index <= 0 || string.charAt(index) !== ")") 99 { 100 return -1; 101 } 102 if (index > string.length - 1) 103 { 104 index = string.length - 1; 105 } 106 var unmatchedBracketCount = -1; 107 while (unmatchedBracketCount !== 0) 108 { 109 index = index - 1; 110 if (index < 0) 111 { 112 return -1; 113 } 114 else if (string.charAt(index) === ")") 115 { 116 unmatchedBracketCount = unmatchedBracketCount - 1; 117 } 118 else if (string.charAt(index) === "(") 119 { 120 unmatchedBracketCount = unmatchedBracketCount + 1; 121 } 122 } 123 return index; 124 } 125 126 //Returns the given String with all occurrences of the given regExp marked at the beginning and the end with the given symbol 127 function surroundString(string, regExp, mark, lookBehind) 128 { 129 var replacement, startIndex, m = regExp.exec(string); 130 if (m !== null && lookBehind !== null) 131 { 132 while (m !== null && string.substring(0, regExp.lastIndex).match(lookBehind) !== null) 133 { 134 startIndex = regExp.lastIndex - m[0].length; 135 replacement = mark + m[0] + mark; 136 string = replaceBetween(string, startIndex, regExp.lastIndex, replacement); 137 regExp.lastIndex += replacement.length - m[0].length; 138 m = regExp.exec(string); 139 } 140 } 141 else 142 { 97 143 while (m !== null) 144 { 145 startIndex = regExp.lastIndex - m[0].length; 146 replacement = mark + m[0] + mark; 147 string = replaceBetween(string, startIndex, regExp.lastIndex, replacement); 148 regExp.lastIndex += replacement.length - m[0].length; 149 m = regExp.exec(string); 150 } 151 } 152 return string; 153 } 154 155 //Return the type of the given argument 156 function getType(argument) 157 { 158 var type = typeof argument, 159 constructor; 160 if (type === "object") 161 { 162 constructor = argument.constructor. toString (); 163 constructor = constructor.substring(constructor.match(/function\s*[_a-zA-Z]/)[0].length - 1); 164 constructor = constructor.substring(0, constructor.search(/\s|\(/)); 165 return constructor; 166 } 167 else 168 { 169 return type; 170 } 171 } 172 173 //Returns an integer representing the number of decimal places in a given number determined using the given decimal separator 174 function countDecimals(number, decimalSeparator) 175 { 176 var numberString = number.toString(), 177 decimalSeparatorIndex = numberString.indexOf(decimalSeparator); 178 if (decimalSeparatorIndex > -1) 179 { 180 return numberString.length - decimalSeparatorIndex - 1; 181 } 182 else 183 { 184 return 0; 185 } 186 } 187 188 //Returns the given string in a format which shows its character indexes above its characters 189 function printStringIndexes(string) 190 { 191 var result = "", 192 character, space; 193 for (character = 0; character < string.length; character = character + 1) 194 { 195 result += character + " " ; 196 } 197 result += "\n" ; 198 for (character = 0; character < string.length; character = character + 1) 199 { 200 result += string. charAt (character); 98 201 for (space = 0; space < character.toString().length; space = space + 1) 202 { 203 result += " " ; 204 } 205 } 206 return result; 207 } 208 209 //Returns the difference between the given parameters (assumed to be numbers) 210 function sortNumber(x, y) 211 { 212 return x - y; 213 } 214 215 //Returns a given string (first parameter) with all html tags removed except if the second parameter is specified in which case it only removes tag s specified in the second parameter (an array) 216 function removeTags() 217 { 218 var tag, string = XRegExp.escape(arguments[0]); 219 if (arguments.length > 1) 220 { 221 for (tag = 0; tag < arguments[1].length; tag = tag + 1) 222 { 223 string = string. replace (new RegExp("<\\s*\\/?" + arguments[1][tag] + "[^>]*>", "g"), ""); 224 } 225 } 226 else 227 { 228 string = string. replace (/<[^>]*>/g, ""); 229 } 230 return string.replace(/\\/g, ""); 231 } 9.13.2.3 ES Operators.js 1 "use strict"; 2 3 //Creates an Operator object with its name, precedence, associativity and argumentCount set to the given parameters 4 function Operator(name, precedence, associativity, argumentCount) 5{ 6 this.name = name; 7 this.precedence = precedence; 8 this.associativity = associativity; 9 this.argumentCount = argumentCount; 10 } 11 12 //Override the Operator toString method 13 Operator.prototype.toString = function () 14 { 15 return this.name.toString(); 16 }; 17 18 //Operators supported by the system 19 var operators = []; 20 operators[0] = new Operator("(", 0, 0, 0); 21 operators[1] = new Operator(")", 0, 0, 0); 22 operators[2] = new Operator("^", 1, 2, 2); 99 23 operators[3] = new Operator("_", 2, 2, 1); 24 operators[4] = new Operator("*", 3, 1, 2); 25 operators[5] = new Operator("/", 3, 1, 2); 26 operators[6] = new Operator("+", 4, 1, 2); 27 operators[7] = new Operator("-", 4, 1, 2); 28 29 //Returns the Operator Object which has a string property matching the given string 30 function getOperator(string) 31 { 32 for (var operator = 0; operator < operators.length; operator = operator + 1) 33 { 34 if (operators[operator].name === string) 35 { 36 return operators[operator]; 37 } 38 } 39 return null; 40 } 41 42 //Returns the English name for the given operation name or symbol 43 function getOperatorLongName(string) 44 { 45 if (string === "^") 46 { 47 return "Exponentiation"; 48 } 49 else if (string === "_") 50 { 51 return "Unary minus"; 52 } 53 else if (string === "*") 54 { 55 return "Multiplication"; 56 } 57 else if (string === "/") 58 { 59 return "Division"; 60 } 61 else if (string === "+") 62 { 63 return "Addition"; 64 } 65 else if (string === "-") 66 { 67 return "Subtraction"; 68 } 69 else 70 { 71 return null; 72 } 73 } 74 75 //Evaluates the given operation assumed to have the operation name or symbol as the last parameter and the first to the last operands as the first to the last parameters 76 function evaluateOperation(myArguments) 77 { 78 var operator = myArguments[myArguments.length - 1], 79 operandOne = Number(myArguments[0]), 100 80 operandTwo = Number(myArguments[1]); 81 82 if (operator.name === "^") 83 { 84 return Math.pow(operandOne, operandTwo); 85 } 86 else if (operator.name === "_") 87 { 88 return -operandOne; 89 } 90 else if (operator.name === "*") 91 { 92 return operandOne * operandTwo; 93 } 94 else if (operator.name === "/") 95 { 96 return operandOne / operandTwo; 97 } 98 else if (operator.name === "+") 99 { 100 return operandOne + operandTwo; 101 } 102 else if (operator.name === "-") 103 { 104 return operandOne - operandTwo; 105 } 106 else 107 { 108 return null; 109 } 110 } 9.13.2.4 ES Functions.js 1 "use strict"; 2 3 //Function names supported by the system 4 var functions = ["sin", "asin", "cos", "acos", "tan", "atan", "exp", "Exp", "log", "logn", "max", "maxn", "min", "minn", "round", "roundTo", "power", "ti mes", "divide", "plus", "minus", "nroot", "ceil", "floor", "avg", "abs", "random", "randomBetween", "Rationalize", "Expand", "Factor", "Log", "Combin eBases", "Rationalise", "RationaliseMonomial", "RationaliseBinomial", "Hold", "TrigToExp", "Sin", "ArcSin", "Cos", "ArcCos", "Tan", "ArcTan", "Nu meric", "Power", "Times", "Divide", "Plus", "Minus", "Nroot", "pii", "Pii", "Im", "Roots"]; 5 6 //Creates an AFunction object with its name and argumentCount set to the given parameters 7 function AFunction(name, argumentCount) 8{ 9 this.name = name; 10 this.argumentCount = argumentCount; 11 } 12 13 //Override the AFunction toString method 14 AFunction.prototype.toString = function () 15 { 16 return this.name.toString(); 17 }; 18 19 //Returns true if the given String is a function 101 20 function isFunction(string, functionList) 21 { 22 for (func = 0; func < functionList.length; func = func + 1) 23 { 24 if (string === functionList[func]) 25 { 26 return true; 27 } 28 } 29 return false; 30 } 31 32 //Returns the result of calling the function specified as the last argument to the method with the first to second to last arguments to the method 33 function evaluateFunction() 34 { 35 var args = arguments[0].slice(0), 36 theLastArgument; 37 theLastArgument = args.pop(); 38 39 if (theLastArgument.toString() === "max") 40 { 41 return Math.max(args[0], args[1]); 42 } 43 else if (theLastArgument.toString() === "min") 44 { 45 return Math.min(args[0], args[1]); 46 } 47 else 48 { 49 return window[theLastArgument](args); 50 } 51 } 52 53 //Returns the sine of a given number 54 function sin(number) 55 { 56 return Math.sin(number); 57 } 58 59 //Returns the arcsine of a given number 60 functionasin(number) 61 { 62 return Math.asin(number); 63 } 64 65 //Returns the cosine of a given number 66 functioncos(number) 67 { 68 return Math.cos(number); 69 } 70 71 //Returns the arccosine of a given number 72 functionacos(number) 73 { 74 return Math.acos(number); 75 } 76 77 //Returns the tangent of a given number 102 78 functiontan(number) 79 { 80 return Math.tan(number); 81 } 82 83 //Returns the arctangent of a given number 84 functionatan(number) 85 { 86 return Math.atan(number); 87 } 88 89 //Returns e if the given number is null otherwise returns e to the power of that number 90 functionexp() 91 { 92 if (arguments[0].length > 0) 93 { 94 return Math.exp(arguments[0][0]); 95 } 96 else 97 { 98 return Math.E; 99 } 100 } 101 102 //Returns pi if the given number is null otherwise returns pi to the power of that number 103 function pii() 104 { 105 if (arguments[0].length > 0) 106 { 107 return Math.pow(Math.PI, arguments[0][0]); 108 } 109 else 110 { 111 return Math.PI; 112 } 113 } 114 115 //Returns a random number greater than 0 but less than 1 116 function random() 117 { 118 return Math.random(); 119 } 120 121 //Returns a given base logarithm of a given number 122 function logn() 123 { 124 return (Math.log(arguments[0][1])) / (Math.log(arguments[0][0])); 125 } 126 127 /*var oldMax = Math.max; //The original maximum function 128 //Returns the maximum of two given numbers 129 Math.max = function() 130 { 131 return oldMax(arguments[0][0], arguments[0][1]); 132 }; 133 134 var oldMin = Math.min; //The original minimum function 135 //Returns the minimum of two given numbers 136 Math.min = function() 103 137 { 138 return oldMin(arguments[0][0], arguments[0][1]); 139 };*/ 140 141 //Returns the maximum of the given parameters (assumed to be numerical) 142 function maxn() 143 { 144 var result = arguments[0][0], 145 argument; 146 for (argument = 1; argument < arguments[0].length; argument = argument + 1) 147 { 148 result = max(result, arguments[0][argument]); 149 } 150 return result; 151 } 152 153 //Returns the minimum of the given parameters (assumed to be numerical) 154 function minn() 155 { 156 var result = arguments[0][0], 157 argument; 158 for (argument = 1; argument < arguments[0].length; argument = argument + 1) 159 { 160 result = min (result, arguments[0][argument]); 161 } 162 return result; 163 } 164 165 //Returns a given number rounded to a given number of decimal places 166 function roundTo() 167 { 168 return (Math.round(arguments[0][0] * Math.pow(10, arguments[0][1]))) / Math.pow(10, arguments[0][1]); 169 } 170 171 //Returns a given number to a given power 172 function power() 173 { 174 return Math.pow(arguments[0][0], arguments[0][1]); 175 } 176 177 //Returns a given number multiplied by another given number 178 function times() 179 { 180 return arguments[0][0] * arguments[0][1]; 181 } 182 183 //Returns a given number divided by another given number 184 function divide() 185 { 186 return arguments[0][0] / arguments[0][1]; 187 } 188 189 //Returns a given number added to another given number 190 function plus() 191 { 192 return arguments[0][0] + arguments[0][1]; 193 } 194 104 195 //Returns a given number multiplied by another given number 196 function minus() 197 { 198 return arguments[0][0] - arguments[0][1]; 199 } 200 201 //Returns a given root of a given number 202 function nroot() 203 { 204 return Math.pow(arguments[0][1], (1 / arguments[0][0])); 205 } 206 207 //Returns a given number rounded up to the nearest integer 208 functionceil(number) 209 { 210 return Math.ceil(number); 211 } 212 213 //Returns a given number rounded down to the nearest integer 214 functionfloor(number) 215 { 216 return Math.floor(number); 217 } 218 219 //Returns the average of the given parameters (assumed to be numerical) 220 function avg() 221 { 222 var result = 0, 223 parameter; 224 for (parameter = 0; parameter < arguments[0].length; parameter = parameter + 1) 225 { 226 result += arguments [0][parameter]; 227 } 228 result = result / arguments [0].length; 229 return result; 230 } 231 232 //Returns the absolute value of a given number 233 functionabs(number) 234 { 235 return Math.abs(number); 236 } 237 238 //Returns a random number between a given minimum and maximum value with an optionally specified number of floating point digits 239 function randomBetween(minimum, maximum, float) 240 { 241 var randomNumber = arguments[0][0] + (Math.random() * (arguments[0][1] arguments[0][0])); 242 return typeof arguments[0][2] === 'undefined' ? Math.round(randomNumber) : randomNumber.toFixed(arguments[0][2]); 243 } 244 245 //Returns a given number rounded to a given number of decimal places (alternative method) 246 function roundNumber() 247 { 248 var roundedNumberString, numberString, truncatePoint, lastDecimal, lastDecimalPlusOne, roundedNumber, decs, decimal; 249 if (arguments[0][1] < 1) 105 250 { 251 roundedNumberString = (Math.round(arguments[0][0])).toString(); 252 } 253 else 254 { 255 numberString = arguments [0][0].toString(); 256 if (numberString.lastIndexOf(".") === -1) 257 { 258 numberString += "." ; 259 } 260 truncatePoint = numberString. lastIndexOf (".") + arguments[0][1]; 261 lastDecimal = Number(numberString.substring(truncatePoint, truncatePoint + 1)); 262 lastDecimalPlusOne = Number(numberString.substring(truncatePoint + 1, truncatePoint + 2)); 263 if (lastDecimalPlusOne >= 5) 264 { 265 if (lastDecimal === 9 && truncatePoint > 0) 266 { 267 while (truncatePoint > 0 && (lastDecimal === 9 || isNaN(lastDecimal))) 268 { 269 if (lastDecimal !== ".") 270 { 271 truncatePoint -= 1; 272 lastDecimal = Number (numberString.substring(truncatePoint, truncatePoint + 1)); 273 } 274 else 275 { 276 truncatePoint -= 1; 277 } 278 } 279 } 280 lastDecimal += 1; 281 } 282 if (lastDecimal === 10) 283 { 284 numberString = numberString. substring (0, numberString.lastIndexOf(".")); 285 roundedNumber = Number(numberString) + 1; 286 roundedNumberString = roundedNumber.toString() + '.'; 287 } 288 else 289 { 290 roundedNumberString = numberString. substring (0, truncatePoint) + lastDecimal.toString(); 291 } 292 } 293 if (roundedNumberString.lastIndexOf(".") === -1) 294 { 295 roundedNumberString += "." ; 296 } 297 decs = (roundedNumberString.substring(roundedNumberString.lastIndexOf(".") + 1)).length; 298 for (decimal = 0; decimal < arguments[0][1] - decs; decimal = decimal + 1) 299 { 300 roundedNumberString += "0" ; 301 } 302 return Number(roundedNumberString); 303 } 304 305 //Returns a fraction approximately equal to the given number with a denominator less than or equal to the given maximum denominator accordin g to the given maximum error 306 function approximateFraction() 106 307 { 308 var approximate = 0, 309 error = 0, 310 best = 0, 311 bestError = 0, 312 denominator; 313 for (denominator = 1; denominator <= arguments[0][1]; denominator = denominator + 1) 314 { 315 approximate = Math.round (arguments[0][0] / (1 / denominator)); 316 error = (arguments[0][0] - (approximate / denominator)); 317 if (denominator === 1) 318 { 319 best = denominator; 320 bestError = error; 321 } 322 if (Math.abs(error) < Math.abs(bestError)) 323 { 324 best = denominator; 325 bestError = error; 326 } 327 } 328 if (bestError > arguments[0][2]) 329 { 330 return null; 331 } 332 else 333 { 334 return (Math.round(arguments[0][0] / (1 / best)) + "/" + best); 335 } 336 } 9.13.2.5 ES Variables.js 1 "use strict"; 2 3 //Creates an ArgumentSeparator object with its variable set to the given parameter 4 function Variable(variable) 5{ 6 this.variable = variable; 7} 8 9 //Override the Variable toString method 10 Variable.prototype.toString = function () 11 { 12 return this.variable; 13 }; 9.13.2.6 ES Argument Separators.js 1 "use strict"; 2 3 //Creates an ArgumentSeparator object with its value set to the given parameter 4 function ArgumentSeparator(value) 5{ 6 this.value = value; 7} 8 9 //Override the ArgumentSeparator toString method 10 ArgumentSeparator.prototype.toString = function () 107 11 { 12 return this.value.toString(); 13 }; 9.13.2.7 ES Format.js 1 "use strict"; 2 3 //Returns the given String with any detected opening or closing brackets replaced with round brackets i.e. "(" and ")" 4 function replaceBrackets(string) 5{ 6 var re1 = new RegExp("[" + possibleOpeningBrackets + "]", "g"), 7 re2 = new RegExp("[" + possibleClosingBrackets + "]", "g"); 8 return string.replace(re1, "(").replace(re2, ")"); 9} 10 11 //Returns the given String with multiplications inserted between dual characters not part of function names 12 function insertMultiplication(string) 13 { 14 var re = /(?:[a-zA-Z\)][\(a-zA-Z])|(?:[a-zA-Z\)][0-9])|(?:[0-9][\(a-zA-Z])/g, 15 m, nextOpenBracket; 16 re.lastIndex = 0; 17 do 18 { 19 m = re.exec (string); 20 if (m !== null) 21 { 22 nextOpenBracket = string. indexOf ("(", re.lastIndex - m[0].length); 23 if (nextOpenBracket === -1 || !isFunction(string.substring(re.lastIndex - m[0].length, nextOpenBracket), functions)) 24 { 25 string = replaceBetween (string, re.lastIndex - 1, re.lastIndex - 1, "*"); 26 re.lastIndex = re.lastIndex - 1; 27 } 28 else 29 { 30 re.lastIndex = nextOpenBracket + 1; 31 } 32 } 33 } 34 while (m !== null); 35 return string; 36 } 37 38 //Returns the given String with all invalid characters removed 39 function removeInvalidCharacters(string) 40 { 41 var re = new RegExp("[^" + validCharacters + "]", "g"); 42 re.lastIndex = 0; 43 return string.replace(re, ""); 44 } 45 46 //Returns the given String with all unmatched parentheses closed 47 function closeUnmatchedParentheses(string) 48 { 49 var parenthesesCount, re = /[\(\)]/g, 50 m; 108 51 re.lastIndex = 0; 52 do 53 { 54 m = re.exec (string); 55 if (m !== null) 56 { 57 if (m[0] === "(") 58 { 59 parenthesesCount = countMatchesBetween (string, re.lastIndex - 1, string.length, /\(/g) countMatchesBetween(string, re.lastIndex 1, string.length, /\)/g); 60 for (; parenthesesCount > 0; parenthesesCount = parenthesesCount - 1) 61 { 62 string = string + ")" ; 63 } 64 } 65 else 66 { 67 parenthesesCount = countMatchesBetween (string, 0, re.lastIndex, /\(/g) countMatchesBetween(string, 0, re.lastIndex, /\)/g); 68 for (; parenthesesCount < 0; parenthesesCount = parenthesesCount + 1) 69 { 70 string = "(" + string; 71 } 72 } 73 } 74 } 75 while (m !== null); 76 return string; 77 } 78 79 //Returns the given String with all empty parentheses removed 80 function removeEmptyParentheses(string) 81 { 82 var re = /\(\)/g, 83 m, func; 84 re.lastIndex = 0; 85 do 86 { 87 m = re.exec (string); 88 if (m !== null) 89 { 90 for (func = 0; func < functions.length; func = func + 1) 91 { 92 if (isFunction(string.substring(re.lastIndex - (functions[func].length + m[0].length), re.lastIndex m[0].length), functions)) 93 { 94 //string = replaceBetween(string, re.lastIndex - (m[0].length - 1), re.lastIndex - (m[0].length - 1), "0"); 95 break; 96 } 97 else if (func === functions.length - 1) 98 { 99 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex, ""); 100 re.lastIndex = 0; 101 } 102 } 103 } 104 } 109 105 while (m !== null); 106 return string; 107 } 108 109 //Returns the given String with all unnecessary parentheses removed 110 function removeUnnecessaryParentheses(string) 111 { 112 var re1 = /\([~#a-zA-Z0-9.,\^*\/+\-]*?\)/g, 113 re2 = /\(~[~#a-zA-Z0-9.,\^*\/+\-]*?#\)/g, 114 re3 = /(?:^|[^a-zA-Z0-9])\([a-zA-Z0-9.]*?\)/g, 115 m1, m2, m3; //fail, func; 116 117 re1.lastIndex = 0; 118 re2.lastIndex = 0; 119 120 do 121 { 122 m1 = re1.exec (string); 123 if (m1 !== null) 124 { 125 string = replaceBetween (string, re1.lastIndex - m1[0].length, re1.lastIndex, "~" + m1[0].substring(1, m1[0].length - 1) + "#"); 126 m2 = re2.exec(string); 127 if (m2 !== null) 128 { 129 /*for (func = 0; func < functions.length; func = func + 1) 130 { 131 if (isFunction(string.substring(re2.lastIndex - (functions[func].length + m2[0].length), re2.lastIndex - m2[0].length), function s)) 132 { 133 fail = true; 134 break; 135 } 136 else if (func === functions.length - 1) 137 { 138 fail = false; 139 } 140 } 141 if (!fail) 142 {*/ 143 if (string.charAt((re2.lastIndex - m2[0].length) - 1).toString().search(/[a-zA-Z]/) === -1) 144 { 145 string = replaceBetween (string, re2.lastIndex - m2[0].length, re2.lastIndex, "(" + m2[0].substring(2, m2[0].length - 2) + ")"); 146 re2.lastIndex = 0; 147 } 148 //} 149 150 /*if (m2[0].charAt(0) !== '(') 151 { 152 string = replaceBetween(string, re2.lastIndex - (m2[0].length - 1), re2.lastIndex, "(" + m2[0].substring(3, m2[0].length - 2) + " )"); 153 } 154 else 155 { 156 alert(m2[0]); 110 157 string = replaceBetween(string, re2.lastIndex - m2[0].length, re2.lastIndex, "(" + m2[0].substring(2, m2[0].length - 2) + ")"); 158 }*/ 159 } 160 re1.lastIndex = 0; 161 } 162 } 163 while (m1 !== null); 164 string = string.replace(/~/g, "(").replace(/#/g, ")"); 165 do 166 { 167 m3 = re3.exec (string); 168 if (m3 !== null) 169 { 170 if (m3[0].charAt(0) === "(") 171 { 172 string = replaceBetween (string, re3.lastIndex - m3[0].length, re3.lastIndex, m3[0].substring(1, m3[0].length - 1)); 173 } 174 else 175 { 176 string = replaceBetween (string, (re3.lastIndex - m3[0].length) + 1, re3.lastIndex, m3[0].substring(2, m3[0].length - 1)); 177 } 178 re3.lastIndex = 0; 179 } 180 } 181 while (m3 !== null); 182 return string; 183 } 184 185 //Returns the given Strng where two pluses are replaced with a plus, a plus and a minus are replaced with a minus and two minuses are replace d with a plus 186 function replaceCombinedOperators(string) 187 { 188 var re = /\+\+|\+-|-\+|--/g, 189 m, r; 190 re.lastIndex = 0; 191 do 192 { 193 m = re.exec (string); 194 if (m !== null) 195 { 196 if (m[0] === "++" || m[0] === "--") 197 { 198 r = "+" ; 199 } 200 else 201 { 202 r = "-" ; 203 } 204 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex, r); 205 re.lastIndex = 0; 206 } 207 } 208 while (m !== null); 209 return string; 210 } 111 211 212 //Returns the given Strng with any unary operators without valid right hand operands removed 213 function removeUnmatchedUnaryOperators(string) 214 { 215 var re = /-[^\(a-zA-Z0-9]|-$/g, 216 m; 217 re.lastIndex = 0; 218 do 219 { 220 m = re.exec (string); 221 if (m !== null) 222 { 223 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex - m[0].length + 1, ""); 224 re.lastIndex = 0; 225 } 226 } 227 while (m !== null); 228 return string; 229 } 230 231 //Returns the given Strng with any binary operators without valid operands removed 232 function removeUnmatchedBinaryOperators(string) 233 { 234 var re = /^[\^*\/+]|[^\)a-zA-Z0-9][\^*\/+]|[\^*\/+][^\(a-zA-Z0-9\-]|[\^*\/+]$/g, 235 m, r = ""; 236 re.lastIndex = 0; 237 do 238 { 239 m = re.exec (string); 240 if (m !== null) 241 { 242 if (m[0].charAt(0).toString().match(/[\^*\/+]/) !== null) 243 { 244 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex - m[0].length + 1, r); 245 } 246 else 247 { 248 string = replaceBetween (string, re.lastIndex - m[0].length + 1, re.lastIndex - m[0].length + 2, r); 249 } 250 } 251 } 252 while (m !== null); 253 return string; 254 } 255 256 //Returns the given Strng with any unary minus operator replaced with an underscore or with parentheses surrounding its operand multiplied by 1 to differentiate it from binary minus 257 function replaceUnaryMinus(string) 258 { 259 var re = /-(?:\d+(?:\.\d)?\d*|[\(a-zA-Z])/g, 260 m, r; 261 re.lastIndex = 0; 262 do 263 { 264 m = re.exec (string); 265 if (m !== null) 266 { 112 267 if ((re.lastIndex - m[0].length - 1 < 0) || string.charAt(re.lastIndex - m[0].length 1).match(/[^\)a-zA-Z0-9]/)) 268 { 269 /*if (string.charAt(re.lastIndex - m[0].length + 1) === "(") 270 { 271 end = findClosingBracket(string, (re.lastIndex - m[0].length) + 1); 272 r = "(-1*" + string.substring((re.lastIndex - m[0].length) + 1, end + 1) + ")"; 273 string = replaceBetween(string, re.lastIndex - m[0].length, end + 1, r); 274 } 275 else if (string.charAt(re.lastIndex - m[0].length + 1).toString().match(/[a-zA-Z]/) !== null && looksLikeFunction(string, re.lastIn dex - m[0].length + 1)) 276 { 277 end = findClosingBracket(string, string.indexOf("(", re.lastIndex - m[0].length + 1)); 278 r = "(-1*" + string.substring((re.lastIndex - m[0].length) + 1, end + 1) + ")"; 279 string = replaceBetween(string, re.lastIndex - m[0].length, end + 1, r); 280 }*/ 281 //else 282 //{ 283 r = "_" ; 284 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex - m[0].length + 1, r); 285 //} 286 re.lastIndex = 0; 287 } 288 } 289 } 290 while (m !== null); 291 return string; 292 } 293 294 //Escapes function names using double quotes 295 function escapeFunctionNames(string, functionList) 296 { 297 var func, re, m; 298 for (func = 0; func < functionList.length; func = func + 1) 299 { 300 re = new RegExp("(?:^|[^a-zA-Z\\\"])" + functionList[func] + "\\(", "g"); 301 do 302 { 303 m = re.exec (string); 304 if (m !== null) 305 { 306 if (m[0].length === (functionList[func].length + 1)) 307 { 308 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex - 1, "\"" + m[0].substring(0, m[0].length - 1) + "\""); 309 } 310 else 311 { 312 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex - 1, m[0].substring(0, 1) + "\"" + m[0].substring(1, m[0].leng th - 1) + "\""); 313 } 314 } 315 } 316 while (m !== null); 317 } 318 return string; 319 } 113 320 321 //Returns true if the given string contains only English alphabet letters up to and excluding the first occurrence of a left parenthesis 322 function looksLikeFunction(string, startIndex) 323 { 324 var re = /^[a-zA-Z]+$/, 325 leftParenthesisIndex; 326 string = string.substring(startIndex); 327 leftParenthesisIndex = string.indexOf("("); 328 if (leftParenthesisIndex !== -1) 329 { 330 string = string. substring (0, leftParenthesisIndex); 331 if (string.search(re) !== -1) 332 { 333 return true; 334 } 335 } 336 return false; 337 } 338 339 //Returns the given string (assumed to be a Mathematical Markup Language String) in an embeddable format 340 function fixMathMl(string) 341 { 342 string = string. substring (string.indexOf("<math")); 343 string = string.replace("<math mode=\"display\">", "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">"); 344 return string; 345 } 9.13.2.8 ES Error.js 1 "use strict"; 2 3 //Returns an array containing the indexes of all possible opening and closing brackets that could be replaced with round brackets i.e. "(" and ")" 4 function findReplaceableBrackets(string) 5{ 6 var re = new RegExp("[" + possibleOpeningBrackets + possibleClosingBrackets + "]", "g"), 7 replaceableBracketIndexes = [], 8 m; 9 re.lastIndex = 0; 10 do 11 { 12 m = re.exec (string); 13 if (m !== null) 14 { 15 replaceableBracketIndexes. push (re.lastIndex - m[0].length); 16 } 17 } 18 while (m !== null); 19 return replaceableBracketIndexes; 20 } 21 22 //Returns an array containing the indexes of all invalid characters in the given String 23 function findInvalidCharacters(string) 24 { 25 var re = new RegExp("[^" + validCharacters + "]", "g"), 26 invalidCharacterIndexes = [], 114 27 m; 28 re.lastIndex = 0; 29 do 30 { 31 m = re.exec (string); 32 if (m !== null) 33 { 34 invalidCharacterIndexes. push (re.lastIndex - m[0].length); 35 } 36 } 37 while (m !== null); 38 return invalidCharacterIndexes; 39 } 40 41 //Returns an array containing the indexes of all unmatched parentheses in the given String 42 function findUnmatchedParentheses(string) 43 { 44 var re = /\([^\(\)]*\)/g, 45 unmatchedParenthesesIndexes = [], 46 m, r; 47 re.lastIndex = 0; 48 do 49 { 50 m = re.exec (string); 51 if (m !== null) 52 { 53 r = "#" + m[0].substring(1, m[0].length - 1) + "#"; 54 string = replaceBetween(string, re.lastIndex - m[0].length, re.lastIndex, r); 55 re.lastIndex = 0; 56 } 57 } 58 while (m !== null); 59 re = /[\(\)]/g; 60 do 61 { 62 m = re.exec (string); 63 if (m !== null) 64 { 65 unmatchedParenthesesIndexes. push (re.lastIndex - m[0].length); 66 } 67 } 68 while (m !== null); 69 return unmatchedParenthesesIndexes; 70 } 71 72 //Returns an array containing the indexes of all empty parentheses in the given String 73 function findEmptyParentheses(string) 74 { 75 var re = /\(#*\)/g, 76 emptyParenthesesIndexes = [], 77 m; 78 re.lastIndex = 0; 79 do 80 { 81 m = re.exec (string); 82 if (m !== null) 83 { 84 emptyParenthesesIndexes. push (re.lastIndex - m[0].length); 85 emptyParenthesesIndexes.push(re.lastIndex - 1); 115 86 string = replaceBetween(string, re.lastIndex - m[0].length, re.lastIndex, m[0].replace(/./g, "#")); 87 re.lastIndex = 0; 88 } 89 } 90 while (m !== null); 91 return emptyParenthesesIndexes; 92 } 93 94 //Returns an array containing the indexes of all unnecessary parentheses in the given String 95 function findUnnecessaryParentheses(string) 96 { 97 var re1 = /\([@~#a-zA-Z0-9.,\^*\/+\-]*?\)/g, 98 re2 = /\(~[@~#a-zA-Z0-9.,\^*\/+\-]*?#\)/g, 99 re3 = /(?:^|[^a-zA-Z0-9])~[@~#a-zA-Z0-9.]*?#/g, 100 m1, m2, m3, unnecessaryParenthesesIndexes = []; 101 102 re1.lastIndex = 0; 103 re2.lastIndex = 0; 104 105 do 106 { 107 m1 = re1.exec (string); 108 if (m1 !== null) 109 { 110 string = replaceBetween (string, re1.lastIndex - m1[0].length, re1.lastIndex, "~" + m1[0].substring(1, m1[0].length - 1) + "#"); 111 m2 = re2.exec(string); 112 if (m2 !== null) 113 { 114 if (string.charAt((re2.lastIndex - m2[0].length) - 1).toString().search(/[a-zA-Z]/) === -1) 115 { 116 unnecessaryParenthesesIndexes. push (re2.lastIndex - m2[0].length); 117 unnecessaryParenthesesIndexes.push(re2.lastIndex - 1); 118 string = replaceBetween(string, re2.lastIndex - m2[0].length, re2.lastIndex, "(@" + m2[0].substring(2, m2[0].length - 2) + "@)"); 119 re2.lastIndex = 0; 120 } 121 } 122 re1.lastIndex = 0; 123 } 124 } 125 while (m1 !== null); 126 do 127 { 128 m3 = re3.exec (string); 129 if (m3 !== null) 130 { 131 if (m3[0].charAt(0) === "~") 132 { 133 unnecessaryParenthesesIndexes. push (re3.lastIndex - m3[0].length); 134 unnecessaryParenthesesIndexes.push(re3.lastIndex - 1); 135 string = replaceBetween(string, re3.lastIndex - m3[0].length, re3.lastIndex, "@" + m3[0].substring(1, m3[0].length - 1) + "@"); 136 } 137 else 138 { 139 unnecessaryParenthesesIndexes. push ((re3.lastIndex - m3[0].length) + 1); 140 unnecessaryParenthesesIndexes.push(re3.lastIndex - 1); 116 141 string = replaceBetween(string, (re3.lastIndex - m3[0].length) + 1, re3.lastIndex, "@" + m3[0].substring(2, m3[0].length - 1) + "@"); 142 } 143 re3.lastIndex = 0; 144 } 145 } 146 while (m3 !== null); 147 return unnecessaryParenthesesIndexes; 148 } 149 150 //Returns an array containing the indexes of all combined operators in the given String 151 function findCombinedOperators(string) 152 { 153 var re = /[+\-]{2,}/g, 154 combinedOperatorsIndexes = [], 155 char, m; 156 re.lastIndex = 0; 157 do 158 { 159 m = re.exec (string); 160 if (m !== null) 161 { 162 for (char = 0; char < m[0].length; char = char + 1) 163 { 164 combinedOperatorsIndexes. push (re.lastIndex - m[0].length + char); 165 } 166 string = replaceBetween (string, re.lastIndex - m[0].length, re.lastIndex, m[0].replace(/./g, "#")); 167 re.lastIndex = 0; 168 } 169 } 170 while (m !== null); 171 return combinedOperatorsIndexes; 172 } 173 174 //Returns an array containing the indexes of all unmatched unary operators in the given String 175 function findUnmatchedUnaryOperators(string) 176 { 177 var re = /-[^\(a-zA-Z0-9]|-$/g, 178 unmatchedUnaryOperatorsIndexes = [], 179 m; 180 re.lastIndex = 0; 181 do 182 { 183 m = re.exec (string); 184 if (m !== null) 185 { 186 unmatchedUnaryOperatorsIndexes. push (re.lastIndex - m[0].length); 187 } 188 } 189 while (m !== null); 190 return unmatchedUnaryOperatorsIndexes; 191 } 192 193 //Returns an array containing the indexes of all unmatched binary operators in the given String 194 function findUnmatchedBinaryOperators(string) 195 { 196 var re = /^[\^*\/+]|[^\)a-zA-Z0-9][\^*\/+]|[\^*\/+][^\(a-zA-Z0-9\-]|[\^*\/+]$/g, 197 unmatchedBinaryOperatorsIndexes = [], 117 198 m; 199 re.lastIndex = 0; 200 do 201 { 202 m = re.exec (string); 203 if (m !== null) 204 { 205 if (m[0].charAt(0).toString().match(/[\^*\/+]/) !== null) 206 { 207 unmatchedBinaryOperatorsIndexes. push (re.lastIndex - m[0].length); 208 } 209 else 210 { 211 unmatchedBinaryOperatorsIndexes. push (re.lastIndex - m[0].length + 1); 212 } 213 } 214 } 215 while (m !== null); 216 return unmatchedBinaryOperatorsIndexes; 217 } 218 219 //Returns an array containing the indexes of all multiplications inserted into the given String 220 function findInsertedMultiplications(string) 221 { 222 var re = /(?:[a-zA-Z\)][\(a-zA-Z])|(?:[a-zA-Z\)][0-9])|(?:[0-9][\(a-zA-Z])/g, 223 m, nextOpenBracket, insertedMultiplicationIndexes = []; 224 re.lastIndex = 0; 225 do 226 { 227 m = re.exec (string); 228 if (m !== null) 229 { 230 nextOpenBracket = string. indexOf ("(", re.lastIndex - m[0].length); 231 if (nextOpenBracket === -1 || !isFunction(string.substring(re.lastIndex - m[0].length, nextOpenBracket), functions)) 232 { 233 string = replaceBetween (string, re.lastIndex - 1, re.lastIndex - 1, "*"); 234 re.lastIndex = re.lastIndex - 1; 235 insertedMultiplicationIndexes.push(re.lastIndex); 236 } 237 else 238 { 239 re.lastIndex = nextOpenBracket + 1; 240 } 241 } 242 } 243 while (m !== null); 244 return insertedMultiplicationIndexes; 245 } 246 247 //Returns html with all the characters in the given string at indicies in the given errorIndexArray highlighted using span objects with the given colo r 248 function showInputErrors(string, errorIndexArray, color) 249 { 250 var html = "", 251 char, inputErrorIndex; 252 if (errorIndexArray.length > 0) 253 { 118 254 errorIndexArray. sort (sortNumber); 255 inputErrorIndex = errorIndexArray.shift(); 256 for (char = 0; char < string.length; char = char + 1) 257 { 258 if (inputErrorIndex === char) 259 { 260 html += '<span style="color:' + color + '">' + customEscape (string.charAt(char)) + '</span>'; 261 inputErrorIndex = errorIndexArray.shift(); 262 } 263 else 264 { 265 html += customEscape (string.charAt(char)); 266 } 267 } 268 } 269 return html; 270 } 9.13.2.9 ES Tokenize.js 1 "use strict"; 2 3 //Returns the given string with all function names in the given function list surrounded by the given mark using a different given temporary mark f or internal processing 4 function markFunctions(string, functionList, mark, temporaryMark) 5{ 6 var func, startIndex, endIndex; 7 if (mark === temporaryMark) 8{ 9 return null; 10 } 11 for (func = 0; func < functionList.length; func = func + 1) 12 { 13 string = surroundString (string, new RegExp(functionList[func] + "(?=\\()", "g"), temporaryMark, new RegExp("(?:^|[^a-zA-Z])" + functionLi st[func] + "$")); 14 } 15 startIndex = string. indexOf (temporaryMark); 16 while (startIndex !== -1) 17 { 18 startIndex = string. indexOf (temporaryMark, startIndex + 1); 19 endIndex = findClosingBracket(string, startIndex + 1) + 1; 20 string = replaceBetween(string, endIndex, endIndex, mark); 21 startIndex = string.indexOf(temporaryMark); 22 string = replaceBetween(string, startIndex, startIndex + 1, mark); 23 startIndex = string.indexOf(temporaryMark); 24 string = replaceBetween(string, startIndex, startIndex + 1, ""); 25 startIndex = string.indexOf(temporaryMark); 26 } 27 return string; 28 } 29 30 //Returns the name of a function without a mark preceding it 31 function getFunctionName(string) 32 { 33 return string.substring(1, string.length); 34 } 35 119 36 //Returns the number of arguments of a given function 37 function getFunctionArgumentCount(string) 38 { 39 string = string. substring (string.indexOf("(") + 1, string.lastIndexOf(")")); 40 string = string.replace(new RegExp("([" + validCharacters + "]*?)", "g"), ""); 41 return countMatches(string, /,/g) + 1; 42 } 43 44 //Returns an infix token array for a given infix expression string 45 function tokenize(string) 46 { 47 string = markFunctions (string, functions, "$", "#"); 48 var tokens = [], 49 //reNumber = /^_?(?:\d+(?:\.\d)?\d*)/g, 50 reNumber = /^(?:\d+(?:\.\d)?\d*)/g, 51 reOperator = /^[\(\)\^_*\/+\-]/g, 52 reFunction = /^\$[^\(]*/g, 53 //reVariable = /^_?[a-zA-Z]/g, 54 reVariable = /^[a-zA-Z]/g, 55 reArgumentSeparator = /[,]/g, 56 reCurrent, reCount = 0, 57 m, token, endIndex; 58 59 while (string.length > 0) 60 { 61 switch (reCount) 62 { 63 case 0: 64 reCurrent = reNumber; 65 break; 66 case 1: 67 reCurrent = reOperator; 68 break; 69 case 2: 70 reCurrent = reFunction; 71 break; 72 case 3: 73 reCurrent = reVariable; 74 break; 75 case 4: 76 reCurrent = reArgumentSeparator; 77 break; 78 } 79 m = reCurrent.exec (string); 80 if (m !== null) 81 { 82 switch (reCount) 83 { 84 case 0: 85 token = Number(m[0].replace(/_/g, "-")); 86 break; 87 case 1: 88 token = getOperator(m[0]); 89 break; 90 case 2: 91 endIndex = findClosingBracket(string, reCurrent.lastIndex); 92 token = new AFunction(getFunctionName(m[0]), getFunctionArgumentCount(string.substring(0, endIndex))); 93 string = replaceBetween(string, 0, 1, ""); 120 94 string = replaceBetween(string, endIndex, endIndex + 1, ""); 95 reCurrent.lastIndex = token.name.length; 96 break; 97 case 3: 98 token = new Variable(m[0].replace(/_/g, "-")); 99 break; 100 case 4: 101 token = new ArgumentSeparator(m[0]); 102 break; 103 } 104 tokens. push (token); 105 string = string.substring(reCurrent.lastIndex); 106 reCurrent.lastIndex = 0; 107 reCount = 0; 108 } 109 else 110 { 111 reCount = reCount + 1; 112 } 113 } 114 return tokens; 115 } 9.13.2.10 ES Shunting Yard.js 1 "use strict"; 2 3 //Returns a postfix token array for a given infix token array 4 function shunting_yard(string) 5{ 6 var tokens = tokenize(string), 7 token, stack = [], 8 wereValues = [], 9 argCount = [], 10 output = [], 11 tokenType, top, w, a, f; 12 13 for (token = 0; token < tokens.length; token = token + 1) //While there are tokens to be read 14 { 15 tokenType = getType (tokens[token]); 16 if (tokenType === "number" || tokenType === "Variable") //If the token is a number or variable 17 { 18 output. push (tokens[token]); //Add it to the output queue 19 if (wereValues.length > 0) //If the were values stack has a value on it 20 { 21 wereValues. pop (); //Pop it 22 wereValues.push(true); //Push true 23 } 24 } 25 else if (tokenType === "AFunction") //If the token is a function 26 { 27 stack. push (tokens[token]); //Push it onto the stack 28 argCount.push(0); //Push 0 onto the argCount stack 29 if (wereValues.length > 0) //If the were values stack has a value on it 30 { 31 wereValues. pop (); //Pop it 32 wereValues.push(true); //Push true 33 } 34 wereValues. push (false); //Push false onto were values 121 35 } 36 else if (tokenType === "ArgumentSeparator") //If the token is a function argument separator (e.g. , a comma) 37 { 38 //Until the topmost element of the stack is a left parenthesis 39 while (stack.length > 0 && getType(stack[stack.length - 1]) !== "Operator" || stack[stack.length - 1].name !== "(") 40 { 41 top = stack. pop (); //Pop the element 42 output.push(top); //Onto the output queue 43 } 44 //If no left parentheses are encountered, either the separator was misplaced or parentheses were mismatched 45 w = wereValues. pop (); //Pop were values into w 46 if (w) //If w is true 47 { 48 a = argCount. pop (); //Pop arg count into a 49 a = a + 1; //Increment a 50 argCount.push(a); //Push [a] back into arg count 51 } 52 wereValues. push (false); //Push false into were values 53 } 54 else if (tokenType === "Operator" && tokens[token].argumentCount === 1) //If the token is a unary operator 55 { 56 if (tokens[token].associativity === 1) //If the token is left associative 57 { 58 output. push (tokens[token]); //Push it onto the output queue 59 } 60 else //Otherwise the token must be right associative 61 { 62 stack. push (tokens[token]); //Push it onto the stack queue 63 } 64 } 65 else if (tokenType === "Operator" && tokens[token].name !== "(" && tokens[token].name !== ")") //If the token is an operator o1 66 { 67 //While there is an operator, o2, at the top of the stack and 68 while (stack.length > 0 && getType(stack[stack.length - 1]) === "Operator" && stack[stack.length - 1].name !== "(" && 69 //Either o1 is left-associative and its precedence is less than or equal to that of o2 70 ((tokens[token].associativity === 1 && tokens[token].precedence >= stack[stack.length 1].precedence) || 71 //Or o1 is right-associative and its precedence is less than that of o2 72 (tokens[token].associativity === 2 && tokens[token].precedence > stack[stack.length 1].precedence))) 73 { 74 top = stack. pop (); //Pop o2 off the stack 75 output.push(top); //Onto the output queue 76 } 77 stack. push (tokens[token]); //Push o1 onto the operator stack 78 } 79 else if (tokenType === "Operator" && tokens[token].name === "(") //If the token is a left parenthesis 80 { 81 stack. push (tokens[token]); //Push it onto the stack 82 } 83 else if (tokenType === "Operator" && tokens[token].name === ")") //If the token is a right parenthesis 122 84 { 85 //Until the topmost element of the stack is a left parenthesis 86 while (stack.length > 0 && (getType(stack[stack.length - 1]) !== "Operator" || stack[stack.length - 1].name !== "(")) 87 { 88 top = stack. pop (); //Pop the element 89 output.push(top); //Onto the output queue 90 } 91 stack. pop (); //Pop the left parenthesis from the stack, but not onto the output queue 92 if (stack.length > 0 && getType(stack[stack.length - 1]) === "AFunction") //If the token at the top of the stack is a function token 93 { 94 f = stack. pop (); //Pop stack into f 95 a = argCount.pop(); //Pop arg count into a 96 w = wereValues.pop(); //Pop were values into w 97 if (w) //If w is true 98 { 99 a = a + 1; //Increment a 100 } 101 f.argumentCount = a; //Set the argument count of f to a 102 output. push (f); //Push f onto output queue 103 //If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. 104 } 105 } 106 } 107 for (; 0 < stack.length;) //While there are still operator tokens in the stack 108 { 109 //If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses 110 top = stack. pop (); 111 output.push(top); 112 } 113 return output; 114 } 9.13.2.11 ES Process.js 1 "use strict"; 2 3 var noEval = false; //True if no evaluation should be performed for the following subexpressions 4 var subCount; //Keeps track of how many subexpressions have been processed for building the unevaluated infix expression string 5 6 //Returns an xmlhttp object if one can be retrieved otherwise an ActiveX replacement object is returned 7 function getXMLHttpRequest() 8{ 9 if (window.XMLHttpRequest) 10 { 11 return new XMLHttpRequest(); 12 } 13 else 14 { 15 return new ActiveXObject("Microsoft.XMLHTTP"); 16 } 17 } 18 123 19 //Returns the response of an AJAX request sent to the server invoking an expression simplifier application instance with the given par ameters 20 function executeJava() 21 { 22 var xmlhttp = getXMLHttpRequest(), 23 sendString = "arg0" + "=" + encodeURIComponent("\"" + arguments[0] + "\"") + "&arg1" + "=" + encodeURIComponent("\"" + arguments[1] + "\"") + "&arg2" + "=" + encodeURIComponent("\"" + arguments[2] + "\""); 24 xmlhttp.open("POST", "classes/servlet/ES_symja.php", false); 25 xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 26 xmlhttp.send(sendString); 27 return xmlhttp.responseText; 28 } 29 30 //Returns an infix subexpression string for a given postfix subexpression 31 function subexpressionString() 32 { 33 var result = null, 34 operator = arguments[0][arguments[0].length - 1], 35 operandOne = arguments[0][0], 36 operandTwo = arguments[0][1], 37 operatorType = getType(operator); 38 39 if (operatorType === "AFunction") 40 { 41 result = operator.name + "(" ; 42 for (argument = 0; argument < arguments[0].length - 1; argument = argument + 1) 43 { 44 if (argument === arguments[0].length - 2) 45 { 46 result += arguments [0][argument]; 47 } 48 else 49 { 50 result += arguments [0][argument] + ","; 51 } 52 } 53 result += ")" ; 54 } 55 else 56 { 57 if (operator.name === "_") 58 { 59 result = operator.name + operandOne; 60 } 61 else if (operandOne.toString().search(/[\^*\/+\-]/) !== -1) 62 { 63 if (operandTwo.toString().search(/[\^*\/+\-]/) !== -1) 64 { 65 result = "(" + operandOne + ")" + operator.name + "(" + operandTwo + ")" ; 66 } 67 else 68 { 69 result = "(" + operandOne + ")" + operator.name + operandTwo; 70 } 71 } 72 else 73 { 124 74 if (operandTwo.toString().search(/[\^*\/+\-]/) !== -1) 75 { 76 result = operandOne + operator.name + "(" + operandTwo + ")" ; 77 } 78 else 79 { 80 result = operandOne + operator.name + operandTwo; 81 } 82 } 83 } 84 subCount = subCount + 1; 85 return '<span id="sub' + subCount + '">' + result + '</span>'; 86 } 87 88 //Returns an unevaluated/unsimplified infix string representation of the entire postfix token array 89 function postfixToInfixExpression(postfixTokens) 90 { 91 subCount = 0; 92 var operandQueue = [], 93 tokenType, argumentArray, argument, token; 94 for (token = 0; token < postfixTokens.length; token = token + 1) 95 { 96 tokenType = getType (postfixTokens[token]); 97 if (tokenType === "Operator" || tokenType === "AFunction") 98 { 99 argumentArray = []; 100 for (argument = 0; argument < postfixTokens[token].argumentCount; argument = argument + 1) 101 { 102 argumentArray. unshift (operandQueue.pop()); 103 } 104 argumentArray. push (postfixTokens[token]); 105 operandQueue.push(subexpressionString(argumentArray)); 106 } 107 else 108 { 109 operandQueue. push (postfixTokens[token]); 110 } 111 } 112 return operandQueue.pop().toString(); 113 } 114 115 //Returns the result of a server or client side simplification with a fixed time of 2500 milliseconds client side or 10000 milliseconds server side 116 function simplifySubExpression(string) 117 { 118 string = string. toString ().replace(/_/g, "-"); 119 try 120 { 121 if (ES_symjaAppletLoaded || document.getElementById('ES_symja_Applet').isReady() === "true") 122 { 123 string = document .getElementById ('ES_symja_Applet').simplify(string, "2500"); 124 } 125 else 126 { 127 string = executeJava ("simplify", string, "10000"); 128 } 129 } 125 130 catch (exception) 131 { 132 string = executeJava ("simplify", string, "10000"); 133 } 134 return string; 135 } 136 137 //Returns the result of attempting to perform a function or operation with or without evaluation active 138 function argumentTest() 139 { 140 var result = null, 141 simplification, resultString, operator = arguments[0][arguments[0].length - 1], 142 operandOne = arguments[0][0], 143 operandTwo = arguments[0][1], 144 operatorType = getType(operator); 145 146 if (operatorType === "AFunction") 147 { 148 simplification = operator.name; 149 result = "\"" + operator.name + "\"(" ; 150 for (argument = 0; argument < arguments[0].length - 1; argument = argument + 1) 151 { 152 if (getType(arguments[0][argument]) === "string") 153 { 154 arguments [0][argument] = arguments[0][argument].replace(/"/g, ""); 155 } 156 if (argument === arguments[0].length - 2) 157 { 158 result += arguments [0][argument]; 159 } 160 else 161 { 162 result += arguments [0][argument] + ","; 163 } 164 } 165 result += ")" ; 166 resultString = evaluateFunction (arguments[0]); 167 arguments[0].pop(); 168 } 169 else 170 { 171 simplification = getOperatorLongName (operator.name); 172 if (operator.name === "_") 173 { 174 result = operator.name + operandOne; 175 } 176 else if (operandOne.toString().search(/[\^*\/+\-]/) !== -1) 177 { 178 if (operandTwo.toString().search(/[\^*\/+\-]/) !== -1) 179 { 180 result = "(" + operandOne + ")" + operator.name + "(" + operandTwo + ")" ; 181 } 182 else 183 { 184 result = "(" + operandOne + ")" + operator.name + operandTwo; 185 } 186 } 187 else 126 188 { 189 if (operandTwo.toString().search(/[\^*\/+\-]/) !== -1) 190 { 191 result = operandOne + operator.name + "(" + operandTwo + ")" ; 192 } 193 else 194 { 195 result = operandOne + operator.name + operandTwo; 196 } 197 } 198 if (getType(result) === "string") 199 { 200 if (!noEval) 201 { 202 resultString = simplifySubExpression (result); 203 } 204 else 205 { 206 resultString = result; 207 } 208 } 209 } 210 211 addSubExpression (simplification, result, resultString); 212 result = resultString; 213 214 return result; 215 } 216 217 //Returns the position of the last operand of an operator or function at a given position 218 function getLastOperandPosition(position) 219 { 220 var tokenType = getType(postfixTokens[position]), 221 argumentCount; 222 if (tokenType === "Operator" || tokenType === "AFunction") 223 { 224 argumentCount = postfixTokens [position].argumentCount; 225 while (argumentCount !== 0) 226 { 227 position = position - 1; 228 tokenType = getType (postfixTokens[position]); 229 if (tokenType === "Operator" || tokenType === "AFunction") 230 { 231 argumentCount = (argumentCount - 1) + postfixTokens[position].argumentCount; 232 } 233 else 234 { 235 argumentCount = argumentCount - 1; 236 } 237 } 238 } 239 return position; 240 } 241 242 //Marks the arguments of all Hold functions to indicate they should not be simplified 243 function markAllHold(postfixTokens) 244 { 245 var lastOperandPosition; 246 for (token = 0; token < postfixTokens.length; token = token + 1) 127 247 { 248 tokenType = getType (postfixTokens[token]); 249 if (tokenType === "AFunction" && postfixTokens[token].name === "Hold") 250 { 251 lastOperandPosition = getLastOperandPosition (token); 252 postfixTokens.splice(token + 1, 0, "\""); 253 token = token + 1; 254 postfixTokens.splice(lastOperandPosition, 0, "\""); 255 token = token + 1; 256 } 257 } 258 } 259 260 //Returns the final simplification result outputting simplification steps throughout the process by splitting a given postfix token array i nto infix subexpressions 261 function evaluatePostfixExpression(postfixTokens) 262 { 263 document .getElementById ("currentExpression").innerHTML = postfixToInfixExpression(postfixTokens); 264 265 markAllHold(postfixTokens); 266 var operandQueue = [], 267 tokenType, argumentArray, result, argument, token; 268 for (token = 0; token < postfixTokens.length; token = token + 1) 269 { 270 tokenType = getType (postfixTokens[token]); 271 if (tokenType === "Operator" || tokenType === "AFunction") 272 { 273 argumentArray = []; 274 for (argument = 0; argument < postfixTokens[token].argumentCount; argument = argument + 1) 275 { 276 argumentArray. unshift (operandQueue.pop()); 277 } 278 argumentArray. push (postfixTokens[token]); 279 result = argumentTest(argumentArray); 280 if (result.toString().indexOf("NaN") === -1) 281 { 282 operandQueue. push (result); 283 } 284 else 285 { 286 break; 287 } 288 } 289 else if (tokenType === "string") 290 { 291 noEval = !noEval; 292 } 293 else 294 { 295 operandQueue. push (postfixTokens[token]); 296 } 297 } 298 return operandQueue.pop().toString(); 299 } 300 301 //Returns a given string surrounded by quotes preventing it from being simplified 128 302 function Hold(string) 303 { 304 return "\"" + string + "\""; 305 } 306 307 //Expands and returns a given expression string 308 function Expand(string) 309 { 310 if (string.toString().indexOf("(") !== -1) 311 { 312 var expand = simplifySubExpression("Expand[" + string + "]"); 313 if (expand.indexOf("Expand") === -1 && countDecimals(Number(expand), ".") < 5) 314 { 315 string = expand; 316 } 317 } 318 return string; 319 } 320 321 //Factorises and returns a given expression string 322 function Factor(string) 323 { 324 var factor = simplifySubExpression("Factor[" + string + "]"); 325 if (factor.indexOf("Factor") === -1 && countDecimals(Number(factor), ".") < 5) 326 { 327 string = factor; 328 } 329 return string; 330 } 331 332 //Rationalizes and returns a given expression string 333 function Rationalize(string) 334 { 335 var rationalized = simplifySubExpression("Rationalize[" + string + "]"); 336 if (Number(simplifySubExpression("(" + rationalized + ")-(" + string + ")")) === 0) 337 { 338 string = rationalized; 339 } 340 return string; 341 } 342 343 //Combines multiplied bases to the same exponent in the given expression string and returns it 344 function CombineBases(string) 345 { 346 var combinedBases = simplifySubExpression("CombineBases[" + string + "]"); 347 if (combinedBases.indexOf("CombineBases") === -1) 348 { 349 string = combinedBases; 350 } 351 return string; 352 } 353 354 //Rationalises and returns a given expression string 355 function Rationalise(string) 356 { 357 var rationalised = simplifySubExpression("Rationalise[" + string + "]"); 358 if (Number(simplifySubExpression("(" + rationalised + ")-(" + string + ")")) === 0) 359 { 129 360 string = rationalised; 361 } 362 return string; 363 } 364 365 //Rationalises a given monomial surd as a fraction denominator and returns the result 366 function RationaliseMonomial(string) 367 { 368 var rationalMonomial = simplifySubExpression("RationaliseMonomial[" + string + "]"); 369 if (rationalMonomial.indexOf("RationaliseMonomial") === -1) 370 { 371 string = rationalMonomial; 372 } 373 return string; 374 } 375 376 //Rationalises a given binomial with a surd as a fraction denominator and returns the result 377 function RationaliseBinomial(string) 378 { 379 var rationalBinomial = simplifySubExpression("RationaliseBinomial[" + string + "]"); 380 if (rationalBinomial.indexOf("RationaliseBinomial") === -1) 381 { 382 string = rationalBinomial; 383 } 384 return string; 385 } 386 387 //Returns the result of performing a logarithm of the second parameter to the base of the first parameter 388 function Log(string) 389 { 390 var logarithm = simplifySubExpression("Log[" + string + "]"); 391 if (logarithm.indexOf("Log") === -1) 392 { 393 string = logarithm; 394 } 395 else 396 { 397 string = "NaN" ; 398 } 399 return string; 400 } 401 402 //Returns the sine of an expression 403 function Sin(string) 404 { 405 return simplifySubExpression("Sin[" + string + "]"); 406 } 407 408 //Returns the arcsine of an expression 409 function ArcSin(string) 410 { 411 return simplifySubExpression("ArcSin[" + string + "]"); 412 } 413 414 //Returns the cosine of an expression 415 function Cos(string) 416 { 417 return simplifySubExpression("Cos[" + string + "]"); 130 418 } 419 420 //Returns the arccosine of an expression 421 function ArcCos(string) 422 { 423 return simplifySubExpression("ArcCos[" + string + "]"); 424 } 425 426 //Returns the tangent of an expression 427 function Tan(string) 428 { 429 return simplifySubExpression("Tan[" + string + "]"); 430 } 431 432 //Returns the arctangent of an expression 433 function ArcTan(string) 434 { 435 return simplifySubExpression("ArcTan[" + string + "]"); 436 } 437 438 //Returns the numerical evaluation of an expression 439 function Numeric(string) 440 { 441 return simplifySubExpression("N[" + string + "]"); 442 } 443 444 //Returns a given expression to the power of another given expression 445 function Power() 446 { 447 return simplifySubExpression(arguments[0][0] + "^" + arguments[0][1]); 448 } 449 450 //Returns a given expression multiplied by another given expression 451 function Times() 452 { 453 return simplifySubExpression(arguments[0][0] + "*" + arguments[0][1]); 454 } 455 456 //Returns a given expression divided by another given expression 457 function Divide() 458 { 459 return simplifySubExpression(arguments[0][0] + "/" + arguments[0][1]); 460 } 461 462 //Returns a given expression added to another given expression 463 function Plus() 464 { 465 return simplifySubExpression(arguments[0][0] + "+" + arguments[0][1]); 466 } 467 468 //Returns a given expression multiplied by another given expression 469 function Minus() 470 { 471 return simplifySubExpression(arguments[0][0] + "-" + arguments[0][1]); 472 } 473 474 //Returns a given root of a given expression 475 function Nroot() 476 { 131 477 return simplifySubExpression(arguments[0][1] + "^(1/" + arguments[0][0] + ")"); 478 } 479 480 //Returns the result of converting trigonometric functions into their exponential representations 481 function TrigToExp(string) 482 { 483 var conversion = simplifySubExpression("TrigToExp[" + string + "]"); 484 if (conversion.indexOf("TrigToExp") === -1) 485 { 486 string = conversion; 487 } 488 return string; 489 } 490 491 //Returns pi if the given expression string is null otherwise it returns pi to the power of the given expression string 492 function Pii(string) 493 { 494 if (arguments[0].length > 0) 495 { 496 return simplifySubExpression("Pi^" + string); 497 } 498 else 499 { 500 return "Pi"; 501 } 502 } 503 504 //Returns e if the given expression string is null otherwise it returns e to the power of the given expression string 505 function Exp(string) 506 { 507 if (arguments[0].length > 0) 508 { 509 return simplifySubExpression("E^" + string); 510 } 511 else 512 { 513 return "E"; 514 } 515 } 516 517 //Returns i (imaginary number) if the given expression string is null otherwise it returns i to the power of the given expression string 518 function Im(string) 519 { 520 if (arguments[0].length > 0) 521 { 522 return simplifySubExpression("I^" + string); 523 } 524 else 525 { 526 return "I"; 527 } 528 } 529 530 //Returns the roots of a univariate polynomial 531 function Roots(string) 532 { 132 533 var roots = simplifySubExpression("Roots[" + string + "]"); 534 if (roots.indexOf("Roots") === -1) 535 { 536 string = roots; 537 } 538 return string; 539 } 9.13.2.12 ES Main.js 1 "use strict"; 2 3 var postfixTokens, //Holds a postfix token array representation of a given input string 4 subExpressionCount, //Keeps track of the number of subexpressions simplified so far 5 displayCount, //Keeps track of the number of subexpressions displayed so far 6 ES_symjaAppletLoaded = false, //True if the expression simplifier applet has finished loading 7 ExpressionTreeAppletLoaded = false, //True if the expression tree applet has finished loading 8 menuOpen = false, //Indicates if the autocomplete menu is open or not 9 normalColor = "blue", //Normal output color 10 highlightColor = "green", //Highlighted output color 11 errorColor = "red"; //Error output color 12 13 //Escapes all characters in the given string parameter that can be escaped except for those specified in the function 14 function customEscape(string) 15 { 16 string = escape (string); 17 return string.replace(/%28/g, "(").replace(/%29/g, ")").replace(/%2C/g, ",").replace(/%5E/g, "^"); 18 } 19 20 //Returns a given infix expression string that has had 0 or more formatting methods applied to it as necessary and displays its Mathematical Mark up Language notation 21 function format(string) 22 { 23 document .getElementById ('format').innerHTML = ""; 24 25 document.getElementById('format').innerHTML += "Input expression:<br />" + customEscape(string) + "<br /><br />"; 26 27 var newString = replaceBrackets(string); 28 if (string !== newString) 29 { 30 document .getElementById ('format').innerHTML += "Replacing brackets:<br />" + showInputErrors(string, findReplaceableBrackets(string) , errorColor) + " → " + newString + "<br /><br />"; 31 string = newString; 32 } 33 34 newString = removeInvalidCharacters (string); 35 if (string !== newString) 36 { 37 document .getElementById ('format').innerHTML += "Removing invaid characters:<br />" + showInputErrors(string, findInvalidCharacters(st ring), errorColor) + " → " + newString + "<br /><br />"; 38 string = newString; 39 } 40 41 newString = closeUnmatchedParentheses (string); 133 42 if (string !== newString) 43 { 44 document .getElementById ('format').innerHTML += "Matching parentheses:<br />" + showInputErrors(string, findUnmatchedParentheses( string), errorColor) + " → " + newString + "<br /><br />"; 45 string = newString; 46 } 47 48 newString = removeEmptyParentheses (string); 49 if (string !== newString) 50 { 51 document .getElementById ('format').innerHTML += "Removing empty parentheses:<br />" + showInputErrors(string, findEmptyParenthese s(string), errorColor) + " → " + newString + "<br /><br />"; 52 string = newString; 53 } 54 55 newString = replaceCombinedOperators (string); 56 if (string !== newString) 57 { 58 document .getElementById ('format').innerHTML += "Replacing combined operators:<br />" + showInputErrors(string, findCombinedOperat ors(string), errorColor) + " → " + newString + "<br /><br />"; 59 string = newString; 60 } 61 62 newString = removeUnmatchedUnaryOperators (string); 63 if (string !== newString) 64 { 65 document .getElementById ('format').innerHTML += "Removing unmatched unary operators:<br />" + showInputErrors(string, findUnmatch edUnaryOperators(string), errorColor) + " → " + newString + "<br /><br />"; 66 string = newString; 67 } 68 69 newString = removeUnmatchedBinaryOperators (string); 70 if (string !== newString) 71 { 72 document .getElementById ('format').innerHTML += "Removing unmatched binary operators:<br />" + showInputErrors(string, findUnmatch edBinaryOperators(string), errorColor) + " → " + newString + "<br /><br />"; 73 string = newString; 74 } 75 76 newString = removeUnnecessaryParentheses (string); 77 if (string !== newString) 78 { 79 document .getElementById ('format').innerHTML += "Removing unnecessary parentheses:<br />" + showInputErrors(string, findUnnecessa ryParentheses(string), errorColor) + " → " + newString + "<br /><br />"; 80 string = newString; 81 } 82 83 newString = insertMultiplication (string); 84 if (string !== newString) 85 { 86 document .getElementById ('format').innerHTML += "Inserting multiplication:<br />" + showInputErrors(newString, findInsertedMultiplication s(string), errorColor) + " → " + newString + "<br /><br />"; 134 87 string = newString; 88 } 89 90 /*newString = replaceUnaryMinus(string); 91 if (string !== newString) 92 { 93 document.getElementById('format').innerHTML += "Replacing unary minus:<br />" + string + " → " + newString + "<br /><br />"; 94 string = newString; 95 }*/ 96 string = replaceUnaryMinus (string); 97 98 //formattedString = parseMath(escapeFunctionNames(string.replace(/_/g, "-"), functions), false); 99 document.getElementById('format').innerHTML += "Formatted expression:<br />"; 100 //if (formattedString !== null) 101 //{ 102 document.getElementById('format').innerHTML += string.replace(/_/g, "-"); //+ " → "; 103 //document.getElementById('format').appendChild(formattedString); 104 //} 105 //else 106 //{ 107 //document.getElementById('format').innerHTML += string.replace(/_/g, "-"); 108 //} 109 return string; 110 } 111 112 //Invokes the expression tree applet to draw an expression or abstract syntax tree representation of the given postfix token array 113 function drawPostfixExpressionTree(postfixTokens) 114 { 115 document .getElementById ('ExpressionTreeApplet').clearPostfixTokens(); 116 var tokenType, argumentCount, count; 117 for (count = 0; count < postfixTokens.length; count = count + 1) 118 { 119 tokenType = getType (postfixTokens[count]); 120 if (tokenType === "Operator" || tokenType === "AFunction") 121 { 122 argumentCount = postfixTokens [count].argumentCount; 123 } 124 else 125 { 126 argumentCount = 0; 127 } 128 document .getElementById ('ExpressionTreeApplet').addPostfixToken(postfixTokens[count].toString().replace(/_/g, "-"), argumentCount.to String()); 129 } 130 document .getElementById ('ExpressionTreeApplet').buildRootTree(); 131 document.getElementById('ExpressionTreeApplet').repaint(); 132 } 133 134 //Returns the given string with some entities replaced 135 function displayFormat(string) 136 { 137 return string.toString().replace(/\[/g, "(").replace(/\]/g, ")").replace(/\{/g, "(").replace(/\}/g, ")").replace(/"/g, "").replace(/_/g, "-").replace(/"/g, ""); 135 138 } 139 140 //Function to convert hex format to a rgb color 141 function rgb2hex(rgb) 142 { 143 rgb = rgb. match (/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 144 145 function hex(x) 146 { 147 return ("0" + parseInt(x, 10).toString(16)).slice(-2); 148 } 149 return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); 150 } 151 152 //Switches the simplify log to display expressions in seither cript or MathML notation 153 function changeSimplifyInnerHtml() 154 { 155 if (document.getElementById('mathMLCheckbox').checked === true) 156 { 157 document .getElementById ('simplifyMathML').className = "simplify"; 158 document.getElementById('simplifyText').className = "simplify nodisplay"; 159 } 160 else 161 { 162 document .getElementById ('simplifyText').className = "simplify"; 163 document.getElementById('simplifyMathML').className = "simplify nodisplay"; 164 } 165 } 166 167 //Updates both script and MathML notations with a given simplification step 168 function addSubExpression(simplification, input, output) 169 { 170 input = displayFormat (input.toString()); 171 output = displayFormat(output.toString()); 172 if (input !== output) 173 { 174 var string = input + "=" + output, 175 actualHighlightColor, expression, original = document.createElement("div"), 176 replacement = document.createElement("div"), 177 taglessReplacement; 178 179 if (string.indexOf("NaN") === -1) 180 { 181 actualHighlightColor = highlightColor; 182 } 183 else 184 { 185 actualHighlightColor = errorColor; 186 } 187 188 document .getElementById ("sub" + subExpressionCount).innerHTML = "#"; 189 expression = displayFormat(removeTags(document.getElementById("currentExpression").innerHTML)); 190 document.getElementById('simplifyText').innerHTML += displayCount + ". " + simplification + ":<br /><br />"; 191 document.getElementById('simplifyText').innerHTML += expression.replace("#", '<span style="color:' + actualHighlightColor + '">' + input + ' </span>') + "<br />"; 136 192 document.getElementById('simplifyText').innerHTML += '<span style="color:' + actualHighlightColor + '">' + string + '</span>' + "<br />"; 193 document.getElementById('simplifyText').innerHTML += expression.replace("#", '<span style="color:' + actualHighlightColor + '">' + output + '</span>') + "<br /><br />"; 194 195 mathcolor = normalColor; 196 document.getElementById('simplifyMathML').innerHTML += displayCount + ". " + simplification + ":<br /><br />"; 197 original.appendChild(parseMath(escapeFunctionNames(displayFormat(removeTags(document.get ElementById("currentExpression").inner HTML)).replace("#", "\"#\""), functions).toString(), false)); 198 replacement.appendChild(parseMath(escapeFunctionNames(input, functions).toString(), false)); 199 if (navigator.appName === 'Microsoft Internet Explorer') 200 { 201 taglessReplacement = removeTags (replacement.innerHTML, ["m:math", "m:mstyle"]); 202 document.getElementById('simplifyMathML').innerHTML += original.innerHTML.replace('<m:mtext>#</m:mtext>', '<m:mstyle mathcolor=" ' + actualHighlightColor + '">' + taglessReplacement + '</m:mstyle>').replace(/"#"/, escapeFunctionNames(input, functions)) + "<br />"; 203 } 204 else 205 { 206 taglessReplacement = removeTags (replacement.innerHTML, ["math", "mstyle"]); 207 document.getElementById('simplifyMathML').innerHTML += original.innerHTML.replace('<mtext>#</mtext>', '<mstyle mathcolor="' + actu alHighlightColor + '">' + taglessReplacement + '</mstyle>').replace(/"#"/, escapeFunctionNames(input, functions).replace(/"/g, "" ;")) + "<br />"; 208 } 209 mathcolor = actualHighlightColor; 210 document .getElementById ('simplifyMathML').appendChild(parseMath(escapeFunctionNames(string, functions).toString(), false)); 211 mathcolor = normalColor; 212 document.getElementById('simplifyMathML').innerHTML += "<br />"; 213 replacement.removeChild(replacement.firstChild); 214 replacement.appendChild(parseMath(escapeFunctionNames(output, functions).toString(), false)); 215 if (navigator.appName === 'Microsoft Internet Explorer') 216 { 217 taglessReplacement = removeTags (replacement.innerHTML, ["m:math", "m:mstyle"]); 218 document.getElementById('simplifyMathML').innerHTML += original.innerHTML.replace('<m:mtext>#</m:mtext>', '<m:mstyle mathcolor=" ' + actualHighlightColor + '">' + taglessReplacement + '</m:mstyle>').replace(/"#"/, escapeFunctionNames(output, functions)) + "<br /><br />"; 219 } 220 else 221 { 222 taglessReplacement = removeTags (replacement.innerHTML, ["math", "mstyle"]); 223 document.getElementById('simplifyMathML').innerHTML += original.innerHTML.replace('<mtext>#</mtext>', '<mstyle mathcolor="' + actu alHighlightColor + '">' + taglessReplacement + '</mstyle>').replace(/"#"/, escapeFunctionNames(output, functions).replace(/"/g, "&qu ot;")) + "<br /><br />"; 224 } 225 137 226 document .getElementById ("sub" + subExpressionCount).innerHTML = output; 227 228 displayCount = displayCount + 1; 229 } 230 231 changeSimplifyInnerHtml (); 232 233 subExpressionCount = subExpressionCount + 1; 234 } 235 236 //Perform the simplification 237 function doSimplify() 238 { 239 var inputExpression, expressionTreeString; 240 document.getElementById('simplifyText').innerHTML = ""; 241 document.getElementById('simplifyMathML').innerHTML = ""; 242 document.getElementById('currentExpression').innerHTML = ""; 243 document.getElementById('formattedInput').value = ""; 244 document.getElementById('postfixExpression').value = ""; 245 document.getElementById('expressionTreeExpression').value = ""; 246 document.getElementById('result').value = ""; 247 subExpressionCount = 1; 248 displayCount = 1; 249 inputExpression = document.getElementById('input').value; 250 inputExpression = format(inputExpression); 251 document.getElementById('formattedInput').value = inputExpression.replace(/_/g, "-"); 252 postfixTokens = shunting_yard(inputExpression); 253 document.getElementById('postfixExpression').value = postfixTokens.toString().replace(/,/g, " ").replace(/_/g, "-"); 254 drawPostfixExpressionTree(postfixTokens); 255 expressionTreeString = document.getElementById('ExpressionTreeApplet').getExpressionTreeExpression(); 256 document.getElementById('expressionTreeExpression').value = expressionTreeString; 257 document.getElementById('result').value = displayFormat(evaluatePostfixExpression(postfixTokens)); 258 } 259 260 //Returns an array of function names from the given array of function names whose beginnings match the given string 261 function getMatchingFunctions(string, functionList) 262 { 263 var matchingFunctions = [], 264 count; 265 for (count = 0; count < functionList.length; count = count + 1) 266 { 267 if (functionList[count].substring(0, string.length).search(new RegExp("^" + XRegExp.escape(string), "i")) !== -1) 268 { 269 matchingFunctions. push (functionList[count]); 270 } 271 } 272 return matchingFunctions; 273 } 274 275 //Returns the index of the last operator in the given string 276 function getLastDelimeterIndex(string) 277 { 278 var reversedString = string.split("").reverse().join(""), 279 lastDelimeterIndex = reversedString.search(/[\^*\/+\-\(,]/); 138 280 if (lastDelimeterIndex !== -1) 281 { 282 return (string.length - 1) - lastDelimeterIndex; 283 } 284 else 285 { 286 return lastDelimeterIndex; 287 } 288 } 289 290 //Returns a substring of the given string starting from the character after the last operator 291 function extractLast(string) 292 { 293 var lastDelimeterIndex = getLastDelimeterIndex(string); 294 if (lastDelimeterIndex !== -1) 295 { 296 return string.substring(lastDelimeterIndex + 1); 297 } 298 else 299 { 300 return string; 301 } 302 } 303 304 //Hides (closes) the User Guide 305 function closeUserGuide() 306 { 307 document .getElementById ("UserGuide").className = "ui-widget-content hidden"; 308 } 309 310 //Returns elements specified in given intersectorsSelector that are intersecting with the given targetSelector 311 function findIntersectors(targetSelector, intersectorsSelector) 312 { 313 var intersectors = [], 314 $target = $(targetSelector), 315 tAxis = $target.offset(), 316 t_x = [tAxis.left, tAxis.left + $target.outerWidth()], 317 t_y = [tAxis.top, tAxis.top + $target.outerHeight()]; 318 319 $(intersectorsSelector).each(function () 320 { 321 var $this = $(this), 322 thisPos = $this.offset(), 323 i_x = [thisPos.left, thisPos.left + $this.outerWidth()], 324 i_y = [thisPos.top, thisPos.top + $this.outerHeight()]; 325 326 if (t_x[0] < i_x[1] && t_x[1] > i_x[0] && t_y[0] < i_y[1] && t_y[1] > i_y[0]) 327 { 328 intersectors. push ($this); 329 } 330 331 }); 332 return intersectors; 333 } 334 335 //Hides the expression tree applet if any of the web page components are overlapping it 336 function checkExpressionTreeAppletOverlap(elementToCheck, elementsIntersecting) 337 { 139 338 var intersectors = findIntersectors(elementToCheck, elementsIntersecting), 339 intersector; 340 for (intersector = 0; intersector < intersectors.length; intersector = intersector + 1) 341 { 342 if ($(intersectors[intersector]).css('z-index') > $('#ExpressionTree').css('z-index')) 343 { 344 $('#ExpressionTreeApplet').css("visibility", "hidden"); 345 return; 346 } 347 } 348 $('#ExpressionTreeApplet').css("visibility", "visible"); 349 } 350 351 //jQuery center function extension 352 jQuery.fn.center = function (absolute) 353 { 354 return this.each(function () 355 { 356 var t = jQuery(this); 357 t.css( 358 { 359 position : absolute ? 'absolute' : 'fixed' , 360 left : '50%', 361 top : '50%', 362 zIndex : '99' 363 }).css( 364 { 365 marginLeft : '-' + (t.outerWidth() / 2) + 'px', 366 marginTop: '-' + (t.outerHeight() / 2) + 'px' 367 }); 368 369 if (absolute) 370 { 371 t.css ( 372 { 373 marginTop : parseInt (t.css('marginTop'), 10) + jQuery(window).scrollTop(), 374 marginLeft: parseInt(t.css('marginLeft'), 10) + jQuery(window).scrollLeft() 375 }); 376 } 377 }); 378 }; 379 380 //Called by the expression simplifier applet when it is finished loading 381 function ES_symjaLoaded() 382 { 383 ES_symjaAppletLoaded = true; 384 } 385 386 //Called by the expression tree applet when it is finished loading 387 function ExpressionTreeLoaded() 388 { 389 ExpressionTreeAppletLoaded = true; 390 } 391 392 //Returns a JSON Object with a boolean which is true if Java is enabled and a version string which is non-empty if Java is installed and a version could be obtained 393 /*function checkJavaSupport() 394 { 140 395 var result = 396 { 397 javaEnabled: false, 398 version: '' 399 }; 400 if (typeof navigator !== 'undefined' && typeof navigator.javaEnabled !== 'undefined') 401 { 402 result.javaEnabled = navigator.javaEnabled(); 403 if (typeof java !== 'undefined') 404 { 405 result.version = java.lang.System.getProperty("java.version"); 406 } 407 } 408 else 409 { 410 result.javaEnabled = 'unknown'; 411 } 412 413 return result; 414 }*/ 415 416 //Displays hidden webpage components and hides the loading image 417 function showElements() 418 { 419 document .getElementById ("loader").className = "hidden"; 420 document.getElementById("ExpressionTree").className = "ui-widget-content"; 421 document.getElementById("header").className = "ui-widget-header"; 422 document.getElementById("help").className = "ui-widget-header"; 423 document.getElementById("TextBoxes").className = "ui-widget-content"; 424 document.getElementById("FormatLog").className = "ui-widget-content"; 425 document.getElementById("SimplifyLog").className = "ui-widget-content"; 426 document.getElementById("ExpressionTree").className = "ui-widget-content"; 427 document.getElementById("bottomLeft").className = "ui-widget-header"; 428 document.getElementById("bottomRight").className = "ui-widget-header"; 429 $('#ExpressionTreeApplet').css("width", "100%"); 430 $('#ExpressionTreeApplet').css("height", "100%"); 431 } 432 433 //Shows web page components if the expression tree applet has finished loading 434 function waitForExpressionTreeApplet() 435 { 436 try 437 { 438 if (ExpressionTreeAppletLoaded document.getElementById('ExpressionTreeApplet').isReady() == "true") 439 { 440 showElements (); 441 } 442 } 443 catch (e) 444 { 445 setTimeout (function () 446 { 447 waitForExpressionTreeApplet (); 448 }, 500); 449 } 450 } 451 452 /*function changeMathMLColorOnThemeChange() 141 || 453 { 454 var newColor = rgb2hex($('.ui-widget-content').css("color")); 455 document.getElementById('simplifyMathML').innerHTML = document.getElementById("simplifyMathML").innerHTML.replace(new R egExp(normalColor, "g"), newColor); 456 normalColor = newColor; 457 if (document.getElementById("mathMLCheckbox").checked === true) 458 { 459 document.getElementById("simplify").innerHTML = document.getElementById("simplifyMathML").innerHTML; 460 } 461 }*/ 462 463 //Initializes jQuery components 464 function bodyInit() 465 { 466 $('#TextBoxes').draggable( 467 { 468 cancel : 'input, span' , 469 stack : '#ExpressionTree, #SimplifyLog, #FormatLog, #UserGuide' , 470 drag : function () 471 { 472 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#TextBoxes"); 473 } 474 }); 475 476 $('#ExpressionTree').draggable( 477 { 478 cancel : 'span' , 479 stack : '#TextBoxes, #SimplifyLog, #FormatLog, #UserGuide' , 480 start : function () 481 { 482 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#TextBoxes, #FormatLog, #SimplifyLog, #UserGuide, .jquery-ui-theme switcher"); 483 } 484 }); 485 486 $('input:button', '#TextBoxes, #UserGuide').button(); 487 488 $('#TextBoxes').click(function () 489 { 490 return false; 491 }); 492 493 $('#input').keypress(function (event) 494 { 495 if (event.keyCode === 13 && !menuOpen) 496 { 497 $('#calculate').click(); 498 } 499 }); 500 501 /*$('#input').keydown(function (event) 502 { 503 if ((event.keyCode > 0 && event.keyCode < 8) || 504 (event.keyCode > 8 && event.keyCode < 13) || 505 (event.keyCode > 13 && event.keyCode < 40) || 506 (event.keyCode > 57 && event.keyCode < 65) || 142 507 (event.keyCode > 90 && event.keyCode < 94) || 508 (event.keyCode > 94 && event.keyCode < 97) || 509 (event.keyCode > 122 && event.keyCode < 127)) 510 { 511 alert(event.keyCode); 512 return false; 513 } 514 });*/ 515 516 $('#FormatLog').draggable( 517 { 518 cancel : 'span, #format' , 519 stack : '#TextBoxes, #ExpressionTree, #SimplifyLog, #UserGuide' , 520 drag : function () 521 { 522 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#FormatLog"); 523 } 524 }); 525 526 $('#SimplifyLog').draggable( 527 { 528 cancel : 'span, .simplify' , 529 stack : '#TextBoxes, #ExpressionTree, #FormatLog, #UserGuide' , 530 drag : function () 531 { 532 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#SimplifyLog"); 533 } 534 }); 535 536 if (navigator.appName === 'Microsoft Internet Explorer') 537 { 538 $('#TextBoxes').resizable( 539 { 540 resize : function () 541 { 542 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#TextBoxes"); 543 } 544 }); 545 546 $('#FormatLog').resizable( 547 { 548 alsoResize : '#format' , 549 resize : function () 550 { 551 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#FormatLog"); 552 } 553 }); 554 555 $('#SimplifyLog').resizable( 556 { 557 alsoResize : '.simplify' , 558 resize : function () 559 { 560 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#SimplifyLog"); 561 } 562 }); 563 564 $('#ExpressionTree').resizable( 565 { 143 566 alsoResize : '#ExpressionTreeAppletWrapper, #ExpressionTreeAppletWrapper h3' , 567 stop : function (event, ui) 568 { 569 $('#ExpressionTreeAppletWrapper').css('height', ($('#ExpressionTree').height() $('#ExpressionTree h3').height()) - 30); 570 }, 571 resize: function () 572 { 573 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#TextBoxes, #FormatLog, #SimplifyLog, #UserGuide, .jquery-ui-the meswitcher"); 574 } 575 }); 576 } 577 else 578 { 579 $('#TextBoxes').resizable( 580 { 581 helper : 'ui-resizable-helper' , 582 stop : function () 583 { 584 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#TextBoxes"); 585 } 586 }); 587 588 $('#FormatLog').resizable( 589 { 590 helper : 'ui-resizable-helper' , 591 stop : function (event, ui) 592 { 593 $('#format').css('height', $('#FormatLog').height() - $('#FormatLog h3').height()); 594 checkExpressionTreeAppletOverlap("#ExpressionTreeApplet", "#FormatLog"); 595 } 596 }); 597 598 $('#SimplifyLog').resizable( 599 { 600 helper : 'ui-resizable-helper' , 601 stop : function (event, ui) 602 { 603 $('.simplify').css('height', $('#SimplifyLog').height() - $('#SimplifyLogHeader').height()); 604 checkExpressionTreeAppletOverlap("#ExpressionTreeApplet", "#SimplifyLog"); 605 } 606 }); 607 608 $('#ExpressionTree').resizable( 609 { 610 helper : 'ui-resizable-helper' , 611 stop : function (event, ui) 612 { 613 $('#ExpressionTreeAppletWrapper').css('height', ($('#ExpressionTree').height() $('#ExpressionTree h3').height()) - 20); 614 checkExpressionTreeAppletOverlap("#ExpressionTreeApplet", "#TextBoxes, #FormatLog, #SimplifyLog, #UserGuide, .jquery-ui-theme switcher"); 615 } 616 }); 617 } 618 144 619 $('#buttonWrapper').height($('#calculate').height()); 620 $('#TextBoxes').resizable("option", "minHeight", $('#TextBoxes').height()); 621 $('#TextBoxes').resizable("option", "minWidth", $('#TextBoxes span:contains("Expression Tree Expression")').width() + 10); 622 $('#TextBoxes').css("height", $('#TextBoxes').resizable("option", "minHeight") + 10 + "px"); 623 if ($(window).width() / 100 * 19 > $('#TextBoxes').resizable("option", "minWidth")) 624 { 625 $('#TextBoxes').css("width", "19%"); 626 } 627 else 628 { 629 $('#TextBoxes').css("width", $('#TextBoxes').resizable("option", "minWidth")); 630 } 631 632 $('#ExpressionTree').resizable("option", "minHeight", $('#ExpressionTree h3').height() * 2); 633 $('#ExpressionTree').resizable("option", "minWidth", $('#ExpressionTree span').width() + 10); 634 if ($(window).width() / 100 * 40 > $('#ExpressionTree').resizable("option", "minWidth")) 635 { 636 $('#ExpressionTree').css("height", "42%"); 637 $('#ExpressionTree').css("width", "40%"); 638 } 639 else 640 { 641 $('#ExpressionTree').css("height", $('#ExpressionTree').resizable("option", "minHeight")); 642 $('#ExpressionTree').css("width", $('#ExpressionTree').resizable("option", "minWidth")); 643 } 644 if (navigator.appName === 'Microsoft Internet Explorer') 645 { 646 $('#ExpressionTreeAppletWrapper').css('height', (($('#ExpressionTree').height() $('#ExpressionTree h3').height()) - 30)); 647 $('#input, #formattedInput, #postfixExpression, #expressionTreeExpression, #result').css('width', '100%'); 648 } 649 else 650 { 651 $('#ExpressionTreeAppletWrapper').css('height', (($('#ExpressionTree').height() $('#ExpressionTree h3').height()) - 20)); 652 $('#input, #formattedInput, #postfixExpression, #expressionTreeExpression, #result').css('width', '98%'); 653 } 654 655 $('#FormatLog').resizable("option", "minHeight", $('#FormatLog h3').height()); 656 $('#FormatLog').resizable("option", "minWidth", $('#FormatLog span:contains("Format Log")').width() + 10); 657 if ($(window).width() / 100 * 26 > $('#FormatLog').resizable("option", "minWidth")) 658 { 659 $('#FormatLog').css("height", "29%"); 660 $('#FormatLog').css("width", "27%"); 661 } 662 else 663 { 664 $('#FormatLog').css("height", $('#FormatLog').resizable("option", "minHeight")); 665 $('#FormatLog').css("width", $('#FormatLog').resizable("option", "minWidth")); 666 } 667 $('#format').css('height', $('#FormatLog').height() - $('#FormatLog h3').height()); 668 669 $('#SimplifyLog').resizable("option", "minHeight", $('#SimplifyLogHeader').height()); 670 $('#SimplifyLog').resizable("option", "minWidth", $('#SimplifyLog span:contains("Simplify Log")').width() + $('#useMathML').width() + 10); 145 671 if ($(window).width() / 100 * 26 > $('#SimplifyLog').resizable("option", "minWidth")) 672 { 673 $('#SimplifyLog').css("height", "29%"); 674 $('#SimplifyLog').css("width", "27%"); 675 } 676 else 677 { 678 $('#SimplifyLog').css("height", $('#SimplifyLog').resizable("option", "minHeight")); 679 $('#SimplifyLog').css("width", $('#SimplifyLog').resizable("option", "minWidth")); 680 } 681 $('.simplify').css('height', $('#SimplifyLog').height() - $('#SimplifyLogHeader').height()); 682 683 $(window).resize(function () 684 { 685 if ($(window).width() / 100 * 19 > $('#TextBoxes').resizable("option", "minWidth")) 686 { 687 $('#TextBoxes').css("width", "19%"); 688 } 689 else 690 { 691 $('#TextBoxes').css("width", $('#TextBoxes').resizable("option", "minWidth")); 692 } 693 694 if ($(window).width() / 100 * 40 > $('#ExpressionTree').resizable("option", "minWidth")) 695 { 696 $('#ExpressionTree').css("height", "42%"); 697 $('#ExpressionTree').css("width", "40%"); 698 } 699 else 700 { 701 $('#ExpressionTree').css("height", $('#ExpressionTree').resizable("option", "minHeight")); 702 $('#ExpressionTree').css("width", $('#ExpressionTree').resizable("option", "minWidth")); 703 } 704 if (navigator.appName === 'Microsoft Internet Explorer') 705 { 706 $('#ExpressionTreeAppletWrapper').css('height', (($('#ExpressionTree').height() $('#ExpressionTree h3').height()) - 30)); 707 $('#input, #formattedInput, #postfixExpression, #expressionTreeExpression, #result').css('width', '100%'); 708 } 709 else 710 { 711 $('#ExpressionTreeAppletWrapper').css('height', (($('#ExpressionTree').height() $('#ExpressionTree h3').height()) - 20)); 712 $('#input, #formattedInput, #postfixExpression, #expressionTreeExpression, #result').css('width', '98%'); 713 } 714 715 if ($(window).width() / 100 * 26 > $('#FormatLog').resizable("option", "minWidth")) 716 { 717 $('#FormatLog').css("height", "29%"); 718 $('#FormatLog').css("width", "27%"); 719 } 720 else 721 { 722 $('#FormatLog').css("height", $('#FormatLog').resizable("option", "minHeight")); 723 $('#FormatLog').css("width", $('#FormatLog').resizable("option", "minWidth")); 724 } 725 $('#format').css('height', $('#FormatLog').height() - $('#FormatLog h3').height()); 146 726 727 if ($(window).width() / 100 * 26 > $('#SimplifyLog').resizable("option", "minWidth")) 728 { 729 $('#SimplifyLog').css("height", "29%"); 730 $('#SimplifyLog').css("width", "27%"); 731 } 732 else 733 { 734 $('#SimplifyLog').css("height", $('#SimplifyLog').resizable("option", "minHeight")); 735 $('#SimplifyLog').css("width", $('#SimplifyLog').resizable("option", "minWidth")); 736 } 737 $('.simplify').css('height', $('#SimplifyLog').height() - $('#SimplifyLogHeader').height()); 738 }); 739 740 $('#input').autocomplete( 741 { 742 minLength : 1, 743 source : function (request, response) 744 { 745 var lastTerm = extractLast(request.term); 746 if (lastTerm.length >= $('#input').autocomplete("option", "minLength")) 747 { 748 response ($.ui.autocomplete.filter(getMatchingFunctions(lastTerm, functions), lastTerm)); 749 } 750 else 751 { 752 response (false); 753 } 754 }, 755 focus: function () 756 { 757 menuOpen = true; 758 return false; 759 }, 760 close: function (event, ui) 761 { 762 setTimeout (function () 763 { 764 menuOpen = false; 765 }, 100); 766 }, 767 select: function (event, ui) 768 { 769 var terms = this.value; 770 terms = terms.substring(0, getLastDelimeterIndex(terms) + 1); 771 terms += ui.item.value; 772 this.value = terms; 773 return false; 774 } 775 }); 776 777 $('#operations').dataTable( 778 { 779 "bJQueryUI": true, 780 "bLengthChange": true, 781 "bSort": true, 782 "bAutoWidth": true, 783 "bFilter": false, 784 "bPaginate": false, 147 785 "bInfo": false 786 }); 787 788 $('#functions').dataTable( 789 { 790 "bJQueryUI": true, 791 "bLengthChange": true, 792 "bFilter": true, 793 "bSort": true, 794 "bAutoWidth": true, 795 "bPaginate": false, 796 "bInfo": false 797 }); 798 799 $('#UserGuide').draggable( 800 { 801 cancel : 'span, p, ol, ul, li, table, input, #guide' , 802 stack : '#TextBoxes, #ExpressionTree, #FormatLog, #SimplifyLog' , 803 drag : function () 804 { 805 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#UserGuide"); 806 } 807 }).resizable( 808 { 809 alsoResize : '#guide, table' , 810 resize : function () 811 { 812 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", "#UserGuide"); 813 }, 814 minHeight: $('#FormatLog').resizable("option", "minHeight"), 815 minWidth: $('#FormatLog').resizable("option", "minWidth") 816 }); 817 818 $.maxZIndex = $.fn.maxZIndex = function (opt) 819 { 820 var def = 821 { 822 inc : 10, 823 group : "*" 824 }, 825 zmax = 0; 826 $.extend(def, opt); 827 $(def.group).each(function () 828 { 829 var cur = parseInt($(this).css('z-index'), 10); 830 zmax = cur > zmax ? cur : zmax; 831 }); 832 if (!this.jquery) 833 { 834 return zmax; 835 } 836 return this.each(function () 837 { 838 zmax += def.inc; 839 $(this).css("z-index", zmax); 840 }); 841 }; 842 843 $('#help').click(function () 148 844 { 845 if ($('#UserGuide').css('visibility') === 'hidden') 846 { 847 document .getElementById ("UserGuide").className = "ui-widget-content"; 848 $('#UserGuide').maxZIndex( 849 { 850 inc : 1 851 }); 852 $('#guide').css('height', $('#UserGuide').height() - $('#UserGuideHeader h3').height()); 853 } 854 else 855 { 856 document .getElementById ("UserGuide").className = "ui-widget-content hidden"; 857 } 858 859 return false; 860 }); 861 862 $('#switcher').themeswitcher( 863 { 864 loadTheme : 'Cupertino' , 865 width : 300, 866 onOpen : function () 867 { 868 setTimeout (function () 869 { 870 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", ".jquery-ui-themeswitcher"); 871 }, 100); 872 }, 873 onClose: function () 874 { 875 checkExpressionTreeAppletOverlap ("#ExpressionTreeApplet", ".jquery-ui-themeswitcher"); 876 } 877 }); 878 879 document.getElementById('bottomLeft').innerHTML += "<span id=\"me\">By Richard Andrew Mealing, [email protected], last updated " + document.lastModified + "<\/span>"; 880 881 waitForExpressionTreeApplet(); 882 } 883 884 //Initializes the body if Java is supported 885 function checkJavaSupport() 886 { 887 PluginDetect.getVersion ("."); 888 //var version = PluginDetect.getVersion("Java", "classes/applet/getJavaInfo2.jar", null), 889 var isMinimumVersion = PluginDetect.isMinVersion("Java", "1.6", "classes/applet/getJavaInfo2.jar", null), 890 information = PluginDetect.getInfo("Java", "classes/applet/getJavaInfo2.jar", null); 891 if (isMinimumVersion === 1 && (information.objectTag === null || information.objectTag === 1) && information.isPlugin2 > -1) 892 { 893 jQuery ('#loader').center(true); 894 document.getElementById("loader").className = ""; 895 bodyInit(); 896 } 897 else 898 { 149 899 document .getElementById ("javaError").className = "ui-widget"; 900 } 901 } 902 903 //Hides the JavaScript error if JavaScript is supported 904 function checkJavaScriptSupport() 905 { 906 document .getElementById ("javaScriptError").className = "ui-widget hidden"; 907 } 908 909 //Center the loading image when the document is ready (i.e. when the DOM tree is loaded) 910 $(document).ready(function () 911 { 912 checkJavaScriptSupport (); 913 checkJavaSupport(); 914 }); 9.13.3 Java source code 9.13.3.1 ExpressionTree.java 1 import java.awt.Dimension; 2 import java.awt.FontMetrics; 3 import java.awt.Graphics; 4 import java.net.URL; 5 import java.util.Stack; 6 import java.util.Vector; 7 import javax.swing.JApplet; 8 import javax.swing.JPanel; 9 import javax.swing.JScrollBar; 10 import javax.swing.JScrollPane; 11 12 /** 13 * Constructs and draws an expression tree given a postfix expression. 14 * 15 * @author Richard Andrew Mealing 16 * @version 12/07/2010 17 */ 18 @SuppressWarnings("serial") 19 public class ExpressionTree extends JApplet 20 { 21 /** 22 * An integer representing the size (diameter) of each node. 23 */ 24 private static final int NODE_SIZE = 40; 25 /** 26 * The {@link FontMetrics} of this class. 27 */ 28 private FontMetrics fontMetrics; 29 /** 30 * A {@link Stack} of each postfix token followed by its number of arguments 31 */ 32 private Stack<String> postfixTokens; 33 /** 34 * The {@link ExpressionTreeNode} representing the root of this expression tree. 35 */ 36 private ExpressionTreeNode root; 37 /** 150 38 * A {@link JScrollPane} for scrolling the view of an expression tree larger than the size of this applet. 39 */ 40 private JScrollPane scrollPane; 41 /** 42 * A {@link JPanel} to paint the expression tree onto. 43 */ 44 private JPanel panel; 45 /** 46 * A {@link String} representing the expression tree. 47 */ 48 private String expression; 49 /** 50 * An integer representing the maximum horizontal distance reached by the tree. 51 */ 52 private int maxHorizontalDistance; 53 /** 54 * An integer representing the maximum vertical distance reached by the tree. 55 */ 56 private int maxVerticalDistance; 57 /** 58 * A {@link Vector} of each unique postfix token that is too long to be displayed inside a node and so must be abbreviated and displayed in a key 59 */ 60 private Vector<String> abbreviatedContent; 61 62 /** 63 * Initialises some components and overrides panel's paintComponent method to paint the expression tree. 64 * 65 * The expression tree is only painted if the root node is initialised. 66 */ 67 @Override 68 public void init() 69 { 70 fontMetrics = this.getFontMetrics(this.getFont()); 71 postfixTokens = new Stack<String>(); 72 abbreviatedContent = new Vector<String>(); 73 expression = null; 74 panel = new JPanel() 75 { 76 @Override 77 public void paintComponent(Graphics g) 78 { 79 super.paintComponent(g); 80 if(root != null) 81 { 82 paintTree(g, root, this.getWidth() / 2, NODE_SIZE / 2 + drawAbbreviatedContent(g, 0, fontMetrics.getHeight())); 83 panel.setPreferredSize(new Dimension(maxHorizontalDistance, maxVerticalDistance)); 84 scrollPane.invalidate(); 85 scrollPane.validate(); 86 } 87 } 88 }; 89 scrollPane = new JScrollPane(panel); 90 scrollPane.setSize(this.getWidth(), this.getHeight()); 91 this.getContentPane().add(scrollPane); 151 92 93 //Try to inform the web page in which the applet is embedded it is loaded 94 try 95 { 96 getAppletContext().showDocument(new URL("javascript:ExpressionTreeLoaded()")); 97 } 98 catch (Exception ex) 99 { 100 ex.printStackTrace(); 101 } 102 } 103 104 /** 105 * Adds a postfix token and its number of arguments each as a <code>String</code> to postfixTokens. 106 * 107 * @param postfixToken a <code>String</code> representing a postfix token to add to postfixTokens. 108 * @param postfixArgumentCount a <code>String</code> representing the number of arguments (if any) associated with the given postfix token. 109 */ 110 public void addPostfixToken(String postfixToken, String postfixArgumentCount) 111 { 112 postfixTokens.push(postfixToken); 113 postfixTokens.push(postfixArgumentCount); 114 } 115 116 /** 117 * Removes all elements from postfixTokens. 118 */ 119 public void clearPostfixTokens() 120 { 121 postfixTokens.clear(); 122 } 123 124 /** 125 * Initialises (constructs) the expression tree and its <code>String</code> representation, sets the panel and scrollPane sizes and clears abbreviatedContent. 126 */ 127 public void buildRootTree() 128 { 129 abbreviatedContent.clear(); 130 root = buildPostfixTree(); 131 expression = root.getTreeContent(new Vector<StringBuffer>(), 0).toString().replaceAll(",", ""); 132 expression = expression.substring(1, expression.length() - 1); 133 JScrollBar horizontalScrollBar = scrollPane.getHorizontalScrollBar(); 134 horizontalScrollBar.setValue(horizontalScrollBar.getMaximum() / 2 horizontalScrollBar.getVisibleAmount() / 2); 135 } 136 137 /** 138 * Returns a <code>String</code> representing the expression tree. 139 * 140 * @return a <code>String</code> representing the expression tree. 141 */ 142 public String getExpressionTreeExpression() 143 { 152 144 return expression; 145 } 146 147 /** 148 * Returns a <code>String</code> representing the content that should be displayed. 149 * 150 * If the content <code>String</code> parameter is larger than the node size (width) it is abbreviated and returned otherwise it is returned unmodified. 151 * 152 * @param content a <code>String</code> representing the content to be abbreviated if necessary. 153 * @return a <code>String</code> representing the expression tree. 154 */ 155 private String checkContentWidth(String content) 156 { 157 int contentWidth = fontMetrics.stringWidth(content); 158 if(contentWidth >= NODE_SIZE) 159 { 160 int contentIndex = abbreviatedContent.indexOf(content); 161 if(contentIndex != -1) 162 { 163 content = "F" + contentIndex; 164 } 165 else 166 { 167 abbreviatedContent.addElement(content); 168 content = "F" + (abbreviatedContent.size() - 1); 169 } 170 } 171 return content; 172 } 173 174 /** 175 * Draws each unabbreviated content <code>String</code> and its matching abbreviation (if any). 176 * 177 * Each <code>String</code> is drawn on the given <code>Graphics</code> object below the previous with the first appearing at the given x and y coordinates. 178 * 179 * @param g a <code>Graphics</code> object to paint each <code>String</code> onto. 180 * @param xPos an integer representing the position to have the top left of the first <code>String</code> along the x axis. 181 * @param yPos an integer representing the position to have the top left of the first <code>String</code> along the y axis. 182 * @return an integer representing the y position of the bottom of the last <code>String</code>. 183 */ 184 private int drawAbbreviatedContent(Graphics g, int xPos, int yPos) 185 { 186 for(int count = 0; count < abbreviatedContent.size(); count++) 187 { 188 g.drawString("F" + count + " = " + abbreviatedContent.elementAt(count), xPos, yPos); 189 yPos = yPos + fontMetrics.getHeight(); 190 } 191 return yPos; 192 } 153 193 194 /** 195 * Builds an expression tree from each postfix token and its number of arguments in postfixTokens. 196 * 197 * @return an <code>ExpressionTreeNode</code> representing the root node of the expression tree. 198 */ 199 private ExpressionTreeNode buildPostfixTree() 200 { 201 int argumentCount = Integer.parseInt(postfixTokens.pop()); 202 ExpressionTreeNode node = new ExpressionTreeNode(checkContentWidth(postfixTokens.pop())); 203 204 for(int child = 0; child < argumentCount; child++) 205 { 206 node.addChildAt(buildPostfixTree(), 0); 207 } 208 209 return node; 210 } 211 212 /** 213 * Paints an expression tree node with the specified content on the specified {@link Graphics} object whose centre is at the specified x and y coordinates. 214 * 215 * @param g a <code>Graphics</code> object to paint the expression tree node onto. 216 * @param content a <code>String</code> to draw inside the node. 217 * @param xPos an integer representing the position to have the centre of the node along the x axis. 218 * @param yPos an integer representing the position to have the centre of the node along the y axis. 219 */ 220 private void paintNode(Graphics g, String content, int xPos, int yPos) 221 { 222 int radius = NODE_SIZE / 2; 223 int xCentre = xPos - radius; 224 int yCentre = yPos - radius; 225 g.drawOval(xCentre, yCentre, NODE_SIZE - 1, NODE_SIZE - 1); 226 227 int contentWidth = fontMetrics.stringWidth(content); 228 g.drawString(content, xCentre + radius - contentWidth / 2, yCentre + radius); 229 } 230 231 /** 232 * Paints an expression tree on the specified {@link Graphics} object whose root node is at the specified x and y coordinates. 233 * 234 * @param g a <code>Graphics</code> object to paint the expression tree onto. 235 * @param root an <code>ExpressionTreeNode</code> representing the root of the expression tree. 236 * @param xPos an integer representing the position to have the centre of the root node along the x axis. 237 * @param yPos an integer representing the position to have the centre of the root node along the y axis. 238 */ 239 private void paintTree(Graphics g, ExpressionTreeNode root, int xPos, int yPos) 154 240 { 241 Vector<ExpressionTreeNode> parents = new Vector<ExpressionTreeNode>(); 242 Vector<Integer> parentXPositions = new Vector<Integer>(); 243 Vector<Vector<ExpressionTreeNode>> children = new Vector<Vector<ExpressionTreeNode>>(); 244 245 parents.addElement(root); 246 parentXPositions.addElement(xPos); 247 248 paintNode(g, root.getContent().toString(), xPos, yPos); 249 maxVerticalDistance = NODE_SIZE; 250 maxHorizontalDistance = NODE_SIZE; 251 252 int childX; 253 int childY; 254 int lineX1; 255 int lineY1; 256 int lineX2; 257 int lineY2; 258 int parentCount = parents.size(); 259 int childrenCount = 0; 260 int childTotal = 0; 261 int layerDistance; 262 263 while(true) 264 { 265 //Add parent children 266 for(int parent = 0; parent < parentCount; parent++) 267 { 268 children.addElement(parents.elementAt(parent).getChildren()); 269 } 270 271 //Count children 272 childrenCount = 0; 273 for(int parentChildren = 0; parentChildren < children.size(); parentChildren++) 274 { 275 childrenCount += children.elementAt(parentChildren).size(); 276 } 277 278 if(childrenCount == 0) 279 { 280 break; 281 } 282 283 //Work out total horizontal distance needed 284 layerDistance = (childrenCount * (NODE_SIZE * 2)) - NODE_SIZE; 285 286 //Set the vertical positions 287 childY = yPos + NODE_SIZE * 2; 288 lineY1 = yPos + NODE_SIZE / 2; 289 lineY2 = childY - NODE_SIZE / 2; 290 291 //Draw the children using the parent x positions, add the children as new parents with their x positions 292 for(int parentChildren = 0; parentChildren < children.size(); parentChildren++) 293 { 294 lineX1 = parentXPositions.elementAt(parentChildren); 155 295 for(int child = 0; child < children.elementAt(parentChildren).size(); child++) 296 { 297 parents.addElement(children.elementAt(parentChildren).elementAt(child)); 298 childX = xPos - (layerDistance / 2) + (childTotal * NODE_SIZE * 2) + (NODE_SIZE / 2); 299 parentXPositions.addElement(childX); 300 lineX2 = childX; 301 paintNode(g, children.elementAt(parentChildren).elementAt(child).getContent().toString(), childX, childY); 302 g.drawLine(lineX1, lineY1, lineX2, lineY2); 303 childTotal++; 304 } 305 } 306 307 //Update maximum horizontal layer distance if necessary 308 maxHorizontalDistance = Math.max(maxHorizontalDistance, layerDistance); 309 310 //Remove the old parents 311 for(int parent = 0; parent < parentCount; parent++) 312 { 313 parents.removeElementAt(0); 314 parentXPositions.removeElementAt(0); 315 children.removeElementAt(0); 316 } 317 318 //Update parentCount 319 parentCount = parents.size(); 320 321 //Reset childTotal 322 childTotal = 0; 323 324 //Increment vertical position 325 yPos += NODE_SIZE * 2; 326 } 327 //Update maximum vertical layer distance 328 maxVerticalDistance = yPos + NODE_SIZE / 2; 329 } 330 331 /** 332 * Returns a <code>String</code> with a value of true if the applet is loaded. 333 * 334 * @return a <code>String</code> with a value of true if the applet is loaded. 335 */ 336 public String isReady() 337 { 338 return "true"; 339 } 340 } 341 9.13.3.2 ExpressionTreeNode.java 1 import java.util.Vector; 2 3 /** 4 * Implements an expression tree node. 156 5* 6 * @author Richard Andrew Mealing 7 * @version 18/04/2010 8 */ 9 public class ExpressionTreeNode 10 { 11 /** 12 * An {@link Object} representing the content of the expression tree node. 13 */ 14 private Object content; 15 /** 16 * A {@link Vector} containing the <code>ExpressionTreeNode</code> children of the expression tree node. 17 */ 18 private Vector<ExpressionTreeNode> children; 19 20 /** 21 * Sets the expression tree node's content and initialises its children (to empty). 22 * 23 * @param aContent an <code>Object</code> representing the expression tree node's content. 24 */ 25 public ExpressionTreeNode(Object aContent) 26 { 27 content = aContent; 28 children = new Vector<ExpressionTreeNode>(); 29 } 30 31 /** 32 * Returns a reference to the expression tree node's content. 33 * 34 * @return a reference to the <code>Object</code> representing the expression tree node's content. 35 */ 36 public Object getContent() 37 { 38 return content; 39 } 40 41 /** 42 * Adds an <code>ExpressionTreeNode</code> child to the end of the expression tree node's children. 43 * 44 * @param etn an <code>ExpressionTreeNode</code> representing the child to add to the expression tree node's children. 45 */ 46 public void addChild(ExpressionTreeNode etn) 47 { 48 children.add(etn); 49 } 50 51 /** 52 * Adds an <code>ExpressionTreeNode</code> child to the expression tree node's children at the specified index. 53 * 54 * @param etn an <code>ExpressionTreeNode</code> representing the child to add to the expression tree node's children. 55 * @param index an integer representing the index where the child should be 157 inserted. 56 */ 57 public void addChildAt(ExpressionTreeNode etn, int index) 58 { 59 children.add(index, etn); 60 } 61 62 /** 63 * Removes the given <code>ExpressionTreeNode</code> child from the expression tree node's children. 64 * 65 * @param etn an <code>ExpressionTreeNode</code> representing the child to remove from the expression tree node's children. 66 */ 67 public void removeChild(ExpressionTreeNode etn) 68 { 69 children.removeElement(etn); 70 } 71 72 /** 73 * Removes an <code>ExpressionTreeNode</code> child from the expression tree node's children at the specified index. 74 * 75 * @param index an integer representing the index of the child to be removed. 76 */ 77 public void removeChildAt(int index) 78 { 79 children.removeElement(index); 80 } 81 82 /** 83 * Returns a reference to the expression tree node's children. 84 * 85 * @return a reference to the <code>Vector</code> containing the expression tree node's children. 86 */ 87 public Vector<ExpressionTreeNode> getChildren() 88 { 89 return (Vector<ExpressionTreeNode>)children; 90 } 91 92 /** 93 * Returns a <code>Vector</code> containing a <code>String</code> representation of all the content of each depth of the expression tree starting from this node. 94 * 95 * If depthContent has no element representing the current depth then the 96 * the expression tree node's content at the current depth is added in a 97 * new {@link StringBuffer} to the end of depthContent. Otherwise the 98 * <code>StringBuffer</code> in depthContent representing this depth has 99 * (a space and) the <code>String</code> representation of this expression 100 * tree node's content appended to it. If this expression tree node has 101 * children then each child has this method called using depthContent and 102 * the next depth (depth + 1) as parameters. Finally a reference to 103 * depthContent is returned. 104 * 105 * @param depthContent a <code>Vector</code> to contain a <code>String</code> representation of all the content of each depth of the expression tree starting from this node. 158 106 * @param depth an integer representing the depth the expression tree node is at. 107 * @return a <code>Vector</code> containing a <code>String</code> representation of all the content of each depth of the expression tree starting from this node. 108 */ 109 public Vector<StringBuffer> getTreeContent(Vector<StringBuffer> depthContent, int depth) 110 { 111 if(depthContent.size() - 1 < depth) 112 { 113 depthContent.addElement(new StringBuffer(content.toString())); 114 } 115 else 116 { 117 depthContent.elementAt(depth).append(" " + content.toString()); 118 } 119 if(children.size() > 0) 120 { 121 for(int child = 0; child < children.size(); child++) 122 { 123 children.elementAt(child).getTreeContent(depthContent, depth + 1); 124 } 125 } 126 return depthContent; 127 } 128 } 129 9.13.3.3 ES_symja.java 1 import java.net.URL; 2 import java.util.Timer; 3 import java.util.TimerTask; 4 5 import javax.swing.JApplet; 6 7 import org.matheclipse.core.eval.EvalEngine; 8 import org.matheclipse.core.eval.EvalUtilities; 9 import org.matheclipse.core.eval.SystemNamespace; 10 import org.matheclipse.core.expression.F; 11 import org.matheclipse.core.form.output.OutputFormFactory; 12 import org.matheclipse.core.form.output.StringBufferWriter; 13 import org.matheclipse.core.interfaces.IExpr; 14 15 /** 16 * Can simplify infix expression strings and return their equivalent mathematical markup language (MathML) notation. 17 * 18 * @author Richard Andrew Mealing 19 * @version 08/08/2010 20 */ 21 @SuppressWarnings("serial") 22 public class ES_symja extends JApplet 23 { 24 /** 25 * Initialises the applet. 26 */ 27 public void init() 159 28 { 29 //Add Symja library extensions to the system namespace 30 SystemNamespace.DEFAULT.add("org.matheclipse.root.rationalisation"); 31 SystemNamespace.DEFAULT.add("org.matheclipse.power"); 32 SystemNamespace.DEFAULT.add("org.matheclipse.logarithm"); 33 SystemNamespace.DEFAULT.add("org.matheclipse.util"); 34 35 F.initSymbols(); //Initialise the MathEclipse expression creation factory 36 37 //Try to inform the web page in which the applet is embedded it is loaded 38 try 39 { 40 getAppletContext().showDocument(new URL("javascript:ES_symjaLoaded()")); 41 } 42 catch (Exception ex) 43 { 44 ex.printStackTrace(); 45 } 46 } 47 48 /** 49 * Calls evaluate with the given {@link String} parameters and returns the result if it is not null otherwise it returns anExpression. 50 * 51 * @param anExpression the infix expression <code>String</code> to be simplified 52 * @param aSimplifyMilliSeconds a <code>String</code> representing the number of milliseconds to allow the simplification to proceed 53 * @return a simplified <code>String</code> if evaluation succeeds or anExpression if it fails 54 */ 55 public String simplify(final String anExpression, final String aSimplifyMilliSeconds) 56 { 57 final IExpr result = evaluate(anExpression, Integer.parseInt(aSimplifyMilliSeconds)); 58 if(result != null) 59 { 60 return IExprToString(result); 61 } 62 else 63 { 64 return anExpression; 65 } 66 } 67 68 /** 69 * Attempts to simplify the given <code>String</code> for a maximum of the given amount of time. 70 * 71 * @param anExpression the infix expression <code>String</code> to be simplified 72 * @param aSimplifyMilliSeconds an integer representing the number of milliseconds to allow the simplification to proceed 73 * @return a simplified {@link IExpr} if evaluation succeeds otherwise null 74 */ 75 private IExpr evaluate(final String anExpression, final int simplifyMilliSeconds) 160 76 { 77 final Thread thread = Thread.currentThread(); //Get the main (current) thread 78 79 //Schedule the main thread to be interrupted by another newly created thread after simplifyMilliSeconds 80 new Timer().schedule(new TimerTask() 81 { 82 public void run() 83 { 84 thread.interrupt(); 85 } 86 }, simplifyMilliSeconds); 87 88 //Try to perform the simplification 89 try 90 { 91 return EvalEngine.get().evaluate(StringToIExpr(anExpression)); 92 } 93 catch (Exception ex) 94 { 95 ex.printStackTrace(); 96 return null; 97 } 98 } 99 100 /** 101 * Converts a given <code>IExpr</code> into an infix expression <code>String</code>. 102 * 103 * @param iexpr the <code>IExpr</code> to be converted into an infix expression <code>String</code>. 104 * @return an infix expression <code>String</code> representation of the given <code>IExpr</code> or null if conversion fails. 105 */ 106 private String IExprToString(final IExpr iexpr) 107 { 108 try 109 { 110 final StringBufferWriter stringBufferWriter = new StringBufferWriter(); 111 //Get an instance of an OutputFormFactory object and get it to convert the given IExpr into an infix expression String storing it in a StringBufferWriter 112 OutputFormFactory.get().convert(stringBufferWriter, iexpr); 113 return stringBufferWriter.toString(); 114 } 115 catch (Exception ex) 116 { 117 ex.printStackTrace(); 118 return null; 119 } 120 } 121 122 /** 123 * Converts a given infix expression <code>String</code> into an <code>IExpr</code>. 124 * 125 * @param anExpression the infix expression <code>String</code> to be converted into an <code>IExpr</code>. 126 * @return an <code>IExpr</code> representation of the given infix expression 161 <code>String</code>. 127 */ 128 private IExpr StringToIExpr(final String anExpression) 129 { 130 return EvalEngine.get().parse(anExpression); 131 } 132 133 /** 134 * Returns the equivalent mathematical markup language (MathML) for a given infix expression <code>String</code>. 135 * 136 * @param anExpression the infix expression <code>String</code> for which MathML notation will be returned. 137 * @return an infix expression <code>String</code> represented in MathML. 138 */ 139 public String getMathML(final String anExpression) 140 { 141 final StringBufferWriter stringBufferWriter = new StringBufferWriter(); 142 //Get an instance of an EvalUtilities object and get it to convert the given infix expression String into its equivalent MathML 143 new EvalUtilities().toMathML(StringToIExpr(anExpression), stringBufferWriter); 144 return stringBufferWriter.toString(); 145 } 146 147 /** 148 * Returns a <code>String</code> with a value of true if the applet is loaded. 149 * 150 * @return a <code>String</code> with a value of true if the applet is loaded. 151 */ 152 public String isReady() 153 { 154 return "true"; 155 } 156 } 157 9.13.3.4 ES_symja.php 1 <?php 2 3 $arg0 = $_POST['arg0']; 4 $arg1 = addcslashes($_POST['arg1'], '"'); 5 $arg1 = substr($arg1, 2, $arg1.length - 2); 6 $arg1 = '"'.$arg1.'"'; 7 $arg2 = $_POST['arg2']; 8 9 $output = shell_exec("java -jar ES_symja.jar $arg0 $arg1 $arg2"); 10 11 echo $output; 12 13 ?> 9.13.3.5 ES_symja.java (server side) 1 import java.util.Timer; 2 import java.util.TimerTask; 3 4 import org.matheclipse.core.eval.EvalEngine; 162 5 import org.matheclipse.core.eval.EvalUtilities; 6 import org.matheclipse.core.eval.SystemNamespace; 7 import org.matheclipse.core.expression.F; 8 import org.matheclipse.core.form.output.OutputFormFactory; 9 import org.matheclipse.core.form.output.StringBufferWriter; 10 import org.matheclipse.core.interfaces.IExpr; 11 12 /** 13 * Can simplify infix expression strings and return their equivalent mathematical markup language (MathML) notation. 14 * 15 * @author Richard Andrew Mealing 16 * @version 08/08/2010 17 */ 18 public class ES_symja 19 { 20 21 /** 22 * Initialises the application. 23 */ 24 public ES_symja() 25 { 26 //Add Symja library extensions to the system namespace 27 SystemNamespace.DEFAULT.add("org.matheclipse.root.rationalisation"); 28 SystemNamespace.DEFAULT.add("org.matheclipse.power"); 29 SystemNamespace.DEFAULT.add("org.matheclipse.logarithm"); 30 SystemNamespace.DEFAULT.add("org.matheclipse.util"); 31 32 F.initSymbols(); //Initialise the MathEclipse expression creation factory 33 } 34 35 /** 36 * Calls evaluate with the given {@link String} parameters and returns the result if it is not null otherwise it returns anExpression. 37 * 38 * @param anExpression the infix expression <code>String</code> to be simplified 39 * @param aSimplifyMilliSeconds a <code>String</code> representing the number of milliseconds to allow the simplification to proceed 40 * @return a simplified <code>String</code> if evaluation succeeds or anExpression if it fails 41 */ 42 public String simplify(final String anExpression, final String aSimplifyMilliSeconds) 43 { 44 final IExpr result = evaluate(anExpression, Integer.parseInt(aSimplifyMilliSeconds)); 45 if(result != null) 46 { 47 return IExprToString(result); 48 } 49 else 50 { 51 return anExpression; 52 } 53 } 54 55 /** 56 * Attempts to simplify the given <code>String</code> for a maximum of the 163 given amount of time. 57 * 58 * @param anExpression the infix expression <code>String</code> to be simplified 59 * @param aSimplifyMilliSeconds an integer representing the number of milliseconds to allow the simplification to proceed 60 * @return a simplified {@link IExpr} if evaluation succeeds otherwise null 61 */ 62 private IExpr evaluate(final String anExpression, final int simplifyMilliSeconds) 63 { 64 final Thread thread = Thread.currentThread(); //Get the main (current) thread 65 66 //Schedule the main thread to be interrupted by another newly created thread after simplifyMilliSeconds 67 new Timer().schedule(new TimerTask() 68 { 69 public void run() 70 { 71 thread.interrupt(); 72 } 73 }, simplifyMilliSeconds); 74 75 //Try to perform the simplification 76 try 77 { 78 return EvalEngine.get().evaluate(StringToIExpr(anExpression)); 79 } 80 catch (Exception ex) 81 { 82 ex.printStackTrace(); 83 return null; 84 } 85 } 86 87 /** 88 * Converts a given <code>IExpr</code> into an infix expression <code>String</code>. 89 * 90 * @param iexpr the <code>IExpr</code> to be converted into an infix expression <code>String</code>. 91 * @return an infix expression <code>String</code> representation of the given <code>IExpr</code> or null if conversion fails. 92 */ 93 private String IExprToString(final IExpr iexpr) 94 { 95 try 96 { 97 final StringBufferWriter stringBufferWriter = new StringBufferWriter(); 98 //Get an instance of an OutputFormFactory object and get it to convert the given IExpr into an infix expression String storing it in a StringBufferWriter 99 OutputFormFactory.get().convert(stringBufferWriter, iexpr); 100 return stringBufferWriter.toString(); 101 } 102 catch (Exception ex) 103 { 104 ex.printStackTrace(); 105 return null; 164 106 } 107 } 108 109 /** 110 * Converts a given infix expression <code>String</code> into an <code>IExpr</code>. 111 * 112 * @param anExpression the infix expression <code>String</code> to be converted into an <code>IExpr</code>. 113 * @return an <code>IExpr</code> representation of the given infix expression <code>String</code>. 114 */ 115 private IExpr StringToIExpr(final String anExpression) 116 { 117 return EvalEngine.get().parse(anExpression); 118 } 119 120 /** 121 * Returns the equivalent mathematical markup language (MathML) for a given infix expression <code>String</code>. 122 * 123 * @param anExpression the infix expression <code>String</code> for which MathML notation will be returned. 124 * @return an infix expression <code>String</code> represented in MathML. 125 */ 126 public String getMathML(final String anExpression) 127 { 128 final StringBufferWriter stringBufferWriter = new StringBufferWriter(); 129 //Get an instance of an EvalUtilities object and get it to convert the given infix expression String into its equivalent MathML 130 new EvalUtilities().toMathML(StringToIExpr(anExpression), stringBufferWriter); 131 return stringBufferWriter.toString(); 132 } 133 134 /** 135 * Calls the simplify or getMathML method with the second and third or second argument respectively depending on which method name the first argument matches. 136 * 137 * @param args a <code>String</code> array for command line arguments. 138 */ 139 public static void main(String[] args) 140 { 141 ES_symja es_symja = new ES_symja(); 142 /*if(args[0].toLowerCase().matches("simplify")) 143 { 144 System.out.println(es_symja.simplify(args[1], args[2])); 145 } 146 else if(args[0].toLowerCase().matches("getmathml")) 147 { 148 System.out.println(es_symja.getMathML(args[1])); 149 }*/ 150 System.out.println(es_symja.simplify("x+x+x+x", "10000")); 151 System.exit(0); 152 } 153 } 9.13.3.6 Log.java 165 1 package org.matheclipse.logarithm; 2 3 import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; 9 10 /** 11 * Performs logarithms to any base. 12 * 13 * @author Richard Andrew Mealing 14 * @version 08/08/2010 15 */ 16 public class Log extends AbstractFunctionEvaluator 17 { 18 @Override 19 public void setUp(final ISymbol symbol) 20 { 21 //Give the function a numeric attribute indicating it only deals with numbers 22 symbol.setAttributes(ISymbol.NUMERICFUNCTION); 23 } 24 25 @Override 26 public IExpr evaluate(final IAST functionList) 27 { 28 //The input AST should have 3 arguments 29 if(functionList.size() == 3) 30 { 31 IExpr base = functionList.getAt(1); //The second argument should be the logarithm base 32 IExpr number = functionList.getAt(2); //The third argument should be the number to apply the logarithm to 33 if(base.isNumber() && number.isNumber()) 34 { 35 //Evaluate the base and number to ensure they are in decimal format 36 base = F.evaln(base); 37 number = F.evaln(number); 38 //Log(x,y) = log(10,y)/log(10,x) where x and y are numbers 39 double result = Math.log(Num.valueOf((number.toString())))/Math.log(Num.valueOf(base.toString())); 40 return Num.valueOf(result); 41 } 42 } 43 return null; 44 } 45 } 9.13.3.7 CombineBases.java 1 package org.matheclipse.power; 2 3 import java.util.Vector; 11 12 /** 13 * Multiplies bases to the same exponent and puts the result to the shared exponent. 14 * 15 * @author Richard Andrew Mealing 16 * @version 08/08/2010 17 */ 18 public class CombineBases extends AbstractFunctionEvaluator 166 19 { 20 @Override 21 public void setUp(final ISymbol symbol) 22 { 23 //Give the function an attribute indicating it should not evaluate (hold) its first argument 24 symbol.setAttributes(ISymbol.HOLDFIRST); 25 } 26 27 @Override 28 public IExpr evaluate(final IAST functionList) 29 { 30 //The input AST should have two arguments 31 if(functionList.size() == 2) 32 { 33 Vector<ISymbol> symbols = new Vector<ISymbol>(); 34 symbols.addElement(F.symbol("Power")); 35 symbols.addElement(F.symbol("Times")); 36 37 //If the AST only contains power and multiplication operations 38 if(onlyContains(symbols, functionList)) 39 { 40 IExpr noPowers = functionList.getAt(1); //The second argument should be the AST 41 IExpr combinedBases; 42 Vector<IExpr> powers = getAllHead(F.symbol("Power"), functionList, new Vector<IExpr>()); //Find and store all powers in the AST 43 //If more than one power was found 44 if(powers.size() > 1) 45 { 46 IExpr temp; 47 //Replace all the found powers in the AST with the number 1 48 for(int power = 0; power < powers.size(); power++) 49 { 50 temp = AST.COPY.replace(noPowers, powers.elementAt(power), F.C1); 51 if(temp != null) 52 { 53 noPowers = temp; 54 } 55 } 56 //Multiply all AST elements with the bases of all the found powers 57 combinedBases = noPowers.multiply(powers.firstElement().getAt(1)); 58 for(int power = 1; power < powers.size(); power++) 59 { 60 if(F.equals(F.eval(powers.elementAt(power - 1).getAt(2)), F.eval(powers.elementAt(power).getAt(2)))) 61 { 62 combinedBases = combinedBases.multiply(powers.elementAt(power).getAt(1)); 63 } 64 else 65 { 66 return null; 67 } 68 } 69 combinedBases = F.eval(combinedBases); 167 70 //Hold and return the result of the multiplication to the shared exponent 71 return F.Hold(combinedBases.power(powers.firstElement().getAt(2))); 72 } 73 } 74 /*if(functionList.getAt(1).isAST("Times")) 75 { 76 AST times = (AST) functionList.getAt(1); 77 if(times.getAt(1).isAST("Power") && times.getAt(2).isAST("Power")) 78 { 79 AST power1 = (AST) times.getAt(1); 80 AST power2 = (AST) times.getAt(2); 81 if(F.equals(F.eval(power1.getAt(2)), F.eval(power2.getAt(2)))) 82 { 83 IExpr combinedBases = power1.getAt(1).multiply(power2.getAt(1)); 84 return F.Hold(combinedBases.power(power1.getAt(2))); 85 } 86 } 87 }*/ 88 } 89 return null; 90 } 91 92 /** 93 * Returns all occurrences of the function with the specified head within the given {@link IAST}. 94 * 95 * @param head an {@link ISymbol} of a function to find and return all occurrences of. 96 * @param functionList an <code>IAST</code> to search through and return functions with the specified head. 97 * @param results a {@link Vector} of <code>IExpr</code> instances containing the found function occurrences in the given <code>IAST</code> with the specified head. 98 * @return a <code>Vector</code> of <code>IExpr</code> instances containing the found function occurrences in the given <code>IAST</code> with the specified head. 99 */ 100 private Vector<IExpr> getAllHead(final ISymbol head, final IAST functionList, final Vector<IExpr> results) 101 { 102 //For all the AST elements 103 for(int argument = 1; argument < functionList.size(); argument++) 104 { 105 //If the element is a function with the specified head 106 if(functionList.getAt(argument).isAST(head)) 107 { 108 results.addElement(functionList.getAt(argument)); //Add it to the results 109 } 110 //Otherwise if it is another AST 111 else if(functionList.getAt(argument) instanceof AST) 112 { 113 //Recursively call this function passing the given head, it and the current results as parameters 114 getAllHead(head, (AST) functionList.getAt(argument), results); 115 } 116 } 168 117 return results; 118 } 119 120 /** 121 * Returns true if the given <code>IAST</code> only contains <code>ISymbol</code> instances that are within the given <code>Vector</code>. 122 * 123 * @param heads a <code>Vector</code> of <code>ISymbol</code> instances to see if the given <code>IAST</code> only contains. 124 * @param functionList an <code>IAST</code> to search through to see if it only contains the specified <code>ISymbol</code> instances. 125 * @return true if the given <code>IAST</code> only contains <code>ISymbol</code> instances that are within the given <code>Vector</code>. 126 */ 127 private boolean onlyContains(final Vector<ISymbol> heads, final IAST functionList) 128 { 129 //For all the AST elements 130 for(int argument = 1; argument < functionList.size(); argument++) 131 { 132 //If the element is an AST 133 if(functionList.getAt(argument) instanceof AST) 134 { 135 //If the AST head is not in the given Vector 136 if(!heads.contains(functionList.getAST(argument).head())) 137 { 138 return false; 139 } 140 //If the AST does not only contain heads in the given vector (recursive call to this function) 141 if(!onlyContains(heads, (AST)functionList.getAt(argument))) 142 { 143 return false; 144 } 145 } 146 } 147 return true; 148 } 149 } 9.13.3.8 Rationalise.java 1 package org.matheclipse.root.rationalisation; 2 3 import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; 11 12 /** 13 * Rationalises the exponent of a power if it is in decimal format. 14 * 15 * @author Richard Andrew Mealing 16 * @version 08/08/2010 17 */ 18 public class Rationalise extends AbstractFunctionEvaluator 19 { 20 public void setUp(final ISymbol symbol) 21 { 22 //Give the function an attribute indicating it should not evaluate (hold) its first argument 23 symbol.setAttributes(ISymbol.HOLDFIRST); 169 24 } 25 26 @Override 27 public IExpr evaluate(final IAST functionList) 28 { 29 //The input AST should have 2 arguments 30 if(functionList.size() == 2) 31 { 32 //If the AST second argument is a power function 33 if(functionList.getAt(1).isAST("Power")) 34 { 35 AST power = (AST) functionList.getAt(1); 36 IExpr base = power.getAt(1); 37 IExpr exponent = power.getAt(2); 38 if(exponent.isNumber()) 39 { 40 IExpr exponentValue = F.evaln(exponent); //Evaluate the exponent to ensure it is in decimal format 41 IFraction exponentFraction = F.fraction(Double.parseDouble(exponentValue.toString())); //Convert the exponent into a fractional value 42 //If the decimal value equals the fractional value (meaning it can be rationalised) 43 if(F.equals(exponentValue, F.evaln(exponentFraction))) 44 { 45 //If the base is a number 46 if(base.isNumber()) 47 { 48 //Try to rationalise the base as well 49 IExpr baseValue = F.evaln(base); 50 IFraction baseFraction = F.fraction(Num.valueOf(baseValue.toString())); 51 if(F.equals(baseValue, F.evaln(baseFraction))) 52 { 53 return F.eval(F.symbol("ReduceRoot"), baseFraction.power(exponentFraction)); //Try to reduce the power to an irreducible form 54 } 55 } 56 else 57 { 58 return F.Hold(base.power(exponentFraction)); //Hold and return the base to the fractional exponent 59 } 60 } 61 } 62 } 63 } 64 return null; 65 } 66 } 9.13.3.9 RationaliseBinomial.java 1 package org.matheclipse.root.rationalisation; 2 3 import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; 10 11 /** 170 12 * Rationalises a fraction with a binomial expression containing one or two square roots as its denominator. 13 * 14 * @author Richard Andrew Mealing 15 * @version 08/08/2010 16 */ 17 public class RationaliseBinomial extends AbstractFunctionEvaluator 18 { 19 public void setUp(final ISymbol symbol) 20 { 21 //Give the function an attribute indicating it should not evaluate (hold) its first argument 22 symbol.setAttributes(ISymbol.HOLDFIRST); 23 } 24 25 @Override 26 public IExpr evaluate(final IAST functionList) 27 { 28 //The input AST should have 2 arguments 29 if(functionList.size() == 2) 30 { 31 //If the AST second argument is a power function 32 if(functionList.getAt(1).isAST("Power")) 33 { 34 AST power = (AST) functionList.getAt(1); 35 //If the power's second argument is a plus function and its third argument is -1 (i.e. it is of the form (x+y)^-1) 36 if(power.getAt(1).isAST("Plus") && power.getAt(2).isSame(IntegerSym.valueOf(-1))) 37 { 38 AST denominator = (AST) power.getAt(1); 39 //The new numerator will be the conjugate of the denominator 40 IExpr numerator = F.eval(denominator.getAt(1).plus(denominator.getAt(2).negative())); 41 //The new denominator will be the conjugate multiplied by the old denominator 42 IExpr firstFactor = F.eval(denominator.getAt(1).multiply(denominator.getAt(1))); 43 IExpr secondFactor = F.eval(denominator.getAt(2).multiply(denominator.getAt(2).negative())); 44 IExpr newDenominator = F.eval(firstFactor.plus(secondFactor)); 45 return numerator.multiply(newDenominator.inverse()); 46 } 47 } 48 } 49 return null; 50 } 51 } 9.13.3.10 RationaliseMonomial.java 1 package org.matheclipse.root.rationalisation; 2 3 import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; 10 11 /** 12 * Rationalises a fraction with a monomial surd as its denominator. 13 * 14 * @author Richard Andrew Mealing 171 15 * @version 08/08/2010 16 */ 17 public class RationaliseMonomial extends AbstractFunctionEvaluator 18 { 19 public void setUp(final ISymbol symbol) 20 { 21 //Give the function an attribute indicating it should not evaluate (hold) its first argument 22 symbol.setAttributes(ISymbol.HOLDFIRST); 23 } 24 25 @Override 26 public IExpr evaluate(final IAST functionList) 27 { 28 //The input AST should have 2 arguments 29 if(functionList.size() == 2) 30 { 31 AST power = null; 32 IExpr factor = null; 33 //If the AST second argument is a power function 34 if(functionList.getAt(1).isAST("Power")) 35 { 36 power = (AST) functionList.getAt(1); 37 } 38 //Otherwise if the AST second argument is a times function 39 else if(functionList.getAt(1).isAST("Times")) 40 { 41 AST times = (AST) functionList.getAt(1); 42 //Find the power function and store it along with the multiplication factor 43 if(times.getAt(1).isAST("Power")) 44 { 45 power = (AST) times.getAST(1); 46 factor = times.getAt(2); 47 } 48 else if(times.getAt(2).isAST("Power")) 49 { 50 factor = times.getAt(1); 51 power = (AST) times.getAST(2); 52 } 53 } 54 if(power != null) 55 { 56 if(power.getAt(2).isNumber()) 57 { 58 IExpr base = power.getAt(1); 59 IExpr exponentValue = F.evaln(power.getAt(2)); 60 IFraction fraction = (IFraction) F.eval(F.symbol("Rationalize"), exponentValue); 61 //If the exponent can be rationalised and it is negative 62 if(F.equals(exponentValue, F.evaln(fraction)) && fraction.isNegative()) 63 { 64 power = (AST)F.Hold(base).power(fraction); 65 //The factor to multiply numerator and denominator by to rationalise the fraction will be the monomial surd base to the power of 1 - the monomial surd exponent 66 IExpr rationaliser = F.Hold(base).power(fraction.getDenominator().inverse().negative()).power(fraction.ge 172 tDenominator().minus(F.integer(1))); 67 IExpr numerator = F.eval(rationaliser.inverse()); 68 numerator = F.eval(AST.COPY.replace(numerator, F.Hold(base), base)); //"Let go" of the base so it can be evaluated 69 IExpr denominator = F.eval(power.multiply(rationaliser)); 70 denominator = F.eval(AST.COPY.replace(denominator, F.Hold(base), base)); //"Let go" of the base so it can be evaluated 71 if(factor != null) 72 { 73 denominator = denominator.multiply(factor); 74 } 75 if(base.isNumber()) 76 { 77 return numerator.multiply(denominator); 78 } 79 else 80 { 81 return F.Hold(numerator.multiply(denominator)); //Hold and return the numerator multiplied by the denominator 82 } 83 } 84 } 85 } 86 } 87 return null; 88 } 89 } 9.13.3.11 ReduceRoot.java 1 package org.matheclipse.root.rationalisation; 2 3 import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; 9 10 /** 11 * Reduces a surd to an irreducible form. 12 * 13 * @author Richard Andrew Mealing 14 * @version 08/08/2010 15 */ 16 public class ReduceRoot extends AbstractFunctionEvaluator 17 { 18 public void setUp(final ISymbol symbol) 19 { 20 //Give the function an attribute indicating it should not evaluate (hold) its first argument 21 symbol.setAttributes(ISymbol.HOLDFIRST); 22 } 23 24 @Override 25 public IExpr evaluate(final IAST functionList) 26 { 27 //The input AST should have 2 arguments 28 if(functionList.size() == 2) 29 { 30 //If the AST second argument is a power function 31 if(functionList.getAt(1).isAST("Power")) 32 { 33 AST power = (AST) functionList.getAt(1); 173 34 IExpr base = power.getAt(1); 35 IExpr exponent = power.getAt(2); 36 if(base.isNumber() && exponent.isNumber()) 37 { 38 //Try to rationalise and return the power (i.e. in the case of a whole power like 2^2 return 4) 39 IExpr powerValue = F.evaln(power); 40 IExpr powerRationalValue = F.eval(F.symbol("Rationalize"), powerValue); 41 if(F.equals(powerValue, F.evaln(powerRationalValue))) 42 { 43 return powerRationalValue; 44 } 45 else 46 { 47 //Try to rationalise the exponent in the power 48 IExpr exponentValue = F.evaln(exponent); 49 IExpr fraction = F.eval(F.symbol("Rationalize"), exponentValue); 50 if(F.equals(exponentValue, F.evaln(fraction))) 51 { 52 return F.eval(base.power(fraction)); 53 /*IExpr denominator = fraction.getDenominator(); 54 Num log = (Num) F.eval(F.symbol("Log"), denominator, base); 55 ISignedNumber logInteger = log.floor(); 56 IExpr high = null; 57 Num div = null; 58 if(!logInteger.inverse().isSame(Num.valueOf(0))) 59 { 60 while(!logInteger.equalsInt(1)) 61 { 62 high = F.evaln(logInteger.power(denominator)); 63 div = (Num) F.evaln(base.div(high)); 64 if(div.equalsInt(div.intValue())) 65 { 66 return F.Hold(Num.valueOf(((Num)F.evaln(high.power(exponent))).intValue()).multiply(div.pow er(exponent))); 67 } 68 logInteger = (ISignedNumber) F.evaln(logInteger.minus(Num.valueOf(1))); 69 } 70 } 71 return F.Hold(base.power(fraction));*/ 72 } 73 } 74 } 75 } 76 } 77 return null; 78 } 79 } 9.13.3.12 RemoveAllHead.java 1 package org.matheclipse.util; 2 3 import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator; 174 8 9 /** 10 * Removes all occurrences of a specified unary function from a specified {@link IAST} while keeping each occurrence's argument. 11 * 12 * @author Richard Andrew Mealing 13 * @version 08/08/2010 14 */ 15 public class RemoveAllHead extends AbstractFunctionEvaluator 16 { 17 @Override 18 public void setUp(final ISymbol symbol) 19 { 20 //Give the function an attribute indicating it should not evaluate (hold) any of its arguments 21 symbol.setAttributes(ISymbol.HOLDALL); 22 } 23 24 @Override 25 public IExpr evaluate(final IAST functionList) 26 { 27 //The input AST should have 3 arguments 28 if(functionList.size() == 3) 29 { 30 //The second argument should be a function head/name 31 if(functionList.getAt(1) instanceof ISymbol) 32 { 33 ISymbol head = (ISymbol) functionList.getAt(1); 34 //The third argument should be an AST 35 if(functionList.getAt(2) instanceof IAST) 36 { 37 return removeAllHead(head, functionList.getAST(2)); 38 } 39 } 40 } 41 return null; 42 } 43 44 /** 45 * Removes all occurrences of a specified unary function from a specified <code>IAST</code> while keeping each occurrence's argument. 46 * 47 * @param head an {@link ISymbol} representing a unary function head (name) to remove all occurrences from the specified <code>IAST</code> while keeping each occurrence's argument 48 * @param functionList an <code>IAST</code> to remove all occurrences of a given function from while keeping each occurrence's argument 49 */ 50 private IExpr removeAllHead(final ISymbol head, final IAST functionList) 51 { 52 for(int argument = 1; argument < functionList.size(); argument++) 53 { 54 //If the argument is an AST 55 if(functionList.getAt(argument) instanceof AST) 56 { 57 //Set it to the result of recursively calling this function with the given head and it as parameters 58 functionList.set(argument, removeAllHead(head, functionList.getAST(argument))); 175 59 } 60 } 61 //If the given AST is an instance of the given function return its argument otherwise return it 62 if(functionList.isAST(head)) 63 { 64 return functionList.getAt(1); 65 } 66 else 67 { 68 return functionList; 69 } 70 } 71 } 176
© Copyright 2026 Paperzz