Project Euler is a collection of mathematics/computer science problems, as you probably already know. I've solved almost 50 of them so far, and I've developd a collection of utilities to make my job easier. Some of them (factorization routines, prime generators, etc.) I'm not going to share as they are fundamental to solving certain problems. However, I will share my test harness, as it's probably generally useful, and it contains no "secrets".
Why build a custom harness? Well, I originally considered using JUnit (or whatever) as a simple runner, but since there isn't anything to test, it didn't feel right. It also doesn't provide a good way to get timing information for the runs. And since I was originally using strictly Groovy, I didn't want to impose any more "ceremony" than I needed to. I've since started doing some problems in Java simply for the performance (as well as converting some of those "secret" routines), and have generalized my harness.
First, here are the file templates for Groovy:
import util.Runner new Runner() { // put some code here, eh? }
and for Java:
import util.Runner; import util.java.Solver; public class BruteForce { public static void main(String[] args) { new Runner(new Solver() { public Object solve() { // put some code here, eh? return null; } }); } }
As expected the Java version requires quite a bit more ceremony, even though the semantics of the two templates are identical. The Solver interface (used for Java) is as follows:
package util.java;
public interface Solver {
public Object solve();
}
Finally, the Runner class, which is implemented in Groovy. As you can see from the templates, it accepts either a Solver instance or a Groovy closure, and treats the two in exactly the same way, excepting that it picks between Solver.solve() and Closure.call() based on the type.
package util import util.java.Solver class Runner { private static ThreadLocal timer = new InheritableThreadLocal() private static int timerCount = 0 def Runner(solver) { this(true, solver) } def Runner(doIt, solver) { if (! doIt) { return } startTimer() // this will send an update every five or so seconds volatile def updateThread updateThread = Thread.startDaemon { while (true) { Thread.sleep(5000) if (updateThread == null) { break // after the sleep, before the log } log("still executing...") } } try { log("starting...") def result; if (solver instanceof Solver) { // the closure/Solver switch result = solver.solve() // | } else { // better be a Closure // | result = solver.call() // | } // V stopTimer() if (result instanceof String) { try { result = new BigInteger(result) } catch (e) { // oh well } } if (result instanceof Integer || result instanceof Long || result instanceof BigInteger) { log("result: $result") } else { def s = "" + result if (s.length() > 100) { s = s[0..<97] + "..." } log("result was not numeric: $s", System.err) } } finally { updateThread = null } } def elapsed() { get().elapsed } def formatElapsed(e) { def seconds = e.intdiv(1000) def millis = e % 1000 def s = "" if (seconds >= 60) { s += seconds.intdiv(60) + ":" seconds = seconds % 60 } if (s.length() > 0 && seconds < 10) { s += "0$seconds" } else { s += seconds } if (millis < 10) { s += ".00$millis" } else if (millis < 100) { s += ".0$millis" } else { s += ".$millis" } s } def log(Object msg, out=System.out) { def t = get() out.println "[${t.index}: ${formatElapsed(t.elapsed)}] $msg" } private startTimer() { Runner.timer.set(new TimerData(++timerCount)) } private stopTimer() { get().stop() } private get() { def t = Runner.timer.get() if (! t) { throw new IllegalStateException("No timer has been started...") } t } } class TimerData { def index def startDate def stopDate def TimerData(threadIndex) { index = threadIndex startDate = new Date() } def stop() { stopDate = new Date() } def getElapsed() { if (running) { return new Date().time - startDate.time } else { return stopDate.time - startDate.time } } boolean isRunning() { stopDate == null } }
There's a lot of stuff going on there, but primarily it's setting up a timing framework, a background thread to tell you it's still running every 5 seconds, and doing some magic with the returned solution. As you can see, the constructor takes an optional first parameter for whether to execute. Useful if you have multiple Runners in a single script/class, but only want to run some of them. https://maxinternet.biz/best-gas-grills-under-300
It wasn't my intent, but building this out was quite educational in the way the Java/Groovy interplay works. Write Groovy for whatever you can, and write Java for the bits that need it. As you can see from the templates, Java requires a lot more work, but it has benefits. I was quite pleased to be able to use my Groovy harness (which leverages all kinds of Groovy goodness) for my Java solutions. I originally figured I'd have to port it to Java to use it with both languages, but not the case.
Comments are closed.