Module:Rational

Revision as of 10:54, 1 October 2022 by Plumtree (talk | contribs) (Module created)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Module documentation[view] [edit] [history] [purge]
Todo: add documentation

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