Simplifying Numerical Expressions

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>&#928; (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 &#060; 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) + " &#8594; " + 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) + " &#8594; " + 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) + " &#8594; " + 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) + " &#8594; " + 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) + " &#8594; " + 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) + " &#8594; " + 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) + " &#8594; " + 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) + " &#8594; " + 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) + " &#8594; " + 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 + "
&#8594; " + 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, "-"); //+ " &#8594; ";
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(/&quot;/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(/&quot;#&quot;/,
escapeFunctionNames(input, functions).replace(/"/g, "&quot
;")) + "<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(/&quot;#&quot;/,
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