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; long num; int sqrt; int[] primes; int step; int offset; boolean running; boolean done; FactorWorker(FactorMonitor monitor, int step, int offset) { this.monitor = monitor; this.primes = monitor.primes; this.num = monitor.num; this.sqrt = (int)Math.sqrt(num); this.step = step; this.offset = offset; this.done = false; } void go() { int start = offset + monitor.sequentialPrimes; for (int i = start; monitor.running; i += step) { if (primes[i] == 0 || primes[i] > sqrt) break; if (num % primes[i] == 0) { monitor.addFactor(primes[i], num); break; } } running = false; monitor.workerStopped(); } synchronized void start(long num) { this.num = num; this.sqrt = (int)Math.sqrt(num); running = true; notify(); } synchronized void stop() { done = true; notify(); } synchronized public void run() { while (!done) { while (!running && !done) try { wait(); } catch (InterruptedException ex) {} if (!done) go(); } } } class FactorMonitor { final int sequentialPrimes = 4; Sieve sieve; long num; int nThreads; int[] primes; boolean running; boolean done; boolean foundFactor; int workersLeft; FactorWorker[] workers; Thread[] threads; ArrayList factors = new ArrayList<>(); FactorMonitor(Sieve sieve, long num, int nThreads) { this.primes = sieve.getPrimes(); this.num = num; this.nThreads = nThreads; this.done = false; // We do the first few primes sequentially, as they're // very likely to be factors for (int i = 0; i < sequentialPrimes; ++i) { while (this.num % primes[i] == 0) { factors.add(new Long(primes[i])); this.num /= primes[i]; } } this.workers = new FactorWorker[nThreads]; this.threads = new Thread[nThreads]; for (int i = 0; i < nThreads; ++i) { FactorWorker w = new FactorWorker(this, nThreads, i); Thread t = new Thread(w); this.workers[i] = w; this.threads[i] = t; t.start(); } go(); } synchronized void addFactor(int prime, long num) { foundFactor = true; if (num != this.num) return; do { this.num /= prime; factors.add(new Long(prime)); } while (this.num % prime == 0); running = false; } synchronized void workerStopped() { workersLeft -= 1; if (workersLeft != 0) return; if (foundFactor) { go(); } else { done = true; notify(); for (FactorWorker w: workers) w.stop(); } } synchronized long[] getFactors() { while (!done) try { wait(); } catch (InterruptedException ex) {} if (num != 1) factors.add(new Long(num)); long[] arr = new long[factors.size()]; for (int i = 0; i < arr.length; ++i) arr[i] = factors.get(i); Arrays.sort(arr); return arr; } synchronized void go() { running = true; foundFactor = false; workersLeft = nThreads; for (FactorWorker w: workers) w.start(num); } } public long[] factor(Sieve sieve, long num) { int nThreads = Runtime.getRuntime().availableProcessors(); nThreads = Math.min(nThreads, 8); FactorMonitor monitor = new FactorMonitor(sieve, num, nThreads); return monitor.getFactors(); } public void stopThreads() { } }