local u = require('Module:Utils')
local p = {}
local function signum(x)
if x > 0 then return 1 end
if x < 0 then return -1 end
return 0
end
-- construct a rational number n/m
function p.new(n, m)
m = m or 1
if n == 0 then
if m == 0 then
return { nan = true }
else
return { zero = true, sign = signum(m) }
end
else
if m == 0 then
return { inf = true, sign = signum(n) }
end
end
local sign = signum(n) * signum(m)
n = n * signum(n)
m = m * signum(m)
local n_factors = u.prime_factorization_raw(n)
local m_factors = u.prime_factorization_raw(m)
local factors = n_factors
factors.sign = sign
for factor, power in pairs(m_factors) do
factors[factor] = factors[factor] or 0
factors[factor] = factors[factor] - power
if factors[factor] == 0 then
factors[factor] = nil
end
end
return factors
end
function p.copy(a)
b = {}
for factor, power in pairs(a) do
b[factor] = power
end
end
-- multiply two rational numbers; integers are also allowed
function p.mul(a, b)
if type(a) == 'number' then
a = p.new(a)
end
if type(b) == 'number' then
b = p.new(b)
end
-- special case: NaN
if a.nan or b.nan then
return { nan = true }
end
-- special case: infinities
if (a.inf and not b.zero) or (b.inf and not a.zero) then
return { inf = true, sign = a.sign * b.sign }
end
-- special case: infinity * zero
if (a.inf and b.zero) or (b.inf and a.zero) then
return { nan = true }
end
-- special case: zeros
if a.zero or b.zero then
return { zero = true, sign = a.sign * b.sign }
end
-- the regular case: both not NaN, not infinities, not zeros
local c = p.copy(a)
for factor, power in pairs(b) do
if type(factor) == 'number' then
c[factor] = c[factor] or 0
c[factor] = c[factor] + power
if c[factor] == 0 then
c[factor] = nil
end
end
end
c.sign = a.sign * b.sign
return c
end
-- compute 1/a for a rational number a; integers are also allowed
function p.inv(a)
if type(a) == 'number' then
a = p.new(a)
end
-- special case: NaN
if a.nan then
return { nan = true }
end
-- special case: infinity
if a.inf then
return { zero = true, sign = a.sign }
end
-- special case: zero
if a.zero then
return { inf = true, sign = a.sign }
end
-- the regular case: not NaN, not infinity, not zero
b = {}
for factor, power in pairs(a) do
if type(factor) == 'number' then
b[factor] = -power
end
end
b.sign = a.sign
return b
end
-- divide a rational number a by b; integers are also allowed
function p.div(a, b)
return p.mul(a, p.inv(b))
end
-- add two rational numbers; integers are also allowed
function p.add(a, b)
if type(a) == 'number' then
a = p.new(a)
end
if type(b) == 'number' then
b = p.new(b)
end
-- special case: NaN
if a.nan or b.nan then
return { nan = true }
end
-- special case: infinities
if a.inf and b.inf then
if a.sign == b.sign then
return { inf = true, sign = a.sign }
else
return { nan = true }
end
end
if a.inf then
return { inf = true, sign = a.sign }
end
if b.inf then
return { inf = true, sign = b.sign }
end
-- regular case: both not NaN, not infinities
n_a, m_a = p.as_pair(a)
n_b, m_b = p.as_pair(b)
n = n_a * m_b + n_b * m_a
m = m_a * m_b
return p.new(n, m)
end
-- substract a rational number from another; integers are also allowed
function p.sub(a, b)
return p.add(a, p.mul(b, -1))
end
-- return the (n, m) pair as a Lua tuple
function p.as_pair(a)
if type(a) == 'number' then
a = p.new(a)
end
-- special case: NaN
if a.nan then
return 0, 0
end
-- special case: infinity
if a.inf then
return a.sign, 0
end
-- special case: zero
if a.zero then
return 0, a.sign
end
-- regular case: not NaN, not infinity, not zero
local n = 1
local m = 1
for factor, power in pairs(a) do
if type(factor) == 'number' then
if power > 0 then
n = n * (factor ^ power)
else
m = m * (factor ^ (-power))
end
end
end
n = n * a.sign
return n, m
end
-- return the {n, m} pair as a Lua table
function p.as_table(a)
return {p.as_pair()}
end
-- return the n / m as a float approximation
function p.as_float(a)
local n, m = p.as_pair(a)
return n / m
end
return p