Module:Rational: Difference between revisions

Sintel (talk | contribs)
merge changes from dev
ArrowHead294 (talk | contribs)
mNo edit summary
 
(12 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local p = {}
local seq = require("Module:Sequence")
local seq = require("Module:Sequence")
local utils = require("Module:Utils")
local utils = require("Module:Utils")
local p = {}


-- construct a rational number n/m
-- enter a numerator n and denominator m
-- returns a table of prime factors
-- similar to a monzo, but the indices are prime numbers.
function p.new(n, m)
function p.new(n, m)
m = m or 1
m = m or 1
Line 14: Line 17:
end
end
local sign = utils.signum(n) * utils.signum(m)
local sign = utils.signum(n) * utils.signum(m)
-- ensure n and m are positive
n = n * utils.signum(n)
n = n * utils.signum(n)
m = m * utils.signum(m)
m = m * utils.signum(m)
-- factorize n and m separately
local n_factors = utils.prime_factorization_raw(n)
local n_factors = utils.prime_factorization_raw(n)
local m_factors = utils.prime_factorization_raw(m)
local m_factors = utils.prime_factorization_raw(m)
local factors = n_factors
local factors = n_factors
factors.sign = sign
factors.sign = sign
-- subtract the factors of m from the factors of n
for factor, power in pairs(m_factors) do
for factor, power in pairs(m_factors) do
factors[factor] = factors[factor] or 0
factors[factor] = factors[factor] or 0
factors[factor] = factors[factor] - power
factors[factor] = factors[factor] - power
if factors[factor] == 0 then
if factors[factor] == 0 then
factors[factor] = nil
factors[factor] = nil -- clear the zeros
end
end
end
end
Line 327: Line 333:


-- compute a canonical representation of `a` modulo powers of `b`
-- compute a canonical representation of `a` modulo powers of `b`
-- TODO: describe the exact behavior
--      it seems bugged when the equave is a fraction
function p.modulo_mul(a, b)
function p.modulo_mul(a, b)
if type(a) == "number" then
if type(a) == "number" then
Line 547: Line 555:
end
end


-- determine whether a rational number represents a harmonic
-- determine whether a rational number represents a harmonic.
-- reduced: check for reduced harmonic instead.
function p.is_harmonic(a, reduced, large)
function p.is_harmonic(a, reduced, large)
if type(a) == "number" then
if type(a) == "number" then
Line 557: Line 566:
for factor, power in pairs(a) do
for factor, power in pairs(a) do
if type(factor) == "number" then
if type(factor) == "number" then
if power < 0 then
if factor == 2 and reduced then
-- pass (ignore factors of 2 for reduced harmonic check)
elseif power < 0 then
return false
return false
end
end
Line 568: Line 579:
end
end


-- determine whether a rational number represents a subharmonic
-- determine whether a rational number represents a subharmonic.
-- reduced: check for reduced subharmonic instead.
function p.is_subharmonic(a, reduced, large)
function p.is_subharmonic(a, reduced, large)
if type(a) == "number" then
if type(a) == "number" then
Line 578: Line 590:
for factor, power in pairs(a) do
for factor, power in pairs(a) do
if type(factor) == "number" then
if type(factor) == "number" then
if power > 0 then
if factor == 2 and reduced then
-- pass (ignore factors of 2 for reduced subharmonic check)
elseif power > 0 then
return false
return false
end
end
Line 648: Line 662:
local den = p.mul(p.add(k, 1), p.sub(k, 1))
local den = p.mul(p.add(k, 1), p.sub(k, 1))
return p.eq(a, p.div(p.pow(k, 2), den))
return p.eq(a, p.div(p.pow(k, 2), den))
end
-- check if an integer is prime
function p.is_prime(a)
if type(a) == "number" then
a = p.new(a)
end
-- nan, inf, zero, and negative numbers are not prime
if a.nan or a.inf or a.zero or a.sign < 0 then
return false
end
local flag = false -- flag for having exactly one prime factor
for factor, power in pairs(a) do
if type(factor) == "number" and power then
if flag or power ~= 1 then
return false
else
flag = true
end
end
end
return flag
end
end


Line 655: Line 693:
a = p.new(a)
a = p.new(a)
end
end
if a.nan or a.inf or a.zero then
return false
-- nan, inf, zero, and negative numbers are not highly composite
end
if a.nan or a.inf or a.zero or a.sign == -1 then
-- negative numbers are not highly composite
if a.sign == -1 then
return false
return false
end
end
-- non-integers are not highly composite
-- non-integers are not highly composite
for factor, power in pairs(a) do
for factor, power in pairs(a) do
Line 670: Line 707:
end
end
end
end
local last_power = 1 / 0
local last_power = 1 / 0
local max_prime = p.max_prime(a)
local max_prime = p.max_prime(a)
Line 858: Line 896:
end
end


-- find max prime involved in the factorisation
-- Check if ratio is within an int limit; that is, neither its numerator nor
-- (a.k.a. prime limit or harmonic class) of a rational number
-- denominator exceed that limit.
function p.max_prime(a)
function p.is_within_int_limit(a, lim)
return p.int_limit(a) <= lim
end
 
-- Find integer limit of a ratio
-- For a ratio p/q, this is simply max(p, q)
function p.int_limit(a)
if type(a) == "number" then
if type(a) == "number" then
a = p.new(a)
a = p.new(a)
Line 867: Line 911:
return nil
return nil
end
end
local max_factor = 0
local a_copy = p.copy(a)
for factor, _ in pairs(a) do
local num, den = p.as_pair(a_copy)
if type(factor) == "number" then
return math.max(num, den)
if factor > max_factor then
max_factor = factor
end
end
end
return max_factor
end
end


Line 893: Line 931:
local num, den = p.as_pair(a_copy)
local num, den = p.as_pair(a_copy)
return math.max(num, den)
return math.max(num, den)
end
-- find max prime involved in the factorisation
-- (a.k.a. prime limit or harmonic class) of a rational number
function p.max_prime(a)
if type(a) == "number" then
a = p.new(a)
end
if a.nan or a.inf or a.zero then
return nil
end
local max_factor = 0
for factor, _ in pairs(a) do
if type(factor) == "number" then
if factor > max_factor then
max_factor = factor
end
end
end
return max_factor
end
end


Line 1,366: Line 1,424:
end
end


-- return a rational number in ket notation
-- return a string of a rational number in monzo notation
-- NaN, infinity, zero values use special representations
-- calling Template: Monzo
function p.as_ket(a, frame, skip_many_zeros, only_numbers)
function p.as_ket(a, frame, skip_many_zeros, only_numbers)
if skip_many_zeros == nil then
if skip_many_zeros == nil then
Line 1,376: Line 1,434:
a = p.new(a)
a = p.new(a)
end
end
-- special case: NaN
if a.nan then
-- special cases
return "NaN"
if a.nan or a.inf or a.zero or a.sign < 0 then
return "n/a"
end
end
-- special case: infinity
if a.inf then
-- regular case: positive finite ratio
local sign = "+"
local s = ""
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 not only_numbers and a.sign < 0 then
s = s .. "-"
end
-- preparing the argument
-- preparing the argument
local max_prime = p.max_prime(a)
local max_prime = p.max_prime(a)
Line 1,490: Line 1,535:
function p.ket(frame)
function p.ket(frame)
local unparsed = frame.args[1] or "1"
local unparsed = frame.args[1] or "1"
local result = ""
local a = p.parse(unparsed)
local a = p.parse(unparsed)
if a == nil then
if a == nil then
return '<span style="color:red;">Invalid rational number: ' .. unparsed .. ".</span>"
result = '{{error|Invalid rational number: ' .. unparsed .. ".}}"
else
result = p.as_ket(a, frame)
end
end
return p.as_ket(a, frame)
return frame:preprocess(result)
end
end
p.monzo = p.ket
p.monzo = p.ket


return p
return p