summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/luacas/tex/algebra/luacas-rootexpression.lua
blob: a66182579a4591ce57ff31295265bf8255e8e4c3 (plain)
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
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
--- @class RootExpression
--- An expression that represents the solutions to expression = 0.
--- @field expression Expression
RootExpression = {}
__RootExpression = {}

----------------------------
-- Instance functionality --
----------------------------

--- Creates a new root expression with the given expression.
--- @param expression Expression
--- @return RootExpression
function RootExpression:new(expression)
    local o = {}
    local __o = Copy(__ExpressionOperations)

    o.expression = Copy(expression)

    __o.__index = RootExpression
    __o.__tostring = function(a)
        return 'Root Of: (' .. tostring(a.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 rungs this anyway
        if not b:type() == RootExpression then
            return false
        end
        return a.expression == b.expression
    end
    o = setmetatable(o, __o)

    return o
end

--- @return Expression
function RootExpression:autosimplify(subpart)
    local simplified = self.expression:autosimplify()
    local simplified, ispoly = simplified:topolynomial()

    if simplified:isconstant() then
        -- 0 = 0 is always true (obviously).
        return simplified == simplified:zero()
    end

    if ispoly then
        if simplified.degree == Integer.zero() then
            return simplified == simplified:zero()
        end
        if simplified.degree == Integer.one() then
            return {-simplified.coefficients[0] / simplified.coefficients[1]}
        end
        if simplified.degree == Integer(2) then
            local a = simplified.coefficients[2]
            local b = simplified.coefficients[1]
            local c = simplified.coefficients[0]
            -- This is a hack until we can get more expression manipulation working, but that's okay.
            if subpart then
                c = (c - subpart):autosimplify()
            end
            return {((-b + sqrt(b^Integer(2) - Integer(4) * a * c)) / (Integer(2) * a)):autosimplify(),
                    ((-b - sqrt(b^Integer(2) - Integer(4) * a * c)) / (Integer(2) * a)):autosimplify()}
        end
        if simplified.degree == Integer(3) then
            local a = simplified.coefficients[3]
            local b = simplified.coefficients[2]
            local c = simplified.coefficients[1]
            local d = simplified.coefficients[0]
            -- This is a hack until we can get more expression manipulation working, but that's okay.
            if subpart then
                d = (d - subpart):autosimplify()
            end

            local delta0 = (b^Integer(2) - Integer(3)*a*c):autosimplify()
            local delta1 = (Integer(2) * b^Integer(3) - Integer(9)*a*b*c+Integer(27)*a^Integer(2)*d):autosimplify()

            local C = sqrt((delta1 + sqrt(delta1 ^ Integer(2) - Integer(4) * delta0 ^ Integer(3))) / Integer(2), Integer(3)):autosimplify()

            if C == Integer.zero() then
                C = sqrt((delta1 - sqrt(delta1 ^ Integer(2) - Integer(4) * delta0 ^ Integer(3))) / Integer(2), Integer(3)):autosimplify()
            end

            if C == Integer.zero() then
                C = (-b/(Integer(3)*a)):autosimplify()
            end

            local eta = ((Integer(-1) + sqrt(Integer(-3))) / Integer(2)):autosimplify()

            return {((-Integer.one() / (Integer(3) * a)) * (b + C + delta0 / C)):autosimplify(),
                    ((-Integer.one() / (Integer(3) * a)) * (b + C*eta + delta0 / (C*eta))):autosimplify(),
                    ((-Integer.one() / (Integer(3) * a)) * (b + C*eta^Integer(2) + delta0 / (C*eta^Integer(2)))):autosimplify()}
        end
    end
    if ispoly then
        simplified = simplified:autosimplify()
    end
    if subpart then
        simplified = (simplified - subpart):autosimplify()
    end
    return {RootExpression(simplified)}
end

--- @return table<number, Expression>
function RootExpression:subexpressions()
    return {self.expression}
end

--- @param subexpressions table<number, Expression>
--- @return RootExpression
function RootExpression:setsubexpressions(subexpressions)
    return RootExpression(subexpressions[1])
end

--- @param other Expression
--- @return boolean
function RootExpression:order(other)
    --- TODO: Fix ordering on new expression types
    if other:type() ~= RootExpression then
        return false
    end

    return self.expression:order(other.expression)
end

--- @return string
function RootExpression:tolatex()
    return '\\operatorname{RootOf}\\left(' .. self.expression:tolatex() .. '\\right)'
end

-----------------
-- Inheritance --
-----------------
__RootExpression.__index = CompoundExpression
__RootExpression.__call = RootExpression.new
RootExpression = setmetatable(RootExpression, __RootExpression)