Skip to content

Commit ac69f89

Browse files
committed
Merge pull request #17 from ryblovAV/master
Answer to exercise 1-4 ch.5
2 parents 9b1eac7 + 412d936 commit ac69f89

File tree

5 files changed

+372
-0
lines changed

5 files changed

+372
-0
lines changed

build.sbt

+4
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,8 @@ libraryDependencies += "org.scalaz" %% "scalaz-concurrent" % "7.0.6"
3939

4040
libraryDependencies += "com.typesafe.akka" %% "akka-stream-experimental" % "0.4"
4141

42+
libraryDependencies += "com.quantifind" %% "wisp" % "0.0.4"
43+
44+
libraryDependencies += "org.scalafx" %% "scalafx" % "8.0.0-R4"
45+
4246
libraryDependencies += "com.storm-enroute" %% "reactive-collections" % "0.5"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package org.learningconcurrency
2+
package exercises
3+
package ch5
4+
5+
import scala.util.Random
6+
7+
/**
8+
* Count the occurrences of the whitespace character in a randomly generated string,
9+
* where the probability of a whitespace at each position is determined by a p parameter.
10+
*
11+
* Use the parallel foreach method.
12+
*
13+
* Plot a graph that correlates the running time of this operation with the p parameter.
14+
*/
15+
object Ex2 extends App {
16+
17+
import org.learningconcurrency.ch5._
18+
19+
var r = new Random
20+
21+
val chars = ('a' to 'z') ++ ('A' to 'Z')
22+
23+
def generateSymbol(p: Double): Char =
24+
if (r.nextDouble() > p) chars(Random.nextInt(chars.length)) else ' '
25+
26+
def generateString(p: Double, length: Int = 10000): Seq[Char] = {
27+
(0 to length).map((i) => generateSymbol(p))
28+
}
29+
30+
def timedForeach(s: Seq[Char]) = {
31+
var count = 0
32+
def add = synchronized {
33+
count += 1
34+
}
35+
36+
warmedTimed(times = 400) {
37+
s.par.foreach((s) => if (s == ' ') add)
38+
}
39+
}
40+
41+
def timedCount(s: Seq[Char]) = {
42+
warmedTimed(times = 400) {
43+
s.par.count(_ == ' ')
44+
}
45+
}
46+
47+
//probability
48+
val p = (0 until 10).map { i => i / 9.0 }
49+
50+
log("---- Calculation occurrences with foreach method")
51+
val dataForeach = p.map((p) => (p, generateString(p))).map {
52+
case (p, s) => log(s"p = $p"); (p, timedForeach(s))
53+
}
54+
55+
log("---- Calculation occurrences with count method")
56+
val dataCount = p.map((p) => (p, generateString(p))).map {
57+
case (p, s) => log(s"p = $p"); (p, timedCount(s))
58+
}
59+
60+
//plot graph
61+
//uses https://github.com/quantifind/wisp
62+
63+
import com.quantifind.charts.Highcharts._
64+
65+
hold
66+
line(dataForeach)
67+
line(dataCount)
68+
title("Ch5 Ex2")
69+
legend(Seq("foreach method", "count method"))
70+
xAxis("probability")
71+
yAxis("time (ms)")
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package org.learningconcurrency
2+
package exercises
3+
package ch5
4+
5+
import scala.annotation.tailrec
6+
import scalafx.application.JFXApp
7+
import scalafx.scene.Scene
8+
import scalafx.scene.layout.Pane
9+
import scalafx.scene.paint.Color
10+
import scalafx.scene.shape.Rectangle
11+
12+
/**
13+
* Implement a program that renders the Mandelbrot set in parallel.
14+
*/
15+
16+
object Ex3 extends JFXApp {
17+
18+
val width = 400
19+
val height = 400
20+
21+
val minX = -1.75
22+
val maxX = 0.8
23+
24+
val minY = -1.2
25+
val maxY = 1.2
26+
27+
val stepX = (maxX - minX) / width
28+
val stepY = (maxY - minY) / height
29+
30+
val threshold = 300
31+
32+
case class RGB(red: Int, green: Int, blue: Int)
33+
34+
case class Pixel(x: Int, y: Int, gray: Double)
35+
36+
def buildPixel(x: Int, y: Int) = {
37+
val xc = minX + stepX * x
38+
val yc = minY + stepY * y
39+
40+
val count = calcIterations(x = 0, y = 0, xc = xc, yc = yc, i = 0)
41+
42+
Pixel(x = x, y = y, gray = 1.0 - count * 1.0 / threshold)
43+
}
44+
45+
@tailrec
46+
def calcIterations(x: Double, y: Double, xc: Double, yc: Double, i: Int): Int = {
47+
48+
if ((x * x + y * y < 2) && (i < threshold))
49+
calcIterations(
50+
x = x * x - y * y + xc,
51+
y = 2 * x * y + yc,
52+
xc = xc,
53+
yc = yc,
54+
i = i + 1
55+
)
56+
else {
57+
i
58+
}
59+
}
60+
61+
//render set
62+
val pixels = (for {
63+
x <- 0 until width
64+
y <- 0 until height
65+
} yield (x,y)).par.map {
66+
case (x,y) => buildPixel(x,y)
67+
}
68+
69+
//build javaFX rectangle
70+
val rectangles = pixels.map {
71+
case p: Pixel => new Rectangle() {
72+
x = p.x
73+
y = p.y
74+
width = 1
75+
height = 1
76+
fill = Color.gray(p.gray)
77+
}
78+
}
79+
80+
val pane = new Pane {
81+
content = rectangles.toList
82+
}
83+
84+
//build scene
85+
stage = new JFXApp.PrimaryStage {
86+
title.value = "Ch5 Ex3"
87+
scene = new Scene {
88+
fill = Color.gray(1)
89+
content = pane
90+
}
91+
}
92+
93+
stage.setHeight(width)
94+
stage.setWidth(height)
95+
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.learningconcurrency
2+
package exercises
3+
package ch5
4+
5+
/**
6+
* Measure the average running time of allocating a simple object on the JVM.
7+
*/
8+
9+
object Ex1 extends App {
10+
11+
object Timed {
12+
13+
@volatile
14+
var dummy: Any = _
15+
16+
def buildObjects(count:Int) = {
17+
var i = 0
18+
val start = System.nanoTime
19+
while (i < count) {
20+
dummy = new Object
21+
i += 1
22+
}
23+
(System.nanoTime - start)/count.toDouble
24+
}
25+
26+
}
27+
28+
var i = 0
29+
var summ = 0D
30+
31+
var timePrev = 0D
32+
while (i < 30) {
33+
34+
val time = Timed.buildObjects(10000000)
35+
val e = Math.abs(time - timePrev)/time*100
36+
37+
//check steady state
38+
if (e < 10) {
39+
i += 1
40+
summ += time
41+
} else {
42+
i = 0
43+
summ = time
44+
}
45+
46+
timePrev = time
47+
log(s"time = ${time.toString} e = ${Math.round(e)}, i = $i")
48+
49+
}
50+
51+
log("----------------------------------------------------")
52+
log(s"avg = ${summ/(i+1)} nanoseconds")
53+
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package org.learningconcurrency
2+
package exercises
3+
package ch5
4+
5+
import javafx.scene.layout.HBox
6+
7+
import scalafx.Includes._
8+
import scalafx.application.JFXApp
9+
import scalafx.application.JFXApp.PrimaryStage
10+
import scalafx.geometry.Insets
11+
import scalafx.scene.Scene
12+
import scalafx.scene.control._
13+
import scalafx.scene.layout.{Pane, VBox}
14+
import scalafx.scene.paint.Color
15+
import scalafx.scene.shape.Rectangle
16+
17+
object Ex4 extends JFXApp {
18+
19+
/**
20+
* Implement a program that simulates a cellular automaton in parallel.
21+
*/
22+
23+
val maxX = 40
24+
val maxY = 40
25+
26+
case class Cell(state: Boolean, index: Int, neighbors: Seq[Int])
27+
28+
def index(i: Int, j: Int) = i + j * maxX
29+
30+
def findNeighbors(x: Int, y: Int) = {
31+
for {
32+
i <- x - 1 to x + 1
33+
j <- y - 1 to y + 1
34+
if i > -1 && i < maxX && j > -1 && j < maxY && ((i != x) || (j != x))
35+
} yield index(i, j)
36+
}
37+
38+
def checkInitialState(x: Int, y: Int) = (x == maxX / 2 - 1 || x == maxX / 2 + 1) && y == maxY / 2
39+
40+
def initialize: IndexedSeq[Cell] = {
41+
(
42+
for {
43+
x <- 0 until maxX
44+
y <- 0 until maxY
45+
} yield (x, y)
46+
).map {
47+
case (x, y) => Cell(
48+
state = checkInitialState(x,y),
49+
index = index(x, y),
50+
neighbors = findNeighbors(x, y)
51+
)
52+
}
53+
}
54+
55+
def countNeighbors(cell: Cell, cells: Seq[Cell]) =
56+
cell.neighbors.count((i) => cells(i).state)
57+
58+
def defState(countNeighbors: Int) = {
59+
(countNeighbors == 2) || (countNeighbors == 3)
60+
}
61+
62+
def next(cells: Seq[Cell]) = {
63+
cells.par.map(
64+
(cell) => cell.copy(state = defState(countNeighbors(cell, cells)))).toIndexedSeq
65+
}
66+
67+
//Test App
68+
69+
var cells = List(initialize)
70+
71+
val cellAreaWidth = 400
72+
val cellAreaHeight = 400
73+
74+
val rectWidth = cellAreaWidth / maxX
75+
val rectHeight = cellAreaHeight / maxY
76+
77+
def buildRectangles = (
78+
for {
79+
y <- 0 until maxY
80+
x <- 0 until maxX
81+
} yield (x, y)).
82+
map {
83+
case (posX, posY) => new Rectangle() {
84+
x = posX * rectWidth
85+
y = posY * rectHeight
86+
width = rectWidth
87+
height = rectHeight
88+
fill = Color.GRAY
89+
}
90+
}.toArray
91+
92+
93+
val rectangles = buildRectangles
94+
95+
val cellsPane = new Pane {
96+
maxWidth = cellAreaWidth
97+
maxHeight = cellAreaHeight
98+
}
99+
rectangles.foreach(cellsPane.children.add(_))
100+
101+
def drawCells = cells.head.foreach((c) => rectangles(c.index).setFill(if (c.state) Color.RED else Color.WHITE))
102+
103+
val nextButton = new Button() {
104+
text = "=>"
105+
onAction = handle {
106+
cells = next(cells.head) :: cells
107+
drawCells
108+
}
109+
}
110+
111+
val prevButton = new Button() {
112+
text = "<="
113+
onAction = handle {
114+
cells = cells match {
115+
case h :: Nil => cells
116+
case h :: t => t
117+
}
118+
drawCells
119+
}
120+
}
121+
122+
val hbox = new HBox()
123+
hbox.children.add(prevButton)
124+
hbox.children.add(nextButton)
125+
126+
127+
val vbox = new VBox {
128+
padding = Insets(20)
129+
spacing = 10
130+
}
131+
132+
vbox.children.add(hbox)
133+
vbox.children.add(cellsPane)
134+
135+
stage = new PrimaryStage {
136+
title.value = "Ch5 Ex4"
137+
scene = new Scene {
138+
content = vbox
139+
}
140+
}
141+
142+
stage.sizeToScene()
143+
144+
drawCells
145+
146+
}

0 commit comments

Comments
 (0)