├── .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 |
5 |
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 |
7 |
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 |
--------------------------------------------------------------------------------