diff options
Diffstat (limited to 'macros/luatex/latex/luacas/tex/core/expression.lua')
-rw-r--r-- | macros/luatex/latex/luacas/tex/core/expression.lua | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/macros/luatex/latex/luacas/tex/core/expression.lua b/macros/luatex/latex/luacas/tex/core/expression.lua new file mode 100644 index 0000000000..8ff4659f79 --- /dev/null +++ b/macros/luatex/latex/luacas/tex/core/expression.lua @@ -0,0 +1,280 @@ +--- @class Expression +--- Interface for an arbitrary mathematical expression. +Expression = {} + +---------------------- +-- Required methods -- +---------------------- + +--- Evaluates the current expression recursively by evaluating each sub-expression. +--- @return Expression +function Expression:evaluate() + error("Called unimplemented method : evaluate()") +end + +--- Performs automatic simplification of an expression. Called on every expression before being output from the CAS. +--- @return Expression +function Expression:autosimplify() + error("Called unimplemented method : autosimplify()") +end + +--- Performs more rigorous simplification of an expression. Checks different equivalent forms and determines the 'smallest' expresion. +--- @return Expression +function Expression:simplify() + local me = self:unlock():autosimplify() + local results = {} + for index, expression in ipairs(me:subexpressions()) do + results[index] = expression:simplify() + end + me = me:setsubexpressions(results) + + local out = me + local minsize = self:size() + + local test = me:expand() + if test:size() < minsize then + out = test + minsize = test:size() + end + + test = me:factor() + if test:size() < minsize then + out = test + minsize = test:size() + end + + return out +end + +--- Changes the autosimplify behavior of an expression depending on its parameters. +--- THIS METHOD MUTATES THE OBJECT IT ACTS ON. +--- @param mode number +--- @param permanent boolean +--- @param recursive boolean +--- @return Expression +function Expression:lock(mode, permanent, recursive) + function self:autosimplify() + if not permanent then + self.autosimplify = nil + end + + if mode == Expression.NIL then + return self + elseif mode == Expression.SUBS then + local results = {} + for index, expression in ipairs(self:subexpressions()) do + results[index] = expression:autosimplify() + end + self = self:setsubexpressions(results, true) -- TODO: Add mutate setsubexpressions + return self + end + end + if recursive then + for _, expression in ipairs(self:subexpressions()) do + expression:lock(mode, permanent, recursive) + end + end + return self +end + +--- Frees any locks on expressions. +--- THIS METHOD MUTATES THE OBJECT IT ACTS ON. +--- @param recursive boolean +--- @return Expression +function Expression:unlock(recursive) + self.autosimplify = nil + if recursive then + for _, expression in ipairs(self:subexpressions()) do + expression:unlock(recursive) + end + end + return self +end + +--- Returns a list of all subexpressions of an expression. +--- @return table<number, Expression> +function Expression:subexpressions() + error("Called unimplemented method : subexpressions()") +end + +--- Returns the total number of atomic and compound expressions that make up an expression, or the number of nodes in the expression tree. +--- @return Integer +function Expression:size() + local out = Integer.one() + for _, expression in ipairs(self:subexpressions()) do + out = out + expression:size() + end + return out +end + +--- Returns a copy of the original expression with each subexpression substituted with a new one, or a mutated version if mutate is true. +--- @param subexpressions table<number, Expression> +--- @param mutate boolean +--- @return Expression +function Expression:setsubexpressions(subexpressions, mutate) + error("Called unimplemented method : setsubexpressions()") +end + +--- Determines whether or not an expression contains a particular symbol. +--- @param symbol SymbolExpression +--- @return boolean +function Expression:freeof(symbol) + error("Called unimplemented method : freeof()") +end + +--- Substitutes occurances of specified sub-expressions with other sub-expressions. +--- @param map table<Expression, Expression> +--- @return Expression +function Expression:substitute(map) + error("Called unimplemented method : substitute()") +end + +--- Algebraically expands an expression by turning products of sums into sums of products and expanding powers. +--- @return Expression +function Expression:expand() + return self +end + +--- Attempts to factor an expression by turning sums of products into products of sums, and identical terms multiplied together into factors. +--- @return Expression +function Expression:factor() + return self +end + +--- Attempts to combine an expression by collapsing sums of expressions together into a single factor, e.g. common denominator +--- @return Expression +function Expression:combine() + return self +end + +--- Attempts to collect all occurances of an expression in this expression. +--- @param collect Expression +--- @return Expression +function Expression:collect(collect) + return self +end + +--- Returns all non-constant subexpressions of this expression - helper method for factor. +--- @return table<number, Expression> +function Expression:getsubexpressionsrec() + local result = {} + + for _, expression in ipairs(self:subexpressions()) do + if not expression:isconstant() then + result[#result+1] = expression + end + result = JoinArrays(result, expression:getsubexpressionsrec()) + end + + return result +end + +--- Determines whether an expression is atomic. +--- Atomic expressions are not necessarily constant, since polynomial rings, for instance, are atomic parts that contain symbols. +--- @return boolean +function Expression:isatomic() + error("Called unimplemented method: isatomic()") +end + +--- Determines whether an expression is a constant, i.e., an atomic expression that is not a varaible and cannot be converted into an equivalent compound expression. +--- @return boolean +function Expression:isconstant() + error("Called unimplemented method: isconstant()") +end + +--- Determines whether an expression is a 'proper' real constant, i.e., is free of every varaible. +function Expression:isrealconstant() + if self:isconstant() or self == PI or self == E then + return true + end + + for _, expression in ipairs(self:subexpressions()) do + if not expression:isrealconstant() then + return false + end + end + + return self:type() ~= SymbolExpression +end + +--- Determines whether an expression is a 'proper' complex constant, i.e., is free of every varaible and is of the form a + bI for nonzero a and b. +--- @return boolean +function Expression:iscomplexconstant() + return self:isrealconstant() or (self.operation == BinaryOperation.ADD and #self.expressions == 2 and self.expressions[1]:isrealconstant() + and ((self.expressions[2].operation == BinaryOperation.MUL and #self.expressions[2].expressions == 2 and self.expressions[2].expressions[1]:isrealconstant() and self.expressions[2].expressions[2] == I) + or self.expressions[2] == I)) or (self.operation == BinaryOperation.MUL and #self.expressions == 2 and self.expressions[1]:isrealconstant() and self.expressions[2] == I) +end + +--- A total order on autosimplified expressions. Returns true if self < other. +--- @param other Expression +--- @return boolean +function Expression:order(other) + error("Called unimplemented method: order()") +end + +--- Returns an autosimplified expression as a single-variable polynomial in a ring, if it can be converted. Returns itself otherwise. +--- @return PolynomialRing, boolean +function Expression:topolynomial() + return self, false +end + +--- Converts this expression to LaTeX code. +--- @return string +function Expression:tolatex() + error("Called Unimplemented method: tolatex()") +end + +---------------------- +-- Instance methods -- +---------------------- + +--- Returns the type of the expression, i.e., the table used to create objects of that type. +--- @return table +function Expression:type() + return getmetatable(self).__index +end + +-------------------------- +-- Instance metamethods -- +-------------------------- + +__ExpressionOperations = {} + +__ExpressionOperations.__unm = function(a) + return BinaryOperation.SUBEXP({a}) +end + +__ExpressionOperations.__add = function(a, b) + return BinaryOperation.ADDEXP({a, b}) +end + +__ExpressionOperations.__sub = function(a, b) + return BinaryOperation.SUBEXP({a, b}) +end + +__ExpressionOperations.__mul = function(a, b) + return BinaryOperation.MULEXP({a, b}) +end + +__ExpressionOperations.__div = function(a, b) + return BinaryOperation.DIVEXP({a, b}) +end + +__ExpressionOperations.__pow = function(a, b) + return BinaryOperation.POWEXP({a, b}) +end + +-- For iterating over the subexpressions of a easily. +__ExpressionOperations.__call = function(a, ...) + if a:type() == SymbolExpression then + return FunctionExpression(a, table.pack(...)) + end + return BinaryOperation.MULEXP({a, table.pack(...)[1]}) +end + +---------------------- +-- Static constants -- +---------------------- + +Expression.NIL = 0 +Expression.SUBS = 1
\ No newline at end of file |