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