summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua')
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua186
1 files changed, 186 insertions, 0 deletions
diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua
new file mode 100644
index 0000000000..07a9db84d1
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua
@@ -0,0 +1,186 @@
+--- @class Logarithm
+--- An expression for the logarithm of an expression with respect to another.
+--- Currently, logarithms are not being evaluated since we are just doing symbolic computation.
+--- @field base Expression
+--- @field expression Expression
+Logarithm = {}
+__Logarithm = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new logarithm expression with the given symbol and expression.
+--- @param base Expression
+--- @param expression Expression
+--- @
+function Logarithm:new(base, expression)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.base = Copy(base)
+ o.expression = Copy(expression)
+
+ __o.__index = Logarithm
+ __o.__tostring = function(a)
+ return 'log(' .. tostring(base) .. ', ' .. tostring(expression) .. ')'
+ 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() == Logarithm then
+ return false
+ end
+ return a.base == b.base and a.expression == b.expression
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return Expression
+function Logarithm:evaluate()
+
+ if (self.base:isconstant() and (self.base <= Integer.zero() or self.base == Integer.one())) or
+ (self.expression:isconstant() and self.expression <= Integer.zero()) then
+ error("Arithmetic error: division by zero")
+ end
+
+ if not self.base:isconstant() or not self.expression:isconstant() then
+ return self
+ end
+
+ local power = Integer.one()
+ local base = self.base
+ if base:type() == Integer then
+ local pp, b, p = base:isperfectpower()
+ if pp then
+ base = b
+ power = p
+ end
+ end
+
+ if base:type() == Rational then
+ local ppn, bn, pn = base.numerator:isperfectpower()
+ local ppd, bd, pd = base.denominator:isperfectpower()
+ if base.numerator == Integer.one() then
+ ppn = true
+ bn = Integer.one()
+ pn = pd
+ end
+ if ppn and ppd and pn == pd then
+ base = bn / bd
+ power = pn
+ end
+ end
+
+ local result = Integer.one()
+ local expression = self.expression
+ local sign = Integer.one()
+ if base < Integer.one() then
+ base = Integer.one() / base
+ sign = -sign
+ end
+
+
+ local current = base
+ while current < expression do
+ current = current * base
+ result = result + Integer.one()
+ end
+ if current == expression then
+ return sign * result / power
+ else
+ while current > expression do
+ current = current / base
+ result = result - Integer.one()
+ end
+ if current == expression then
+ return sign * result / power
+ end
+ end
+
+ return self
+end
+
+--- @return Expression
+function Logarithm:autosimplify()
+
+ local base = self.base:autosimplify()
+ local expression = self.expression:autosimplify()
+
+ local evaluated = Logarithm(base, expression):evaluate()
+ if evaluated:type() ~= Logarithm then
+ return evaluated
+ end
+
+ -- Uses the property that log(b, 1) = 0
+ if expression == Integer.one() then
+ return Integer.zero()
+ end
+
+ -- Uses the property that log(b, b) = 1
+ if expression == base then
+ return Integer.one()
+ end
+
+ -- Uses the propery that log(b, x^y) = y * log(b, x)
+ if expression.operation == BinaryOperation.POW then
+ return BinaryOperation.MULEXP({expression.expressions[2], Logarithm(base, expression.expressions[1])}):autosimplify()
+ end
+
+ if expression:type() == Rational and expression.numerator == Integer.one() then
+ return (-Logarithm(base, expression.denominator)):autosimplify()
+ end
+
+ -- Our expression cannot be simplified
+ return Logarithm(base, expression)
+end
+
+--- @return Expression
+function Logarithm:expand()
+ return Logarithm(self.base:expand(), self.expression:expand()):autosimplify()
+end
+
+--- @return table<number, Expression>
+function Logarithm:subexpressions()
+ return {self.base, self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return Logarithm
+function Logarithm:setsubexpressions(subexpressions)
+ return Logarithm(subexpressions[1], subexpressions[2])
+end
+
+--- @param other Expression
+--- @return boolean
+function Logarithm:order(other)
+ return FunctionExpression("log", {self.base, self.expression}):order(other)
+end
+
+--- @return string
+function Logarithm:tolatex()
+ if self.base == E then
+ return '\\ln\\mathopen{}\\left(' .. self.expression:tolatex() .. '\\right)'
+ end
+ return '\\log_' .. self.base:tolatex() .. '\\mathopen{}\\left(' .. self.expression:tolatex() .. '\\right)'
+end
+
+-----------------
+-- Inheritance --
+-----------------
+__Logarithm.__index = CompoundExpression
+__Logarithm.__call = Logarithm.new
+Logarithm = setmetatable(Logarithm, __Logarithm)
+
+----------------------
+-- Static constants --
+----------------------
+
+LOG = function(base, expression)
+ return Logarithm(base, expression)
+end
+
+LN = function(expression)
+ return Logarithm(E, expression)
+end \ No newline at end of file