Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import ctypes
- import multiprocessing
- import threading
- import time
- import gmpy2
- import sys
- import os
- CHECK_FOR_TWIN_PRIMES = True
- class Coefficients:
- def __init__(self, a, b, n, c):
- self.a = a
- self.b = b
- self.n = n
- self.c = c
- class MainConfig:
- def __init__(self, argv):
- self.args = argv
- self.programName = "None"
- self.doClearFile = False
- self.nMaxCount = 1024
- def writeToFile(file,inStr,type="w"):
- with open(file, type) as f: # Write first unsolved to progress file
- f.write(str(inStr))
- def listenKeyQuit(killSignal, keySignal):
- import select, sys
- while not killSignal.value:
- # Use select to check for input availability without blocking
- if select.select([sys.stdin], [], [], 0.1)[0]:
- user_input = sys.stdin.readline().strip()
- if user_input == 'q':
- print("Quit received! Waiting for threads.")
- keySignal.value = True
- break
- def findPrimeThread(coef, threadStorage, workPool, killSignal):
- while True:
- workPool[2] = True # Indicate to manager that work is needed
- while workPool[2] == True:
- if killSignal.value:
- return
- time.sleep(0.01) # Yield until manager assigns work
- # Process assigned work range
- for a in range(*coef.a):
- for c in range(*coef.c):
- if (not (a&1)) and (not (c&1)): # Early continue if result is always even
- continue
- for b in range(*coef.b):
- for n in range(workPool[0], workPool[1], coef.n[2]): #Use range from workPool given by workManager
- resultValue = a * (b ** n) + c
- if gmpy2.is_prime(resultValue):
- threadStorage.put((a, b, n, c, False)) # Append prime info to threadStorage
- if CHECK_FOR_TWIN_PRIMES:
- for i in range(-2,3,4,):
- if gmpy2.is_prime(resultValue+i):
- threadStorage.put((a, b, n, c+i, True))
- def queueToFormulas(storageBuffer, bigFormula, nBig):
- outString = ""
- while not storageBuffer.empty():
- (a, b, n, c, isTwin) = storageBuffer.get()
- formula = f"{b}**{n}"
- if a != 1: # Ignore 1*
- formula = f"{a}*" + formula
- if c >= 0: # only add '+' sign to positive c
- formula += f"+"
- formula += f"{c}" # +c
- outString += f"{formula}"
- if isTwin:
- outString+=" T"
- outString+="\n"
- if n > nBig: # store biggest n
- nBig = n
- bigFormula = formula
- return (outString, bigFormula, nBig)
- def stopWorkers(workPools, threadCount=1, updateRate=0.01):
- allDone = False
- while allDone == False: # wait till threads have finished their last tasks
- allDone = True
- for tid in range(threadCount):
- if workPools[tid][2] == False:
- allDone = False
- time.sleep(updateRate)
- return True
- def getConsoleUpdate(coef,finishedCount):
- numerator = finishedCount - coef.n[0]
- denominator = max(0, coef.n[1] - coef.n[0] + 1)
- separator = " | "
- printString = ""
- printString+= f"{coef.n[0]} to {coef.n[1]}"
- printString+= separator
- printString+= f"progress={numerator}/{denominator}"
- printString+= separator
- printString+= f"{100.0 * numerator / (denominator):.2f}%"
- return printString
- def workManagerThread(coef, batchSize, workPools, threadStorage, threadCount, killSignal, keySignal, progressFile, outputFile):
- nLast = coef.n[0] # first unsolved value of n
- oldN = -1 # used to compare n changes
- nBig = 0 # console print biggest n
- updateRate = 0.05 # time in seconds between check and file writes
- printRate = 2.0 # time between console prints in seconds
- startTime = 0
- printTime = 0
- endTime = 1000
- finishedCount = 0
- bigFormula = ""
- while not killSignal.value:
- if keySignal.value == True:
- killSignal.value = stopWorkers(workPools, threadCount, updateRate)
- # Update progress tracking
- if nLast > oldN:
- oldN = nLast
- # subtract the n values that are being worked on. Accurate unless a thread is more than 2 or more batches behind
- finishedCount = nLast - batchSize * threadCount
- finishedCount = max(finishedCount, coef.n[0]) #ensure the corrective offset doesn't result lower count than we started
- writeToFile(progressFile,finishedCount)
- if nLast > coef.n[1]:
- killSignal.value = stopWorkers(workPools, threadCount, updateRate)
- # Assign work to available processes
- for tid in range(threadCount):
- if workPools[tid][2] == True: # Access doneFlag using indexing
- storageBuffer = threadStorage[tid]
- # Assign new work
- if killSignal.value == False:
- workPools[tid][0] = nLast # workPools[tid].start
- workPools[tid][1] = nLast + batchSize*coef.n[2] # workPools[tid].end
- workPools[tid][2] = False # workPools[tid].doneFlag
- nLast += batchSize
- # Process results from workers
- (outString, bigFormula, nBig) = queueToFormulas(storageBuffer, bigFormula, nBig)
- if outString: # print result strings to file
- writeToFile(outputFile,outString,"a")
- if nLast > coef.n[1] and killSignal.value == False: # stop passing new tasks after max
- break
- # Sleep until the next update
- endTime = time.time()
- time.sleep(max(0, updateRate - (endTime - startTime)))
- startTime = endTime
- # console update
- if (endTime - printTime) > printRate:
- printString = getConsoleUpdate(coef,finishedCount)
- if (bigFormula != ""):
- printString += f" | high prime={bigFormula}"
- print(printString)
- printTime = endTime
- return nLast # last unsolved n
- def manPagePrintUsage(cfg):
- print("Much of this script is yet to be done")
- print("Search primes at form a*b^n+c")
- print("Output file: primeForms.txt")
- print("Input file: nProgress.txt")
- print(f"Usage:\n")
- print(f"python {cfg.args[0]}.py -? -h --help")
- print(f"python {cfg.args[0]}.py [largest n]")
- def manPage(cfg):
- if len(cfg.args) > 1:
- try: # Read last progress from file if available
- cfg.nMaxCount = int(cfg.args[1].strip())
- return 0
- except (FileNotFoundError, ValueError):
- cfg.nMaxCount = 1
- manPagePrintUsage(cfg)
- return 1
- def main():
- cfg = MainConfig(sys.argv)
- if manPage(cfg):
- return 0
- progressFile = "nProgress.txt"
- outputFile = "primeForms.txt"
- #write header
- if os.stat(outputFile).st_size == 0 or cfg.doClearFile==True:
- writeToFile(outputFile,"Prime a*b^n+c\n")
- writeToFile(progressFile,int(1))
- nStart = 1
- batchSize = 1
- try: # Check if outputFile is empty or exists
- with open(outputFile, "r") as f:
- err = (f.read().strip())
- except (FileNotFoundError):
- with open(outputFile, "w") as f:
- f.write("Prime a*b^n+c\n")
- # Try to resume from last saved progress
- try:
- with open(progressFile, "r") as f:
- nStart = int(f.read().strip())
- except (FileNotFoundError, ValueError):
- nStart = 1 # Start from scratch if no progress file
- nMax = nStart + cfg.nMaxCount - 1
- aList = [9,10,1]
- bList = [13238717,13238718,1]
- nList = [nStart,nMax,1]
- cList = [-2, 3,4]
- threadCount = os.cpu_count()
- threadCount = min(threadCount,nMax-nStart+1)
- coef = Coefficients(aList, bList, nList, cList)
- # Create a Queue to store results
- threadStorage = [multiprocessing.Queue() for _ in range(threadCount)]
- # Initialize shared data structures
- workPools = [multiprocessing.Array(ctypes.c_int, 3) for _ in range(threadCount)] # (nStart, nEnd, doneFlag)
- for wp in workPools:
- wp[0] = 0 # start
- wp[1] = 0 # end
- wp[2] = False # doneFlag
- killSignal = multiprocessing.Value(ctypes.c_bool, False)
- keySignal = multiprocessing.Value(ctypes.c_bool, False)
- listener_thread = threading.Thread(target=listenKeyQuit, args=(killSignal, keySignal,))
- listener_thread.start()
- # Launch worker processes
- processes = []
- for tid in range(threadCount):
- p = multiprocessing.Process(target=findPrimeThread, args=(coef, threadStorage[tid], workPools[tid], killSignal))
- p.start()
- processes.append(p)
- # Run work manager thread
- nLast = workManagerThread(coef, batchSize, workPools, threadStorage, threadCount, killSignal, keySignal, progressFile, outputFile)
- killSignal.value = True # This will signal listener_thread to stop
- # Wait for all processes to complete
- for p in processes:
- p.join()
- writeToFile(progressFile,nLast)
- print(getConsoleUpdate(coef,nLast))
- print("All threads done.")
- sys.exit()
- listener_thread.join() # Wait for listener_thread to cleanly exit
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement