import java.util.ArrayList; import java.util.Arrays; class Parallel implements Solver { class SolverWorker implements Runnable { Sieve sieve; int step; int offset; SolverWorker(Sieve mainSieve, int step, int offset) { byte[] arr = new byte[mainSieve.arr.length]; System.arraycopy(mainSieve.arr, 0, arr, 0, arr.length); this.sieve = new Sieve(mainSieve.maxNum, arr); this.sieve.smallPrimesGenerated = true; this.step = step; this.offset = offset; } public void run() { sieve.generatePrimes(step, offset); } } public void solve(Sieve sieve) { // Generate primes less than sqrt(n) sieve.generateSmallPrimes(); int nThreads = Runtime.getRuntime().availableProcessors(); nThreads = Math.min(nThreads, 8); Thread[] threads = new Thread[nThreads]; SolverWorker[] workers = new SolverWorker[nThreads]; // Start threads for (int i = 0; i < nThreads; ++i) { SolverWorker w = new SolverWorker(sieve, nThreads, (i * 2) + 1); workers[i] = w; Thread t = new Thread(w); threads[i] = t; t.start(); } // Wait for threads for (Thread t: threads) { try { t.join(); } catch (InterruptedException ex) {} } // Merge for (int i = 0; i < sieve.arr.length; ++i) { for (SolverWorker w: workers) { sieve.arr[i] = (byte)(sieve.arr[i] | w.sieve.arr[i]); } } } class FactorWorker implements Runnable { FactorMonitor monitor; int step; int offset; long num; long currentPrime; long biggestPossible; Sieve sieve; boolean ready = false; boolean done = false; // Offset should start at 0 synchronized public void start(FactorMonitor monitor, int step, int offset) { this.monitor = monitor; this.step = step; this.offset = offset; this.num = monitor.currentNum; this.biggestPossible = (long)Math.sqrt(num); this.sieve = monitor.sieve; notify(); this.ready = true; this.done = false; findFirstPrime(); } void findFirstPrime() { currentPrime = 3; for (int i = 0; i < offset; ++i) { currentPrime += 2; while (!monitor.isPrime(currentPrime)) currentPrime += 2; } } synchronized public void stop() { ready = false; done = true; notify(); } void nextPrime() { if (currentPrime > biggestPossible) return; for (int i = 0; i < step; ++i) { currentPrime += 2; while (!monitor.isPrime(currentPrime) && currentPrime <= biggestPossible) currentPrime += 2; if (currentPrime > biggestPossible) { this.ready = false; monitor.foundBiggest(); return; } } } void findFactor() { boolean found = false; while (!found && this.ready) { if ((num % currentPrime) == 0) { found = true; if (!monitor.addFactor(currentPrime, num)) { this.num = monitor.currentNum; findFirstPrime(); return; } } nextPrime(); } } synchronized public void run() { while (!ready && !done) try { wait(); } catch (InterruptedException ex) {} while (!done) { while (!monitor.isDone() && this.ready) { findFactor(); } ready = false; while (!ready && !done) try { wait(); } catch (InterruptedException ex) {} } } } class FactorMonitor { Sieve sieve; long original; long currentNum; int biggestLeft; boolean done = false; ArrayList arr = new ArrayList<>(); FactorMonitor(Sieve sieve, long num, int nThreads) { this.sieve = sieve; this.original = num; this.currentNum = num; this.biggestLeft = nThreads; // We don't want to have to deal with the fact that 2 is a prime if (num % 2 == 0) addFactor(2, num); } synchronized boolean addFactor(long factor, long num) { // If the thread is working with a stale num, // just discard it and tell the thread to restart. if (num != currentNum) return false; while ((currentNum % factor) == 0) { currentNum /= factor; Long i = new Long(factor); arr.add(i); } notify(); return true; } synchronized void foundBiggest() { this.biggestLeft -= 1; if (biggestLeft == 0) { done = true; notify(); } } boolean isPrime(long num) { if (num > sieve.maxNum) return false; else return sieve.isPrime((int)num); } synchronized boolean isDone() { if (done) { return true; } else if (isPrime(currentNum)) { done = true; if (currentNum != 1) arr.add(new Long(currentNum)); notify(); return true; } else { return false; } } synchronized long[] getFactors() { while (!isDone()) try { wait(); } catch (InterruptedException ex) {} Long curr = new Long(currentNum); if (currentNum > (long)Math.sqrt(original) && !arr.contains(curr)) arr.add(curr); long[] a = new long[arr.size()]; for (int i = 0; i < a.length; ++i) { a[i] = arr.get(i); } return a; } } FactorWorker[] factorWorkers = null; Thread[] factorThreadPool = null; public long[] factor(Sieve sieve, long num) { int nThreads = Runtime.getRuntime().availableProcessors(); nThreads = Math.min(nThreads, 8); if (num <= sieve.maxNum && sieve.isPrime((int)num)) return new long[] { num }; // Initiate thread pool if (factorThreadPool == null) { factorThreadPool = new Thread[nThreads]; factorWorkers = new FactorWorker[nThreads]; for (int i = 0; i < nThreads; ++i) { FactorWorker w = new FactorWorker(); factorWorkers[i] = w; factorThreadPool[i] = new Thread(w); factorThreadPool[i].start(); } } FactorMonitor monitor = new FactorMonitor(sieve, num, nThreads); // Start workers for (int i = 0; i < nThreads; ++i) { FactorWorker w = factorWorkers[i]; w.start(monitor, nThreads, i); } long[] arr = monitor.getFactors(); Arrays.sort(arr); return arr; } public void stopThreads() { if (factorWorkers == null) return; for (FactorWorker w: factorWorkers) { w.stop(); } } }