Module:Rational: Difference between revisions
Jump to navigation
Jump to search
m signum() -> u.signum() |
Ket notation added |
||
Line 197: | Line 197: | ||
local n, m = p.as_pair(a) | local n, m = p.as_pair(a) | ||
return n / m | 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 | end | ||
return p | return p |
Revision as of 13:59, 1 October 2022
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
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 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