summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/luacas/tex/algebra/equation.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/latex/luacas/tex/algebra/equation.lua')
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/equation.lua183
1 files changed, 183 insertions, 0 deletions
diff --git a/macros/luatex/latex/luacas/tex/algebra/equation.lua b/macros/luatex/latex/luacas/tex/algebra/equation.lua
new file mode 100644
index 0000000000..4e81d5ca95
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/equation.lua
@@ -0,0 +1,183 @@
+--- @class Equation
+--- An expression that represents an equation of the form lhs = rhs.
+--- @field lhs Expression
+--- @field rhs Expression
+Equation = {}
+__Equation = {}
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+--- Attempts to isolate the variable var in lhs by moving expressions to rhs. Ony performs a single step.
+--- @param lhs Expression
+--- @param rhs Expression
+--- @param var SymbolExpression
+--- @return Expression, Expression
+function Equation.isolatelhs(lhs, rhs, var)
+ if lhs:type() == BinaryOperation then
+ local stay = Integer.zero()
+ local switch = Integer.zero()
+ if lhs.operation == BinaryOperation.ADD then
+ for _, exp in ipairs(lhs:subexpressions()) do
+ if exp:freeof(var) then
+ switch = switch + exp
+ else
+ stay = stay + exp
+ end
+ end
+ if switch == Integer.zero() then
+ lhs = lhs:factor() -- TODO: Replace with collect for efficiency reasons
+ else
+ return stay:autosimplify(), (rhs - switch):autosimplify()
+ end
+ end
+ if lhs.operation == BinaryOperation.MUL then
+ stay = Integer.one()
+ switch = Integer.one()
+ for _, exp in ipairs(lhs:subexpressions()) do
+ if exp:freeof(var) then
+ switch = switch * exp
+ else
+ stay = stay * exp
+ end
+ end
+ return stay:autosimplify(), (rhs / switch):autosimplify()
+ end
+ if lhs.operation == BinaryOperation.POW then
+ if lhs:subexpressions()[1]:freeof(var) then
+ return lhs:subexpressions()[2]:autosimplify(), Logarithm(lhs:subexpressions()[1], rhs):autosimplify()
+ elseif lhs:subexpressions()[2]:freeof(var) then
+ return lhs:subexpressions()[1]:autosimplify(), (rhs ^ (Integer.one()/lhs:subexpressions()[2])):autosimplify()
+ end
+ end
+ elseif lhs:type() == Logarithm then
+ if lhs.base:freeof(var) then
+ return lhs.expression:autosimplify(), (lhs.base ^ rhs):autosimplify()
+ elseif lhs.expression:freeof(var) then
+ return lhs.base:autosimplify(), (lhs.expression ^ (Integer.one()/rhs)):autosimplify()
+ end
+ elseif lhs:type() == TrigExpression then
+ return lhs.expression:autosimplify(), TrigExpression(TrigExpression.INVERSES[lhs.name], rhs):autosimplify()
+ end
+
+ return lhs, rhs
+end
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new equation with the given expressions.
+--- @param lhs Expression
+--- @param rhs Expression
+--- @return Equation
+function Equation:new(lhs, rhs)
+
+ if lhs:type() == Equation or rhs:type() == Equation then
+ error("Sent parameter of wrong type: cannot nest equations or inequalities")
+ end
+
+ local o = {}
+ local __o = Copy(__ExpressionOperations) -- TODO: Ensure only one metatable for each instance of a class
+
+ o.lhs = lhs
+ o.rhs = rhs
+
+ __o.__index = Equation
+ __o.__tostring = function(a)
+ return tostring(a.lhs) .. ' = ' .. tostring(a.rhs)
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway
+ if not b:type() == Equation then
+ return false
+ end
+ return a.lhs == b.lhs and a.rhs == b.rhs
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- Evaluation in this case just checks for structural equality, or guarenteed inequality in the case of constants
+--- @return Equation|boolean
+function Equation:evaluate()
+ if self.lhs == self.rhs then
+ return true -- TODO: Add Boolean Expressions
+ end
+ if self.lhs:isconstant() and self.rhs:isconstant() and self.lhs ~= self.rhs then
+ return false
+ end
+ return self
+end
+
+--- @return Equation|boolean
+function Equation:autosimplify()
+ local lhs = self.lhs:autosimplify()
+ local rhs = self.rhs:autosimplify()
+
+ return Equation(lhs, rhs):evaluate()
+end
+
+--- @return table<number, Expression>
+function Equation:subexpressions()
+ return {self.lhs, self.rhs}
+end
+
+--- Attempts to solve the equation for a particular variable.
+--- @param var SymbolExpression
+--- @return Equation
+function Equation:solvefor(var)
+ local lhs = self.lhs
+ local rhs = self.rhs
+
+ if lhs:freeof(var) and rhs:freeof(var) then
+ return self
+ end
+
+ -- Check for monovariate polynomial expressions
+ local root = (lhs - rhs):autosimplify()
+ local poly, status = root:expand():topolynomial()
+ if status then
+ -- TODO: Add Set expressions
+ return Equation(var, poly:roots()[1])
+ end
+
+ local newlhs, newrhs = root, Integer(0)
+ local oldlhs
+ while newlhs ~= var and oldlhs ~= newlhs do
+ oldlhs = newlhs
+ newlhs, newrhs = Equation.isolatelhs(newlhs, newrhs, var)
+ end
+
+ return Equation(newlhs, newrhs)
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return Equation
+function Equation:setsubexpressions(subexpressions)
+ return Equation(subexpressions[1], subexpressions[2])
+end
+
+--- @param other Expression
+--- @return boolean
+function Equation:order(other)
+ if other:isatomic() then
+ return false
+ end
+
+ return self.lhs:order(other)
+end
+
+--- @return string
+function Equation:tolatex()
+ return self.lhs:tolatex() .. '=' .. self.rhs:tolatex()
+end
+
+-----------------
+-- Inheritance --
+-----------------
+__Equation.__index = CompoundExpression
+__Equation.__call = Equation.new
+Equation = setmetatable(Equation, __Equation) \ No newline at end of file