summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/luacas/tex/core/binaryoperation/power.lua
blob: 35df72c44016e6903a22fd7857a66b117a1da81c (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
-- Seperates the various binary operations into their own files for readability

--- Automatic simplification of power expressions.
--- @return BinaryOperation
function BinaryOperation:simplifypower()
    local base = self.expressions[1]
    local exponent = self.expressions[2]

    if base:isconstant() and exponent:isconstant() and exponent:getring() ~= Rational:getring() then
        return self:evaluate()
    end

    -- Simplifies i^x for x integer.
    if base == I and exponent:isconstant() and exponent:getring() == Integer:getring() then
        if exponent % Integer(4) == Integer(0) then
            return Integer(1)
        end
        if exponent % Integer(4) == Integer(1) then
            return I
        end
        if exponent % Integer(4) == Integer(2) then
            return Integer(-1)
        end
        if exponent % Integer(4) == Integer(3) then
            return -I
        end
    end

    -- Simplifies complex numbers raised to negative integer powers
    if not base:isrealconstant() and base:iscomplexconstant() and exponent:isconstant() and exponent:getring() == Integer:getring() and exponent < Integer.zero() then
        local a
        local b
        if base.operation == BinaryOperation.MUL then
            a = Integer.zero()
            b = base.expressions[1]
        elseif base.operation == BinaryOperation.ADD and base.expressions[2] == I then
            a = base.expressions[1]
            b = Integer.one()
        else
            a = base.expressions[1]
            b = base.expressions[2].expressions[1]
        end
        return (((a-b*I)/(a^Integer(2)+b^Integer(2)))^(-exponent)):expand():autosimplify()
    end

    -- Uses the property that 0^x = 0 if x does not equal 0
    if base:isconstant() and base == base:zero() then
        return Integer.zero()
    end

    -- Uses the property that 1^x = 1
    if base:isconstant() and base == base:one() then
        return base:one()
    end

    -- Uses the property that x^0 = 1
    if exponent:isconstant() and exponent == exponent:zero() then
        return exponent:one()
    end

    -- Uses the property that x^1 = x
    if exponent:isconstant() and exponent == exponent:one() then
        return base
    end

    -- Uses the property that b ^ (log(b, x)) == x
    if exponent:type() == Logarithm and exponent.base == base then
        return exponent.expression
    end

    -- Uses the property that b ^ (a * log(b, x)) == x ^ a
    if exponent.operation == BinaryOperation.MUL then
        local x
        local rest = Integer.one()
        for _, expression in ipairs(exponent.expressions) do
            if expression:type() == Logarithm and expression.base == base and not log then
                x = expression.expression
            else
                rest = rest * expression
            end
        end
        if x then
            return (x ^ rest):autosimplify()
        end
    end

    -- Uses the property that (x^a)^b = x^(a*b)
    if not base:isatomic() and base.operation == BinaryOperation.POW and exponent:isconstant() then
        base, exponent = base.expressions[1], BinaryOperation(BinaryOperation.MUL, {exponent, base.expressions[2]}):autosimplify()
        return BinaryOperation(BinaryOperation.POW, {base, exponent}):autosimplify()
    end

    -- Uses the property that (x_1*x_2*...*x_n)^a = x_1^a*x_2^a*..x_n^a if a is an integer
    if base.operation == BinaryOperation.MUL and exponent:type() == Integer then
        local results = {}
        for index, expression in ipairs(base.expressions) do
            results[index] = BinaryOperation(BinaryOperation.POW, {expression, exponent}):autosimplify()
        end
        return BinaryOperation(BinaryOperation.MUL, results):autosimplify()
    end

    -- Uses the property that sqrt(x,r)^d == sqrt(x,r/d)
    if base:type() == SqrtExpression and exponent:type() == Integer and exponent > Integer.zero() then
        local root = base.root
        local expr = base.expression
        local comm = Integer.gcd(root,exponent)
        root = root / comm
        local expo = exponent / comm
        expr = expr ^ expo
        return SqrtExpression(expr,root):autosimplify()
    end

    -- Rationalizing SqrtExpressions
    if base:type() == SqrtExpression and exponent:type() == Integer and base.expression:type() == Integer and exponent < Integer.zero() then
        local root = base.root
        local expr = base.expression
        local result =  (SqrtExpression(expr ^ (root - Integer.one()),root) / expr) ^ exponent:neg()
        return result:autosimplify()
    end

    if base:isconstant() and exponent:isconstant() and exponent:getring() == Rational.getring() then
        return self --:simplifyrationalpower()
    end

    -- Our expression cannot be simplified
    return self
end

-- Automatic simplification of rational power expressions
function BinaryOperation:simplifyrationalpower()
    local base = self.expressions[1]
    local exponent = self.expressions[2]

    if base:getring() == Rational.getring() then
        return (BinaryOperation(BinaryOperation.POW, {base.numerator, exponent}):simplifyrationalpower()) /
                (BinaryOperation(BinaryOperation.POW, {base.denominator, exponent}):simplifyrationalpower())
    end

    if base == Integer(-1) then
        if exponent == Integer(1) / Integer(2) then
            return I
        end

        return self
    end

    local primes = base:primefactorization()

    if primes.expressions[1] and not primes.expressions[2] then
        local primeexponent = primes.expressions[1].expressions[2]
        local primebase = primes.expressions[1].expressions[1]
        local newexponent = primeexponent * exponent
        local integerpart
        if newexponent.getring() == Rational.getring() then
            integerpart = newexponent.numerator // newexponent.denominator
        else
            integerpart = newexponent
        end

        if integerpart == Integer.zero() then
            return BinaryOperation(BinaryOperation.POW, {primebase, newexponent})
        end
        return BinaryOperation(BinaryOperation.MUL,
                    {BinaryOperation(BinaryOperation.POW, {primebase, integerpart}),
                    BinaryOperation(BinaryOperation.POW, {primebase, newexponent - integerpart})}):autosimplify()
    end

    return BinaryOperation(BinaryOperation.POW, {primes:autosimplify(), exponent})
end