builtin prime factor program
This commit is contained in:
parent
49eaf55b95
commit
b23652ad92
140
src/bin/factor.lua
Normal file
140
src/bin/factor.lua
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
local function gen_vers()
|
||||||
|
return ("%d.%d.%d"):format(math.random(0, 4), math.random(0, 20), math.random(0, 8))
|
||||||
|
end
|
||||||
|
local vers = potatOS.registry.get "potatOS.factor_version"
|
||||||
|
if not vers then
|
||||||
|
vers = gen_vers()
|
||||||
|
potatOS.registry.set("potatOS.factor_version", vers)
|
||||||
|
end
|
||||||
|
print(fs.getName(shell.getRunningProgram()) - "%.lua$", "v" .. vers)
|
||||||
|
local x
|
||||||
|
repeat
|
||||||
|
write "Provide an integer to factorize: "
|
||||||
|
x = tonumber(read())
|
||||||
|
if not x or math.floor(x) ~= x then print("That is NOT an integer.") end
|
||||||
|
until x
|
||||||
|
|
||||||
|
if x > (2^40) then print("WARNING: Number is quite big. Due to Lua floating point limitations, draconic entities MAY be present. If this runs for several seconds, it's probably frozen due to this.") end
|
||||||
|
|
||||||
|
local floor, abs, random, log, pow = math.floor, math.abs, math.random, math.log, math.pow
|
||||||
|
|
||||||
|
local function gcd(x, y)
|
||||||
|
local r = x % y
|
||||||
|
if r == 0 then return y end
|
||||||
|
return gcd(y, r)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function eps_compare(x, y)
|
||||||
|
return abs(x - y) < 1e-14
|
||||||
|
end
|
||||||
|
|
||||||
|
local function modexp(a, b, n)
|
||||||
|
if b == 0 then return 1 % n end
|
||||||
|
if b == 1 then return a % n end
|
||||||
|
local bdiv2 = b / 2
|
||||||
|
local fbdiv2 = floor(bdiv2)
|
||||||
|
if eps_compare(bdiv2, fbdiv2) then
|
||||||
|
-- b is even, so it is possible to just modexp with HALF the exponent and square it (mod n)
|
||||||
|
local x = modexp(a, fbdiv2, n)
|
||||||
|
return (x * x) % n
|
||||||
|
else
|
||||||
|
-- not even, so subtract 1 (this is even), modexp that, and multiply by a again (mod n)
|
||||||
|
return (modexp(a, b - 1, n) * a) % n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local bases = {2, 3, 5, 7, 11, 13, 17, 19}
|
||||||
|
local primes = {}
|
||||||
|
for _, k in pairs(bases) do primes[k] = true end
|
||||||
|
|
||||||
|
local function is_probably_prime(n)
|
||||||
|
if primes[n] then return true end
|
||||||
|
if n > 2 and n % 2 == 0 then return false end
|
||||||
|
-- express n as 2^r * d + 1
|
||||||
|
-- by dividing n - 1 by 2 until this is no longer possible
|
||||||
|
local d = n - 1
|
||||||
|
local r = 0
|
||||||
|
while true do
|
||||||
|
local ddiv = d / 2
|
||||||
|
if ddiv == floor(ddiv) then
|
||||||
|
r = r + 1
|
||||||
|
d = ddiv
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sleep()
|
||||||
|
for _, a in pairs(bases) do
|
||||||
|
local x = modexp(a, d, n)
|
||||||
|
if x == 1 or x == n - 1 then
|
||||||
|
-- continue looping
|
||||||
|
else
|
||||||
|
local c = true
|
||||||
|
for i = 2, r do
|
||||||
|
x = (x * x) % n
|
||||||
|
if x == n - 1 then c = false break end
|
||||||
|
end
|
||||||
|
if c then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
primes[n] = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_power(n)
|
||||||
|
local i = 2
|
||||||
|
while true do
|
||||||
|
local x = pow(n, 1/i)
|
||||||
|
if x == floor(x) then
|
||||||
|
return i, x
|
||||||
|
elseif x < 2 then return end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function insertmany(xs, ys)
|
||||||
|
for _, y in pairs(ys) do table.insert(xs, y) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- pollard's rho algorithm
|
||||||
|
-- it iterates again if it doesn't find a factor in one iteration, which causes infinite loops for actual primes
|
||||||
|
-- so a Miller-Rabin primality test is used to detect these (plus optimization for small primes); this will work for any number Lua can represent accurately, apparently
|
||||||
|
-- this also checks if something is an integer power of something else
|
||||||
|
-- You may argue that this is "stupid" and "pointless" and that "trial division would be faster anyway, the numbers are quite small" in which case bee you.
|
||||||
|
local function factor(n, c)
|
||||||
|
if is_probably_prime(n) then return {n} end
|
||||||
|
local p, q = is_power(n)
|
||||||
|
if p then
|
||||||
|
local qf = factor(q)
|
||||||
|
local o = {}
|
||||||
|
for i = 1, p do
|
||||||
|
insertmany(o, qf)
|
||||||
|
end
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
local c = (c or 0) + random(1, 1000)
|
||||||
|
local function g(x) return ((x * x) + c) % n end
|
||||||
|
local x, y, d = 2, 2, 1
|
||||||
|
local count = 0
|
||||||
|
while d == 1 do
|
||||||
|
x = g(x)
|
||||||
|
y = g(g(y))
|
||||||
|
d = gcd(abs(x - y), n)
|
||||||
|
count = count + 1
|
||||||
|
if count % 1e6 == 0 then sleep() end
|
||||||
|
end
|
||||||
|
if d == n then return factor(n, c) end
|
||||||
|
local facs = {}
|
||||||
|
insertmany(facs, factor(d))
|
||||||
|
insertmany(facs, factor(n / d))
|
||||||
|
return facs
|
||||||
|
end
|
||||||
|
|
||||||
|
local facs = factor(x)
|
||||||
|
|
||||||
|
if (potatOS.is_uninstalling and potatOS.is_uninstalling()) and x > 1e5 then
|
||||||
|
for k, v in pairs(facs) do facs[k] = facs[k] + random(-1000, 1000) end
|
||||||
|
end
|
||||||
|
print("Factors:", unpack(facs))
|
@ -1081,6 +1081,7 @@ local function run_with_sandbox()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local is_uninstalling = false
|
||||||
-- PotatOS API functionality
|
-- PotatOS API functionality
|
||||||
local potatOS = {
|
local potatOS = {
|
||||||
ecc = require "ecc",
|
ecc = require "ecc",
|
||||||
@ -1148,12 +1149,14 @@ local function run_with_sandbox()
|
|||||||
full_build = full_build,
|
full_build = full_build,
|
||||||
-- Just pass on the hidden-ness option to the PotatoBIOS code.
|
-- Just pass on the hidden-ness option to the PotatoBIOS code.
|
||||||
hidden = registry.get "potatOS.hidden" or settings.get "potatOS.hidden",
|
hidden = registry.get "potatOS.hidden" or settings.get "potatOS.hidden",
|
||||||
|
is_uninstalling = function() return is_uninstalling end,
|
||||||
-- Allow uninstallation of potatOS with the simple challenge of factoring a 14-digit or so (UPDATE: ~10) semiprime.
|
-- Allow uninstallation of potatOS with the simple challenge of factoring a 14-digit or so (UPDATE: ~10) semiprime.
|
||||||
-- Yes, computers can factorize semiprimes easily (it's intended to have users use a computer for this anyway) but
|
-- Yes, computers can factorize semiprimes easily (it's intended to have users use a computer for this anyway) but
|
||||||
-- it is not (assuming no flaws elsewhere!) possible for sandboxed code to READ what the prime is, although
|
-- it is not (assuming no flaws elsewhere!) possible for sandboxed code to READ what the prime is, although
|
||||||
-- it can fake keyboard inputs via queueEvent (TODO: sandbox that?)
|
-- it can fake keyboard inputs via queueEvent (TODO: sandbox that?)
|
||||||
begin_uninstall_process = function()
|
begin_uninstall_process = function()
|
||||||
if settings.get "potatOS.pjals_mode" then error "Protocol Omega Initialized. Access Denied." end
|
if settings.get "potatOS.pjals_mode" then error "Protocol Omega Initialized. Access Denied." end
|
||||||
|
is_uninstalling = true
|
||||||
math.randomseed(secureish_randomseed)
|
math.randomseed(secureish_randomseed)
|
||||||
secureish_randomseed = math.random(0xFFFFFFF)
|
secureish_randomseed = math.random(0xFFFFFFF)
|
||||||
print "Please wait. Generating semiprime number..."
|
print "Please wait. Generating semiprime number..."
|
||||||
@ -1163,11 +1166,11 @@ local function run_with_sandbox()
|
|||||||
print("Please find the prime factors of the following number (or enter 'quit') to exit:", num)
|
print("Please find the prime factors of the following number (or enter 'quit') to exit:", num)
|
||||||
write "Factor 1: "
|
write "Factor 1: "
|
||||||
local r1 = read()
|
local r1 = read()
|
||||||
if r1 == "quit" then return end
|
if r1 == "quit" then is_uninstalling = false return end
|
||||||
local f1 = tonumber(r1)
|
local f1 = tonumber(r1)
|
||||||
write "Factor 2: "
|
write "Factor 2: "
|
||||||
local r2 = read()
|
local r2 = read()
|
||||||
if r2 == "quit" then return end
|
if r2 == "quit" then is_uninstalling = false return end
|
||||||
local f2 = tonumber(r2)
|
local f2 = tonumber(r2)
|
||||||
if (f1 == p1 and f2 == p2) or (f1 == p2 and f2 == p1) then
|
if (f1 == p1 and f2 == p2) or (f1 == p2 and f2 == p1) then
|
||||||
term.clear()
|
term.clear()
|
||||||
@ -1180,6 +1183,7 @@ local function run_with_sandbox()
|
|||||||
})
|
})
|
||||||
print("Factors", f1, f2, "invalid.", p1, p2, "expected. This incident has been reported.")
|
print("Factors", f1, f2, "invalid.", p1, p2, "expected. This incident has been reported.")
|
||||||
end
|
end
|
||||||
|
is_uninstalling = false
|
||||||
end,
|
end,
|
||||||
--[[
|
--[[
|
||||||
Fix bug PS#5A1549BE
|
Fix bug PS#5A1549BE
|
||||||
|
Loading…
Reference in New Issue
Block a user