--- @class Equation --- An expression that represents an equation of the form lhs = rhs. --- @field lhs Expression --- @field rhs Expression Equation = {} local __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 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 --- @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)