package net.jakubkorab.pi_evaluator import scala.actors._ import scala.actors.Actor._ import scala.math._ object PiEvaluator extends SimpleLogger { val sideLength = 1D val radius = sideLength / 2 val pointsPerPointGeneratorLoop = 10000L val simulationLengthSeconds = 30 override def toString() = "PiEvaluator" def main(args : Array[String]) = { for (numGenerators <- 1 to 5) { evaluate(numGenerators); } } def evaluate(generatorActorsInSimulation : Int) { val aggregator = new Aggregator().start var generators = Set[Actor]() for (generatorCount <- 1 to generatorActorsInSimulation) { val pointGenerator = new PointGenerator(generatorCount, aggregator).start pointGenerator ! new Generate(pointsPerPointGeneratorLoop) generators = generators + pointGenerator } for (i <- 1 to simulationLengthSeconds) { Thread.sleep(1000) aggregator ! Report() } log("Shutting down generators") generators.foreach( _ ! Shutdown() ) Thread.sleep(1000) log("Shutting down aggregator") aggregator ! FullReport() aggregator ! Shutdown() } class Aggregator() extends Actor with SimpleLogger { override def toString() = "Aggregator" override def act() = { var totalPointsEvaluated = 0L var totalPointsInCircle = 0L while (true) { receive { case PointCount(pointsEvaluated : Long, pointsInCircle : Long) => { totalPointsEvaluated += pointsEvaluated totalPointsInCircle += pointsInCircle } //case Report() => approximatePi(totalPointsEvaluated, totalPointsInCircle) case FullReport() => {log("Considered " + totalPointsEvaluated + " points"); approximatePi(totalPointsEvaluated, totalPointsInCircle) } case Shutdown() => exit() } } } def approximatePi(pointsEvaluated : Long, pointsInCircle : Long) = { //log("After " + pointsEvaluated + " samples, the number of points in a circle was " + pointsInCircle) val areaOfSquare = sideLength * sideLength // areaOfCircle/areaOfSquare =~ pointsInCircle/pointsEvaluated, so val areaOfCircle = pointsInCircle * areaOfSquare / pointsEvaluated // areaOfCircle = pi * r^2, so val approximatePi = areaOfCircle / (radius * radius) log("Pi is approx: " + approximatePi) } } class PointGenerator(val id : Int, val aggregator : Actor) extends Actor with SimpleLogger { log("Created PointGenerator[" + id + "]") var pointsGenerated = 0L override def toString() = "PointGenerator[" + id + "]" override def act() { while(true) { receive { case Generate(pointsToEvaluate : Long) => { aggregator ! new PointCount(pointsToEvaluate, pointsInCircle(pointsToEvaluate)) pointsGenerated += pointsToEvaluate self ! new Generate(pointsToEvaluate) } case Shutdown() => { log("exiting having generated " + pointsGenerated + " points"); exit() } } } } def isRandomPointWithinCircleRadius() : Boolean = { val x = (random * sideLength) - radius val y = (random * sideLength) - radius val hypotenuse = sqrt(x * x + y * y) // pythagoras hypotenuse <= radius } def pointsInCircle(pointsToSample : Long) : Long = { (1L to pointsToSample) .map((i : Long) => isRandomPointWithinCircleRadius()) .foldLeft(0L) { (pointCount : Long, pointInCircle : Boolean) => if (pointInCircle) pointCount + 1 else pointCount } } } } trait SimpleLogger { protected var name:String = this.toString() def log(message : String) { println(name + ":" + message) } } sealed abstract class Message() case class Generate(val numberOfPoints : Long) extends Message case class PointCount(val pointsEvaluated : Long, val pointsInCircle : Long) extends Message case class Report() extends Message case class FullReport() extends Message case class Shutdown() extends Message