Module:Rational

From Xenharmonic Wiki
Revision as of 16:17, 1 October 2022 by Plumtree (talk | contribs)
Jump to navigation Jump to search
Module documentation[view] [edit] [history] [purge]
This module primarily serves as a library for other modules and has no corresponding template.


Introspection summary for Module:Rational 
Functions provided (19)
Line Function Params
5 new (n, m)
35 copy (a)
44 mul (a, b)
83 inv (a)
111 div (a, b)
116 add (a, b)
154 sub (a, b)
159 lt (a, b)
169 leq (a, b)
179 gt (a, b)
189 geq (a, b)
199 eq (a, b)
205 is_int (a)
226 as_pair (a)
258 as_ratio (a, separator)
265 as_table (a)
270 as_float (a)
277 as_ket (a, frame)
327 ket (invokable) (frame)
Lua modules required (1)
Variable Module Functions used
u Module:Utils signum
prime_factorization_raw
is_prime

No function descriptions were provided. The Lua code may have further information.


local u = require('Module:Utils')
local p = {}

-- 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 = u.signum(m) }
		end
	else
		if m == 0 then
			return { inf = true, sign = u.signum(n) }
		end
	end
	local sign = u.signum(n) * u.signum(m)
	n = n * u.signum(n)
	m = m * u.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
	return b
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

-- determine whether a rational number is less than another; integers are also allowed
function p.lt(a, b)
	local c = p.sub(a, b)
	if c.zero then
		return false
	else
		return c.sign == -1
	end
end

-- determine whether a rational number is less or equal to the other; integers are also allowed
function p.leq(a, b)
	local c = p.sub(a, b)
	if c.zero then
		return true
	else
		return c.sign == -1
	end
end

-- determine whether a rational number is greater than another; integers are also allowed
function p.gt(a, b)
	local c = p.sub(a, b)
	if c.zero then
		return false
	else
		return c.sign == 1
	end
end

-- determine whether a rational number is greater or equal to the other; integers are also allowed
function p.geq(a, b)
	local c = p.sub(a, b)
	if c.zero then
		return true
	else
		return c.sign == 1
	end
end

-- determine whether a rational number is equal to another; integers are also allowed
function p.eq(a, b)
	local c = p.sub(a, b)
	return c.zero
end

-- determine whether a rational number is integer
function p.is_int(a)
	if type(a) == 'number' then
		return true
	end
	if a.nan then
		return false
	end
	if a.inf then
		return false
	end
	for factor, power in pairs(a) do
		if type(factor) == 'number' then
			if power < 0 then
				return false
			end
		end
	end
	return true
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

function p.as_ratio(a, separator)
	separator = separator or '/'
	local n, m = p.as_pair(a)
	return n .. separator .. m
end

-- return the {n, m} pair as a Lua table
function p.as_table(a)
	return {p.as_pair(a)}
end

-- return n / m as a float approximation
function p.as_float(a)
	local n, m = p.as_pair(a)
	return n / m
end

-- return a rational number in ket notation
-- NaN, infinity, zero values use special representations
function p.as_ket(a, frame)
	if type(a) == 'number' then
		a = p.new(a)
	end
	-- special case: NaN
	if a.nan then
		return 'NaN'
	end
	-- special case: infinity
	if a.inf then
		local sign = '+'
		if a.sign < 0 then
			sign = '-'
		end
		return sign .. '∞'
	end
	-- special case: zero
	if a.zero then
		return '0'
	end
	-- regular case: not NaN, not infinity, not zero
	
	local s = ''
	if a.sign < 0 then
		s = s .. '-'
	end
	-- preparing the argument
	local largest_prime = -1
	for factor, power in pairs(a) do
		if type(factor) == 'number' then
			if factor > largest_prime then
				largest_prime = factor
			end
		end
	end
	local template_arg = ''
	for i = 2, largest_prime do
		if u.is_prime(i) then
			if i > 2 then template_arg = template_arg .. ' ' end
			template_arg = template_arg .. (a[i] or 0)
		end
	end
	s = s .. frame:expandTemplate{
		title = 'Monzo',
		args = {template_arg}
	}
	return s
end

-- a version of as_ket() that can be {{#invoke:}}d
function p.ket(frame)
	local unparsed = frame.args[1] or '1'
	-- rational form
	local sign, n, _m, m = unparsed:match('(%-?)%s*(%d+)%s*(/%s*(%d+))')
	if n == nil then
		-- integer form
		sign, n = unparsed:match('(%-?)%s*(%d+)')
		if n == nil then
			-- parsing failure
			return '<span style="color:red;">Invalid rational number: ' .. unparsed .. '.</span>'
		else
			m = 1
			n = tonumber(n)
			if #sign > 0 then
				n = -n
			end
		end
	else
		n = tonumber(n)
		m = tonumber(m)
		if #sign > 0 then
			n = -n
		end
	end
	a = p.new(n, m)
	return p.as_ket(a, frame)
end

return p