├── .idea ├── .name ├── copyright │ └── profiles_settings.xml ├── vcs.xml ├── scala_settings.xml ├── scala_compiler.xml ├── modules.xml ├── compiler.xml ├── sbt.xml ├── misc.xml └── modules │ ├── root.iml │ └── root-build.iml ├── project ├── build.properties ├── StudentBuild.scala ├── plugins.sbt ├── buildSettings.sbt ├── Settings.scala ├── StyleChecker.scala ├── CommonBuild.scala ├── GradingFeedback.scala ├── ScalaTestRunner.scala └── StudentBuildLike.scala ├── assignment.sbt ├── assigments ├── week0example.pdf └── week1boxblur.pdf ├── src ├── main │ ├── resources │ │ ├── kmeans │ │ │ └── epfl-view.jpg │ │ └── scalashop │ │ │ └── scala.jpg │ └── scala │ │ ├── reductions │ │ ├── lineOfSight-result.txt │ │ ├── balance-result.txt │ │ ├── countChange-result.txt │ │ ├── wip.sc │ │ ├── graderOutput.txt │ │ ├── ParallelCountChange.scala │ │ ├── ParallelParenthesesBalancing.scala │ │ └── LineOfSight.scala │ │ ├── kmeans │ │ ├── result-KMeansRunner.txt │ │ ├── fun │ │ │ ├── package.scala │ │ │ ├── PhotoCanvas.scala │ │ │ ├── IndexedColors.scala │ │ │ └── ScalaShop.scala │ │ └── KMeans.scala │ │ ├── barneshut │ │ ├── conctrees │ │ │ ├── package.scala │ │ │ ├── ConcBuffer.scala │ │ │ └── Conc.scala │ │ ├── SimulationModel.scala │ │ ├── graderOutput.txt │ │ ├── Simulator.scala │ │ ├── BarnesHut.scala │ │ ├── SimulationCanvas.scala │ │ └── package.scala │ │ ├── scalashop │ │ ├── runResults.txt │ │ ├── wip.sc │ │ ├── package.scala │ │ ├── HorizontalBoxBlur.scala │ │ ├── VerticalBoxBlur.scala │ │ ├── PhotoCanvas.scala │ │ └── ScalaShop.scala │ │ ├── common │ │ └── package.scala │ │ └── example │ │ └── Lists.scala └── test │ └── scala │ ├── reductions │ ├── LineOfSightSuite.scala │ ├── ParallelParenthesesBalancingSuite.scala │ └── ParallelCountChangeSuite.scala │ ├── scalashop │ └── BlurSuite.scala │ ├── kmeans │ └── KMeansSuite.scala │ ├── example │ └── ListsSuite.scala │ └── barneshut │ └── BarnesHutSuite.scala └── .gitignore /.idea/.name: -------------------------------------------------------------------------------- 1 | parprog1-barneshut -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 -------------------------------------------------------------------------------- /assignment.sbt: -------------------------------------------------------------------------------- 1 | course := "parprog1" 2 | assignment := "barneshut" 3 | -------------------------------------------------------------------------------- /project/StudentBuild.scala: -------------------------------------------------------------------------------- 1 | object StudentBuild extends StudentBuildLike 2 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /assigments/week0example.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomLous/coursera-parallel-programming-scala/HEAD/assigments/week0example.pdf -------------------------------------------------------------------------------- /assigments/week1boxblur.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomLous/coursera-parallel-programming-scala/HEAD/assigments/week1boxblur.pdf -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0") 2 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.2") 3 | -------------------------------------------------------------------------------- /src/main/resources/kmeans/epfl-view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomLous/coursera-parallel-programming-scala/HEAD/src/main/resources/kmeans/epfl-view.jpg -------------------------------------------------------------------------------- /src/main/resources/scalashop/scala.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomLous/coursera-parallel-programming-scala/HEAD/src/main/resources/scalashop/scala.jpg -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/scala/reductions/lineOfSight-result.txt: -------------------------------------------------------------------------------- 1 | > runMain reductions.LineOfSightRunner 2 | 3 | sequential time: 2084.96239805 ms 4 | 5 | parallel time: 757.1070782 ms 6 | speedup: 2.76252671560349 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/scala_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/main/scala/kmeans/result-KMeansRunner.txt: -------------------------------------------------------------------------------- 1 | runMain kmeans.KMeansRunner 2 | 3 | sequential time: 91.79862900000002 ms 4 | 5 | parallel time: 82.61482236000002 ms 6 | speedup: 1.1111641516334794 7 | 8 | Process finished with exit code 0 9 | -------------------------------------------------------------------------------- /src/main/scala/reductions/balance-result.txt: -------------------------------------------------------------------------------- 1 | > runMain reductions.ParallelParenthesesBalancingRunner 2 | 3 | sequential result = true 4 | sequential balancing time: 3476.222456000001 ms 5 | 6 | parallel result = true 7 | parallel balancing time: 0.4572732500000002 ms 8 | speedup: 7602.068251313628 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/conctrees/package.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | 3 | package object conctrees { 4 | 5 | implicit class ConcOps[T](val self: Conc[T]) extends AnyVal { 6 | def foreach[U](f: T => U) = Conc.traverse(self, f) 7 | def <>(that: Conc[T]) = Conc.concatTop(self.normalized, that.normalized) 8 | } 9 | 10 | } -------------------------------------------------------------------------------- /.idea/scala_compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /project/buildSettings.sbt: -------------------------------------------------------------------------------- 1 | // used for style-checking submissions 2 | libraryDependencies += "org.scalastyle" %% "scalastyle" % "0.8.0" 3 | 4 | // used for submitting the assignments to Coursera 5 | libraryDependencies += "org.scalaj" %% "scalaj-http" % "2.2.1" 6 | 7 | // used for base64 encoding 8 | libraryDependencies += "commons-codec" % "commons-codec" % "1.10" 9 | 10 | // used to escape json for the submission 11 | libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.4" 12 | -------------------------------------------------------------------------------- /src/main/scala/scalashop/runResults.txt: -------------------------------------------------------------------------------- 1 | > runMain scalashop.VerticalBoxBlurRunner 2 | sequential blur time: 28930.99795119999 ms 3 | fork/join blur time: 9386.2060155 ms 4 | speedup: 3.0822888293123456 5 | [success] Total time: 573 s, completed Jun 11, 2016 10:26:49 PM 6 | 7 | > runMain scalashop.HorizontalBoxBlurRunner 8 | sequential blur time: 31848.198661699997 ms 9 | fork/join blur time: 10288.1240555 ms 10 | speedup: 3.095627394255034 11 | [success] Total time: 646 s, completed Jun 11, 2016 10:47:13 PM 12 | 13 | -------------------------------------------------------------------------------- /src/main/scala/reductions/countChange-result.txt: -------------------------------------------------------------------------------- 1 | > runMain reductions.ParallelCountChangeRunner 2 | 3 | sequential result = 177863 4 | sequential count time: 64.98515082499998 ms 5 | 6 | parallel result = 177863 7 | parallel count time: 19.62264665 ms 8 | speedup: 3.3117423956161374 9 | 10 | parallel result = 177863 11 | parallel count time: 17.183560900000003 ms 12 | speedup: 3.781820962673689 13 | 14 | parallel result = 177863 15 | parallel count time: 20.8402145625 ms 16 | speedup: 3.1182572823378996 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/sbt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /project/Settings.scala: -------------------------------------------------------------------------------- 1 | object Settings { 2 | val maxSubmitFileSize = { 3 | val mb = 1024 * 1024 4 | 10 * mb 5 | } 6 | 7 | val testResultsFileName = "grading-results-log" 8 | 9 | // time in seconds that we give scalatest for running 10 | val scalaTestTimeout = 850 // coursera has a 15 minute timeout anyhow 11 | val individualTestTimeout = 240 12 | 13 | // default weight of each test in a GradingSuite, in case no weight is given 14 | val scalaTestDefaultWeight = 10 15 | 16 | // when students leave print statements in their code, they end up in the output of the 17 | // system process running ScalaTest (ScalaTestRunner.scala); we need some limits. 18 | val maxOutputLines = 10 * 1000 19 | val maxOutputLineLength = 1000 20 | 21 | val scalaTestReportFileProperty = "scalatest.reportFile" 22 | val scalaTestIndividualTestTimeoutProperty = "scalatest.individualTestTimeout" 23 | val scalaTestReadableFilesProperty = "scalatest.readableFiles" 24 | val scalaTestDefaultWeightProperty = "scalatest.defaultWeight" 25 | val scalaTestReporter = "ch.epfl.lamp.grading.GradingReporter" 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/reductions/wip.sc: -------------------------------------------------------------------------------- 1 | object test{ 2 | 3 | 4 | traverse(0,8,0,0,"((test)(c+x)-4)((((4)444)---)xx(x)x)".toCharArray) 5 | traverse(8,18,0,0,"((test)(c+x)-4)((((4)444)---)xx(x)x)".toCharArray) 6 | traverse(18,26,0,0,"((test)(c+x)-4)((((4)444)---)xx(x)x)".toCharArray) 7 | traverse(26,36,0,0,"((test)(c+x)-4)((((4)444)---)xx(x)x)".toCharArray) 8 | 9 | 10 | def traverse(idx: Int, until: Int, startOpen: Int, startClosed: Int, chars: Array[Char]): (Int, Int) = { 11 | 12 | println("\n------") 13 | 14 | var i = idx 15 | var begin = 0 16 | var end = 0 17 | var switched = false 18 | 19 | while (i < until) { 20 | print(chars(i)) 21 | if (chars(i) == '(') { 22 | if (begin < 0) { 23 | switched = true 24 | } 25 | 26 | if (switched) end = end + 1 else begin = begin + 1 27 | } 28 | if (chars(i) == ')') { 29 | if (begin > 0) { 30 | switched = true 31 | 32 | } 33 | 34 | if (switched) end = end - 1 else begin = begin - 1 35 | } 36 | 37 | i = i + 1 38 | } 39 | 40 | println("\n------") 41 | 42 | (begin, end) 43 | } 44 | } -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Android 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/test/scala/reductions/LineOfSightSuite.scala: -------------------------------------------------------------------------------- 1 | package reductions 2 | 3 | import java.util.concurrent._ 4 | import scala.collection._ 5 | import org.scalatest.FunSuite 6 | import org.junit.runner.RunWith 7 | import org.scalatest.junit.JUnitRunner 8 | import common._ 9 | import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class LineOfSightSuite extends FunSuite { 13 | import LineOfSight._ 14 | test("lineOfSight should correctly handle an array of size 4") { 15 | val output = new Array[Float](4) 16 | lineOfSight(Array[Float](0f, 1f, 8f, 9f), output) 17 | assert(output.toList == List(0f, 1f, 4f, 4f)) 18 | } 19 | 20 | 21 | test("upsweepSequential should correctly handle the chunk 1 until 4 of an array of 4 elements") { 22 | val res = upsweepSequential(Array[Float](0f, 1f, 8f, 9f), 1, 4) 23 | assert(res == 4f) 24 | } 25 | 26 | 27 | test("downsweepSequential should correctly handle a 4 element array when the starting angle is zero") { 28 | val output = new Array[Float](4) 29 | downsweepSequential(Array[Float](0f, 1f, 8f, 9f), output, 0f, 1, 4) 30 | assert(output.toList == List(0f, 1f, 4f, 4f)) 31 | } 32 | 33 | 34 | test("parLineOfSight should correctly handle an array of size 4") { 35 | val output = new Array[Float](6) 36 | parLineOfSight(Array[Float](0f, 1f, 8f, 9f, 13f, 23f), output, 1) 37 | assert(output.toList == List(0f, 1f, 4f, 4f, 4f, 4.6f)) 38 | } 39 | 40 | 41 | 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/main/scala/common/package.scala: -------------------------------------------------------------------------------- 1 | 2 | import java.util.concurrent._ 3 | import scala.util.DynamicVariable 4 | 5 | package object common { 6 | 7 | val forkJoinPool = new ForkJoinPool 8 | 9 | abstract class TaskScheduler { 10 | def schedule[T](body: => T): ForkJoinTask[T] 11 | def parallel[A, B](taskA: => A, taskB: => B): (A, B) = { 12 | val right = task { 13 | taskB 14 | } 15 | val left = taskA 16 | (left, right.join()) 17 | } 18 | } 19 | 20 | class DefaultTaskScheduler extends TaskScheduler { 21 | def schedule[T](body: => T): ForkJoinTask[T] = { 22 | val t = new RecursiveTask[T] { 23 | def compute = body 24 | } 25 | Thread.currentThread match { 26 | case wt: ForkJoinWorkerThread => 27 | t.fork() 28 | case _ => 29 | forkJoinPool.execute(t) 30 | } 31 | t 32 | } 33 | } 34 | 35 | val scheduler = 36 | new DynamicVariable[TaskScheduler](new DefaultTaskScheduler) 37 | 38 | def task[T](body: => T): ForkJoinTask[T] = { 39 | scheduler.value.schedule(body) 40 | } 41 | 42 | def parallel[A, B](taskA: => A, taskB: => B): (A, B) = { 43 | // println ("par") 44 | scheduler.value.parallel(taskA, taskB) 45 | } 46 | 47 | def parallel[A, B, C, D](taskA: => A, taskB: => B, taskC: => C, taskD: => D): (A, B, C, D) = { 48 | val ta = task { taskA } 49 | val tb = task { taskB } 50 | val tc = task { taskC } 51 | val td = taskD 52 | (ta.join(), tb.join(), tc.join(), td) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/scala/kmeans/fun/package.scala: -------------------------------------------------------------------------------- 1 | package kmeans 2 | 3 | import common._ 4 | 5 | package object fun { 6 | 7 | /** The value of every pixel is represented as a 32 bit integer. */ 8 | type RGBA = Int 9 | 10 | /** Returns the alpha component. */ 11 | def alpha(c: RGBA): Double = ((0xff000000 & c) >>> 24).toDouble / 256 12 | 13 | /** Returns the red component. */ 14 | def red(c: RGBA): Double = ((0x00ff0000 & c) >>> 16).toDouble / 256 15 | 16 | /** Returns the green component. */ 17 | def green(c: RGBA): Double = ((0x0000ff00 & c) >>> 8).toDouble / 256 18 | 19 | /** Returns the blue component. */ 20 | def blue(c: RGBA): Double = ((0x000000ff & c) >>> 0).toDouble / 256 21 | 22 | /** Used to create an RGBA value from separate components. */ 23 | def rgba(r: Double, g: Double, b: Double, a: Double): RGBA = { 24 | (clamp((a * 256).toInt, 0, 255) << 24) | 25 | (clamp((r * 256).toInt, 0, 255) << 16) | 26 | (clamp((g * 256).toInt, 0, 255) << 8) | 27 | (clamp((b * 256).toInt, 0, 255) << 0) 28 | } 29 | 30 | /** Restricts the integer into the specified range. */ 31 | def clamp(v: Int, min: Int, max: Int): Int = { 32 | if (v < min) min 33 | else if (v > max) max 34 | else v 35 | } 36 | 37 | /** Image is a two-dimensional matrix of pixel values. */ 38 | class Img(val width: Int, val height: Int, private val data: Array[RGBA]) { 39 | def this(w: Int, h: Int) = this(w, h, new Array(w * h)) 40 | def apply(x: Int, y: Int): RGBA = data(y * width + x) 41 | def update(x: Int, y: Int, c: RGBA): Unit = data(y * width + x) = c 42 | } 43 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/workspace.xml 8 | .idea/tasks.xml 9 | .idea/dictionaries 10 | .idea/vcs.xml 11 | .idea/jsLibraryMappings.xml 12 | 13 | # Sensitive or high-churn files: 14 | .idea/dataSources.ids 15 | .idea/dataSources.xml 16 | .idea/dataSources.local.xml 17 | .idea/sqlDataSources.xml 18 | .idea/dynamic.xml 19 | .idea/uiDesigner.xml 20 | 21 | # Gradle: 22 | .idea/gradle.xml 23 | .idea/libraries 24 | 25 | # Mongo Explorer plugin: 26 | .idea/mongoSettings.xml 27 | 28 | ## File-based project format: 29 | *.iws 30 | 31 | ## Plugin-specific files: 32 | 33 | # IntelliJ 34 | /out/ 35 | 36 | # mpeltonen/sbt-idea plugin 37 | .idea_modules/ 38 | 39 | # JIRA plugin 40 | atlassian-ide-plugin.xml 41 | 42 | # Crashlytics plugin (for Android Studio and IntelliJ) 43 | com_crashlytics_export_strings.xml 44 | crashlytics.properties 45 | crashlytics-build.properties 46 | fabric.properties 47 | ### SBT template 48 | # Simple Build Tool 49 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 50 | 51 | target/ 52 | lib_managed/ 53 | src_managed/ 54 | project/boot/ 55 | .history 56 | .cache 57 | ### Scala template 58 | *.class 59 | *.log 60 | 61 | # sbt specific 62 | .lib/ 63 | dist/* 64 | project/plugins/project/ 65 | 66 | # Scala-IDE specific 67 | .scala_dependencies 68 | .worksheet 69 | -------------------------------------------------------------------------------- /src/main/scala/example/Lists.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | object Lists { 4 | 5 | /** 6 | * This method computes the sum of all elements in the list xs. There are 7 | * multiple techniques that can be used for implementing this method, and 8 | * you will learn during the class. 9 | * 10 | * For this example assignment you can use the following methods in class 11 | * `List`: 12 | * 13 | * - `xs.isEmpty: Boolean` returns `true` if the list `xs` is empty 14 | * - `xs.head: Int` returns the head element of the list `xs`. If the list 15 | * is empty an exception is thrown 16 | * - `xs.tail: List[Int]` returns the tail of the list `xs`, i.e. the the 17 | * list `xs` without its `head` element 18 | * 19 | * ''Hint:'' instead of writing a `for` or `while` loop, think of a recursive 20 | * solution. 21 | * 22 | * @param xs A list of natural numbers 23 | * @return The sum of all elements in `xs` 24 | */ 25 | def sum(xs: List[Int]): Int = xs match { 26 | case List() => 0 27 | case _ => xs.head + sum(xs.tail) 28 | } 29 | 30 | /** 31 | * This method returns the largest element in a list of integers. If the 32 | * list `xs` is empty it throws a `java.util.NoSuchElementException`. 33 | * 34 | * You can use the same methods of the class `List` as mentioned above. 35 | * 36 | * ''Hint:'' Again, think of a recursive solution instead of using looping 37 | * constructs. You might need to define an auxiliary method. 38 | * 39 | * @param xs A list of natural numbers 40 | * @return The largest element in `xs` 41 | * @throws java.util.NoSuchElementException if `xs` is an empty list 42 | */ 43 | def max(xs: List[Int]): Int = xs match { 44 | case List() => throw new java.util.NoSuchElementException 45 | case x :: Nil => x 46 | case _ => val t: Int = max(xs.tail); if (t > xs.head) t else xs.head 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/scala/scalashop/wip.sc: -------------------------------------------------------------------------------- 1 | import common._ 2 | 3 | import scalashop._ 4 | 5 | 6 | object test { 7 | // val src = new Img(15, 15) 8 | // 9 | // for (x <- 0 until 5; y <- 0 until 5) 10 | // src(x, y) = rgba(x, y, x + y, math.abs(x - y)) 11 | 12 | // boxBlurKernel(src, 0, 0, 2) 13 | 14 | // val from = 10 15 | // val end = 15 16 | // for(w <- from until end) yield w 17 | 18 | val r = Range(0,5) by Math.max(1/24,1) 19 | val tasks = for( 20 | z <- r 21 | ) yield { 22 | val b = task{ 23 | r.sum 24 | } 25 | } 26 | // b.join() 27 | 28 | 29 | val w = 4 30 | val h = 3 31 | val src = new Img(w, h) 32 | val dst = new Img(w, h) 33 | src(0, 0) = 0; src(1, 0) = 1; src(2, 0) = 2; src(3, 0) = 9 34 | src(0, 1) = 3; src(1, 1) = 4; src(2, 1) = 5; src(3, 1) = 10 35 | src(0, 2) = 6; src(1, 2) = 7; src(2, 2) = 8; src(3, 2) = 11 36 | 37 | blur(src, dst, 0, 4, 2) 38 | 39 | def boxBlurKernel(src: Img, x: Int, y: Int, radius: Int) = { 40 | 41 | val pixels = { 42 | for ( 43 | i <- -radius to radius; 44 | j <- -radius to radius 45 | ) yield (scalashop.clamp(x + i, 0, src.width - 1), scalashop.clamp(y + j, 0, src.height - 1)) 46 | }.distinct.map({ 47 | case (x, y) => 48 | val pixel = src(x, y) 49 | (red(pixel), green(pixel), blue(pixel), alpha(pixel)) 50 | }) 51 | 52 | rgba( 53 | pixels.map(_._1).sum / pixels.length, 54 | pixels.map(_._2).sum / pixels.length, 55 | pixels.map(_._3).sum / pixels.length, 56 | pixels.map(_._4).sum / pixels.length 57 | ) 58 | 59 | 60 | } 61 | 62 | def blur(src: Img, dst: Img, from: Int, end: Int, radius: Int): Unit = { 63 | // TODO implement this method using the `boxBlurKernel` method 64 | for( 65 | x <- from until end; 66 | y <- 0 until src.height; 67 | if x >= 0 && x < src.width 68 | ) yield { 69 | 70 | println(x +", " + y + " = "+ src(x,y) + " => " + boxBlurKernel(src, x, y, radius)) 71 | dst.update(x, y, boxBlurKernel(src, x, y, radius)) 72 | } 73 | dst 74 | } 75 | 76 | 77 | } -------------------------------------------------------------------------------- /src/main/scala/scalashop/package.scala: -------------------------------------------------------------------------------- 1 | 2 | import common._ 3 | 4 | package object scalashop { 5 | 6 | /** The value of every pixel is represented as a 32 bit integer. */ 7 | type RGBA = Int 8 | 9 | /** Returns the red component. */ 10 | def red(c: RGBA): Int = (0xff000000 & c) >>> 24 11 | 12 | /** Returns the green component. */ 13 | def green(c: RGBA): Int = (0x00ff0000 & c) >>> 16 14 | 15 | /** Returns the blue component. */ 16 | def blue(c: RGBA): Int = (0x0000ff00 & c) >>> 8 17 | 18 | /** Returns the alpha component. */ 19 | def alpha(c: RGBA): Int = (0x000000ff & c) >>> 0 20 | 21 | /** Used to create an RGBA value from separate components. */ 22 | def rgba(r: Int, g: Int, b: Int, a: Int): RGBA = { 23 | (r << 24) | (g << 16) | (b << 8) | (a << 0) 24 | } 25 | 26 | /** Restricts the integer into the specified range. */ 27 | def clamp(v: Int, min: Int, max: Int): Int = { 28 | if (v < min) min 29 | else if (v > max) max 30 | else v 31 | } 32 | 33 | /** Image is a two-dimensional matrix of pixel values. */ 34 | class Img(val width: Int, val height: Int, private val data: Array[RGBA]) { 35 | def this(w: Int, h: Int) = this(w, h, new Array(w * h)) 36 | 37 | def apply(x: Int, y: Int): RGBA = data(y * width + x) 38 | 39 | def update(x: Int, y: Int, c: RGBA): Unit = data(y * width + x) = c 40 | } 41 | 42 | /** Computes the blurred RGBA value of a single pixel of the input image. */ 43 | def boxBlurKernel(src: Img, x: Int, y: Int, radius: Int): RGBA = { 44 | // TODO implement using while loops 45 | val pixels = { 46 | for ( 47 | i <- -radius to radius; 48 | j <- -radius to radius 49 | ) yield (scalashop.clamp(x + i, 0, src.width - 1), scalashop.clamp(y + j, 0, src.height - 1)) 50 | }.distinct.map({ 51 | case (x, y) => 52 | val pixel = src(x, y) 53 | (red(pixel), green(pixel), blue(pixel), alpha(pixel)) 54 | }) 55 | 56 | rgba( 57 | pixels.map(_._1).sum / pixels.length, 58 | pixels.map(_._2).sum / pixels.length, 59 | pixels.map(_._3).sum / pixels.length, 60 | pixels.map(_._4).sum / pixels.length 61 | ) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/scala/scalashop/HorizontalBoxBlur.scala: -------------------------------------------------------------------------------- 1 | package scalashop 2 | 3 | import org.scalameter._ 4 | import common._ 5 | 6 | object HorizontalBoxBlurRunner { 7 | 8 | val standardConfig = config( 9 | Key.exec.minWarmupRuns -> 5, 10 | Key.exec.maxWarmupRuns -> 10, 11 | Key.exec.benchRuns -> 10, 12 | Key.verbose -> true 13 | ) withWarmer(new Warmer.Default) 14 | 15 | def main(args: Array[String]): Unit = { 16 | val radius = 3 17 | val width = 1920 18 | val height = 1080 19 | val src = new Img(width, height) 20 | val dst = new Img(width, height) 21 | val seqtime = standardConfig measure { 22 | HorizontalBoxBlur.blur(src, dst, 0, height, radius) 23 | } 24 | println(s"sequential blur time: $seqtime ms") 25 | 26 | val numTasks = 32 27 | val partime = standardConfig measure { 28 | HorizontalBoxBlur.parBlur(src, dst, numTasks, radius) 29 | } 30 | println(s"fork/join blur time: $partime ms") 31 | println(s"speedup: ${seqtime / partime}") 32 | } 33 | } 34 | 35 | 36 | /** A simple, trivially parallelizable computation. */ 37 | object HorizontalBoxBlur { 38 | 39 | /** Blurs the rows of the source image `src` into the destination image `dst`, 40 | * starting with `from` and ending with `end` (non-inclusive). 41 | * 42 | * Within each row, `blur` traverses the pixels by going from left to right. 43 | */ 44 | def blur(src: Img, dst: Img, from: Int, end: Int, radius: Int): Unit = { 45 | for( 46 | x <- 0 until src.width; 47 | y <- from until end; 48 | if y >= 0 && y < src.height 49 | ) yield { 50 | dst.update(x, y, boxBlurKernel(src, x, y, radius)) 51 | } 52 | } 53 | 54 | /** Blurs the rows of the source image in parallel using `numTasks` tasks. 55 | * 56 | * Parallelization is done by stripping the source image `src` into 57 | * `numTasks` separate strips, where each strip is composed of some number of 58 | * rows. 59 | */ 60 | def parBlur(src: Img, dst: Img, numTasks: Int, radius: Int): Unit = { 61 | val rowsPerTaks:Int = Math.max(src.height / numTasks, 1) 62 | val startPoints = Range(0, src.height) by rowsPerTaks 63 | 64 | val tasks = startPoints.map(t => { 65 | task { 66 | blur(src, dst, t, t + rowsPerTaks, radius) 67 | } 68 | }) 69 | 70 | tasks.map(t => t.join()) 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/conctrees/ConcBuffer.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | package conctrees 3 | 4 | import scala.reflect.ClassTag 5 | import org.scalameter._ 6 | 7 | class ConcBuffer[@specialized(Byte, Char, Int, Long, Float, Double) T: ClassTag]( 8 | val k: Int, private var conc: Conc[T] 9 | ) extends Traversable[T] { 10 | require(k > 0) 11 | 12 | def this() = this(128, Conc.Empty) 13 | 14 | private var chunk: Array[T] = new Array(k) 15 | private var lastSize: Int = 0 16 | 17 | def foreach[U](f: T => U): Unit = { 18 | conc.foreach(f) 19 | 20 | var i = 0 21 | while (i < lastSize) { 22 | f(chunk(i)) 23 | i += 1 24 | } 25 | } 26 | 27 | final def +=(elem: T): this.type = { 28 | if (lastSize >= k) expand() 29 | chunk(lastSize) = elem 30 | lastSize += 1 31 | this 32 | } 33 | 34 | final def combine(that: ConcBuffer[T]): ConcBuffer[T] = { 35 | val combinedConc = this.result <> that.result 36 | this.clear() 37 | that.clear() 38 | new ConcBuffer(k, combinedConc) 39 | } 40 | 41 | private def pack() { 42 | conc = Conc.appendTop(conc, new Conc.Chunk(chunk, lastSize, k)) 43 | } 44 | 45 | private def expand() { 46 | pack() 47 | chunk = new Array(k) 48 | lastSize = 0 49 | } 50 | 51 | def clear() { 52 | conc = Conc.Empty 53 | chunk = new Array(k) 54 | lastSize = 0 55 | } 56 | 57 | def result: Conc[T] = { 58 | pack() 59 | conc 60 | } 61 | } 62 | 63 | object ConcBufferRunner { 64 | 65 | val standardConfig = config( 66 | Key.exec.minWarmupRuns -> 20, 67 | Key.exec.maxWarmupRuns -> 40, 68 | Key.exec.benchRuns -> 60, 69 | Key.verbose -> true 70 | ) withWarmer(new Warmer.Default) 71 | 72 | def main(args: Array[String]) { 73 | val size = 1000000 74 | 75 | def run(p: Int) { 76 | val taskSupport = new collection.parallel.ForkJoinTaskSupport( 77 | new scala.concurrent.forkjoin.ForkJoinPool(p)) 78 | val strings = (0 until size).map(_.toString) 79 | val time = standardConfig measure { 80 | val parallelized = strings.par 81 | parallelized.tasksupport = taskSupport 82 | parallelized.aggregate(new ConcBuffer[String])(_ += _, _ combine _).result 83 | } 84 | println(s"p = $p, time = $time ms") 85 | } 86 | 87 | run(1) 88 | run(2) 89 | run(4) 90 | run(8) 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/scala/scalashop/VerticalBoxBlur.scala: -------------------------------------------------------------------------------- 1 | package scalashop 2 | 3 | import org.scalameter._ 4 | import common._ 5 | 6 | object VerticalBoxBlurRunner { 7 | 8 | val standardConfig = config( 9 | Key.exec.minWarmupRuns -> 5, 10 | Key.exec.maxWarmupRuns -> 10, 11 | Key.exec.benchRuns -> 10, 12 | Key.verbose -> true 13 | ) withWarmer(new Warmer.Default) 14 | 15 | def main(args: Array[String]): Unit = { 16 | val radius = 3 17 | val width = 1920 18 | val height = 1080 19 | val src = new Img(width, height) 20 | val dst = new Img(width, height) 21 | val seqtime = standardConfig measure { 22 | VerticalBoxBlur.blur(src, dst, 0, width, radius) 23 | } 24 | println(s"sequential blur time: $seqtime ms") 25 | 26 | val numTasks = 32 27 | val partime = standardConfig measure { 28 | VerticalBoxBlur.parBlur(src, dst, numTasks, radius) 29 | } 30 | println(s"fork/join blur time: $partime ms") 31 | println(s"speedup: ${seqtime / partime}") 32 | } 33 | 34 | } 35 | 36 | /** A simple, trivially parallelizable computation. */ 37 | object VerticalBoxBlur { 38 | 39 | /** Blurs the columns of the source image `src` into the destination image 40 | * `dst`, starting with `from` and ending with `end` (non-inclusive). 41 | * 42 | * Within each column, `blur` traverses the pixels by going from top to 43 | * bottom. 44 | */ 45 | def blur(src: Img, dst: Img, from: Int, end: Int, radius: Int): Unit = { 46 | for( 47 | x <- from until end; 48 | y <- 0 until src.height; 49 | if x >= 0 && x < src.width 50 | ) yield { 51 | dst.update(x, y, boxBlurKernel(src, x, y, radius)) 52 | } 53 | 54 | } 55 | 56 | /** Blurs the columns of the source image in parallel using `numTasks` tasks. 57 | * 58 | * Parallelization is done by stripping the source image `src` into 59 | * `numTasks` separate strips, where each strip is composed of some number of 60 | * columns. 61 | */ 62 | def parBlur(src: Img, dst: Img, numTasks: Int, radius: Int): Unit = { 63 | val colsPerTaks:Int = Math.max(src.width / numTasks,1) 64 | val startPoints = Range(0, src.width) by colsPerTaks 65 | 66 | val tasks = startPoints.map(t => { 67 | task { 68 | blur(src, dst, t, t + colsPerTaks, radius) 69 | } 70 | }) 71 | 72 | tasks.map(t => t.join()) 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/scala/scalashop/PhotoCanvas.scala: -------------------------------------------------------------------------------- 1 | package scalashop 2 | 3 | import java.awt._ 4 | import java.awt.event._ 5 | import java.awt.image._ 6 | import java.io._ 7 | import javax.imageio._ 8 | import javax.swing._ 9 | import javax.swing.event._ 10 | import common._ 11 | 12 | class PhotoCanvas extends JComponent { 13 | 14 | var imagePath: Option[String] = None 15 | 16 | var image = loadScalaImage() 17 | 18 | override def getPreferredSize = { 19 | new Dimension(image.width, image.height) 20 | } 21 | 22 | private def loadScalaImage(): Img = { 23 | val stream = this.getClass.getResourceAsStream("/scalashop/scala.jpg") 24 | try { 25 | loadImage(stream) 26 | } finally { 27 | stream.close() 28 | } 29 | } 30 | 31 | private def loadFileImage(path: String): Img = { 32 | val stream = new FileInputStream(path) 33 | try { 34 | loadImage(stream) 35 | } finally { 36 | stream.close() 37 | } 38 | } 39 | 40 | private def loadImage(inputStream: InputStream): Img = { 41 | val bufferedImage = ImageIO.read(inputStream) 42 | val width = bufferedImage.getWidth 43 | val height = bufferedImage.getHeight 44 | val img = new Img(width, height) 45 | for (x <- 0 until width; y <- 0 until height) img(x, y) = bufferedImage.getRGB(x, y) 46 | img 47 | } 48 | 49 | def reload(): Unit = { 50 | image = imagePath match { 51 | case Some(path) => loadFileImage(path) 52 | case None => loadScalaImage() 53 | } 54 | repaint() 55 | } 56 | 57 | def loadFile(path: String): Unit = { 58 | imagePath = Some(path) 59 | reload() 60 | } 61 | 62 | def applyFilter(filterName: String, numTasks: Int, radius: Int) { 63 | val dst = new Img(image.width, image.height) 64 | filterName match { 65 | case "horizontal-box-blur" => 66 | HorizontalBoxBlur.parBlur(image, dst, numTasks, radius) 67 | case "vertical-box-blur" => 68 | VerticalBoxBlur.parBlur(image, dst, numTasks, radius) 69 | case "" => 70 | } 71 | image = dst 72 | repaint() 73 | } 74 | 75 | override def paintComponent(gcan: Graphics) = { 76 | super.paintComponent(gcan) 77 | 78 | val width = image.width 79 | val height = image.height 80 | val bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) 81 | for (x <- 0 until width; y <- 0 until height) bufferedImage.setRGB(x, y, image(x, y)) 82 | 83 | gcan.drawImage(bufferedImage, 0, 0, null) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/scala/reductions/graderOutput.txt: -------------------------------------------------------------------------------- 1 | Your overall score for this assignment is 9.33 out of 10.00 2 | 3 | 4 | The code you submitted did not pass all of our tests: your submission achieved a score of 5 | 9.33 out of 10.00 in our tests. 6 | 7 | In order to find bugs in your code, we advise to perform the following steps: 8 | - Take a close look at the test output that you can find below: it should point you to 9 | the part of your code that has bugs. 10 | - Run the tests that we provide with the handout on your code. 11 | - The tests we provide do not test your code in depth: they are very incomplete. In order 12 | to test more aspects of your code, write your own unit tests. 13 | - Take another very careful look at the assignment description. Try to find out if you 14 | misunderstood parts of it. While reading through the assignment, write more tests. 15 | 16 | Below you can find a short feedback for every individual test that failed. 17 | 18 | ======== LOG OF FAILED TESTS ======== 19 | Your solution achieved a testing score of 140 out of 150. 20 | 21 | Below you can see a short feedback for every test that failed, 22 | indicating the reason for the test failure and how many points 23 | you lost for each individual test. 24 | 25 | Tests that were aborted took too long too complete or crashed the 26 | JVM. Such crashes can arise due to infinite non-terminitaing 27 | loops or recursion (StackOverflowException) or excessive memory 28 | consumption (OutOfMemoryException). 29 | 30 | [Test Description] parLineOfSight should invoke the parallel construct 30 times (15 times during upsweep and 15 times during downsweep) for an array of size 17, with threshold 1 31 | [Observed Error] 32 did not equal 30 (The number of parallel calls should be 30) 32 | [Lost Points] 3 33 | 34 | [Test Description] parLineOfSight should call parallel constuct 6 times, where the last two parallel constructs should update the 4 sections of the array (1 until 5), (5 until 9), (9 until 13), (13 until 17), respectively 35 | [Observed Error] success was false [During the execution of first part of 5th call to parallel construct, the indices 1 until 5 of the output array was not correctly updated] 36 | [Lost Points] 6 37 | 38 | [Test Description] combinedThreshold should return true when the number of coins times money is less than or equal to half of the initial number of coins times starting money 39 | [Observed Error] false did not equal true combinedThreshold should return true 40 | [Lost Points] 1 41 | 42 | ======== TESTING ENVIRONMENT ======== 43 | Limits: memory: 256m, total time: 850s, per test case time: 240s 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/SimulationModel.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | 3 | import java.awt._ 4 | import java.awt.event._ 5 | import javax.swing._ 6 | import javax.swing.event._ 7 | import scala.collection.parallel.{TaskSupport, defaultTaskSupport} 8 | 9 | class SimulationModel { 10 | 11 | var screen = new Boundaries 12 | 13 | var bodies: Seq[Body] = Nil 14 | 15 | var quad: Quad = Empty(screen.centerX, screen.centerY, Float.MaxValue) 16 | 17 | var shouldRenderQuad = false 18 | 19 | var timeStats = new TimeStatistics 20 | 21 | var taskSupport: TaskSupport = defaultTaskSupport 22 | 23 | def initialize(parallelismLevel: Int, pattern: String, totalBodies: Int) { 24 | taskSupport = new collection.parallel.ForkJoinTaskSupport( 25 | new scala.concurrent.forkjoin.ForkJoinPool(parallelismLevel)) 26 | 27 | pattern match { 28 | case "two-galaxies" => init2Galaxies(totalBodies) 29 | case _ => sys.error(s"no such initial pattern: $pattern") 30 | } 31 | } 32 | 33 | def init2Galaxies(totalBodies: Int) { 34 | val bodyArray = new Array[Body](totalBodies) 35 | val random = new scala.util.Random(213L) 36 | 37 | def galaxy(from: Int, num: Int, maxradius: Float, cx: Float, cy: Float, sx: Float, sy: Float) { 38 | val totalM = 1.5f * num 39 | val blackHoleM = 1.0f * num 40 | val cubmaxradius = maxradius * maxradius * maxradius 41 | for (i <- from until (from + num)) { 42 | val b = if (i == from) { 43 | new Body(blackHoleM, cx, cy, sx, sy) 44 | } else { 45 | val angle = random.nextFloat * 2 * math.Pi 46 | val radius = 25 + maxradius * random.nextFloat 47 | val starx = cx + radius * math.sin(angle).toFloat 48 | val stary = cy + radius * math.cos(angle).toFloat 49 | val speed = math.sqrt(gee * blackHoleM / radius + gee * totalM * radius * radius / cubmaxradius) 50 | val starspeedx = sx + (speed * math.sin(angle + math.Pi / 2)).toFloat 51 | val starspeedy = sy + (speed * math.cos(angle + math.Pi / 2)).toFloat 52 | val starmass = 1.0f + 1.0f * random.nextFloat 53 | new Body(starmass, starx, stary, starspeedx, starspeedy) 54 | } 55 | bodyArray(i) = b 56 | } 57 | } 58 | 59 | galaxy(0, bodyArray.length / 8, 300.0f, 0.0f, 0.0f, 0.0f, 0.0f) 60 | galaxy(bodyArray.length / 8, bodyArray.length / 8 * 7, 350.0f, -1800.0f, -1200.0f, 0.0f, 0.0f) 61 | 62 | bodies = bodyArray.toSeq 63 | 64 | // compute center and boundaries 65 | screen = new Boundaries 66 | screen.minX = -2200.0f 67 | screen.minY = -1600.0f 68 | screen.maxX = 350.0f 69 | screen.maxY = 350.0f 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /project/StyleChecker.scala: -------------------------------------------------------------------------------- 1 | import sbt.File 2 | import java.io.ByteArrayOutputStream 3 | import java.io.PrintStream 4 | import org.scalastyle._ 5 | import com.typesafe.config.ConfigFactory 6 | 7 | object StyleChecker { 8 | val maxResult = 100 9 | 10 | class CustomTextOutput[T <: FileSpec](stream: PrintStream) extends Output[T] { 11 | private val messageHelper = new MessageHelper(ConfigFactory.load()) 12 | 13 | var fileCount: Int = _ 14 | override def message(m: Message[T]): Unit = m match { 15 | case StartWork() => 16 | case EndWork() => 17 | case StartFile(file) => 18 | stream.print("Checking file " + file + "...") 19 | fileCount = 0 20 | case EndFile(file) => 21 | if (fileCount == 0) stream.println(" OK!") 22 | case StyleError(file, clazz, key, level, args, line, column, customMessage) => 23 | report(line, column, messageHelper.text(level.name), 24 | Output.findMessage(messageHelper, key, args, customMessage)) 25 | case StyleException(file, clazz, message, stacktrace, line, column) => 26 | report(line, column, "error", message) 27 | } 28 | 29 | private def report(line: Option[Int], column: Option[Int], level: String, message: String) { 30 | if (fileCount == 0) stream.println("") 31 | fileCount += 1 32 | stream.println(" " + fileCount + ". " + level + pos(line, column) + ":") 33 | stream.println(" " + message) 34 | } 35 | 36 | private def pos(line: Option[Int], column: Option[Int]): String = line match { 37 | case Some(lineNumber) => " at line " + lineNumber + (column match { 38 | case Some(columnNumber) => " character " + columnNumber 39 | case None => "" 40 | }) 41 | case None => "" 42 | } 43 | } 44 | 45 | def score(outputResult: OutputResult) = { 46 | val penalties = outputResult.errors + outputResult.warnings 47 | scala.math.max(maxResult - penalties, 0) 48 | } 49 | 50 | def assess(sources: Seq[File], styleSheetPath: String): (String, Int) = { 51 | val configFile = new File(styleSheetPath).getAbsolutePath 52 | 53 | val messages = new ScalastyleChecker().checkFiles( 54 | ScalastyleConfiguration.readFromXml(configFile), 55 | Directory.getFiles(None, sources)) 56 | 57 | val output = new ByteArrayOutputStream() 58 | val outputResult = new CustomTextOutput(new PrintStream(output)).output(messages) 59 | 60 | val msg = s"""${output.toString} 61 | |Processed ${outputResult.files} file(s) 62 | |Found ${outputResult.errors} errors 63 | |Found ${outputResult.warnings} warnings 64 | |""".stripMargin 65 | 66 | (msg, score(outputResult)) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /project/CommonBuild.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | /** 4 | * @param packageName Used as the prefix for: (1) handout name, (2) the Scala package, (3) source folder. 5 | * @param key Per assignment key specified by coursera. 6 | * @param partId Identifies the part of the assignment. (We always have one-part assignments.) 7 | * @param maxScore Maximum score that can be given for the assignment. Must match the value in the WebAPI. 8 | * @param styleScoreRatio Defines the portion of the grade that is assigned to style. 9 | * @param dependencies Library dependencies specific to this module. 10 | * @param styleSheet Path to the scalastyle configuration for this assignment. 11 | * @param options Options passed to the java process or coursera infrastructure. Following values are 12 | * supported: 13 | * 14 | * NAME DEFAULT DESCRIPTION 15 | * Xms 10m -Xms for jvm 16 | * Xmx 256m -Xmx for jvm, should less than `grader-memory` 17 | * individualTimeout 240 time out of one test case 18 | * totalTimeout 850 total time out, should less than `grader-timeout` 19 | * grader-cpu 1 number of cpu for coursera infrastructure 20 | * grader-memory 1024 memory for coursera infrastructure 21 | * grader-timeout 1200 grading timeout for coursera infrastructure 22 | */ 23 | case class Assignment(packageName: String, 24 | key: String, 25 | itemId: String, 26 | partId: String, 27 | maxScore: Double, 28 | styleScoreRatio: Double = 0.0d, 29 | styleSheet: String = "", 30 | dependencies: Seq[ModuleID] = Seq(), 31 | options: Map[String, String] = Map()) { 32 | assert(!(styleScoreRatio == 0.0d ^ styleSheet == ""), "Style sheet and style ratio should be defined in pair.") 33 | } 34 | 35 | 36 | trait CommonBuild extends Build { 37 | 38 | val course = SettingKey[String]("course") 39 | 40 | val assignment = SettingKey[String]("assignment") 41 | 42 | val assignmentsMap = SettingKey[Map[String, Assignment]]("assignmentsMap") 43 | 44 | val courseId = SettingKey[String]("courseId") 45 | 46 | val commonSourcePackages = SettingKey[Seq[String]]("commonSourcePackages") 47 | 48 | lazy val scalaTestDependency = "org.scalatest" %% "scalatest" % "2.2.4" 49 | } 50 | -------------------------------------------------------------------------------- /src/main/scala/kmeans/fun/PhotoCanvas.scala: -------------------------------------------------------------------------------- 1 | package kmeans 2 | package fun 3 | 4 | import java.awt._ 5 | import java.awt.event._ 6 | import java.awt.image._ 7 | import java.io._ 8 | import javax.imageio._ 9 | import javax.swing._ 10 | import javax.swing.event._ 11 | import common._ 12 | 13 | class PhotoCanvas extends JComponent { 14 | 15 | var imagePath: Option[String] = None 16 | 17 | var image = loadEPFLImage() 18 | 19 | val timerDelay = 100 20 | val timer = 21 | new Timer(timerDelay, new ActionListener() { 22 | def actionPerformed(e: ActionEvent): Unit = repaint() 23 | }) 24 | 25 | override def getPreferredSize = { 26 | new Dimension(image.width, image.height) 27 | } 28 | 29 | private def loadEPFLImage(): Img = { 30 | val stream = this.getClass.getResourceAsStream("/kmeans/epfl-view.jpg") 31 | try { 32 | loadImage(stream) 33 | } finally { 34 | stream.close() 35 | } 36 | } 37 | 38 | private def loadFileImage(path: String): Img = { 39 | val stream = new FileInputStream(path) 40 | try { 41 | loadImage(stream) 42 | } finally { 43 | stream.close() 44 | } 45 | } 46 | 47 | private def loadImage(inputStream: InputStream): Img = { 48 | val bufferedImage = ImageIO.read(inputStream) 49 | val width = bufferedImage.getWidth 50 | val height = bufferedImage.getHeight 51 | val img = new Img(width, height) 52 | for (x <- 0 until width; y <- 0 until height) 53 | img(x, y) = bufferedImage.getRGB(x, y) 54 | img 55 | } 56 | 57 | def reload(): Unit = { 58 | image = imagePath match { 59 | case Some(path) => loadFileImage(path) 60 | case None => loadEPFLImage() 61 | } 62 | repaint() 63 | } 64 | 65 | def loadFile(path: String): Unit = { 66 | imagePath = Some(path) 67 | reload() 68 | } 69 | 70 | def saveFile(path: String): Unit = { 71 | reload() 72 | val stream = new FileOutputStream(path) 73 | val bufferedImage = new BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_ARGB) 74 | for (x <- 0 until image.width; y <- 0 until image.height) bufferedImage.setRGB(x, y, image(x, y)) 75 | ImageIO.write(bufferedImage, "png", stream) 76 | } 77 | 78 | def applyIndexedColors(colorCount: Int, initStrategy: InitialSelectionStrategy, convStrategy: ConvergenceStrategy): String = { 79 | val filter = new IndexedColorFilter(image, colorCount, initStrategy, convStrategy) 80 | image = filter.getResult() 81 | repaint() 82 | filter.getStatus() 83 | } 84 | 85 | override def paintComponent(gcan: Graphics) = { 86 | super.paintComponent(gcan) 87 | 88 | val width = image.width 89 | val height = image.height 90 | val bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) 91 | for (x <- 0 until width; y <- 0 until height) bufferedImage.setRGB(x, y, image(x, y)) 92 | 93 | gcan.drawImage(bufferedImage, 0, 0, null) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/graderOutput.txt: -------------------------------------------------------------------------------- 1 | Your overall score for this assignment is 9.05 out of 10.00 2 | 3 | 4 | The code you submitted did not pass all of our tests: your submission achieved a score of 5 | 9.05 out of 10.00 in our tests. 6 | 7 | In order to find bugs in your code, we advise to perform the following steps: 8 | - Take a close look at the test output that you can find below: it should point you to 9 | the part of your code that has bugs. 10 | - Run the tests that we provide with the handout on your code. 11 | - The tests we provide do not test your code in depth: they are very incomplete. In order 12 | to test more aspects of your code, write your own unit tests. 13 | - Take another very careful look at the assignment description. Try to find out if you 14 | misunderstood parts of it. While reading through the assignment, write more tests. 15 | 16 | Below you can find a short feedback for every individual test that failed. 17 | 18 | ======== LOG OF FAILED TESTS ======== 19 | Your solution achieved a testing score of 57 out of 63. 20 | 21 | Below you can see a short feedback for every test that failed, 22 | indicating the reason for the test failure and how many points 23 | you lost for each individual test. 24 | 25 | Tests that were aborted took too long too complete or crashed the 26 | JVM. Such crashes can arise due to infinite non-terminitaing 27 | loops or recursion (StackOverflowException) or excessive memory 28 | consumption (OutOfMemoryException). 29 | 30 | [Test Description] Leaf.insert(b) should return a new Fork if size > minimumSize 31 | [Observed Error] nw of the Fork, Empty(17.5,27.5,5.0), should be a Leaf 32 | [Lost Points] 2 33 | 34 | [Test Description] Fork.insert(b) should insert recursively in the appropriate quadrant 35 | [Observed Error] Fork(Empty(10.0,30.0,10.0),Leaf(20.0,30.0,10.0,List(barneshut.package$Body@30ee2816)),Leaf(10.0,40.0,10.0,List(barneshut.package$Body@31d7b7bf)),Leaf(20.0,40.0,10.0,List(barneshut.package$Body@635eaaf1))) should be a Fork where only ne changed 36 | [Lost Points] 2 37 | 38 | [Test Description] 'insert' should work correctly on a leaf with center (1,1) and size 2 39 | [Observed Error] Fork(Empty(0.5,0.5,1.0),Empty(1.5,0.5,1.0),Empty(0.5,1.5,1.0),Empty(1.5,1.5,1.0)) did not equal Fork(Leaf(0.5,0.5,1.0,List(barneshut.package$Body@1ddf84b8)),Leaf(1.5,0.5,1.0,List(barneshut.package$Body@1139b2f3)),Empty(0.5,1.5,1.0),Empty(1.5,1.5,1.0)) expected Fork(Leaf(0.5,0.5,1.0,List(barneshut.package$Body@1ddf84b8)),Leaf(1.5,0.5,1.0,List(barneshut.package$Body@1139b2f3)),Empty(0.5,1.5,1.0),Empty(1.5,1.5,1.0)) found Fork(Empty(0.5,0.5,1.0),Empty(1.5,0.5,1.0),Empty(0.5,1.5,1.0),Empty(1.5,1.5,1.0)) 40 | [Lost Points] 2 41 | 42 | ======== TESTING ENVIRONMENT ======== 43 | Limits: memory: 256m, total time: 850s, per test case time: 240s 44 | 45 | ======== DEBUG OUTPUT OF TESTING TOOL ======== 46 | matrix: 1353 ms; avg: NaN 47 | matrix: 9 ms; avg: NaN 48 | matrix: 1129 ms; avg: NaN 49 | update: 123 ms; avg: NaN 50 | update: 106 ms; avg: NaN -------------------------------------------------------------------------------- /src/test/scala/reductions/ParallelParenthesesBalancingSuite.scala: -------------------------------------------------------------------------------- 1 | package reductions 2 | 3 | import java.util.concurrent._ 4 | import scala.collection._ 5 | import org.scalatest.FunSuite 6 | import org.junit.runner.RunWith 7 | import org.scalatest.junit.JUnitRunner 8 | import common._ 9 | 10 | import ParallelParenthesesBalancing._ 11 | 12 | @RunWith(classOf[JUnitRunner]) 13 | class ParallelParenthesesBalancingSuite extends FunSuite { 14 | 15 | test("balance should work for empty string") { 16 | def check(input: String, expected: Boolean) = 17 | assert(balance(input.toArray) == expected, 18 | s"balance($input) should be $expected") 19 | 20 | check("", true) 21 | } 22 | 23 | test("balance should work for string of length 1") { 24 | def check(input: String, expected: Boolean) = 25 | assert(balance(input.toArray) == expected, 26 | s"balance($input) should be $expected") 27 | 28 | check("(", false) 29 | check(")", false) 30 | check(".", true) 31 | } 32 | 33 | test("balance should work for string of length 2") { 34 | def check(input: String, expected: Boolean) = 35 | assert(balance(input.toArray) == expected, 36 | s"balance($input) should be $expected") 37 | 38 | check("()", true) 39 | check(")(", false) 40 | check("((", false) 41 | check("))", false) 42 | check(".)", false) 43 | check(".(", false) 44 | check("(.", false) 45 | check(").", false) 46 | } 47 | 48 | 49 | test("parBalance should work for empty string") { 50 | def check(input: String, expected: Boolean) = 51 | assert(parBalance(input.toArray,2) == expected, 52 | s"balance($input) should be $expected") 53 | 54 | check("", true) 55 | } 56 | 57 | test("parBalance should work for string of length 1") { 58 | def check(input: String, expected: Boolean) = 59 | assert(parBalance(input.toArray,2) == expected, 60 | s"balance($input) should be $expected") 61 | 62 | check("(", false) 63 | check(")", false) 64 | check(".", true) 65 | } 66 | 67 | test("parBalance should work for string of length 2") { 68 | def check(input: String, expected: Boolean) = 69 | assert(parBalance(input.toArray,2) == expected, 70 | s"balance($input) should be $expected") 71 | 72 | check("()", true) 73 | check(")(", false) 74 | check("((", false) 75 | check("))", false) 76 | check(".)", false) 77 | check(".(", false) 78 | check("(.", false) 79 | check(").", false) 80 | } 81 | 82 | 83 | test("parBalance should work for long strings ") { 84 | def check(input: String, expected: Boolean) = 85 | assert(parBalance(input.toArray,4) == expected, 86 | s"balance($input) should be $expected") 87 | 88 | check("((0)(1)-2)((((3)456)---)78(9))", true) 89 | check("((x))(x)-x)((((x)xxx)---)xx(x)x)", false) 90 | check("((x)(x)-x)((()()()((x)xxx)---)xx(x)x)", true) 91 | check("((x)(x)-x)()()(((x)xxx)---)xx(x)x)", false) 92 | 93 | } 94 | 95 | 96 | } -------------------------------------------------------------------------------- /src/test/scala/scalashop/BlurSuite.scala: -------------------------------------------------------------------------------- 1 | package scalashop 2 | 3 | import java.util.concurrent._ 4 | import scala.collection._ 5 | import org.scalatest.FunSuite 6 | import org.junit.runner.RunWith 7 | import org.scalatest.junit.JUnitRunner 8 | import common._ 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class BlurSuite extends FunSuite { 12 | test("boxBlurKernel should correctly handle radius 0") { 13 | val src = new Img(5, 5) 14 | 15 | for (x <- 0 until 5; y <- 0 until 5) 16 | src(x, y) = rgba(x, y, x + y, math.abs(x - y)) 17 | 18 | for (x <- 0 until 5; y <- 0 until 5) 19 | assert(boxBlurKernel(src, x, y, 0) === rgba(x, y, x + y, math.abs(x - y)), 20 | "boxBlurKernel(_,_,0) should be identity.") 21 | } 22 | 23 | test("boxBlurKernel should return the correct value on an interior pixel " + 24 | "of a 3x4 image with radius 1") { 25 | val src = new Img(3, 4) 26 | src(0, 0) = 0; src(1, 0) = 1; src(2, 0) = 2 27 | src(0, 1) = 3; src(1, 1) = 4; src(2, 1) = 5 28 | src(0, 2) = 6; src(1, 2) = 7; src(2, 2) = 8 29 | src(0, 3) = 50; src(1, 3) = 11; src(2, 3) = 16 30 | 31 | assert(boxBlurKernel(src, 1, 2, 1) === 12, 32 | s"(boxBlurKernel(1, 2, 1) should be 12, " + 33 | s"but it's ${boxBlurKernel(src, 1, 2, 1)})") 34 | } 35 | 36 | test("HorizontalBoxBlur.blur with radius 1 should correctly blur the entire 3x3 image") { 37 | val w = 3 38 | val h = 3 39 | val src = new Img(w, h) 40 | val dst = new Img(w, h) 41 | src(0, 0) = 0; src(1, 0) = 1; src(2, 0) = 2 42 | src(0, 1) = 3; src(1, 1) = 4; src(2, 1) = 5 43 | src(0, 2) = 6; src(1, 2) = 7; src(2, 2) = 8 44 | 45 | HorizontalBoxBlur.blur(src, dst, 0, 2, 1) 46 | 47 | def check(x: Int, y: Int, expected: Int) = 48 | assert(dst(x, y) == expected, 49 | s"(destination($x, $y) should be $expected)") 50 | 51 | check(0, 0, 2) 52 | check(1, 0, 2) 53 | check(2, 0, 3) 54 | check(0, 1, 3) 55 | check(1, 1, 4) 56 | check(2, 1, 4) 57 | check(0, 2, 0) 58 | check(1, 2, 0) 59 | check(2, 2, 0) 60 | } 61 | 62 | test("VerticalBoxBlur.blur with radius 2 should correctly blur the entire " + 63 | "4x3 image") { 64 | val w = 4 65 | val h = 3 66 | val src = new Img(w, h) 67 | val dst = new Img(w, h) 68 | src(0, 0) = 0; src(1, 0) = 1; src(2, 0) = 2; src(3, 0) = 9 69 | src(0, 1) = 3; src(1, 1) = 4; src(2, 1) = 5; src(3, 1) = 10 70 | src(0, 2) = 6; src(1, 2) = 7; src(2, 2) = 8; src(3, 2) = 11 71 | 72 | VerticalBoxBlur.blur(src, dst, 0, 4, 2) 73 | 74 | 75 | def check(x: Int, y: Int, expected: Int) = 76 | assert(dst(x, y) == expected, 77 | s"(destination($x, $y) should be $expected)") 78 | 79 | check(0, 0, 4) 80 | check(1, 0, 5) 81 | check(2, 0, 5) 82 | check(3, 0, 6) 83 | check(0, 1, 4) 84 | check(1, 1, 5) 85 | check(2, 1, 5) 86 | check(3, 1, 6) 87 | check(0, 2, 4) 88 | check(1, 2, 5) 89 | check(2, 2, 5) 90 | check(3, 2, 6) 91 | 92 | 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/test/scala/kmeans/KMeansSuite.scala: -------------------------------------------------------------------------------- 1 | package kmeans 2 | 3 | import java.util.concurrent._ 4 | import scala.collection._ 5 | import org.scalatest.FunSuite 6 | import org.junit.runner.RunWith 7 | import org.scalatest.junit.JUnitRunner 8 | import common._ 9 | import scala.math._ 10 | 11 | object KM extends KMeans 12 | import KM._ 13 | 14 | @RunWith(classOf[JUnitRunner]) 15 | class KMeansSuite extends FunSuite { 16 | 17 | def checkClassify(points: GenSeq[Point], means: GenSeq[Point], expected: GenMap[Point, GenSeq[Point]]) { 18 | assert(classify(points, means) == expected, 19 | s"classify($points, $means) should equal to $expected") 20 | } 21 | 22 | test("'classify should work for empty 'points' and empty 'means'") { 23 | val points: GenSeq[Point] = IndexedSeq() 24 | val means: GenSeq[Point] = IndexedSeq() 25 | val expected = GenMap[Point, GenSeq[Point]]() 26 | checkClassify(points, means, expected) 27 | } 28 | 29 | test("'classify' should work for empty 'points' and 'means' == GenSeq(Point(1,1,1))") { 30 | val points: GenSeq[Point] = IndexedSeq() 31 | val mean = new Point(1, 1, 1) 32 | val means: GenSeq[Point] = IndexedSeq(mean) 33 | val expected = GenMap[Point, GenSeq[Point]]((mean, GenSeq())) 34 | checkClassify(points, means, expected) 35 | } 36 | 37 | test("'classify' should work for 'points' == GenSeq((1, 1, 0), (1, -1, 0), (-1, 1, 0), (-1, -1, 0)) and 'means' == GenSeq((0, 0, 0))") { 38 | val p1 = new Point(1, 1, 0) 39 | val p2 = new Point(1, -1, 0) 40 | val p3 = new Point(-1, 1, 0) 41 | val p4 = new Point(-1, -1, 0) 42 | val points: GenSeq[Point] = IndexedSeq(p1, p2, p3, p4) 43 | val mean = new Point(0, 0, 0) 44 | val means: GenSeq[Point] = IndexedSeq(mean) 45 | val expected = GenMap((mean, GenSeq(p1, p2, p3, p4))) 46 | checkClassify(points, means, expected) 47 | } 48 | 49 | test("'classify' should work for 'points' == GenSeq((1, 1, 0), (1, -1, 0), (-1, 1, 0), (-1, -1, 0)) and 'means' == GenSeq((1, 0, 0), (-1, 0, 0))") { 50 | val p1 = new Point(1, 1, 0) 51 | val p2 = new Point(1, -1, 0) 52 | val p3 = new Point(-1, 1, 0) 53 | val p4 = new Point(-1, -1, 0) 54 | val points: GenSeq[Point] = IndexedSeq(p1, p2, p3, p4) 55 | val mean1 = new Point(1, 0, 0) 56 | val mean2 = new Point(-1, 0, 0) 57 | val means: GenSeq[Point] = IndexedSeq(mean1, mean2) 58 | val expected = GenMap((mean1, GenSeq(p1, p2)), (mean2, GenSeq(p3, p4))) 59 | checkClassify(points, means, expected) 60 | } 61 | 62 | def checkParClassify(points: GenSeq[Point], means: GenSeq[Point], expected: GenMap[Point, GenSeq[Point]]) { 63 | assert(classify(points.par, means.par) == expected, 64 | s"classify($points par, $means par) should equal to $expected") 65 | } 66 | 67 | test("'classify with data parallelism should work for empty 'points' and empty 'means'") { 68 | val points: GenSeq[Point] = IndexedSeq() 69 | val means: GenSeq[Point] = IndexedSeq() 70 | val expected = GenMap[Point,GenSeq[Point]]() 71 | checkParClassify(points, means, expected) 72 | } 73 | 74 | } 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/main/scala/reductions/ParallelCountChange.scala: -------------------------------------------------------------------------------- 1 | package reductions 2 | 3 | import org.scalameter._ 4 | import common._ 5 | 6 | object ParallelCountChangeRunner { 7 | 8 | @volatile var seqResult = 0 9 | 10 | @volatile var parResult = 0 11 | 12 | val standardConfig = config( 13 | Key.exec.minWarmupRuns -> 20, 14 | Key.exec.maxWarmupRuns -> 40, 15 | Key.exec.benchRuns -> 80, 16 | Key.verbose -> true 17 | ) withWarmer(new Warmer.Default) 18 | 19 | def main(args: Array[String]): Unit = { 20 | val amount = 250 21 | val coins = List(1, 2, 5, 10, 20, 50) 22 | val seqtime = standardConfig measure { 23 | seqResult = ParallelCountChange.countChange(amount, coins) 24 | } 25 | println(s"sequential result = $seqResult") 26 | println(s"sequential count time: $seqtime ms") 27 | 28 | def measureParallelCountChange(threshold: ParallelCountChange.Threshold): Unit = { 29 | val fjtime = standardConfig measure { 30 | parResult = ParallelCountChange.parCountChange(amount, coins, threshold) 31 | } 32 | println(s"parallel result = $parResult") 33 | println(s"parallel count time: $fjtime ms") 34 | println(s"speedup: ${seqtime / fjtime}") 35 | } 36 | 37 | measureParallelCountChange(ParallelCountChange.moneyThreshold(amount)) 38 | measureParallelCountChange(ParallelCountChange.totalCoinsThreshold(coins.length)) 39 | measureParallelCountChange(ParallelCountChange.combinedThreshold(amount, coins)) 40 | } 41 | } 42 | 43 | object ParallelCountChange { 44 | 45 | /** Returns the number of ways change can be made from the specified list of 46 | * coins for the specified amount of money. 47 | */ 48 | def countChange(money: Int, coins: List[Int]): Int = { 49 | if(money < 0) 0 50 | else if (money == 0) 1 51 | else if(coins.isEmpty) 0 52 | else countChange(money - coins.head, coins) + countChange(money, coins.tail) 53 | } 54 | 55 | type Threshold = (Int, List[Int]) => Boolean 56 | 57 | /** In parallel, counts the number of ways change can be made from the 58 | * specified list of coins for the specified amount of money. 59 | */ 60 | def parCountChange(money: Int, coins: List[Int], threshold: Threshold): Int = { 61 | if(threshold(money, coins) || money <= 0 || coins.isEmpty) countChange(money, coins) 62 | else { 63 | val (left,right) = parallel[Int, Int](parCountChange(money - coins.head, coins, threshold),parCountChange(money, coins.tail, threshold)) 64 | left + right 65 | } 66 | } 67 | 68 | /** Threshold heuristic based on the starting money. */ 69 | def moneyThreshold(startingMoney: Int): Threshold = (money, coins) => money <= (2 * startingMoney) / 3 70 | 71 | /** Threshold heuristic based on the total number of initial coins. */ 72 | def totalCoinsThreshold(totalCoins: Int): Threshold = (money, coins) => coins.length <= (2 * totalCoins) / 3 73 | 74 | /** Threshold heuristic based on the starting money and the initial list of coins. */ 75 | def combinedThreshold(startingMoney: Int, allCoins: List[Int]): Threshold = (money, coins) => moneyThreshold(startingMoney)(money, coins) && totalCoinsThreshold(allCoins.length)(money, coins) 76 | } 77 | -------------------------------------------------------------------------------- /.idea/modules/root.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/test/scala/reductions/ParallelCountChangeSuite.scala: -------------------------------------------------------------------------------- 1 | package reductions 2 | 3 | import java.util.concurrent._ 4 | import scala.collection._ 5 | import org.scalatest.FunSuite 6 | import org.junit.runner.RunWith 7 | import org.scalatest.junit.JUnitRunner 8 | import common._ 9 | 10 | import ParallelCountChange._ 11 | 12 | @RunWith(classOf[JUnitRunner]) 13 | class ParallelCountChangeSuite extends FunSuite { 14 | 15 | test("countChange should return 0 for money < 0") { 16 | def check(money: Int, coins: List[Int]) = 17 | assert(countChange(money, coins) == 0, 18 | s"countChang($money, _) should be 0") 19 | 20 | check(-1, List()) 21 | check(-1, List(1, 2, 3)) 22 | check(-Int.MinValue, List()) 23 | check(-Int.MinValue, List(1, 2, 3)) 24 | } 25 | 26 | test("countChange should return 1 when money == 0") { 27 | def check(coins: List[Int]) = 28 | assert(countChange(0, coins) == 1, 29 | s"countChang(0, _) should be 1") 30 | 31 | check(List()) 32 | check(List(1, 2, 3)) 33 | check(List.range(1, 100)) 34 | } 35 | 36 | test("countChange should return 0 for money > 0 and coins = List()") { 37 | def check(money: Int) = 38 | assert(countChange(money, List()) == 0, 39 | s"countChang($money, List()) should be 0") 40 | 41 | check(1) 42 | check(Int.MaxValue) 43 | } 44 | 45 | test("countChange should work when there is only one coin") { 46 | def check(money: Int, coins: List[Int], expected: Int) = 47 | assert(countChange(money, coins) == expected, 48 | s"countChange($money, $coins) should be $expected") 49 | 50 | check(1, List(1), 1) 51 | check(2, List(1), 1) 52 | check(1, List(2), 0) 53 | check(Int.MaxValue, List(Int.MaxValue), 1) 54 | check(Int.MaxValue - 1, List(Int.MaxValue), 0) 55 | } 56 | 57 | test("countChange should work for multi-coins") { 58 | def check(money: Int, coins: List[Int], expected: Int) = 59 | assert(countChange(money, coins) == expected, 60 | s"countChange($money, $coins) should be $expected") 61 | 62 | check(50, List(1, 2, 5, 10), 341) 63 | check(250, List(1, 2, 5, 10, 20, 50), 177863) 64 | } 65 | 66 | test("parCountChange should return 0 for money < 0") { 67 | def check(money: Int, coins: List[Int]) = 68 | assert(parCountChange(money, coins,ParallelCountChange.moneyThreshold(money)) == 0, 69 | s"countChang($money, _) should be 0") 70 | 71 | check(-1, List()) 72 | check(-1, List(1, 2, 3)) 73 | check(-Int.MinValue, List()) 74 | check(-Int.MinValue, List(1, 2, 3)) 75 | } 76 | 77 | test("parCountChange should return 1 when money == 0") { 78 | def check(coins: List[Int]) = 79 | assert(parCountChange(0, coins,ParallelCountChange.moneyThreshold(0)) == 1, 80 | s"countChang(0, _) should be 1") 81 | 82 | check(List()) 83 | check(List(1, 2, 3)) 84 | check(List.range(1, 100)) 85 | } 86 | 87 | test("parCountChange should return 0 for money > 0 and coins = List()") { 88 | def check(money: Int) = 89 | assert(parCountChange(money, List(), ParallelCountChange.moneyThreshold(money)) == 0, 90 | s"countChang($money, List()) should be 0") 91 | 92 | check(1) 93 | check(Int.MaxValue) 94 | } 95 | 96 | test("parCountChange should work when there is only one coin") { 97 | def check(money: Int, coins: List[Int], expected: Int) = 98 | assert(parCountChange(money, coins,ParallelCountChange.moneyThreshold(money)) == expected, 99 | s"countChange($money, $coins) should be $expected") 100 | 101 | check(1, List(1), 1) 102 | check(2, List(1), 1) 103 | check(1, List(2), 0) 104 | check(Int.MaxValue, List(Int.MaxValue), 1) 105 | check(Int.MaxValue - 1, List(Int.MaxValue), 0) 106 | } 107 | 108 | test("parCountChange should work for multi-coins") { 109 | def check(money: Int, coins: List[Int], expected: Int) = 110 | assert(parCountChange(money, coins, ParallelCountChange.moneyThreshold(money)) == expected, 111 | s"countChange($money, $coins) should be $expected") 112 | 113 | check(50, List(1, 2, 5, 10), 341) 114 | check(250, List(1, 2, 5, 10, 20, 50), 177863) 115 | } 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/scala/reductions/ParallelParenthesesBalancing.scala: -------------------------------------------------------------------------------- 1 | package reductions 2 | 3 | import scala.annotation._ 4 | import org.scalameter._ 5 | import common._ 6 | 7 | object ParallelParenthesesBalancingRunner { 8 | 9 | @volatile var seqResult = false 10 | 11 | @volatile var parResult = false 12 | 13 | // changed values because it took too long 14 | val standardConfig = config( 15 | Key.exec.minWarmupRuns -> 10, // was 40 16 | Key.exec.maxWarmupRuns -> 20, // was 80 17 | Key.exec.benchRuns -> 20, // was 120 18 | Key.verbose -> true 19 | ) withWarmer (new Warmer.Default) 20 | 21 | def main(args: Array[String]): Unit = { 22 | val length = 100000 // was +0000 23 | val chars = new Array[Char](length) 24 | val threshold = 100 // was +000 25 | val seqtime = standardConfig measure { 26 | seqResult = ParallelParenthesesBalancing.balance(chars) 27 | } 28 | println(s"sequential result = $seqResult") 29 | println(s"sequential balancing time: $seqtime ms") 30 | 31 | val fjtime = standardConfig measure { 32 | parResult = ParallelParenthesesBalancing.parBalance(chars, threshold) 33 | } 34 | println(s"parallel result = $parResult") 35 | println(s"parallel balancing time: $fjtime ms") 36 | println(s"speedup: ${seqtime / fjtime}") 37 | } 38 | } 39 | 40 | object ParallelParenthesesBalancing { 41 | 42 | /** Returns `true` iff the parentheses in the input `chars` are balanced. 43 | */ 44 | def balance(chars: Array[Char]): Boolean = { 45 | def checkOpenClose(chars: Array[Char], openCount: Int): Boolean = { 46 | if (chars.isEmpty) openCount == 0 47 | else if (openCount < 0) false 48 | else if (chars.head == '(') checkOpenClose(chars.tail, openCount + 1) 49 | else if (chars.head == ')') checkOpenClose(chars.tail, openCount - 1) 50 | else checkOpenClose(chars.tail, openCount) 51 | 52 | } 53 | 54 | checkOpenClose(chars, 0) 55 | } 56 | 57 | /** Returns `true` iff the parentheses in the input `chars` are balanced. 58 | */ 59 | def parBalance(chars: Array[Char], threshold: Int): Boolean = { 60 | 61 | def traverse(idx: Int, until: Int): (Int, Int) = { 62 | // God I hate this imperative coding for parallelism's sake! 63 | var i = idx 64 | var begin = 0 65 | var end = 0 66 | var switched = false 67 | 68 | while (i < until){ 69 | if(begin < 0){ 70 | switched = true 71 | }else{ 72 | switched = false 73 | } 74 | 75 | if(chars(i) == '('){ 76 | if(switched){ 77 | end = end + 1 78 | }else{ 79 | begin = begin + 1 80 | } 81 | 82 | // if(switched) end = end + 1 else begin = begin + 1 83 | } 84 | if(chars(i) == ')') 85 | { 86 | if (switched) { 87 | end = end - 1 88 | }else{ 89 | begin = begin - 1 90 | } 91 | //if(switched) end = end - 1 else begin = begin - 1 92 | } 93 | 94 | i = i + 1 95 | } 96 | 97 | (begin, end) 98 | 99 | } 100 | 101 | /* 102 | (0,0 ) 103 | ((test)(c+x)-4)((((4)444)---)xx(x)x) 104 | / \ 105 | (0,3) => (-3, 0) 106 | ((test)(c+x)-4)((( (4)444)---)xx(x)x) 107 | 108 | (2,0) (-2,3) (-1,0) (-2, 0) 109 | / \ / \ 110 | ((test)(c +x)-4)((()( (4)444)-- -)xx(x)x) 111 | 112 | 113 | 114 | 115 | 116 | */ 117 | 118 | 119 | 120 | def reduce(from: Int, until: Int): (Int, Int) = { 121 | if(until - from <= threshold) traverse(from, until) 122 | else { 123 | val mid = from + (until - from) / 2 124 | val (pair1, pair2) = parallel(reduce(from, mid), reduce(mid, until)) 125 | 126 | // This is my best piece of code ever 127 | if(pair1._1 < 0 && pair2._1 > 0) (pair1._1 , pair2._1 + pair1._2 + pair2._2) 128 | else if(pair2._1 < 0 && pair1._2 > 0) (pair1._1 + pair2._1 + pair1._2 , + pair2._2) 129 | else (pair1._1 + pair2._1, pair1._2 + pair2._2) 130 | } 131 | } 132 | 133 | val res = reduce(0, chars.length) 134 | res._1 + res._2 == 0 && res._1 >= 0 135 | } 136 | 137 | // For those who want more: 138 | // Prove that your reduction operator is associative! 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/conctrees/Conc.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | package conctrees 3 | 4 | import scala.annotation.tailrec 5 | 6 | sealed trait Conc[@specialized(Int, Long, Float, Double) +T] { 7 | def level: Int 8 | def size: Int 9 | def left: Conc[T] 10 | def right: Conc[T] 11 | def normalized = this 12 | } 13 | 14 | object Conc { 15 | 16 | case class <>[+T](left: Conc[T], right: Conc[T]) extends Conc[T] { 17 | val level = 1 + math.max(left.level, right.level) 18 | val size = left.size + right.size 19 | } 20 | 21 | sealed trait Leaf[T] extends Conc[T] { 22 | def left = sys.error("Leaves do not have children.") 23 | def right = sys.error("Leaves do not have children.") 24 | } 25 | 26 | case object Empty extends Leaf[Nothing] { 27 | def level = 0 28 | def size = 0 29 | } 30 | 31 | class Single[@specialized(Int, Long, Float, Double) T](val x: T) extends Leaf[T] { 32 | def level = 0 33 | def size = 1 34 | override def toString = s"Single($x)" 35 | } 36 | 37 | class Chunk[@specialized(Int, Long, Float, Double) T](val array: Array[T], val size: Int, val k: Int) 38 | extends Leaf[T] { 39 | def level = 0 40 | override def toString = s"Chunk(${array.mkString("", ", ", "")}; $size; $k)" 41 | } 42 | 43 | case class Append[+T](left: Conc[T], right: Conc[T]) extends Conc[T] { 44 | val level = 1 + math.max(left.level, right.level) 45 | val size = left.size + right.size 46 | override def normalized = { 47 | def wrap[T](xs: Conc[T], ys: Conc[T]): Conc[T] = (xs: @unchecked) match { 48 | case Append(ws, zs) => wrap(ws, zs <> ys) 49 | case xs => xs <> ys 50 | } 51 | wrap(left, right) 52 | } 53 | } 54 | 55 | def concatTop[T](xs: Conc[T], ys: Conc[T]) = { 56 | if (xs == Empty) ys 57 | else if (ys == Empty) xs 58 | else concat(xs, ys) 59 | } 60 | 61 | private def concat[T](xs: Conc[T], ys: Conc[T]): Conc[T] = { 62 | val diff = ys.level - xs.level 63 | if (diff >= -1 && diff <= 1) new <>(xs, ys) 64 | else if (diff < -1) { 65 | if (xs.left.level >= xs.right.level) { 66 | val nr = concat(xs.right, ys) 67 | new <>(xs.left, nr) 68 | } else { 69 | val nrr = concat(xs.right.right, ys) 70 | if (nrr.level == xs.level - 3) { 71 | val nl = xs.left 72 | val nr = new <>(xs.right.left, nrr) 73 | new <>(nl, nr) 74 | } else { 75 | val nl = new <>(xs.left, xs.right.left) 76 | val nr = nrr 77 | new <>(nl, nr) 78 | } 79 | } 80 | } else { 81 | if (ys.right.level >= ys.left.level) { 82 | val nl = concat(xs, ys.left) 83 | new <>(nl, ys.right) 84 | } else { 85 | val nll = concat(xs, ys.left.left) 86 | if (nll.level == ys.level - 3) { 87 | val nl = new <>(nll, ys.left.right) 88 | val nr = ys.right 89 | new <>(nl, nr) 90 | } else { 91 | val nl = nll 92 | val nr = new <>(ys.left.right, ys.right) 93 | new <>(nl, nr) 94 | } 95 | } 96 | } 97 | } 98 | 99 | def appendTop[T](xs: Conc[T], ys: Leaf[T]): Conc[T] = (xs: @unchecked) match { 100 | case xs: Append[T] => append(xs, ys) 101 | case _ <> _ => new Append(xs, ys) 102 | case Empty => ys 103 | case xs: Leaf[T] => new <>(xs, ys) 104 | } 105 | @tailrec private def append[T](xs: Append[T], ys: Conc[T]): Conc[T] = { 106 | if (xs.right.level > ys.level) new Append(xs, ys) 107 | else { 108 | val zs = new <>(xs.right, ys) 109 | xs.left match { 110 | case ws @ Append(_, _) => append(ws, zs) 111 | case ws if ws.level <= zs.level => ws <> zs 112 | case ws => new Append(ws, zs) 113 | } 114 | } 115 | } 116 | 117 | def traverse[@specialized(Int, Long, Float, Double) T, @specialized(Int, Long, Float, Double) U](xs: Conc[T], f: T => U): Unit = (xs: @unchecked) match { 118 | case left <> right => 119 | traverse(left, f) 120 | traverse(right, f) 121 | case s: Single[T] => 122 | f(s.x) 123 | case c: Chunk[T] => 124 | val a = c.array 125 | val sz = c.size 126 | var i = 0 127 | while (i < sz) { 128 | f(a(i)) 129 | i += 1 130 | } 131 | case Empty => 132 | case Append(left, right) => 133 | traverse(left, f) 134 | traverse(right, f) 135 | case _ => 136 | sys.error("All cases should have been covered: " + xs + ", " + xs.getClass) 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/scala/scalashop/ScalaShop.scala: -------------------------------------------------------------------------------- 1 | package scalashop 2 | 3 | import java.awt._ 4 | import java.awt.event._ 5 | import javax.swing._ 6 | import javax.swing.event._ 7 | import scala.collection.parallel._ 8 | import scala.collection.par._ 9 | import scala.collection.mutable.ArrayBuffer 10 | import scala.reflect.ClassTag 11 | import org.scalameter._ 12 | import common._ 13 | 14 | object ScalaShop { 15 | 16 | class ScalaShopFrame extends JFrame("ScalaShop\u2122") { 17 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) 18 | setSize(1024, 600) 19 | setLayout(new BorderLayout) 20 | 21 | val rightpanel = new JPanel 22 | rightpanel.setBorder(BorderFactory.createEtchedBorder(border.EtchedBorder.LOWERED)) 23 | rightpanel.setLayout(new BorderLayout) 24 | add(rightpanel, BorderLayout.EAST) 25 | 26 | val controls = new JPanel 27 | controls.setLayout(new GridLayout(0, 2)) 28 | rightpanel.add(controls, BorderLayout.NORTH) 29 | 30 | val filterLabel = new JLabel("Filter") 31 | controls.add(filterLabel) 32 | 33 | val filterCombo = new JComboBox(Array( 34 | "horizontal-box-blur", 35 | "vertical-box-blur" 36 | )) 37 | controls.add(filterCombo) 38 | 39 | val radiusLabel = new JLabel("Radius") 40 | controls.add(radiusLabel) 41 | 42 | val radiusSpinner = new JSpinner(new SpinnerNumberModel(3, 1, 16, 1)) 43 | controls.add(radiusSpinner) 44 | 45 | val tasksLabel = new JLabel("Tasks") 46 | controls.add(tasksLabel) 47 | 48 | val tasksSpinner = new JSpinner(new SpinnerNumberModel(32, 1, 128, 1)) 49 | controls.add(tasksSpinner) 50 | 51 | val stepbutton = new JButton("Apply filter") 52 | stepbutton.addActionListener(new ActionListener { 53 | def actionPerformed(e: ActionEvent) { 54 | val time = measure { 55 | canvas.applyFilter(getFilterName, getNumTasks, getRadius) 56 | } 57 | updateInformationBox(time) 58 | } 59 | }) 60 | controls.add(stepbutton) 61 | 62 | val clearButton = new JButton("Reload") 63 | clearButton.addActionListener(new ActionListener { 64 | def actionPerformed(e: ActionEvent) { 65 | canvas.reload() 66 | } 67 | }) 68 | controls.add(clearButton) 69 | 70 | val info = new JTextArea(" ") 71 | info.setBorder(BorderFactory.createLoweredBevelBorder) 72 | rightpanel.add(info, BorderLayout.SOUTH) 73 | 74 | val mainMenuBar = new JMenuBar() 75 | 76 | val fileMenu = new JMenu("File") 77 | val openMenuItem = new JMenuItem("Open...") 78 | openMenuItem.addActionListener(new ActionListener { 79 | def actionPerformed(e: ActionEvent) { 80 | val fc = new JFileChooser() 81 | if (fc.showOpenDialog(ScalaShopFrame.this) == JFileChooser.APPROVE_OPTION) { 82 | canvas.loadFile(fc.getSelectedFile.getPath) 83 | } 84 | } 85 | }) 86 | fileMenu.add(openMenuItem) 87 | val exitMenuItem = new JMenuItem("Exit") 88 | exitMenuItem.addActionListener(new ActionListener { 89 | def actionPerformed(e: ActionEvent) { 90 | sys.exit(0) 91 | } 92 | }) 93 | fileMenu.add(exitMenuItem) 94 | 95 | mainMenuBar.add(fileMenu) 96 | 97 | val helpMenu = new JMenu("Help") 98 | val aboutMenuItem = new JMenuItem("About") 99 | aboutMenuItem.addActionListener(new ActionListener { 100 | def actionPerformed(e: ActionEvent) { 101 | JOptionPane.showMessageDialog(null, "ScalaShop, the ultimate image manipulation tool\nBrought to you by EPFL, 2015") 102 | } 103 | }) 104 | helpMenu.add(aboutMenuItem) 105 | 106 | mainMenuBar.add(helpMenu) 107 | 108 | setJMenuBar(mainMenuBar) 109 | 110 | val canvas = new PhotoCanvas 111 | 112 | val scrollPane = new JScrollPane(canvas) 113 | 114 | add(scrollPane, BorderLayout.CENTER) 115 | setVisible(true) 116 | 117 | def updateInformationBox(time: Double) { 118 | info.setText(s"Time: $time") 119 | } 120 | 121 | def getNumTasks: Int = tasksSpinner.getValue.asInstanceOf[Int] 122 | 123 | def getRadius: Int = radiusSpinner.getValue.asInstanceOf[Int] 124 | 125 | def getFilterName: String = { 126 | filterCombo.getSelectedItem.asInstanceOf[String] 127 | } 128 | 129 | } 130 | 131 | try { 132 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) 133 | } catch { 134 | case _: Exception => println("Cannot set look and feel, using the default one.") 135 | } 136 | 137 | val frame = new ScalaShopFrame 138 | 139 | def main(args: Array[String]) { 140 | frame.repaint() 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/scala/kmeans/KMeans.scala: -------------------------------------------------------------------------------- 1 | package kmeans 2 | 3 | import scala.annotation.tailrec 4 | import scala.collection._ 5 | import scala.util.Random 6 | import org.scalameter._ 7 | import common._ 8 | 9 | import scala.collection.concurrent.TrieMap 10 | 11 | class KMeans { 12 | 13 | def generatePoints(k: Int, num: Int): Seq[Point] = { 14 | val randx = new Random(1) 15 | val randy = new Random(3) 16 | val randz = new Random(5) 17 | (0 until num) 18 | .map({ i => 19 | val x = ((i + 1) % k) * 1.0 / k + randx.nextDouble() * 0.5 20 | val y = ((i + 5) % k) * 1.0 / k + randy.nextDouble() * 0.5 21 | val z = ((i + 7) % k) * 1.0 / k + randz.nextDouble() * 0.5 22 | new Point(x, y, z) 23 | }).to[mutable.ArrayBuffer] 24 | } 25 | 26 | def initializeMeans(k: Int, points: Seq[Point]): Seq[Point] = { 27 | val rand = new Random(7) 28 | (0 until k).map(_ => points(rand.nextInt(points.length))).to[mutable.ArrayBuffer] 29 | } 30 | 31 | def findClosest(p: Point, means: GenSeq[Point]): Point = { 32 | assert(means.size > 0) 33 | var minDistance = p.squareDistance(means(0)) 34 | var closest = means(0) 35 | var i = 1 36 | while (i < means.length) { 37 | val distance = p.squareDistance(means(i)) 38 | if (distance < minDistance) { 39 | minDistance = distance 40 | closest = means(i) 41 | } 42 | i += 1 43 | } 44 | closest 45 | } 46 | 47 | 48 | 49 | def classify(points: GenSeq[Point], means: GenSeq[Point]):GenMap[Point, GenSeq[Point]] = { 50 | // group each point by the mean. Should be enough, but needs to return empty lists for unused means 51 | val pointsMeanMap = points.par.groupBy(findClosest(_, means)) 52 | // So iterate over means get (empty) list and return map 53 | means.par.map(mean => mean -> pointsMeanMap.getOrElse(mean, GenSeq())).toMap 54 | } 55 | 56 | def findAverage(oldMean: Point, points: GenSeq[Point]): Point = if (points.length == 0) oldMean else { 57 | var x = 0.0 58 | var y = 0.0 59 | var z = 0.0 60 | points.seq.foreach { p => 61 | x += p.x 62 | y += p.y 63 | z += p.z 64 | } 65 | new Point(x / points.length, y / points.length, z / points.length) 66 | } 67 | 68 | def update(classified: GenMap[Point, GenSeq[Point]], oldMeans: GenSeq[Point]): GenSeq[Point] = { 69 | oldMeans.par.map(oldMean => findAverage(oldMean, classified(oldMean))) 70 | } 71 | 72 | 73 | 74 | def converged(eta: Double)(oldMeans: GenSeq[Point], newMeans: GenSeq[Point]): Boolean = { 75 | (oldMeans zip newMeans).forall{ 76 | case (oldMean, newMean) => oldMean.squareDistance(newMean) <= eta 77 | } 78 | } 79 | 80 | @tailrec 81 | final def kMeans(points: GenSeq[Point], means: GenSeq[Point], eta: Double): GenSeq[Point] = { 82 | val classified = classify(points, means) 83 | val newMeans = update(classified, means) 84 | 85 | if (!converged(eta)(means, newMeans)) kMeans(points, newMeans, eta) else newMeans // your implementation need to be tail recursive 86 | } 87 | } 88 | 89 | /** Describes one point in three-dimensional space. 90 | * 91 | * Note: deliberately uses reference equality. 92 | */ 93 | class Point(val x: Double, val y: Double, val z: Double) { 94 | private def square(v: Double): Double = v * v 95 | def squareDistance(that: Point): Double = { 96 | square(that.x - x) + square(that.y - y) + square(that.z - z) 97 | } 98 | private def round(v: Double): Double = (v * 100).toInt / 100.0 99 | override def toString = s"(${round(x)}, ${round(y)}, ${round(z)})" 100 | } 101 | 102 | 103 | object KMeansRunner { 104 | 105 | val standardConfig = config( 106 | Key.exec.minWarmupRuns -> 20, 107 | Key.exec.maxWarmupRuns -> 40, 108 | Key.exec.benchRuns -> 25, 109 | Key.verbose -> true 110 | ) withWarmer(new Warmer.Default) 111 | 112 | def main(args: Array[String]) { 113 | val kMeans = new KMeans() 114 | 115 | val numPoints = 500000 116 | val eta = 0.01 117 | val k = 32 118 | val points = kMeans.generatePoints(k, numPoints) 119 | val means = kMeans.initializeMeans(k, points) 120 | 121 | val seqtime = standardConfig measure { 122 | kMeans.kMeans(points, means, eta) 123 | } 124 | println(s"sequential time: $seqtime ms") 125 | 126 | val partime = standardConfig measure { 127 | val parPoints = points.par 128 | val parMeans = means.par 129 | kMeans.kMeans(parPoints, parMeans, eta) 130 | } 131 | println(s"parallel time: $partime ms") 132 | println(s"speedup: ${seqtime / partime}") 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/Simulator.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | 3 | import java.awt._ 4 | import java.awt.event._ 5 | import javax.swing._ 6 | import javax.swing.event._ 7 | 8 | import scala.collection.parallel.TaskSupport 9 | import scala.collection.parallel.Combiner 10 | import scala.collection.parallel.mutable.ParHashSet 11 | import common._ 12 | 13 | class Simulator(val taskSupport: TaskSupport, val timeStats: TimeStatistics) { 14 | 15 | def updateBoundaries(boundaries: Boundaries, body: Body): Boundaries = { 16 | boundaries.maxX = body.x max boundaries.maxX 17 | boundaries.minX = body.x min boundaries.minX 18 | boundaries.maxY = body.y max boundaries.maxY 19 | boundaries.minY = body.y min boundaries.minY 20 | boundaries 21 | } 22 | 23 | def mergeBoundaries(a: Boundaries, b: Boundaries): Boundaries = { 24 | val res = new Boundaries 25 | res.maxX = a.maxX max b.maxX 26 | res.minX = a.minX min b.minX 27 | res.maxY = a.maxY max b.maxY 28 | res.minY = a.minY min b.minY 29 | res 30 | } 31 | 32 | def computeBoundaries(bodies: Seq[Body]): Boundaries = timeStats.timed("boundaries") { 33 | val parBodies = bodies.par 34 | parBodies.tasksupport = taskSupport 35 | parBodies.aggregate(new Boundaries)(updateBoundaries, mergeBoundaries) 36 | } 37 | 38 | def computeSectorMatrix(bodies: Seq[Body], boundaries: Boundaries): SectorMatrix = timeStats.timed("matrix") { 39 | val parBodies = bodies.par 40 | parBodies.tasksupport = taskSupport 41 | 42 | parBodies.aggregate(new SectorMatrix(boundaries, SECTOR_PRECISION))( 43 | { case (sectorMatrix, body) => sectorMatrix += body}, 44 | { case (sectorMatrixLeft, sectorMatrixRight) => sectorMatrixLeft combine sectorMatrixRight }) 45 | } 46 | 47 | def computeQuad(sectorMatrix: SectorMatrix): Quad = timeStats.timed("quad") { 48 | sectorMatrix.toQuad(taskSupport.parallelismLevel) 49 | } 50 | 51 | def updateBodies(bodies: Seq[Body], quad: Quad): Seq[Body] = timeStats.timed("update") { 52 | val parBodies = bodies.par 53 | parBodies.tasksupport = taskSupport 54 | parBodies.map(body => body.updated(quad)).toIndexedSeq 55 | } 56 | 57 | def eliminateOutliers(bodies: Seq[Body], sectorMatrix: SectorMatrix, quad: Quad): Seq[Body] = timeStats.timed("eliminate") { 58 | def isOutlier(b: Body): Boolean = { 59 | val dx = quad.massX - b.x 60 | val dy = quad.massY - b.y 61 | val d = math.sqrt(dx * dx + dy * dy) 62 | // object is far away from the center of the mass 63 | if (d > eliminationThreshold * sectorMatrix.boundaries.size) { 64 | val nx = dx / d 65 | val ny = dy / d 66 | val relativeSpeed = b.xspeed * nx + b.yspeed * ny 67 | // object is moving away from the center of the mass 68 | if (relativeSpeed < 0) { 69 | val escapeSpeed = math.sqrt(2 * gee * quad.mass / d) 70 | // object has the espace velocity 71 | -relativeSpeed > 2 * escapeSpeed 72 | } else false 73 | } else false 74 | } 75 | 76 | def outliersInSector(x: Int, y: Int): Combiner[Body, ParHashSet[Body]] = { 77 | val combiner = ParHashSet.newCombiner[Body] 78 | combiner ++= sectorMatrix(x, y).filter(isOutlier) 79 | combiner 80 | } 81 | 82 | val sectorPrecision = sectorMatrix.sectorPrecision 83 | val horizontalBorder = for (x <- 0 until sectorPrecision; y <- Seq(0, sectorPrecision - 1)) yield (x, y) 84 | val verticalBorder = for (y <- 1 until sectorPrecision - 1; x <- Seq(0, sectorPrecision - 1)) yield (x, y) 85 | val borderSectors = horizontalBorder ++ verticalBorder 86 | 87 | // compute the set of outliers 88 | val parBorderSectors = borderSectors.par 89 | parBorderSectors.tasksupport = taskSupport 90 | val outliers = parBorderSectors.map({ case (x, y) => outliersInSector(x, y) }).reduce(_ combine _).result 91 | 92 | // filter the bodies that are outliers 93 | val parBodies = bodies.par 94 | parBodies.filter(!outliers(_)).seq 95 | } 96 | 97 | def step(bodies: Seq[Body]): (Seq[Body], Quad) = { 98 | // 1. compute boundaries 99 | val boundaries = computeBoundaries(bodies) 100 | 101 | // 2. compute sector matrix 102 | val sectorMatrix = computeSectorMatrix(bodies, boundaries) 103 | 104 | // 3. compute quad tree 105 | val quad = computeQuad(sectorMatrix) 106 | 107 | // 4. eliminate outliers 108 | val filteredBodies = eliminateOutliers(bodies, sectorMatrix, quad) 109 | 110 | // 5. update body velocities and positions 111 | val newBodies = updateBodies(filteredBodies, quad) 112 | 113 | (newBodies, quad) 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/test/scala/example/ListsSuite.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest.FunSuite 4 | 5 | //import ch.epfl.lamp.grading.GradingSuite 6 | import org.junit.runner.RunWith 7 | import org.scalatest.junit.JUnitRunner 8 | 9 | /** 10 | * This class implements a ScalaTest test suite for the methods in object 11 | * `Lists` that need to be implemented as part of this assignment. A test 12 | * suite is simply a collection of individual tests for some specific 13 | * component of a program. 14 | * 15 | * A test suite is created by defining a class which extends the type 16 | * `org.scalatest.FunSuite`. When running ScalaTest, it will automatically 17 | * find this class and execute all of its tests. 18 | * 19 | * Adding the `@RunWith` annotation enables the test suite to be executed 20 | * inside eclipse using the built-in JUnit test runner. 21 | * 22 | * You have two options for running this test suite: 23 | * 24 | * - Start the sbt console and run the "test" command 25 | * - Right-click this file in eclipse and chose "Run As" - "JUnit Test" 26 | */ 27 | @RunWith(classOf[JUnitRunner]) 28 | class ListsSuite extends FunSuite { 29 | 30 | /** 31 | * Tests are written using the `test` operator which takes two arguments: 32 | * 33 | * - A description of the test. This description has to be unique, no two 34 | * tests can have the same description. 35 | * - The test body, a piece of Scala code that implements the test 36 | * 37 | * The most common way to implement a test body is using the method `assert` 38 | * which tests that its argument evaluates to `true`. So one of the simplest 39 | * successful tests is the following: 40 | */ 41 | test("one plus one is two")(assert(1 + 1 == 2)) 42 | 43 | 44 | /** 45 | * In Scala, it is allowed to pass an argument to a method using the block 46 | * syntax, i.e. `{ argument }` instead of parentheses `(argument)`. 47 | * 48 | * This allows tests to be written in a more readable manner: 49 | */ 50 | test("one plus one is three?") { 51 | assert(1 + 1 != 3) // This assertion fails! Go ahead and fix it. 52 | } 53 | 54 | 55 | /** 56 | * One problem with the previous (failing) test is that ScalaTest will 57 | * only tell you that a test failed, but it will not tell you what was 58 | * the reason for the failure. The output looks like this: 59 | * 60 | * {{{ 61 | * [info] - one plus one is three? *** FAILED *** 62 | * }}} 63 | * 64 | * This situation can be improved by using a special equality operator 65 | * `===` instead of `==` (this is only possible in ScalaTest). So if you 66 | * run the next test, ScalaTest will show the following output: 67 | * 68 | * {{{ 69 | * [info] - details why one plus one is not three *** FAILED *** 70 | * [info] 2 did not equal 3 (ListsSuite.scala:67) 71 | * }}} 72 | * 73 | * We recommend to always use the `===` equality operator when writing tests. 74 | */ 75 | test("details why one plus one is not three") { 76 | assert(1 + 1 === 2) // Fix me, please! 77 | } 78 | 79 | /** 80 | * In order to test the exceptional behavior of a methods, ScalaTest offers 81 | * the `intercept` operation. 82 | * 83 | * In the following example, we test the fact that the method `intNotZero` 84 | * throws an `IllegalArgumentException` if its argument is `0`. 85 | */ 86 | test("intNotZero throws an exception if its argument is 0") { 87 | intercept[IllegalArgumentException] { 88 | intNotZero(0) 89 | } 90 | } 91 | 92 | def intNotZero(x: Int): Int = { 93 | if (x == 0) throw new IllegalArgumentException("zero is not allowed") 94 | else x 95 | } 96 | 97 | 98 | /** 99 | * Now we finally write some tests for the list functions that have to be 100 | * implemented for this assignment. We fist import all members of the 101 | * `List` object. 102 | */ 103 | import Lists._ 104 | 105 | 106 | /** 107 | * We only provide two very basic tests for you. Write more tests to make 108 | * sure your `sum` and `max` methods work as expected. 109 | * 110 | * In particular, write tests for corner cases: negative numbers, zeros, 111 | * empty lists, lists with repeated elements, etc. 112 | * 113 | * It is allowed to have multiple `assert` statements inside one test, 114 | * however it is recommended to write an individual `test` statement for 115 | * every tested aspect of a method. 116 | */ 117 | test("sum of a few numbers") { 118 | assert(sum(List(1,2,0)) === 3) 119 | } 120 | 121 | test("max of a few numbers") { 122 | assert(max(List(3, 7, 2)) === 7) 123 | } 124 | 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/scala/kmeans/fun/IndexedColors.scala: -------------------------------------------------------------------------------- 1 | package kmeans 2 | package fun 3 | 4 | import scala.collection.GenSeq 5 | 6 | abstract sealed trait InitialSelectionStrategy 7 | case object RandomSampling extends InitialSelectionStrategy 8 | case object UniformSampling extends InitialSelectionStrategy 9 | case object UniformChoice extends InitialSelectionStrategy 10 | 11 | abstract sealed trait ConvergenceStrategy 12 | case class ConvergedWhenSNRAbove(x: Double) extends ConvergenceStrategy 13 | case class ConvergedAfterNSteps(n: Int) extends ConvergenceStrategy 14 | case class ConvergedAfterMeansAreStill(eta: Double) extends ConvergenceStrategy 15 | 16 | 17 | class IndexedColorFilter(initialImage: Img, 18 | colorCount: Int, 19 | initStrategy: InitialSelectionStrategy, 20 | convStrategy: ConvergenceStrategy) extends KMeans { 21 | 22 | private var steps = 0 23 | 24 | // What could we do here to speed up the computation? 25 | val points = imageToPoints(initialImage) 26 | val means = initializeIndex(colorCount, points) 27 | 28 | /* The work is done here: */ 29 | private val newMeans = kMeans(points.par, means.par, 0.01) 30 | 31 | /* And these are the results exposed */ 32 | def getStatus() = s"Converged after $steps steps." 33 | def getResult() = indexedImage(initialImage, newMeans) 34 | 35 | private def imageToPoints(img: Img): GenSeq[Point] = 36 | for (x <- 0 until img.width; y <- 0 until img.height) yield { 37 | val rgba = img(x, y) 38 | new Point(red(rgba), green(rgba), blue(rgba)) 39 | } 40 | 41 | private def indexedImage(img: Img, means: GenSeq[Point]) = { 42 | val dst = new Img(img.width, img.height) 43 | val pts = collection.mutable.Set[Point]() 44 | 45 | for (x <- 0 until img.width; y <- 0 until img.height) yield { 46 | val v = img(x, y) 47 | var point = new Point(red(v), green(v), blue(v)) 48 | point = findClosest(point, means) 49 | pts += point 50 | dst(x, y) = rgba(point.x, point.y, point.z, 1d) 51 | } 52 | 53 | dst 54 | } 55 | 56 | private def initializeIndex(numColors: Int, points: GenSeq[Point]): GenSeq[Point] = { 57 | val initialPoints: GenSeq[Point] = 58 | initStrategy match { 59 | case RandomSampling => 60 | val d: Int = points.size / numColors 61 | (0 until numColors) map (idx => points(d * idx)) 62 | case UniformSampling => 63 | val sep: Int = 32 64 | (for (r <- 0 until 255 by sep; g <- 0 until 255 by sep; b <- 0 until 255 by sep) yield { 65 | def inside(p: Point): Boolean = 66 | (p.x >= (r.toDouble / 255)) && 67 | (p.x <= ((r.toDouble + sep) / 255)) && 68 | (p.y >= (g.toDouble / 255)) && 69 | (p.y <= ((g.toDouble + sep) / 255)) && 70 | (p.z >= (b.toDouble / 255)) && 71 | (p.z <= ((b.toDouble + sep) / 255)) 72 | 73 | val pts = points.filter(inside(_)) 74 | val cnt = pts.size * 3 * numColors / points.size 75 | if (cnt >= 1) { 76 | val d = pts.size / cnt 77 | (0 until cnt) map (idx => pts(d * idx)) 78 | } else 79 | Seq() 80 | }).flatten 81 | case UniformChoice => 82 | val d: Int = math.max(1, (256 / math.cbrt(numColors.toDouble).ceil).toInt) 83 | for (r <- 0 until 256 by d; g <- 0 until 256 by d; b <- 0 until 256 by d) yield 84 | new Point(r.toDouble / 256,g.toDouble / 256, b.toDouble / 256) 85 | } 86 | 87 | val d2 = initialPoints.size.toDouble / numColors 88 | (0 until numColors) map (idx => initialPoints((idx * d2).toInt)) 89 | } 90 | 91 | private def computeSNR(points: GenSeq[Point], means: GenSeq[Point]): Double = { 92 | var sound = 0.0 93 | var noise = 0.0 94 | 95 | for (point <- points) { 96 | import math.{pow, sqrt} 97 | val closest = findClosest(point, means) 98 | sound += sqrt(pow(point.x, 2) + pow(point.y, 2) + pow(point.z, 2)) 99 | noise += sqrt(pow(point.x - closest.x, 2) + pow(point.y - closest.y, 2) + pow(point.z - closest.z, 2)) 100 | } 101 | sound/noise 102 | } 103 | 104 | override def converged(eta: Double)(oldMeans: GenSeq[Point], newMeans: GenSeq[Point]): Boolean = { 105 | steps += 1 106 | convStrategy match { 107 | case ConvergedAfterNSteps(n) => 108 | steps >= n 109 | case ConvergedAfterMeansAreStill(eta) => 110 | super.converged(eta)(oldMeans, newMeans) 111 | case ConvergedWhenSNRAbove(snr_desired) => 112 | val snr_computed = computeSNR(points, newMeans) 113 | snr_computed >= snr_desired 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /src/main/scala/barneshut/BarnesHut.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | 3 | import java.awt._ 4 | import java.awt.event._ 5 | import javax.swing._ 6 | import javax.swing.event._ 7 | import scala.collection.parallel._ 8 | import scala.collection.par._ 9 | import scala.collection.mutable.ArrayBuffer 10 | import scala.reflect.ClassTag 11 | 12 | object BarnesHut { 13 | 14 | val model = new SimulationModel 15 | 16 | var simulator: Simulator = _ 17 | 18 | def initialize(parallelismLevel: Int, pattern: String, nbodies: Int) { 19 | model.initialize(parallelismLevel, pattern, nbodies) 20 | model.timeStats.clear() 21 | simulator = new Simulator(model.taskSupport, model.timeStats) 22 | } 23 | 24 | class BarnesHutFrame extends JFrame("Barnes-Hut") { 25 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) 26 | setSize(1024, 600) 27 | setLayout(new BorderLayout) 28 | 29 | val rightpanel = new JPanel 30 | rightpanel.setBorder(BorderFactory.createEtchedBorder(border.EtchedBorder.LOWERED)) 31 | rightpanel.setLayout(new BorderLayout) 32 | add(rightpanel, BorderLayout.EAST) 33 | 34 | val controls = new JPanel 35 | controls.setLayout(new GridLayout(0, 2)) 36 | rightpanel.add(controls, BorderLayout.NORTH) 37 | 38 | val parallelismLabel = new JLabel("Parallelism") 39 | controls.add(parallelismLabel) 40 | 41 | val items = (1 to Runtime.getRuntime.availableProcessors).map(_.toString).toArray 42 | val parcombo = new JComboBox[String](items) 43 | parcombo.setSelectedIndex(items.length - 1) 44 | parcombo.addActionListener(new ActionListener { 45 | def actionPerformed(e: ActionEvent) = { 46 | initialize(getParallelism, "two-galaxies", getTotalBodies) 47 | canvas.repaint() 48 | } 49 | }) 50 | controls.add(parcombo) 51 | 52 | val bodiesLabel = new JLabel("Total bodies") 53 | controls.add(bodiesLabel) 54 | 55 | val bodiesSpinner = new JSpinner(new SpinnerNumberModel(25000, 32, 1000000, 1000)) 56 | bodiesSpinner.addChangeListener(new ChangeListener { 57 | def stateChanged(e: ChangeEvent) = { 58 | if (frame != null) { 59 | initialize(getParallelism, "two-galaxies", getTotalBodies) 60 | canvas.repaint() 61 | } 62 | } 63 | }) 64 | controls.add(bodiesSpinner) 65 | 66 | val stepbutton = new JButton("Step") 67 | stepbutton.addActionListener(new ActionListener { 68 | def actionPerformed(e: ActionEvent) { 69 | stepThroughSimulation() 70 | } 71 | }) 72 | controls.add(stepbutton) 73 | 74 | val startButton = new JToggleButton("Start/Pause") 75 | val startTimer = new javax.swing.Timer(0, new ActionListener { 76 | def actionPerformed(e: ActionEvent) { 77 | stepThroughSimulation() 78 | } 79 | }) 80 | startButton.addActionListener(new ActionListener { 81 | def actionPerformed(e: ActionEvent) { 82 | if (startButton.isSelected) startTimer.start() 83 | else startTimer.stop() 84 | } 85 | }) 86 | controls.add(startButton) 87 | 88 | val quadcheckbox = new JToggleButton("Show quad") 89 | quadcheckbox.addActionListener(new ActionListener { 90 | def actionPerformed(e: ActionEvent) { 91 | model.shouldRenderQuad = quadcheckbox.isSelected 92 | repaint() 93 | } 94 | }) 95 | controls.add(quadcheckbox) 96 | 97 | val clearButton = new JButton("Restart") 98 | clearButton.addActionListener(new ActionListener { 99 | def actionPerformed(e: ActionEvent) { 100 | initialize(getParallelism, "two-galaxies", getTotalBodies) 101 | } 102 | }) 103 | controls.add(clearButton) 104 | 105 | val info = new JTextArea(" ") 106 | info.setBorder(BorderFactory.createLoweredBevelBorder) 107 | rightpanel.add(info, BorderLayout.SOUTH) 108 | 109 | val canvas = new SimulationCanvas(model) 110 | add(canvas, BorderLayout.CENTER) 111 | setVisible(true) 112 | 113 | def updateInformationBox() { 114 | val text = model.timeStats.toString 115 | frame.info.setText("--- Statistics: ---\n" + text) 116 | } 117 | 118 | def stepThroughSimulation() { 119 | SwingUtilities.invokeLater(new Runnable { 120 | def run() = { 121 | val (bodies, quad) = simulator.step(model.bodies) 122 | model.bodies = bodies 123 | model.quad = quad 124 | updateInformationBox() 125 | repaint() 126 | } 127 | }) 128 | } 129 | 130 | def getParallelism = { 131 | val selidx = parcombo.getSelectedIndex 132 | parcombo.getItemAt(selidx).toInt 133 | } 134 | 135 | def getTotalBodies = bodiesSpinner.getValue.asInstanceOf[Int] 136 | 137 | initialize(getParallelism, "two-galaxies", getTotalBodies) 138 | } 139 | 140 | try { 141 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) 142 | } catch { 143 | case _: Exception => println("Cannot set look and feel, using the default one.") 144 | } 145 | 146 | val frame = new BarnesHutFrame 147 | 148 | def main(args: Array[String]) { 149 | frame.repaint() 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/test/scala/barneshut/BarnesHutSuite.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | 3 | import java.util.concurrent._ 4 | import scala.collection._ 5 | import org.scalatest.FunSuite 6 | import org.junit.runner.RunWith 7 | import org.scalatest.junit.JUnitRunner 8 | import common._ 9 | import scala.math._ 10 | import scala.collection.parallel._ 11 | import barneshut.conctrees.ConcBuffer 12 | 13 | @RunWith(classOf[JUnitRunner]) 14 | class BarnesHutSuite extends FunSuite { 15 | 16 | // test cases for quad tree 17 | 18 | import FloatOps._ 19 | test("Empty: center of mass should be the center of the cell") { 20 | val quad = Empty(51f, 46.3f, 5f) 21 | assert(quad.massX == 51f, s"${quad.massX} should be 51f") 22 | assert(quad.massY == 46.3f, s"${quad.massY} should be 46.3f") 23 | } 24 | 25 | test("Empty: mass should be 0") { 26 | val quad = Empty(51f, 46.3f, 5f) 27 | assert(quad.mass == 0f, s"${quad.mass} should be 0f") 28 | } 29 | 30 | test("Empty: total should be 0") { 31 | val quad = Empty(51f, 46.3f, 5f) 32 | assert(quad.total == 0, s"${quad.total} should be 0") 33 | } 34 | 35 | test("Leaf with 1 body") { 36 | val b = new Body(123f, 18f, 26f, 0f, 0f) 37 | val quad = Leaf(17.5f, 27.5f, 5f, Seq(b)) 38 | 39 | assert(quad.mass ~= 123f, s"${quad.mass} should be 123f") 40 | assert(quad.massX ~= 18f, s"${quad.massX} should be 18f") 41 | assert(quad.massY ~= 26f, s"${quad.massY} should be 26f") 42 | assert(quad.total == 1, s"${quad.total} should be 1") 43 | } 44 | 45 | 46 | test("Fork with 3 empty quadrants and 1 leaf (nw)") { 47 | val b = new Body(123f, 18f, 26f, 0f, 0f) 48 | val nw = Leaf(17.5f, 27.5f, 5f, Seq(b)) 49 | val ne = Empty(22.5f, 27.5f, 5f) 50 | val sw = Empty(17.5f, 32.5f, 5f) 51 | val se = Empty(22.5f, 32.5f, 5f) 52 | val quad = Fork(nw, ne, sw, se) 53 | 54 | assert(quad.centerX == 20f, s"${quad.centerX} should be 20f") 55 | assert(quad.centerY == 30f, s"${quad.centerY} should be 30f") 56 | assert(quad.mass ~= 123f, s"${quad.mass} should be 123f") 57 | assert(quad.massX ~= 18f, s"${quad.massX} should be 18f") 58 | assert(quad.massY ~= 26f, s"${quad.massY} should be 26f") 59 | assert(quad.total == 1, s"${quad.total} should be 1") 60 | } 61 | 62 | test("Empty.insert(b) should return a Leaf with only that body") { 63 | val quad = Empty(51f, 46.3f, 5f) 64 | val b = new Body(3f, 54f, 46f, 0f, 0f) 65 | val inserted = quad.insert(b) 66 | inserted match { 67 | case Leaf(centerX, centerY, size, bodies) => 68 | assert(centerX == 51f, s"$centerX should be 51f") 69 | assert(centerY == 46.3f, s"$centerY should be 46.3f") 70 | assert(size == 5f, s"$size should be 5f") 71 | assert(bodies == Seq(b), s"$bodies should contain only the inserted body") 72 | case _ => 73 | fail("Empty.insert() should have returned a Leaf, was $inserted") 74 | } 75 | } 76 | 77 | // test cases for Body 78 | 79 | test("Body.updated should do nothing for Empty quad trees") { 80 | val b1 = new Body(123f, 18f, 26f, 0f, 0f) 81 | val body = b1.updated(Empty(50f, 60f, 5f)) 82 | 83 | assert(body.xspeed == 0f) 84 | assert(body.yspeed == 0f) 85 | } 86 | 87 | test("Body.updated should take bodies in a Leaf into account") { 88 | val b1 = new Body(123f, 18f, 26f, 0f, 0f) 89 | val b2 = new Body(524.5f, 24.5f, 25.5f, 0f, 0f) 90 | val b3 = new Body(245f, 22.4f, 41f, 0f, 0f) 91 | 92 | val quad = Leaf(15f, 30f, 20f, Seq(b2, b3)) 93 | 94 | val body = b1.updated(quad) 95 | 96 | assert(body.xspeed ~= 12.587037f) 97 | assert(body.yspeed ~= 0.015557117f) 98 | } 99 | 100 | // test cases for sector matrix 101 | 102 | test("'SectorMatrix.+=' should add a body at (25,47) to the correct bucket of a sector matrix of size 96") { 103 | val body = new Body(5, 25, 47, 0.1f, 0.1f) 104 | val boundaries = new Boundaries() 105 | boundaries.minX = 1 106 | boundaries.minY = 1 107 | boundaries.maxX = 97 108 | boundaries.maxY = 97 109 | val sm = new SectorMatrix(boundaries, SECTOR_PRECISION) 110 | sm += body 111 | val res = sm(2, 3).size == 1 && sm(2, 3).find(_ == body).isDefined 112 | assert(res, s"Body not found in the right sector") 113 | } 114 | 115 | } 116 | 117 | object FloatOps { 118 | private val precisionThreshold = 1e-4 119 | 120 | /** Floating comparison: assert(float ~= 1.7f). */ 121 | implicit class FloatOps(val self: Float) extends AnyVal { 122 | def ~=(that: Float): Boolean = 123 | abs(self - that) < precisionThreshold 124 | } 125 | 126 | /** Long floating comparison: assert(double ~= 1.7). */ 127 | implicit class DoubleOps(val self: Double) extends AnyVal { 128 | def ~=(that: Double): Boolean = 129 | abs(self - that) < precisionThreshold 130 | } 131 | 132 | /** Floating sequences comparison: assert(floatSeq ~= Seq(0.5f, 1.7f). */ 133 | implicit class FloatSequenceOps(val self: Seq[Float]) extends AnyVal { 134 | def ~=(that: Seq[Float]): Boolean = 135 | self.size == that.size && 136 | self.zip(that).forall { case (a, b) => 137 | abs(a - b) < precisionThreshold 138 | } 139 | } 140 | } 141 | 142 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/SimulationCanvas.scala: -------------------------------------------------------------------------------- 1 | package barneshut 2 | 3 | import java.awt._ 4 | import java.awt.event._ 5 | import javax.swing._ 6 | import javax.swing.event._ 7 | 8 | class SimulationCanvas(val model: SimulationModel) extends JComponent { 9 | 10 | val MAX_RES = 3000 11 | 12 | val pixels = new Array[Int](MAX_RES * MAX_RES) 13 | 14 | override def paintComponent(gcan: Graphics) = { 15 | super.paintComponent(gcan) 16 | 17 | val width = getWidth 18 | val height = getHeight 19 | val img = new image.BufferedImage(width, height, image.BufferedImage.TYPE_INT_ARGB) 20 | 21 | // clear canvas pixels 22 | for (x <- 0 until MAX_RES; y <- 0 until MAX_RES) pixels(y * width + x) = 0 23 | 24 | // count number of bodies in each pixel 25 | for (b <- model.bodies) { 26 | val px = ((b.x - model.screen.minX) / model.screen.width * width).toInt 27 | val py = ((b.y - model.screen.minY) / model.screen.height * height).toInt 28 | if (px >= 0 && px < width && py >= 0 && py < height) pixels(py * width + px) += 1 29 | } 30 | 31 | // set image intensity depending on the number of bodies in the pixel 32 | for (y <- 0 until height; x <- 0 until width) { 33 | val count = pixels(y * width + x) 34 | val intensity = if (count > 0) math.min(255, 70 + count * 50) else 0 35 | val color = (255 << 24) | (intensity << 16) | (intensity << 8) | intensity 36 | img.setRGB(x, y, color) 37 | } 38 | 39 | // for debugging purposes, if the number of bodies is small, output their locations 40 | val g = img.getGraphics.asInstanceOf[Graphics2D] 41 | g.setColor(Color.GRAY) 42 | if (model.bodies.length < 350) for (b <- model.bodies) { 43 | def round(x: Float) = (x * 100).toInt / 100.0f 44 | val px = ((b.x - model.screen.minX) / model.screen.width * width).toInt 45 | val py = ((b.y - model.screen.minY) / model.screen.height * height).toInt 46 | if (px >= 0 && px < width && py >= 0 && py < height) { 47 | g.drawString(s"${round(b.x)}, ${round(b.y)}", px, py) 48 | } 49 | } 50 | 51 | // render quad if necessary 52 | if (model.shouldRenderQuad) { 53 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) 54 | val green = new Color(0, 225, 80, 150) 55 | val red = new Color(200, 0, 0, 150) 56 | g.setColor(green) 57 | def drawQuad(depth: Int, quad: Quad): Unit = { 58 | def drawRect(fx: Float, fy: Float, fsz: Float, q: Quad, fill: Boolean = false) { 59 | val x = ((fx - model.screen.minX) / model.screen.width * width).toInt 60 | val y = ((fy - model.screen.minY) / model.screen.height * height).toInt 61 | val w = ((fx + fsz - model.screen.minX) / model.screen.width * width).toInt - x 62 | val h = ((fy + fsz - model.screen.minY) / model.screen.height * height).toInt - y 63 | g.drawRect(x, y, w, h) 64 | if (fill) g.fillRect(x, y, w, h) 65 | if (depth <= 5) g.drawString("#:" + q.total, x + w / 2, y + h / 2) 66 | } 67 | quad match { 68 | case Fork(nw, ne, sw, se) => 69 | val cx = quad.centerX 70 | val cy = quad.centerY 71 | val sz = quad.size 72 | drawRect(cx - sz / 2, cy - sz / 2, sz / 2, nw) 73 | drawRect(cx - sz / 2, cy, sz / 2, sw) 74 | drawRect(cx, cy - sz / 2, sz / 2, ne) 75 | drawRect(cx, cy, sz / 2, se) 76 | drawQuad(depth + 1, nw) 77 | drawQuad(depth + 1, ne) 78 | drawQuad(depth + 1, sw) 79 | drawQuad(depth + 1, se) 80 | case Empty(_, _, _) | Leaf(_, _, _, _) => 81 | // done 82 | } 83 | } 84 | drawQuad(0, model.quad) 85 | 86 | } 87 | gcan.drawImage(img, 0, 0, null) 88 | } 89 | 90 | // zoom on mouse rotation 91 | addMouseWheelListener(new MouseAdapter { 92 | override def mouseWheelMoved(e: MouseWheelEvent) { 93 | val rot = e.getWheelRotation 94 | val cx = model.screen.centerX 95 | val cy = model.screen.centerY 96 | val w = model.screen.width 97 | val h = model.screen.height 98 | val factor = { 99 | if (rot > 0) 0.52f 100 | else if (rot < 0) 0.48f 101 | else 0.5f 102 | } 103 | model.screen.minX = cx - w * factor 104 | model.screen.minY = cy - h * factor 105 | model.screen.maxX = cx + w * factor 106 | model.screen.maxY = cy + h * factor 107 | repaint() 108 | } 109 | }) 110 | 111 | // reset the last known mouse drag position on mouse press 112 | var xlast = Int.MinValue 113 | var ylast = Int.MinValue 114 | addMouseListener(new MouseAdapter { 115 | override def mousePressed(e: MouseEvent) { 116 | xlast = Int.MinValue 117 | ylast = Int.MinValue 118 | } 119 | }) 120 | 121 | // update the last known mouse drag position on mouse drag, 122 | // update the boundaries of the visible area 123 | addMouseMotionListener(new MouseMotionAdapter { 124 | override def mouseDragged(e: MouseEvent) { 125 | val xcurr = e.getX 126 | val ycurr = e.getY 127 | if (xlast != Int.MinValue) { 128 | val xd = xcurr - xlast 129 | val yd = ycurr - ylast 130 | val w = model.screen.width 131 | val h = model.screen.height 132 | val cx = model.screen.centerX - xd * w / 1000 133 | val cy = model.screen.centerY - yd * h / 1000 134 | model.screen.minX = cx - w / 2 135 | model.screen.minY = cy - h / 2 136 | model.screen.maxX = cx + w / 2 137 | model.screen.maxY = cy + h / 2 138 | println(model.screen) 139 | } 140 | xlast = xcurr 141 | ylast = ycurr 142 | repaint() 143 | } 144 | }) 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/scala/reductions/LineOfSight.scala: -------------------------------------------------------------------------------- 1 | package reductions 2 | 3 | import org.scalameter._ 4 | import common._ 5 | 6 | object LineOfSightRunner { 7 | 8 | val standardConfig = config( 9 | Key.exec.minWarmupRuns -> 10, //40, 10 | Key.exec.maxWarmupRuns -> 20, //80, 11 | Key.exec.benchRuns -> 20, //100, 12 | Key.verbose -> true 13 | ) withWarmer(new Warmer.Default) 14 | 15 | def main(args: Array[String]) { 16 | val length = 10000000 17 | val threshold = 100 // 10000 18 | val input = (0 until length).map(_ % 100 * 1.0f).toArray 19 | val output = new Array[Float](length + 1) 20 | val seqtime = standardConfig measure { 21 | LineOfSight.lineOfSight(input, output) 22 | } 23 | println(s"sequential time: $seqtime ms") 24 | 25 | val partime = standardConfig measure { 26 | LineOfSight.parLineOfSight(input, output, threshold ) 27 | } 28 | println(s"parallel time: $partime ms") 29 | println(s"speedup: ${seqtime / partime}") 30 | } 31 | } 32 | 33 | object LineOfSight { 34 | 35 | def max(a: Float, b: Float): Float = if (a > b) a else b 36 | 37 | def lineOfSight(input: Array[Float], output: Array[Float]): Unit = { 38 | input.zipWithIndex.foreach{ 39 | case (xs, 0) => output(0) = 0 40 | case (xs, i) => output(i) = Math.max(xs / i, output(i-1)) 41 | } 42 | } 43 | 44 | sealed abstract class Tree { 45 | def maxPrevious: Float 46 | // override def toString(): String 47 | } 48 | 49 | case class Node(left: Tree, right: Tree) extends Tree { 50 | val maxPrevious = max(left.maxPrevious, right.maxPrevious) 51 | // override def toString(): String = " (" + left + ")|(" + right + ")> ["+maxPrevious+"] " 52 | 53 | } 54 | 55 | case class Leaf(from: Int, until: Int, maxPrevious: Float) extends Tree { 56 | // override def toString(): String = maxPrevious.toString 57 | } 58 | 59 | /** Traverses the specified part of the array and returns the maximum angle. 60 | */ 61 | def upsweepSequential(input: Array[Float], from: Int, until: Int): Float = { 62 | (from until until). 63 | foldLeft(0f)( 64 | (curMax, i) => List(input(i) / i, curMax).max 65 | ) 66 | 67 | // Imperative, but better performant? 68 | // var i = from 69 | // var max:Float = 0 70 | // while (i < until){ 71 | // max = Math.max(max, input(i) / i) 72 | // i = i + 1 73 | // } 74 | // max 75 | } 76 | 77 | /** Traverses the part of the array starting at `from` and until `end`, and 78 | * returns the reduction tree for that part of the array. 79 | * 80 | * The reduction tree is a `Leaf` if the length of the specified part of the 81 | * array is smaller or equal to `threshold`, and a `Node` otherwise. 82 | * If the specified part of the array is longer than `threshold`, then the 83 | * work is divided and done recursively in parallel. 84 | */ 85 | def upsweep(input: Array[Float], from: Int, end: Int, 86 | threshold: Int): Tree = { 87 | // println(from + " - " + end ) 88 | //Leaf(from, end, upsweepSequential(input, from, end)) 89 | 90 | if(end - from <= threshold) Leaf(from, end, upsweepSequential(input, from, end)) 91 | else { 92 | val mid = from + (end - from) / 2 93 | val (left, right) = parallel( 94 | upsweep(input, from, mid, threshold), 95 | upsweep(input, mid, end, threshold)) 96 | Node (left, right) 97 | } 98 | } 99 | 100 | /** Traverses the part of the `input` array starting at `from` and until 101 | * `until`, and computes the maximum angle for each entry of the output array, 102 | * given the `startingAngle`. 103 | */ 104 | def downsweepSequential(input: Array[Float], output: Array[Float], 105 | startingAngle: Float, from: Int, until: Int): Unit = { 106 | def calculateOutput(i: Int, end: Int, max:Float): Unit = { 107 | if(i < end){ 108 | output(i) = List(input(i) / i, max).max 109 | calculateOutput(i+1, end, output(i)) 110 | } 111 | } 112 | 113 | calculateOutput(from, until, startingAngle) 114 | 115 | // More convoluted then recursive solution IMHO / performance diff? 116 | // (from until until).foreach{ i => 117 | // if(i == 0) output(0) = 0 118 | // else if(i == from) output(i) = Math.max(input(i) / i, startingAngle) 119 | // else output(i) = Math.max(input(i) / i, output(i-1)) 120 | // } 121 | } 122 | 123 | /** Pushes the maximum angle in the prefix of the array to each leaf of the 124 | * reduction `tree` in parallel, and then calls `downsweepTraverse` to write 125 | * the `output` angles. 126 | */ 127 | def downsweep(input: Array[Float], output: Array[Float], startingAngle: Float, 128 | tree: Tree): Unit = { 129 | tree match { 130 | case Leaf(from, until, _) => downsweepSequential(input, output, startingAngle, from, until) 131 | case Node(left, right) => { 132 | parallel( 133 | downsweep(input, output, startingAngle, left), // 0 134 | downsweep(input, output, left.maxPrevious max startingAngle,right)) // 6 135 | } 136 | } 137 | } 138 | 139 | /* 140 | 6 141 | / \ 142 | 6 7 143 | / \ / \ 144 | 6 4 7 2 145 | / \ / \ 146 | 2 3 6 1 2 4 3 7 2 1 1 2 147 | */ 148 | 149 | 150 | /** Compute the line-of-sight in parallel. */ 151 | def parLineOfSight(input: Array[Float], output: Array[Float], 152 | threshold: Int): Unit = { 153 | // println("up") 154 | val tree = upsweep(input, 0, input.length, threshold) 155 | // println("down") 156 | // println(input.toList) 157 | // println(tree) 158 | downsweep(input, output, 0f, tree) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/scala/kmeans/fun/ScalaShop.scala: -------------------------------------------------------------------------------- 1 | package kmeans 2 | package fun 3 | 4 | import java.awt._ 5 | import java.awt.event._ 6 | import javax.swing._ 7 | import javax.swing.event._ 8 | import scala.collection.parallel._ 9 | import scala.collection.par._ 10 | import scala.collection.mutable.ArrayBuffer 11 | import scala.reflect.ClassTag 12 | import org.scalameter._ 13 | import common._ 14 | 15 | object ScalaShop { 16 | 17 | class ScalaShopFrame extends JFrame("ScalaShop\u2122") { 18 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) 19 | setSize(800, 500) 20 | setLayout(new BorderLayout) 21 | 22 | val rightpanel = new JPanel 23 | rightpanel.setBorder(BorderFactory.createEtchedBorder(border.EtchedBorder.LOWERED)) 24 | rightpanel.setLayout(new BorderLayout) 25 | add(rightpanel, BorderLayout.EAST) 26 | 27 | val allControls = new JPanel 28 | allControls.setLayout(new BoxLayout(allControls, BoxLayout.Y_AXIS)) 29 | rightpanel.add(allControls, BorderLayout.NORTH) 30 | 31 | // Color count selection 32 | val colorControls = new JPanel 33 | colorControls.setLayout(new GridLayout(0, 2)) 34 | allControls.add(colorControls) 35 | 36 | val colorCountLabel = new JLabel("Colors") 37 | colorControls.add(colorCountLabel) 38 | 39 | val colorCountSpinner = new JSpinner(new SpinnerNumberModel(32, 16, 512, 16)) 40 | colorControls.add(colorCountSpinner) 41 | 42 | // Initial selection 43 | val initSelectionControls = new JPanel 44 | initSelectionControls.setLayout(new GridLayout(0, 1)) 45 | allControls.add(initSelectionControls) 46 | 47 | val initialSelectionGroup = new ButtonGroup() 48 | 49 | val initSelectionLabel = new JLabel("Initial Color Selection:") 50 | initSelectionControls.add(initSelectionLabel) 51 | 52 | val uniformSamplingButton = new JRadioButton("Uniform Sampling") 53 | uniformSamplingButton.setSelected(true); 54 | initSelectionControls.add(uniformSamplingButton) 55 | 56 | val randomSamplingButton = new JRadioButton("Random Sampling") 57 | initSelectionControls.add(randomSamplingButton) 58 | 59 | val uniformChoiceButton = new JRadioButton("Uniform Choice") 60 | initSelectionControls.add(uniformChoiceButton) 61 | 62 | initialSelectionGroup.add(randomSamplingButton) 63 | initialSelectionGroup.add(uniformSamplingButton) 64 | initialSelectionGroup.add(uniformChoiceButton) 65 | 66 | // Initial means selection 67 | val convergenceControls = new JPanel 68 | convergenceControls.setLayout(new BoxLayout(convergenceControls, BoxLayout.Y_AXIS)) 69 | allControls.add(convergenceControls) 70 | 71 | val convergenceGroup = new ButtonGroup() 72 | 73 | val convergenceLabel = new JLabel("Convergence criteria:") 74 | initSelectionControls.add(convergenceLabel) 75 | 76 | val criteriaControls = new JPanel 77 | criteriaControls.setLayout(new GridLayout(0, 2)) 78 | convergenceControls.add(criteriaControls) 79 | 80 | val stepConvergenceButton = new JRadioButton("Steps") 81 | criteriaControls.add(stepConvergenceButton) 82 | 83 | val stepCountSpinner = new JSpinner(new SpinnerNumberModel(5, 1, 50, 1)) 84 | criteriaControls.add(stepCountSpinner) 85 | 86 | val etaConvergenceButton = new JRadioButton("Eta") 87 | etaConvergenceButton.setSelected(true); 88 | criteriaControls.add(etaConvergenceButton) 89 | 90 | val etaCountSpinner = new JSpinner(new SpinnerNumberModel(0.001, 0.00001, 0.01, 0.00001)) 91 | criteriaControls.add(etaCountSpinner) 92 | 93 | val snrConvergenceButton = new JRadioButton("Sound-to-noise") 94 | criteriaControls.add(snrConvergenceButton) 95 | 96 | val snrCountSpinner = new JSpinner(new SpinnerNumberModel(40, 10, 80, 1)) 97 | criteriaControls.add(snrCountSpinner) 98 | 99 | convergenceGroup.add(stepConvergenceButton) 100 | convergenceGroup.add(etaConvergenceButton) 101 | convergenceGroup.add(snrConvergenceButton) 102 | 103 | // Action Buttons 104 | val actionControls = new JPanel 105 | actionControls.setLayout(new GridLayout(0, 2)) 106 | allControls.add(actionControls) 107 | 108 | val stepbutton = new JButton("Apply filter") 109 | stepbutton.addActionListener(new ActionListener { 110 | def actionPerformed(e: ActionEvent) { 111 | var status = "" 112 | val time = measure { 113 | status = canvas.applyIndexedColors(getColorCount, getInitialSelectionStrategy, getConvergenceStragegy) 114 | } 115 | updateInformationBox(status, time) 116 | } 117 | }) 118 | actionControls.add(stepbutton) 119 | 120 | val clearButton = new JButton("Reload") 121 | clearButton.addActionListener(new ActionListener { 122 | def actionPerformed(e: ActionEvent) { 123 | canvas.reload() 124 | } 125 | }) 126 | actionControls.add(clearButton) 127 | 128 | val info = new JTextArea(" ") 129 | info.setBorder(BorderFactory.createLoweredBevelBorder) 130 | rightpanel.add(info, BorderLayout.SOUTH) 131 | 132 | val mainMenuBar = new JMenuBar() 133 | 134 | val fileMenu = new JMenu("File") 135 | val openMenuItem = new JMenuItem("Open...") 136 | openMenuItem.addActionListener(new ActionListener { 137 | def actionPerformed(e: ActionEvent) { 138 | val fc = new JFileChooser() 139 | if (fc.showOpenDialog(ScalaShopFrame.this) == JFileChooser.APPROVE_OPTION) { 140 | canvas.loadFile(fc.getSelectedFile.getPath) 141 | } 142 | } 143 | }) 144 | fileMenu.add(openMenuItem) 145 | val saveMenuItem = new JMenuItem("Save...") 146 | saveMenuItem.addActionListener(new ActionListener { 147 | def actionPerformed(e: ActionEvent) { 148 | val fc = new JFileChooser("epfl-view.png") 149 | if (fc.showSaveDialog(ScalaShopFrame.this) == JFileChooser.APPROVE_OPTION) { 150 | canvas.saveFile(fc.getSelectedFile.getPath) 151 | } 152 | } 153 | }) 154 | fileMenu.add(saveMenuItem) 155 | val exitMenuItem = new JMenuItem("Exit") 156 | exitMenuItem.addActionListener(new ActionListener { 157 | def actionPerformed(e: ActionEvent) { 158 | sys.exit(0) 159 | } 160 | }) 161 | fileMenu.add(exitMenuItem) 162 | 163 | mainMenuBar.add(fileMenu) 164 | 165 | val helpMenu = new JMenu("Help") 166 | val aboutMenuItem = new JMenuItem("About") 167 | aboutMenuItem.addActionListener(new ActionListener { 168 | def actionPerformed(e: ActionEvent) { 169 | JOptionPane.showMessageDialog(null, "ScalaShop, the ultimate image manipulation tool\nBrought to you by EPFL, 2015") 170 | } 171 | }) 172 | helpMenu.add(aboutMenuItem) 173 | 174 | mainMenuBar.add(helpMenu) 175 | 176 | setJMenuBar(mainMenuBar) 177 | 178 | val canvas = new PhotoCanvas 179 | 180 | val scrollPane = new JScrollPane(canvas) 181 | 182 | add(scrollPane, BorderLayout.CENTER) 183 | setVisible(true) 184 | 185 | def updateInformationBox(status: String, time: Double) { 186 | info.setText(s"$status\nTime: ${time.toInt} ms.") 187 | } 188 | 189 | def getColorCount: Int = 190 | colorCountSpinner.getValue.asInstanceOf[Int] 191 | 192 | def getInitialSelectionStrategy: InitialSelectionStrategy = 193 | if (randomSamplingButton.isSelected()) 194 | RandomSampling 195 | else if (uniformSamplingButton.isSelected()) 196 | UniformSampling 197 | else 198 | UniformChoice 199 | 200 | def getConvergenceStragegy: ConvergenceStrategy = 201 | if (stepConvergenceButton.isSelected()) 202 | ConvergedAfterNSteps(stepCountSpinner.getValue.asInstanceOf[Int]) 203 | else if (etaConvergenceButton.isSelected()) 204 | ConvergedAfterMeansAreStill(etaCountSpinner.getValue.asInstanceOf[Double]) 205 | else 206 | ConvergedWhenSNRAbove(snrCountSpinner.getValue.asInstanceOf[Int]) 207 | } 208 | 209 | try { 210 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) 211 | } catch { 212 | case _: Exception => println("Cannot set look and feel, using the default one.") 213 | } 214 | 215 | val frame = new ScalaShopFrame 216 | 217 | def main(args: Array[String]) { 218 | frame.repaint() 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /project/GradingFeedback.scala: -------------------------------------------------------------------------------- 1 | import sbt.{Logger, Level} 2 | 3 | import collection.mutable.ListBuffer 4 | 5 | class GradingFeedback { 6 | 7 | def maxTestScore = maxScore * (1 - styleScoreRatio) 8 | 9 | def maxStyleScore = maxScore * styleScoreRatio 10 | 11 | def totalScore = vTestScore + vStyleScore 12 | 13 | def maxTotalScore = maxTestScore + maxStyleScore 14 | 15 | def feedbackString = 16 | s"""|${totalGradeMessage(totalScore)} 17 | | 18 | | 19 | |${feedbackSummary.mkString("\n\n")} 20 | | 21 | |${feedbackDetails.mkString("\n")}""".stripMargin 22 | 23 | /** 24 | * `failed` means that there was an unexpected error during grading. This includes 25 | * - student's code does not compile 26 | * - our tests don't compile (against the student's code) 27 | * - crash while executing ScalaTest (not test failures, but problems trying to run the tests!) 28 | * - crash while executing the style checker (again, not finding style problems!) 29 | * 30 | * When failed is `true`, later grading stages will not be executed: this is handled automatically 31 | * by SBT, tasks depending on a failed one are not run. 32 | * 33 | * However, these dependent tasks still fail (i.e. mapR on them is invoked). The variable below 34 | * allows us to know if something failed before. In this case, we don't add any more things to 35 | * the log. (see `ProgFunBuild.handleFailure`) 36 | */ 37 | def isFailed = failed 38 | 39 | /* Methods to build up the feedback log */ 40 | 41 | def compileFailed(log: String) { 42 | failed = true 43 | addSummary(compileFailedMessage) 44 | addDetails("======== COMPILATION FAILURES ========") 45 | addDetails(log) 46 | } 47 | 48 | def testCompileFailed(log: String) { 49 | failed = true 50 | addSummary(testCompileFailedMessage) 51 | addDetails("======== TEST COMPILATION FAILURES ========") 52 | addDetails(log) 53 | } 54 | 55 | def allTestsPassed() { 56 | addSummary(allTestsPassedMessage) 57 | vTestScore = maxTestScore 58 | } 59 | 60 | def testsFailed(log: String, score: Double) { 61 | failed = true 62 | addSummary(testsFailedMessage(score)) 63 | vTestScore = score 64 | addDetails("======== LOG OF FAILED TESTS ========") 65 | addDetails(log) 66 | } 67 | 68 | def testExecutionFailed(log: String) { 69 | failed = true 70 | addSummary(testExecutionFailedMessage) 71 | addDetails("======== ERROR LOG OF TESTING TOOL ========") 72 | addDetails(log) 73 | } 74 | 75 | def testExecutionDebugLog(log: String) { 76 | addDetails("======== DEBUG OUTPUT OF TESTING TOOL ========") 77 | addDetails(log) 78 | } 79 | 80 | def perfectStyle() { 81 | addSummary(perfectStyleMessage) 82 | vStyleScore = maxStyleScore 83 | } 84 | 85 | def styleProblems(log: String, score: Double) { 86 | addSummary(styleProblemsMessage(score)) 87 | vStyleScore = score 88 | addDetails("======== CODING STYLE ISSUES ========") 89 | addDetails(log) 90 | } 91 | 92 | def unpackFailed(log: String) { 93 | failed = true 94 | addSummary(unpackFailedMessage) 95 | addDetails("======== FAILURES WHILE EXTRACTING THE SUBMISSION ========") 96 | addDetails(log) 97 | } 98 | 99 | def setMaxScore(newMaxScore: Double, newStyleScoreRatio: Double): Unit = { 100 | maxScore = newMaxScore 101 | styleScoreRatio = newStyleScoreRatio 102 | } 103 | 104 | private var maxScore: Double = _ 105 | private var styleScoreRatio: Double = _ 106 | 107 | private var vTestScore: Double = 0d 108 | private var vStyleScore: Double = 0d 109 | 110 | private val feedbackSummary = new ListBuffer[String]() 111 | private val feedbackDetails = new ListBuffer[String]() 112 | 113 | private var failed = false 114 | 115 | private def addSummary(msg: String): Unit = 116 | feedbackSummary += msg 117 | 118 | private def addDetails(msg: String): Unit = 119 | feedbackDetails += msg 120 | 121 | /* Feedback Messages */ 122 | 123 | private val unpackFailedMessage = 124 | """Extracting the archive containing your source code failed. 125 | | 126 | |If you see this error message as your grade feedback, please verify that you used the unchanged 127 | |`sbt submit` command to upload your assignment and verify that you have the latest assignment 128 | |handout. If you did all of the above and grading still fails, please check the forums to see if 129 | |this issue has already been reported. See below for a detailed error log.""".stripMargin 130 | 131 | private val compileFailedMessage = 132 | """We were not able to compile the source code you submitted. This is not expected to happen, 133 | |because the `submit` command in SBT can only be executed if your source code compiles. 134 | | 135 | |Please verify the following points: 136 | | - You should use the `submit` command in SBT to upload your solution 137 | | - You should not perform any changes to the SBT project definition files, i.e. the *.sbt 138 | | files, and the files in the `project/` directory 139 | | 140 | |Take a careful look at the compiler output below - maybe you can find out what the problem is. 141 | | 142 | |If you cannot find a solution, ask for help on the discussion forums on the course website.""".stripMargin 143 | 144 | private val testCompileFailedMessage = 145 | """We were not able to compile our tests, and therefore we could not correct your submission. 146 | | 147 | |The most likely reason for this problem is that your submitted code uses different names 148 | |for methods, classes, objects or different types than expected. 149 | | 150 | |In principle, this can only arise if you changed some names or types in the code that we 151 | |provide, for instance a method name or a parameter type. 152 | | 153 | |To diagnose your problem, perform the following steps: 154 | | - Run the tests that we provide with our hand-out. These tests verify that all names and 155 | | types are correct. In case these tests pass, but you still see this message, please post 156 | | a report on the forums [1]. 157 | | - Take a careful look at the error messages from the Scala compiler below. They should give 158 | | you a hint where your code has an unexpected shape. 159 | | 160 | |If you cannot find a solution, ask for help on the discussion forums on the course website.""".stripMargin 161 | 162 | private def testsFailedMessage(score: Double) = 163 | """The code you submitted did not pass all of our tests: your submission achieved a score of 164 | |%.2f out of %.2f in our tests. 165 | | 166 | |In order to find bugs in your code, we advise to perform the following steps: 167 | | - Take a close look at the test output that you can find below: it should point you to 168 | | the part of your code that has bugs. 169 | | - Run the tests that we provide with the handout on your code. 170 | | - The tests we provide do not test your code in depth: they are very incomplete. In order 171 | | to test more aspects of your code, write your own unit tests. 172 | | - Take another very careful look at the assignment description. Try to find out if you 173 | | misunderstood parts of it. While reading through the assignment, write more tests. 174 | | 175 | |Below you can find a short feedback for every individual test that failed.""".stripMargin.format(score, maxTestScore) 176 | 177 | // def so that we read the right value of vMaxTestScore (initialize modifies it) 178 | private def allTestsPassedMessage = 179 | """Your solution passed all of our tests, congratulations! You obtained the maximal test 180 | |score of %.2f.""".stripMargin.format(maxTestScore) 181 | 182 | private val testExecutionFailedMessage = 183 | """An error occurred while running our tests on your submission. 184 | | 185 | |In order for us to help you, please contact one of the teaching assistants and send 186 | |them the entire feedback message that you received.""".stripMargin 187 | 188 | // def so that we read the right value of vMaxStyleScore (initialize modifies it) 189 | private def perfectStyleMessage = 190 | """Our automated style checker tool could not find any issues with your code. You obtained the maximal 191 | |style score of %.2f.""".stripMargin.format(maxStyleScore) 192 | 193 | private def styleProblemsMessage(score: Double) = 194 | """Our automated style checker tool found issues in your code with respect to coding style: it 195 | |computed a style score of %.2f out of %.2f for your submission. See below for detailed feedback.""".stripMargin.format(score, maxStyleScore) 196 | 197 | private def totalGradeMessage(score: Double) = 198 | """Your overall score for this assignment is %.2f out of %.2f""".format(score, maxTestScore + maxStyleScore) 199 | 200 | } 201 | 202 | /** 203 | * Logger to capture compiler output, test output 204 | */ 205 | 206 | object RecordingLogger extends Logger { 207 | private val buffer = ListBuffer[String]() 208 | 209 | def hasErrors = buffer.nonEmpty 210 | 211 | def readAndClear() = { 212 | val res = buffer.mkString("\n") 213 | buffer.clear() 214 | res 215 | } 216 | 217 | def clear() { 218 | buffer.clear() 219 | } 220 | 221 | def log(level: Level.Value, message: => String) = 222 | if (level == Level.Error) { 223 | buffer += message 224 | } 225 | 226 | // we don't log success here 227 | def success(message: => String) = () 228 | 229 | // invoked when a task throws an exception. invoked late, when the exception is logged, i.e. 230 | // just before returning to the prompt. therefore we do nothing: storing the exception in the 231 | // buffer would happen *after* the `handleFailure` reads the buffer. 232 | def trace(t: => Throwable) = () 233 | } 234 | -------------------------------------------------------------------------------- /project/ScalaTestRunner.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import sys.process.{Process => SysProc, ProcessLogger} 4 | import java.util.concurrent._ 5 | import collection.mutable.ListBuffer 6 | import scala.pickling.Defaults._ 7 | import scala.pickling.json._ 8 | 9 | final case class GradingSummary(score: Int, maxScore: Int, feedback: String) 10 | 11 | object ScalaTestRunner { 12 | 13 | class LimitedStringBuffer { 14 | val buf = new ListBuffer[String]() 15 | private var lines = 0 16 | private var lengthCropped = false 17 | 18 | override def toString = buf.mkString("\n").trim 19 | 20 | def append(s: String) = 21 | if (lines < Settings.maxOutputLines) { 22 | val shortS = 23 | if (s.length > Settings.maxOutputLineLength) { 24 | if (!lengthCropped) { 25 | val msg = 26 | """WARNING: OUTPUT LINES CROPPED 27 | |Your program generates very long lines on the standard (or error) output. Some of 28 | |the lines have been cropped. 29 | |This should not have an impact on your grade or the grading process; however it is 30 | |bad style to leave `print` statements in production code, so consider removing and 31 | |replacing them by proper tests. 32 | | """.stripMargin 33 | buf.prepend(msg) 34 | lengthCropped = true 35 | } 36 | s.substring(0, Settings.maxOutputLineLength) 37 | } else s 38 | buf.append(shortS) 39 | lines += 1 40 | } else if (lines == Settings.maxOutputLines) { 41 | val msg = 42 | """WARNING: PROGRAM OUTPUT TOO LONG 43 | |Your program generates massive amounts of data on the standard (or error) output. 44 | |You are probably using `print` statements to debug your code. 45 | |This should not have an impact on your grade or the grading process; however it is 46 | |bad style to leave `print` statements in production code, so consider removing and 47 | |replacing them by proper tests. 48 | | """.stripMargin 49 | buf.prepend(msg) 50 | lines += 1 51 | } 52 | } 53 | 54 | private def forkProcess(proc: SysProc, timeout: Int): Unit = { 55 | val executor = Executors.newSingleThreadExecutor() 56 | val future: Future[Unit] = executor.submit(new Callable[Unit] { 57 | def call { 58 | proc.exitValue() 59 | } 60 | }) 61 | try { 62 | future.get(timeout, TimeUnit.SECONDS) 63 | } catch { 64 | case to: TimeoutException => 65 | future.cancel(true) 66 | throw to 67 | } finally { 68 | executor.shutdown() 69 | } 70 | } 71 | 72 | private def runPathString(file: File) = file.getAbsolutePath.replace(" ", "\\ ") 73 | 74 | private def invokeScalaTestInSeparateProcess(scalaTestCommand: List[String], logError: String => Unit, timeout: Int): String = { 75 | val out = new LimitedStringBuffer() 76 | var proc: SysProc = null 77 | try { 78 | proc = SysProc(scalaTestCommand).run(ProcessLogger(out.append, out.append)) 79 | forkProcess(proc, timeout) 80 | } catch { 81 | case e: TimeoutException => 82 | val msg = "Timeout when running ScalaTest\n" + out.toString() 83 | logError(msg) 84 | proc.destroy() 85 | case e: Throwable => 86 | val msg = "Error occurred while running the ScalaTest command\n" + e.toString + "\n" + out.toString() 87 | logError(msg) 88 | proc.destroy() 89 | throw e 90 | } finally { 91 | println(out.toString) 92 | if (proc != null) { 93 | println("Exit process: " + proc.exitValue()) 94 | } 95 | } 96 | 97 | out.toString 98 | } 99 | 100 | private def computeSummary(outFilePath: String, classpathString: String, logError: String => Unit): String = { 101 | val summaryFilePath = outFilePath + ".summary" 102 | val summaryCmd = "java" :: 103 | "-cp" :: classpathString :: 104 | "ch.epfl.lamp.grading.GradingSummaryRunner" :: 105 | outFilePath :: summaryFilePath :: Nil 106 | var summaryProc: SysProc = null 107 | try { 108 | summaryProc = SysProc(summaryCmd).run() 109 | summaryProc.exitValue 110 | } catch { 111 | case e: Throwable => 112 | val msg = "Error occurred while running the test ScalaTest summary command\n" + e.toString 113 | logError(msg) 114 | summaryProc.destroy() 115 | throw e 116 | } /* finally { // Useful for debugging when Coursera kills our grader 117 | println(scala.io.Source.fromFile(outFilePath).getLines().mkString("\n")) 118 | println(scala.io.Source.fromFile(summaryFilePath).getLines().mkString("\n")) 119 | }*/ 120 | // Example output: 121 | // { 122 | // "$type": "ch.epfl.lamp.grading.Entry.SuiteStart", 123 | // "suiteId": "ParallelCountChangeSuite::50" 124 | // } 125 | summaryFilePath 126 | } 127 | 128 | def runScalaTest(classpath: Classpath, testClasses: File, outfile: File, 129 | resourceFiles: List[File], gradeOptions: Map[String, String], 130 | logError: String => Unit, instragentPath: String) = { 131 | 132 | // invoke scalatest in the separate process 133 | val classpathString = classpath map { case Attributed(file) => file.getAbsolutePath } mkString ":" 134 | val cmd = scalaTestCommand(testClasses, outfile, resourceFiles, gradeOptions, classpathString, instragentPath) 135 | 136 | val timeout = gradeOptions.getOrElse("totalTimeout", Settings.scalaTestTimeout.toString).toInt 137 | val runLog = invokeScalaTestInSeparateProcess(cmd, logError, timeout) 138 | 139 | // compute the summary 140 | val summaryFilePath = computeSummary(outfile.getAbsolutePath, classpathString, logError) 141 | val summary = unpickleSummary(logError, runLog, summaryFilePath) 142 | 143 | // cleanup all the files 144 | IO.delete(new File(summaryFilePath) :: outfile :: Nil) 145 | 146 | (summary.score, summary.maxScore, summary.feedback, runLog) 147 | } 148 | 149 | private def unpickleSummary(logError: (String) => Unit, runLog: String, summaryFileStr: String): GradingSummary = { 150 | try { 151 | io.Source.fromFile(summaryFileStr).getLines.mkString("\n").unpickle[GradingSummary] 152 | } catch { 153 | case e: Throwable => 154 | val msg = "Error occured while reading ScalaTest summary file\n" + e.toString + "\n" + runLog 155 | logError(msg) 156 | throw e 157 | } 158 | } 159 | 160 | private def scalaTestCommand(testClasses: File, outfile: File, resourceFiles: List[File], gradeOptions: Map[String, String], classpathString: String, 161 | instragentPath: String): List[String] = { 162 | val testRunPath = runPathString(testClasses) 163 | val resourceFilesString = resourceFiles.map(_.getAbsolutePath).mkString(":") 164 | // Deleting the file is helpful: it makes reading the file below crash in case ScalaTest doesn't 165 | // run as expected. Problem is, it's hard to detect if ScalaTest ran successfully or not: it 166 | // exits with non-zero if there are failed tests, and also if it crashes... 167 | outfile.delete() 168 | 169 | def prop(name: String, value: String) = "-D" + name + "=" + value 170 | 171 | // grade options 172 | val xmx = gradeOptions.get("Xmx").map("-Xmx" + _).getOrElse("-Xmx256m") 173 | val xms = gradeOptions.get("Xms").map("-Xms" + _).getOrElse("-Xms10m") 174 | val timeoutPerTest = gradeOptions.getOrElse("individualTimeout", Settings.individualTestTimeout.toString) 175 | 176 | // we don't specify "-w packageToTest" - the build file only compiles the tests 177 | // for the current project. so we don't need to do it again here. 178 | 179 | // NOTICE: DON'T start test in parallel, it would break profiling. Check the 180 | // implementation of @InstrumentedSuite for more details. 181 | "java" :: 182 | xmx :: xms :: 183 | s"-javaagent:$instragentPath" :: 184 | prop(Settings.scalaTestReportFileProperty, outfile.getAbsolutePath) :: 185 | prop(Settings.scalaTestIndividualTestTimeoutProperty, timeoutPerTest) :: 186 | prop(Settings.scalaTestReadableFilesProperty, resourceFilesString) :: 187 | prop(Settings.scalaTestDefaultWeightProperty, Settings.scalaTestDefaultWeight.toString) :: 188 | "-cp" :: classpathString :: 189 | "org.scalatest.tools.Runner" :: 190 | "-R" :: testRunPath :: 191 | "-C" :: Settings.scalaTestReporter :: 192 | Nil 193 | } 194 | 195 | private def testEnv(options: Map[String, String]): String = { 196 | val memory = options.get("Xmx").getOrElse("256m") 197 | val timeout = options.get("totalTimeout").map(_.toInt).getOrElse(Settings.scalaTestTimeout) 198 | val timeoutPerTest = options.get("individualTimeout").map(_.toInt).getOrElse(Settings.individualTestTimeout) 199 | 200 | "======== TESTING ENVIRONMENT ========\n" + 201 | s"Limits: memory: $memory, total time: ${timeout}s, per test case time: ${timeoutPerTest}s\n" 202 | } 203 | 204 | def scalaTestGrade(gradingReporter: GradingFeedback, classpath: Classpath, testClasses: File, outfile: File, 205 | resourceFiles: List[File], gradeOptions: Map[String, String], instragentPath: String): Unit = { 206 | 207 | val (score, maxScore, feedback, runLog) = 208 | runScalaTest(classpath, testClasses, outfile, resourceFiles, gradeOptions, gradingReporter.testExecutionFailed, instragentPath) 209 | 210 | if (score == maxScore) { 211 | gradingReporter.allTestsPassed() 212 | } else { 213 | val scaledScore = gradingReporter.maxTestScore * score / maxScore 214 | gradingReporter.testsFailed(feedback + testEnv(gradeOptions), scaledScore) 215 | } 216 | 217 | if (!runLog.isEmpty) { 218 | gradingReporter.testExecutionDebugLog(runLog) 219 | } 220 | } 221 | } 222 | 223 | -------------------------------------------------------------------------------- /src/main/scala/barneshut/package.scala: -------------------------------------------------------------------------------- 1 | import common._ 2 | import barneshut.conctrees._ 3 | 4 | package object barneshut { 5 | 6 | class Boundaries { 7 | var minX = Float.MaxValue 8 | 9 | var minY = Float.MaxValue 10 | 11 | var maxX = Float.MinValue 12 | 13 | var maxY = Float.MinValue 14 | 15 | def width = maxX - minX 16 | 17 | def height = maxY - minY 18 | 19 | def size = math.max(width, height) 20 | 21 | def centerX = minX + width / 2 22 | 23 | def centerY = minY + height / 2 24 | 25 | override def toString = s"Boundaries($minX, $minY, $maxX, $maxY)" 26 | } 27 | 28 | sealed abstract class Quad { 29 | def massX: Float 30 | 31 | def massY: Float 32 | 33 | def mass: Float 34 | 35 | def centerX: Float 36 | 37 | def centerY: Float 38 | 39 | def size: Float 40 | 41 | def total: Int 42 | 43 | def insert(b: Body): Quad 44 | } 45 | 46 | case class Empty(centerX: Float, centerY: Float, size: Float) extends Quad { 47 | def massX: Float = centerX 48 | 49 | def massY: Float = centerY 50 | 51 | def mass: Float = 0f 52 | 53 | def total: Int = 0 54 | 55 | def insert(b: Body): Quad = Leaf(centerX, centerY, size, Seq(b)) 56 | } 57 | 58 | case class Fork( 59 | nw: Quad, ne: Quad, sw: Quad, se: Quad 60 | ) extends Quad { 61 | val quadList = List(nw, ne, sw, se) 62 | val centerX: Float = (quadList.map(_.centerX).min + quadList.map(_.centerX).max) / 2f 63 | val centerY: Float = (quadList.map(_.centerY).min + quadList.map(_.centerY).max) / 2f 64 | val size: Float = nw.size * 2 65 | val mass: Float = quadList.foldLeft(0f)(_ + _.mass) 66 | val massX: Float = { 67 | if (mass == 0) centerX 68 | else quadList.foldLeft(0f) { case (sum, quad) => sum + quad.mass * quad.massX } / mass 69 | } 70 | val massY: Float = { 71 | if (mass == 0) centerY 72 | else quadList.foldLeft(0f) { case (sum, quad) => sum + quad.mass * quad.massY } / mass 73 | } 74 | val total: Int = quadList.foldLeft(0)(_ + _.total) 75 | 76 | def insert(b: Body): Fork = { 77 | if (b.x < centerX && b.y < centerY) Fork(nw.insert(b), ne, sw, se) 78 | else if (b.x < centerX && b.y >= centerY) Fork(nw, ne.insert(b), sw, se) 79 | else if (b.x >= centerX && b.y < centerY) Fork(nw, ne, sw.insert(b), se) 80 | else Fork(nw, ne, sw, se.insert(b)) 81 | 82 | } 83 | } 84 | 85 | case class Leaf(centerX: Float, centerY: Float, size: Float, bodies: Seq[Body]) 86 | extends Quad { 87 | val mass: Float = bodies.foldLeft(0f)(_ + _.mass) 88 | val massX: Float = bodies.foldLeft(0f) { case (sum, b) => sum + b.mass * b.x } / mass 89 | val massY: Float = bodies.foldLeft(0f) { case (sum, b) => sum + b.mass * b.y } / mass 90 | val total: Int = bodies.length 91 | 92 | def insert(b: Body): Quad = { 93 | if (size <= minimumSize) Leaf(centerX, centerY, size, b +: bodies) 94 | else { 95 | val halfSize: Float = size / 2 96 | val quarterSize: Float = size / 4 97 | val westX: Float = centerX - quarterSize 98 | val eastX: Float = centerX + quarterSize 99 | val northY: Float = centerY - quarterSize 100 | val southY: Float = centerY + quarterSize 101 | 102 | val fork = Fork(Empty(westX, northY, halfSize), 103 | Empty(eastX, northY, halfSize), 104 | Empty(westX, southY, halfSize), 105 | Empty(eastX, southY, halfSize)) 106 | 107 | b +: bodies foreach fork.insert 108 | fork 109 | 110 | } 111 | } 112 | } 113 | 114 | def minimumSize = 0.00001f 115 | 116 | def gee: Float = 100.0f 117 | 118 | def delta: Float = 0.01f 119 | 120 | def theta = 0.5f 121 | 122 | def eliminationThreshold = 0.5f 123 | 124 | def force(m1: Float, m2: Float, dist: Float): Float = gee * m1 * m2 / (dist * dist) 125 | 126 | def distance(x0: Float, y0: Float, x1: Float, y1: Float): Float = { 127 | math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)).toFloat 128 | } 129 | 130 | class Body(val mass: Float, val x: Float, val y: Float, val xspeed: Float, val yspeed: Float) { 131 | 132 | def updated(quad: Quad): Body = { 133 | var netforcex = 0.0f 134 | var netforcey = 0.0f 135 | 136 | def addForce(thatMass: Float, thatMassX: Float, thatMassY: Float): Unit = { 137 | val dist = distance(thatMassX, thatMassY, x, y) 138 | /* If the distance is smaller than 1f, we enter the realm of close 139 | * body interactions. Since we do not model them in this simplistic 140 | * implementation, bodies at extreme proximities get a huge acceleration, 141 | * and are catapulted from each other's gravitational pull at extreme 142 | * velocities (something like this: 143 | * http://en.wikipedia.org/wiki/Interplanetary_spaceflight#Gravitational_slingshot). 144 | * To decrease the effect of this gravitational slingshot, as a very 145 | * simple approximation, we ignore gravity at extreme proximities. 146 | */ 147 | if (dist > 1f) { 148 | val dforce = force(mass, thatMass, dist) 149 | val xn = (thatMassX - x) / dist 150 | val yn = (thatMassY - y) / dist 151 | val dforcex = dforce * xn 152 | val dforcey = dforce * yn 153 | netforcex += dforcex 154 | netforcey += dforcey 155 | } 156 | } 157 | 158 | def traverse(quad: Quad): Unit = (quad: Quad) match { 159 | case Empty(_, _, _) => 160 | // no force 161 | case Leaf(_, _, _, bodies) => 162 | // add force contribution of each body by calling addForce 163 | bodies foreach (body => addForce(body.mass, body.x, body.y)) 164 | case Fork(nw, ne, sw, se) => { 165 | // see if node is far enough from the body, 166 | // or recursion is needed 167 | if (quad.size / distance(quad.massX, quad.massY, x, y) < theta) { 168 | addForce(quad.mass, quad.massX, quad.massY) 169 | } else { 170 | traverse(nw) 171 | traverse(ne) 172 | traverse(sw) 173 | traverse(se) 174 | } 175 | } 176 | 177 | } 178 | 179 | traverse(quad) 180 | 181 | val nx = x + xspeed * delta 182 | val ny = y + yspeed * delta 183 | val nxspeed = xspeed + netforcex / mass * delta 184 | val nyspeed = yspeed + netforcey / mass * delta 185 | 186 | new Body(mass, nx, ny, nxspeed, nyspeed) 187 | } 188 | 189 | } 190 | 191 | val SECTOR_PRECISION = 8 192 | 193 | class SectorMatrix(val boundaries: Boundaries, val sectorPrecision: Int) { 194 | val sectorSize = boundaries.size / sectorPrecision 195 | val matrix = new Array[ConcBuffer[Body]](sectorPrecision * sectorPrecision) 196 | for (i <- 0 until matrix.length) matrix(i) = new ConcBuffer 197 | 198 | def +=(b: Body): SectorMatrix = { 199 | def getPos(p1: Float, p2: Float):Int = { 200 | ((p1 - p2) / sectorSize).toInt max 0 min sectorPrecision - 1 201 | } 202 | this(getPos(b.x,boundaries.minX), getPos(b.y,boundaries.minY)) += b 203 | this 204 | } 205 | 206 | def apply(x: Int, y: Int) = matrix(y * sectorPrecision + x) 207 | 208 | def combine(that: SectorMatrix): SectorMatrix = { 209 | // val x = matrix zip that.matrix map{case (a,b) => a.combine(b)} 210 | for (i <- 0 until matrix.length) matrix.update(i, matrix(i).combine(that.matrix(i))) 211 | this 212 | } 213 | 214 | def toQuad(parallelism: Int): Quad = { 215 | def BALANCING_FACTOR = 4 216 | def quad(x: Int, y: Int, span: Int, achievedParallelism: Int): Quad = { 217 | if (span == 1) { 218 | val sectorSize = boundaries.size / sectorPrecision 219 | val centerX = boundaries.minX + x * sectorSize + sectorSize / 2 220 | val centerY = boundaries.minY + y * sectorSize + sectorSize / 2 221 | var emptyQuad: Quad = Empty(centerX, centerY, sectorSize) 222 | val sectorBodies = this (x, y) 223 | sectorBodies.foldLeft(emptyQuad)(_ insert _) 224 | } else { 225 | val nspan = span / 2 226 | val nAchievedParallelism = achievedParallelism * 4 227 | val (nw, ne, sw, se) = 228 | if (parallelism > 1 && achievedParallelism < parallelism * BALANCING_FACTOR) parallel( 229 | quad(x, y, nspan, nAchievedParallelism), 230 | quad(x + nspan, y, nspan, nAchievedParallelism), 231 | quad(x, y + nspan, nspan, nAchievedParallelism), 232 | quad(x + nspan, y + nspan, nspan, nAchievedParallelism) 233 | ) 234 | else ( 235 | quad(x, y, nspan, nAchievedParallelism), 236 | quad(x + nspan, y, nspan, nAchievedParallelism), 237 | quad(x, y + nspan, nspan, nAchievedParallelism), 238 | quad(x + nspan, y + nspan, nspan, nAchievedParallelism) 239 | ) 240 | Fork(nw, ne, sw, se) 241 | } 242 | } 243 | 244 | quad(0, 0, sectorPrecision, 1) 245 | } 246 | 247 | override def toString = s"SectorMatrix(#bodies: ${matrix.map(_.size).sum})" 248 | } 249 | 250 | class TimeStatistics { 251 | private val timeMap = collection.mutable.Map[String, (Double, Int)]() 252 | 253 | def clear() = timeMap.clear() 254 | 255 | def timed[T](title: String)(body: => T): T = { 256 | var res: T = null.asInstanceOf[T] 257 | val totalTime = /*measure*/ { 258 | val startTime = System.currentTimeMillis() 259 | res = body 260 | (System.currentTimeMillis() - startTime) 261 | } 262 | 263 | timeMap.get(title) match { 264 | case Some((total, num)) => timeMap(title) = (total + totalTime, num + 1) 265 | case None => timeMap(title) = (0.0, 0) 266 | } 267 | 268 | println(s"$title: ${totalTime} ms; avg: ${timeMap(title)._1 / timeMap(title)._2}") 269 | res 270 | } 271 | 272 | override def toString = { 273 | timeMap map { 274 | case (k, (total, num)) => k + ": " + (total / num * 100).toInt / 100.0 + " ms" 275 | } mkString ("\n") 276 | } 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /project/StudentBuildLike.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import Settings._ 4 | 5 | import java.io.{File, IOException, FileInputStream} 6 | import org.apache.commons.codec.binary.Base64 7 | 8 | import scala.util.parsing.json.JSON 9 | import scalaj.http._ 10 | 11 | import scala.util.{Try, Success, Failure} 12 | 13 | case class MapMapString (val map: Map[String, Map[String, String]]) 14 | /** 15 | * Note: keep this class concrete (i.e., do not convert it to abstract class or trait). 16 | */ 17 | class StudentBuildLike protected() extends CommonBuild { 18 | 19 | lazy val root = project.in(file(".")).settings( 20 | course := "", 21 | assignment := "", 22 | submitSetting, 23 | submitLocalSetting, 24 | commonSourcePackages := Seq(), // see build.sbt 25 | courseId := "", 26 | styleCheckSetting, 27 | libraryDependencies += scalaTestDependency 28 | ).settings(packageSubmissionFiles: _*) 29 | 30 | /** ********************************************************** 31 | * SUBMITTING A SOLUTION TO COURSERA 32 | */ 33 | 34 | val packageSubmission = TaskKey[File]("packageSubmission") 35 | 36 | val sourceMappingsWithoutPackages = 37 | (scalaSource, commonSourcePackages, unmanagedSources, unmanagedSourceDirectories, baseDirectory, compile in Test) map { (scalaSource, commonSourcePackages, srcs, sdirs, base, _) => 38 | val allFiles = srcs --- sdirs --- base 39 | val commonSourcePaths = commonSourcePackages.map(scalaSource / _).map(_.getPath) 40 | val withoutCommonSources = allFiles.filter(f => !commonSourcePaths.exists(f.getPath.startsWith)) 41 | withoutCommonSources pair (relativeTo(sdirs) | relativeTo(base) | flat) 42 | } 43 | 44 | val packageSubmissionFiles = { 45 | // in the packageSubmission task we only use the sources of the assignment and not the common sources. We also do not package resources. 46 | inConfig(Compile)(Defaults.packageTaskSettings(packageSubmission, sourceMappingsWithoutPackages)) 47 | } 48 | 49 | /** Check that the jar exists, isn't empty, isn't crazy big, and can be read 50 | * If so, encode jar as base64 so we can send it to Coursera 51 | */ 52 | def prepareJar(jar: File, s: TaskStreams): String = { 53 | val errPrefix = "Error submitting assignment jar: " 54 | val fileLength = jar.length() 55 | if (!jar.exists()) { 56 | s.log.error(errPrefix + "jar archive does not exist\n" + jar.getAbsolutePath) 57 | failSubmit() 58 | } else if (fileLength == 0L) { 59 | s.log.error(errPrefix + "jar archive is empty\n" + jar.getAbsolutePath) 60 | failSubmit() 61 | } else if (fileLength > maxSubmitFileSize) { 62 | s.log.error(errPrefix + "jar archive is too big. Allowed size: " + 63 | maxSubmitFileSize + " bytes, found " + fileLength + " bytes.\n" + 64 | jar.getAbsolutePath) 65 | failSubmit() 66 | } else { 67 | val bytes = new Array[Byte](fileLength.toInt) 68 | val sizeRead = try { 69 | val is = new FileInputStream(jar) 70 | val read = is.read(bytes) 71 | is.close() 72 | read 73 | } catch { 74 | case ex: IOException => 75 | s.log.error(errPrefix + "failed to read sources jar archive\n" + ex.toString) 76 | failSubmit() 77 | } 78 | if (sizeRead != bytes.length) { 79 | s.log.error(errPrefix + "failed to read the sources jar archive, size read: " + sizeRead) 80 | failSubmit() 81 | } else encodeBase64(bytes) 82 | } 83 | } 84 | 85 | /** Task to submit solution locally to a given file path */ 86 | val submitLocal = inputKey[Unit]("submit local to a given file path") 87 | lazy val submitLocalSetting = submitLocal := { 88 | val args: Seq[String] = Def.spaceDelimited("").parsed 89 | val s: TaskStreams = streams.value // for logging 90 | val jar = (packageSubmission in Compile).value 91 | 92 | val base64Jar = prepareJar(jar, s) 93 | args match { 94 | case path :: Nil => 95 | scala.tools.nsc.io.File(path).writeAll(base64Jar) 96 | case _ => 97 | val inputErr = 98 | s"""|Invalid input to `submitLocal`. The required syntax for `submitLocal` is: 99 | |submitLocal 100 | """.stripMargin 101 | s.log.error(inputErr) 102 | failSubmit() 103 | } 104 | } 105 | 106 | /** Task to submit a solution to coursera */ 107 | val submit = inputKey[Unit]("submit") 108 | lazy val submitSetting = submit := { 109 | val args: Seq[String] = Def.spaceDelimited("").parsed 110 | val s: TaskStreams = streams.value // for logging 111 | val jar = (packageSubmission in Compile).value 112 | 113 | val assignmentName = assignment.value 114 | val assignmentDetails = assignmentsMap.value(assignmentName) 115 | val assignmentKey = assignmentDetails.key 116 | val courseName = course.value 117 | val partId = assignmentDetails.partId 118 | val itemId = assignmentDetails.itemId 119 | 120 | val (email, secret) = args match { 121 | case email :: secret :: Nil => 122 | (email, secret) 123 | case _ => 124 | val inputErr = 125 | s"""|Invalid input to `submit`. The required syntax for `submit` is: 126 | |submit 127 | | 128 | |The submit token is NOT YOUR LOGIN PASSWORD. 129 | |It can be obtained from the assignment page: 130 | |https://www.coursera.org/learn/$courseName/programming/$itemId 131 | """.stripMargin 132 | s.log.error(inputErr) 133 | failSubmit() 134 | } 135 | 136 | val base64Jar = prepareJar(jar, s) 137 | val json = 138 | s"""|{ 139 | | "assignmentKey":"$assignmentKey", 140 | | "submitterEmail":"$email", 141 | | "secret":"$secret", 142 | | "parts":{ 143 | | "$partId":{ 144 | | "output":"$base64Jar" 145 | | } 146 | | } 147 | |}""".stripMargin 148 | 149 | def postSubmission[T](data: String): Try[HttpResponse[String]] = { 150 | val http = Http("https://www.coursera.org/api/onDemandProgrammingScriptSubmissions.v1") 151 | val hs = List( 152 | ("Cache-Control", "no-cache"), 153 | ("Content-Type", "application/json") 154 | ) 155 | s.log.info("Connecting to Coursera...") 156 | val response = Try(http.postData(data) 157 | .headers(hs) 158 | .option(HttpOptions.connTimeout(10000)) // scalaj default timeout is only 100ms, changing that to 10s 159 | .asString) // kick off HTTP POST 160 | response 161 | } 162 | 163 | val connectMsg = 164 | s"""|Attempting to submit "$assignmentName" assignment in "$courseName" course 165 | |Using: 166 | |- email: $email 167 | |- submit token: $secret""".stripMargin 168 | s.log.info(connectMsg) 169 | 170 | def reportCourseraResponse(response: HttpResponse[String]): Unit = { 171 | val code = response.code 172 | val respBody = response.body 173 | 174 | /* Sample JSON response from Coursera 175 | { 176 | "message": "Invalid email or token.", 177 | "details": { 178 | "learnerMessage": "Invalid email or token." 179 | } 180 | } 181 | */ 182 | 183 | code match { 184 | // case Success, Coursera responds with 2xx HTTP status code 185 | case cde if cde >= 200 && cde < 300 => 186 | val successfulSubmitMsg = 187 | s"""|Successfully connected to Coursera. (Status $code) 188 | | 189 | |Assignment submitted successfully! 190 | | 191 | |You can see how you scored by going to: 192 | |https://www.coursera.org/learn/$courseName/programming/$itemId/ 193 | |and clicking on "My Submission".""".stripMargin 194 | s.log.info(successfulSubmitMsg) 195 | 196 | // case Failure, Coursera responds with 4xx HTTP status code (client-side failure) 197 | case cde if cde >= 400 && cde < 500 => 198 | val result = JSON.parseFull(respBody) 199 | val learnerMsg = result match { 200 | case Some(resp: MapMapString) => // MapMapString to get around erasure 201 | resp.map("details")("learnerMessage") 202 | case Some(x) => // shouldn't happen 203 | "Could not parse Coursera's response:\n" + x 204 | case None => 205 | "Could not parse Coursera's response:\n" + respBody 206 | } 207 | val failedSubmitMsg = 208 | s"""|Submission failed. 209 | |There was something wrong while attempting to submit. 210 | |Coursera says: 211 | |$learnerMsg (Status $code)""".stripMargin 212 | s.log.error(failedSubmitMsg) 213 | } 214 | } 215 | 216 | // kick it all off, actually make request 217 | postSubmission(json) match { 218 | case Success(resp) => reportCourseraResponse(resp) 219 | case Failure(e) => 220 | val failedConnectMsg = 221 | s"""|Connection to Coursera failed. 222 | |There was something wrong while attempting to connect to Coursera. 223 | |Check your internet connection. 224 | |${e.toString}""".stripMargin 225 | s.log.error(failedConnectMsg) 226 | } 227 | 228 | } 229 | 230 | def failSubmit(): Nothing = { 231 | sys.error("Submission failed") 232 | } 233 | 234 | /** 235 | * ***************** 236 | * DEALING WITH JARS 237 | */ 238 | def encodeBase64(bytes: Array[Byte]): String = 239 | new String(Base64.encodeBase64(bytes)) 240 | 241 | 242 | /** ***************************************************************** 243 | * RUNNING WEIGHTED SCALATEST & STYLE CHECKER ON DEVELOPMENT SOURCES 244 | */ 245 | 246 | val styleCheck = TaskKey[Unit]("styleCheck") 247 | val styleCheckSetting = styleCheck := { 248 | val (_, sourceFiles, assignments, assignmentName) = ((compile in Compile).value, (sources in Compile).value, assignmentsMap.value, assignment.value) 249 | val styleSheet = assignments(assignmentName).styleSheet 250 | val logger = streams.value.log 251 | if (styleSheet != "") { 252 | val (feedback, score) = StyleChecker.assess(sourceFiles, styleSheet) 253 | logger.info( 254 | s"""|$feedback 255 | |Style Score: $score out of ${StyleChecker.maxResult}""".stripMargin) 256 | } else logger.warn("Can't check style: there is no style sheet provided.") 257 | } 258 | 259 | } 260 | -------------------------------------------------------------------------------- /.idea/modules/root-build.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | --------------------------------------------------------------------------------