builtin prime factor program
This commit is contained in:
		
							
								
								
									
										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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user