diff --git a/.gitignore b/.gitignore
index f1c181e..3f34a71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
+/.idea/misc.xml
+/.idea/modules.xml
+/.idea/Game-of-Life.iml
+/.idea/vcs.xml
diff --git a/.idea/$PRODUCT_WORKSPACE_FILE$ b/.idea/$PRODUCT_WORKSPACE_FILE$
new file mode 100644
index 0000000..79be354
--- /dev/null
+++ b/.idea/$PRODUCT_WORKSPACE_FILE$
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ 11
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..0e40fe8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/Game-of-Life.iml b/.idea/Game-of-Life.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/Game-of-Life.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/oh18767.xml b/.idea/dictionaries/oh18767.xml
new file mode 100644
index 0000000..e0be3ae
--- /dev/null
+++ b/.idea/dictionaries/oh18767.xml
@@ -0,0 +1,7 @@
+
+
+
+ chans
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..28a804d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..a0a62b6
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
index f289db6..be5c6b1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,3 +1,4 @@
+
focus ='worker|distributor'
ignore = 'strings|fmt'
diff --git a/src/aws/deploy.sh b/src/aws/deploy.sh
new file mode 100755
index 0000000..af16ec9
--- /dev/null
+++ b/src/aws/deploy.sh
@@ -0,0 +1 @@
+pscp -h hosts.txt -x "-i /home/razvi/ubuntuMicro0.pem" worker /home/ubuntu/
diff --git a/src/aws/hosts.txt b/src/aws/hosts.txt
new file mode 100644
index 0000000..f688590
--- /dev/null
+++ b/src/aws/hosts.txt
@@ -0,0 +1,16 @@
+ubuntu@13.58.113.121
+ubuntu@18.191.111.235
+ubuntu@3.17.74.121
+ubuntu@18.191.178.37
+ubuntu@3.133.146.95
+ubuntu@3.15.229.118
+ubuntu@3.14.247.40
+ubuntu@18.217.72.152
+ubuntu@3.16.83.76
+ubuntu@3.135.249.214
+ubuntu@18.191.208.62
+ubuntu@52.14.159.161
+ubuntu@18.223.237.33
+ubuntu@3.133.131.146
+ubuntu@3.14.244.159
+ubuntu@3.17.80.209
diff --git a/src/aws/populateHosts.sh b/src/aws/populateHosts.sh
new file mode 100755
index 0000000..eba391c
--- /dev/null
+++ b/src/aws/populateHosts.sh
@@ -0,0 +1,3 @@
+aws ec2 describe-instances | grep PublicIpAddress | grep -o -P "\d+\.\d+\.\d+\.\d+" | grep -v '^10\.' > hosts.txt
+
+sed -i -e 's/^/ubuntu@/' hosts.txt
diff --git a/src/aws/run.sh b/src/aws/run.sh
new file mode 100755
index 0000000..ddfb6cc
--- /dev/null
+++ b/src/aws/run.sh
@@ -0,0 +1 @@
+pssh -i -h hosts.txt -x "-i /home/razvi/ubuntuMicro0.pem" ./worker
diff --git a/src/aws/worker.go b/src/aws/worker.go
new file mode 100644
index 0000000..cf32f86
--- /dev/null
+++ b/src/aws/worker.go
@@ -0,0 +1,521 @@
+package main
+
+import (
+ "encoding/gob"
+ "flag"
+ "fmt"
+ "io"
+ "net"
+)
+
+const defaultHostname = "192.168.0.9"
+
+const INIT = 0
+
+const (
+ pause = iota
+ ping = iota
+ resume = iota
+ quit = iota
+ save = iota
+)
+
+// Client initialization packet
+type initPacket struct {
+ Clients int
+ Workers int
+ IpBefore, IpAfter string
+ Turns int
+ Width int
+}
+
+// Individual worker initialisation packet
+type workerPacket struct {
+ StartX int
+ EndX int
+ World [][]byte
+ Index int
+}
+
+// Sent localDistributor packet
+type outgoingDistPacket struct {
+ Index int
+ Type int
+ Data int
+ OutputWorld [][]byte
+}
+
+// Received localDistributor packet
+type incomingDistPacket struct {
+ Index, Data int
+}
+
+type haloPacket struct {
+ Index int
+ Data []byte
+}
+
+type workerData struct {
+ inputHalo [2]chan byte
+ outputHalo [2]chan byte
+ distributorInput chan int
+ localDistributor chan byte
+}
+
+// Makes a matrix slice
+func makeMatrix(width, height int) [][]byte {
+ M := make([][]byte, height)
+ for i := range M {
+ M[i] = make([]byte, width)
+ }
+ return M
+}
+
+// Returns the new state of a cell from the number of alive neighbours and current state
+func getNewState(numberOfAlive int, cellState bool) int {
+ if cellState == true {
+ if numberOfAlive < 2 {
+ return -1
+ }
+ if numberOfAlive <= 3 {
+ return 0
+ }
+ if numberOfAlive > 3 {
+ return -1
+ }
+ } else {
+ if numberOfAlive == 3 {
+ return 1
+ }
+ }
+ return 0
+}
+
+func try(error error) {
+ if error != nil {
+ fmt.Println("Err", error)
+ }
+}
+
+/* NETWORKING */
+
+// Send external halos to another client
+func serveToClient(conn net.Conn, index int, c chan byte, width int, turns int, exit chan byte) {
+ enc := gob.NewEncoder(conn)
+ for i := 0; i < turns; i++ {
+ var haloData = make([]byte, width)
+
+ for i := 0; i < width; i++ {
+ haloData[i] = <-c
+ }
+
+ err := enc.Encode(haloPacket{index, haloData})
+ if err != nil {
+ fmt.Println("err", err)
+ break
+ }
+ }
+ exit <- 1
+}
+
+// Wait for 2 other clients to connect to this one
+func waitForClients(clients []net.Conn, done chan net.Listener, listening chan byte) {
+ ln, err := net.Listen("tcp4", ":4001")
+ if err != nil {
+ fmt.Println("err", err)
+ return
+ }
+ listening <- 1
+
+ if ln != nil {
+ for i := 0; i < 2; i++ {
+ conn, _ := ln.Accept()
+ clients[i] = conn
+ }
+ done <- ln
+ }
+}
+
+// Receive external halos from another client
+func receiveFromClient(ip string, c [2]chan byte, turns int, exit chan byte) {
+ conn, err := net.Dial("tcp4", ip+":4001")
+ if err != nil {
+ fmt.Println("err", err)
+ exit <- 1
+ return
+ }
+
+ dec := gob.NewDecoder(conn)
+ for i := 0; i < turns; i++ {
+ var haloP haloPacket
+
+ err := dec.Decode(&haloP)
+ if err != nil {
+ fmt.Println("err", err)
+ return
+ }
+
+ // Take bytes from haloData row slice and put them in the channel
+ for _, b := range haloP.Data {
+ c[haloP.Index] <- b
+ }
+ }
+ exit <- 1
+}
+
+// Receive data from distributor server
+func receiveFromDistributor(decoder *gob.Decoder, channels []workerData, exit chan byte) {
+ for {
+ var p incomingDistPacket
+ try(decoder.Decode(&p))
+
+ // Exit signal
+ if p.Index == -1 {
+ break
+ }
+ channels[p.Index].distributorInput <- p.Data
+ }
+ exit <- 1
+}
+
+// With the help of the distributor server, sync all clients to the same state
+func syncWithOtherClients(encoder *gob.Encoder, decoder *gob.Decoder) {
+ // This client is ready to receive
+ try(encoder.Encode(1))
+
+ var p int
+ try(decoder.Decode(&p))
+ // All clients now ready to receive
+ if p != 1 {
+ fmt.Println("Received mismatched value from distributor")
+ }
+}
+
+/* ***** */
+
+// Receive halo, or receive command from localDistributor
+func receiveOrInterrupt(world [][]byte, data workerData, encoder *gob.Encoder, workerIndex, turn int, halo *bool, stopAtTurn *int, lineToReceive, haloIndex int) {
+ select {
+ case c := <-data.inputHalo[haloIndex]:
+ length := len(world[lineToReceive])
+ world[lineToReceive][0] = c
+ for j := 1; j < length; j++ {
+ world[lineToReceive][j] = <-data.inputHalo[haloIndex]
+ }
+ *halo = true
+ case <-data.distributorInput:
+ try(encoder.Encode(&outgoingDistPacket{workerIndex, 0, turn, nil}))
+ *stopAtTurn = <-data.distributorInput
+ }
+}
+
+// Send halo, or receive command from localDistributor
+func sendOrInterrupt(world [][]byte, data workerData, encoder *gob.Encoder, workerIndex, turn int, out *bool, stopAtTurn *int, lineToSend, haloIndex int) {
+ select {
+ case data.outputHalo[haloIndex] <- world[lineToSend][0]:
+ length := len(world[lineToSend])
+ for j := 1; j < length; j++ {
+ data.outputHalo[haloIndex] <- world[lineToSend][j]
+ }
+ *out = true
+ case <-data.distributorInput:
+ try(encoder.Encode(&outgoingDistPacket{workerIndex, 0, turn, nil}))
+ *stopAtTurn = <-data.distributorInput
+ }
+}
+
+func worker(p initPacket, data workerData, wp workerPacket, encoder *gob.Encoder) {
+ endX := wp.EndX
+ startX := wp.StartX
+ endY := p.Width
+ startY := 0
+
+ world := makeMatrix(endY-startY, endX-startX+2)
+ newWorld := makeMatrix(endY-startY, endX-startX+2)
+
+ // Receive initial world from localDistributor
+ for i := range world {
+ for j := 0; j < endY; j++ {
+ newWorld[i][j] = wp.World[i][j]
+ world[i][j] = newWorld[i][j]
+ }
+ }
+
+ halo0, halo1 := true, true
+ stopAtTurn := -2
+
+ for turn := 0; turn < p.Turns; {
+
+ // This is the turn in which all workers synchronise and stop
+ if turn == stopAtTurn+1 {
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 0, pause, nil}))
+ // Process IO
+ for {
+ r := <-data.distributorInput
+ if r == resume {
+ break
+ } else if r == save {
+ // Send the world to the localDistributor
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 1, 0, newWorld[1 : endX-startX+1]}))
+ } else if r == quit {
+ data.localDistributor <- 1
+ return
+ } else if r == ping {
+ // Send the number of alive cells to the localDistributor
+ alive := 0
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ if newWorld[i][j] == 0xFF {
+ alive++
+ }
+ }
+ }
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 0, alive, nil}))
+ break
+ }
+ }
+ }
+
+ // Get halos or command
+ if turn != 0 {
+ // Either receive the top halo, or a command from localDistributor
+ if !halo0 {
+ receiveOrInterrupt(world, data, encoder, wp.Index, turn, &halo0, &stopAtTurn, 0, 0)
+ }
+ // Either receive the bottom halo, or a command from localDistributor
+ if !halo1 {
+ receiveOrInterrupt(world, data, encoder, wp.Index, turn, &halo1, &stopAtTurn, endX-startX+1, 1)
+ }
+ }
+
+ // Move on to next turn
+ if halo0 && halo1 {
+
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ // Compute alive neighbours
+ aliveNeighbours := 0
+ yp1 := (j + 1) % endY
+ ym1 := j - 1
+ if ym1 < 0 {
+ ym1 += endY
+ }
+
+ aliveNeighbours = int(world[i+1][j]) + int(world[i-1][j]) +
+ int(world[i][yp1]) + int(world[i][ym1]) +
+ int(world[i+1][yp1]) + int(world[i+1][ym1]) +
+ int(world[i-1][yp1]) + int(world[i-1][ym1])
+
+ switch getNewState(aliveNeighbours/255, world[i][j] == 0xFF) {
+ case -1:
+ newWorld[i][j] = 0x00
+ case 1:
+ newWorld[i][j] = 0xFF
+ case 0:
+ newWorld[i][j] = world[i][j]
+ }
+ }
+ }
+ halo0, halo1 = false, false
+ turn++
+
+ // Try sending the halos, or a command from localDistributor
+ out0, out1 := false, false
+ for !(out0 && out1) {
+ if !out0 {
+ sendOrInterrupt(newWorld, data, encoder, wp.Index, turn, &out0, &stopAtTurn, 1, 0)
+ }
+ if !out1 {
+ sendOrInterrupt(newWorld, data, encoder, wp.Index, turn, &out1, &stopAtTurn, endX-startX, 1)
+ }
+ }
+
+ // Update old world
+ for i := range world {
+ copy(world[i], newWorld[i])
+ }
+ }
+
+ }
+
+ // Send the final world
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 1, 0, newWorld[1 : endX-startX+1]}))
+ // Done
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, -1, -1, nil}))
+ data.localDistributor <- 0
+}
+
+// Initialise all worker channels
+func initialiseChannels(data []workerData, workers, clients, imageWidth, i int) {
+ data[i].inputHalo[0] = make(chan byte, imageWidth)
+ data[i].inputHalo[1] = make(chan byte, imageWidth)
+ data[i].localDistributor = make(chan byte)
+ data[i].distributorInput = make(chan int, 1)
+
+ if workers == 1 && clients > 1 { // Just one worker but more than one client => channels are external
+ data[0].outputHalo[0] = make(chan byte, imageWidth)
+ data[0].outputHalo[1] = make(chan byte, imageWidth)
+ } else if workers == 1 { // Just one client, with just one worker => channels are internal
+ data[0].outputHalo[0] = data[0].inputHalo[1]
+ data[0].outputHalo[1] = data[0].inputHalo[0]
+ } else if i == 0 { // >1 client & >1 worker AND this is the first channel => it is connected externally
+ data[0].outputHalo[0] = make(chan byte, imageWidth)
+ data[i+1].outputHalo[0] = data[i].inputHalo[1]
+ } else if i == workers-1 { // >1 client & >1 worker AND this is the last channel => it is connected externally
+ data[workers-1].outputHalo[1] = make(chan byte, imageWidth)
+ data[i-1].outputHalo[1] = data[i].inputHalo[0]
+ } else { // Normal internal channels
+ data[i-1].outputHalo[1] = data[i].inputHalo[0]
+ data[i+1].outputHalo[0] = data[i].inputHalo[1]
+ }
+}
+
+func localDistributor(encoder *gob.Encoder, decoder *gob.Decoder, exitThread []chan byte) int {
+ var haloClients = make([]net.Conn, 2)
+ var done = make(chan net.Listener)
+ var listening = make(chan byte)
+ var initP initPacket
+ var ln net.Listener = nil
+ var r byte
+
+ // Get initialisation packet
+ try(decoder.Decode(&initP))
+
+ // Wait for external halo connections
+ if initP.Clients != 1 {
+ go waitForClients(haloClients, done, listening)
+ }
+
+ // Receive and initialise worker channels
+ workerData := make([]workerData, initP.Workers)
+ workerPackets := make([]workerPacket, initP.Workers)
+ for i := 0; i < initP.Workers; i++ {
+ var w workerPacket
+ try(decoder.Decode(&w))
+
+ workerPackets[i] = w
+ initialiseChannels(workerData, initP.Workers, initP.Clients, initP.Width, i)
+ }
+
+ // Wait until the program binds to port, then sync to this point with all clients.
+ // At this point all of them are listening and ready for connections
+ if initP.Clients > 1 {
+ <-listening
+ }
+ syncWithOtherClients(encoder, decoder)
+
+ // Connect to external halo sockets
+ if initP.Clients > 1 {
+ go receiveFromClient(initP.IpBefore, [2]chan byte{workerData[0].inputHalo[0], workerData[initP.Workers-1].inputHalo[1]}, initP.Turns, exitThread[1])
+ go receiveFromClient(initP.IpAfter, [2]chan byte{workerData[0].inputHalo[0], workerData[initP.Workers-1].inputHalo[1]}, initP.Turns, exitThread[2])
+
+ ln = <-done
+ }
+
+ // Distributor server receiver thread
+ go receiveFromDistributor(decoder, workerData, exitThread[0])
+
+ // External halo serving functions
+ if initP.Clients != 1 {
+ ip0, _, _ := net.SplitHostPort(haloClients[0].RemoteAddr().String())
+ ip1, _, _ := net.SplitHostPort(haloClients[1].RemoteAddr().String())
+ if ip0 == initP.IpBefore && ip1 == initP.IpAfter {
+ go serveToClient(haloClients[0], 1, workerData[0].outputHalo[0], initP.Width, initP.Turns, exitThread[3])
+ go serveToClient(haloClients[1], 0, workerData[initP.Workers-1].outputHalo[1], initP.Width, initP.Turns, exitThread[4])
+ } else if ip0 == initP.IpAfter && ip1 == initP.IpBefore {
+ go serveToClient(haloClients[1], 1, workerData[0].outputHalo[0], initP.Width, initP.Turns, exitThread[3])
+ go serveToClient(haloClients[0], 0, workerData[initP.Workers-1].outputHalo[1], initP.Width, initP.Turns, exitThread[4])
+ } else {
+ fmt.Println("IPs are mismatched")
+ }
+ } else {
+ // Only local workers, no external halos
+ workerData[initP.Workers-1].outputHalo[1] = workerData[0].inputHalo[0]
+ workerData[0].outputHalo[0] = workerData[initP.Workers-1].inputHalo[1]
+ }
+
+ // Start all workers
+ for i := 0; i < initP.Workers; i++ {
+ go worker(initP, workerData[i], workerPackets[i], encoder)
+ }
+ fmt.Println("All workers active")
+
+ // Wait for workers to finish work
+ for i := 0; i < initP.Workers; i++ {
+ r = <-workerData[i].localDistributor
+ }
+ fmt.Println("All workers finished")
+
+ // Close connections, if open
+ if ln != nil {
+ try(ln.Close())
+ }
+
+ // Tell main what happened
+ if r == 1 {
+ return -1 // Quit command
+ }
+ if initP.Clients == 1 {
+ return 1 // Just local client, no halo sockets
+ }
+ return 0 // Normal termination
+}
+
+// Game of Life client
+func main() {
+ // process hostname flag
+ var hostname string
+ flag.StringVar(&hostname, "hostname", defaultHostname, "The hostname of the server.")
+ flag.Parse()
+
+ // Connect to server
+ fmt.Println("Connecting to", hostname)
+
+ conn, err := net.Dial("tcp4", hostname+":4000")
+ if err != nil {
+ fmt.Println("Server is offline")
+ return
+ }
+ fmt.Println("Connected to server")
+
+ // Create channels used on networking thread exit
+ exitThread := make([]chan byte, 5)
+ for i := 0; i < 5; i++ {
+ exitThread[i] = make(chan byte, 1)
+ }
+
+ // Main server encoder and decoder
+ enc := gob.NewEncoder(conn)
+ dec := gob.NewDecoder(conn)
+
+ for {
+ var packetType = 0
+ err = dec.Decode(&packetType)
+ if err != nil { // If server is no longer responding, the work is done.
+ if err == io.EOF {
+ fmt.Println("Connection closed, exiting worker.")
+ return
+ }
+ fmt.Println("err", err)
+ return
+ }
+
+ // Run game of life
+ if packetType == INIT {
+ fmt.Println("Starting workers..")
+ result := localDistributor(enc, dec, exitThread)
+ waitForX := 5
+
+ if result == -1 || result == 1 {
+ waitForX = 1
+ }
+ for i := 0; i < waitForX; i++ {
+ <-exitThread[i]
+ }
+
+ if result == -1 { // If quit command, quit worker program
+ return
+ }
+ }
+ }
+}
diff --git a/src/gol.go b/src/gol.go
index 909ee93..b962380 100644
--- a/src/gol.go
+++ b/src/gol.go
@@ -1,20 +1,15 @@
package main
import (
+ "encoding/gob"
"fmt"
+ "io"
"strconv"
"strings"
"time"
)
-type workerChannel struct {
- inputByte,
- outputByte chan byte
- inputHalo [2]chan byte
- outputHalo [2]chan byte
- distributorInput chan int
- distributorOutput chan int
-}
+const INIT = 0
const (
pause = iota
@@ -24,254 +19,137 @@ const (
save = iota
)
-// Modulus that only returns positive number
-func positiveModulo(x, m int) int {
- if x > 0 {
- return x % m
- } else {
- for x < 0 {
- x += m
- }
- return x % m
- }
+type workerData struct {
+ outputWorld chan [][]byte
+ distributorOutput chan int
+ encoder *gob.Encoder
+ index int
}
-// Return the number of alive neighbours
-func getAliveNeighbours(world [][]byte, x, y, imageWidth int) int {
- aliveNeighbours := 0
+// Client information
+type clientData struct {
+ encoder *gob.Encoder
+ decoder *gob.Decoder
+ ip string
+}
- dx := [8]int{-1, -1, 0, 1, 1, 1, 0, -1}
- dy := [8]int{0, 1, 1, 1, 0, -1, -1, -1}
+// User interaction data packet
+type controllerPacket struct {
+ Index, Data int
+}
- for i := 0; i < 8; i++ {
- newX := x + dx[i]
- newY := positiveModulo(y+dy[i], imageWidth)
- if world[newX][newY] == 0xFF {
- aliveNeighbours++
- }
- }
- return aliveNeighbours
+// Client initialization packet
+type initPacket struct {
+ Clients int
+ Workers int
+ IpBefore, IpAfter string
+ Turns int
+ Width int
}
-// Returns the new state of a cell from the number of alive neighbours and current state
-func getNewState(numberOfAlive int, cellState bool) int {
- if cellState == true {
- if numberOfAlive < 2 {
- return -1
- }
- if numberOfAlive <= 3 {
- return 0
- }
- if numberOfAlive > 3 {
- return -1
- }
- } else {
- if numberOfAlive == 3 {
- return 1
- }
- }
- return 0
+// Individual worker initialisation packet
+type workerPacket struct {
+ StartX int
+ EndX int
+ World [][]byte
+ Index int
}
-// Worker function
-func worker(p golParams, channels workerChannel, startX, endX, startY, endY int) {
+// General client inbout packet
+type distributorPacket struct {
+ Index int
+ Type int
+ Data int
+ OutputWorld [][]byte
+}
- world := make([][]byte, endX-startX+2)
- for i := range world {
- world[i] = make([]byte, endY-startY)
+func try(error error) {
+ if error != nil {
+ fmt.Println("Err", error)
}
+}
- newWorld := make([][]byte, endX-startX+2)
- for i := range world {
- newWorld[i] = make([]byte, endY-startY)
+func positiveModulo(x, m int) int {
+ for x < 0 {
+ x += m
}
+ return x % m
+}
- for i := range world {
- for j := 0; j < p.imageWidth; j++ {
- newWorld[i][j] = <-channels.inputByte
- }
+// Makes a matrix slice
+func makeMatrix(width, height int) [][]byte {
+ M := make([][]byte, height)
+ for i := range M {
+ M[i] = make([]byte, width)
}
+ return M
+}
+// Sends world to output
+func outputWorld(p golParams, state int, d distributorChans, world [][]byte) {
+ d.io.command <- ioOutput
+ d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x") + "_state_" + strconv.Itoa(state)
for i := range world {
for j := range world[i] {
- world[i][j] = newWorld[i][j]
+ d.io.world <- world[i][j]
}
}
+}
- halo0 := true
- halo1 := true
- stopAtTurn := -2
-
- for turn := 0; turn < p.turns; {
-
- if turn == stopAtTurn+1 {
- channels.distributorOutput <- pause
- for {
- r := <-channels.distributorInput
- if r == resume {
- break
- } else if r == save {
- for i := 1; i < endX-startX+1; i++ {
- for j := startY; j < endY; j++ {
- channels.outputByte <- newWorld[i][j]
- }
- }
- } else if r == quit {
- return
- } else if r == ping {
- alive := 0
- for i := 1; i < endX-startX+1; i++ {
- for j := startY; j < endY; j++ {
- if newWorld[i][j] == 0xFF {
- alive++
- }
- }
- }
- channels.distributorOutput <- alive
- break
- } else {
- fmt.Println("Something went wrong, r = ", r)
- }
- }
- }
-
- // Process something
- if turn != 0 {
- if !halo0 {
- select {
- case c := <-channels.inputHalo[0]:
- world[0][0] = c
- for j := 1; j < p.imageWidth; j++ {
- world[0][j] = <-channels.inputHalo[0]
- }
- halo0 = true
- case <-channels.distributorInput:
- channels.distributorOutput <- turn
- stopAtTurn = <-channels.distributorInput
- }
- }
- if !halo1 {
- select {
- case c := <-channels.inputHalo[1]:
- world[endX-startX+1][0] = c
- for j := 1; j < p.imageWidth; j++ {
- world[endX-startX+1][j] = <-channels.inputHalo[1]
- }
- halo1 = true
- case <-channels.distributorInput:
- channels.distributorOutput <- turn
- stopAtTurn = <-channels.distributorInput
- }
- }
- }
-
- // Move on to next turn
- if halo0 && halo1 {
-
- for i := 1; i < endX-startX+1; i++ {
- for j := startY; j < endY; j++ {
- switch getNewState(getAliveNeighbours(world, i, j, p.imageWidth), world[i][j] == 0xFF) {
- case -1:
- newWorld[i][j] = 0x00
- case 1:
- newWorld[i][j] = 0xFF
- case 0:
- newWorld[i][j] = world[i][j]
- }
- }
- }
- halo0 = false
- halo1 = false
- turn++
-
- out0 := false
- out1 := false
- for !(out0 && out1) {
- if !out0 {
- select {
- case channels.outputHalo[0] <- newWorld[1][0]:
- for j := 1; j < p.imageWidth; j++ {
- channels.outputHalo[0] <- newWorld[1][j]
- }
- out0 = true
- case <-channels.distributorInput:
- channels.distributorOutput <- turn
- stopAtTurn = <-channels.distributorInput
- }
- }
- if !out1 {
- select {
- case channels.outputHalo[1] <- newWorld[endX-startX][0]:
- for j := 1; j < p.imageWidth; j++ {
- channels.outputHalo[1] <- newWorld[endX-startX][j]
- }
- out1 = true
- case <-channels.distributorInput:
- channels.distributorOutput <- turn
- stopAtTurn = <-channels.distributorInput
- }
- }
- }
-
- for i := range world {
- for j := range world[i] {
- world[i][j] = newWorld[i][j]
- }
- }
+// Pauses and synchronises all workers
+func pauseWorkers(workerData []workerData, stopAtTurn *int) {
+ // Pause and get current turns
+ for _, worker := range workerData {
+ try(worker.encoder.Encode(controllerPacket{worker.index, pause}))
+ }
+ for _, worker := range workerData {
+ t := <-worker.distributorOutput
+ if t > *stopAtTurn {
+ *stopAtTurn = t
}
-
}
- for i := 1; i < endX-startX+1; i++ {
- for j := startY; j < endY; j++ {
- channels.outputByte <- newWorld[i][j]
+ // Tell all workers to stop after turn stopAtTurn
+ for _, worker := range workerData {
+ try(worker.encoder.Encode(controllerPacket{worker.index, *stopAtTurn}))
+ }
+ for _, channel := range workerData {
+ r := <-channel.distributorOutput
+ if r != pause {
+ fmt.Println("Something has gone wrong, r =", r)
}
}
-
- // Done
- channels.distributorOutput <- -1
-
}
-// Sends world to output
-func outputWorld(p golParams, state int, d distributorChans, world [][]byte) {
- d.io.command <- ioOutput
- d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x") + "_state_" + strconv.Itoa(state)
- for i := range world {
- for j := range world[i] {
- d.io.world <- world[i][j]
+// Receive the world from all workers
+func receiveWorld(world [][]byte, workerData []workerData, threadsSmall, threadsSmallHeight, threadsLargeHeight int) {
+ startX := 0
+ endX := threadsSmallHeight
+ for i, worker := range workerData {
+ tw := <-worker.outputWorld
+ for i := range tw {
+ copy(world[startX+i], tw[i])
}
- }
-}
-// initialise worker channels
-func initialiseChannels(workerChannels []workerChannel, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight int, p golParams) {
- for i := 0; i < threadsSmall; i++ {
- workerChannels[i].inputByte = make(chan byte, threadsSmallHeight+2)
- workerChannels[i].outputByte = make(chan byte, threadsSmallHeight*p.imageWidth)
- workerChannels[i].inputHalo[0] = make(chan byte, p.imageWidth)
- workerChannels[i].inputHalo[1] = make(chan byte, p.imageWidth)
- workerChannels[i].distributorInput = make(chan int, 1)
- workerChannels[i].distributorOutput = make(chan int, 1)
-
- workerChannels[positiveModulo(i-1, p.threads)].outputHalo[1] = workerChannels[i].inputHalo[0]
- workerChannels[positiveModulo(i+1, p.threads)].outputHalo[0] = workerChannels[i].inputHalo[1]
+ // New startX, endX
+ startX = endX
+ if i < threadsSmall-1 {
+ endX += threadsSmallHeight
+ } else {
+ endX += threadsLargeHeight
+ }
}
+}
- for i := 0; i < threadsLarge; i++ {
- workerChannels[i+threadsSmall].inputByte = make(chan byte, threadsLargeHeight+2)
- workerChannels[i+threadsSmall].outputByte = make(chan byte, threadsLargeHeight*p.imageWidth)
- workerChannels[i+threadsSmall].inputHalo[0] = make(chan byte, p.imageWidth)
- workerChannels[i+threadsSmall].inputHalo[1] = make(chan byte, p.imageWidth)
- workerChannels[i+threadsSmall].distributorInput = make(chan int, 1)
- workerChannels[i+threadsSmall].distributorOutput = make(chan int, 1)
-
- workerChannels[positiveModulo(i+threadsSmall-1, p.threads)].outputHalo[1] = workerChannels[i+threadsSmall].inputHalo[0]
- workerChannels[positiveModulo(i+threadsSmall+1, p.threads)].outputHalo[0] = workerChannels[i+threadsSmall].inputHalo[1]
+// Sends data over all worker channels
+func sendToWorkers(workerData []workerData, data int) {
+ for _, worker := range workerData {
+ try(worker.encoder.Encode(controllerPacket{worker.index, data}))
}
}
-func workerController(p golParams, world [][]byte, workerChannels []workerChannel, d distributorChans, keyChan <-chan rune, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight int) {
+// Controlls user interaction
+func workerController(p golParams, world [][]byte, workerData []workerData, d distributorChans, keyChan <-chan rune, threadsSmall, threadsSmallHeight, threadsLargeHeight int) {
stopAtTurn := 0
paused := false
timer := time.NewTimer(2 * time.Second)
@@ -280,32 +158,12 @@ func workerController(p golParams, world [][]byte, workerChannels []workerChanne
case <-timer.C:
// Get alive cells
alive := 0
-
if !paused {
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- pause
- }
- for i := 0; i < p.threads; i++ {
- t := <-workerChannels[i].distributorOutput
- if t > stopAtTurn {
- stopAtTurn = t
- }
- }
- // Tell all workers to stop after turn stopAtTurn
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- stopAtTurn
- }
- for i := 0; i < p.threads; i++ {
- r := <-workerChannels[i].distributorOutput
- if r != pause {
- fmt.Println("Something has gone wrong, r =", r)
- }
- }
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- ping
- }
- for i := 0; i < p.threads; i++ {
- alive += <-workerChannels[i].distributorOutput
+ pauseWorkers(workerData, &stopAtTurn)
+ // Ping unpauses workers
+ sendToWorkers(workerData, ping)
+ for _, channel := range workerData {
+ alive += <-channel.distributorOutput
}
fmt.Println("There are", alive, "alive cells in the world.")
}
@@ -315,36 +173,14 @@ func workerController(p golParams, world [][]byte, workerChannels []workerChanne
if k == 'p' || k == 's' || k == 'q' {
// If not already paused
if !paused {
- // Get turn from all workers
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- pause
- }
- // Compute turn to be stopped after
- for i := 0; i < p.threads; i++ {
- t := <-workerChannels[i].distributorOutput
- if t > stopAtTurn {
- stopAtTurn = t
- }
- }
- // Tell all workers to stop after turn stopAtTurn
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- stopAtTurn
- }
- for i := 0; i < p.threads; i++ {
- r := <-workerChannels[i].distributorOutput
- if r != pause {
- fmt.Println("Something has gone wrong, r =", r)
- }
- }
+ pauseWorkers(workerData, &stopAtTurn)
// Paused until resume
if k == 'p' {
fmt.Println("Pausing. The turn number", stopAtTurn+1, "is currently being processed.")
}
} else if k == 'p' { // If this was a pause command and we are already paused, resume
// Resume all workers
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- resume
- }
+ sendToWorkers(workerData, resume)
fmt.Println("Continuing.")
}
// If this was a save or quit command
@@ -354,40 +190,21 @@ func workerController(p golParams, world [][]byte, workerChannels []workerChanne
} else {
fmt.Println("Saving and quitting on turn", stopAtTurn)
}
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- save
- }
- // If not saving while already paused
- if !paused {
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- resume
- }
- }
- // Get the world and save it
- for t := 0; t < threadsSmall; t++ {
- startX := threadsSmallHeight * t
- for x := 0; x < threadsSmallHeight; x++ {
- for y := 0; y < p.imageWidth; y++ {
- world[x+startX][y] = <-workerChannels[t].outputByte
- }
- }
- }
- for t := 0; t < threadsLarge; t++ {
- startX := threadsSmallHeight*threadsSmall + threadsLargeHeight*t
- for x := 0; x < threadsLargeHeight; x++ {
- for y := 0; y < p.imageWidth; y++ {
- world[x+startX][y] = <-workerChannels[t+threadsSmall].outputByte
- }
- }
+ sendToWorkers(workerData, save)
+
+ // If paused just to save, unpause. If quit, don't unpause
+ if !paused && k == 's' {
+ sendToWorkers(workerData, resume)
}
+
+ // Receive and output world
+ receiveWorld(world, workerData, threadsSmall, threadsSmallHeight, threadsLargeHeight)
outputWorld(p, stopAtTurn, d, world)
// Quit workers
if k == 'q' {
- for i := 0; i < p.threads; i++ {
- workerChannels[i].distributorInput <- quit
- q = true
- }
+ q = true
+ sendToWorkers(workerData, quit)
}
}
// If this was a pause command, actually pause
@@ -395,43 +212,64 @@ func workerController(p golParams, world [][]byte, workerChannels []workerChanne
paused = !paused
}
}
- case o := <-workerChannels[0].distributorOutput:
+ case o := <-workerData[0].distributorOutput: // Workers are starting to finish
if o != -1 {
fmt.Println("Something has gone wrong, o =", o)
}
for i := 1; i < p.threads; i++ {
- <-workerChannels[i].distributorOutput
- }
- // Get the world and save it
- for t := 0; t < threadsSmall; t++ {
- startX := threadsSmallHeight * t
- for x := 0; x < threadsSmallHeight; x++ {
- for y := 0; y < p.imageWidth; y++ {
- world[x+startX][y] = <-workerChannels[t].outputByte
- }
- }
- }
- for t := 0; t < threadsLarge; t++ {
- startX := threadsSmallHeight*threadsSmall + threadsLargeHeight*t
- for x := 0; x < threadsLargeHeight; x++ {
- for y := 0; y < p.imageWidth; y++ {
- world[x+startX][y] = <-workerChannels[t+threadsSmall].outputByte
- }
- }
+ <-workerData[i].distributorOutput
}
+ // Receive the world and quit
+ receiveWorld(world, workerData, threadsSmall, threadsSmallHeight, threadsLargeHeight)
q = true
}
}
}
+// Listens to a client's connection and transfers received packets to intended channels
+func listenToWorker(decoder *gob.Decoder, channel []workerData, workersServed int) {
+ // This for terminates when all workers sent their final data
+ for i := 0; i < workersServed; {
+ var p distributorPacket
+ err := decoder.Decode(&p)
+ if err == io.EOF {
+ fmt.Println("Client disconnected.")
+ } else {
+ try(err)
+ }
+
+ if p.Type == 1 { // a worker sent the world
+ channel[p.Index].outputWorld <- p.OutputWorld
+ } else if p.Type == 0 { // a worker sent some data
+ channel[p.Index].distributorOutput <- p.Data
+ } else if p.Type == -1 { // a worker sent final data, so no more data will be received from it
+ channel[p.Index].distributorOutput <- p.Data
+ i++ // we are finished with this worker
+ }
+ }
+}
+
+// Initialises workers on the clients
+func startWorkers(client clientData, initP initPacket, workerP []workerPacket, workerData []workerData) {
+ // Let the client know we are about to send an init packet
+ try(client.encoder.Encode(INIT))
+
+ // Send the init packet
+ try(client.encoder.Encode(initP))
+
+ // Send worker packets
+ for i, p := range workerP {
+ workerData[i].encoder = client.encoder
+ workerData[i].index = i
+ try(client.encoder.Encode(p))
+ }
+}
+
// distributor divides the work between workers and interacts with other goroutines.
-func distributor(p golParams, d distributorChans, alive chan []cell, keyChan <-chan rune) {
+func distributor(p golParams, d distributorChans, alive chan []cell, keyChan <-chan rune, clients []clientData, clientNumber int) {
// Create the 2D slice to store the world.
- world := make([][]byte, p.imageHeight)
- for i := range world {
- world[i] = make([]byte, p.imageWidth)
- }
+ world := makeMatrix(p.imageWidth, p.imageHeight)
// Request the io goroutine to read in the image with the given filename.
d.io.command <- ioInput
@@ -442,7 +280,7 @@ func distributor(p golParams, d distributorChans, alive chan []cell, keyChan <-c
for x := 0; x < p.imageWidth; x++ {
val := <-d.io.inputVal
if val != 0 {
- fmt.Println("Alive cell at", x, y)
+ //fmt.Println("Alive cell at", x, y)
world[y][x] = val
}
}
@@ -456,53 +294,89 @@ func distributor(p golParams, d distributorChans, alive chan []cell, keyChan <-c
// Thread calculations
// 16x16 with 10 threads: 6 large threads with 2 height + 4 small threads with 1 height
- threadsLarge := p.imageHeight % p.threads
+ //threadsLarge := p.imageHeight % p.threads
threadsSmall := p.threads - p.imageHeight%p.threads
-
threadsLargeHeight := p.imageHeight/p.threads + 1
threadsSmallHeight := p.imageHeight / p.threads
- // Worker channels
- workerChannels := make([]workerChannel, p.threads)
- initialiseChannels(workerChannels, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight, p)
-
- // start workers
- for i := 0; i < threadsSmall; i++ {
- startX := threadsSmallHeight * i
- endX := threadsSmallHeight * (i + 1)
- go worker(p, workerChannels[i], startX, endX, 0, p.imageWidth)
+ // Worker channel initialisation
+ workerData := make([]workerData, p.threads)
+ for i := 0; i < p.threads; i++ {
+ workerData[i].outputWorld = make(chan [][]byte, 1)
+ workerData[i].distributorOutput = make(chan int, 1)
}
- for i := 0; i < threadsLarge; i++ {
- startX := threadsSmallHeight*threadsSmall + threadsLargeHeight*i
- endX := threadsSmallHeight*threadsSmall + threadsLargeHeight*(i+1)
- go worker(p, workerChannels[i+threadsSmall], startX, endX, 0, p.imageWidth)
+ // Threads per client
+ //clientLarge := p.threads % clientNumber
+ clientSmall := clientNumber - p.threads%clientNumber
+
+ clientLargeWorkers := p.threads/clientNumber + 1
+ clientSmallWorkers := p.threads / clientNumber
+
+ workerBounds := make([]workerPacket, p.threads)
+
+ // Copy of world, but with extra 2 lines (one at the start, one at the end)
+ borderedWorld := make([][]byte, p.imageHeight+2)
+ for i := range world {
+ borderedWorld[i+1] = world[i]
}
+ borderedWorld[0] = world[p.imageHeight-1]
+ borderedWorld[p.imageHeight+1] = world[0]
- // send data to workers
- for t := 0; t < threadsSmall; t++ {
- startX := threadsSmallHeight * t
- endX := threadsSmallHeight * (t + 1)
+ // Create worker initialisation packets
+ startX := 0
+ endX := threadsSmallHeight
+ for i := 0; i < p.threads; i++ {
+ workerBounds[i] = workerPacket{startX, endX,
+ borderedWorld[startX : endX+2],
+ i}
+
+ startX = endX
+ if i < threadsSmall-1 {
+ endX += threadsSmallHeight
+ } else {
+ endX += threadsLargeHeight
+ }
+ }
- for i := startX - 1; i < endX+1; i++ {
- for j := 0; j < p.imageWidth; j++ {
- workerChannels[t].inputByte <- world[positiveModulo(i, p.imageHeight)][positiveModulo(j, p.imageWidth)]
- }
+ // Start workers on remote clients
+ t := 0
+ for i := 0; i < clientNumber; i++ {
+ host0 := clients[positiveModulo(i-1, clientNumber)].ip
+ host1 := clients[positiveModulo(i+1, clientNumber)].ip
+ workers := clientSmallWorkers
+ if i >= clientSmall {
+ workers = clientLargeWorkers
}
+ fmt.Println(workers, "workers started on client", i)
+ startWorkers(clients[i],
+ initPacket{clientNumber, workers, host0, host1, p.turns, p.imageWidth},
+ workerBounds[t:t+workers], workerData[t:t+workers])
+ t += workers
}
- for t := 0; t < threadsLarge; t++ {
- startX := threadsSmallHeight*threadsSmall + threadsLargeHeight*t
- endX := threadsSmallHeight*threadsSmall + threadsLargeHeight*(t+1)
- for i := startX - 1; i < endX+1; i++ {
- for j := 0; j < p.imageWidth; j++ {
- workerChannels[t+threadsSmall].inputByte <- world[positiveModulo(i, p.imageHeight)][positiveModulo(j, p.imageWidth)]
- }
+ // Client synchronisation
+ for i := 0; i < clientNumber; i++ {
+ var p int
+ try(clients[i].decoder.Decode(&p))
+
+ if p != 1 {
+ fmt.Println("Ready packet mismatch")
+ }
+ }
+ for i := 0; i < clientNumber; i++ {
+ p := 1
+ try(clients[i].encoder.Encode(&p))
+
+ if i < clientSmall {
+ go listenToWorker(clients[i].decoder, workerData, clientSmallWorkers)
+ } else {
+ go listenToWorker(clients[i].decoder, workerData, clientLargeWorkers)
}
}
- // main worker controller function
- workerController(p, world, workerChannels, d, keyChan, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight)
+ // Process IO and control workers
+ workerController(p, world, workerData, d, keyChan, threadsSmall, threadsSmallHeight, threadsLargeHeight)
// Create an empty slice to store coordinates of cells that are still alive after p.turns are done.
var finalAlive []cell
@@ -515,10 +389,17 @@ func distributor(p golParams, d distributorChans, alive chan []cell, keyChan <-c
}
}
+ // Tell workers to exit listening functions
+ for i := 0; i < clientNumber; i++ {
+ try(clients[i].encoder.Encode(controllerPacket{-1, 0}))
+ }
+
+ //outputWorld(p, p.turns, d, world)
+
// Make sure that the Io has finished any output before exiting.
d.io.command <- ioCheckIdle
<-d.io.idle
-
// Return the coordinates of cells that are still alive.
alive <- finalAlive
+
}
diff --git a/src/main.go b/src/main.go
index 3cefe96..31ff2f9 100644
--- a/src/main.go
+++ b/src/main.go
@@ -1,6 +1,13 @@
package main
-import "flag"
+import (
+ "encoding/gob"
+ "flag"
+ "fmt"
+ "net"
+)
+
+const clientNumber = 32
// golParams provides the details of how to run the Game of Life and which image to load.
type golParams struct {
@@ -65,7 +72,7 @@ type ioChans struct {
// It makes some channels and starts relevant goroutines.
// It places the created channels in the relevant structs.
// It returns an array of alive cells returned by the distributor.
-func gameOfLife(p golParams, keyChan <-chan rune) []cell {
+func gameOfLife(p golParams, keyChan <-chan rune, clientNumber int, clients []clientData) []cell {
var dChans distributorChans
var ioChans ioChans
@@ -91,13 +98,44 @@ func gameOfLife(p golParams, keyChan <-chan rune) []cell {
aliveCells := make(chan []cell)
- go distributor(p, dChans, aliveCells, keyChan)
+ if p.threads < clientNumber {
+ p.threads = clientNumber
+ }
+
+ go distributor(p, dChans, aliveCells, keyChan, clients, clientNumber)
go pgmIo(p, ioChans)
alive := <-aliveCells
return alive
}
+// Listens to port 4000, accepts connections and returns client data.
+func processClients(clientNumber int) []clientData {
+
+ clients := make([]clientData, clientNumber)
+
+ ln, err := net.Listen("tcp4", ":4000")
+ if err != nil {
+ fmt.Println("Could not listen to port 4000", err)
+ }
+
+ if ln != nil {
+ for i := 0; i < clientNumber; i++ {
+ conn, err := ln.Accept()
+ if err != nil {
+ fmt.Println(err)
+ }
+ fmt.Println("Client number", i+1, "/", clientNumber, "connected <=>", conn.RemoteAddr())
+
+ clients[i].encoder = gob.NewEncoder(conn)
+ clients[i].decoder = gob.NewDecoder(conn)
+ clients[i].ip, _, _ = net.SplitHostPort(conn.RemoteAddr().String())
+ }
+ }
+
+ return clients
+}
+
// main is the function called when starting Game of Life with 'make gol'
// Do not edit until Stage 2.
func main() {
@@ -106,7 +144,7 @@ func main() {
flag.IntVar(
¶ms.threads,
"t",
- 8,
+ 128,
"Specify the number of worker threads to use. Defaults to 8.")
flag.IntVar(
@@ -123,11 +161,16 @@ func main() {
flag.Parse()
- params.turns = 50000
+ params.turns = 100000
+
+ fmt.Println("Waiting for", clientNumber, "clients to connect.")
+ clients := processClients(clientNumber)
startControlServer(params)
keyChan := make(chan rune)
go getKeyboardCommand(keyChan)
- gameOfLife(params, keyChan)
+
+ gameOfLife(params, keyChan, clientNumber, clients)
+
StopControlServer()
}
diff --git a/src/main_test.go b/src/main_test.go
index 7b0ac0e..37ec29e 100644
--- a/src/main_test.go
+++ b/src/main_test.go
@@ -1,11 +1,15 @@
package main
import (
+ "fmt"
"github.com/stretchr/testify/assert"
"os"
"testing"
+ "time"
)
+var clients []clientData
+
func Test(t *testing.T) {
type args struct {
p golParams
@@ -256,6 +260,46 @@ func Test(t *testing.T) {
},
}},
+ {"16x16x7-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 13,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"512x512x128-1000", args{
+ p: golParams{
+ turns: 1000,
+ threads: 128,
+ imageWidth: 512,
+ imageHeight: 512,
+ },
+ expectedAlive: []cell{
+ {88, 0}, {90, 0}, {137, 0}, {142, 0}, {143, 0}, {157, 0}, {158, 0}, {166, 0}, {89, 1}, {90, 1}, {137, 1}, {161, 1}, {138, 2}, {142, 2}, {160, 2}, {161, 2}, {164, 2}, {165, 2}, {166, 2}, {268, 2}, {269, 2}, {127, 3}, {139, 3}, {160, 3}, {161, 3}, {164, 3}, {268, 3}, {269, 3}, {444, 3}, {445, 3}, {495, 3}, {496, 3}, {126, 4}, {128, 4}, {140, 4}, {142, 4}, {159, 4}, {162, 4}, {165, 4}, {166, 4}, {167, 4}, {168, 4}, {444, 4}, {445, 4}, {495, 4}, {496, 4}, {126, 5}, {128, 5}, {129, 5}, {139, 5}, {140, 5}, {144, 5}, {160, 5}, {161, 5}, {165, 5}, {167, 5}, {168, 5}, {169, 5}, {128, 6}, {140, 6}, {144, 6}, {145, 6}, {150, 6}, {151, 6}, {159, 6}, {161, 6}, {164, 6}, {165, 6}, {168, 6}, {170, 6}, {268, 6}, {269, 6}, {145, 7}, {149, 7}, {152, 7}, {159, 7}, {160, 7}, {163, 7}, {166, 7}, {168, 7}, {169, 7}, {268, 7}, {269, 7}, {141, 8}, {144, 8}, {150, 8}, {151, 8}, {158, 8}, {161, 8}, {162, 8}, {166, 8}, {167, 8}, {168, 8}, {169, 8}, {122, 9}, {142, 9}, {143, 9}, {144, 9}, {158, 9}, {159, 9}, {162, 9}, {79, 10}, {80, 10}, {121, 10}, {123, 10}, {158, 10}, {159, 10}, {163, 10}, {164, 10}, {79, 11}, {80, 11}, {123, 11}, {440, 11}, {441, 11}, {122, 12}, {123, 12}, {125, 12}, {440, 12}, {441, 12}, {122, 13}, {127, 13}, {123, 14}, {127, 14}, {160, 14}, {161, 14}, {163, 14}, {264, 14}, {265, 14}, {123, 15}, {126, 15}, {161, 15}, {162, 15}, {163, 15}, {264, 15}, {265, 15}, {505, 15}, {506, 15}, {162, 16}, {504, 16}, {507, 16}, {504, 17}, {506, 17}, {83, 18}, {84, 18}, {490, 18}, {491, 18}, {492, 18}, {505, 18}, {83, 19}, {84, 19}, {133, 21}, {134, 21}, {450, 21}, {451, 21}, {132, 22}, {134, 22}, {191, 22}, {192, 22}, {449, 22}, {451, 22}, {132, 23}, {133, 23}, {191, 23}, {193, 23}, {449, 23}, {450, 23}, {192, 24}, {193, 24}, {274, 24}, {275, 24}, {273, 25}, {275, 25}, {273, 26}, {274, 26}, {476, 27}, {477, 27}, {439, 28}, {440, 28}, {447, 28}, {448, 28}, {472, 28}, {473, 28}, {476, 28}, {477, 28}, {438, 29}, {440, 29}, {447, 29}, {448, 29}, {453, 29}, {454, 29}, {472, 29}, {473, 29}, {439, 30}, {453, 30}, {454, 30}, {112, 31}, {458, 31}, {459, 31}, {489, 31}, {490, 31}, {508, 31}, {509, 31}, {510, 31}, {111, 32}, {113, 32}, {450, 32}, {451, 32}, {458, 32}, {459, 32}, {489, 32}, {490, 32}, {111, 33}, {113, 33}, {182, 33}, {183, 33}, {450, 33}, {451, 33}, {112, 34}, {182, 34}, {183, 34}, {79, 35}, {388, 35}, {389, 35}, {78, 36}, {80, 36}, {116, 36}, {117, 36}, {388, 36}, {390, 36}, {447, 36}, {448, 36}, {78, 37}, {81, 37}, {115, 37}, {118, 37}, {389, 37}, {391, 37}, {425, 37}, {426, 37}, {447, 37}, {448, 37}, {79, 38}, {80, 38}, {116, 38}, {117, 38}, {156, 38}, {390, 38}, {425, 38}, {426, 38}, {156, 39}, {382, 39}, {383, 39}, {112, 40}, {156, 40}, {229, 40}, {230, 40}, {382, 40}, {383, 40}, {98, 41}, {111, 41}, {113, 41}, {229, 41}, {230, 41}, {98, 42}, {111, 42}, {113, 42}, {152, 42}, {153, 42}, {154, 42}, {158, 42}, {159, 42}, {160, 42}, {173, 42}, {174, 42}, {396, 42}, {98, 43}, {112, 43}, {173, 43}, {174, 43}, {317, 43}, {318, 43}, {395, 43}, {397, 43}, {458, 43}, {504, 43}, {505, 43}, {150, 44}, {156, 44}, {317, 44}, {318, 44}, {395, 44}, {396, 44}, {457, 44}, {459, 44}, {504, 44}, {505, 44}, {149, 45}, {151, 45}, {156, 45}, {456, 45}, {459, 45}, {149, 46}, {150, 46}, {156, 46}, {230, 46}, {231, 46}, {457, 46}, {458, 46}, {212, 47}, {213, 47}, {230, 47}, {231, 47}, {419, 47}, {420, 47}, {421, 47}, {497, 47}, {498, 47}, {107, 48}, {108, 48}, {146, 48}, {174, 48}, {175, 48}, {176, 48}, {212, 48}, {213, 48}, {497, 48}, {498, 48}, {107, 49}, {108, 49}, {146, 49}, {50, 50}, {51, 50}, {146, 50}, {172, 50}, {178, 50}, {426, 50}, {50, 51}, {51, 51}, {149, 51}, {150, 51}, {172, 51}, {178, 51}, {214, 51}, {386, 51}, {387, 51}, {425, 51}, {427, 51}, {79, 52}, {80, 52}, {148, 52}, {151, 52}, {172, 52}, {178, 52}, {214, 52}, {228, 52}, {229, 52}, {385, 52}, {388, 52}, {426, 52}, {437, 52}, {438, 52}, {79, 53}, {81, 53}, {94, 53}, {95, 53}, {149, 53}, {150, 53}, {214, 53}, {228, 53}, {229, 53}, {386, 53}, {387, 53}, {437, 53}, {438, 53}, {458, 53}, {80, 54}, {81, 54}, {94, 54}, {96, 54}, {122, 54}, {123, 54}, {175, 54}, {176, 54}, {452, 54}, {453, 54}, {458, 54}, {95, 55}, {96, 55}, {122, 55}, {123, 55}, {174, 55}, {177, 55}, {451, 55}, {454, 55}, {458, 55}, {48, 56}, {49, 56}, {68, 56}, {69, 56}, {175, 56}, {176, 56}, {248, 56}, {249, 56}, {250, 56}, {381, 56}, {382, 56}, {451, 56}, {454, 56}, {48, 57}, {49, 57}, {68, 57}, {69, 57}, {112, 57}, {113, 57}, {380, 57}, {383, 57}, {452, 57}, {453, 57}, {111, 58}, {114, 58}, {381, 58}, {382, 58}, {111, 59}, {113, 59}, {152, 59}, {153, 59}, {237, 59}, {238, 59}, {112, 60}, {117, 60}, {118, 60}, {124, 60}, {125, 60}, {152, 60}, {153, 60}, {237, 60}, {238, 60}, {496, 60}, {117, 61}, {118, 61}, {124, 61}, {125, 61}, {173, 61}, {174, 61}, {212, 61}, {213, 61}, {408, 61}, {409, 61}, {448, 61}, {449, 61}, {450, 61}, {471, 61}, {472, 61}, {484, 61}, {495, 61}, {497, 61}, {61, 62}, {62, 62}, {63, 62}, {172, 62}, {175, 62}, {212, 62}, {213, 62}, {276, 62}, {408, 62}, {410, 62}, {442, 62}, {443, 62}, {471, 62}, {472, 62}, {483, 62}, {485, 62}, {495, 62}, {497, 62}, {53, 63}, {54, 63}, {55, 63}, {97, 63}, {98, 63}, {121, 63}, {122, 63}, {141, 63}, {173, 63}, {174, 63}, {276, 63}, {409, 63}, {442, 63}, {444, 63}, {483, 63}, {485, 63}, {496, 63}, {97, 64}, {98, 64}, {120, 64}, {123, 64}, {129, 64}, {130, 64}, {141, 64}, {191, 64}, {276, 64}, {342, 64}, {435, 64}, {436, 64}, {443, 64}, {467, 64}, {468, 64}, {484, 64}, {51, 65}, {121, 65}, {123, 65}, {128, 65}, {131, 65}, {141, 65}, {190, 65}, {192, 65}, {233, 65}, {234, 65}, {258, 65}, {259, 65}, {313, 65}, {314, 65}, {342, 65}, {435, 65}, {436, 65}, {466, 65}, {469, 65}, {51, 66}, {93, 66}, {94, 66}, {95, 66}, {122, 66}, {129, 66}, {130, 66}, {155, 66}, {156, 66}, {190, 66}, {192, 66}, {232, 66}, {235, 66}, {257, 66}, {260, 66}, {278, 66}, {279, 66}, {280, 66}, {291, 66}, {292, 66}, {298, 66}, {299, 66}, {300, 66}, {313, 66}, {314, 66}, {342, 66}, {467, 66}, {468, 66}, {51, 67}, {154, 67}, {157, 67}, {191, 67}, {233, 67}, {234, 67}, {258, 67}, {259, 67}, {291, 67}, {293, 67}, {125, 68}, {155, 68}, {156, 68}, {292, 68}, {332, 68}, {333, 68}, {338, 68}, {339, 68}, {340, 68}, {344, 68}, {345, 68}, {346, 68}, {435, 68}, {436, 68}, {124, 69}, {126, 69}, {144, 69}, {145, 69}, {331, 69}, {334, 69}, {434, 69}, {437, 69}, {447, 69}, {448, 69}, {90, 70}, {124, 70}, {126, 70}, {144, 70}, {145, 70}, {310, 70}, {311, 70}, {332, 70}, {333, 70}, {342, 70}, {361, 70}, {362, 70}, {376, 70}, {377, 70}, {435, 70}, {437, 70}, {446, 70}, {448, 70}, {89, 71}, {91, 71}, {125, 71}, {204, 71}, {205, 71}, {300, 71}, {310, 71}, {311, 71}, {342, 71}, {361, 71}, {362, 71}, {376, 71}, {377, 71}, {399, 71}, {400, 71}, {401, 71}, {436, 71}, {445, 71}, {447, 71}, {74, 72}, {75, 72}, {76, 72}, {80, 72}, {81, 72}, {82, 72}, {88, 72}, {91, 72}, {121, 72}, {204, 72}, {205, 72}, {299, 72}, {301, 72}, {342, 72}, {446, 72}, {89, 73}, {90, 73}, {120, 73}, {122, 73}, {173, 73}, {174, 73}, {187, 73}, {188, 73}, {300, 73}, {310, 73}, {311, 73}, {365, 73}, {366, 73}, {485, 73}, {486, 73}, {72, 74}, {78, 74}, {120, 74}, {122, 74}, {172, 74}, {175, 74}, {187, 74}, {188, 74}, {245, 74}, {246, 74}, {247, 74}, {309, 74}, {312, 74}, {324, 74}, {365, 74}, {366, 74}, {395, 74}, {467, 74}, {484, 74}, {487, 74}, {72, 75}, {78, 75}, {121, 75}, {154, 75}, {173, 75}, {174, 75}, {310, 75}, {311, 75}, {323, 75}, {325, 75}, {394, 75}, {396, 75}, {412, 75}, {413, 75}, {455, 75}, {466, 75}, {468, 75}, {485, 75}, {486, 75}, {490, 75}, {491, 75}, {72, 76}, {78, 76}, {154, 76}, {243, 76}, {323, 76}, {325, 76}, {394, 76}, {396, 76}, {411, 76}, {413, 76}, {454, 76}, {456, 76}, {466, 76}, {468, 76}, {490, 76}, {491, 76}, {99, 77}, {100, 77}, {154, 77}, {175, 77}, {176, 77}, {178, 77}, {243, 77}, {324, 77}, {395, 77}, {411, 77}, {412, 77}, {455, 77}, {467, 77}, {474, 77}, {475, 77}, {74, 78}, {75, 78}, {76, 78}, {98, 78}, {101, 78}, {178, 78}, {243, 78}, {296, 78}, {297, 78}, {460, 78}, {461, 78}, {473, 78}, {476, 78}, {99, 79}, {101, 79}, {175, 79}, {177, 79}, {296, 79}, {297, 79}, {460, 79}, {461, 79}, {474, 79}, {475, 79}, {100, 80}, {106, 80}, {107, 80}, {176, 80}, {184, 80}, {185, 80}, {189, 80}, {291, 80}, {106, 81}, {107, 81}, {120, 81}, {121, 81}, {170, 81}, {171, 81}, {179, 81}, {180, 81}, {184, 81}, {185, 81}, {188, 81}, {189, 81}, {190, 81}, {290, 81}, {292, 81}, {412, 81}, {93, 82}, {94, 82}, {120, 82}, {121, 82}, {169, 82}, {170, 82}, {171, 82}, {172, 82}, {178, 82}, {180, 82}, {183, 82}, {184, 82}, {186, 82}, {187, 82}, {189, 82}, {190, 82}, {242, 82}, {243, 82}, {274, 82}, {275, 82}, {276, 82}, {290, 82}, {293, 82}, {412, 82}, {93, 83}, {94, 83}, {169, 83}, {172, 83}, {173, 83}, {183, 83}, {184, 83}, {242, 83}, {243, 83}, {291, 83}, {292, 83}, {302, 83}, {303, 83}, {338, 83}, {339, 83}, {340, 83}, {412, 83}, {45, 84}, {46, 84}, {114, 84}, {115, 84}, {170, 84}, {172, 84}, {173, 84}, {184, 84}, {186, 84}, {188, 84}, {272, 84}, {278, 84}, {302, 84}, {303, 84}, {44, 85}, {45, 85}, {113, 85}, {115, 85}, {166, 85}, {172, 85}, {173, 85}, {182, 85}, {183, 85}, {184, 85}, {187, 85}, {272, 85}, {278, 85}, {356, 85}, {46, 86}, {114, 86}, {166, 86}, {172, 86}, {181, 86}, {184, 86}, {234, 86}, {272, 86}, {278, 86}, {356, 86}, {154, 87}, {155, 87}, {156, 87}, {159, 87}, {160, 87}, {166, 87}, {170, 87}, {173, 87}, {179, 87}, {183, 87}, {233, 87}, {235, 87}, {316, 87}, {317, 87}, {356, 87}, {446, 87}, {447, 87}, {136, 88}, {137, 88}, {159, 88}, {161, 88}, {170, 88}, {172, 88}, {173, 88}, {176, 88}, {177, 88}, {178, 88}, {179, 88}, {180, 88}, {181, 88}, {182, 88}, {233, 88}, {235, 88}, {274, 88}, {275, 88}, {276, 88}, {316, 88}, {317, 88}, {329, 88}, {330, 88}, {363, 88}, {364, 88}, {445, 88}, {448, 88}, {469, 88}, {470, 88}, {136, 89}, {137, 89}, {152, 89}, {153, 89}, {160, 89}, {172, 89}, {173, 89}, {176, 89}, {177, 89}, {181, 89}, {234, 89}, {267, 89}, {268, 89}, {286, 89}, {287, 89}, {329, 89}, {330, 89}, {352, 89}, {353, 89}, {354, 89}, {358, 89}, {359, 89}, {360, 89}, {363, 89}, {364, 89}, {445, 89}, {447, 89}, {469, 89}, {470, 89}, {90, 90}, {91, 90}, {152, 90}, {153, 90}, {176, 90}, {177, 90}, {178, 90}, {267, 90}, {268, 90}, {285, 90}, {288, 90}, {446, 90}, {90, 91}, {91, 91}, {132, 91}, {133, 91}, {157, 91}, {238, 91}, {239, 91}, {286, 91}, {287, 91}, {348, 91}, {356, 91}, {132, 92}, {133, 92}, {147, 92}, {148, 92}, {154, 92}, {157, 92}, {158, 92}, {159, 92}, {160, 92}, {168, 92}, {169, 92}, {237, 92}, {240, 92}, {348, 92}, {356, 92}, {115, 93}, {147, 93}, {148, 93}, {154, 93}, {157, 93}, {158, 93}, {159, 93}, {160, 93}, {167, 93}, {168, 93}, {170, 93}, {173, 93}, {174, 93}, {175, 93}, {176, 93}, {177, 93}, {184, 93}, {186, 93}, {188, 93}, {238, 93}, {239, 93}, {348, 93}, {356, 93}, {114, 94}, {116, 94}, {154, 94}, {155, 94}, {159, 94}, {168, 94}, {170, 94}, {173, 94}, {174, 94}, {176, 94}, {177, 94}, {178, 94}, {182, 94}, {183, 94}, {184, 94}, {188, 94}, {390, 94}, {114, 95}, {117, 95}, {154, 95}, {157, 95}, {158, 95}, {169, 95}, {174, 95}, {183, 95}, {184, 95}, {185, 95}, {188, 95}, {244, 95}, {309, 95}, {310, 95}, {344, 95}, {345, 95}, {346, 95}, {389, 95}, {391, 95}, {409, 95}, {410, 95}, {411, 95}, {447, 95}, {448, 95}, {75, 96}, {76, 96}, {115, 96}, {116, 96}, {155, 96}, {156, 96}, {173, 96}, {184, 96}, {186, 96}, {244, 96}, {308, 96}, {311, 96}, {389, 96}, {390, 96}, {447, 96}, {448, 96}, {74, 97}, {77, 97}, {175, 97}, {178, 97}, {183, 97}, {208, 97}, {209, 97}, {210, 97}, {244, 97}, {309, 97}, {311, 97}, {75, 98}, {76, 98}, {132, 98}, {141, 98}, {142, 98}, {171, 98}, {173, 98}, {175, 98}, {281, 98}, {282, 98}, {283, 98}, {310, 98}, {131, 99}, {132, 99}, {133, 99}, {140, 99}, {142, 99}, {170, 99}, {174, 99}, {182, 99}, {183, 99}, {184, 99}, {199, 99}, {246, 99}, {247, 99}, {248, 99}, {444, 99}, {445, 99}, {130, 100}, {131, 100}, {133, 100}, {134, 100}, {141, 100}, {169, 100}, {170, 100}, {172, 100}, {173, 100}, {183, 100}, {184, 100}, {199, 100}, {382, 100}, {383, 100}, {443, 100}, {446, 100}, {135, 101}, {171, 101}, {199, 101}, {332, 101}, {382, 101}, {383, 101}, {443, 101}, {446, 101}, {135, 102}, {136, 102}, {308, 102}, {309, 102}, {332, 102}, {444, 102}, {445, 102}, {109, 103}, {135, 103}, {136, 103}, {308, 103}, {309, 103}, {332, 103}, {396, 103}, {397, 103}, {108, 104}, {110, 104}, {131, 104}, {162, 104}, {163, 104}, {208, 104}, {209, 104}, {395, 104}, {398, 104}, {423, 104}, {430, 104}, {108, 105}, {110, 105}, {132, 105}, {157, 105}, {158, 105}, {162, 105}, {164, 105}, {207, 105}, {210, 105}, {328, 105}, {329, 105}, {330, 105}, {334, 105}, {335, 105}, {336, 105}, {372, 105}, {373, 105}, {395, 105}, {398, 105}, {406, 105}, {407, 105}, {423, 105}, {430, 105}, {95, 106}, {109, 106}, {120, 106}, {157, 106}, {158, 106}, {163, 106}, {208, 106}, {209, 106}, {365, 106}, {366, 106}, {372, 106}, {373, 106}, {396, 106}, {397, 106}, {406, 106}, {407, 106}, {423, 106}, {430, 106}, {94, 107}, {96, 107}, {120, 107}, {182, 107}, {332, 107}, {364, 107}, {367, 107}, {94, 108}, {96, 108}, {120, 108}, {135, 108}, {136, 108}, {181, 108}, {183, 108}, {217, 108}, {332, 108}, {364, 108}, {366, 108}, {419, 108}, {420, 108}, {421, 108}, {432, 108}, {433, 108}, {434, 108}, {95, 109}, {135, 109}, {136, 109}, {141, 109}, {181, 109}, {183, 109}, {216, 109}, {218, 109}, {332, 109}, {365, 109}, {130, 110}, {131, 110}, {140, 110}, {141, 110}, {146, 110}, {147, 110}, {182, 110}, {199, 110}, {216, 110}, {218, 110}, {312, 110}, {313, 110}, {423, 110}, {430, 110}, {129, 111}, {132, 111}, {140, 111}, {146, 111}, {147, 111}, {198, 111}, {200, 111}, {217, 111}, {312, 111}, {313, 111}, {423, 111}, {430, 111}, {35, 112}, {50, 112}, {51, 112}, {52, 112}, {129, 112}, {131, 112}, {199, 112}, {200, 112}, {423, 112}, {430, 112}, {34, 113}, {36, 113}, {82, 113}, {83, 113}, {130, 113}, {265, 113}, {35, 114}, {37, 114}, {64, 114}, {65, 114}, {82, 114}, {83, 114}, {265, 114}, {379, 114}, {380, 114}, {36, 115}, {37, 115}, {45, 115}, {64, 115}, {65, 115}, {164, 115}, {165, 115}, {265, 115}, {379, 115}, {380, 115}, {44, 116}, {46, 116}, {164, 116}, {165, 116}, {44, 117}, {46, 117}, {47, 117}, {45, 118}, {46, 118}, {47, 118}, {105, 118}, {106, 118}, {134, 118}, {135, 118}, {136, 118}, {141, 118}, {143, 118}, {156, 118}, {263, 118}, {264, 118}, {46, 119}, {47, 119}, {105, 119}, {106, 119}, {133, 119}, {142, 119}, {155, 119}, {157, 119}, {262, 119}, {265, 119}, {363, 119}, {364, 119}, {365, 119}, {382, 119}, {383, 119}, {384, 119}, {133, 120}, {136, 120}, {137, 120}, {139, 120}, {156, 120}, {162, 120}, {163, 120}, {263, 120}, {264, 120}, {302, 120}, {303, 120}, {47, 121}, {48, 121}, {133, 121}, {134, 121}, {137, 121}, {138, 121}, {139, 121}, {140, 121}, {162, 121}, {163, 121}, {302, 121}, {304, 121}, {361, 121}, {367, 121}, {46, 122}, {49, 122}, {112, 122}, {113, 122}, {141, 122}, {149, 122}, {200, 122}, {201, 122}, {202, 122}, {303, 122}, {304, 122}, {361, 122}, {367, 122}, {36, 123}, {38, 123}, {41, 123}, {42, 123}, {45, 123}, {49, 123}, {112, 123}, {113, 123}, {139, 123}, {141, 123}, {142, 123}, {148, 123}, {150, 123}, {180, 123}, {361, 123}, {367, 123}, {41, 124}, {43, 124}, {46, 124}, {137, 124}, {138, 124}, {147, 124}, {150, 124}, {180, 124}, {419, 124}, {420, 124}, {422, 124}, {423, 124}, {41, 125}, {43, 125}, {104, 125}, {105, 125}, {139, 125}, {148, 125}, {149, 125}, {180, 125}, {363, 125}, {364, 125}, {365, 125}, {419, 125}, {420, 125}, {422, 125}, {423, 125}, {38, 126}, {103, 126}, {105, 126}, {136, 126}, {139, 126}, {142, 126}, {143, 126}, {186, 126}, {316, 126}, {317, 126}, {34, 127}, {42, 127}, {43, 127}, {44, 127}, {103, 127}, {104, 127}, {139, 127}, {143, 127}, {185, 127}, {187, 127}, {317, 127}, {318, 127}, {34, 128}, {39, 128}, {43, 128}, {82, 128}, {83, 128}, {136, 128}, {138, 128}, {141, 128}, {143, 128}, {185, 128}, {187, 128}, {199, 128}, {200, 128}, {316, 128}, {35, 129}, {39, 129}, {81, 129}, {84, 129}, {136, 129}, {139, 129}, {141, 129}, {142, 129}, {153, 129}, {154, 129}, {186, 129}, {198, 129}, {201, 129}, {371, 129}, {372, 129}, {420, 129}, {421, 129}, {35, 130}, {49, 130}, {82, 130}, {83, 130}, {111, 130}, {112, 130}, {137, 130}, {140, 130}, {153, 130}, {154, 130}, {199, 130}, {201, 130}, {371, 130}, {372, 130}, {421, 130}, {45, 131}, {49, 131}, {110, 131}, {112, 131}, {140, 131}, {170, 131}, {171, 131}, {181, 131}, {200, 131}, {418, 131}, {44, 132}, {45, 132}, {46, 132}, {47, 132}, {48, 132}, {49, 132}, {111, 132}, {138, 132}, {139, 132}, {169, 132}, {172, 132}, {180, 132}, {182, 132}, {418, 132}, {419, 132}, {43, 133}, {44, 133}, {46, 133}, {47, 133}, {155, 133}, {156, 133}, {170, 133}, {171, 133}, {180, 133}, {183, 133}, {424, 133}, {425, 133}, {42, 134}, {43, 134}, {155, 134}, {156, 134}, {181, 134}, {182, 134}, {393, 134}, {394, 134}, {423, 134}, {426, 134}, {43, 135}, {44, 135}, {48, 135}, {147, 135}, {392, 135}, {395, 135}, {424, 135}, {425, 135}, {44, 136}, {45, 136}, {48, 136}, {128, 136}, {129, 136}, {147, 136}, {231, 136}, {232, 136}, {393, 136}, {394, 136}, {46, 137}, {128, 137}, {129, 137}, {147, 137}, {188, 137}, {189, 137}, {230, 137}, {233, 137}, {43, 138}, {103, 138}, {187, 138}, {190, 138}, {230, 138}, {232, 138}, {42, 139}, {43, 139}, {44, 139}, {101, 139}, {102, 139}, {103, 139}, {188, 139}, {189, 139}, {228, 139}, {229, 139}, {231, 139}, {235, 139}, {41, 140}, {42, 140}, {44, 140}, {45, 140}, {100, 140}, {101, 140}, {102, 140}, {227, 140}, {230, 140}, {234, 140}, {236, 140}, {42, 141}, {43, 141}, {44, 141}, {100, 141}, {101, 141}, {102, 141}, {227, 141}, {229, 141}, {233, 141}, {236, 141}, {43, 142}, {100, 142}, {101, 142}, {106, 142}, {107, 142}, {228, 142}, {232, 142}, {234, 142}, {235, 142}, {421, 142}, {422, 142}, {83, 143}, {84, 143}, {231, 143}, {233, 143}, {421, 143}, {422, 143}, {83, 144}, {84, 144}, {99, 144}, {105, 144}, {106, 144}, {125, 144}, {126, 144}, {218, 144}, {219, 144}, {230, 144}, {233, 144}, {97, 145}, {98, 145}, {104, 145}, {105, 145}, {106, 145}, {124, 145}, {127, 145}, {218, 145}, {219, 145}, {231, 145}, {232, 145}, {102, 146}, {107, 146}, {108, 146}, {125, 146}, {126, 146}, {150, 146}, {151, 146}, {152, 146}, {170, 146}, {260, 146}, {261, 146}, {101, 147}, {107, 147}, {108, 147}, {170, 147}, {260, 147}, {261, 147}, {364, 147}, {365, 147}, {381, 147}, {382, 147}, {100, 148}, {101, 148}, {105, 148}, {106, 148}, {170, 148}, {241, 148}, {242, 148}, {243, 148}, {363, 148}, {366, 148}, {381, 148}, {383, 148}, {100, 149}, {102, 149}, {105, 149}, {215, 149}, {216, 149}, {304, 149}, {305, 149}, {364, 149}, {365, 149}, {382, 149}, {383, 149}, {102, 150}, {104, 150}, {105, 150}, {214, 150}, {217, 150}, {304, 150}, {305, 150}, {103, 151}, {107, 151}, {215, 151}, {216, 151}, {251, 151}, {252, 151}, {105, 152}, {108, 152}, {109, 152}, {242, 152}, {243, 152}, {244, 152}, {251, 152}, {252, 152}, {367, 152}, {368, 152}, {388, 152}, {107, 153}, {108, 153}, {110, 153}, {115, 153}, {274, 153}, {275, 153}, {276, 153}, {312, 153}, {313, 153}, {341, 153}, {342, 153}, {367, 153}, {368, 153}, {387, 153}, {389, 153}, {74, 154}, {75, 154}, {76, 154}, {110, 154}, {115, 154}, {212, 154}, {213, 154}, {256, 154}, {312, 154}, {313, 154}, {341, 154}, {342, 154}, {386, 154}, {389, 154}, {504, 154}, {57, 155}, {58, 155}, {73, 155}, {77, 155}, {107, 155}, {109, 155}, {110, 155}, {115, 155}, {211, 155}, {213, 155}, {256, 155}, {387, 155}, {388, 155}, {504, 155}, {56, 156}, {58, 156}, {73, 156}, {77, 156}, {107, 156}, {108, 156}, {185, 156}, {186, 156}, {211, 156}, {212, 156}, {244, 156}, {245, 156}, {256, 156}, {504, 156}, {56, 157}, {57, 157}, {72, 157}, {77, 157}, {78, 157}, {185, 157}, {186, 157}, {244, 157}, {245, 157}, {71, 158}, {72, 158}, {75, 158}, {202, 158}, {203, 158}, {293, 158}, {294, 158}, {500, 158}, {501, 158}, {502, 158}, {506, 158}, {507, 158}, {508, 158}, {70, 159}, {71, 159}, {72, 159}, {76, 159}, {106, 159}, {107, 159}, {202, 159}, {203, 159}, {293, 159}, {295, 159}, {362, 159}, {368, 159}, {68, 160}, {74, 160}, {106, 160}, {107, 160}, {281, 160}, {294, 160}, {295, 160}, {340, 160}, {341, 160}, {362, 160}, {368, 160}, {67, 161}, {73, 161}, {74, 161}, {77, 161}, {99, 161}, {100, 161}, {280, 161}, {282, 161}, {325, 161}, {326, 161}, {339, 161}, {342, 161}, {362, 161}, {368, 161}, {374, 161}, {375, 161}, {73, 162}, {74, 162}, {76, 162}, {77, 162}, {98, 162}, {101, 162}, {280, 162}, {282, 162}, {325, 162}, {326, 162}, {340, 162}, {341, 162}, {373, 162}, {376, 162}, {379, 162}, {380, 162}, {67, 163}, {68, 163}, {70, 163}, {74, 163}, {99, 163}, {100, 163}, {129, 163}, {130, 163}, {148, 163}, {163, 163}, {281, 163}, {374, 163}, {375, 163}, {378, 163}, {381, 163}, {399, 163}, {400, 163}, {401, 163}, {81, 164}, {82, 164}, {129, 164}, {130, 164}, {148, 164}, {162, 164}, {164, 164}, {357, 164}, {379, 164}, {380, 164}, {81, 165}, {83, 165}, {89, 165}, {90, 165}, {148, 165}, {161, 165}, {164, 165}, {194, 165}, {262, 165}, {263, 165}, {351, 165}, {356, 165}, {358, 165}, {404, 165}, {405, 165}, {83, 166}, {89, 166}, {90, 166}, {162, 166}, {163, 166}, {193, 166}, {195, 166}, {262, 166}, {263, 166}, {350, 166}, {352, 166}, {356, 166}, {358, 166}, {403, 166}, {406, 166}, {81, 167}, {83, 167}, {193, 167}, {195, 167}, {341, 167}, {349, 167}, {352, 167}, {357, 167}, {403, 167}, {406, 167}, {81, 168}, {82, 168}, {126, 168}, {127, 168}, {194, 168}, {340, 168}, {342, 168}, {350, 168}, {351, 168}, {404, 168}, {405, 168}, {67, 169}, {68, 169}, {70, 169}, {74, 169}, {125, 169}, {127, 169}, {263, 169}, {340, 169}, {341, 169}, {399, 169}, {400, 169}, {401, 169}, {73, 170}, {74, 170}, {76, 170}, {77, 170}, {126, 170}, {262, 170}, {264, 170}, {335, 170}, {336, 170}, {67, 171}, {73, 171}, {74, 171}, {77, 171}, {117, 171}, {118, 171}, {263, 171}, {335, 171}, {336, 171}, {68, 172}, {74, 172}, {106, 172}, {107, 172}, {117, 172}, {118, 172}, {163, 172}, {164, 172}, {402, 172}, {70, 173}, {71, 173}, {72, 173}, {76, 173}, {81, 173}, {82, 173}, {106, 173}, {107, 173}, {163, 173}, {164, 173}, {366, 173}, {367, 173}, {368, 173}, {379, 173}, {401, 173}, {403, 173}, {71, 174}, {72, 174}, {75, 174}, {81, 174}, {82, 174}, {255, 174}, {378, 174}, {380, 174}, {400, 174}, {403, 174}, {56, 175}, {72, 175}, {77, 175}, {78, 175}, {168, 175}, {169, 175}, {254, 175}, {256, 175}, {378, 175}, {379, 175}, {401, 175}, {402, 175}, {55, 176}, {57, 176}, {59, 176}, {60, 176}, {61, 176}, {73, 176}, {77, 176}, {123, 176}, {124, 176}, {168, 176}, {169, 176}, {255, 176}, {257, 176}, {48, 177}, {49, 177}, {50, 177}, {54, 177}, {55, 177}, {57, 177}, {58, 177}, {59, 177}, {60, 177}, {73, 177}, {77, 177}, {97, 177}, {123, 177}, {124, 177}, {184, 177}, {185, 177}, {240, 177}, {241, 177}, {256, 177}, {257, 177}, {48, 178}, {49, 178}, {50, 178}, {51, 178}, {52, 178}, {55, 178}, {74, 178}, {75, 178}, {76, 178}, {80, 178}, {81, 178}, {96, 178}, {98, 178}, {137, 178}, {160, 178}, {161, 178}, {183, 178}, {186, 178}, {239, 178}, {242, 178}, {52, 179}, {56, 179}, {60, 179}, {61, 179}, {65, 179}, {66, 179}, {80, 179}, {82, 179}, {96, 179}, {98, 179}, {136, 179}, {138, 179}, {149, 179}, {160, 179}, {161, 179}, {184, 179}, {185, 179}, {240, 179}, {241, 179}, {397, 179}, {398, 179}, {47, 180}, {48, 180}, {49, 180}, {50, 180}, {51, 180}, {56, 180}, {62, 180}, {64, 180}, {67, 180}, {71, 180}, {72, 180}, {73, 180}, {81, 180}, {97, 180}, {136, 180}, {138, 180}, {148, 180}, {150, 180}, {396, 180}, {398, 180}, {47, 181}, {59, 181}, {60, 181}, {61, 181}, {68, 181}, {71, 181}, {109, 181}, {137, 181}, {149, 181}, {151, 181}, {397, 181}, {48, 182}, {50, 182}, {61, 182}, {65, 182}, {66, 182}, {67, 182}, {70, 182}, {71, 182}, {73, 182}, {100, 182}, {101, 182}, {102, 182}, {109, 182}, {150, 182}, {272, 182}, {273, 182}, {41, 183}, {43, 183}, {49, 183}, {50, 183}, {62, 183}, {63, 183}, {64, 183}, {68, 183}, {70, 183}, {71, 183}, {73, 183}, {103, 183}, {109, 183}, {272, 183}, {273, 183}, {41, 184}, {42, 184}, {63, 184}, {68, 184}, {69, 184}, {71, 184}, {98, 184}, {104, 184}, {105, 184}, {42, 185}, {66, 185}, {98, 185}, {106, 185}, {107, 185}, {111, 185}, {112, 185}, {113, 185}, {275, 185}, {276, 185}, {379, 185}, {380, 185}, {66, 186}, {69, 186}, {70, 186}, {71, 186}, {98, 186}, {104, 186}, {105, 186}, {122, 186}, {275, 186}, {276, 186}, {342, 186}, {379, 186}, {380, 186}, {393, 186}, {70, 187}, {75, 187}, {94, 187}, {95, 187}, {96, 187}, {103, 187}, {109, 187}, {121, 187}, {123, 187}, {124, 187}, {126, 187}, {127, 187}, {340, 187}, {342, 187}, {393, 187}, {70, 188}, {75, 188}, {100, 188}, {101, 188}, {102, 188}, {109, 188}, {121, 188}, {123, 188}, {124, 188}, {126, 188}, {127, 188}, {128, 188}, {341, 188}, {342, 188}, {393, 188}, {71, 189}, {74, 189}, {109, 189}, {123, 189}, {129, 189}, {248, 189}, {129, 190}, {130, 190}, {238, 190}, {239, 190}, {248, 190}, {254, 190}, {255, 190}, {256, 190}, {56, 191}, {57, 191}, {85, 191}, {86, 191}, {121, 191}, {122, 191}, {128, 191}, {129, 191}, {148, 191}, {185, 191}, {186, 191}, {187, 191}, {238, 191}, {239, 191}, {248, 191}, {263, 191}, {366, 191}, {367, 191}, {390, 191}, {391, 191}, {392, 191}, {410, 191}, {56, 192}, {57, 192}, {85, 192}, {86, 192}, {116, 192}, {117, 192}, {127, 192}, {148, 192}, {156, 192}, {157, 192}, {158, 192}, {262, 192}, {264, 192}, {281, 192}, {282, 192}, {334, 192}, {335, 192}, {366, 192}, {367, 192}, {410, 192}, {115, 193}, {117, 193}, {126, 193}, {148, 193}, {261, 193}, {263, 193}, {280, 193}, {283, 193}, {334, 193}, {335, 193}, {410, 193}, {92, 194}, {116, 194}, {143, 194}, {144, 194}, {261, 194}, {262, 194}, {281, 194}, {282, 194}, {91, 195}, {93, 195}, {143, 195}, {144, 195}, {206, 195}, {207, 195}, {208, 195}, {503, 195}, {504, 195}, {91, 196}, {93, 196}, {133, 196}, {134, 196}, {502, 196}, {505, 196}, {92, 197}, {133, 197}, {134, 197}, {353, 197}, {354, 197}, {503, 197}, {505, 197}, {40, 198}, {246, 198}, {247, 198}, {352, 198}, {355, 198}, {400, 198}, {401, 198}, {504, 198}, {39, 199}, {41, 199}, {87, 199}, {88, 199}, {96, 199}, {97, 199}, {246, 199}, {247, 199}, {258, 199}, {259, 199}, {338, 199}, {339, 199}, {353, 199}, {354, 199}, {400, 199}, {401, 199}, {39, 200}, {41, 200}, {86, 200}, {89, 200}, {95, 200}, {98, 200}, {163, 200}, {164, 200}, {258, 200}, {259, 200}, {337, 200}, {340, 200}, {40, 201}, {87, 201}, {88, 201}, {96, 201}, {97, 201}, {163, 201}, {164, 201}, {226, 201}, {337, 201}, {339, 201}, {34, 202}, {194, 202}, {195, 202}, {226, 202}, {338, 202}, {34, 203}, {92, 203}, {123, 203}, {124, 203}, {193, 203}, {196, 203}, {226, 203}, {261, 203}, {34, 204}, {91, 204}, {93, 204}, {123, 204}, {124, 204}, {129, 204}, {134, 204}, {146, 204}, {147, 204}, {194, 204}, {195, 204}, {260, 204}, {262, 204}, {353, 204}, {354, 204}, {372, 204}, {91, 205}, {93, 205}, {128, 205}, {130, 205}, {134, 205}, {146, 205}, {147, 205}, {260, 205}, {261, 205}, {353, 205}, {354, 205}, {372, 205}, {467, 205}, {468, 205}, {34, 206}, {55, 206}, {56, 206}, {92, 206}, {128, 206}, {130, 206}, {134, 206}, {164, 206}, {165, 206}, {213, 206}, {278, 206}, {279, 206}, {289, 206}, {290, 206}, {291, 206}, {358, 206}, {359, 206}, {372, 206}, {467, 206}, {468, 206}, {33, 207}, {35, 207}, {55, 207}, {56, 207}, {78, 207}, {129, 207}, {158, 207}, {159, 207}, {164, 207}, {165, 207}, {213, 207}, {278, 207}, {279, 207}, {358, 207}, {359, 207}, {404, 207}, {33, 208}, {36, 208}, {79, 208}, {136, 208}, {137, 208}, {138, 208}, {149, 208}, {150, 208}, {157, 208}, {160, 208}, {213, 208}, {287, 208}, {293, 208}, {368, 208}, {369, 208}, {370, 208}, {374, 208}, {375, 208}, {376, 208}, {378, 208}, {379, 208}, {380, 208}, {404, 208}, {507, 208}, {508, 208}, {33, 209}, {34, 209}, {36, 209}, {37, 209}, {77, 209}, {78, 209}, {79, 209}, {115, 209}, {148, 209}, {151, 209}, {158, 209}, {159, 209}, {287, 209}, {293, 209}, {404, 209}, {414, 209}, {475, 209}, {476, 209}, {506, 209}, {509, 209}, {34, 210}, {37, 210}, {115, 210}, {149, 210}, {150, 210}, {209, 210}, {210, 210}, {211, 210}, {215, 210}, {216, 210}, {217, 210}, {273, 210}, {274, 210}, {287, 210}, {293, 210}, {372, 210}, {413, 210}, {415, 210}, {475, 210}, {476, 210}, {507, 210}, {508, 210}, {29, 211}, {30, 211}, {31, 211}, {34, 211}, {36, 211}, {115, 211}, {262, 211}, {263, 211}, {273, 211}, {274, 211}, {282, 211}, {290, 211}, {372, 211}, {413, 211}, {416, 211}, {29, 212}, {32, 212}, {128, 212}, {129, 212}, {213, 212}, {262, 212}, {263, 212}, {282, 212}, {289, 212}, {291, 212}, {372, 212}, {414, 212}, {415, 212}, {29, 213}, {30, 213}, {32, 213}, {127, 213}, {130, 213}, {213, 213}, {245, 213}, {246, 213}, {247, 213}, {282, 213}, {289, 213}, {291, 213}, {385, 213}, {386, 213}, {31, 214}, {128, 214}, {130, 214}, {213, 214}, {245, 214}, {265, 214}, {266, 214}, {267, 214}, {290, 214}, {377, 214}, {378, 214}, {384, 214}, {386, 214}, {399, 214}, {411, 214}, {412, 214}, {456, 214}, {457, 214}, {26, 215}, {27, 215}, {49, 215}, {50, 215}, {66, 215}, {129, 215}, {136, 215}, {137, 215}, {246, 215}, {376, 215}, {379, 215}, {385, 215}, {398, 215}, {400, 215}, {411, 215}, {412, 215}, {456, 215}, {458, 215}, {26, 216}, {27, 216}, {49, 216}, {50, 216}, {65, 216}, {67, 216}, {135, 216}, {138, 216}, {376, 216}, {378, 216}, {398, 216}, {400, 216}, {457, 216}, {458, 216}, {41, 217}, {43, 217}, {65, 217}, {67, 217}, {135, 217}, {138, 217}, {221, 217}, {222, 217}, {377, 217}, {399, 217}, {4, 218}, {5, 218}, {42, 218}, {43, 218}, {66, 218}, {136, 218}, {137, 218}, {221, 218}, {222, 218}, {237, 218}, {238, 218}, {4, 219}, {5, 219}, {42, 219}, {237, 219}, {238, 219}, {236, 220}, {237, 220}, {238, 220}, {241, 220}, {242, 220}, {419, 220}, {420, 220}, {232, 221}, {233, 221}, {238, 221}, {239, 221}, {241, 221}, {242, 221}, {243, 221}, {419, 221}, {420, 221}, {38, 222}, {39, 222}, {68, 222}, {69, 222}, {231, 222}, {232, 222}, {233, 222}, {235, 222}, {236, 222}, {241, 222}, {242, 222}, {245, 222}, {247, 222}, {492, 222}, {493, 222}, {13, 223}, {14, 223}, {38, 223}, {39, 223}, {61, 223}, {62, 223}, {63, 223}, {67, 223}, {69, 223}, {232, 223}, {233, 223}, {236, 223}, {237, 223}, {238, 223}, {245, 223}, {246, 223}, {256, 223}, {257, 223}, {258, 223}, {492, 223}, {493, 223}, {13, 224}, {14, 224}, {61, 224}, {63, 224}, {64, 224}, {68, 224}, {129, 224}, {236, 224}, {237, 224}, {246, 224}, {298, 224}, {377, 224}, {378, 224}, {379, 224}, {61, 225}, {64, 225}, {65, 225}, {66, 225}, {67, 225}, {128, 225}, {130, 225}, {166, 225}, {167, 225}, {236, 225}, {237, 225}, {254, 225}, {263, 225}, {284, 225}, {297, 225}, {299, 225}, {66, 226}, {67, 226}, {127, 226}, {130, 226}, {165, 226}, {168, 226}, {254, 226}, {263, 226}, {284, 226}, {297, 226}, {300, 226}, {375, 226}, {381, 226}, {394, 226}, {395, 226}, {63, 227}, {64, 227}, {65, 227}, {68, 227}, {94, 227}, {128, 227}, {129, 227}, {165, 227}, {168, 227}, {180, 227}, {181, 227}, {254, 227}, {263, 227}, {284, 227}, {298, 227}, {299, 227}, {361, 227}, {362, 227}, {375, 227}, {381, 227}, {394, 227}, {395, 227}, {64, 228}, {65, 228}, {66, 228}, {68, 228}, {69, 228}, {71, 228}, {72, 228}, {94, 228}, {166, 228}, {167, 228}, {179, 228}, {182, 228}, {361, 228}, {362, 228}, {375, 228}, {381, 228}, {63, 229}, {65, 229}, {66, 229}, {67, 229}, {70, 229}, {73, 229}, {94, 229}, {180, 229}, {181, 229}, {223, 229}, {224, 229}, {259, 229}, {260, 229}, {261, 229}, {265, 229}, {266, 229}, {267, 229}, {57, 230}, {58, 230}, {63, 230}, {71, 230}, {72, 230}, {223, 230}, {224, 230}, {292, 230}, {293, 230}, {377, 230}, {378, 230}, {379, 230}, {388, 230}, {389, 230}, {48, 231}, {49, 231}, {56, 231}, {59, 231}, {231, 231}, {263, 231}, {292, 231}, {293, 231}, {365, 231}, {366, 231}, {388, 231}, {389, 231}, {0, 232}, {1, 232}, {48, 232}, {49, 232}, {56, 232}, {57, 232}, {59, 232}, {61, 232}, {63, 232}, {231, 232}, {232, 232}, {263, 232}, {365, 232}, {366, 232}, {0, 233}, {1, 233}, {60, 233}, {61, 233}, {233, 233}, {263, 233}, {61, 234}, {223, 234}, {224, 234}, {230, 234}, {231, 234}, {232, 234}, {109, 235}, {110, 235}, {203, 235}, {204, 235}, {205, 235}, {222, 235}, {223, 235}, {224, 235}, {229, 235}, {230, 235}, {328, 235}, {329, 235}, {19, 236}, {20, 236}, {47, 236}, {48, 236}, {54, 236}, {55, 236}, {104, 236}, {105, 236}, {109, 236}, {110, 236}, {213, 236}, {214, 236}, {221, 236}, {223, 236}, {229, 236}, {230, 236}, {269, 236}, {270, 236}, {328, 236}, {329, 236}, {370, 236}, {504, 236}, {505, 236}, {19, 237}, {20, 237}, {47, 237}, {48, 237}, {54, 237}, {56, 237}, {103, 237}, {106, 237}, {213, 237}, {214, 237}, {221, 237}, {223, 237}, {243, 237}, {255, 237}, {256, 237}, {269, 237}, {270, 237}, {369, 237}, {371, 237}, {421, 237}, {502, 237}, {503, 237}, {505, 237}, {506, 237}, {55, 238}, {104, 238}, {106, 238}, {221, 238}, {225, 238}, {226, 238}, {243, 238}, {255, 238}, {256, 238}, {342, 238}, {343, 238}, {369, 238}, {370, 238}, {420, 238}, {422, 238}, {491, 238}, {492, 238}, {502, 238}, {503, 238}, {507, 238}, {508, 238}, {105, 239}, {222, 239}, {226, 239}, {243, 239}, {302, 239}, {336, 239}, {337, 239}, {341, 239}, {344, 239}, {420, 239}, {423, 239}, {491, 239}, {492, 239}, {503, 239}, {504, 239}, {505, 239}, {507, 239}, {509, 239}, {510, 239}, {2, 240}, {3, 240}, {4, 240}, {26, 240}, {91, 240}, {197, 240}, {198, 240}, {199, 240}, {223, 240}, {259, 240}, {260, 240}, {261, 240}, {279, 240}, {280, 240}, {286, 240}, {287, 240}, {292, 240}, {301, 240}, {303, 240}, {336, 240}, {337, 240}, {342, 240}, {344, 240}, {421, 240}, {422, 240}, {505, 240}, {507, 240}, {510, 240}, {26, 241}, {74, 241}, {75, 241}, {76, 241}, {90, 241}, {92, 241}, {152, 241}, {214, 241}, {239, 241}, {240, 241}, {241, 241}, {246, 241}, {261, 241}, {279, 241}, {280, 241}, {285, 241}, {287, 241}, {291, 241}, {293, 241}, {301, 241}, {302, 241}, {343, 241}, {482, 241}, {483, 241}, {484, 241}, {506, 241}, {510, 241}, {26, 242}, {73, 242}, {76, 242}, {90, 242}, {92, 242}, {152, 242}, {195, 242}, {201, 242}, {213, 242}, {215, 242}, {246, 242}, {247, 242}, {251, 242}, {252, 242}, {253, 242}, {261, 242}, {262, 242}, {263, 242}, {284, 242}, {286, 242}, {290, 242}, {293, 242}, {506, 242}, {508, 242}, {510, 242}, {511, 242}, {73, 243}, {74, 243}, {77, 243}, {78, 243}, {91, 243}, {126, 243}, {127, 243}, {128, 243}, {152, 243}, {195, 243}, {201, 243}, {213, 243}, {215, 243}, {245, 243}, {248, 243}, {251, 243}, {252, 243}, {253, 243}, {256, 243}, {261, 243}, {264, 243}, {274, 243}, {275, 243}, {276, 243}, {285, 243}, {291, 243}, {292, 243}, {480, 243}, {486, 243}, {507, 243}, {508, 243}, {74, 244}, {75, 244}, {78, 244}, {195, 244}, {201, 244}, {214, 244}, {236, 244}, {237, 244}, {246, 244}, {248, 244}, {249, 244}, {255, 244}, {257, 244}, {262, 244}, {263, 244}, {317, 244}, {318, 244}, {361, 244}, {362, 244}, {480, 244}, {486, 244}, {76, 245}, {148, 245}, {149, 245}, {150, 245}, {154, 245}, {155, 245}, {156, 245}, {217, 245}, {229, 245}, {230, 245}, {231, 245}, {236, 245}, {237, 245}, {246, 245}, {247, 245}, {248, 245}, {254, 245}, {258, 245}, {317, 245}, {319, 245}, {361, 245}, {362, 245}, {480, 245}, {486, 245}, {78, 246}, {79, 246}, {197, 246}, {198, 246}, {199, 246}, {216, 246}, {218, 246}, {229, 246}, {230, 246}, {231, 246}, {247, 246}, {248, 246}, {249, 246}, {254, 246}, {258, 246}, {259, 246}, {318, 246}, {319, 246}, {5, 247}, {6, 247}, {75, 247}, {79, 247}, {152, 247}, {216, 247}, {218, 247}, {227, 247}, {228, 247}, {249, 247}, {250, 247}, {253, 247}, {254, 247}, {255, 247}, {257, 247}, {258, 247}, {482, 247}, {483, 247}, {484, 247}, {5, 248}, {6, 248}, {49, 248}, {50, 248}, {51, 248}, {152, 248}, {217, 248}, {227, 248}, {238, 248}, {248, 248}, {251, 248}, {255, 248}, {257, 248}, {390, 248}, {79, 249}, {87, 249}, {88, 249}, {152, 249}, {227, 249}, {237, 249}, {238, 249}, {239, 249}, {249, 249}, {250, 249}, {389, 249}, {391, 249}, {76, 250}, {80, 250}, {86, 250}, {89, 250}, {228, 250}, {231, 250}, {232, 250}, {236, 250}, {238, 250}, {240, 250}, {389, 250}, {391, 250}, {77, 251}, {80, 251}, {87, 251}, {88, 251}, {106, 251}, {107, 251}, {232, 251}, {235, 251}, {236, 251}, {237, 251}, {239, 251}, {240, 251}, {241, 251}, {259, 251}, {308, 251}, {309, 251}, {390, 251}, {80, 252}, {81, 252}, {83, 252}, {84, 252}, {105, 252}, {108, 252}, {128, 252}, {129, 252}, {148, 252}, {149, 252}, {229, 252}, {230, 252}, {231, 252}, {236, 252}, {238, 252}, {240, 252}, {260, 252}, {308, 252}, {309, 252}, {380, 252}, {381, 252}, {38, 253}, {79, 253}, {80, 253}, {83, 253}, {84, 253}, {105, 253}, {108, 253}, {127, 253}, {130, 253}, {148, 253}, {149, 253}, {230, 253}, {231, 253}, {237, 253}, {238, 253}, {239, 253}, {260, 253}, {380, 253}, {381, 253}, {506, 253}, {507, 253}, {508, 253}, {37, 254}, {39, 254}, {101, 254}, {106, 254}, {107, 254}, {128, 254}, {129, 254}, {231, 254}, {232, 254}, {238, 254}, {490, 254}, {36, 255}, {37, 255}, {39, 255}, {40, 255}, {78, 255}, {79, 255}, {100, 255}, {102, 255}, {135, 255}, {214, 255}, {312, 255}, {313, 255}, {477, 255}, {489, 255}, {491, 255}, {493, 255}, {494, 255}, {504, 255}, {510, 255}, {36, 256}, {77, 256}, {78, 256}, {80, 256}, {101, 256}, {102, 256}, {134, 256}, {136, 256}, {213, 256}, {215, 256}, {225, 256}, {226, 256}, {312, 256}, {313, 256}, {476, 256}, {478, 256}, {489, 256}, {491, 256}, {493, 256}, {494, 256}, {504, 256}, {510, 256}, {72, 257}, {73, 257}, {77, 257}, {80, 257}, {119, 257}, {134, 257}, {136, 257}, {212, 257}, {215, 257}, {225, 257}, {226, 257}, {307, 257}, {308, 257}, {405, 257}, {406, 257}, {407, 257}, {476, 257}, {478, 257}, {490, 257}, {504, 257}, {510, 257}, {33, 258}, {34, 258}, {35, 258}, {71, 258}, {74, 258}, {79, 258}, {119, 258}, {135, 258}, {213, 258}, {214, 258}, {221, 258}, {306, 258}, {309, 258}, {477, 258}, {496, 258}, {11, 259}, {33, 259}, {34, 259}, {72, 259}, {73, 259}, {119, 259}, {220, 259}, {222, 259}, {306, 259}, {308, 259}, {387, 259}, {496, 259}, {506, 259}, {507, 259}, {508, 259}, {10, 260}, {12, 260}, {20, 260}, {21, 260}, {27, 260}, {28, 260}, {34, 260}, {220, 260}, {222, 260}, {307, 260}, {386, 260}, {388, 260}, {496, 260}, {10, 261}, {12, 261}, {19, 261}, {22, 261}, {27, 261}, {28, 261}, {59, 261}, {60, 261}, {61, 261}, {115, 261}, {116, 261}, {117, 261}, {121, 261}, {122, 261}, {123, 261}, {221, 261}, {385, 261}, {388, 261}, {422, 261}, {423, 261}, {11, 262}, {20, 262}, {21, 262}, {36, 262}, {37, 262}, {43, 262}, {59, 262}, {323, 262}, {386, 262}, {387, 262}, {421, 262}, {423, 262}, {477, 262}, {478, 262}, {41, 263}, {42, 263}, {43, 263}, {60, 263}, {100, 263}, {101, 263}, {119, 263}, {264, 263}, {322, 263}, {324, 263}, {421, 263}, {422, 263}, {477, 263}, {478, 263}, {40, 264}, {41, 264}, {42, 264}, {43, 264}, {44, 264}, {86, 264}, {87, 264}, {99, 264}, {100, 264}, {119, 264}, {131, 264}, {132, 264}, {258, 264}, {259, 264}, {263, 264}, {265, 264}, {322, 264}, {324, 264}, {23, 265}, {24, 265}, {39, 265}, {85, 265}, {88, 265}, {95, 265}, {96, 265}, {101, 265}, {108, 265}, {119, 265}, {131, 265}, {132, 265}, {225, 265}, {226, 265}, {258, 265}, {259, 265}, {263, 265}, {265, 265}, {323, 265}, {22, 266}, {25, 266}, {38, 266}, {39, 266}, {40, 266}, {44, 266}, {45, 266}, {86, 266}, {87, 266}, {95, 266}, {96, 266}, {108, 266}, {225, 266}, {226, 266}, {264, 266}, {310, 266}, {311, 266}, {402, 266}, {22, 267}, {25, 267}, {40, 267}, {42, 267}, {43, 267}, {44, 267}, {111, 267}, {112, 267}, {310, 267}, {311, 267}, {402, 267}, {23, 268}, {24, 268}, {41, 268}, {42, 268}, {43, 268}, {91, 268}, {110, 268}, {113, 268}, {398, 268}, {402, 268}, {415, 268}, {416, 268}, {41, 269}, {42, 269}, {43, 269}, {90, 269}, {92, 269}, {111, 269}, {112, 269}, {217, 269}, {218, 269}, {219, 269}, {277, 269}, {278, 269}, {397, 269}, {399, 269}, {415, 269}, {416, 269}, {14, 270}, {15, 270}, {90, 270}, {92, 270}, {221, 270}, {251, 270}, {252, 270}, {276, 270}, {278, 270}, {312, 270}, {326, 270}, {327, 270}, {397, 270}, {399, 270}, {14, 271}, {15, 271}, {91, 271}, {203, 271}, {204, 271}, {219, 271}, {220, 271}, {222, 271}, {241, 271}, {250, 271}, {253, 271}, {277, 271}, {312, 271}, {326, 271}, {327, 271}, {398, 271}, {499, 271}, {500, 271}, {38, 272}, {39, 272}, {202, 272}, {205, 272}, {222, 272}, {223, 272}, {241, 272}, {251, 272}, {252, 272}, {312, 272}, {355, 272}, {499, 272}, {500, 272}, {38, 273}, {39, 273}, {202, 273}, {205, 273}, {221, 273}, {222, 273}, {241, 273}, {354, 273}, {356, 273}, {379, 273}, {380, 273}, {393, 273}, {394, 273}, {203, 274}, {204, 274}, {212, 274}, {218, 274}, {220, 274}, {221, 274}, {317, 274}, {354, 274}, {356, 274}, {378, 274}, {381, 274}, {392, 274}, {395, 274}, {405, 274}, {406, 274}, {47, 275}, {88, 275}, {89, 275}, {211, 275}, {213, 275}, {219, 275}, {311, 275}, {312, 275}, {316, 275}, {318, 275}, {355, 275}, {379, 275}, {380, 275}, {393, 275}, {394, 275}, {405, 275}, {406, 275}, {47, 276}, {87, 276}, {90, 276}, {211, 276}, {213, 276}, {219, 276}, {311, 276}, {312, 276}, {316, 276}, {317, 276}, {321, 276}, {23, 277}, {48, 277}, {88, 277}, {89, 277}, {109, 277}, {110, 277}, {111, 277}, {212, 277}, {320, 277}, {322, 277}, {447, 277}, {489, 277}, {500, 277}, {22, 278}, {24, 278}, {25, 278}, {319, 278}, {322, 278}, {384, 278}, {400, 278}, {401, 278}, {402, 278}, {415, 278}, {416, 278}, {446, 278}, {448, 278}, {488, 278}, {490, 278}, {499, 278}, {501, 278}, {22, 279}, {52, 279}, {53, 279}, {207, 279}, {208, 279}, {216, 279}, {217, 279}, {320, 279}, {321, 279}, {372, 279}, {373, 279}, {384, 279}, {415, 279}, {416, 279}, {446, 279}, {448, 279}, {489, 279}, {500, 279}, {14, 280}, {23, 280}, {24, 280}, {26, 280}, {27, 280}, {53, 280}, {54, 280}, {206, 280}, {209, 280}, {215, 280}, {218, 280}, {253, 280}, {372, 280}, {373, 280}, {384, 280}, {438, 280}, {447, 280}, {13, 281}, {15, 281}, {22, 281}, {23, 281}, {24, 281}, {27, 281}, {53, 281}, {54, 281}, {207, 281}, {208, 281}, {216, 281}, {217, 281}, {253, 281}, {261, 281}, {289, 281}, {290, 281}, {422, 281}, {423, 281}, {437, 281}, {439, 281}, {12, 282}, {15, 282}, {21, 282}, {22, 282}, {23, 282}, {25, 282}, {50, 282}, {51, 282}, {52, 282}, {53, 282}, {231, 282}, {232, 282}, {233, 282}, {243, 282}, {253, 282}, {261, 282}, {289, 282}, {290, 282}, {422, 282}, {423, 282}, {437, 282}, {439, 282}, {13, 283}, {14, 283}, {24, 283}, {25, 283}, {28, 283}, {50, 283}, {212, 283}, {242, 283}, {244, 283}, {261, 283}, {438, 283}, {24, 284}, {26, 284}, {27, 284}, {28, 284}, {29, 284}, {49, 284}, {52, 284}, {211, 284}, {213, 284}, {241, 284}, {244, 284}, {427, 284}, {428, 284}, {24, 285}, {27, 285}, {30, 285}, {51, 285}, {52, 285}, {211, 285}, {213, 285}, {242, 285}, {243, 285}, {312, 285}, {427, 285}, {428, 285}, {21, 286}, {23, 286}, {28, 286}, {212, 286}, {226, 286}, {227, 286}, {312, 286}, {406, 286}, {12, 287}, {29, 287}, {30, 287}, {31, 287}, {225, 287}, {228, 287}, {289, 287}, {290, 287}, {312, 287}, {406, 287}, {10, 288}, {11, 288}, {13, 288}, {226, 288}, {228, 288}, {289, 288}, {290, 288}, {406, 288}, {412, 288}, {413, 288}, {422, 288}, {423, 288}, {426, 288}, {11, 289}, {13, 289}, {199, 289}, {200, 289}, {227, 289}, {263, 289}, {264, 289}, {411, 289}, {414, 289}, {420, 289}, {422, 289}, {423, 289}, {427, 289}, {12, 290}, {199, 290}, {201, 290}, {241, 290}, {242, 290}, {263, 290}, {264, 290}, {412, 290}, {413, 290}, {423, 290}, {29, 291}, {30, 291}, {65, 291}, {66, 291}, {67, 291}, {109, 291}, {126, 291}, {127, 291}, {128, 291}, {200, 291}, {201, 291}, {240, 291}, {243, 291}, {425, 291}, {427, 291}, {29, 292}, {30, 292}, {96, 292}, {97, 292}, {108, 292}, {110, 292}, {241, 292}, {242, 292}, {289, 292}, {290, 292}, {372, 292}, {373, 292}, {426, 292}, {427, 292}, {428, 292}, {89, 293}, {90, 293}, {96, 293}, {97, 293}, {109, 293}, {274, 293}, {289, 293}, {290, 293}, {372, 293}, {373, 293}, {424, 293}, {427, 293}, {504, 293}, {88, 294}, {91, 294}, {274, 294}, {425, 294}, {426, 294}, {503, 294}, {505, 294}, {88, 295}, {90, 295}, {215, 295}, {216, 295}, {217, 295}, {225, 295}, {274, 295}, {309, 295}, {310, 295}, {379, 295}, {504, 295}, {89, 296}, {224, 296}, {226, 296}, {308, 296}, {311, 296}, {379, 296}, {403, 296}, {404, 296}, {224, 297}, {225, 297}, {309, 297}, {310, 297}, {379, 297}, {384, 297}, {403, 297}, {404, 297}, {438, 297}, {472, 297}, {473, 297}, {46, 298}, {326, 298}, {327, 298}, {383, 298}, {385, 298}, {437, 298}, {439, 298}, {472, 298}, {473, 298}, {46, 299}, {105, 299}, {212, 299}, {213, 299}, {326, 299}, {327, 299}, {345, 299}, {346, 299}, {347, 299}, {383, 299}, {384, 299}, {410, 299}, {411, 299}, {437, 299}, {439, 299}, {34, 300}, {35, 300}, {46, 300}, {105, 300}, {211, 300}, {213, 300}, {410, 300}, {411, 300}, {438, 300}, {34, 301}, {35, 301}, {105, 301}, {211, 301}, {212, 301}, {217, 301}, {218, 301}, {343, 301}, {349, 301}, {217, 302}, {218, 302}, {343, 302}, {349, 302}, {386, 302}, {392, 302}, {207, 303}, {343, 303}, {349, 303}, {385, 303}, {387, 303}, {392, 303}, {207, 304}, {385, 304}, {387, 304}, {392, 304}, {103, 305}, {104, 305}, {207, 305}, {213, 305}, {214, 305}, {345, 305}, {346, 305}, {347, 305}, {386, 305}, {404, 305}, {405, 305}, {498, 305}, {39, 306}, {40, 306}, {102, 306}, {105, 306}, {213, 306}, {214, 306}, {403, 306}, {406, 306}, {497, 306}, {499, 306}, {38, 307}, {41, 307}, {103, 307}, {104, 307}, {404, 307}, {405, 307}, {497, 307}, {499, 307}, {24, 308}, {25, 308}, {39, 308}, {40, 308}, {118, 308}, {270, 308}, {498, 308}, {24, 309}, {25, 309}, {117, 309}, {119, 309}, {269, 309}, {271, 309}, {291, 309}, {292, 309}, {484, 309}, {43, 310}, {44, 310}, {117, 310}, {119, 310}, {237, 310}, {269, 310}, {271, 310}, {291, 310}, {292, 310}, {399, 310}, {484, 310}, {43, 311}, {44, 311}, {56, 311}, {57, 311}, {118, 311}, {236, 311}, {238, 311}, {270, 311}, {398, 311}, {400, 311}, {470, 311}, {471, 311}, {484, 311}, {21, 312}, {56, 312}, {57, 312}, {82, 312}, {83, 312}, {102, 312}, {103, 312}, {236, 312}, {238, 312}, {387, 312}, {388, 312}, {389, 312}, {398, 312}, {400, 312}, {411, 312}, {412, 312}, {413, 312}, {470, 312}, {471, 312}, {21, 313}, {32, 313}, {65, 313}, {66, 313}, {82, 313}, {83, 313}, {102, 313}, {103, 313}, {113, 313}, {114, 313}, {122, 313}, {123, 313}, {237, 313}, {399, 313}, {486, 313}, {487, 313}, {488, 313}, {21, 314}, {31, 314}, {33, 314}, {65, 314}, {66, 314}, {112, 314}, {115, 314}, {121, 314}, {124, 314}, {275, 314}, {276, 314}, {32, 315}, {33, 315}, {113, 315}, {114, 315}, {122, 315}, {123, 315}, {274, 315}, {277, 315}, {286, 315}, {287, 315}, {17, 316}, {18, 316}, {19, 316}, {23, 316}, {24, 316}, {25, 316}, {212, 316}, {242, 316}, {243, 316}, {275, 316}, {276, 316}, {285, 316}, {288, 316}, {38, 317}, {39, 317}, {65, 317}, {66, 317}, {118, 317}, {211, 317}, {213, 317}, {241, 317}, {244, 317}, {285, 317}, {288, 317}, {305, 317}, {503, 317}, {504, 317}, {21, 318}, {38, 318}, {39, 318}, {65, 318}, {66, 318}, {117, 318}, {119, 318}, {211, 318}, {213, 318}, {242, 318}, {243, 318}, {286, 318}, {287, 318}, {304, 318}, {306, 318}, {348, 318}, {502, 318}, {505, 318}, {21, 319}, {117, 319}, {119, 319}, {131, 319}, {132, 319}, {212, 319}, {304, 319}, {306, 319}, {348, 319}, {478, 319}, {479, 319}, {503, 319}, {504, 319}, {21, 320}, {118, 320}, {131, 320}, {132, 320}, {305, 320}, {348, 320}, {374, 320}, {375, 320}, {477, 320}, {480, 320}, {53, 321}, {103, 321}, {104, 321}, {105, 321}, {153, 321}, {154, 321}, {207, 321}, {208, 321}, {374, 321}, {375, 321}, {478, 321}, {480, 321}, {10, 322}, {53, 322}, {153, 322}, {154, 322}, {206, 322}, {209, 322}, {294, 322}, {295, 322}, {479, 322}, {508, 322}, {509, 322}, {510, 322}, {10, 323}, {53, 323}, {127, 323}, {128, 323}, {206, 323}, {209, 323}, {284, 323}, {294, 323}, {295, 323}, {10, 324}, {126, 324}, {128, 324}, {207, 324}, {208, 324}, {263, 324}, {264, 324}, {283, 324}, {285, 324}, {301, 324}, {46, 325}, {127, 325}, {156, 325}, {263, 325}, {264, 325}, {282, 325}, {285, 325}, {300, 325}, {302, 325}, {454, 325}, {455, 325}, {491, 325}, {492, 325}, {12, 326}, {13, 326}, {14, 326}, {19, 326}, {20, 326}, {21, 326}, {38, 326}, {39, 326}, {45, 326}, {47, 326}, {76, 326}, {77, 326}, {100, 326}, {131, 326}, {132, 326}, {155, 326}, {157, 326}, {283, 326}, {284, 326}, {300, 326}, {302, 326}, {454, 326}, {455, 326}, {491, 326}, {492, 326}, {37, 327}, {40, 327}, {45, 327}, {47, 327}, {75, 327}, {78, 327}, {84, 327}, {85, 327}, {86, 327}, {100, 327}, {131, 327}, {132, 327}, {145, 327}, {154, 327}, {156, 327}, {161, 327}, {162, 327}, {301, 327}, {402, 327}, {403, 327}, {37, 328}, {39, 328}, {46, 328}, {76, 328}, {77, 328}, {100, 328}, {144, 328}, {146, 328}, {154, 328}, {155, 328}, {160, 328}, {163, 328}, {402, 328}, {403, 328}, {38, 329}, {144, 329}, {146, 329}, {161, 329}, {162, 329}, {123, 330}, {145, 330}, {209, 330}, {210, 330}, {211, 330}, {229, 330}, {230, 330}, {318, 330}, {319, 330}, {459, 330}, {123, 331}, {166, 331}, {167, 331}, {168, 331}, {229, 331}, {230, 331}, {268, 331}, {269, 331}, {317, 331}, {320, 331}, {364, 331}, {365, 331}, {403, 331}, {404, 331}, {422, 331}, {423, 331}, {458, 331}, {460, 331}, {498, 331}, {499, 331}, {76, 332}, {77, 332}, {107, 332}, {108, 332}, {109, 332}, {123, 332}, {140, 332}, {141, 332}, {149, 332}, {150, 332}, {177, 332}, {178, 332}, {179, 332}, {268, 332}, {270, 332}, {317, 332}, {319, 332}, {364, 332}, {365, 332}, {403, 332}, {404, 332}, {422, 332}, {423, 332}, {459, 332}, {460, 332}, {497, 332}, {500, 332}, {76, 333}, {77, 333}, {139, 333}, {142, 333}, {148, 333}, {151, 333}, {269, 333}, {318, 333}, {340, 333}, {341, 333}, {497, 333}, {499, 333}, {119, 334}, {120, 334}, {121, 334}, {125, 334}, {126, 334}, {127, 334}, {140, 334}, {141, 334}, {149, 334}, {150, 334}, {175, 334}, {181, 334}, {340, 334}, {341, 334}, {498, 334}, {175, 335}, {181, 335}, {423, 335}, {424, 335}, {123, 336}, {145, 336}, {175, 336}, {181, 336}, {224, 336}, {423, 336}, {424, 336}, {493, 336}, {494, 336}, {47, 337}, {48, 337}, {49, 337}, {123, 337}, {144, 337}, {146, 337}, {223, 337}, {225, 337}, {360, 337}, {446, 337}, {493, 337}, {494, 337}, {509, 337}, {123, 338}, {144, 338}, {146, 338}, {177, 338}, {178, 338}, {179, 338}, {223, 338}, {226, 338}, {360, 338}, {446, 338}, {508, 338}, {510, 338}, {145, 339}, {224, 339}, {225, 339}, {360, 339}, {446, 339}, {507, 339}, {509, 339}, {468, 340}, {469, 340}, {507, 340}, {508, 340}, {467, 341}, {470, 341}, {186, 342}, {187, 342}, {238, 342}, {239, 342}, {467, 342}, {470, 342}, {186, 343}, {187, 343}, {238, 343}, {239, 343}, {263, 343}, {264, 343}, {265, 343}, {447, 343}, {448, 343}, {468, 343}, {469, 343}, {510, 343}, {511, 343}, {442, 344}, {443, 344}, {446, 344}, {449, 344}, {510, 344}, {511, 344}, {314, 345}, {315, 345}, {371, 345}, {442, 345}, {443, 345}, {447, 345}, {448, 345}, {101, 346}, {102, 346}, {198, 346}, {199, 346}, {200, 346}, {256, 346}, {257, 346}, {271, 346}, {272, 346}, {313, 346}, {316, 346}, {344, 346}, {345, 346}, {370, 346}, {372, 346}, {419, 346}, {420, 346}, {100, 347}, {102, 347}, {190, 347}, {248, 347}, {249, 347}, {256, 347}, {257, 347}, {271, 347}, {272, 347}, {313, 347}, {316, 347}, {344, 347}, {345, 347}, {371, 347}, {377, 347}, {419, 347}, {420, 347}, {101, 348}, {189, 348}, {191, 348}, {241, 348}, {242, 348}, {243, 348}, {247, 348}, {249, 348}, {314, 348}, {315, 348}, {376, 348}, {378, 348}, {189, 349}, {191, 349}, {208, 349}, {209, 349}, {248, 349}, {275, 349}, {276, 349}, {285, 349}, {286, 349}, {327, 349}, {328, 349}, {370, 349}, {376, 349}, {378, 349}, {190, 350}, {208, 350}, {209, 350}, {275, 350}, {276, 350}, {285, 350}, {286, 350}, {311, 350}, {327, 350}, {328, 350}, {369, 350}, {371, 350}, {377, 350}, {418, 350}, {419, 350}, {237, 351}, {238, 351}, {254, 351}, {311, 351}, {369, 351}, {372, 351}, {418, 351}, {419, 351}, {237, 352}, {238, 352}, {247, 352}, {248, 352}, {253, 352}, {255, 352}, {311, 352}, {331, 352}, {332, 352}, {370, 352}, {371, 352}, {381, 352}, {382, 352}, {449, 352}, {489, 352}, {490, 352}, {217, 353}, {246, 353}, {249, 353}, {253, 353}, {255, 353}, {295, 353}, {330, 353}, {332, 353}, {380, 353}, {383, 353}, {396, 353}, {413, 353}, {448, 353}, {450, 353}, {489, 353}, {490, 353}, {203, 354}, {204, 354}, {210, 354}, {211, 354}, {216, 354}, {218, 354}, {247, 354}, {248, 354}, {254, 354}, {261, 354}, {262, 354}, {295, 354}, {331, 354}, {356, 354}, {357, 354}, {358, 354}, {381, 354}, {382, 354}, {395, 354}, {397, 354}, {413, 354}, {448, 354}, {450, 354}, {471, 354}, {202, 355}, {205, 355}, {210, 355}, {211, 355}, {216, 355}, {218, 355}, {261, 355}, {262, 355}, {295, 355}, {395, 355}, {397, 355}, {413, 355}, {449, 355}, {459, 355}, {463, 355}, {464, 355}, {471, 355}, {175, 356}, {202, 356}, {204, 356}, {217, 356}, {354, 356}, {366, 356}, {372, 356}, {396, 356}, {410, 356}, {443, 356}, {444, 356}, {459, 356}, {462, 356}, {465, 356}, {471, 356}, {174, 357}, {176, 357}, {184, 357}, {185, 357}, {203, 357}, {254, 357}, {255, 357}, {275, 357}, {291, 357}, {292, 357}, {293, 357}, {297, 357}, {298, 357}, {299, 357}, {353, 357}, {355, 357}, {366, 357}, {372, 357}, {409, 357}, {411, 357}, {442, 357}, {445, 357}, {459, 357}, {463, 357}, {464, 357}, {174, 358}, {176, 358}, {184, 358}, {185, 358}, {230, 358}, {231, 358}, {254, 358}, {255, 358}, {274, 358}, {276, 358}, {338, 358}, {339, 358}, {340, 358}, {353, 358}, {356, 358}, {366, 358}, {372, 358}, {391, 358}, {392, 358}, {400, 358}, {401, 358}, {409, 358}, {411, 358}, {443, 358}, {444, 358}, {108, 359}, {175, 359}, {209, 359}, {210, 359}, {211, 359}, {230, 359}, {231, 359}, {274, 359}, {275, 359}, {295, 359}, {338, 359}, {341, 359}, {354, 359}, {355, 359}, {390, 359}, {393, 359}, {399, 359}, {402, 359}, {410, 359}, {424, 359}, {425, 359}, {455, 359}, {456, 359}, {457, 359}, {478, 359}, {479, 359}, {107, 360}, {109, 360}, {220, 360}, {270, 360}, {271, 360}, {295, 360}, {336, 360}, {368, 360}, {369, 360}, {370, 360}, {391, 360}, {392, 360}, {400, 360}, {401, 360}, {423, 360}, {425, 360}, {478, 360}, {479, 360}, {108, 361}, {109, 361}, {219, 361}, {221, 361}, {270, 361}, {271, 361}, {295, 361}, {336, 361}, {342, 361}, {424, 361}, {105, 362}, {219, 362}, {221, 362}, {318, 362}, {319, 362}, {336, 362}, {341, 362}, {396, 362}, {500, 362}, {82, 363}, {83, 363}, {84, 363}, {104, 363}, {106, 363}, {220, 363}, {317, 363}, {319, 363}, {339, 363}, {395, 363}, {397, 363}, {471, 363}, {499, 363}, {501, 363}, {87, 364}, {96, 364}, {97, 364}, {104, 364}, {106, 364}, {171, 364}, {172, 364}, {317, 364}, {318, 364}, {395, 364}, {397, 364}, {419, 364}, {420, 364}, {470, 364}, {472, 364}, {499, 364}, {501, 364}, {86, 365}, {88, 365}, {95, 365}, {97, 365}, {105, 365}, {171, 365}, {172, 365}, {215, 365}, {216, 365}, {270, 365}, {271, 365}, {396, 365}, {418, 365}, {421, 365}, {440, 365}, {441, 365}, {470, 365}, {472, 365}, {500, 365}, {86, 366}, {88, 366}, {96, 366}, {214, 366}, {217, 366}, {270, 366}, {272, 366}, {419, 366}, {420, 366}, {427, 366}, {439, 366}, {442, 366}, {471, 366}, {488, 366}, {87, 367}, {215, 367}, {216, 367}, {226, 367}, {271, 367}, {291, 367}, {292, 367}, {427, 367}, {440, 367}, {441, 367}, {453, 367}, {487, 367}, {489, 367}, {226, 368}, {227, 368}, {290, 368}, {293, 368}, {427, 368}, {453, 368}, {486, 368}, {489, 368}, {194, 369}, {195, 369}, {196, 369}, {220, 369}, {221, 369}, {225, 369}, {228, 369}, {290, 369}, {292, 369}, {453, 369}, {487, 369}, {488, 369}, {221, 370}, {224, 370}, {227, 370}, {228, 370}, {291, 370}, {404, 370}, {405, 370}, {461, 370}, {510, 370}, {511, 370}, {220, 371}, {221, 371}, {224, 371}, {404, 371}, {405, 371}, {449, 371}, {450, 371}, {451, 371}, {455, 371}, {456, 371}, {457, 371}, {460, 371}, {462, 371}, {510, 371}, {511, 371}, {198, 372}, {199, 372}, {200, 372}, {220, 372}, {221, 372}, {222, 372}, {440, 372}, {441, 372}, {461, 372}, {462, 372}, {222, 373}, {223, 373}, {224, 373}, {313, 373}, {314, 373}, {433, 373}, {434, 373}, {440, 373}, {441, 373}, {453, 373}, {181, 374}, {196, 374}, {202, 374}, {223, 374}, {224, 374}, {276, 374}, {313, 374}, {314, 374}, {394, 374}, {395, 374}, {433, 374}, {434, 374}, {453, 374}, {469, 374}, {470, 374}, {180, 375}, {182, 375}, {196, 375}, {202, 375}, {223, 375}, {224, 375}, {276, 375}, {394, 375}, {395, 375}, {417, 375}, {418, 375}, {419, 375}, {453, 375}, {468, 375}, {471, 375}, {180, 376}, {182, 376}, {196, 376}, {202, 376}, {229, 376}, {233, 376}, {234, 376}, {276, 376}, {469, 376}, {470, 376}, {498, 376}, {499, 376}, {500, 376}, {181, 377}, {228, 377}, {230, 377}, {232, 377}, {233, 377}, {234, 377}, {415, 377}, {421, 377}, {228, 378}, {231, 378}, {232, 378}, {233, 378}, {260, 378}, {261, 378}, {296, 378}, {348, 378}, {349, 378}, {359, 378}, {401, 378}, {402, 378}, {415, 378}, {421, 378}, {446, 378}, {447, 378}, {448, 378}, {209, 379}, {210, 379}, {229, 379}, {230, 379}, {234, 379}, {260, 379}, {261, 379}, {295, 379}, {297, 379}, {315, 379}, {347, 379}, {350, 379}, {359, 379}, {400, 379}, {403, 379}, {415, 379}, {421, 379}, {209, 380}, {210, 380}, {234, 380}, {294, 380}, {297, 380}, {314, 380}, {316, 380}, {347, 380}, {349, 380}, {359, 380}, {401, 380}, {402, 380}, {188, 381}, {189, 381}, {295, 381}, {296, 381}, {314, 381}, {317, 381}, {348, 381}, {417, 381}, {418, 381}, {419, 381}, {464, 381}, {188, 382}, {189, 382}, {215, 382}, {315, 382}, {316, 382}, {367, 382}, {368, 382}, {463, 382}, {465, 382}, {500, 382}, {501, 382}, {502, 382}, {214, 383}, {216, 383}, {366, 383}, {369, 383}, {463, 383}, {465, 383}, {500, 383}, {501, 383}, {502, 383}, {503, 383}, {214, 384}, {215, 384}, {367, 384}, {368, 384}, {464, 384}, {500, 384}, {503, 384}, {8, 385}, {9, 385}, {206, 385}, {207, 385}, {501, 385}, {502, 385}, {8, 386}, {9, 386}, {206, 386}, {207, 386}, {496, 386}, {497, 386}, {498, 386}, {501, 386}, {213, 387}, {214, 387}, {257, 387}, {351, 387}, {352, 387}, {372, 387}, {373, 387}, {497, 387}, {499, 387}, {500, 387}, {501, 387}, {213, 388}, {215, 388}, {238, 388}, {239, 388}, {256, 388}, {258, 388}, {351, 388}, {352, 388}, {372, 388}, {373, 388}, {383, 388}, {384, 388}, {498, 388}, {499, 388}, {500, 388}, {501, 388}, {502, 388}, {214, 389}, {237, 389}, {240, 389}, {255, 389}, {258, 389}, {383, 389}, {384, 389}, {490, 389}, {499, 389}, {503, 389}, {238, 390}, {239, 390}, {256, 390}, {257, 390}, {489, 390}, {490, 390}, {498, 390}, {499, 390}, {501, 390}, {502, 390}, {335, 391}, {488, 391}, {490, 391}, {492, 391}, {493, 391}, {501, 391}, {223, 392}, {224, 392}, {225, 392}, {284, 392}, {285, 392}, {333, 392}, {334, 392}, {335, 392}, {336, 392}, {337, 392}, {350, 392}, {351, 392}, {488, 392}, {490, 392}, {491, 392}, {493, 392}, {498, 392}, {499, 392}, {500, 392}, {227, 393}, {231, 393}, {232, 393}, {233, 393}, {234, 393}, {235, 393}, {236, 393}, {284, 393}, {285, 393}, {333, 393}, {334, 393}, {336, 393}, {337, 393}, {338, 393}, {350, 393}, {351, 393}, {487, 393}, {494, 393}, {499, 393}, {194, 394}, {195, 394}, {221, 394}, {230, 394}, {237, 394}, {333, 394}, {334, 394}, {337, 394}, {415, 394}, {416, 394}, {486, 394}, {193, 395}, {196, 395}, {221, 395}, {223, 395}, {228, 395}, {230, 395}, {234, 395}, {236, 395}, {237, 395}, {310, 395}, {311, 395}, {312, 395}, {316, 395}, {317, 395}, {318, 395}, {334, 395}, {335, 395}, {336, 395}, {415, 395}, {416, 395}, {485, 395}, {486, 395}, {491, 395}, {194, 396}, {195, 396}, {228, 396}, {231, 396}, {232, 396}, {238, 396}, {335, 396}, {440, 396}, {441, 396}, {484, 396}, {485, 396}, {486, 396}, {490, 396}, {491, 396}, {507, 396}, {508, 396}, {215, 397}, {221, 397}, {222, 397}, {224, 397}, {234, 397}, {235, 397}, {237, 397}, {238, 397}, {314, 397}, {327, 397}, {440, 397}, {441, 397}, {485, 397}, {486, 397}, {489, 397}, {490, 397}, {491, 397}, {507, 397}, {508, 397}, {13, 398}, {182, 398}, {183, 398}, {214, 398}, {215, 398}, {216, 398}, {221, 398}, {222, 398}, {225, 398}, {226, 398}, {227, 398}, {234, 398}, {238, 398}, {314, 398}, {326, 398}, {336, 398}, {337, 398}, {489, 398}, {12, 399}, {14, 399}, {181, 399}, {183, 399}, {184, 399}, {213, 399}, {214, 399}, {216, 399}, {217, 399}, {235, 399}, {236, 399}, {237, 399}, {314, 399}, {329, 399}, {335, 399}, {338, 399}, {13, 400}, {14, 400}, {181, 400}, {183, 400}, {214, 400}, {215, 400}, {216, 400}, {218, 400}, {324, 400}, {325, 400}, {326, 400}, {336, 400}, {337, 400}, {357, 400}, {358, 400}, {182, 401}, {185, 401}, {215, 401}, {218, 401}, {219, 401}, {220, 401}, {221, 401}, {222, 401}, {284, 401}, {285, 401}, {286, 401}, {323, 401}, {329, 401}, {357, 401}, {358, 401}, {394, 401}, {395, 401}, {486, 401}, {503, 401}, {504, 401}, {183, 402}, {184, 402}, {185, 402}, {188, 402}, {219, 402}, {220, 402}, {221, 402}, {222, 402}, {223, 402}, {323, 402}, {326, 402}, {327, 402}, {328, 402}, {345, 402}, {346, 402}, {347, 402}, {393, 402}, {396, 402}, {485, 402}, {487, 402}, {503, 402}, {504, 402}, {189, 403}, {219, 403}, {220, 403}, {223, 403}, {227, 403}, {304, 403}, {324, 403}, {325, 403}, {326, 403}, {394, 403}, {395, 403}, {435, 403}, {436, 403}, {437, 403}, {486, 403}, {487, 403}, {185, 404}, {189, 404}, {220, 404}, {221, 404}, {222, 404}, {226, 404}, {228, 404}, {304, 404}, {349, 404}, {423, 404}, {424, 404}, {172, 405}, {173, 405}, {174, 405}, {185, 405}, {186, 405}, {187, 405}, {188, 405}, {189, 405}, {221, 405}, {227, 405}, {228, 405}, {304, 405}, {349, 405}, {405, 405}, {406, 405}, {422, 405}, {425, 405}, {0, 406}, {163, 406}, {164, 406}, {165, 406}, {186, 406}, {188, 406}, {265, 406}, {300, 406}, {301, 406}, {307, 406}, {308, 406}, {349, 406}, {405, 406}, {407, 406}, {423, 406}, {424, 406}, {1, 407}, {187, 407}, {264, 407}, {266, 407}, {300, 407}, {301, 407}, {307, 407}, {308, 407}, {312, 407}, {313, 407}, {405, 407}, {511, 407}, {0, 408}, {1, 408}, {264, 408}, {266, 408}, {312, 408}, {313, 408}, {324, 408}, {325, 408}, {327, 408}, {373, 408}, {374, 408}, {265, 409}, {320, 409}, {325, 409}, {327, 409}, {328, 409}, {335, 409}, {336, 409}, {368, 409}, {369, 409}, {373, 409}, {374, 409}, {282, 410}, {283, 410}, {319, 410}, {327, 410}, {328, 410}, {335, 410}, {336, 410}, {368, 410}, {369, 410}, {481, 410}, {482, 410}, {254, 411}, {255, 411}, {256, 411}, {282, 411}, {283, 411}, {304, 411}, {305, 411}, {319, 411}, {324, 411}, {480, 411}, {483, 411}, {487, 411}, {488, 411}, {9, 412}, {10, 412}, {11, 412}, {304, 412}, {305, 412}, {319, 412}, {326, 412}, {327, 412}, {470, 412}, {471, 412}, {481, 412}, {482, 412}, {487, 412}, {488, 412}, {166, 413}, {320, 413}, {324, 413}, {325, 413}, {326, 413}, {327, 413}, {415, 413}, {417, 413}, {470, 413}, {471, 413}, {166, 414}, {308, 414}, {309, 414}, {326, 414}, {327, 414}, {390, 414}, {391, 414}, {392, 414}, {418, 414}, {166, 415}, {224, 415}, {225, 415}, {308, 415}, {309, 415}, {322, 415}, {325, 415}, {347, 415}, {348, 415}, {349, 415}, {390, 415}, {393, 415}, {411, 415}, {418, 415}, {224, 416}, {225, 416}, {324, 416}, {388, 416}, {393, 416}, {410, 416}, {419, 416}, {215, 417}, {216, 417}, {217, 417}, {388, 417}, {390, 417}, {391, 417}, {393, 417}, {394, 417}, {411, 417}, {413, 417}, {414, 417}, {415, 417}, {416, 417}, {417, 417}, {418, 417}, {469, 417}, {381, 418}, {388, 418}, {391, 418}, {393, 418}, {394, 418}, {417, 418}, {468, 418}, {470, 418}, {157, 419}, {182, 419}, {183, 419}, {213, 419}, {219, 419}, {381, 419}, {389, 419}, {390, 419}, {391, 419}, {392, 419}, {468, 419}, {470, 419}, {156, 420}, {158, 420}, {181, 420}, {183, 420}, {213, 420}, {219, 420}, {250, 420}, {251, 420}, {316, 420}, {317, 420}, {381, 420}, {389, 420}, {390, 420}, {391, 420}, {415, 420}, {469, 420}, {156, 421}, {158, 421}, {182, 421}, {213, 421}, {219, 421}, {250, 421}, {251, 421}, {316, 421}, {317, 421}, {384, 421}, {414, 421}, {415, 421}, {416, 421}, {465, 421}, {466, 421}, {107, 422}, {108, 422}, {149, 422}, {157, 422}, {330, 422}, {331, 422}, {383, 422}, {385, 422}, {394, 422}, {411, 422}, {412, 422}, {416, 422}, {465, 422}, {467, 422}, {483, 422}, {484, 422}, {489, 422}, {490, 422}, {106, 423}, {108, 423}, {149, 423}, {215, 423}, {216, 423}, {217, 423}, {329, 423}, {331, 423}, {383, 423}, {385, 423}, {395, 423}, {396, 423}, {411, 423}, {414, 423}, {461, 423}, {462, 423}, {466, 423}, {483, 423}, {485, 423}, {488, 423}, {491, 423}, {107, 424}, {149, 424}, {210, 424}, {211, 424}, {330, 424}, {334, 424}, {384, 424}, {394, 424}, {395, 424}, {412, 424}, {413, 424}, {414, 424}, {428, 424}, {429, 424}, {461, 424}, {462, 424}, {484, 424}, {489, 424}, {490, 424}, {205, 425}, {206, 425}, {210, 425}, {211, 425}, {288, 425}, {289, 425}, {303, 425}, {304, 425}, {305, 425}, {333, 425}, {335, 425}, {428, 425}, {429, 425}, {205, 426}, {207, 426}, {281, 426}, {282, 426}, {288, 426}, {289, 426}, {333, 426}, {334, 426}, {397, 426}, {138, 427}, {143, 427}, {144, 427}, {206, 427}, {228, 427}, {229, 427}, {280, 427}, {283, 427}, {397, 427}, {399, 427}, {419, 427}, {420, 427}, {492, 427}, {493, 427}, {137, 428}, {139, 428}, {143, 428}, {144, 428}, {228, 428}, {229, 428}, {281, 428}, {282, 428}, {397, 428}, {398, 428}, {419, 428}, {420, 428}, {492, 428}, {493, 428}, {107, 429}, {137, 429}, {139, 429}, {174, 429}, {234, 429}, {235, 429}, {465, 429}, {466, 429}, {467, 429}, {74, 430}, {75, 430}, {106, 430}, {108, 430}, {138, 430}, {173, 430}, {175, 430}, {233, 430}, {236, 430}, {74, 431}, {75, 431}, {106, 431}, {109, 431}, {173, 431}, {175, 431}, {234, 431}, {235, 431}, {298, 431}, {107, 432}, {108, 432}, {174, 432}, {298, 432}, {85, 433}, {86, 433}, {137, 433}, {138, 433}, {144, 433}, {145, 433}, {298, 433}, {395, 433}, {396, 433}, {85, 434}, {86, 434}, {137, 434}, {138, 434}, {144, 434}, {145, 434}, {395, 434}, {396, 434}, {102, 435}, {103, 435}, {294, 435}, {295, 435}, {296, 435}, {300, 435}, {301, 435}, {302, 435}, {101, 436}, {103, 436}, {114, 436}, {115, 436}, {120, 436}, {171, 436}, {172, 436}, {173, 436}, {309, 436}, {310, 436}, {360, 436}, {361, 436}, {372, 436}, {373, 436}, {385, 436}, {101, 437}, {102, 437}, {114, 437}, {115, 437}, {120, 437}, {230, 437}, {231, 437}, {232, 437}, {298, 437}, {308, 437}, {311, 437}, {360, 437}, {361, 437}, {372, 437}, {373, 437}, {384, 437}, {386, 437}, {99, 438}, {100, 438}, {120, 438}, {175, 438}, {241, 438}, {242, 438}, {289, 438}, {290, 438}, {298, 438}, {309, 438}, {310, 438}, {384, 438}, {386, 438}, {412, 438}, {413, 438}, {414, 438}, {90, 439}, {91, 439}, {98, 439}, {100, 439}, {106, 439}, {107, 439}, {175, 439}, {234, 439}, {235, 439}, {241, 439}, {242, 439}, {282, 439}, {289, 439}, {290, 439}, {298, 439}, {385, 439}, {488, 439}, {489, 439}, {90, 440}, {91, 440}, {98, 440}, {99, 440}, {105, 440}, {107, 440}, {141, 440}, {142, 440}, {144, 440}, {145, 440}, {175, 440}, {233, 440}, {236, 440}, {282, 440}, {488, 440}, {490, 440}, {105, 441}, {106, 441}, {141, 441}, {142, 441}, {144, 441}, {145, 441}, {160, 441}, {161, 441}, {189, 441}, {233, 441}, {235, 441}, {282, 441}, {299, 441}, {477, 441}, {478, 441}, {489, 441}, {103, 442}, {104, 442}, {159, 442}, {162, 442}, {171, 442}, {172, 442}, {173, 442}, {188, 442}, {190, 442}, {234, 442}, {298, 442}, {300, 442}, {395, 442}, {396, 442}, {409, 442}, {476, 442}, {479, 442}, {102, 443}, {104, 443}, {159, 443}, {161, 443}, {188, 443}, {190, 443}, {278, 443}, {279, 443}, {280, 443}, {284, 443}, {285, 443}, {286, 443}, {298, 443}, {300, 443}, {395, 443}, {396, 443}, {408, 443}, {410, 443}, {465, 443}, {466, 443}, {467, 443}, {476, 443}, {479, 443}, {102, 444}, {103, 444}, {128, 444}, {129, 444}, {130, 444}, {160, 444}, {189, 444}, {299, 444}, {308, 444}, {364, 444}, {365, 444}, {408, 444}, {411, 444}, {477, 444}, {478, 444}, {282, 445}, {307, 445}, {309, 445}, {364, 445}, {365, 445}, {383, 445}, {384, 445}, {385, 445}, {409, 445}, {410, 445}, {449, 445}, {450, 445}, {126, 446}, {132, 446}, {165, 446}, {184, 446}, {237, 446}, {238, 446}, {282, 446}, {308, 446}, {309, 446}, {315, 446}, {316, 446}, {448, 446}, {451, 446}, {126, 447}, {132, 447}, {165, 447}, {184, 447}, {237, 447}, {238, 447}, {282, 447}, {314, 447}, {317, 447}, {336, 447}, {337, 447}, {449, 447}, {450, 447}, {126, 448}, {132, 448}, {138, 448}, {139, 448}, {165, 448}, {184, 448}, {201, 448}, {208, 448}, {209, 448}, {230, 448}, {231, 448}, {315, 448}, {316, 448}, {336, 448}, {337, 448}, {138, 449}, {139, 449}, {160, 449}, {200, 449}, {202, 449}, {208, 449}, {209, 449}, {229, 449}, {231, 449}, {128, 450}, {129, 450}, {130, 450}, {160, 450}, {180, 450}, {181, 450}, {182, 450}, {186, 450}, {187, 450}, {188, 450}, {200, 450}, {202, 450}, {230, 450}, {160, 451}, {201, 451}, {387, 451}, {395, 451}, {184, 452}, {386, 452}, {388, 452}, {394, 452}, {396, 452}, {184, 453}, {196, 453}, {197, 453}, {205, 453}, {206, 453}, {385, 453}, {388, 453}, {394, 453}, {396, 453}, {184, 454}, {195, 454}, {198, 454}, {204, 454}, {207, 454}, {354, 454}, {355, 454}, {386, 454}, {387, 454}, {395, 454}, {196, 455}, {197, 455}, {205, 455}, {206, 455}, {354, 455}, {356, 455}, {320, 456}, {321, 456}, {355, 456}, {356, 456}, {382, 456}, {383, 456}, {384, 456}, {450, 456}, {451, 456}, {201, 457}, {319, 457}, {322, 457}, {395, 457}, {396, 457}, {450, 457}, {451, 457}, {183, 458}, {200, 458}, {202, 458}, {222, 458}, {320, 458}, {321, 458}, {386, 458}, {395, 458}, {396, 458}, {133, 459}, {134, 459}, {177, 459}, {183, 459}, {200, 459}, {202, 459}, {222, 459}, {386, 459}, {466, 459}, {467, 459}, {52, 460}, {132, 460}, {135, 460}, {176, 460}, {178, 460}, {183, 460}, {201, 460}, {222, 460}, {344, 460}, {345, 460}, {386, 460}, {465, 460}, {468, 460}, {50, 461}, {52, 461}, {133, 461}, {134, 461}, {158, 461}, {159, 461}, {175, 461}, {178, 461}, {344, 461}, {345, 461}, {441, 461}, {442, 461}, {466, 461}, {467, 461}, {51, 462}, {52, 462}, {98, 462}, {99, 462}, {158, 462}, {159, 462}, {176, 462}, {177, 462}, {292, 462}, {440, 462}, {443, 462}, {98, 463}, {99, 463}, {204, 463}, {205, 463}, {292, 463}, {441, 463}, {442, 463}, {173, 464}, {174, 464}, {203, 464}, {206, 464}, {292, 464}, {109, 465}, {110, 465}, {119, 465}, {120, 465}, {130, 465}, {131, 465}, {173, 465}, {174, 465}, {204, 465}, {205, 465}, {314, 465}, {95, 466}, {108, 466}, {111, 466}, {119, 466}, {120, 466}, {130, 466}, {131, 466}, {288, 466}, {289, 466}, {290, 466}, {294, 466}, {295, 466}, {296, 466}, {313, 466}, {315, 466}, {94, 467}, {96, 467}, {109, 467}, {110, 467}, {313, 467}, {315, 467}, {457, 467}, {458, 467}, {94, 468}, {96, 468}, {210, 468}, {292, 468}, {314, 468}, {348, 468}, {349, 468}, {457, 468}, {458, 468}, {95, 469}, {209, 469}, {211, 469}, {292, 469}, {348, 469}, {349, 469}, {165, 470}, {166, 470}, {209, 470}, {211, 470}, {228, 470}, {229, 470}, {292, 470}, {113, 471}, {114, 471}, {164, 471}, {167, 471}, {210, 471}, {227, 471}, {230, 471}, {113, 472}, {114, 472}, {134, 472}, {135, 472}, {136, 472}, {164, 472}, {167, 472}, {222, 472}, {223, 472}, {228, 472}, {229, 472}, {165, 473}, {166, 473}, {222, 473}, {223, 473}, {245, 473}, {313, 473}, {314, 473}, {215, 474}, {216, 474}, {232, 474}, {233, 474}, {234, 474}, {243, 474}, {244, 474}, {313, 474}, {314, 474}, {417, 474}, {418, 474}, {215, 475}, {216, 475}, {243, 475}, {244, 475}, {246, 475}, {247, 475}, {248, 475}, {416, 475}, {419, 475}, {463, 475}, {464, 475}, {465, 475}, {475, 475}, {476, 475}, {477, 475}, {230, 476}, {236, 476}, {243, 476}, {244, 476}, {246, 476}, {258, 476}, {259, 476}, {260, 476}, {416, 476}, {419, 476}, {111, 477}, {139, 477}, {230, 477}, {236, 477}, {247, 477}, {248, 477}, {312, 477}, {313, 477}, {417, 477}, {418, 477}, {461, 477}, {467, 477}, {110, 478}, {112, 478}, {138, 478}, {140, 478}, {230, 478}, {236, 478}, {250, 478}, {251, 478}, {311, 478}, {314, 478}, {338, 478}, {339, 478}, {461, 478}, {467, 478}, {111, 479}, {138, 479}, {140, 479}, {203, 479}, {204, 479}, {205, 479}, {249, 479}, {250, 479}, {251, 479}, {277, 479}, {278, 479}, {279, 479}, {312, 479}, {313, 479}, {338, 479}, {340, 479}, {461, 479}, {467, 479}, {120, 480}, {121, 480}, {123, 480}, {124, 480}, {139, 480}, {199, 480}, {232, 480}, {233, 480}, {234, 480}, {249, 480}, {250, 480}, {251, 480}, {270, 480}, {271, 480}, {339, 480}, {340, 480}, {486, 480}, {487, 480}, {120, 481}, {121, 481}, {123, 481}, {124, 481}, {198, 481}, {200, 481}, {229, 481}, {248, 481}, {250, 481}, {270, 481}, {271, 481}, {304, 481}, {305, 481}, {424, 481}, {425, 481}, {438, 481}, {463, 481}, {464, 481}, {465, 481}, {469, 481}, {470, 481}, {486, 481}, {487, 481}, {102, 482}, {103, 482}, {198, 482}, {200, 482}, {228, 482}, {230, 482}, {248, 482}, {249, 482}, {304, 482}, {305, 482}, {424, 482}, {425, 482}, {437, 482}, {439, 482}, {469, 482}, {470, 482}, {101, 483}, {104, 483}, {199, 483}, {228, 483}, {230, 483}, {249, 483}, {437, 483}, {439, 483}, {102, 484}, {103, 484}, {229, 484}, {307, 484}, {308, 484}, {433, 484}, {438, 484}, {99, 485}, {194, 485}, {195, 485}, {307, 485}, {308, 485}, {432, 485}, {434, 485}, {99, 486}, {193, 486}, {196, 486}, {235, 486}, {236, 486}, {237, 486}, {432, 486}, {433, 486}, {99, 487}, {163, 487}, {164, 487}, {194, 487}, {195, 487}, {272, 487}, {273, 487}, {459, 487}, {153, 488}, {163, 488}, {164, 488}, {198, 488}, {199, 488}, {200, 488}, {233, 488}, {272, 488}, {274, 488}, {458, 488}, {460, 488}, {146, 489}, {147, 489}, {152, 489}, {154, 489}, {220, 489}, {221, 489}, {233, 489}, {273, 489}, {280, 489}, {281, 489}, {282, 489}, {458, 489}, {459, 489}, {146, 490}, {147, 490}, {152, 490}, {154, 490}, {196, 490}, {202, 490}, {219, 490}, {222, 490}, {233, 490}, {245, 490}, {451, 490}, {452, 490}, {87, 491}, {88, 491}, {153, 491}, {196, 491}, {202, 491}, {220, 491}, {221, 491}, {245, 491}, {451, 491}, {452, 491}, {87, 492}, {88, 492}, {108, 492}, {109, 492}, {110, 492}, {196, 492}, {202, 492}, {235, 492}, {236, 492}, {237, 492}, {245, 492}, {437, 492}, {465, 492}, {466, 492}, {155, 493}, {156, 493}, {437, 493}, {465, 493}, {466, 493}, {106, 494}, {112, 494}, {121, 494}, {133, 494}, {134, 494}, {154, 494}, {157, 494}, {198, 494}, {199, 494}, {200, 494}, {268, 494}, {437, 494}, {106, 495}, {112, 495}, {121, 495}, {133, 495}, {134, 495}, {154, 495}, {156, 495}, {267, 495}, {269, 495}, {285, 495}, {286, 495}, {106, 496}, {112, 496}, {121, 496}, {132, 496}, {155, 496}, {219, 496}, {220, 496}, {267, 496}, {269, 496}, {285, 496}, {286, 496}, {131, 497}, {132, 497}, {134, 497}, {164, 497}, {218, 497}, {221, 497}, {234, 497}, {235, 497}, {268, 497}, {464, 497}, {465, 497}, {108, 498}, {109, 498}, {110, 498}, {117, 498}, {118, 498}, {119, 498}, {131, 498}, {164, 498}, {219, 498}, {220, 498}, {233, 498}, {236, 498}, {463, 498}, {466, 498}, {131, 499}, {155, 499}, {164, 499}, {234, 499}, {235, 499}, {464, 499}, {466, 499}, {472, 499}, {473, 499}, {79, 500}, {80, 500}, {81, 500}, {131, 500}, {132, 500}, {133, 500}, {155, 500}, {159, 500}, {160, 500}, {465, 500}, {471, 500}, {474, 500}, {128, 501}, {129, 501}, {131, 501}, {132, 501}, {150, 501}, {151, 501}, {155, 501}, {158, 501}, {161, 501}, {228, 501}, {229, 501}, {472, 501}, {473, 501}, {77, 502}, {129, 502}, {131, 502}, {150, 502}, {151, 502}, {159, 502}, {160, 502}, {228, 502}, {229, 502}, {468, 502}, {469, 502}, {77, 503}, {127, 503}, {128, 503}, {129, 503}, {130, 503}, {160, 503}, {161, 503}, {468, 503}, {469, 503}, {77, 504}, {126, 504}, {161, 504}, {162, 504}, {125, 505}, {130, 505}, {160, 505}, {163, 505}, {218, 505}, {219, 505}, {254, 505}, {276, 505}, {277, 505}, {74, 506}, {79, 506}, {80, 506}, {81, 506}, {126, 506}, {130, 506}, {131, 506}, {160, 506}, {162, 506}, {163, 506}, {218, 506}, {219, 506}, {254, 506}, {276, 506}, {277, 506}, {73, 507}, {75, 507}, {109, 507}, {110, 507}, {126, 507}, {127, 507}, {130, 507}, {163, 507}, {164, 507}, {254, 507}, {73, 508}, {75, 508}, {108, 508}, {111, 508}, {127, 508}, {130, 508}, {151, 508}, {152, 508}, {159, 508}, {160, 508}, {163, 508}, {164, 508}, {266, 508}, {74, 509}, {101, 509}, {102, 509}, {108, 509}, {110, 509}, {114, 509}, {115, 509}, {116, 509}, {130, 509}, {151, 509}, {152, 509}, {159, 509}, {160, 509}, {266, 509}, {101, 510}, {102, 510}, {109, 510}, {127, 510}, {130, 510}, {137, 510}, {138, 510}, {139, 510}, {158, 510}, {161, 510}, {164, 510}, {266, 510}, {88, 511}, {89, 511}, {128, 511}, {129, 511}, {137, 511}, {140, 511}, {141, 511}, {157, 511}, {159, 511},
+ },
+ }},
+
+ {"512x512x113-1000", args{
+ p: golParams{
+ turns: 1000,
+ threads: 247,
+ imageWidth: 512,
+ imageHeight: 512,
+ },
+ expectedAlive: []cell{
+ {88, 0}, {90, 0}, {137, 0}, {142, 0}, {143, 0}, {157, 0}, {158, 0}, {166, 0}, {89, 1}, {90, 1}, {137, 1}, {161, 1}, {138, 2}, {142, 2}, {160, 2}, {161, 2}, {164, 2}, {165, 2}, {166, 2}, {268, 2}, {269, 2}, {127, 3}, {139, 3}, {160, 3}, {161, 3}, {164, 3}, {268, 3}, {269, 3}, {444, 3}, {445, 3}, {495, 3}, {496, 3}, {126, 4}, {128, 4}, {140, 4}, {142, 4}, {159, 4}, {162, 4}, {165, 4}, {166, 4}, {167, 4}, {168, 4}, {444, 4}, {445, 4}, {495, 4}, {496, 4}, {126, 5}, {128, 5}, {129, 5}, {139, 5}, {140, 5}, {144, 5}, {160, 5}, {161, 5}, {165, 5}, {167, 5}, {168, 5}, {169, 5}, {128, 6}, {140, 6}, {144, 6}, {145, 6}, {150, 6}, {151, 6}, {159, 6}, {161, 6}, {164, 6}, {165, 6}, {168, 6}, {170, 6}, {268, 6}, {269, 6}, {145, 7}, {149, 7}, {152, 7}, {159, 7}, {160, 7}, {163, 7}, {166, 7}, {168, 7}, {169, 7}, {268, 7}, {269, 7}, {141, 8}, {144, 8}, {150, 8}, {151, 8}, {158, 8}, {161, 8}, {162, 8}, {166, 8}, {167, 8}, {168, 8}, {169, 8}, {122, 9}, {142, 9}, {143, 9}, {144, 9}, {158, 9}, {159, 9}, {162, 9}, {79, 10}, {80, 10}, {121, 10}, {123, 10}, {158, 10}, {159, 10}, {163, 10}, {164, 10}, {79, 11}, {80, 11}, {123, 11}, {440, 11}, {441, 11}, {122, 12}, {123, 12}, {125, 12}, {440, 12}, {441, 12}, {122, 13}, {127, 13}, {123, 14}, {127, 14}, {160, 14}, {161, 14}, {163, 14}, {264, 14}, {265, 14}, {123, 15}, {126, 15}, {161, 15}, {162, 15}, {163, 15}, {264, 15}, {265, 15}, {505, 15}, {506, 15}, {162, 16}, {504, 16}, {507, 16}, {504, 17}, {506, 17}, {83, 18}, {84, 18}, {490, 18}, {491, 18}, {492, 18}, {505, 18}, {83, 19}, {84, 19}, {133, 21}, {134, 21}, {450, 21}, {451, 21}, {132, 22}, {134, 22}, {191, 22}, {192, 22}, {449, 22}, {451, 22}, {132, 23}, {133, 23}, {191, 23}, {193, 23}, {449, 23}, {450, 23}, {192, 24}, {193, 24}, {274, 24}, {275, 24}, {273, 25}, {275, 25}, {273, 26}, {274, 26}, {476, 27}, {477, 27}, {439, 28}, {440, 28}, {447, 28}, {448, 28}, {472, 28}, {473, 28}, {476, 28}, {477, 28}, {438, 29}, {440, 29}, {447, 29}, {448, 29}, {453, 29}, {454, 29}, {472, 29}, {473, 29}, {439, 30}, {453, 30}, {454, 30}, {112, 31}, {458, 31}, {459, 31}, {489, 31}, {490, 31}, {508, 31}, {509, 31}, {510, 31}, {111, 32}, {113, 32}, {450, 32}, {451, 32}, {458, 32}, {459, 32}, {489, 32}, {490, 32}, {111, 33}, {113, 33}, {182, 33}, {183, 33}, {450, 33}, {451, 33}, {112, 34}, {182, 34}, {183, 34}, {79, 35}, {388, 35}, {389, 35}, {78, 36}, {80, 36}, {116, 36}, {117, 36}, {388, 36}, {390, 36}, {447, 36}, {448, 36}, {78, 37}, {81, 37}, {115, 37}, {118, 37}, {389, 37}, {391, 37}, {425, 37}, {426, 37}, {447, 37}, {448, 37}, {79, 38}, {80, 38}, {116, 38}, {117, 38}, {156, 38}, {390, 38}, {425, 38}, {426, 38}, {156, 39}, {382, 39}, {383, 39}, {112, 40}, {156, 40}, {229, 40}, {230, 40}, {382, 40}, {383, 40}, {98, 41}, {111, 41}, {113, 41}, {229, 41}, {230, 41}, {98, 42}, {111, 42}, {113, 42}, {152, 42}, {153, 42}, {154, 42}, {158, 42}, {159, 42}, {160, 42}, {173, 42}, {174, 42}, {396, 42}, {98, 43}, {112, 43}, {173, 43}, {174, 43}, {317, 43}, {318, 43}, {395, 43}, {397, 43}, {458, 43}, {504, 43}, {505, 43}, {150, 44}, {156, 44}, {317, 44}, {318, 44}, {395, 44}, {396, 44}, {457, 44}, {459, 44}, {504, 44}, {505, 44}, {149, 45}, {151, 45}, {156, 45}, {456, 45}, {459, 45}, {149, 46}, {150, 46}, {156, 46}, {230, 46}, {231, 46}, {457, 46}, {458, 46}, {212, 47}, {213, 47}, {230, 47}, {231, 47}, {419, 47}, {420, 47}, {421, 47}, {497, 47}, {498, 47}, {107, 48}, {108, 48}, {146, 48}, {174, 48}, {175, 48}, {176, 48}, {212, 48}, {213, 48}, {497, 48}, {498, 48}, {107, 49}, {108, 49}, {146, 49}, {50, 50}, {51, 50}, {146, 50}, {172, 50}, {178, 50}, {426, 50}, {50, 51}, {51, 51}, {149, 51}, {150, 51}, {172, 51}, {178, 51}, {214, 51}, {386, 51}, {387, 51}, {425, 51}, {427, 51}, {79, 52}, {80, 52}, {148, 52}, {151, 52}, {172, 52}, {178, 52}, {214, 52}, {228, 52}, {229, 52}, {385, 52}, {388, 52}, {426, 52}, {437, 52}, {438, 52}, {79, 53}, {81, 53}, {94, 53}, {95, 53}, {149, 53}, {150, 53}, {214, 53}, {228, 53}, {229, 53}, {386, 53}, {387, 53}, {437, 53}, {438, 53}, {458, 53}, {80, 54}, {81, 54}, {94, 54}, {96, 54}, {122, 54}, {123, 54}, {175, 54}, {176, 54}, {452, 54}, {453, 54}, {458, 54}, {95, 55}, {96, 55}, {122, 55}, {123, 55}, {174, 55}, {177, 55}, {451, 55}, {454, 55}, {458, 55}, {48, 56}, {49, 56}, {68, 56}, {69, 56}, {175, 56}, {176, 56}, {248, 56}, {249, 56}, {250, 56}, {381, 56}, {382, 56}, {451, 56}, {454, 56}, {48, 57}, {49, 57}, {68, 57}, {69, 57}, {112, 57}, {113, 57}, {380, 57}, {383, 57}, {452, 57}, {453, 57}, {111, 58}, {114, 58}, {381, 58}, {382, 58}, {111, 59}, {113, 59}, {152, 59}, {153, 59}, {237, 59}, {238, 59}, {112, 60}, {117, 60}, {118, 60}, {124, 60}, {125, 60}, {152, 60}, {153, 60}, {237, 60}, {238, 60}, {496, 60}, {117, 61}, {118, 61}, {124, 61}, {125, 61}, {173, 61}, {174, 61}, {212, 61}, {213, 61}, {408, 61}, {409, 61}, {448, 61}, {449, 61}, {450, 61}, {471, 61}, {472, 61}, {484, 61}, {495, 61}, {497, 61}, {61, 62}, {62, 62}, {63, 62}, {172, 62}, {175, 62}, {212, 62}, {213, 62}, {276, 62}, {408, 62}, {410, 62}, {442, 62}, {443, 62}, {471, 62}, {472, 62}, {483, 62}, {485, 62}, {495, 62}, {497, 62}, {53, 63}, {54, 63}, {55, 63}, {97, 63}, {98, 63}, {121, 63}, {122, 63}, {141, 63}, {173, 63}, {174, 63}, {276, 63}, {409, 63}, {442, 63}, {444, 63}, {483, 63}, {485, 63}, {496, 63}, {97, 64}, {98, 64}, {120, 64}, {123, 64}, {129, 64}, {130, 64}, {141, 64}, {191, 64}, {276, 64}, {342, 64}, {435, 64}, {436, 64}, {443, 64}, {467, 64}, {468, 64}, {484, 64}, {51, 65}, {121, 65}, {123, 65}, {128, 65}, {131, 65}, {141, 65}, {190, 65}, {192, 65}, {233, 65}, {234, 65}, {258, 65}, {259, 65}, {313, 65}, {314, 65}, {342, 65}, {435, 65}, {436, 65}, {466, 65}, {469, 65}, {51, 66}, {93, 66}, {94, 66}, {95, 66}, {122, 66}, {129, 66}, {130, 66}, {155, 66}, {156, 66}, {190, 66}, {192, 66}, {232, 66}, {235, 66}, {257, 66}, {260, 66}, {278, 66}, {279, 66}, {280, 66}, {291, 66}, {292, 66}, {298, 66}, {299, 66}, {300, 66}, {313, 66}, {314, 66}, {342, 66}, {467, 66}, {468, 66}, {51, 67}, {154, 67}, {157, 67}, {191, 67}, {233, 67}, {234, 67}, {258, 67}, {259, 67}, {291, 67}, {293, 67}, {125, 68}, {155, 68}, {156, 68}, {292, 68}, {332, 68}, {333, 68}, {338, 68}, {339, 68}, {340, 68}, {344, 68}, {345, 68}, {346, 68}, {435, 68}, {436, 68}, {124, 69}, {126, 69}, {144, 69}, {145, 69}, {331, 69}, {334, 69}, {434, 69}, {437, 69}, {447, 69}, {448, 69}, {90, 70}, {124, 70}, {126, 70}, {144, 70}, {145, 70}, {310, 70}, {311, 70}, {332, 70}, {333, 70}, {342, 70}, {361, 70}, {362, 70}, {376, 70}, {377, 70}, {435, 70}, {437, 70}, {446, 70}, {448, 70}, {89, 71}, {91, 71}, {125, 71}, {204, 71}, {205, 71}, {300, 71}, {310, 71}, {311, 71}, {342, 71}, {361, 71}, {362, 71}, {376, 71}, {377, 71}, {399, 71}, {400, 71}, {401, 71}, {436, 71}, {445, 71}, {447, 71}, {74, 72}, {75, 72}, {76, 72}, {80, 72}, {81, 72}, {82, 72}, {88, 72}, {91, 72}, {121, 72}, {204, 72}, {205, 72}, {299, 72}, {301, 72}, {342, 72}, {446, 72}, {89, 73}, {90, 73}, {120, 73}, {122, 73}, {173, 73}, {174, 73}, {187, 73}, {188, 73}, {300, 73}, {310, 73}, {311, 73}, {365, 73}, {366, 73}, {485, 73}, {486, 73}, {72, 74}, {78, 74}, {120, 74}, {122, 74}, {172, 74}, {175, 74}, {187, 74}, {188, 74}, {245, 74}, {246, 74}, {247, 74}, {309, 74}, {312, 74}, {324, 74}, {365, 74}, {366, 74}, {395, 74}, {467, 74}, {484, 74}, {487, 74}, {72, 75}, {78, 75}, {121, 75}, {154, 75}, {173, 75}, {174, 75}, {310, 75}, {311, 75}, {323, 75}, {325, 75}, {394, 75}, {396, 75}, {412, 75}, {413, 75}, {455, 75}, {466, 75}, {468, 75}, {485, 75}, {486, 75}, {490, 75}, {491, 75}, {72, 76}, {78, 76}, {154, 76}, {243, 76}, {323, 76}, {325, 76}, {394, 76}, {396, 76}, {411, 76}, {413, 76}, {454, 76}, {456, 76}, {466, 76}, {468, 76}, {490, 76}, {491, 76}, {99, 77}, {100, 77}, {154, 77}, {175, 77}, {176, 77}, {178, 77}, {243, 77}, {324, 77}, {395, 77}, {411, 77}, {412, 77}, {455, 77}, {467, 77}, {474, 77}, {475, 77}, {74, 78}, {75, 78}, {76, 78}, {98, 78}, {101, 78}, {178, 78}, {243, 78}, {296, 78}, {297, 78}, {460, 78}, {461, 78}, {473, 78}, {476, 78}, {99, 79}, {101, 79}, {175, 79}, {177, 79}, {296, 79}, {297, 79}, {460, 79}, {461, 79}, {474, 79}, {475, 79}, {100, 80}, {106, 80}, {107, 80}, {176, 80}, {184, 80}, {185, 80}, {189, 80}, {291, 80}, {106, 81}, {107, 81}, {120, 81}, {121, 81}, {170, 81}, {171, 81}, {179, 81}, {180, 81}, {184, 81}, {185, 81}, {188, 81}, {189, 81}, {190, 81}, {290, 81}, {292, 81}, {412, 81}, {93, 82}, {94, 82}, {120, 82}, {121, 82}, {169, 82}, {170, 82}, {171, 82}, {172, 82}, {178, 82}, {180, 82}, {183, 82}, {184, 82}, {186, 82}, {187, 82}, {189, 82}, {190, 82}, {242, 82}, {243, 82}, {274, 82}, {275, 82}, {276, 82}, {290, 82}, {293, 82}, {412, 82}, {93, 83}, {94, 83}, {169, 83}, {172, 83}, {173, 83}, {183, 83}, {184, 83}, {242, 83}, {243, 83}, {291, 83}, {292, 83}, {302, 83}, {303, 83}, {338, 83}, {339, 83}, {340, 83}, {412, 83}, {45, 84}, {46, 84}, {114, 84}, {115, 84}, {170, 84}, {172, 84}, {173, 84}, {184, 84}, {186, 84}, {188, 84}, {272, 84}, {278, 84}, {302, 84}, {303, 84}, {44, 85}, {45, 85}, {113, 85}, {115, 85}, {166, 85}, {172, 85}, {173, 85}, {182, 85}, {183, 85}, {184, 85}, {187, 85}, {272, 85}, {278, 85}, {356, 85}, {46, 86}, {114, 86}, {166, 86}, {172, 86}, {181, 86}, {184, 86}, {234, 86}, {272, 86}, {278, 86}, {356, 86}, {154, 87}, {155, 87}, {156, 87}, {159, 87}, {160, 87}, {166, 87}, {170, 87}, {173, 87}, {179, 87}, {183, 87}, {233, 87}, {235, 87}, {316, 87}, {317, 87}, {356, 87}, {446, 87}, {447, 87}, {136, 88}, {137, 88}, {159, 88}, {161, 88}, {170, 88}, {172, 88}, {173, 88}, {176, 88}, {177, 88}, {178, 88}, {179, 88}, {180, 88}, {181, 88}, {182, 88}, {233, 88}, {235, 88}, {274, 88}, {275, 88}, {276, 88}, {316, 88}, {317, 88}, {329, 88}, {330, 88}, {363, 88}, {364, 88}, {445, 88}, {448, 88}, {469, 88}, {470, 88}, {136, 89}, {137, 89}, {152, 89}, {153, 89}, {160, 89}, {172, 89}, {173, 89}, {176, 89}, {177, 89}, {181, 89}, {234, 89}, {267, 89}, {268, 89}, {286, 89}, {287, 89}, {329, 89}, {330, 89}, {352, 89}, {353, 89}, {354, 89}, {358, 89}, {359, 89}, {360, 89}, {363, 89}, {364, 89}, {445, 89}, {447, 89}, {469, 89}, {470, 89}, {90, 90}, {91, 90}, {152, 90}, {153, 90}, {176, 90}, {177, 90}, {178, 90}, {267, 90}, {268, 90}, {285, 90}, {288, 90}, {446, 90}, {90, 91}, {91, 91}, {132, 91}, {133, 91}, {157, 91}, {238, 91}, {239, 91}, {286, 91}, {287, 91}, {348, 91}, {356, 91}, {132, 92}, {133, 92}, {147, 92}, {148, 92}, {154, 92}, {157, 92}, {158, 92}, {159, 92}, {160, 92}, {168, 92}, {169, 92}, {237, 92}, {240, 92}, {348, 92}, {356, 92}, {115, 93}, {147, 93}, {148, 93}, {154, 93}, {157, 93}, {158, 93}, {159, 93}, {160, 93}, {167, 93}, {168, 93}, {170, 93}, {173, 93}, {174, 93}, {175, 93}, {176, 93}, {177, 93}, {184, 93}, {186, 93}, {188, 93}, {238, 93}, {239, 93}, {348, 93}, {356, 93}, {114, 94}, {116, 94}, {154, 94}, {155, 94}, {159, 94}, {168, 94}, {170, 94}, {173, 94}, {174, 94}, {176, 94}, {177, 94}, {178, 94}, {182, 94}, {183, 94}, {184, 94}, {188, 94}, {390, 94}, {114, 95}, {117, 95}, {154, 95}, {157, 95}, {158, 95}, {169, 95}, {174, 95}, {183, 95}, {184, 95}, {185, 95}, {188, 95}, {244, 95}, {309, 95}, {310, 95}, {344, 95}, {345, 95}, {346, 95}, {389, 95}, {391, 95}, {409, 95}, {410, 95}, {411, 95}, {447, 95}, {448, 95}, {75, 96}, {76, 96}, {115, 96}, {116, 96}, {155, 96}, {156, 96}, {173, 96}, {184, 96}, {186, 96}, {244, 96}, {308, 96}, {311, 96}, {389, 96}, {390, 96}, {447, 96}, {448, 96}, {74, 97}, {77, 97}, {175, 97}, {178, 97}, {183, 97}, {208, 97}, {209, 97}, {210, 97}, {244, 97}, {309, 97}, {311, 97}, {75, 98}, {76, 98}, {132, 98}, {141, 98}, {142, 98}, {171, 98}, {173, 98}, {175, 98}, {281, 98}, {282, 98}, {283, 98}, {310, 98}, {131, 99}, {132, 99}, {133, 99}, {140, 99}, {142, 99}, {170, 99}, {174, 99}, {182, 99}, {183, 99}, {184, 99}, {199, 99}, {246, 99}, {247, 99}, {248, 99}, {444, 99}, {445, 99}, {130, 100}, {131, 100}, {133, 100}, {134, 100}, {141, 100}, {169, 100}, {170, 100}, {172, 100}, {173, 100}, {183, 100}, {184, 100}, {199, 100}, {382, 100}, {383, 100}, {443, 100}, {446, 100}, {135, 101}, {171, 101}, {199, 101}, {332, 101}, {382, 101}, {383, 101}, {443, 101}, {446, 101}, {135, 102}, {136, 102}, {308, 102}, {309, 102}, {332, 102}, {444, 102}, {445, 102}, {109, 103}, {135, 103}, {136, 103}, {308, 103}, {309, 103}, {332, 103}, {396, 103}, {397, 103}, {108, 104}, {110, 104}, {131, 104}, {162, 104}, {163, 104}, {208, 104}, {209, 104}, {395, 104}, {398, 104}, {423, 104}, {430, 104}, {108, 105}, {110, 105}, {132, 105}, {157, 105}, {158, 105}, {162, 105}, {164, 105}, {207, 105}, {210, 105}, {328, 105}, {329, 105}, {330, 105}, {334, 105}, {335, 105}, {336, 105}, {372, 105}, {373, 105}, {395, 105}, {398, 105}, {406, 105}, {407, 105}, {423, 105}, {430, 105}, {95, 106}, {109, 106}, {120, 106}, {157, 106}, {158, 106}, {163, 106}, {208, 106}, {209, 106}, {365, 106}, {366, 106}, {372, 106}, {373, 106}, {396, 106}, {397, 106}, {406, 106}, {407, 106}, {423, 106}, {430, 106}, {94, 107}, {96, 107}, {120, 107}, {182, 107}, {332, 107}, {364, 107}, {367, 107}, {94, 108}, {96, 108}, {120, 108}, {135, 108}, {136, 108}, {181, 108}, {183, 108}, {217, 108}, {332, 108}, {364, 108}, {366, 108}, {419, 108}, {420, 108}, {421, 108}, {432, 108}, {433, 108}, {434, 108}, {95, 109}, {135, 109}, {136, 109}, {141, 109}, {181, 109}, {183, 109}, {216, 109}, {218, 109}, {332, 109}, {365, 109}, {130, 110}, {131, 110}, {140, 110}, {141, 110}, {146, 110}, {147, 110}, {182, 110}, {199, 110}, {216, 110}, {218, 110}, {312, 110}, {313, 110}, {423, 110}, {430, 110}, {129, 111}, {132, 111}, {140, 111}, {146, 111}, {147, 111}, {198, 111}, {200, 111}, {217, 111}, {312, 111}, {313, 111}, {423, 111}, {430, 111}, {35, 112}, {50, 112}, {51, 112}, {52, 112}, {129, 112}, {131, 112}, {199, 112}, {200, 112}, {423, 112}, {430, 112}, {34, 113}, {36, 113}, {82, 113}, {83, 113}, {130, 113}, {265, 113}, {35, 114}, {37, 114}, {64, 114}, {65, 114}, {82, 114}, {83, 114}, {265, 114}, {379, 114}, {380, 114}, {36, 115}, {37, 115}, {45, 115}, {64, 115}, {65, 115}, {164, 115}, {165, 115}, {265, 115}, {379, 115}, {380, 115}, {44, 116}, {46, 116}, {164, 116}, {165, 116}, {44, 117}, {46, 117}, {47, 117}, {45, 118}, {46, 118}, {47, 118}, {105, 118}, {106, 118}, {134, 118}, {135, 118}, {136, 118}, {141, 118}, {143, 118}, {156, 118}, {263, 118}, {264, 118}, {46, 119}, {47, 119}, {105, 119}, {106, 119}, {133, 119}, {142, 119}, {155, 119}, {157, 119}, {262, 119}, {265, 119}, {363, 119}, {364, 119}, {365, 119}, {382, 119}, {383, 119}, {384, 119}, {133, 120}, {136, 120}, {137, 120}, {139, 120}, {156, 120}, {162, 120}, {163, 120}, {263, 120}, {264, 120}, {302, 120}, {303, 120}, {47, 121}, {48, 121}, {133, 121}, {134, 121}, {137, 121}, {138, 121}, {139, 121}, {140, 121}, {162, 121}, {163, 121}, {302, 121}, {304, 121}, {361, 121}, {367, 121}, {46, 122}, {49, 122}, {112, 122}, {113, 122}, {141, 122}, {149, 122}, {200, 122}, {201, 122}, {202, 122}, {303, 122}, {304, 122}, {361, 122}, {367, 122}, {36, 123}, {38, 123}, {41, 123}, {42, 123}, {45, 123}, {49, 123}, {112, 123}, {113, 123}, {139, 123}, {141, 123}, {142, 123}, {148, 123}, {150, 123}, {180, 123}, {361, 123}, {367, 123}, {41, 124}, {43, 124}, {46, 124}, {137, 124}, {138, 124}, {147, 124}, {150, 124}, {180, 124}, {419, 124}, {420, 124}, {422, 124}, {423, 124}, {41, 125}, {43, 125}, {104, 125}, {105, 125}, {139, 125}, {148, 125}, {149, 125}, {180, 125}, {363, 125}, {364, 125}, {365, 125}, {419, 125}, {420, 125}, {422, 125}, {423, 125}, {38, 126}, {103, 126}, {105, 126}, {136, 126}, {139, 126}, {142, 126}, {143, 126}, {186, 126}, {316, 126}, {317, 126}, {34, 127}, {42, 127}, {43, 127}, {44, 127}, {103, 127}, {104, 127}, {139, 127}, {143, 127}, {185, 127}, {187, 127}, {317, 127}, {318, 127}, {34, 128}, {39, 128}, {43, 128}, {82, 128}, {83, 128}, {136, 128}, {138, 128}, {141, 128}, {143, 128}, {185, 128}, {187, 128}, {199, 128}, {200, 128}, {316, 128}, {35, 129}, {39, 129}, {81, 129}, {84, 129}, {136, 129}, {139, 129}, {141, 129}, {142, 129}, {153, 129}, {154, 129}, {186, 129}, {198, 129}, {201, 129}, {371, 129}, {372, 129}, {420, 129}, {421, 129}, {35, 130}, {49, 130}, {82, 130}, {83, 130}, {111, 130}, {112, 130}, {137, 130}, {140, 130}, {153, 130}, {154, 130}, {199, 130}, {201, 130}, {371, 130}, {372, 130}, {421, 130}, {45, 131}, {49, 131}, {110, 131}, {112, 131}, {140, 131}, {170, 131}, {171, 131}, {181, 131}, {200, 131}, {418, 131}, {44, 132}, {45, 132}, {46, 132}, {47, 132}, {48, 132}, {49, 132}, {111, 132}, {138, 132}, {139, 132}, {169, 132}, {172, 132}, {180, 132}, {182, 132}, {418, 132}, {419, 132}, {43, 133}, {44, 133}, {46, 133}, {47, 133}, {155, 133}, {156, 133}, {170, 133}, {171, 133}, {180, 133}, {183, 133}, {424, 133}, {425, 133}, {42, 134}, {43, 134}, {155, 134}, {156, 134}, {181, 134}, {182, 134}, {393, 134}, {394, 134}, {423, 134}, {426, 134}, {43, 135}, {44, 135}, {48, 135}, {147, 135}, {392, 135}, {395, 135}, {424, 135}, {425, 135}, {44, 136}, {45, 136}, {48, 136}, {128, 136}, {129, 136}, {147, 136}, {231, 136}, {232, 136}, {393, 136}, {394, 136}, {46, 137}, {128, 137}, {129, 137}, {147, 137}, {188, 137}, {189, 137}, {230, 137}, {233, 137}, {43, 138}, {103, 138}, {187, 138}, {190, 138}, {230, 138}, {232, 138}, {42, 139}, {43, 139}, {44, 139}, {101, 139}, {102, 139}, {103, 139}, {188, 139}, {189, 139}, {228, 139}, {229, 139}, {231, 139}, {235, 139}, {41, 140}, {42, 140}, {44, 140}, {45, 140}, {100, 140}, {101, 140}, {102, 140}, {227, 140}, {230, 140}, {234, 140}, {236, 140}, {42, 141}, {43, 141}, {44, 141}, {100, 141}, {101, 141}, {102, 141}, {227, 141}, {229, 141}, {233, 141}, {236, 141}, {43, 142}, {100, 142}, {101, 142}, {106, 142}, {107, 142}, {228, 142}, {232, 142}, {234, 142}, {235, 142}, {421, 142}, {422, 142}, {83, 143}, {84, 143}, {231, 143}, {233, 143}, {421, 143}, {422, 143}, {83, 144}, {84, 144}, {99, 144}, {105, 144}, {106, 144}, {125, 144}, {126, 144}, {218, 144}, {219, 144}, {230, 144}, {233, 144}, {97, 145}, {98, 145}, {104, 145}, {105, 145}, {106, 145}, {124, 145}, {127, 145}, {218, 145}, {219, 145}, {231, 145}, {232, 145}, {102, 146}, {107, 146}, {108, 146}, {125, 146}, {126, 146}, {150, 146}, {151, 146}, {152, 146}, {170, 146}, {260, 146}, {261, 146}, {101, 147}, {107, 147}, {108, 147}, {170, 147}, {260, 147}, {261, 147}, {364, 147}, {365, 147}, {381, 147}, {382, 147}, {100, 148}, {101, 148}, {105, 148}, {106, 148}, {170, 148}, {241, 148}, {242, 148}, {243, 148}, {363, 148}, {366, 148}, {381, 148}, {383, 148}, {100, 149}, {102, 149}, {105, 149}, {215, 149}, {216, 149}, {304, 149}, {305, 149}, {364, 149}, {365, 149}, {382, 149}, {383, 149}, {102, 150}, {104, 150}, {105, 150}, {214, 150}, {217, 150}, {304, 150}, {305, 150}, {103, 151}, {107, 151}, {215, 151}, {216, 151}, {251, 151}, {252, 151}, {105, 152}, {108, 152}, {109, 152}, {242, 152}, {243, 152}, {244, 152}, {251, 152}, {252, 152}, {367, 152}, {368, 152}, {388, 152}, {107, 153}, {108, 153}, {110, 153}, {115, 153}, {274, 153}, {275, 153}, {276, 153}, {312, 153}, {313, 153}, {341, 153}, {342, 153}, {367, 153}, {368, 153}, {387, 153}, {389, 153}, {74, 154}, {75, 154}, {76, 154}, {110, 154}, {115, 154}, {212, 154}, {213, 154}, {256, 154}, {312, 154}, {313, 154}, {341, 154}, {342, 154}, {386, 154}, {389, 154}, {504, 154}, {57, 155}, {58, 155}, {73, 155}, {77, 155}, {107, 155}, {109, 155}, {110, 155}, {115, 155}, {211, 155}, {213, 155}, {256, 155}, {387, 155}, {388, 155}, {504, 155}, {56, 156}, {58, 156}, {73, 156}, {77, 156}, {107, 156}, {108, 156}, {185, 156}, {186, 156}, {211, 156}, {212, 156}, {244, 156}, {245, 156}, {256, 156}, {504, 156}, {56, 157}, {57, 157}, {72, 157}, {77, 157}, {78, 157}, {185, 157}, {186, 157}, {244, 157}, {245, 157}, {71, 158}, {72, 158}, {75, 158}, {202, 158}, {203, 158}, {293, 158}, {294, 158}, {500, 158}, {501, 158}, {502, 158}, {506, 158}, {507, 158}, {508, 158}, {70, 159}, {71, 159}, {72, 159}, {76, 159}, {106, 159}, {107, 159}, {202, 159}, {203, 159}, {293, 159}, {295, 159}, {362, 159}, {368, 159}, {68, 160}, {74, 160}, {106, 160}, {107, 160}, {281, 160}, {294, 160}, {295, 160}, {340, 160}, {341, 160}, {362, 160}, {368, 160}, {67, 161}, {73, 161}, {74, 161}, {77, 161}, {99, 161}, {100, 161}, {280, 161}, {282, 161}, {325, 161}, {326, 161}, {339, 161}, {342, 161}, {362, 161}, {368, 161}, {374, 161}, {375, 161}, {73, 162}, {74, 162}, {76, 162}, {77, 162}, {98, 162}, {101, 162}, {280, 162}, {282, 162}, {325, 162}, {326, 162}, {340, 162}, {341, 162}, {373, 162}, {376, 162}, {379, 162}, {380, 162}, {67, 163}, {68, 163}, {70, 163}, {74, 163}, {99, 163}, {100, 163}, {129, 163}, {130, 163}, {148, 163}, {163, 163}, {281, 163}, {374, 163}, {375, 163}, {378, 163}, {381, 163}, {399, 163}, {400, 163}, {401, 163}, {81, 164}, {82, 164}, {129, 164}, {130, 164}, {148, 164}, {162, 164}, {164, 164}, {357, 164}, {379, 164}, {380, 164}, {81, 165}, {83, 165}, {89, 165}, {90, 165}, {148, 165}, {161, 165}, {164, 165}, {194, 165}, {262, 165}, {263, 165}, {351, 165}, {356, 165}, {358, 165}, {404, 165}, {405, 165}, {83, 166}, {89, 166}, {90, 166}, {162, 166}, {163, 166}, {193, 166}, {195, 166}, {262, 166}, {263, 166}, {350, 166}, {352, 166}, {356, 166}, {358, 166}, {403, 166}, {406, 166}, {81, 167}, {83, 167}, {193, 167}, {195, 167}, {341, 167}, {349, 167}, {352, 167}, {357, 167}, {403, 167}, {406, 167}, {81, 168}, {82, 168}, {126, 168}, {127, 168}, {194, 168}, {340, 168}, {342, 168}, {350, 168}, {351, 168}, {404, 168}, {405, 168}, {67, 169}, {68, 169}, {70, 169}, {74, 169}, {125, 169}, {127, 169}, {263, 169}, {340, 169}, {341, 169}, {399, 169}, {400, 169}, {401, 169}, {73, 170}, {74, 170}, {76, 170}, {77, 170}, {126, 170}, {262, 170}, {264, 170}, {335, 170}, {336, 170}, {67, 171}, {73, 171}, {74, 171}, {77, 171}, {117, 171}, {118, 171}, {263, 171}, {335, 171}, {336, 171}, {68, 172}, {74, 172}, {106, 172}, {107, 172}, {117, 172}, {118, 172}, {163, 172}, {164, 172}, {402, 172}, {70, 173}, {71, 173}, {72, 173}, {76, 173}, {81, 173}, {82, 173}, {106, 173}, {107, 173}, {163, 173}, {164, 173}, {366, 173}, {367, 173}, {368, 173}, {379, 173}, {401, 173}, {403, 173}, {71, 174}, {72, 174}, {75, 174}, {81, 174}, {82, 174}, {255, 174}, {378, 174}, {380, 174}, {400, 174}, {403, 174}, {56, 175}, {72, 175}, {77, 175}, {78, 175}, {168, 175}, {169, 175}, {254, 175}, {256, 175}, {378, 175}, {379, 175}, {401, 175}, {402, 175}, {55, 176}, {57, 176}, {59, 176}, {60, 176}, {61, 176}, {73, 176}, {77, 176}, {123, 176}, {124, 176}, {168, 176}, {169, 176}, {255, 176}, {257, 176}, {48, 177}, {49, 177}, {50, 177}, {54, 177}, {55, 177}, {57, 177}, {58, 177}, {59, 177}, {60, 177}, {73, 177}, {77, 177}, {97, 177}, {123, 177}, {124, 177}, {184, 177}, {185, 177}, {240, 177}, {241, 177}, {256, 177}, {257, 177}, {48, 178}, {49, 178}, {50, 178}, {51, 178}, {52, 178}, {55, 178}, {74, 178}, {75, 178}, {76, 178}, {80, 178}, {81, 178}, {96, 178}, {98, 178}, {137, 178}, {160, 178}, {161, 178}, {183, 178}, {186, 178}, {239, 178}, {242, 178}, {52, 179}, {56, 179}, {60, 179}, {61, 179}, {65, 179}, {66, 179}, {80, 179}, {82, 179}, {96, 179}, {98, 179}, {136, 179}, {138, 179}, {149, 179}, {160, 179}, {161, 179}, {184, 179}, {185, 179}, {240, 179}, {241, 179}, {397, 179}, {398, 179}, {47, 180}, {48, 180}, {49, 180}, {50, 180}, {51, 180}, {56, 180}, {62, 180}, {64, 180}, {67, 180}, {71, 180}, {72, 180}, {73, 180}, {81, 180}, {97, 180}, {136, 180}, {138, 180}, {148, 180}, {150, 180}, {396, 180}, {398, 180}, {47, 181}, {59, 181}, {60, 181}, {61, 181}, {68, 181}, {71, 181}, {109, 181}, {137, 181}, {149, 181}, {151, 181}, {397, 181}, {48, 182}, {50, 182}, {61, 182}, {65, 182}, {66, 182}, {67, 182}, {70, 182}, {71, 182}, {73, 182}, {100, 182}, {101, 182}, {102, 182}, {109, 182}, {150, 182}, {272, 182}, {273, 182}, {41, 183}, {43, 183}, {49, 183}, {50, 183}, {62, 183}, {63, 183}, {64, 183}, {68, 183}, {70, 183}, {71, 183}, {73, 183}, {103, 183}, {109, 183}, {272, 183}, {273, 183}, {41, 184}, {42, 184}, {63, 184}, {68, 184}, {69, 184}, {71, 184}, {98, 184}, {104, 184}, {105, 184}, {42, 185}, {66, 185}, {98, 185}, {106, 185}, {107, 185}, {111, 185}, {112, 185}, {113, 185}, {275, 185}, {276, 185}, {379, 185}, {380, 185}, {66, 186}, {69, 186}, {70, 186}, {71, 186}, {98, 186}, {104, 186}, {105, 186}, {122, 186}, {275, 186}, {276, 186}, {342, 186}, {379, 186}, {380, 186}, {393, 186}, {70, 187}, {75, 187}, {94, 187}, {95, 187}, {96, 187}, {103, 187}, {109, 187}, {121, 187}, {123, 187}, {124, 187}, {126, 187}, {127, 187}, {340, 187}, {342, 187}, {393, 187}, {70, 188}, {75, 188}, {100, 188}, {101, 188}, {102, 188}, {109, 188}, {121, 188}, {123, 188}, {124, 188}, {126, 188}, {127, 188}, {128, 188}, {341, 188}, {342, 188}, {393, 188}, {71, 189}, {74, 189}, {109, 189}, {123, 189}, {129, 189}, {248, 189}, {129, 190}, {130, 190}, {238, 190}, {239, 190}, {248, 190}, {254, 190}, {255, 190}, {256, 190}, {56, 191}, {57, 191}, {85, 191}, {86, 191}, {121, 191}, {122, 191}, {128, 191}, {129, 191}, {148, 191}, {185, 191}, {186, 191}, {187, 191}, {238, 191}, {239, 191}, {248, 191}, {263, 191}, {366, 191}, {367, 191}, {390, 191}, {391, 191}, {392, 191}, {410, 191}, {56, 192}, {57, 192}, {85, 192}, {86, 192}, {116, 192}, {117, 192}, {127, 192}, {148, 192}, {156, 192}, {157, 192}, {158, 192}, {262, 192}, {264, 192}, {281, 192}, {282, 192}, {334, 192}, {335, 192}, {366, 192}, {367, 192}, {410, 192}, {115, 193}, {117, 193}, {126, 193}, {148, 193}, {261, 193}, {263, 193}, {280, 193}, {283, 193}, {334, 193}, {335, 193}, {410, 193}, {92, 194}, {116, 194}, {143, 194}, {144, 194}, {261, 194}, {262, 194}, {281, 194}, {282, 194}, {91, 195}, {93, 195}, {143, 195}, {144, 195}, {206, 195}, {207, 195}, {208, 195}, {503, 195}, {504, 195}, {91, 196}, {93, 196}, {133, 196}, {134, 196}, {502, 196}, {505, 196}, {92, 197}, {133, 197}, {134, 197}, {353, 197}, {354, 197}, {503, 197}, {505, 197}, {40, 198}, {246, 198}, {247, 198}, {352, 198}, {355, 198}, {400, 198}, {401, 198}, {504, 198}, {39, 199}, {41, 199}, {87, 199}, {88, 199}, {96, 199}, {97, 199}, {246, 199}, {247, 199}, {258, 199}, {259, 199}, {338, 199}, {339, 199}, {353, 199}, {354, 199}, {400, 199}, {401, 199}, {39, 200}, {41, 200}, {86, 200}, {89, 200}, {95, 200}, {98, 200}, {163, 200}, {164, 200}, {258, 200}, {259, 200}, {337, 200}, {340, 200}, {40, 201}, {87, 201}, {88, 201}, {96, 201}, {97, 201}, {163, 201}, {164, 201}, {226, 201}, {337, 201}, {339, 201}, {34, 202}, {194, 202}, {195, 202}, {226, 202}, {338, 202}, {34, 203}, {92, 203}, {123, 203}, {124, 203}, {193, 203}, {196, 203}, {226, 203}, {261, 203}, {34, 204}, {91, 204}, {93, 204}, {123, 204}, {124, 204}, {129, 204}, {134, 204}, {146, 204}, {147, 204}, {194, 204}, {195, 204}, {260, 204}, {262, 204}, {353, 204}, {354, 204}, {372, 204}, {91, 205}, {93, 205}, {128, 205}, {130, 205}, {134, 205}, {146, 205}, {147, 205}, {260, 205}, {261, 205}, {353, 205}, {354, 205}, {372, 205}, {467, 205}, {468, 205}, {34, 206}, {55, 206}, {56, 206}, {92, 206}, {128, 206}, {130, 206}, {134, 206}, {164, 206}, {165, 206}, {213, 206}, {278, 206}, {279, 206}, {289, 206}, {290, 206}, {291, 206}, {358, 206}, {359, 206}, {372, 206}, {467, 206}, {468, 206}, {33, 207}, {35, 207}, {55, 207}, {56, 207}, {78, 207}, {129, 207}, {158, 207}, {159, 207}, {164, 207}, {165, 207}, {213, 207}, {278, 207}, {279, 207}, {358, 207}, {359, 207}, {404, 207}, {33, 208}, {36, 208}, {79, 208}, {136, 208}, {137, 208}, {138, 208}, {149, 208}, {150, 208}, {157, 208}, {160, 208}, {213, 208}, {287, 208}, {293, 208}, {368, 208}, {369, 208}, {370, 208}, {374, 208}, {375, 208}, {376, 208}, {378, 208}, {379, 208}, {380, 208}, {404, 208}, {507, 208}, {508, 208}, {33, 209}, {34, 209}, {36, 209}, {37, 209}, {77, 209}, {78, 209}, {79, 209}, {115, 209}, {148, 209}, {151, 209}, {158, 209}, {159, 209}, {287, 209}, {293, 209}, {404, 209}, {414, 209}, {475, 209}, {476, 209}, {506, 209}, {509, 209}, {34, 210}, {37, 210}, {115, 210}, {149, 210}, {150, 210}, {209, 210}, {210, 210}, {211, 210}, {215, 210}, {216, 210}, {217, 210}, {273, 210}, {274, 210}, {287, 210}, {293, 210}, {372, 210}, {413, 210}, {415, 210}, {475, 210}, {476, 210}, {507, 210}, {508, 210}, {29, 211}, {30, 211}, {31, 211}, {34, 211}, {36, 211}, {115, 211}, {262, 211}, {263, 211}, {273, 211}, {274, 211}, {282, 211}, {290, 211}, {372, 211}, {413, 211}, {416, 211}, {29, 212}, {32, 212}, {128, 212}, {129, 212}, {213, 212}, {262, 212}, {263, 212}, {282, 212}, {289, 212}, {291, 212}, {372, 212}, {414, 212}, {415, 212}, {29, 213}, {30, 213}, {32, 213}, {127, 213}, {130, 213}, {213, 213}, {245, 213}, {246, 213}, {247, 213}, {282, 213}, {289, 213}, {291, 213}, {385, 213}, {386, 213}, {31, 214}, {128, 214}, {130, 214}, {213, 214}, {245, 214}, {265, 214}, {266, 214}, {267, 214}, {290, 214}, {377, 214}, {378, 214}, {384, 214}, {386, 214}, {399, 214}, {411, 214}, {412, 214}, {456, 214}, {457, 214}, {26, 215}, {27, 215}, {49, 215}, {50, 215}, {66, 215}, {129, 215}, {136, 215}, {137, 215}, {246, 215}, {376, 215}, {379, 215}, {385, 215}, {398, 215}, {400, 215}, {411, 215}, {412, 215}, {456, 215}, {458, 215}, {26, 216}, {27, 216}, {49, 216}, {50, 216}, {65, 216}, {67, 216}, {135, 216}, {138, 216}, {376, 216}, {378, 216}, {398, 216}, {400, 216}, {457, 216}, {458, 216}, {41, 217}, {43, 217}, {65, 217}, {67, 217}, {135, 217}, {138, 217}, {221, 217}, {222, 217}, {377, 217}, {399, 217}, {4, 218}, {5, 218}, {42, 218}, {43, 218}, {66, 218}, {136, 218}, {137, 218}, {221, 218}, {222, 218}, {237, 218}, {238, 218}, {4, 219}, {5, 219}, {42, 219}, {237, 219}, {238, 219}, {236, 220}, {237, 220}, {238, 220}, {241, 220}, {242, 220}, {419, 220}, {420, 220}, {232, 221}, {233, 221}, {238, 221}, {239, 221}, {241, 221}, {242, 221}, {243, 221}, {419, 221}, {420, 221}, {38, 222}, {39, 222}, {68, 222}, {69, 222}, {231, 222}, {232, 222}, {233, 222}, {235, 222}, {236, 222}, {241, 222}, {242, 222}, {245, 222}, {247, 222}, {492, 222}, {493, 222}, {13, 223}, {14, 223}, {38, 223}, {39, 223}, {61, 223}, {62, 223}, {63, 223}, {67, 223}, {69, 223}, {232, 223}, {233, 223}, {236, 223}, {237, 223}, {238, 223}, {245, 223}, {246, 223}, {256, 223}, {257, 223}, {258, 223}, {492, 223}, {493, 223}, {13, 224}, {14, 224}, {61, 224}, {63, 224}, {64, 224}, {68, 224}, {129, 224}, {236, 224}, {237, 224}, {246, 224}, {298, 224}, {377, 224}, {378, 224}, {379, 224}, {61, 225}, {64, 225}, {65, 225}, {66, 225}, {67, 225}, {128, 225}, {130, 225}, {166, 225}, {167, 225}, {236, 225}, {237, 225}, {254, 225}, {263, 225}, {284, 225}, {297, 225}, {299, 225}, {66, 226}, {67, 226}, {127, 226}, {130, 226}, {165, 226}, {168, 226}, {254, 226}, {263, 226}, {284, 226}, {297, 226}, {300, 226}, {375, 226}, {381, 226}, {394, 226}, {395, 226}, {63, 227}, {64, 227}, {65, 227}, {68, 227}, {94, 227}, {128, 227}, {129, 227}, {165, 227}, {168, 227}, {180, 227}, {181, 227}, {254, 227}, {263, 227}, {284, 227}, {298, 227}, {299, 227}, {361, 227}, {362, 227}, {375, 227}, {381, 227}, {394, 227}, {395, 227}, {64, 228}, {65, 228}, {66, 228}, {68, 228}, {69, 228}, {71, 228}, {72, 228}, {94, 228}, {166, 228}, {167, 228}, {179, 228}, {182, 228}, {361, 228}, {362, 228}, {375, 228}, {381, 228}, {63, 229}, {65, 229}, {66, 229}, {67, 229}, {70, 229}, {73, 229}, {94, 229}, {180, 229}, {181, 229}, {223, 229}, {224, 229}, {259, 229}, {260, 229}, {261, 229}, {265, 229}, {266, 229}, {267, 229}, {57, 230}, {58, 230}, {63, 230}, {71, 230}, {72, 230}, {223, 230}, {224, 230}, {292, 230}, {293, 230}, {377, 230}, {378, 230}, {379, 230}, {388, 230}, {389, 230}, {48, 231}, {49, 231}, {56, 231}, {59, 231}, {231, 231}, {263, 231}, {292, 231}, {293, 231}, {365, 231}, {366, 231}, {388, 231}, {389, 231}, {0, 232}, {1, 232}, {48, 232}, {49, 232}, {56, 232}, {57, 232}, {59, 232}, {61, 232}, {63, 232}, {231, 232}, {232, 232}, {263, 232}, {365, 232}, {366, 232}, {0, 233}, {1, 233}, {60, 233}, {61, 233}, {233, 233}, {263, 233}, {61, 234}, {223, 234}, {224, 234}, {230, 234}, {231, 234}, {232, 234}, {109, 235}, {110, 235}, {203, 235}, {204, 235}, {205, 235}, {222, 235}, {223, 235}, {224, 235}, {229, 235}, {230, 235}, {328, 235}, {329, 235}, {19, 236}, {20, 236}, {47, 236}, {48, 236}, {54, 236}, {55, 236}, {104, 236}, {105, 236}, {109, 236}, {110, 236}, {213, 236}, {214, 236}, {221, 236}, {223, 236}, {229, 236}, {230, 236}, {269, 236}, {270, 236}, {328, 236}, {329, 236}, {370, 236}, {504, 236}, {505, 236}, {19, 237}, {20, 237}, {47, 237}, {48, 237}, {54, 237}, {56, 237}, {103, 237}, {106, 237}, {213, 237}, {214, 237}, {221, 237}, {223, 237}, {243, 237}, {255, 237}, {256, 237}, {269, 237}, {270, 237}, {369, 237}, {371, 237}, {421, 237}, {502, 237}, {503, 237}, {505, 237}, {506, 237}, {55, 238}, {104, 238}, {106, 238}, {221, 238}, {225, 238}, {226, 238}, {243, 238}, {255, 238}, {256, 238}, {342, 238}, {343, 238}, {369, 238}, {370, 238}, {420, 238}, {422, 238}, {491, 238}, {492, 238}, {502, 238}, {503, 238}, {507, 238}, {508, 238}, {105, 239}, {222, 239}, {226, 239}, {243, 239}, {302, 239}, {336, 239}, {337, 239}, {341, 239}, {344, 239}, {420, 239}, {423, 239}, {491, 239}, {492, 239}, {503, 239}, {504, 239}, {505, 239}, {507, 239}, {509, 239}, {510, 239}, {2, 240}, {3, 240}, {4, 240}, {26, 240}, {91, 240}, {197, 240}, {198, 240}, {199, 240}, {223, 240}, {259, 240}, {260, 240}, {261, 240}, {279, 240}, {280, 240}, {286, 240}, {287, 240}, {292, 240}, {301, 240}, {303, 240}, {336, 240}, {337, 240}, {342, 240}, {344, 240}, {421, 240}, {422, 240}, {505, 240}, {507, 240}, {510, 240}, {26, 241}, {74, 241}, {75, 241}, {76, 241}, {90, 241}, {92, 241}, {152, 241}, {214, 241}, {239, 241}, {240, 241}, {241, 241}, {246, 241}, {261, 241}, {279, 241}, {280, 241}, {285, 241}, {287, 241}, {291, 241}, {293, 241}, {301, 241}, {302, 241}, {343, 241}, {482, 241}, {483, 241}, {484, 241}, {506, 241}, {510, 241}, {26, 242}, {73, 242}, {76, 242}, {90, 242}, {92, 242}, {152, 242}, {195, 242}, {201, 242}, {213, 242}, {215, 242}, {246, 242}, {247, 242}, {251, 242}, {252, 242}, {253, 242}, {261, 242}, {262, 242}, {263, 242}, {284, 242}, {286, 242}, {290, 242}, {293, 242}, {506, 242}, {508, 242}, {510, 242}, {511, 242}, {73, 243}, {74, 243}, {77, 243}, {78, 243}, {91, 243}, {126, 243}, {127, 243}, {128, 243}, {152, 243}, {195, 243}, {201, 243}, {213, 243}, {215, 243}, {245, 243}, {248, 243}, {251, 243}, {252, 243}, {253, 243}, {256, 243}, {261, 243}, {264, 243}, {274, 243}, {275, 243}, {276, 243}, {285, 243}, {291, 243}, {292, 243}, {480, 243}, {486, 243}, {507, 243}, {508, 243}, {74, 244}, {75, 244}, {78, 244}, {195, 244}, {201, 244}, {214, 244}, {236, 244}, {237, 244}, {246, 244}, {248, 244}, {249, 244}, {255, 244}, {257, 244}, {262, 244}, {263, 244}, {317, 244}, {318, 244}, {361, 244}, {362, 244}, {480, 244}, {486, 244}, {76, 245}, {148, 245}, {149, 245}, {150, 245}, {154, 245}, {155, 245}, {156, 245}, {217, 245}, {229, 245}, {230, 245}, {231, 245}, {236, 245}, {237, 245}, {246, 245}, {247, 245}, {248, 245}, {254, 245}, {258, 245}, {317, 245}, {319, 245}, {361, 245}, {362, 245}, {480, 245}, {486, 245}, {78, 246}, {79, 246}, {197, 246}, {198, 246}, {199, 246}, {216, 246}, {218, 246}, {229, 246}, {230, 246}, {231, 246}, {247, 246}, {248, 246}, {249, 246}, {254, 246}, {258, 246}, {259, 246}, {318, 246}, {319, 246}, {5, 247}, {6, 247}, {75, 247}, {79, 247}, {152, 247}, {216, 247}, {218, 247}, {227, 247}, {228, 247}, {249, 247}, {250, 247}, {253, 247}, {254, 247}, {255, 247}, {257, 247}, {258, 247}, {482, 247}, {483, 247}, {484, 247}, {5, 248}, {6, 248}, {49, 248}, {50, 248}, {51, 248}, {152, 248}, {217, 248}, {227, 248}, {238, 248}, {248, 248}, {251, 248}, {255, 248}, {257, 248}, {390, 248}, {79, 249}, {87, 249}, {88, 249}, {152, 249}, {227, 249}, {237, 249}, {238, 249}, {239, 249}, {249, 249}, {250, 249}, {389, 249}, {391, 249}, {76, 250}, {80, 250}, {86, 250}, {89, 250}, {228, 250}, {231, 250}, {232, 250}, {236, 250}, {238, 250}, {240, 250}, {389, 250}, {391, 250}, {77, 251}, {80, 251}, {87, 251}, {88, 251}, {106, 251}, {107, 251}, {232, 251}, {235, 251}, {236, 251}, {237, 251}, {239, 251}, {240, 251}, {241, 251}, {259, 251}, {308, 251}, {309, 251}, {390, 251}, {80, 252}, {81, 252}, {83, 252}, {84, 252}, {105, 252}, {108, 252}, {128, 252}, {129, 252}, {148, 252}, {149, 252}, {229, 252}, {230, 252}, {231, 252}, {236, 252}, {238, 252}, {240, 252}, {260, 252}, {308, 252}, {309, 252}, {380, 252}, {381, 252}, {38, 253}, {79, 253}, {80, 253}, {83, 253}, {84, 253}, {105, 253}, {108, 253}, {127, 253}, {130, 253}, {148, 253}, {149, 253}, {230, 253}, {231, 253}, {237, 253}, {238, 253}, {239, 253}, {260, 253}, {380, 253}, {381, 253}, {506, 253}, {507, 253}, {508, 253}, {37, 254}, {39, 254}, {101, 254}, {106, 254}, {107, 254}, {128, 254}, {129, 254}, {231, 254}, {232, 254}, {238, 254}, {490, 254}, {36, 255}, {37, 255}, {39, 255}, {40, 255}, {78, 255}, {79, 255}, {100, 255}, {102, 255}, {135, 255}, {214, 255}, {312, 255}, {313, 255}, {477, 255}, {489, 255}, {491, 255}, {493, 255}, {494, 255}, {504, 255}, {510, 255}, {36, 256}, {77, 256}, {78, 256}, {80, 256}, {101, 256}, {102, 256}, {134, 256}, {136, 256}, {213, 256}, {215, 256}, {225, 256}, {226, 256}, {312, 256}, {313, 256}, {476, 256}, {478, 256}, {489, 256}, {491, 256}, {493, 256}, {494, 256}, {504, 256}, {510, 256}, {72, 257}, {73, 257}, {77, 257}, {80, 257}, {119, 257}, {134, 257}, {136, 257}, {212, 257}, {215, 257}, {225, 257}, {226, 257}, {307, 257}, {308, 257}, {405, 257}, {406, 257}, {407, 257}, {476, 257}, {478, 257}, {490, 257}, {504, 257}, {510, 257}, {33, 258}, {34, 258}, {35, 258}, {71, 258}, {74, 258}, {79, 258}, {119, 258}, {135, 258}, {213, 258}, {214, 258}, {221, 258}, {306, 258}, {309, 258}, {477, 258}, {496, 258}, {11, 259}, {33, 259}, {34, 259}, {72, 259}, {73, 259}, {119, 259}, {220, 259}, {222, 259}, {306, 259}, {308, 259}, {387, 259}, {496, 259}, {506, 259}, {507, 259}, {508, 259}, {10, 260}, {12, 260}, {20, 260}, {21, 260}, {27, 260}, {28, 260}, {34, 260}, {220, 260}, {222, 260}, {307, 260}, {386, 260}, {388, 260}, {496, 260}, {10, 261}, {12, 261}, {19, 261}, {22, 261}, {27, 261}, {28, 261}, {59, 261}, {60, 261}, {61, 261}, {115, 261}, {116, 261}, {117, 261}, {121, 261}, {122, 261}, {123, 261}, {221, 261}, {385, 261}, {388, 261}, {422, 261}, {423, 261}, {11, 262}, {20, 262}, {21, 262}, {36, 262}, {37, 262}, {43, 262}, {59, 262}, {323, 262}, {386, 262}, {387, 262}, {421, 262}, {423, 262}, {477, 262}, {478, 262}, {41, 263}, {42, 263}, {43, 263}, {60, 263}, {100, 263}, {101, 263}, {119, 263}, {264, 263}, {322, 263}, {324, 263}, {421, 263}, {422, 263}, {477, 263}, {478, 263}, {40, 264}, {41, 264}, {42, 264}, {43, 264}, {44, 264}, {86, 264}, {87, 264}, {99, 264}, {100, 264}, {119, 264}, {131, 264}, {132, 264}, {258, 264}, {259, 264}, {263, 264}, {265, 264}, {322, 264}, {324, 264}, {23, 265}, {24, 265}, {39, 265}, {85, 265}, {88, 265}, {95, 265}, {96, 265}, {101, 265}, {108, 265}, {119, 265}, {131, 265}, {132, 265}, {225, 265}, {226, 265}, {258, 265}, {259, 265}, {263, 265}, {265, 265}, {323, 265}, {22, 266}, {25, 266}, {38, 266}, {39, 266}, {40, 266}, {44, 266}, {45, 266}, {86, 266}, {87, 266}, {95, 266}, {96, 266}, {108, 266}, {225, 266}, {226, 266}, {264, 266}, {310, 266}, {311, 266}, {402, 266}, {22, 267}, {25, 267}, {40, 267}, {42, 267}, {43, 267}, {44, 267}, {111, 267}, {112, 267}, {310, 267}, {311, 267}, {402, 267}, {23, 268}, {24, 268}, {41, 268}, {42, 268}, {43, 268}, {91, 268}, {110, 268}, {113, 268}, {398, 268}, {402, 268}, {415, 268}, {416, 268}, {41, 269}, {42, 269}, {43, 269}, {90, 269}, {92, 269}, {111, 269}, {112, 269}, {217, 269}, {218, 269}, {219, 269}, {277, 269}, {278, 269}, {397, 269}, {399, 269}, {415, 269}, {416, 269}, {14, 270}, {15, 270}, {90, 270}, {92, 270}, {221, 270}, {251, 270}, {252, 270}, {276, 270}, {278, 270}, {312, 270}, {326, 270}, {327, 270}, {397, 270}, {399, 270}, {14, 271}, {15, 271}, {91, 271}, {203, 271}, {204, 271}, {219, 271}, {220, 271}, {222, 271}, {241, 271}, {250, 271}, {253, 271}, {277, 271}, {312, 271}, {326, 271}, {327, 271}, {398, 271}, {499, 271}, {500, 271}, {38, 272}, {39, 272}, {202, 272}, {205, 272}, {222, 272}, {223, 272}, {241, 272}, {251, 272}, {252, 272}, {312, 272}, {355, 272}, {499, 272}, {500, 272}, {38, 273}, {39, 273}, {202, 273}, {205, 273}, {221, 273}, {222, 273}, {241, 273}, {354, 273}, {356, 273}, {379, 273}, {380, 273}, {393, 273}, {394, 273}, {203, 274}, {204, 274}, {212, 274}, {218, 274}, {220, 274}, {221, 274}, {317, 274}, {354, 274}, {356, 274}, {378, 274}, {381, 274}, {392, 274}, {395, 274}, {405, 274}, {406, 274}, {47, 275}, {88, 275}, {89, 275}, {211, 275}, {213, 275}, {219, 275}, {311, 275}, {312, 275}, {316, 275}, {318, 275}, {355, 275}, {379, 275}, {380, 275}, {393, 275}, {394, 275}, {405, 275}, {406, 275}, {47, 276}, {87, 276}, {90, 276}, {211, 276}, {213, 276}, {219, 276}, {311, 276}, {312, 276}, {316, 276}, {317, 276}, {321, 276}, {23, 277}, {48, 277}, {88, 277}, {89, 277}, {109, 277}, {110, 277}, {111, 277}, {212, 277}, {320, 277}, {322, 277}, {447, 277}, {489, 277}, {500, 277}, {22, 278}, {24, 278}, {25, 278}, {319, 278}, {322, 278}, {384, 278}, {400, 278}, {401, 278}, {402, 278}, {415, 278}, {416, 278}, {446, 278}, {448, 278}, {488, 278}, {490, 278}, {499, 278}, {501, 278}, {22, 279}, {52, 279}, {53, 279}, {207, 279}, {208, 279}, {216, 279}, {217, 279}, {320, 279}, {321, 279}, {372, 279}, {373, 279}, {384, 279}, {415, 279}, {416, 279}, {446, 279}, {448, 279}, {489, 279}, {500, 279}, {14, 280}, {23, 280}, {24, 280}, {26, 280}, {27, 280}, {53, 280}, {54, 280}, {206, 280}, {209, 280}, {215, 280}, {218, 280}, {253, 280}, {372, 280}, {373, 280}, {384, 280}, {438, 280}, {447, 280}, {13, 281}, {15, 281}, {22, 281}, {23, 281}, {24, 281}, {27, 281}, {53, 281}, {54, 281}, {207, 281}, {208, 281}, {216, 281}, {217, 281}, {253, 281}, {261, 281}, {289, 281}, {290, 281}, {422, 281}, {423, 281}, {437, 281}, {439, 281}, {12, 282}, {15, 282}, {21, 282}, {22, 282}, {23, 282}, {25, 282}, {50, 282}, {51, 282}, {52, 282}, {53, 282}, {231, 282}, {232, 282}, {233, 282}, {243, 282}, {253, 282}, {261, 282}, {289, 282}, {290, 282}, {422, 282}, {423, 282}, {437, 282}, {439, 282}, {13, 283}, {14, 283}, {24, 283}, {25, 283}, {28, 283}, {50, 283}, {212, 283}, {242, 283}, {244, 283}, {261, 283}, {438, 283}, {24, 284}, {26, 284}, {27, 284}, {28, 284}, {29, 284}, {49, 284}, {52, 284}, {211, 284}, {213, 284}, {241, 284}, {244, 284}, {427, 284}, {428, 284}, {24, 285}, {27, 285}, {30, 285}, {51, 285}, {52, 285}, {211, 285}, {213, 285}, {242, 285}, {243, 285}, {312, 285}, {427, 285}, {428, 285}, {21, 286}, {23, 286}, {28, 286}, {212, 286}, {226, 286}, {227, 286}, {312, 286}, {406, 286}, {12, 287}, {29, 287}, {30, 287}, {31, 287}, {225, 287}, {228, 287}, {289, 287}, {290, 287}, {312, 287}, {406, 287}, {10, 288}, {11, 288}, {13, 288}, {226, 288}, {228, 288}, {289, 288}, {290, 288}, {406, 288}, {412, 288}, {413, 288}, {422, 288}, {423, 288}, {426, 288}, {11, 289}, {13, 289}, {199, 289}, {200, 289}, {227, 289}, {263, 289}, {264, 289}, {411, 289}, {414, 289}, {420, 289}, {422, 289}, {423, 289}, {427, 289}, {12, 290}, {199, 290}, {201, 290}, {241, 290}, {242, 290}, {263, 290}, {264, 290}, {412, 290}, {413, 290}, {423, 290}, {29, 291}, {30, 291}, {65, 291}, {66, 291}, {67, 291}, {109, 291}, {126, 291}, {127, 291}, {128, 291}, {200, 291}, {201, 291}, {240, 291}, {243, 291}, {425, 291}, {427, 291}, {29, 292}, {30, 292}, {96, 292}, {97, 292}, {108, 292}, {110, 292}, {241, 292}, {242, 292}, {289, 292}, {290, 292}, {372, 292}, {373, 292}, {426, 292}, {427, 292}, {428, 292}, {89, 293}, {90, 293}, {96, 293}, {97, 293}, {109, 293}, {274, 293}, {289, 293}, {290, 293}, {372, 293}, {373, 293}, {424, 293}, {427, 293}, {504, 293}, {88, 294}, {91, 294}, {274, 294}, {425, 294}, {426, 294}, {503, 294}, {505, 294}, {88, 295}, {90, 295}, {215, 295}, {216, 295}, {217, 295}, {225, 295}, {274, 295}, {309, 295}, {310, 295}, {379, 295}, {504, 295}, {89, 296}, {224, 296}, {226, 296}, {308, 296}, {311, 296}, {379, 296}, {403, 296}, {404, 296}, {224, 297}, {225, 297}, {309, 297}, {310, 297}, {379, 297}, {384, 297}, {403, 297}, {404, 297}, {438, 297}, {472, 297}, {473, 297}, {46, 298}, {326, 298}, {327, 298}, {383, 298}, {385, 298}, {437, 298}, {439, 298}, {472, 298}, {473, 298}, {46, 299}, {105, 299}, {212, 299}, {213, 299}, {326, 299}, {327, 299}, {345, 299}, {346, 299}, {347, 299}, {383, 299}, {384, 299}, {410, 299}, {411, 299}, {437, 299}, {439, 299}, {34, 300}, {35, 300}, {46, 300}, {105, 300}, {211, 300}, {213, 300}, {410, 300}, {411, 300}, {438, 300}, {34, 301}, {35, 301}, {105, 301}, {211, 301}, {212, 301}, {217, 301}, {218, 301}, {343, 301}, {349, 301}, {217, 302}, {218, 302}, {343, 302}, {349, 302}, {386, 302}, {392, 302}, {207, 303}, {343, 303}, {349, 303}, {385, 303}, {387, 303}, {392, 303}, {207, 304}, {385, 304}, {387, 304}, {392, 304}, {103, 305}, {104, 305}, {207, 305}, {213, 305}, {214, 305}, {345, 305}, {346, 305}, {347, 305}, {386, 305}, {404, 305}, {405, 305}, {498, 305}, {39, 306}, {40, 306}, {102, 306}, {105, 306}, {213, 306}, {214, 306}, {403, 306}, {406, 306}, {497, 306}, {499, 306}, {38, 307}, {41, 307}, {103, 307}, {104, 307}, {404, 307}, {405, 307}, {497, 307}, {499, 307}, {24, 308}, {25, 308}, {39, 308}, {40, 308}, {118, 308}, {270, 308}, {498, 308}, {24, 309}, {25, 309}, {117, 309}, {119, 309}, {269, 309}, {271, 309}, {291, 309}, {292, 309}, {484, 309}, {43, 310}, {44, 310}, {117, 310}, {119, 310}, {237, 310}, {269, 310}, {271, 310}, {291, 310}, {292, 310}, {399, 310}, {484, 310}, {43, 311}, {44, 311}, {56, 311}, {57, 311}, {118, 311}, {236, 311}, {238, 311}, {270, 311}, {398, 311}, {400, 311}, {470, 311}, {471, 311}, {484, 311}, {21, 312}, {56, 312}, {57, 312}, {82, 312}, {83, 312}, {102, 312}, {103, 312}, {236, 312}, {238, 312}, {387, 312}, {388, 312}, {389, 312}, {398, 312}, {400, 312}, {411, 312}, {412, 312}, {413, 312}, {470, 312}, {471, 312}, {21, 313}, {32, 313}, {65, 313}, {66, 313}, {82, 313}, {83, 313}, {102, 313}, {103, 313}, {113, 313}, {114, 313}, {122, 313}, {123, 313}, {237, 313}, {399, 313}, {486, 313}, {487, 313}, {488, 313}, {21, 314}, {31, 314}, {33, 314}, {65, 314}, {66, 314}, {112, 314}, {115, 314}, {121, 314}, {124, 314}, {275, 314}, {276, 314}, {32, 315}, {33, 315}, {113, 315}, {114, 315}, {122, 315}, {123, 315}, {274, 315}, {277, 315}, {286, 315}, {287, 315}, {17, 316}, {18, 316}, {19, 316}, {23, 316}, {24, 316}, {25, 316}, {212, 316}, {242, 316}, {243, 316}, {275, 316}, {276, 316}, {285, 316}, {288, 316}, {38, 317}, {39, 317}, {65, 317}, {66, 317}, {118, 317}, {211, 317}, {213, 317}, {241, 317}, {244, 317}, {285, 317}, {288, 317}, {305, 317}, {503, 317}, {504, 317}, {21, 318}, {38, 318}, {39, 318}, {65, 318}, {66, 318}, {117, 318}, {119, 318}, {211, 318}, {213, 318}, {242, 318}, {243, 318}, {286, 318}, {287, 318}, {304, 318}, {306, 318}, {348, 318}, {502, 318}, {505, 318}, {21, 319}, {117, 319}, {119, 319}, {131, 319}, {132, 319}, {212, 319}, {304, 319}, {306, 319}, {348, 319}, {478, 319}, {479, 319}, {503, 319}, {504, 319}, {21, 320}, {118, 320}, {131, 320}, {132, 320}, {305, 320}, {348, 320}, {374, 320}, {375, 320}, {477, 320}, {480, 320}, {53, 321}, {103, 321}, {104, 321}, {105, 321}, {153, 321}, {154, 321}, {207, 321}, {208, 321}, {374, 321}, {375, 321}, {478, 321}, {480, 321}, {10, 322}, {53, 322}, {153, 322}, {154, 322}, {206, 322}, {209, 322}, {294, 322}, {295, 322}, {479, 322}, {508, 322}, {509, 322}, {510, 322}, {10, 323}, {53, 323}, {127, 323}, {128, 323}, {206, 323}, {209, 323}, {284, 323}, {294, 323}, {295, 323}, {10, 324}, {126, 324}, {128, 324}, {207, 324}, {208, 324}, {263, 324}, {264, 324}, {283, 324}, {285, 324}, {301, 324}, {46, 325}, {127, 325}, {156, 325}, {263, 325}, {264, 325}, {282, 325}, {285, 325}, {300, 325}, {302, 325}, {454, 325}, {455, 325}, {491, 325}, {492, 325}, {12, 326}, {13, 326}, {14, 326}, {19, 326}, {20, 326}, {21, 326}, {38, 326}, {39, 326}, {45, 326}, {47, 326}, {76, 326}, {77, 326}, {100, 326}, {131, 326}, {132, 326}, {155, 326}, {157, 326}, {283, 326}, {284, 326}, {300, 326}, {302, 326}, {454, 326}, {455, 326}, {491, 326}, {492, 326}, {37, 327}, {40, 327}, {45, 327}, {47, 327}, {75, 327}, {78, 327}, {84, 327}, {85, 327}, {86, 327}, {100, 327}, {131, 327}, {132, 327}, {145, 327}, {154, 327}, {156, 327}, {161, 327}, {162, 327}, {301, 327}, {402, 327}, {403, 327}, {37, 328}, {39, 328}, {46, 328}, {76, 328}, {77, 328}, {100, 328}, {144, 328}, {146, 328}, {154, 328}, {155, 328}, {160, 328}, {163, 328}, {402, 328}, {403, 328}, {38, 329}, {144, 329}, {146, 329}, {161, 329}, {162, 329}, {123, 330}, {145, 330}, {209, 330}, {210, 330}, {211, 330}, {229, 330}, {230, 330}, {318, 330}, {319, 330}, {459, 330}, {123, 331}, {166, 331}, {167, 331}, {168, 331}, {229, 331}, {230, 331}, {268, 331}, {269, 331}, {317, 331}, {320, 331}, {364, 331}, {365, 331}, {403, 331}, {404, 331}, {422, 331}, {423, 331}, {458, 331}, {460, 331}, {498, 331}, {499, 331}, {76, 332}, {77, 332}, {107, 332}, {108, 332}, {109, 332}, {123, 332}, {140, 332}, {141, 332}, {149, 332}, {150, 332}, {177, 332}, {178, 332}, {179, 332}, {268, 332}, {270, 332}, {317, 332}, {319, 332}, {364, 332}, {365, 332}, {403, 332}, {404, 332}, {422, 332}, {423, 332}, {459, 332}, {460, 332}, {497, 332}, {500, 332}, {76, 333}, {77, 333}, {139, 333}, {142, 333}, {148, 333}, {151, 333}, {269, 333}, {318, 333}, {340, 333}, {341, 333}, {497, 333}, {499, 333}, {119, 334}, {120, 334}, {121, 334}, {125, 334}, {126, 334}, {127, 334}, {140, 334}, {141, 334}, {149, 334}, {150, 334}, {175, 334}, {181, 334}, {340, 334}, {341, 334}, {498, 334}, {175, 335}, {181, 335}, {423, 335}, {424, 335}, {123, 336}, {145, 336}, {175, 336}, {181, 336}, {224, 336}, {423, 336}, {424, 336}, {493, 336}, {494, 336}, {47, 337}, {48, 337}, {49, 337}, {123, 337}, {144, 337}, {146, 337}, {223, 337}, {225, 337}, {360, 337}, {446, 337}, {493, 337}, {494, 337}, {509, 337}, {123, 338}, {144, 338}, {146, 338}, {177, 338}, {178, 338}, {179, 338}, {223, 338}, {226, 338}, {360, 338}, {446, 338}, {508, 338}, {510, 338}, {145, 339}, {224, 339}, {225, 339}, {360, 339}, {446, 339}, {507, 339}, {509, 339}, {468, 340}, {469, 340}, {507, 340}, {508, 340}, {467, 341}, {470, 341}, {186, 342}, {187, 342}, {238, 342}, {239, 342}, {467, 342}, {470, 342}, {186, 343}, {187, 343}, {238, 343}, {239, 343}, {263, 343}, {264, 343}, {265, 343}, {447, 343}, {448, 343}, {468, 343}, {469, 343}, {510, 343}, {511, 343}, {442, 344}, {443, 344}, {446, 344}, {449, 344}, {510, 344}, {511, 344}, {314, 345}, {315, 345}, {371, 345}, {442, 345}, {443, 345}, {447, 345}, {448, 345}, {101, 346}, {102, 346}, {198, 346}, {199, 346}, {200, 346}, {256, 346}, {257, 346}, {271, 346}, {272, 346}, {313, 346}, {316, 346}, {344, 346}, {345, 346}, {370, 346}, {372, 346}, {419, 346}, {420, 346}, {100, 347}, {102, 347}, {190, 347}, {248, 347}, {249, 347}, {256, 347}, {257, 347}, {271, 347}, {272, 347}, {313, 347}, {316, 347}, {344, 347}, {345, 347}, {371, 347}, {377, 347}, {419, 347}, {420, 347}, {101, 348}, {189, 348}, {191, 348}, {241, 348}, {242, 348}, {243, 348}, {247, 348}, {249, 348}, {314, 348}, {315, 348}, {376, 348}, {378, 348}, {189, 349}, {191, 349}, {208, 349}, {209, 349}, {248, 349}, {275, 349}, {276, 349}, {285, 349}, {286, 349}, {327, 349}, {328, 349}, {370, 349}, {376, 349}, {378, 349}, {190, 350}, {208, 350}, {209, 350}, {275, 350}, {276, 350}, {285, 350}, {286, 350}, {311, 350}, {327, 350}, {328, 350}, {369, 350}, {371, 350}, {377, 350}, {418, 350}, {419, 350}, {237, 351}, {238, 351}, {254, 351}, {311, 351}, {369, 351}, {372, 351}, {418, 351}, {419, 351}, {237, 352}, {238, 352}, {247, 352}, {248, 352}, {253, 352}, {255, 352}, {311, 352}, {331, 352}, {332, 352}, {370, 352}, {371, 352}, {381, 352}, {382, 352}, {449, 352}, {489, 352}, {490, 352}, {217, 353}, {246, 353}, {249, 353}, {253, 353}, {255, 353}, {295, 353}, {330, 353}, {332, 353}, {380, 353}, {383, 353}, {396, 353}, {413, 353}, {448, 353}, {450, 353}, {489, 353}, {490, 353}, {203, 354}, {204, 354}, {210, 354}, {211, 354}, {216, 354}, {218, 354}, {247, 354}, {248, 354}, {254, 354}, {261, 354}, {262, 354}, {295, 354}, {331, 354}, {356, 354}, {357, 354}, {358, 354}, {381, 354}, {382, 354}, {395, 354}, {397, 354}, {413, 354}, {448, 354}, {450, 354}, {471, 354}, {202, 355}, {205, 355}, {210, 355}, {211, 355}, {216, 355}, {218, 355}, {261, 355}, {262, 355}, {295, 355}, {395, 355}, {397, 355}, {413, 355}, {449, 355}, {459, 355}, {463, 355}, {464, 355}, {471, 355}, {175, 356}, {202, 356}, {204, 356}, {217, 356}, {354, 356}, {366, 356}, {372, 356}, {396, 356}, {410, 356}, {443, 356}, {444, 356}, {459, 356}, {462, 356}, {465, 356}, {471, 356}, {174, 357}, {176, 357}, {184, 357}, {185, 357}, {203, 357}, {254, 357}, {255, 357}, {275, 357}, {291, 357}, {292, 357}, {293, 357}, {297, 357}, {298, 357}, {299, 357}, {353, 357}, {355, 357}, {366, 357}, {372, 357}, {409, 357}, {411, 357}, {442, 357}, {445, 357}, {459, 357}, {463, 357}, {464, 357}, {174, 358}, {176, 358}, {184, 358}, {185, 358}, {230, 358}, {231, 358}, {254, 358}, {255, 358}, {274, 358}, {276, 358}, {338, 358}, {339, 358}, {340, 358}, {353, 358}, {356, 358}, {366, 358}, {372, 358}, {391, 358}, {392, 358}, {400, 358}, {401, 358}, {409, 358}, {411, 358}, {443, 358}, {444, 358}, {108, 359}, {175, 359}, {209, 359}, {210, 359}, {211, 359}, {230, 359}, {231, 359}, {274, 359}, {275, 359}, {295, 359}, {338, 359}, {341, 359}, {354, 359}, {355, 359}, {390, 359}, {393, 359}, {399, 359}, {402, 359}, {410, 359}, {424, 359}, {425, 359}, {455, 359}, {456, 359}, {457, 359}, {478, 359}, {479, 359}, {107, 360}, {109, 360}, {220, 360}, {270, 360}, {271, 360}, {295, 360}, {336, 360}, {368, 360}, {369, 360}, {370, 360}, {391, 360}, {392, 360}, {400, 360}, {401, 360}, {423, 360}, {425, 360}, {478, 360}, {479, 360}, {108, 361}, {109, 361}, {219, 361}, {221, 361}, {270, 361}, {271, 361}, {295, 361}, {336, 361}, {342, 361}, {424, 361}, {105, 362}, {219, 362}, {221, 362}, {318, 362}, {319, 362}, {336, 362}, {341, 362}, {396, 362}, {500, 362}, {82, 363}, {83, 363}, {84, 363}, {104, 363}, {106, 363}, {220, 363}, {317, 363}, {319, 363}, {339, 363}, {395, 363}, {397, 363}, {471, 363}, {499, 363}, {501, 363}, {87, 364}, {96, 364}, {97, 364}, {104, 364}, {106, 364}, {171, 364}, {172, 364}, {317, 364}, {318, 364}, {395, 364}, {397, 364}, {419, 364}, {420, 364}, {470, 364}, {472, 364}, {499, 364}, {501, 364}, {86, 365}, {88, 365}, {95, 365}, {97, 365}, {105, 365}, {171, 365}, {172, 365}, {215, 365}, {216, 365}, {270, 365}, {271, 365}, {396, 365}, {418, 365}, {421, 365}, {440, 365}, {441, 365}, {470, 365}, {472, 365}, {500, 365}, {86, 366}, {88, 366}, {96, 366}, {214, 366}, {217, 366}, {270, 366}, {272, 366}, {419, 366}, {420, 366}, {427, 366}, {439, 366}, {442, 366}, {471, 366}, {488, 366}, {87, 367}, {215, 367}, {216, 367}, {226, 367}, {271, 367}, {291, 367}, {292, 367}, {427, 367}, {440, 367}, {441, 367}, {453, 367}, {487, 367}, {489, 367}, {226, 368}, {227, 368}, {290, 368}, {293, 368}, {427, 368}, {453, 368}, {486, 368}, {489, 368}, {194, 369}, {195, 369}, {196, 369}, {220, 369}, {221, 369}, {225, 369}, {228, 369}, {290, 369}, {292, 369}, {453, 369}, {487, 369}, {488, 369}, {221, 370}, {224, 370}, {227, 370}, {228, 370}, {291, 370}, {404, 370}, {405, 370}, {461, 370}, {510, 370}, {511, 370}, {220, 371}, {221, 371}, {224, 371}, {404, 371}, {405, 371}, {449, 371}, {450, 371}, {451, 371}, {455, 371}, {456, 371}, {457, 371}, {460, 371}, {462, 371}, {510, 371}, {511, 371}, {198, 372}, {199, 372}, {200, 372}, {220, 372}, {221, 372}, {222, 372}, {440, 372}, {441, 372}, {461, 372}, {462, 372}, {222, 373}, {223, 373}, {224, 373}, {313, 373}, {314, 373}, {433, 373}, {434, 373}, {440, 373}, {441, 373}, {453, 373}, {181, 374}, {196, 374}, {202, 374}, {223, 374}, {224, 374}, {276, 374}, {313, 374}, {314, 374}, {394, 374}, {395, 374}, {433, 374}, {434, 374}, {453, 374}, {469, 374}, {470, 374}, {180, 375}, {182, 375}, {196, 375}, {202, 375}, {223, 375}, {224, 375}, {276, 375}, {394, 375}, {395, 375}, {417, 375}, {418, 375}, {419, 375}, {453, 375}, {468, 375}, {471, 375}, {180, 376}, {182, 376}, {196, 376}, {202, 376}, {229, 376}, {233, 376}, {234, 376}, {276, 376}, {469, 376}, {470, 376}, {498, 376}, {499, 376}, {500, 376}, {181, 377}, {228, 377}, {230, 377}, {232, 377}, {233, 377}, {234, 377}, {415, 377}, {421, 377}, {228, 378}, {231, 378}, {232, 378}, {233, 378}, {260, 378}, {261, 378}, {296, 378}, {348, 378}, {349, 378}, {359, 378}, {401, 378}, {402, 378}, {415, 378}, {421, 378}, {446, 378}, {447, 378}, {448, 378}, {209, 379}, {210, 379}, {229, 379}, {230, 379}, {234, 379}, {260, 379}, {261, 379}, {295, 379}, {297, 379}, {315, 379}, {347, 379}, {350, 379}, {359, 379}, {400, 379}, {403, 379}, {415, 379}, {421, 379}, {209, 380}, {210, 380}, {234, 380}, {294, 380}, {297, 380}, {314, 380}, {316, 380}, {347, 380}, {349, 380}, {359, 380}, {401, 380}, {402, 380}, {188, 381}, {189, 381}, {295, 381}, {296, 381}, {314, 381}, {317, 381}, {348, 381}, {417, 381}, {418, 381}, {419, 381}, {464, 381}, {188, 382}, {189, 382}, {215, 382}, {315, 382}, {316, 382}, {367, 382}, {368, 382}, {463, 382}, {465, 382}, {500, 382}, {501, 382}, {502, 382}, {214, 383}, {216, 383}, {366, 383}, {369, 383}, {463, 383}, {465, 383}, {500, 383}, {501, 383}, {502, 383}, {503, 383}, {214, 384}, {215, 384}, {367, 384}, {368, 384}, {464, 384}, {500, 384}, {503, 384}, {8, 385}, {9, 385}, {206, 385}, {207, 385}, {501, 385}, {502, 385}, {8, 386}, {9, 386}, {206, 386}, {207, 386}, {496, 386}, {497, 386}, {498, 386}, {501, 386}, {213, 387}, {214, 387}, {257, 387}, {351, 387}, {352, 387}, {372, 387}, {373, 387}, {497, 387}, {499, 387}, {500, 387}, {501, 387}, {213, 388}, {215, 388}, {238, 388}, {239, 388}, {256, 388}, {258, 388}, {351, 388}, {352, 388}, {372, 388}, {373, 388}, {383, 388}, {384, 388}, {498, 388}, {499, 388}, {500, 388}, {501, 388}, {502, 388}, {214, 389}, {237, 389}, {240, 389}, {255, 389}, {258, 389}, {383, 389}, {384, 389}, {490, 389}, {499, 389}, {503, 389}, {238, 390}, {239, 390}, {256, 390}, {257, 390}, {489, 390}, {490, 390}, {498, 390}, {499, 390}, {501, 390}, {502, 390}, {335, 391}, {488, 391}, {490, 391}, {492, 391}, {493, 391}, {501, 391}, {223, 392}, {224, 392}, {225, 392}, {284, 392}, {285, 392}, {333, 392}, {334, 392}, {335, 392}, {336, 392}, {337, 392}, {350, 392}, {351, 392}, {488, 392}, {490, 392}, {491, 392}, {493, 392}, {498, 392}, {499, 392}, {500, 392}, {227, 393}, {231, 393}, {232, 393}, {233, 393}, {234, 393}, {235, 393}, {236, 393}, {284, 393}, {285, 393}, {333, 393}, {334, 393}, {336, 393}, {337, 393}, {338, 393}, {350, 393}, {351, 393}, {487, 393}, {494, 393}, {499, 393}, {194, 394}, {195, 394}, {221, 394}, {230, 394}, {237, 394}, {333, 394}, {334, 394}, {337, 394}, {415, 394}, {416, 394}, {486, 394}, {193, 395}, {196, 395}, {221, 395}, {223, 395}, {228, 395}, {230, 395}, {234, 395}, {236, 395}, {237, 395}, {310, 395}, {311, 395}, {312, 395}, {316, 395}, {317, 395}, {318, 395}, {334, 395}, {335, 395}, {336, 395}, {415, 395}, {416, 395}, {485, 395}, {486, 395}, {491, 395}, {194, 396}, {195, 396}, {228, 396}, {231, 396}, {232, 396}, {238, 396}, {335, 396}, {440, 396}, {441, 396}, {484, 396}, {485, 396}, {486, 396}, {490, 396}, {491, 396}, {507, 396}, {508, 396}, {215, 397}, {221, 397}, {222, 397}, {224, 397}, {234, 397}, {235, 397}, {237, 397}, {238, 397}, {314, 397}, {327, 397}, {440, 397}, {441, 397}, {485, 397}, {486, 397}, {489, 397}, {490, 397}, {491, 397}, {507, 397}, {508, 397}, {13, 398}, {182, 398}, {183, 398}, {214, 398}, {215, 398}, {216, 398}, {221, 398}, {222, 398}, {225, 398}, {226, 398}, {227, 398}, {234, 398}, {238, 398}, {314, 398}, {326, 398}, {336, 398}, {337, 398}, {489, 398}, {12, 399}, {14, 399}, {181, 399}, {183, 399}, {184, 399}, {213, 399}, {214, 399}, {216, 399}, {217, 399}, {235, 399}, {236, 399}, {237, 399}, {314, 399}, {329, 399}, {335, 399}, {338, 399}, {13, 400}, {14, 400}, {181, 400}, {183, 400}, {214, 400}, {215, 400}, {216, 400}, {218, 400}, {324, 400}, {325, 400}, {326, 400}, {336, 400}, {337, 400}, {357, 400}, {358, 400}, {182, 401}, {185, 401}, {215, 401}, {218, 401}, {219, 401}, {220, 401}, {221, 401}, {222, 401}, {284, 401}, {285, 401}, {286, 401}, {323, 401}, {329, 401}, {357, 401}, {358, 401}, {394, 401}, {395, 401}, {486, 401}, {503, 401}, {504, 401}, {183, 402}, {184, 402}, {185, 402}, {188, 402}, {219, 402}, {220, 402}, {221, 402}, {222, 402}, {223, 402}, {323, 402}, {326, 402}, {327, 402}, {328, 402}, {345, 402}, {346, 402}, {347, 402}, {393, 402}, {396, 402}, {485, 402}, {487, 402}, {503, 402}, {504, 402}, {189, 403}, {219, 403}, {220, 403}, {223, 403}, {227, 403}, {304, 403}, {324, 403}, {325, 403}, {326, 403}, {394, 403}, {395, 403}, {435, 403}, {436, 403}, {437, 403}, {486, 403}, {487, 403}, {185, 404}, {189, 404}, {220, 404}, {221, 404}, {222, 404}, {226, 404}, {228, 404}, {304, 404}, {349, 404}, {423, 404}, {424, 404}, {172, 405}, {173, 405}, {174, 405}, {185, 405}, {186, 405}, {187, 405}, {188, 405}, {189, 405}, {221, 405}, {227, 405}, {228, 405}, {304, 405}, {349, 405}, {405, 405}, {406, 405}, {422, 405}, {425, 405}, {0, 406}, {163, 406}, {164, 406}, {165, 406}, {186, 406}, {188, 406}, {265, 406}, {300, 406}, {301, 406}, {307, 406}, {308, 406}, {349, 406}, {405, 406}, {407, 406}, {423, 406}, {424, 406}, {1, 407}, {187, 407}, {264, 407}, {266, 407}, {300, 407}, {301, 407}, {307, 407}, {308, 407}, {312, 407}, {313, 407}, {405, 407}, {511, 407}, {0, 408}, {1, 408}, {264, 408}, {266, 408}, {312, 408}, {313, 408}, {324, 408}, {325, 408}, {327, 408}, {373, 408}, {374, 408}, {265, 409}, {320, 409}, {325, 409}, {327, 409}, {328, 409}, {335, 409}, {336, 409}, {368, 409}, {369, 409}, {373, 409}, {374, 409}, {282, 410}, {283, 410}, {319, 410}, {327, 410}, {328, 410}, {335, 410}, {336, 410}, {368, 410}, {369, 410}, {481, 410}, {482, 410}, {254, 411}, {255, 411}, {256, 411}, {282, 411}, {283, 411}, {304, 411}, {305, 411}, {319, 411}, {324, 411}, {480, 411}, {483, 411}, {487, 411}, {488, 411}, {9, 412}, {10, 412}, {11, 412}, {304, 412}, {305, 412}, {319, 412}, {326, 412}, {327, 412}, {470, 412}, {471, 412}, {481, 412}, {482, 412}, {487, 412}, {488, 412}, {166, 413}, {320, 413}, {324, 413}, {325, 413}, {326, 413}, {327, 413}, {415, 413}, {417, 413}, {470, 413}, {471, 413}, {166, 414}, {308, 414}, {309, 414}, {326, 414}, {327, 414}, {390, 414}, {391, 414}, {392, 414}, {418, 414}, {166, 415}, {224, 415}, {225, 415}, {308, 415}, {309, 415}, {322, 415}, {325, 415}, {347, 415}, {348, 415}, {349, 415}, {390, 415}, {393, 415}, {411, 415}, {418, 415}, {224, 416}, {225, 416}, {324, 416}, {388, 416}, {393, 416}, {410, 416}, {419, 416}, {215, 417}, {216, 417}, {217, 417}, {388, 417}, {390, 417}, {391, 417}, {393, 417}, {394, 417}, {411, 417}, {413, 417}, {414, 417}, {415, 417}, {416, 417}, {417, 417}, {418, 417}, {469, 417}, {381, 418}, {388, 418}, {391, 418}, {393, 418}, {394, 418}, {417, 418}, {468, 418}, {470, 418}, {157, 419}, {182, 419}, {183, 419}, {213, 419}, {219, 419}, {381, 419}, {389, 419}, {390, 419}, {391, 419}, {392, 419}, {468, 419}, {470, 419}, {156, 420}, {158, 420}, {181, 420}, {183, 420}, {213, 420}, {219, 420}, {250, 420}, {251, 420}, {316, 420}, {317, 420}, {381, 420}, {389, 420}, {390, 420}, {391, 420}, {415, 420}, {469, 420}, {156, 421}, {158, 421}, {182, 421}, {213, 421}, {219, 421}, {250, 421}, {251, 421}, {316, 421}, {317, 421}, {384, 421}, {414, 421}, {415, 421}, {416, 421}, {465, 421}, {466, 421}, {107, 422}, {108, 422}, {149, 422}, {157, 422}, {330, 422}, {331, 422}, {383, 422}, {385, 422}, {394, 422}, {411, 422}, {412, 422}, {416, 422}, {465, 422}, {467, 422}, {483, 422}, {484, 422}, {489, 422}, {490, 422}, {106, 423}, {108, 423}, {149, 423}, {215, 423}, {216, 423}, {217, 423}, {329, 423}, {331, 423}, {383, 423}, {385, 423}, {395, 423}, {396, 423}, {411, 423}, {414, 423}, {461, 423}, {462, 423}, {466, 423}, {483, 423}, {485, 423}, {488, 423}, {491, 423}, {107, 424}, {149, 424}, {210, 424}, {211, 424}, {330, 424}, {334, 424}, {384, 424}, {394, 424}, {395, 424}, {412, 424}, {413, 424}, {414, 424}, {428, 424}, {429, 424}, {461, 424}, {462, 424}, {484, 424}, {489, 424}, {490, 424}, {205, 425}, {206, 425}, {210, 425}, {211, 425}, {288, 425}, {289, 425}, {303, 425}, {304, 425}, {305, 425}, {333, 425}, {335, 425}, {428, 425}, {429, 425}, {205, 426}, {207, 426}, {281, 426}, {282, 426}, {288, 426}, {289, 426}, {333, 426}, {334, 426}, {397, 426}, {138, 427}, {143, 427}, {144, 427}, {206, 427}, {228, 427}, {229, 427}, {280, 427}, {283, 427}, {397, 427}, {399, 427}, {419, 427}, {420, 427}, {492, 427}, {493, 427}, {137, 428}, {139, 428}, {143, 428}, {144, 428}, {228, 428}, {229, 428}, {281, 428}, {282, 428}, {397, 428}, {398, 428}, {419, 428}, {420, 428}, {492, 428}, {493, 428}, {107, 429}, {137, 429}, {139, 429}, {174, 429}, {234, 429}, {235, 429}, {465, 429}, {466, 429}, {467, 429}, {74, 430}, {75, 430}, {106, 430}, {108, 430}, {138, 430}, {173, 430}, {175, 430}, {233, 430}, {236, 430}, {74, 431}, {75, 431}, {106, 431}, {109, 431}, {173, 431}, {175, 431}, {234, 431}, {235, 431}, {298, 431}, {107, 432}, {108, 432}, {174, 432}, {298, 432}, {85, 433}, {86, 433}, {137, 433}, {138, 433}, {144, 433}, {145, 433}, {298, 433}, {395, 433}, {396, 433}, {85, 434}, {86, 434}, {137, 434}, {138, 434}, {144, 434}, {145, 434}, {395, 434}, {396, 434}, {102, 435}, {103, 435}, {294, 435}, {295, 435}, {296, 435}, {300, 435}, {301, 435}, {302, 435}, {101, 436}, {103, 436}, {114, 436}, {115, 436}, {120, 436}, {171, 436}, {172, 436}, {173, 436}, {309, 436}, {310, 436}, {360, 436}, {361, 436}, {372, 436}, {373, 436}, {385, 436}, {101, 437}, {102, 437}, {114, 437}, {115, 437}, {120, 437}, {230, 437}, {231, 437}, {232, 437}, {298, 437}, {308, 437}, {311, 437}, {360, 437}, {361, 437}, {372, 437}, {373, 437}, {384, 437}, {386, 437}, {99, 438}, {100, 438}, {120, 438}, {175, 438}, {241, 438}, {242, 438}, {289, 438}, {290, 438}, {298, 438}, {309, 438}, {310, 438}, {384, 438}, {386, 438}, {412, 438}, {413, 438}, {414, 438}, {90, 439}, {91, 439}, {98, 439}, {100, 439}, {106, 439}, {107, 439}, {175, 439}, {234, 439}, {235, 439}, {241, 439}, {242, 439}, {282, 439}, {289, 439}, {290, 439}, {298, 439}, {385, 439}, {488, 439}, {489, 439}, {90, 440}, {91, 440}, {98, 440}, {99, 440}, {105, 440}, {107, 440}, {141, 440}, {142, 440}, {144, 440}, {145, 440}, {175, 440}, {233, 440}, {236, 440}, {282, 440}, {488, 440}, {490, 440}, {105, 441}, {106, 441}, {141, 441}, {142, 441}, {144, 441}, {145, 441}, {160, 441}, {161, 441}, {189, 441}, {233, 441}, {235, 441}, {282, 441}, {299, 441}, {477, 441}, {478, 441}, {489, 441}, {103, 442}, {104, 442}, {159, 442}, {162, 442}, {171, 442}, {172, 442}, {173, 442}, {188, 442}, {190, 442}, {234, 442}, {298, 442}, {300, 442}, {395, 442}, {396, 442}, {409, 442}, {476, 442}, {479, 442}, {102, 443}, {104, 443}, {159, 443}, {161, 443}, {188, 443}, {190, 443}, {278, 443}, {279, 443}, {280, 443}, {284, 443}, {285, 443}, {286, 443}, {298, 443}, {300, 443}, {395, 443}, {396, 443}, {408, 443}, {410, 443}, {465, 443}, {466, 443}, {467, 443}, {476, 443}, {479, 443}, {102, 444}, {103, 444}, {128, 444}, {129, 444}, {130, 444}, {160, 444}, {189, 444}, {299, 444}, {308, 444}, {364, 444}, {365, 444}, {408, 444}, {411, 444}, {477, 444}, {478, 444}, {282, 445}, {307, 445}, {309, 445}, {364, 445}, {365, 445}, {383, 445}, {384, 445}, {385, 445}, {409, 445}, {410, 445}, {449, 445}, {450, 445}, {126, 446}, {132, 446}, {165, 446}, {184, 446}, {237, 446}, {238, 446}, {282, 446}, {308, 446}, {309, 446}, {315, 446}, {316, 446}, {448, 446}, {451, 446}, {126, 447}, {132, 447}, {165, 447}, {184, 447}, {237, 447}, {238, 447}, {282, 447}, {314, 447}, {317, 447}, {336, 447}, {337, 447}, {449, 447}, {450, 447}, {126, 448}, {132, 448}, {138, 448}, {139, 448}, {165, 448}, {184, 448}, {201, 448}, {208, 448}, {209, 448}, {230, 448}, {231, 448}, {315, 448}, {316, 448}, {336, 448}, {337, 448}, {138, 449}, {139, 449}, {160, 449}, {200, 449}, {202, 449}, {208, 449}, {209, 449}, {229, 449}, {231, 449}, {128, 450}, {129, 450}, {130, 450}, {160, 450}, {180, 450}, {181, 450}, {182, 450}, {186, 450}, {187, 450}, {188, 450}, {200, 450}, {202, 450}, {230, 450}, {160, 451}, {201, 451}, {387, 451}, {395, 451}, {184, 452}, {386, 452}, {388, 452}, {394, 452}, {396, 452}, {184, 453}, {196, 453}, {197, 453}, {205, 453}, {206, 453}, {385, 453}, {388, 453}, {394, 453}, {396, 453}, {184, 454}, {195, 454}, {198, 454}, {204, 454}, {207, 454}, {354, 454}, {355, 454}, {386, 454}, {387, 454}, {395, 454}, {196, 455}, {197, 455}, {205, 455}, {206, 455}, {354, 455}, {356, 455}, {320, 456}, {321, 456}, {355, 456}, {356, 456}, {382, 456}, {383, 456}, {384, 456}, {450, 456}, {451, 456}, {201, 457}, {319, 457}, {322, 457}, {395, 457}, {396, 457}, {450, 457}, {451, 457}, {183, 458}, {200, 458}, {202, 458}, {222, 458}, {320, 458}, {321, 458}, {386, 458}, {395, 458}, {396, 458}, {133, 459}, {134, 459}, {177, 459}, {183, 459}, {200, 459}, {202, 459}, {222, 459}, {386, 459}, {466, 459}, {467, 459}, {52, 460}, {132, 460}, {135, 460}, {176, 460}, {178, 460}, {183, 460}, {201, 460}, {222, 460}, {344, 460}, {345, 460}, {386, 460}, {465, 460}, {468, 460}, {50, 461}, {52, 461}, {133, 461}, {134, 461}, {158, 461}, {159, 461}, {175, 461}, {178, 461}, {344, 461}, {345, 461}, {441, 461}, {442, 461}, {466, 461}, {467, 461}, {51, 462}, {52, 462}, {98, 462}, {99, 462}, {158, 462}, {159, 462}, {176, 462}, {177, 462}, {292, 462}, {440, 462}, {443, 462}, {98, 463}, {99, 463}, {204, 463}, {205, 463}, {292, 463}, {441, 463}, {442, 463}, {173, 464}, {174, 464}, {203, 464}, {206, 464}, {292, 464}, {109, 465}, {110, 465}, {119, 465}, {120, 465}, {130, 465}, {131, 465}, {173, 465}, {174, 465}, {204, 465}, {205, 465}, {314, 465}, {95, 466}, {108, 466}, {111, 466}, {119, 466}, {120, 466}, {130, 466}, {131, 466}, {288, 466}, {289, 466}, {290, 466}, {294, 466}, {295, 466}, {296, 466}, {313, 466}, {315, 466}, {94, 467}, {96, 467}, {109, 467}, {110, 467}, {313, 467}, {315, 467}, {457, 467}, {458, 467}, {94, 468}, {96, 468}, {210, 468}, {292, 468}, {314, 468}, {348, 468}, {349, 468}, {457, 468}, {458, 468}, {95, 469}, {209, 469}, {211, 469}, {292, 469}, {348, 469}, {349, 469}, {165, 470}, {166, 470}, {209, 470}, {211, 470}, {228, 470}, {229, 470}, {292, 470}, {113, 471}, {114, 471}, {164, 471}, {167, 471}, {210, 471}, {227, 471}, {230, 471}, {113, 472}, {114, 472}, {134, 472}, {135, 472}, {136, 472}, {164, 472}, {167, 472}, {222, 472}, {223, 472}, {228, 472}, {229, 472}, {165, 473}, {166, 473}, {222, 473}, {223, 473}, {245, 473}, {313, 473}, {314, 473}, {215, 474}, {216, 474}, {232, 474}, {233, 474}, {234, 474}, {243, 474}, {244, 474}, {313, 474}, {314, 474}, {417, 474}, {418, 474}, {215, 475}, {216, 475}, {243, 475}, {244, 475}, {246, 475}, {247, 475}, {248, 475}, {416, 475}, {419, 475}, {463, 475}, {464, 475}, {465, 475}, {475, 475}, {476, 475}, {477, 475}, {230, 476}, {236, 476}, {243, 476}, {244, 476}, {246, 476}, {258, 476}, {259, 476}, {260, 476}, {416, 476}, {419, 476}, {111, 477}, {139, 477}, {230, 477}, {236, 477}, {247, 477}, {248, 477}, {312, 477}, {313, 477}, {417, 477}, {418, 477}, {461, 477}, {467, 477}, {110, 478}, {112, 478}, {138, 478}, {140, 478}, {230, 478}, {236, 478}, {250, 478}, {251, 478}, {311, 478}, {314, 478}, {338, 478}, {339, 478}, {461, 478}, {467, 478}, {111, 479}, {138, 479}, {140, 479}, {203, 479}, {204, 479}, {205, 479}, {249, 479}, {250, 479}, {251, 479}, {277, 479}, {278, 479}, {279, 479}, {312, 479}, {313, 479}, {338, 479}, {340, 479}, {461, 479}, {467, 479}, {120, 480}, {121, 480}, {123, 480}, {124, 480}, {139, 480}, {199, 480}, {232, 480}, {233, 480}, {234, 480}, {249, 480}, {250, 480}, {251, 480}, {270, 480}, {271, 480}, {339, 480}, {340, 480}, {486, 480}, {487, 480}, {120, 481}, {121, 481}, {123, 481}, {124, 481}, {198, 481}, {200, 481}, {229, 481}, {248, 481}, {250, 481}, {270, 481}, {271, 481}, {304, 481}, {305, 481}, {424, 481}, {425, 481}, {438, 481}, {463, 481}, {464, 481}, {465, 481}, {469, 481}, {470, 481}, {486, 481}, {487, 481}, {102, 482}, {103, 482}, {198, 482}, {200, 482}, {228, 482}, {230, 482}, {248, 482}, {249, 482}, {304, 482}, {305, 482}, {424, 482}, {425, 482}, {437, 482}, {439, 482}, {469, 482}, {470, 482}, {101, 483}, {104, 483}, {199, 483}, {228, 483}, {230, 483}, {249, 483}, {437, 483}, {439, 483}, {102, 484}, {103, 484}, {229, 484}, {307, 484}, {308, 484}, {433, 484}, {438, 484}, {99, 485}, {194, 485}, {195, 485}, {307, 485}, {308, 485}, {432, 485}, {434, 485}, {99, 486}, {193, 486}, {196, 486}, {235, 486}, {236, 486}, {237, 486}, {432, 486}, {433, 486}, {99, 487}, {163, 487}, {164, 487}, {194, 487}, {195, 487}, {272, 487}, {273, 487}, {459, 487}, {153, 488}, {163, 488}, {164, 488}, {198, 488}, {199, 488}, {200, 488}, {233, 488}, {272, 488}, {274, 488}, {458, 488}, {460, 488}, {146, 489}, {147, 489}, {152, 489}, {154, 489}, {220, 489}, {221, 489}, {233, 489}, {273, 489}, {280, 489}, {281, 489}, {282, 489}, {458, 489}, {459, 489}, {146, 490}, {147, 490}, {152, 490}, {154, 490}, {196, 490}, {202, 490}, {219, 490}, {222, 490}, {233, 490}, {245, 490}, {451, 490}, {452, 490}, {87, 491}, {88, 491}, {153, 491}, {196, 491}, {202, 491}, {220, 491}, {221, 491}, {245, 491}, {451, 491}, {452, 491}, {87, 492}, {88, 492}, {108, 492}, {109, 492}, {110, 492}, {196, 492}, {202, 492}, {235, 492}, {236, 492}, {237, 492}, {245, 492}, {437, 492}, {465, 492}, {466, 492}, {155, 493}, {156, 493}, {437, 493}, {465, 493}, {466, 493}, {106, 494}, {112, 494}, {121, 494}, {133, 494}, {134, 494}, {154, 494}, {157, 494}, {198, 494}, {199, 494}, {200, 494}, {268, 494}, {437, 494}, {106, 495}, {112, 495}, {121, 495}, {133, 495}, {134, 495}, {154, 495}, {156, 495}, {267, 495}, {269, 495}, {285, 495}, {286, 495}, {106, 496}, {112, 496}, {121, 496}, {132, 496}, {155, 496}, {219, 496}, {220, 496}, {267, 496}, {269, 496}, {285, 496}, {286, 496}, {131, 497}, {132, 497}, {134, 497}, {164, 497}, {218, 497}, {221, 497}, {234, 497}, {235, 497}, {268, 497}, {464, 497}, {465, 497}, {108, 498}, {109, 498}, {110, 498}, {117, 498}, {118, 498}, {119, 498}, {131, 498}, {164, 498}, {219, 498}, {220, 498}, {233, 498}, {236, 498}, {463, 498}, {466, 498}, {131, 499}, {155, 499}, {164, 499}, {234, 499}, {235, 499}, {464, 499}, {466, 499}, {472, 499}, {473, 499}, {79, 500}, {80, 500}, {81, 500}, {131, 500}, {132, 500}, {133, 500}, {155, 500}, {159, 500}, {160, 500}, {465, 500}, {471, 500}, {474, 500}, {128, 501}, {129, 501}, {131, 501}, {132, 501}, {150, 501}, {151, 501}, {155, 501}, {158, 501}, {161, 501}, {228, 501}, {229, 501}, {472, 501}, {473, 501}, {77, 502}, {129, 502}, {131, 502}, {150, 502}, {151, 502}, {159, 502}, {160, 502}, {228, 502}, {229, 502}, {468, 502}, {469, 502}, {77, 503}, {127, 503}, {128, 503}, {129, 503}, {130, 503}, {160, 503}, {161, 503}, {468, 503}, {469, 503}, {77, 504}, {126, 504}, {161, 504}, {162, 504}, {125, 505}, {130, 505}, {160, 505}, {163, 505}, {218, 505}, {219, 505}, {254, 505}, {276, 505}, {277, 505}, {74, 506}, {79, 506}, {80, 506}, {81, 506}, {126, 506}, {130, 506}, {131, 506}, {160, 506}, {162, 506}, {163, 506}, {218, 506}, {219, 506}, {254, 506}, {276, 506}, {277, 506}, {73, 507}, {75, 507}, {109, 507}, {110, 507}, {126, 507}, {127, 507}, {130, 507}, {163, 507}, {164, 507}, {254, 507}, {73, 508}, {75, 508}, {108, 508}, {111, 508}, {127, 508}, {130, 508}, {151, 508}, {152, 508}, {159, 508}, {160, 508}, {163, 508}, {164, 508}, {266, 508}, {74, 509}, {101, 509}, {102, 509}, {108, 509}, {110, 509}, {114, 509}, {115, 509}, {116, 509}, {130, 509}, {151, 509}, {152, 509}, {159, 509}, {160, 509}, {266, 509}, {101, 510}, {102, 510}, {109, 510}, {127, 510}, {130, 510}, {137, 510}, {138, 510}, {139, 510}, {158, 510}, {161, 510}, {164, 510}, {266, 510}, {88, 511}, {89, 511}, {128, 511}, {129, 511}, {137, 511}, {140, 511}, {141, 511}, {157, 511}, {159, 511},
+ },
+ }},
+
// Special test to be used to generate traces - not a real test
//{"trace", args{
// p: golParams{
@@ -266,12 +310,21 @@ func Test(t *testing.T) {
// },
//}},
}
+
+ start := time.Now()
+ // Networking
+ fmt.Println("Waiting for", clientNumber, "clients to connect.")
+ clients = processClients(clientNumber)
+ fmt.Println("Waited for", time.Since(start))
+
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- alive := gameOfLife(test.args.p, nil)
- //fmt.Println("Ran test:", test.name)
- if test.name != "trace" {
- assert.ElementsMatch(t, alive, test.args.expectedAlive)
+ if test.args.p.imageHeight >= clientNumber {
+ alive := gameOfLife(test.args.p, nil, clientNumber, clients)
+ //fmt.Println("Ran test:", test.name)
+ if test.name != "trace" {
+ assert.ElementsMatch(t, alive, test.args.expectedAlive)
+ }
}
})
}
@@ -284,7 +337,7 @@ func Benchmark(b *testing.B) {
name string
p golParams
}{
- {
+ /*{
"16x16x2", golParams{
turns: benchLength,
threads: 2,
@@ -403,12 +456,87 @@ func Benchmark(b *testing.B) {
imageWidth: 512,
imageHeight: 512,
}},
+
+ */
+
+ {
+ "512x512x32", golParams{
+ turns: benchLength,
+ threads: 32,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x64", golParams{
+ turns: benchLength,
+ threads: 64,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x96", golParams{
+ turns: benchLength,
+ threads: 96,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x128", golParams{
+ turns: benchLength,
+ threads: 128,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x192", golParams{
+ turns: benchLength,
+ threads: 192,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x256", golParams{
+ turns: benchLength,
+ threads: 256,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "5120x5120x128", golParams{
+ turns: 250,
+ threads: 128,
+ imageWidth: 5120,
+ imageHeight: 5120,
+ }},
+
+ {
+ "5120x5120x192", golParams{
+ turns: 250,
+ threads: 192,
+ imageWidth: 5120,
+ imageHeight: 5120,
+ }},
+
+ {
+ "5120x5120x256", golParams{
+ turns: 250,
+ threads: 256,
+ imageWidth: 5120,
+ imageHeight: 5120,
+ }},
}
+
for _, bm := range benchmarks {
os.Stdout = nil // Disable all program output apart from benchmark results
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
- gameOfLife(bm.p, nil)
+ gameOfLife(bm.p, nil, clientNumber, clients)
//fmt.Println("Ran bench:", bm.name)
}
})
diff --git a/src/worker/worker.go b/src/worker/worker.go
new file mode 100644
index 0000000..cf32f86
--- /dev/null
+++ b/src/worker/worker.go
@@ -0,0 +1,521 @@
+package main
+
+import (
+ "encoding/gob"
+ "flag"
+ "fmt"
+ "io"
+ "net"
+)
+
+const defaultHostname = "192.168.0.9"
+
+const INIT = 0
+
+const (
+ pause = iota
+ ping = iota
+ resume = iota
+ quit = iota
+ save = iota
+)
+
+// Client initialization packet
+type initPacket struct {
+ Clients int
+ Workers int
+ IpBefore, IpAfter string
+ Turns int
+ Width int
+}
+
+// Individual worker initialisation packet
+type workerPacket struct {
+ StartX int
+ EndX int
+ World [][]byte
+ Index int
+}
+
+// Sent localDistributor packet
+type outgoingDistPacket struct {
+ Index int
+ Type int
+ Data int
+ OutputWorld [][]byte
+}
+
+// Received localDistributor packet
+type incomingDistPacket struct {
+ Index, Data int
+}
+
+type haloPacket struct {
+ Index int
+ Data []byte
+}
+
+type workerData struct {
+ inputHalo [2]chan byte
+ outputHalo [2]chan byte
+ distributorInput chan int
+ localDistributor chan byte
+}
+
+// Makes a matrix slice
+func makeMatrix(width, height int) [][]byte {
+ M := make([][]byte, height)
+ for i := range M {
+ M[i] = make([]byte, width)
+ }
+ return M
+}
+
+// Returns the new state of a cell from the number of alive neighbours and current state
+func getNewState(numberOfAlive int, cellState bool) int {
+ if cellState == true {
+ if numberOfAlive < 2 {
+ return -1
+ }
+ if numberOfAlive <= 3 {
+ return 0
+ }
+ if numberOfAlive > 3 {
+ return -1
+ }
+ } else {
+ if numberOfAlive == 3 {
+ return 1
+ }
+ }
+ return 0
+}
+
+func try(error error) {
+ if error != nil {
+ fmt.Println("Err", error)
+ }
+}
+
+/* NETWORKING */
+
+// Send external halos to another client
+func serveToClient(conn net.Conn, index int, c chan byte, width int, turns int, exit chan byte) {
+ enc := gob.NewEncoder(conn)
+ for i := 0; i < turns; i++ {
+ var haloData = make([]byte, width)
+
+ for i := 0; i < width; i++ {
+ haloData[i] = <-c
+ }
+
+ err := enc.Encode(haloPacket{index, haloData})
+ if err != nil {
+ fmt.Println("err", err)
+ break
+ }
+ }
+ exit <- 1
+}
+
+// Wait for 2 other clients to connect to this one
+func waitForClients(clients []net.Conn, done chan net.Listener, listening chan byte) {
+ ln, err := net.Listen("tcp4", ":4001")
+ if err != nil {
+ fmt.Println("err", err)
+ return
+ }
+ listening <- 1
+
+ if ln != nil {
+ for i := 0; i < 2; i++ {
+ conn, _ := ln.Accept()
+ clients[i] = conn
+ }
+ done <- ln
+ }
+}
+
+// Receive external halos from another client
+func receiveFromClient(ip string, c [2]chan byte, turns int, exit chan byte) {
+ conn, err := net.Dial("tcp4", ip+":4001")
+ if err != nil {
+ fmt.Println("err", err)
+ exit <- 1
+ return
+ }
+
+ dec := gob.NewDecoder(conn)
+ for i := 0; i < turns; i++ {
+ var haloP haloPacket
+
+ err := dec.Decode(&haloP)
+ if err != nil {
+ fmt.Println("err", err)
+ return
+ }
+
+ // Take bytes from haloData row slice and put them in the channel
+ for _, b := range haloP.Data {
+ c[haloP.Index] <- b
+ }
+ }
+ exit <- 1
+}
+
+// Receive data from distributor server
+func receiveFromDistributor(decoder *gob.Decoder, channels []workerData, exit chan byte) {
+ for {
+ var p incomingDistPacket
+ try(decoder.Decode(&p))
+
+ // Exit signal
+ if p.Index == -1 {
+ break
+ }
+ channels[p.Index].distributorInput <- p.Data
+ }
+ exit <- 1
+}
+
+// With the help of the distributor server, sync all clients to the same state
+func syncWithOtherClients(encoder *gob.Encoder, decoder *gob.Decoder) {
+ // This client is ready to receive
+ try(encoder.Encode(1))
+
+ var p int
+ try(decoder.Decode(&p))
+ // All clients now ready to receive
+ if p != 1 {
+ fmt.Println("Received mismatched value from distributor")
+ }
+}
+
+/* ***** */
+
+// Receive halo, or receive command from localDistributor
+func receiveOrInterrupt(world [][]byte, data workerData, encoder *gob.Encoder, workerIndex, turn int, halo *bool, stopAtTurn *int, lineToReceive, haloIndex int) {
+ select {
+ case c := <-data.inputHalo[haloIndex]:
+ length := len(world[lineToReceive])
+ world[lineToReceive][0] = c
+ for j := 1; j < length; j++ {
+ world[lineToReceive][j] = <-data.inputHalo[haloIndex]
+ }
+ *halo = true
+ case <-data.distributorInput:
+ try(encoder.Encode(&outgoingDistPacket{workerIndex, 0, turn, nil}))
+ *stopAtTurn = <-data.distributorInput
+ }
+}
+
+// Send halo, or receive command from localDistributor
+func sendOrInterrupt(world [][]byte, data workerData, encoder *gob.Encoder, workerIndex, turn int, out *bool, stopAtTurn *int, lineToSend, haloIndex int) {
+ select {
+ case data.outputHalo[haloIndex] <- world[lineToSend][0]:
+ length := len(world[lineToSend])
+ for j := 1; j < length; j++ {
+ data.outputHalo[haloIndex] <- world[lineToSend][j]
+ }
+ *out = true
+ case <-data.distributorInput:
+ try(encoder.Encode(&outgoingDistPacket{workerIndex, 0, turn, nil}))
+ *stopAtTurn = <-data.distributorInput
+ }
+}
+
+func worker(p initPacket, data workerData, wp workerPacket, encoder *gob.Encoder) {
+ endX := wp.EndX
+ startX := wp.StartX
+ endY := p.Width
+ startY := 0
+
+ world := makeMatrix(endY-startY, endX-startX+2)
+ newWorld := makeMatrix(endY-startY, endX-startX+2)
+
+ // Receive initial world from localDistributor
+ for i := range world {
+ for j := 0; j < endY; j++ {
+ newWorld[i][j] = wp.World[i][j]
+ world[i][j] = newWorld[i][j]
+ }
+ }
+
+ halo0, halo1 := true, true
+ stopAtTurn := -2
+
+ for turn := 0; turn < p.Turns; {
+
+ // This is the turn in which all workers synchronise and stop
+ if turn == stopAtTurn+1 {
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 0, pause, nil}))
+ // Process IO
+ for {
+ r := <-data.distributorInput
+ if r == resume {
+ break
+ } else if r == save {
+ // Send the world to the localDistributor
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 1, 0, newWorld[1 : endX-startX+1]}))
+ } else if r == quit {
+ data.localDistributor <- 1
+ return
+ } else if r == ping {
+ // Send the number of alive cells to the localDistributor
+ alive := 0
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ if newWorld[i][j] == 0xFF {
+ alive++
+ }
+ }
+ }
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 0, alive, nil}))
+ break
+ }
+ }
+ }
+
+ // Get halos or command
+ if turn != 0 {
+ // Either receive the top halo, or a command from localDistributor
+ if !halo0 {
+ receiveOrInterrupt(world, data, encoder, wp.Index, turn, &halo0, &stopAtTurn, 0, 0)
+ }
+ // Either receive the bottom halo, or a command from localDistributor
+ if !halo1 {
+ receiveOrInterrupt(world, data, encoder, wp.Index, turn, &halo1, &stopAtTurn, endX-startX+1, 1)
+ }
+ }
+
+ // Move on to next turn
+ if halo0 && halo1 {
+
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ // Compute alive neighbours
+ aliveNeighbours := 0
+ yp1 := (j + 1) % endY
+ ym1 := j - 1
+ if ym1 < 0 {
+ ym1 += endY
+ }
+
+ aliveNeighbours = int(world[i+1][j]) + int(world[i-1][j]) +
+ int(world[i][yp1]) + int(world[i][ym1]) +
+ int(world[i+1][yp1]) + int(world[i+1][ym1]) +
+ int(world[i-1][yp1]) + int(world[i-1][ym1])
+
+ switch getNewState(aliveNeighbours/255, world[i][j] == 0xFF) {
+ case -1:
+ newWorld[i][j] = 0x00
+ case 1:
+ newWorld[i][j] = 0xFF
+ case 0:
+ newWorld[i][j] = world[i][j]
+ }
+ }
+ }
+ halo0, halo1 = false, false
+ turn++
+
+ // Try sending the halos, or a command from localDistributor
+ out0, out1 := false, false
+ for !(out0 && out1) {
+ if !out0 {
+ sendOrInterrupt(newWorld, data, encoder, wp.Index, turn, &out0, &stopAtTurn, 1, 0)
+ }
+ if !out1 {
+ sendOrInterrupt(newWorld, data, encoder, wp.Index, turn, &out1, &stopAtTurn, endX-startX, 1)
+ }
+ }
+
+ // Update old world
+ for i := range world {
+ copy(world[i], newWorld[i])
+ }
+ }
+
+ }
+
+ // Send the final world
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, 1, 0, newWorld[1 : endX-startX+1]}))
+ // Done
+ try(encoder.Encode(&outgoingDistPacket{wp.Index, -1, -1, nil}))
+ data.localDistributor <- 0
+}
+
+// Initialise all worker channels
+func initialiseChannels(data []workerData, workers, clients, imageWidth, i int) {
+ data[i].inputHalo[0] = make(chan byte, imageWidth)
+ data[i].inputHalo[1] = make(chan byte, imageWidth)
+ data[i].localDistributor = make(chan byte)
+ data[i].distributorInput = make(chan int, 1)
+
+ if workers == 1 && clients > 1 { // Just one worker but more than one client => channels are external
+ data[0].outputHalo[0] = make(chan byte, imageWidth)
+ data[0].outputHalo[1] = make(chan byte, imageWidth)
+ } else if workers == 1 { // Just one client, with just one worker => channels are internal
+ data[0].outputHalo[0] = data[0].inputHalo[1]
+ data[0].outputHalo[1] = data[0].inputHalo[0]
+ } else if i == 0 { // >1 client & >1 worker AND this is the first channel => it is connected externally
+ data[0].outputHalo[0] = make(chan byte, imageWidth)
+ data[i+1].outputHalo[0] = data[i].inputHalo[1]
+ } else if i == workers-1 { // >1 client & >1 worker AND this is the last channel => it is connected externally
+ data[workers-1].outputHalo[1] = make(chan byte, imageWidth)
+ data[i-1].outputHalo[1] = data[i].inputHalo[0]
+ } else { // Normal internal channels
+ data[i-1].outputHalo[1] = data[i].inputHalo[0]
+ data[i+1].outputHalo[0] = data[i].inputHalo[1]
+ }
+}
+
+func localDistributor(encoder *gob.Encoder, decoder *gob.Decoder, exitThread []chan byte) int {
+ var haloClients = make([]net.Conn, 2)
+ var done = make(chan net.Listener)
+ var listening = make(chan byte)
+ var initP initPacket
+ var ln net.Listener = nil
+ var r byte
+
+ // Get initialisation packet
+ try(decoder.Decode(&initP))
+
+ // Wait for external halo connections
+ if initP.Clients != 1 {
+ go waitForClients(haloClients, done, listening)
+ }
+
+ // Receive and initialise worker channels
+ workerData := make([]workerData, initP.Workers)
+ workerPackets := make([]workerPacket, initP.Workers)
+ for i := 0; i < initP.Workers; i++ {
+ var w workerPacket
+ try(decoder.Decode(&w))
+
+ workerPackets[i] = w
+ initialiseChannels(workerData, initP.Workers, initP.Clients, initP.Width, i)
+ }
+
+ // Wait until the program binds to port, then sync to this point with all clients.
+ // At this point all of them are listening and ready for connections
+ if initP.Clients > 1 {
+ <-listening
+ }
+ syncWithOtherClients(encoder, decoder)
+
+ // Connect to external halo sockets
+ if initP.Clients > 1 {
+ go receiveFromClient(initP.IpBefore, [2]chan byte{workerData[0].inputHalo[0], workerData[initP.Workers-1].inputHalo[1]}, initP.Turns, exitThread[1])
+ go receiveFromClient(initP.IpAfter, [2]chan byte{workerData[0].inputHalo[0], workerData[initP.Workers-1].inputHalo[1]}, initP.Turns, exitThread[2])
+
+ ln = <-done
+ }
+
+ // Distributor server receiver thread
+ go receiveFromDistributor(decoder, workerData, exitThread[0])
+
+ // External halo serving functions
+ if initP.Clients != 1 {
+ ip0, _, _ := net.SplitHostPort(haloClients[0].RemoteAddr().String())
+ ip1, _, _ := net.SplitHostPort(haloClients[1].RemoteAddr().String())
+ if ip0 == initP.IpBefore && ip1 == initP.IpAfter {
+ go serveToClient(haloClients[0], 1, workerData[0].outputHalo[0], initP.Width, initP.Turns, exitThread[3])
+ go serveToClient(haloClients[1], 0, workerData[initP.Workers-1].outputHalo[1], initP.Width, initP.Turns, exitThread[4])
+ } else if ip0 == initP.IpAfter && ip1 == initP.IpBefore {
+ go serveToClient(haloClients[1], 1, workerData[0].outputHalo[0], initP.Width, initP.Turns, exitThread[3])
+ go serveToClient(haloClients[0], 0, workerData[initP.Workers-1].outputHalo[1], initP.Width, initP.Turns, exitThread[4])
+ } else {
+ fmt.Println("IPs are mismatched")
+ }
+ } else {
+ // Only local workers, no external halos
+ workerData[initP.Workers-1].outputHalo[1] = workerData[0].inputHalo[0]
+ workerData[0].outputHalo[0] = workerData[initP.Workers-1].inputHalo[1]
+ }
+
+ // Start all workers
+ for i := 0; i < initP.Workers; i++ {
+ go worker(initP, workerData[i], workerPackets[i], encoder)
+ }
+ fmt.Println("All workers active")
+
+ // Wait for workers to finish work
+ for i := 0; i < initP.Workers; i++ {
+ r = <-workerData[i].localDistributor
+ }
+ fmt.Println("All workers finished")
+
+ // Close connections, if open
+ if ln != nil {
+ try(ln.Close())
+ }
+
+ // Tell main what happened
+ if r == 1 {
+ return -1 // Quit command
+ }
+ if initP.Clients == 1 {
+ return 1 // Just local client, no halo sockets
+ }
+ return 0 // Normal termination
+}
+
+// Game of Life client
+func main() {
+ // process hostname flag
+ var hostname string
+ flag.StringVar(&hostname, "hostname", defaultHostname, "The hostname of the server.")
+ flag.Parse()
+
+ // Connect to server
+ fmt.Println("Connecting to", hostname)
+
+ conn, err := net.Dial("tcp4", hostname+":4000")
+ if err != nil {
+ fmt.Println("Server is offline")
+ return
+ }
+ fmt.Println("Connected to server")
+
+ // Create channels used on networking thread exit
+ exitThread := make([]chan byte, 5)
+ for i := 0; i < 5; i++ {
+ exitThread[i] = make(chan byte, 1)
+ }
+
+ // Main server encoder and decoder
+ enc := gob.NewEncoder(conn)
+ dec := gob.NewDecoder(conn)
+
+ for {
+ var packetType = 0
+ err = dec.Decode(&packetType)
+ if err != nil { // If server is no longer responding, the work is done.
+ if err == io.EOF {
+ fmt.Println("Connection closed, exiting worker.")
+ return
+ }
+ fmt.Println("err", err)
+ return
+ }
+
+ // Run game of life
+ if packetType == INIT {
+ fmt.Println("Starting workers..")
+ result := localDistributor(enc, dec, exitThread)
+ waitForX := 5
+
+ if result == -1 || result == 1 {
+ waitForX = 1
+ }
+ for i := 0; i < waitForX; i++ {
+ <-exitThread[i]
+ }
+
+ if result == -1 { // If quit command, quit worker program
+ return
+ }
+ }
+ }
+}
diff --git a/submission/GameOfLife.tex b/submission/GameOfLife.tex
new file mode 100644
index 0000000..d52884f
--- /dev/null
+++ b/submission/GameOfLife.tex
@@ -0,0 +1,222 @@
+\documentclass[a4, 11pt]{article}
+
+\usepackage{graphics,graphicx}
+\usepackage{multicol}
+\usepackage{parskip}
+\usepackage{amsmath}
+\usepackage{multirow}
+\usepackage[utf8]{inputenc}
+\usepackage{fancyhdr}
+\usepackage[title]{appendix}
+\usepackage{wasysym}
+\usepackage{url}
+\usepackage{pgfplots}
+\usepackage{pgfplotstable}
+\usepackage{wrapfig,lipsum,booktabs}
+\usepackage{booktabs}
+\usepackage{float}
+
+\usepackage{booktabs, makecell, tabularx}
+
+\usepackage[font=footnotesize,labelfont=small]{caption}
+\usepackage[compact]{titlesec}
+\captionsetup{width=0.85\linewidth}
+
+\RequirePackage{geometry}
+\geometry{margin=1.5cm}
+
+\setlength{\parskip}{0cm}
+\setlength{\parindent}{0pt}
+
+\usepackage{titling}
+
+\setlength{\droptitle}{-4em}
+
+\title{Concurrent Computing\\Game of Life}
+\author{Razvan David (oh18767)\\Stefan Pruna (mc18112)}
+
+
+\begin{document}
+\maketitle
+
+\section{Introduction}
+\label{sec:intro}
+\begin{multicols}{2}
+
+Game of Life is a zero-player game, it requires no further input other than the initial state of the board. It follows a set of rules with the help of which the board is evolved to the final state. A cell's evolution is determined only by its neighbouring cells and its own state. This means that a Game of Life simulation can be heavily parallelized. This report aims to explore the performance differences between implementations of Game of Life in Go, with different levels of parallelism.
+
+\section{Functionality and Design }
+
+A \textit{worker} is a single-threaded function that simulates Game of Life on a part of the board. The \textit{distributor} is a function that controls the workers and sends them the board. A \textit{halo} can be defined as one of the two extra rows of the board that a worker needs in order to compute the next turn, the row of cells before the first row on which the worker simulates, and the one after the last computed row. \\
+In order to compare the performance gains of parallel computation applied to Game of Life, we implemented simulations using different levels of concurrency. Starting from no concurrency at all, all the way to a multi-server implementation using network sockets over which halos are sent.
+
+\subsection{Different levels of parallelism}
+
+\subsubsection{Sequential and naive parallelism}
+While in the sequential implementation of the simulation, a singular worker evolves the whole board, in the naive parallel version, multiple workers evolve the board. In this naive implementation, the workers send and receive the board and the halos to and from the distributor at each turn. To make the program more efficient, worker functions are only called once, at the beginning of the simulation. Data is sent and received between the workers and the distributor via byte channels. The work is divided evenly between the workers, such that the maximum size difference between any worker's part of the board is 1 row. Our implementation supports any number of workers.
+
+\subsubsection{Improved parallelism}
+In the naive parallel implementation, the distributor receives and sends back the board from and to the workers at each turn. This is a bottleneck, as a worker has to wait for the distributor to collect the new board rows from other workers, reconstruct the board, and send the new rows back, all sequentially. \\
+The optimisation comes from the observation that workers only need to receive the halos with the current board state in order to process the next board state. As such, the distributor is only needed to send the initial board to the workers and to process user interaction.
+
+
+\subsection{User interaction}
+
+Users can interact with a simulation by pausing it, saving the current state of it to file, or stopping it. The number of alive cells on the board is printed every 2 seconds. This means that for all implementations, some form of control and synchronisation of the workers is required.
+
+\subsubsection{Parallel interaction}
+In the sequential and naive parallel implementations of Game of Life, the workers interact with the distributor on every turn. For better performance, in the improved parallel version, apart from sending the initial board, the distributor does not interact with the workers at all. The workers can continue running on their own, and if they finish before being interrupted by the distributor, they will send the board back. On an IO event such as pause, the distributor synchronises the workers to the same turn. It does this by querying them for their current turn, then telling all of them to pause at the maximum turn any of them has reached. For the world to be saved, the distributor must first synchronise and pause all the workers, then request the world. On a timer event, the distributor synchronises the workers again and requests the number of alive cells from them.
+
+\section{Performance}
+
+After running a set of 10 benchmarks on each implementation and recording results, we observed a significant decrease in performance when comparing the naive parallel implementation to the sequential implementation. As it can be seen in Table 1, the naive parallel simulation performed about 2.85 times slower than the sequential simulation. In contrast, the improved parallel version performed about 1.49 times faster than the sequential program.
+
+\vspace{0.5cm}
+
+\begin{center}Table 1: Performance of different implementations\end{center}
+\vspace{0.2cm}
+\resizebox{\columnwidth}{!}{%
+\centering
+\begin{tabular}{c|c|c}
+\hline
+Implementation & Mean time & Standard deviation \\
+\hline
+Sequential & 37.14s & 1.54s \\
+Naive parallel & 105.73s & 1.92s \\
+Improved parallel & 24.94s & 1.22s \\
+\hline
+\end{tabular}
+}
+\vspace{0.4cm}
+
+\subsection{Explaining results}
+\subsubsection{Sequential and naive parallel}
+The degraded performance of the first parallel implementation of Game of Life we benchmarked is caused by a bottleneck in the distributor. The distributor receives, reconstructs and sends the board back to each worker on every turn. The workers can start computing the next turn when they receive the data from the distributor, but this data is sent to the workers in sequential order. By the time the second worker has received the needed data, the first worker is almost done with computing its part of the new board state, as the computation itself does not take a significantly longer time than the transfer of data over byte channels. That being said, by the time the distributor has sent the data to all the workers, a large portion of them are already done and ready to send the data back. The sequential simulation is faster because the single worker that computes the new state of the board always has access to the whole board, and it doesn't need to send or wait to receive data over channels.
+
+\subsubsection{Improved parallelism}
+
+After the distributor has sent the workers their part of the initial board state, they only require the top and bottom halos in order to compute the next state of the board. The halo data that a worker needs in order to continue computation is computed by another worker. In the naive implementation, this data, but also the rest of a worker's section of the board, is sent to the distributor and back each turn. To optimise the naive implementation, the halos are exchanged from one worker to the other, over byte channels, such that no data is needed from the distributor, except for computing the first turn.
+
+\paragraph{Quantifying parallelism}
+To further analyse the difference between the naive parallel implementation and the improved parallel implementation, we ran 10 benchmarks on the 512x512 board. We measured how the performance of our solution scales with the number of threads (workers). The results are shown in Figure 1. The performance has been normalised such that for each implementation, the fastest result is 1. It can be seen that the naive parallel implementation does not scale at all, while the improved parallel implementation scales.
+
+
+\vspace{0.5cm}
+\resizebox{\columnwidth}{!}{%
+\begin{tikzpicture}\vspace{0.5cm}
+
+\begin{axis}[xbar,enlargelimits=0.15,
+ title style={at={(0.5,-0.25)},anchor=north},
+ xlabel={realtive performance},
+ xtick={1,1.25, 1.5, 1.75, 2, 2.25, 2.5},
+ ylabel={workers},
+ ytick{1,2,3},
+ yticklabels = { , , 2, , 4, , 8},
+ title={Figure 1: scalability of parallel implementations},]
+\addplot
+[draw=blue,pattern=horizontal lines light blue]
+coordinates
+ {(1.002, 1) (1, 2) (1.04, 3)};
+
+
+\addplot
+[draw=black,pattern=horizontal lines dark blue]
+coordinates
+ {(2.078, 1) (1.452, 2) (1, 3)};
+\end{axis}
+\end{tikzpicture}
+}
+
+\paragraph{User interaction}
+In the improved parallel solution that uses the halo exchange idea described previously, user interaction can also be implemented in two ways, one slower than the other. The slower idea was for the distributor to send a signal to each worker on every turn. If there are no interactions, the distributor would send the workers a $continue$ signal. For pausing, the distributor would send them a $pause$ signal, and so on. This is slow, as the workers have to tell the distributor they have completed a turn and then wait for the distributor's signal. We improved this slow version by only sending workers signals when there is an event other than $continue$. By not synchronising workers at each turn, they can be computing different turns at some point in time. This problem was solved by synchronising them to the maximum reached turn, as explained in section 2.2.1.
+
+\subsection{General optimisations}
+By using CPU profiling, we were able to conclude that the initial implementations of two of our functions were performing very badly. The first function computed the modulus of negative numbers, but we found no way of improving its performance, so we decided to limit its usage. The second function counted the number of alive neighbours of a cell, and in doing so used the aforementioned modulus function and a $for$ loop. We decided to unroll the $for$ loop and add the values of the neighbouring cells instead of using $if$ statements to check if they are alive. After testing both implementations 10 times, we observed an increase in performance of 1.79 times.
+
+\subsection{Comparing to the baseline}
+Our final parallel implementation completes the benchmark in 24.94 seconds on average.
+The mean relative performance when comparing to the baseline 10 times is shown in Table 2. Although much faster, we concluded that our implementation scales worse than the baseline, as the relative performance decreases with more workers.
+\vspace{0.5cm}
+
+\begin{center}Table 2: Comparing with baseline\end{center}
+\vspace{0.2cm}
+\begin{center}
+\begin{tabular}{c|c}
+\hline
+Benchmark & Relative performance \\
+\hline
+128x128x2 & 402.7\% \\
+128x128x4 & 352,4\% \\
+128x128x8 & 322.5\% \\
+\hline
+\end{tabular}
+\end{center}
+\vspace{0.4cm}
+
+\subsection{Potential improvements}
+
+Our Game of Life implementation has the potential to be improved even more. One improvement could be sending the x and y coordinates of alive cells in the halo rows between workers, instead of sending each byte. This should improve performance when boards are not very dense with alive cells. Another obvious improvement to performance would be using more workers (threads), but this is limited by hardware, as a CPU rarely has over 64 threads. We implemented a multi-server version of the simulation, which sends data over the network.
+
+\section{Networking}
+
+The idea behind the multi-server version of our implementation is simple. We created a new client program that contains just the worker and connection managing logic, and modified our original program to be the main distributor server that controls the clients.
+
+\subsection{Multi-server functionality and Design}
+The server program can handle any number of client instances. These client instances can also have any number of worker instances running on them. For example, if we have 5 servers, one of them is going to host the server program and the other 4 are going to host client programs, each of which can have any number of workers running on them. The client programs connect to the distributor server program, which gives them the initial board sections and necessary information to connect to each other, which is needed in order for halos to be exchanged. User interaction is still functional in this implementation.
+
+\subsection{Linking channels with TCP sockets}
+Continuing with the 4 client example, let's assume that each client is assigned 8 workers by the distributor server. Internally, the workers can exchange halos between them in the same way as in the local version of Game of Life, over byte channels. There will be, however, one or two workers running on this client that each need to exchange two halos to workers on another client instance. This is done by essentially replacing the halo channels with TCP sockets, and sending and receiving data to and from the required clients over the network. In order to avoid sending the halo data through the distributor server, which would result in worse performance, each client listens to a TCP socket, waiting for two other clients to connect to it.
+
+\subsection{Benchmarking}
+By adding multi-server support over networking, we have created additional overhead. We compared our multi-server implementation to the local implementation, and observed that the networking overhead degrades performance (Figure 2). All tests were executed 5 times. The mean of the results was used. We tested the local implementation on a 96 threaded server. The multi-server version used one distributor server with 16 threads and 16 client servers with 8 threads each. These servers were compute-optimised AWS instances.
+
+\vspace{0.3cm}
+\resizebox{\columnwidth}{!}{%
+\begin{tikzpicture}
+\begin{axis}[
+ title style={at={(0.5,-0.25)},anchor=north},
+ title={Figure 2: multi-server and local comparison},
+ xlabel={workers},
+ ylabel={milliseconds / op},
+ xmin=0, xmax=150,
+ ymin=250, ymax=850,
+ xtick={16,32,64,96,128},
+ ytick={250,350,450,550,650,750},
+ legend pos=north west,
+ ymajorgrids=true,
+ grid style=dashed,
+]
+
+
+\addplot[
+ color=blue,
+ mark=square,
+ ]
+ coordinates {
+ (16,652) (32,457) (64,388) (96,369) (128,363)
+
+ };
+
+ \addplot[
+ color=black,
+ mark=square,
+ ]
+ coordinates {
+
+ (16,521) (32,403) (64,356) (96, 363) (128, 384)
+ };
+\addlegendentry{16 client servers, 8 threads each}
+\addlegendentry{local simulation, 96 thread server}
+\end{axis}
+\end{tikzpicture}
+}%
+
+We concluded that the performance difference between the two versions decreases as the number of workers increases. The multi-server version outperforms the local version when using 128 workers, because the server that the local version was tested on only had 96 threads, the maximum available for compute optimized EC2 instances. This is also why the performance difference decreases as the worker number gets bigger, since there are other $goroutines$ besides the workers and even other programs running on the server, so our local version gets slower.
+
+ \section{Conclusion}
+
+We have implemented and analysed different versions of the Game of Life simulation, with different levels of parallelism, and even with networking capabilities. By identifying bottlenecks and thinking of inventive ways of further improving performance, we obtained a local version that performs more than 3.2 times faster than the baseline, on average.\\ The Game of Life simulation is infinitely scalable, but the number of threads CPUs have is limited, so we created a multi-server version that can use any number of CPUs and tested it on AWS instances.
+
+
+\end{multicols}
+\end{document}
\ No newline at end of file
diff --git a/submission/stage4/Makefile b/submission/stage4/Makefile
new file mode 100644
index 0000000..f289db6
--- /dev/null
+++ b/submission/stage4/Makefile
@@ -0,0 +1,78 @@
+focus ='worker|distributor'
+ignore = 'strings|fmt'
+
+gol:
+ go build
+ ./gameoflife
+
+
+# Add -run /[NAME]
+# eg: -run /16x16x2-0
+# to run a specific test
+test:
+ go test
+
+
+# Use -benchtime [TIME][UNIT]
+# eg: -benchtime 60s
+# to force the benchmark to run for the specified amount of time
+
+# Use -bench /[NAME]
+# eg: -bench /16x16x2
+# to run a specific benchmark
+
+# bench will run all tests before benchmarking - they must all pass
+bench:
+ go test -bench .
+
+compare:
+ ./comparison/compare.sh
+
+trace:
+ go test -run=Test/trace -trace trace.out
+ go tool trace trace.out
+
+
+# Requires graphviz to work correctly
+cpuprofile:
+ go test -bench /512x512x8 -cpuprofile cpu.prof
+ go tool pprof -pdf -nodefraction=0 -unit=ms -focus=$(focus) -ignore=$(ignore) cpu.prof
+
+
+# Interactive mode - Useful commands:
+# top10
+# list worker
+# list distributor
+cpuprofile-i:
+ go test -bench /512x512x8 -cpuprofile cpu.prof
+ go tool pprof -nodefraction=0 -unit=ms -focus=$(focus) -ignore=$(ignore) cpu.prof
+
+
+# Requires graphviz to work correctly
+memprofile:
+ go test -bench /512x512x8 -memprofile mem.prof --memprofilerate=1
+ go tool pprof -pdf -alloc_space -nodefraction=0 -unit=B -focus=$(focus) -ignore=$(ignore) mem.prof
+
+
+# Interactive mode - Useful commands:
+# top10
+# list worker
+# list distributor
+memprofile-i:
+ go test -bench /512x512x8 -memprofile mem.prof --memprofilerate=1
+ go tool pprof -alloc_space -nodefraction=0 -unit=B -focus=$(focus) -ignore=$(ignore) mem.prof
+
+
+perf:
+ sudo perf stat -d go test -bench /512x512x2
+ sudo perf stat -d go test -bench /512x512x4
+ sudo perf stat -d go test -bench /512x512x8
+
+
+time:
+ time go test -bench /512x512x2
+ time go test -bench /512x512x4
+ time go test -bench /512x512x8
+
+
+.PHONY: gameoflife compare baseline baseline.test
diff --git a/submission/stage4/baseline b/submission/stage4/baseline
new file mode 100755
index 0000000..d213c40
Binary files /dev/null and b/submission/stage4/baseline differ
diff --git a/submission/stage4/comparison/compare.go b/submission/stage4/comparison/compare.go
new file mode 100644
index 0000000..6777288
--- /dev/null
+++ b/submission/stage4/comparison/compare.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "text/tabwriter"
+)
+
+type bench struct {
+ name string
+ result int64
+}
+
+type time struct {
+ result int
+}
+
+func check(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
+
+func readCpuTimes(file []byte) []time {
+ numberRegex, _ := regexp.Compile(`\d+`)
+ rows := numberRegex.FindAllString(string(file), -1)
+ times := make([]time, len(rows))
+ for i, row := range rows {
+ result, err := strconv.Atoi(row)
+ check(err)
+ times[i] = time{result:result}
+ }
+ return times
+}
+
+//noinspection GoUnhandledErrorResult
+func analyseCpuTimes() {
+ baseBenchmarksFile, err := ioutil.ReadFile(os.Args[3])
+ check(err)
+ baseBenchmarks := readBenchmarks(baseBenchmarksFile)
+
+ baseCpuTimesFile, err := ioutil.ReadFile(os.Args[1])
+ check(err)
+ newCpuTimesFile, err := ioutil.ReadFile(os.Args[2])
+ check(err)
+ baseTimes := readCpuTimes(baseCpuTimesFile)
+ newTimes := readCpuTimes(newCpuTimesFile)
+
+ if len(baseTimes) != len(newTimes) {
+ panic("CPU time lengths don't match")
+ }
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
+ defer w.Flush()
+ fmt.Fprintln(w, "Benchmark\tBaseline CPU usage\tYour CPU usage\t% Difference")
+ fmt.Fprintln(w, "\t\t\tThe smaller the better")
+ for i, b := range baseBenchmarks {
+ fmt.Fprintln(w, b.name, "\t", baseTimes[i].result, "%\t", newTimes[i].result, "%\t", baseTimes[i].result*100/newTimes[i].result, "%")
+ }
+ fmt.Fprintln(w, "This is the percentage of the CPU that this job got. It's computed as (U + S) / E")
+ fmt.Fprintln(w, "Where")
+ fmt.Fprintln(w, "U\tTotal number of CPU-seconds that the process spent in user mode.")
+ fmt.Fprintln(w, "S\tTotal number of CPU-seconds that the process spent in kernel mode.")
+ fmt.Fprintln(w, "E\tElapsed real time")
+}
+
+func readBenchmarks(file []byte) []bench {
+ numberRegex, _ := regexp.Compile(`\d+`)
+ rowRegex, _ := regexp.Compile(`\d+x\d+x\d+-?\d*\s+\d+\s+\d+ ns/op`)
+ rows := rowRegex.FindAllString(string(file), -1)
+ benchmarks := make([]bench, len(rows))
+ for i, row := range rows {
+ fields := strings.Fields(row)
+ result, err := strconv.ParseInt(numberRegex.FindString(fields[2]), 10, 64)
+ check(err)
+ benchmarks[i] = bench{name: fields[0], result: result}
+ }
+ return benchmarks
+}
+
+//noinspection GoUnhandledErrorResult
+func analyseBenchmarks() {
+ baseBenchmarksFile, err := ioutil.ReadFile(os.Args[3])
+ check(err)
+ newBenchmarksFile, err := ioutil.ReadFile(os.Args[4])
+ check(err)
+ baseBenchmarks := readBenchmarks(baseBenchmarksFile)
+ newBenchmarks := readBenchmarks(newBenchmarksFile)
+ if len(baseBenchmarks) != len(newBenchmarks) {
+ panic("Benchmark lengths don't match")
+ }
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
+ defer w.Flush()
+ fmt.Fprintln(w, "Benchmark\tBaseline result\tYour result\t% Difference")
+ fmt.Fprintln(w, "\t(ns/1000 turns)\t(ns/1000 turns)\tThe bigger the better")
+ for i, b := range baseBenchmarks {
+ baselineResult := b.result
+ newResult := newBenchmarks[i].result
+ fmt.Fprintln(w, b.name, "\t", baselineResult, "\t", newResult, "\t", baselineResult*100/newResult, "%")
+
+ }
+}
+
+func main() {
+ fmt.Println()
+ fmt.Println("TIME RESULTS")
+ analyseBenchmarks()
+ fmt.Println()
+ fmt.Println("CPU USAGE RESULTS")
+ analyseCpuTimes()
+}
diff --git a/submission/stage4/comparison/compare.sh b/submission/stage4/comparison/compare.sh
new file mode 100755
index 0000000..cd87f7c
--- /dev/null
+++ b/submission/stage4/comparison/compare.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+rm -f your-time.txt
+touch your-time.txt
+rm -f your-out.txt
+touch your-out.txt
+rm -f base-time.txt
+touch base-time.txt
+rm -f base-out.txt
+touch base-out.txt
+
+go test -c -o gameoflife.test
+
+echo "Benchmarking..."
+
+benchtime=10x
+
+#for b in 128x128x2 128x128x4 128x128x8 512x512x2 512x512x4 512x512x8
+for b in 128x128x2 128x128x4 128x128x8
+do
+ echo ${b} on your solution
+ \time -f '%P' -o your-time.txt -a ./gameoflife.test -test.run XXX -test.bench /${b} -test.benchtime ${benchtime} >> your-out.txt
+ echo ${b} on baseline solution
+ \time -f '%P' -o base-time.txt -a ./baseline.test -test.run XXX -test.bench /${b} -test.benchtime ${benchtime} >> base-out.txt
+done
+
+go build comparison/compare.go
+./compare base-time.txt your-time.txt base-out.txt your-out.txt
\ No newline at end of file
diff --git a/submission/stage4/control.go b/submission/stage4/control.go
new file mode 100644
index 0000000..d216b33
--- /dev/null
+++ b/submission/stage4/control.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "fmt"
+ "github.com/nsf/termbox-go"
+)
+
+// getKeyboardCommand sends all keys pressed on the keyboard as runes (characters) on the key chan.
+// getKeyboardCommand will NOT work if termbox isn't initialised (in startControlServer)
+func getKeyboardCommand(key chan<- rune) {
+ for {
+ event := termbox.PollEvent()
+ if event.Type == termbox.EventKey {
+ if event.Key != 0 {
+ key <- rune(event.Key)
+ } else if event.Ch != 0 {
+ key <- event.Ch
+ }
+ }
+ }
+}
+
+// startControlServer initialises termbox and prints basic information about the game configuration.
+func startControlServer(p golParams) {
+ e := termbox.Init()
+ check(e)
+
+ fmt.Println("Threads:", p.threads)
+ fmt.Println("Width:", p.imageWidth)
+ fmt.Println("Height:", p.imageHeight)
+}
+
+// stopControlServer closes termbox.
+// If the program is terminated without closing termbox the terminal window may misbehave.
+func StopControlServer() {
+ termbox.Close()
+}
diff --git a/submission/stage4/gameoflife b/submission/stage4/gameoflife
new file mode 100755
index 0000000..795a04e
Binary files /dev/null and b/submission/stage4/gameoflife differ
diff --git a/submission/stage4/go.mod b/submission/stage4/go.mod
new file mode 100644
index 0000000..0fcbfa1
--- /dev/null
+++ b/submission/stage4/go.mod
@@ -0,0 +1,10 @@
+module uk.ac.bris.cs/gameoflife
+
+go 1.12
+
+require (
+ github.com/ChrisGora/semaphore v1.0.0
+ github.com/mattn/go-runewidth v0.0.4 // indirect
+ github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e
+ github.com/stretchr/testify v1.3.0
+)
diff --git a/submission/stage4/go.sum b/submission/stage4/go.sum
new file mode 100644
index 0000000..215cd8c
--- /dev/null
+++ b/submission/stage4/go.sum
@@ -0,0 +1,14 @@
+github.com/ChrisGora/semaphore v1.0.0 h1:+8iNL5i426tqjHJwlqsMaH7D9eEiU5vmk00eEuqFdWo=
+github.com/ChrisGora/semaphore v1.0.0/go.mod h1:wwAuK+zqnPweFwSjwiQNtOsZua9khcA6q4/R04KQzxA=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
+github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e h1:Vbib8wJAaMEF9jusI/kMSYMr/LtRzM7+F9MJgt/nH8k=
+github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
diff --git a/submission/stage4/gol.go b/submission/stage4/gol.go
new file mode 100644
index 0000000..b1811dd
--- /dev/null
+++ b/submission/stage4/gol.go
@@ -0,0 +1,453 @@
+package main
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type workerChannel struct {
+ inputByte,
+ outputByte chan byte
+ inputHalo,
+ outputHalo [2]chan byte
+ distributorInput,
+ distributorOutput chan int
+}
+
+const (
+ pause = iota
+ ping = iota
+ resume = iota
+ quit = iota
+ save = iota
+)
+
+func positiveModulo(x, m int) int {
+ for x < 0 {
+ x += m
+ }
+ return x % m
+}
+
+// Returns the new state of a cell from the number of alive neighbours and current state
+func getNewState(numberOfAlive int, cellState bool) int {
+ if cellState == true {
+ if numberOfAlive < 2 || numberOfAlive > 3 {
+ return -1
+ }
+ if numberOfAlive <= 3 {
+ return 0
+ }
+ } else {
+ if numberOfAlive == 3 {
+ return 1
+ }
+ }
+ return 0
+}
+
+// Makes a matrix slice
+func makeMatrix(width, height int) [][]byte {
+ M := make([][]byte, height)
+ for i := range M {
+ M[i] = make([]byte, width)
+ }
+ return M
+}
+
+// Receive halo, or receive command from distributor
+func receiveOrInterrupt(world [][]byte, channels workerChannel, turn int, halo *bool, stopAtTurn *int, lineToReceive, haloIndex int) {
+ select {
+ case c := <-channels.inputHalo[haloIndex]:
+ length := len(world[lineToReceive])
+ world[lineToReceive][0] = c
+ for j := 1; j < length; j++ {
+ world[lineToReceive][j] = <-channels.inputHalo[haloIndex]
+ }
+ *halo = true
+ case <-channels.distributorInput:
+ channels.distributorOutput <- turn
+ *stopAtTurn = <-channels.distributorInput
+ }
+}
+
+// Send halo, or receive command from distributor
+func sendOrInterrupt(world [][]byte, channels workerChannel, turn int, out *bool, stopAtTurn *int, lineToSend, haloIndex int) {
+ select {
+ case channels.outputHalo[haloIndex] <- world[lineToSend][0]:
+ length := len(world[lineToSend])
+ for j := 1; j < length; j++ {
+ channels.outputHalo[haloIndex] <- world[lineToSend][j]
+ }
+ *out = true
+ case <-channels.distributorInput:
+ channels.distributorOutput <- turn
+ *stopAtTurn = <-channels.distributorInput
+ }
+}
+
+// Worker function
+func worker(p golParams, channels workerChannel, startX, endX, startY, endY int) {
+
+ world := makeMatrix(endY-startY, endX-startX+2)
+ newWorld := makeMatrix(endY-startY, endX-startX+2)
+
+ // Receive initial world from distributor
+ for i := range world {
+ for j := 0; j < p.imageWidth; j++ {
+ newWorld[i][j] = <-channels.inputByte
+ world[i][j] = newWorld[i][j]
+ }
+ }
+
+ halo0, halo1 := true, true
+ stopAtTurn := -2
+
+ for turn := 0; turn < p.turns; {
+
+ // This is the turn in which all workers synchronise and stop
+ if turn == stopAtTurn+1 {
+ channels.distributorOutput <- pause
+ // Process IO
+ for {
+ r := <-channels.distributorInput
+ if r == resume {
+ break
+ } else if r == save {
+ // Send the world to the distributor
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ channels.outputByte <- newWorld[i][j]
+ }
+ }
+ } else if r == quit {
+ return
+ } else if r == ping {
+ // Send the number of alive cells to the distributor
+ alive := 0
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ if newWorld[i][j] == 0xFF {
+ alive++
+ }
+ }
+ }
+ channels.distributorOutput <- alive
+ break
+ }
+ }
+ }
+
+ // Get halos or command
+ if turn != 0 {
+ // Either receive the top halo, or a command from distributor
+ if !halo0 {
+ receiveOrInterrupt(world, channels, turn, &halo0, &stopAtTurn, 0, 0)
+ }
+ // Either receive the bottom halo, or a command from distributor
+ if !halo1 {
+ receiveOrInterrupt(world, channels, turn, &halo1, &stopAtTurn, endX-startX+1, 1)
+ }
+ }
+
+ // Move on to next turn, if both halos are present
+ if halo0 && halo1 {
+ // Execute turn
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ // Compute alive neighbours
+ aliveNeighbours := 0
+
+ yp1 := (j + 1) % p.imageWidth
+ ym1 := j - 1
+ if ym1 < 0 {
+ ym1 += p.imageWidth
+ }
+
+ aliveNeighbours = int(world[i+1][j]) + int(world[i-1][j]) +
+ int(world[i][yp1]) + int(world[i][ym1]) +
+ int(world[i+1][yp1]) + int(world[i+1][ym1]) +
+ int(world[i-1][yp1]) + int(world[i-1][ym1])
+
+ switch getNewState(aliveNeighbours/255, world[i][j] == 0xFF) {
+ case -1:
+ newWorld[i][j] = 0x00
+ case 1:
+ newWorld[i][j] = 0xFF
+ case 0:
+ newWorld[i][j] = world[i][j]
+ }
+ }
+ }
+ halo0, halo1 = false, false
+ turn++
+
+ // Try sending the halos, or a command from distributor
+ out0, out1 := false, false
+ for !(out0 && out1) {
+ if !out0 {
+ sendOrInterrupt(newWorld, channels, turn, &out0, &stopAtTurn, 1, 0)
+ }
+ if !out1 {
+ sendOrInterrupt(newWorld, channels, turn, &out1, &stopAtTurn, endX-startX, 1)
+ }
+ }
+
+ // Update old world
+ for i := range world {
+ copy(world[i], newWorld[i])
+ }
+ }
+ }
+
+ // Send the world to the distributor
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ channels.outputByte <- newWorld[i][j]
+ }
+ }
+ // Done
+ channels.distributorOutput <- -1
+}
+
+// Sends world to output
+func outputWorld(p golParams, state int, d distributorChans, world [][]byte) {
+ d.io.command <- ioOutput
+ d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x") + "_state_" + strconv.Itoa(state)
+ for i := range world {
+ for j := range world[i] {
+ d.io.world <- world[i][j]
+ }
+ }
+}
+
+// Initialise worker channels
+func initialiseChannels(workerChannels []workerChannel, threadsSmall, threadsSmallHeight, threadsLargeHeight int, p golParams) {
+ threadHeight := threadsSmallHeight
+ for i := 0; i < p.threads; i++ {
+ if i == threadsSmall {
+ threadHeight = threadsLargeHeight
+ }
+ workerChannels[i].inputByte = make(chan byte, threadHeight+2)
+ workerChannels[i].outputByte = make(chan byte, threadHeight*p.imageWidth)
+ workerChannels[i].inputHalo[0] = make(chan byte, p.imageWidth)
+ workerChannels[i].inputHalo[1] = make(chan byte, p.imageWidth)
+ workerChannels[i].distributorInput = make(chan int, 1)
+ workerChannels[i].distributorOutput = make(chan int, 1)
+
+ // Link channels
+ workerChannels[positiveModulo(i-1, p.threads)].outputHalo[1] = workerChannels[i].inputHalo[0]
+ workerChannels[positiveModulo(i+1, p.threads)].outputHalo[0] = workerChannels[i].inputHalo[1]
+ }
+}
+
+func pauseWorkers(workerChannels []workerChannel, stopAtTurn *int) {
+ // Pause and get current turns
+ for _, channel := range workerChannels {
+ channel.distributorInput <- pause
+ }
+ for _, channel := range workerChannels {
+ t := <-channel.distributorOutput
+ if t > *stopAtTurn {
+ *stopAtTurn = t
+ }
+ }
+
+ // Tell all workers to stop after turn stopAtTurn
+ for _, channel := range workerChannels {
+ channel.distributorInput <- *stopAtTurn
+ }
+ for _, channel := range workerChannels {
+ r := <-channel.distributorOutput
+ if r != pause {
+ fmt.Println("Something has gone wrong, r =", r)
+ }
+ }
+}
+
+func receiveWorld(world [][]byte, workerChannels []workerChannel, threadsSmall, threadsSmallHeight, threadsLargeHeight int) {
+ startX := 0
+ endX := threadsSmallHeight
+ for i, channel := range workerChannels {
+ for x := 0; x < endX-startX; x++ {
+ length := len(world[x])
+ for y := 0; y < length; y++ {
+ world[x+startX][y] = <-channel.outputByte
+ }
+ }
+ // New startX, endX
+ startX = endX
+ if i < threadsSmall-1 {
+ endX += threadsSmallHeight
+ } else {
+ endX += threadsLargeHeight
+ }
+ }
+}
+
+// Sends data over all worker channels
+func sendToWorkers(workerChannels []workerChannel, data int) {
+ for _, channel := range workerChannels {
+ channel.distributorInput <- data
+ }
+}
+
+// Controls IO
+func workerController(p golParams, world [][]byte, workerChannels []workerChannel, d distributorChans, keyChan <-chan rune, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight int) {
+ stopAtTurn := 0
+ paused := false
+ timer := time.NewTimer(2 * time.Second)
+ for q := false; q != true; {
+ select {
+ case <-timer.C:
+ // Get alive cells
+ alive := 0
+ if !paused {
+ pauseWorkers(workerChannels, &stopAtTurn)
+ // Ping unpauses workers
+ sendToWorkers(workerChannels, ping)
+ for _, channel := range workerChannels {
+ alive += <-channel.distributorOutput
+ }
+ fmt.Println("There are", alive, "alive cells in the world.")
+ }
+
+ timer = time.NewTimer(2 * time.Second)
+ case k := <-keyChan:
+ if k == 'p' || k == 's' || k == 'q' {
+ // If not already paused
+ if !paused {
+ pauseWorkers(workerChannels, &stopAtTurn)
+ // Paused until resume
+ if k == 'p' {
+ fmt.Println("Pausing. The turn number", stopAtTurn+1, "is currently being processed.")
+ }
+ } else if k == 'p' { // If this was a pause command and we are already paused, resume
+ // Resume all workers
+ sendToWorkers(workerChannels, resume)
+ fmt.Println("Continuing.")
+ }
+ // If this was a save or quit command
+ if k == 's' || k == 'q' {
+ if k == 's' {
+ fmt.Println("Saving on turn", stopAtTurn)
+ } else {
+ fmt.Println("Saving and quitting on turn", stopAtTurn)
+ }
+ sendToWorkers(workerChannels, save)
+
+ // If paused just to save, unpause. If quit, don't unpause
+ if !paused && k == 's' {
+ sendToWorkers(workerChannels, resume)
+ }
+
+ // Receive and output world
+ receiveWorld(world, workerChannels, threadsSmall, threadsSmallHeight, threadsLargeHeight)
+ outputWorld(p, stopAtTurn, d, world)
+
+ // Quit workers
+ if k == 'q' {
+ q = true
+ sendToWorkers(workerChannels, quit)
+ }
+ }
+ // If this was a pause command, actually pause
+ if k == 'p' {
+ paused = !paused
+ }
+ }
+ case o := <-workerChannels[0].distributorOutput: // Workers are starting to finish
+ if o != -1 {
+ fmt.Println("Something has gone wrong, o =", o)
+ }
+ for i := 1; i < p.threads; i++ {
+ <-workerChannels[i].distributorOutput
+ }
+ // Receive the world and quit
+ receiveWorld(world, workerChannels, threadsSmall, threadsSmallHeight, threadsLargeHeight)
+ q = true
+ }
+ }
+}
+
+// distributor divides the work between workers and interacts with other goroutines.
+func distributor(p golParams, d distributorChans, alive chan []cell, keyChan <-chan rune) {
+
+ // Create the 2D slice to store the world.
+ world := makeMatrix(p.imageWidth, p.imageHeight)
+
+ // Request the io goroutine to read in the image with the given filename.
+ d.io.command <- ioInput
+ d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x")
+
+ // The io goroutine sends the requested image byte by byte, in rows.
+ for y := 0; y < p.imageHeight; y++ {
+ for x := 0; x < p.imageWidth; x++ {
+ val := <-d.io.inputVal
+ if val != 0 {
+ fmt.Println("Alive cell at", x, y)
+ world[y][x] = val
+ }
+ }
+ }
+
+ // Make channels
+ var chans = make([]chan [][]byte, p.threads)
+ for i := 0; i < p.threads; i++ {
+ chans[i] = make(chan [][]byte)
+ }
+
+ // Thread calculations
+ // 16x16 with 10 threads: 6 large threads with 2 height + 4 small threads with 1 height
+ threadsLarge := p.imageHeight % p.threads
+ threadsSmall := p.threads - p.imageHeight%p.threads
+ threadsLargeHeight := p.imageHeight/p.threads + 1
+ threadsSmallHeight := p.imageHeight / p.threads
+
+ // Worker channels
+ workerChannels := make([]workerChannel, p.threads)
+ initialiseChannels(workerChannels, threadsSmall, threadsSmallHeight, threadsLargeHeight, p)
+
+ // Start workers
+ startX := 0
+ endX := threadsSmallHeight
+ for i := 0; i < p.threads; i++ {
+ go worker(p, workerChannels[i], startX, endX, 0, p.imageWidth)
+ // Send initial world to worker
+ for x := startX - 1; x < endX+1; x++ {
+ for y := 0; y < p.imageWidth; y++ {
+ workerChannels[i].inputByte <- world[positiveModulo(x, p.imageHeight)][positiveModulo(y, p.imageWidth)]
+ }
+ }
+ // New startX, endX
+ startX = endX
+ if i < threadsSmall-1 {
+ endX += threadsSmallHeight
+ } else {
+ endX += threadsLargeHeight
+ }
+ }
+
+ // Process IO and control workers
+ workerController(p, world, workerChannels, d, keyChan, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight)
+
+ // Create an empty slice to store coordinates of cells that are still alive after p.turns are done.
+ var finalAlive []cell
+ // Go through the world and append the cells that are still alive.
+ for y := 0; y < p.imageHeight; y++ {
+ for x := 0; x < p.imageWidth; x++ {
+ if world[y][x] != 0 {
+ finalAlive = append(finalAlive, cell{x: x, y: y})
+ }
+ }
+ }
+
+ // Make sure that the Io has finished any output before exiting.
+ d.io.command <- ioCheckIdle
+ <-d.io.idle
+
+ // Return the coordinates of cells that are still alive.
+ alive <- finalAlive
+}
diff --git a/submission/stage4/images/128x128.pgm b/submission/stage4/images/128x128.pgm
new file mode 100644
index 0000000..c187b5d
Binary files /dev/null and b/submission/stage4/images/128x128.pgm differ
diff --git a/submission/stage4/images/16x16.pgm b/submission/stage4/images/16x16.pgm
new file mode 100644
index 0000000..945b809
Binary files /dev/null and b/submission/stage4/images/16x16.pgm differ
diff --git a/submission/stage4/images/256x256.pgm b/submission/stage4/images/256x256.pgm
new file mode 100644
index 0000000..4d24b49
Binary files /dev/null and b/submission/stage4/images/256x256.pgm differ
diff --git a/submission/stage4/images/512x512.pgm b/submission/stage4/images/512x512.pgm
new file mode 100644
index 0000000..fd0b54f
Binary files /dev/null and b/submission/stage4/images/512x512.pgm differ
diff --git a/submission/stage4/images/64x64.pgm b/submission/stage4/images/64x64.pgm
new file mode 100644
index 0000000..8fd5ab8
Binary files /dev/null and b/submission/stage4/images/64x64.pgm differ
diff --git a/submission/stage4/main.go b/submission/stage4/main.go
new file mode 100644
index 0000000..3cefe96
--- /dev/null
+++ b/submission/stage4/main.go
@@ -0,0 +1,133 @@
+package main
+
+import "flag"
+
+// golParams provides the details of how to run the Game of Life and which image to load.
+type golParams struct {
+ turns int
+ threads int
+ imageWidth int
+ imageHeight int
+}
+
+// ioCommand allows requesting behaviour from the io (pgm) goroutine.
+type ioCommand uint8
+
+// This is a way of creating enums in Go.
+// It will evaluate to:
+// ioOutput = 0
+// ioInput = 1
+// ioCheckIdle = 2
+const (
+ ioOutput ioCommand = iota
+ ioInput
+ ioCheckIdle
+)
+
+// cell is used as the return type for the testing framework.
+type cell struct {
+ x, y int
+}
+
+// distributorToIo defines all chans that the distributor goroutine will have to communicate with the io goroutine.
+// Note the restrictions on chans being send-only or receive-only to prevent bugs.
+type distributorToIo struct {
+ command chan<- ioCommand
+ idle <-chan bool
+
+ filename chan<- string
+ inputVal <-chan uint8
+ world chan<- byte
+}
+
+// ioToDistributor defines all chans that the io goroutine will have to communicate with the distributor goroutine.
+// Note the restrictions on chans being send-only or receive-only to prevent bugs.
+type ioToDistributor struct {
+ command <-chan ioCommand
+ idle chan<- bool
+
+ filename <-chan string
+ inputVal chan<- uint8
+ world <-chan byte
+}
+
+// distributorChans stores all the chans that the distributor goroutine will use.
+type distributorChans struct {
+ io distributorToIo
+}
+
+// ioChans stores all the chans that the io goroutine will use.
+type ioChans struct {
+ distributor ioToDistributor
+}
+
+// gameOfLife is the function called by the testing framework.
+// It makes some channels and starts relevant goroutines.
+// It places the created channels in the relevant structs.
+// It returns an array of alive cells returned by the distributor.
+func gameOfLife(p golParams, keyChan <-chan rune) []cell {
+ var dChans distributorChans
+ var ioChans ioChans
+
+ ioCommand := make(chan ioCommand)
+ dChans.io.command = ioCommand
+ ioChans.distributor.command = ioCommand
+
+ ioIdle := make(chan bool)
+ dChans.io.idle = ioIdle
+ ioChans.distributor.idle = ioIdle
+
+ ioFilename := make(chan string)
+ dChans.io.filename = ioFilename
+ ioChans.distributor.filename = ioFilename
+
+ inputVal := make(chan uint8)
+ dChans.io.inputVal = inputVal
+ ioChans.distributor.inputVal = inputVal
+
+ worldChan := make(chan byte)
+ dChans.io.world = worldChan
+ ioChans.distributor.world = worldChan
+
+ aliveCells := make(chan []cell)
+
+ go distributor(p, dChans, aliveCells, keyChan)
+ go pgmIo(p, ioChans)
+
+ alive := <-aliveCells
+ return alive
+}
+
+// main is the function called when starting Game of Life with 'make gol'
+// Do not edit until Stage 2.
+func main() {
+ var params golParams
+
+ flag.IntVar(
+ ¶ms.threads,
+ "t",
+ 8,
+ "Specify the number of worker threads to use. Defaults to 8.")
+
+ flag.IntVar(
+ ¶ms.imageWidth,
+ "w",
+ 512,
+ "Specify the width of the image. Defaults to 512.")
+
+ flag.IntVar(
+ ¶ms.imageHeight,
+ "h",
+ 512,
+ "Specify the height of the image. Defaults to 512.")
+
+ flag.Parse()
+
+ params.turns = 50000
+
+ startControlServer(params)
+ keyChan := make(chan rune)
+ go getKeyboardCommand(keyChan)
+ gameOfLife(params, keyChan)
+ StopControlServer()
+}
diff --git a/submission/stage4/main_test.go b/submission/stage4/main_test.go
new file mode 100644
index 0000000..7b0ac0e
--- /dev/null
+++ b/submission/stage4/main_test.go
@@ -0,0 +1,416 @@
+package main
+
+import (
+ "github.com/stretchr/testify/assert"
+ "os"
+ "testing"
+)
+
+func Test(t *testing.T) {
+ type args struct {
+ p golParams
+ expectedAlive []cell
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ {"16x16x2-0", args{
+ p: golParams{
+ turns: 0,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 4, y: 5},
+ {x: 5, y: 6},
+ {x: 3, y: 7},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ },
+ }},
+
+ {"16x16x4-0", args{
+ p: golParams{
+ turns: 0,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 4, y: 5},
+ {x: 5, y: 6},
+ {x: 3, y: 7},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ },
+ }},
+
+ {"16x16x8-0", args{
+ p: golParams{
+ turns: 0,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 4, y: 5},
+ {x: 5, y: 6},
+ {x: 3, y: 7},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ },
+ }},
+
+ {"16x16x2-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x4-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x8-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x2-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x4-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x8-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ // New 6, 10, 12 thread tests
+ {"16x16x6-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 6,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x10-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 10,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x12-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 12,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x6-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 6,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x10-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 10,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x12-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 12,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ // Special test to be used to generate traces - not a real test
+ //{"trace", args{
+ // p: golParams{
+ // turns: 10,
+ // threads: 4,
+ // imageWidth: 64,
+ // imageHeight: 64,
+ // },
+ //}},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ alive := gameOfLife(test.args.p, nil)
+ //fmt.Println("Ran test:", test.name)
+ if test.name != "trace" {
+ assert.ElementsMatch(t, alive, test.args.expectedAlive)
+ }
+ })
+ }
+}
+
+const benchLength = 1000
+
+func Benchmark(b *testing.B) {
+ benchmarks := []struct {
+ name string
+ p golParams
+ }{
+ {
+ "16x16x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ }},
+
+ {
+ "16x16x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ }},
+
+ {
+ "16x16x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ }},
+
+ {
+ "64x64x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 64,
+ imageHeight: 64,
+ }},
+
+ {
+ "64x64x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 64,
+ imageHeight: 64,
+ }},
+
+ {
+ "64x64x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 64,
+ imageHeight: 64,
+ }},
+
+ {
+ "128x128x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 128,
+ imageHeight: 128,
+ }},
+
+ {
+ "128x128x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 128,
+ imageHeight: 128,
+ }},
+
+ {
+ "128x128x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 128,
+ imageHeight: 128,
+ }},
+
+ {
+ "256x256x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 256,
+ imageHeight: 256,
+ }},
+
+ {
+ "256x256x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 256,
+ imageHeight: 256,
+ }},
+
+ {
+ "256x256x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 256,
+ imageHeight: 256,
+ }},
+
+ {
+ "512x512x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+ }
+ for _, bm := range benchmarks {
+ os.Stdout = nil // Disable all program output apart from benchmark results
+ b.Run(bm.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ gameOfLife(bm.p, nil)
+ //fmt.Println("Ran bench:", bm.name)
+ }
+ })
+ }
+}
diff --git a/submission/stage4/out/512x512_state_1104.pgm b/submission/stage4/out/512x512_state_1104.pgm
new file mode 100644
index 0000000..dc13abb
Binary files /dev/null and b/submission/stage4/out/512x512_state_1104.pgm differ
diff --git a/submission/stage4/out/512x512_state_2296.pgm b/submission/stage4/out/512x512_state_2296.pgm
new file mode 100644
index 0000000..d70fd5c
Binary files /dev/null and b/submission/stage4/out/512x512_state_2296.pgm differ
diff --git a/submission/stage4/pgm.go b/submission/stage4/pgm.go
new file mode 100644
index 0000000..e9d78cf
--- /dev/null
+++ b/submission/stage4/pgm.go
@@ -0,0 +1,111 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+)
+
+func check(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
+
+// writePgmImage receives an array of bytes and writes it to a pgm file.
+// Note that this function is incomplete. Use the commented-out for loop to receive data from the distributor.
+func writePgmImage(p golParams, i ioChans) {
+ _ = os.Mkdir("out", os.ModePerm)
+
+ filename := <-i.distributor.filename
+ file, ioError := os.Create("out/" + filename + ".pgm")
+ check(ioError)
+ defer file.Close()
+
+ _, _ = file.WriteString("P5\n")
+ //_, _ = file.WriteString("# PGM file writer by pnmmodules (https://github.com/owainkenwayucl/pnmmodules).\n")
+ _, _ = file.WriteString(strconv.Itoa(p.imageWidth))
+ _, _ = file.WriteString(" ")
+ _, _ = file.WriteString(strconv.Itoa(p.imageHeight))
+ _, _ = file.WriteString("\n")
+ _, _ = file.WriteString(strconv.Itoa(255))
+ _, _ = file.WriteString("\n")
+
+ world := make([][]byte, p.imageHeight)
+ for i := range world {
+ world[i] = make([]byte, p.imageWidth)
+ }
+
+ // Receives the world from the distributor
+ for x := 0; x < p.imageHeight; x++ {
+ for y := 0; y < p.imageWidth; y++ {
+ world[x][y] = <-i.distributor.world
+ }
+ }
+
+ for y := 0; y < p.imageHeight; y++ {
+ for x := 0; x < p.imageWidth; x++ {
+ _, ioError = file.Write([]byte{world[y][x]})
+ check(ioError)
+ }
+ }
+
+ ioError = file.Sync()
+ check(ioError)
+
+ fmt.Println("File", filename, "output done!")
+}
+
+// readPgmImage opens a pgm file and sends its data as an array of bytes.
+func readPgmImage(p golParams, i ioChans) {
+ filename := <-i.distributor.filename
+ data, ioError := ioutil.ReadFile("images/" + filename + ".pgm")
+ check(ioError)
+
+ fields := strings.Fields(string(data))
+
+ if fields[0] != "P5" {
+ panic("Not a pgm file")
+ }
+
+ width, _ := strconv.Atoi(fields[1])
+ if width != p.imageWidth {
+ panic("Incorrect width")
+ }
+
+ height, _ := strconv.Atoi(fields[2])
+ if height != p.imageHeight {
+ panic("Incorrect height")
+ }
+
+ maxval, _ := strconv.Atoi(fields[3])
+ if maxval != 255 {
+ panic("Incorrect maxval/bit depth")
+ }
+
+ image := []byte(fields[4])
+
+ for _, b := range image {
+ i.distributor.inputVal <- b
+ }
+
+ fmt.Println("File", filename, "input done!")
+}
+
+func pgmIo(p golParams, i ioChans) {
+ for {
+ select {
+ case command := <-i.distributor.command:
+ switch command {
+ case ioInput:
+ readPgmImage(p, i)
+ case ioOutput:
+ writePgmImage(p, i)
+ case ioCheckIdle:
+ i.distributor.idle <- true
+ }
+ }
+ }
+}
diff --git a/submission/stage4/report.pdf b/submission/stage4/report.pdf
new file mode 100644
index 0000000..0cbccb7
Binary files /dev/null and b/submission/stage4/report.pdf differ
diff --git a/submission/stage5/Makefile b/submission/stage5/Makefile
new file mode 100644
index 0000000..be5c6b1
--- /dev/null
+++ b/submission/stage5/Makefile
@@ -0,0 +1,79 @@
+
+focus ='worker|distributor'
+ignore = 'strings|fmt'
+
+gol:
+ go build
+ ./gameoflife
+
+
+# Add -run /[NAME]
+# eg: -run /16x16x2-0
+# to run a specific test
+test:
+ go test
+
+
+# Use -benchtime [TIME][UNIT]
+# eg: -benchtime 60s
+# to force the benchmark to run for the specified amount of time
+
+# Use -bench /[NAME]
+# eg: -bench /16x16x2
+# to run a specific benchmark
+
+# bench will run all tests before benchmarking - they must all pass
+bench:
+ go test -bench .
+
+compare:
+ ./comparison/compare.sh
+
+trace:
+ go test -run=Test/trace -trace trace.out
+ go tool trace trace.out
+
+
+# Requires graphviz to work correctly
+cpuprofile:
+ go test -bench /512x512x8 -cpuprofile cpu.prof
+ go tool pprof -pdf -nodefraction=0 -unit=ms -focus=$(focus) -ignore=$(ignore) cpu.prof
+
+
+# Interactive mode - Useful commands:
+# top10
+# list worker
+# list distributor
+cpuprofile-i:
+ go test -bench /512x512x8 -cpuprofile cpu.prof
+ go tool pprof -nodefraction=0 -unit=ms -focus=$(focus) -ignore=$(ignore) cpu.prof
+
+
+# Requires graphviz to work correctly
+memprofile:
+ go test -bench /512x512x8 -memprofile mem.prof --memprofilerate=1
+ go tool pprof -pdf -alloc_space -nodefraction=0 -unit=B -focus=$(focus) -ignore=$(ignore) mem.prof
+
+
+# Interactive mode - Useful commands:
+# top10
+# list worker
+# list distributor
+memprofile-i:
+ go test -bench /512x512x8 -memprofile mem.prof --memprofilerate=1
+ go tool pprof -alloc_space -nodefraction=0 -unit=B -focus=$(focus) -ignore=$(ignore) mem.prof
+
+
+perf:
+ sudo perf stat -d go test -bench /512x512x2
+ sudo perf stat -d go test -bench /512x512x4
+ sudo perf stat -d go test -bench /512x512x8
+
+
+time:
+ time go test -bench /512x512x2
+ time go test -bench /512x512x4
+ time go test -bench /512x512x8
+
+
+.PHONY: gameoflife compare baseline baseline.test
diff --git a/submission/stage5/base-time.txt b/submission/stage5/base-time.txt
new file mode 100644
index 0000000..1731218
--- /dev/null
+++ b/submission/stage5/base-time.txt
@@ -0,0 +1,6 @@
+Command exited with non-zero status 127
+?%
+Command exited with non-zero status 127
+?%
+Command exited with non-zero status 127
+?%
diff --git a/submission/stage5/baseline b/submission/stage5/baseline
new file mode 100755
index 0000000..d213c40
Binary files /dev/null and b/submission/stage5/baseline differ
diff --git a/submission/stage5/compare b/submission/stage5/compare
new file mode 100755
index 0000000..15bf7e5
Binary files /dev/null and b/submission/stage5/compare differ
diff --git a/submission/stage5/comparison/compare.go b/submission/stage5/comparison/compare.go
new file mode 100644
index 0000000..6777288
--- /dev/null
+++ b/submission/stage5/comparison/compare.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "text/tabwriter"
+)
+
+type bench struct {
+ name string
+ result int64
+}
+
+type time struct {
+ result int
+}
+
+func check(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
+
+func readCpuTimes(file []byte) []time {
+ numberRegex, _ := regexp.Compile(`\d+`)
+ rows := numberRegex.FindAllString(string(file), -1)
+ times := make([]time, len(rows))
+ for i, row := range rows {
+ result, err := strconv.Atoi(row)
+ check(err)
+ times[i] = time{result:result}
+ }
+ return times
+}
+
+//noinspection GoUnhandledErrorResult
+func analyseCpuTimes() {
+ baseBenchmarksFile, err := ioutil.ReadFile(os.Args[3])
+ check(err)
+ baseBenchmarks := readBenchmarks(baseBenchmarksFile)
+
+ baseCpuTimesFile, err := ioutil.ReadFile(os.Args[1])
+ check(err)
+ newCpuTimesFile, err := ioutil.ReadFile(os.Args[2])
+ check(err)
+ baseTimes := readCpuTimes(baseCpuTimesFile)
+ newTimes := readCpuTimes(newCpuTimesFile)
+
+ if len(baseTimes) != len(newTimes) {
+ panic("CPU time lengths don't match")
+ }
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
+ defer w.Flush()
+ fmt.Fprintln(w, "Benchmark\tBaseline CPU usage\tYour CPU usage\t% Difference")
+ fmt.Fprintln(w, "\t\t\tThe smaller the better")
+ for i, b := range baseBenchmarks {
+ fmt.Fprintln(w, b.name, "\t", baseTimes[i].result, "%\t", newTimes[i].result, "%\t", baseTimes[i].result*100/newTimes[i].result, "%")
+ }
+ fmt.Fprintln(w, "This is the percentage of the CPU that this job got. It's computed as (U + S) / E")
+ fmt.Fprintln(w, "Where")
+ fmt.Fprintln(w, "U\tTotal number of CPU-seconds that the process spent in user mode.")
+ fmt.Fprintln(w, "S\tTotal number of CPU-seconds that the process spent in kernel mode.")
+ fmt.Fprintln(w, "E\tElapsed real time")
+}
+
+func readBenchmarks(file []byte) []bench {
+ numberRegex, _ := regexp.Compile(`\d+`)
+ rowRegex, _ := regexp.Compile(`\d+x\d+x\d+-?\d*\s+\d+\s+\d+ ns/op`)
+ rows := rowRegex.FindAllString(string(file), -1)
+ benchmarks := make([]bench, len(rows))
+ for i, row := range rows {
+ fields := strings.Fields(row)
+ result, err := strconv.ParseInt(numberRegex.FindString(fields[2]), 10, 64)
+ check(err)
+ benchmarks[i] = bench{name: fields[0], result: result}
+ }
+ return benchmarks
+}
+
+//noinspection GoUnhandledErrorResult
+func analyseBenchmarks() {
+ baseBenchmarksFile, err := ioutil.ReadFile(os.Args[3])
+ check(err)
+ newBenchmarksFile, err := ioutil.ReadFile(os.Args[4])
+ check(err)
+ baseBenchmarks := readBenchmarks(baseBenchmarksFile)
+ newBenchmarks := readBenchmarks(newBenchmarksFile)
+ if len(baseBenchmarks) != len(newBenchmarks) {
+ panic("Benchmark lengths don't match")
+ }
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
+ defer w.Flush()
+ fmt.Fprintln(w, "Benchmark\tBaseline result\tYour result\t% Difference")
+ fmt.Fprintln(w, "\t(ns/1000 turns)\t(ns/1000 turns)\tThe bigger the better")
+ for i, b := range baseBenchmarks {
+ baselineResult := b.result
+ newResult := newBenchmarks[i].result
+ fmt.Fprintln(w, b.name, "\t", baselineResult, "\t", newResult, "\t", baselineResult*100/newResult, "%")
+
+ }
+}
+
+func main() {
+ fmt.Println()
+ fmt.Println("TIME RESULTS")
+ analyseBenchmarks()
+ fmt.Println()
+ fmt.Println("CPU USAGE RESULTS")
+ analyseCpuTimes()
+}
diff --git a/submission/stage5/comparison/compare.sh b/submission/stage5/comparison/compare.sh
new file mode 100755
index 0000000..cd87f7c
--- /dev/null
+++ b/submission/stage5/comparison/compare.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+rm -f your-time.txt
+touch your-time.txt
+rm -f your-out.txt
+touch your-out.txt
+rm -f base-time.txt
+touch base-time.txt
+rm -f base-out.txt
+touch base-out.txt
+
+go test -c -o gameoflife.test
+
+echo "Benchmarking..."
+
+benchtime=10x
+
+#for b in 128x128x2 128x128x4 128x128x8 512x512x2 512x512x4 512x512x8
+for b in 128x128x2 128x128x4 128x128x8
+do
+ echo ${b} on your solution
+ \time -f '%P' -o your-time.txt -a ./gameoflife.test -test.run XXX -test.bench /${b} -test.benchtime ${benchtime} >> your-out.txt
+ echo ${b} on baseline solution
+ \time -f '%P' -o base-time.txt -a ./baseline.test -test.run XXX -test.bench /${b} -test.benchtime ${benchtime} >> base-out.txt
+done
+
+go build comparison/compare.go
+./compare base-time.txt your-time.txt base-out.txt your-out.txt
\ No newline at end of file
diff --git a/submission/stage5/control.go b/submission/stage5/control.go
new file mode 100644
index 0000000..d216b33
--- /dev/null
+++ b/submission/stage5/control.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "fmt"
+ "github.com/nsf/termbox-go"
+)
+
+// getKeyboardCommand sends all keys pressed on the keyboard as runes (characters) on the key chan.
+// getKeyboardCommand will NOT work if termbox isn't initialised (in startControlServer)
+func getKeyboardCommand(key chan<- rune) {
+ for {
+ event := termbox.PollEvent()
+ if event.Type == termbox.EventKey {
+ if event.Key != 0 {
+ key <- rune(event.Key)
+ } else if event.Ch != 0 {
+ key <- event.Ch
+ }
+ }
+ }
+}
+
+// startControlServer initialises termbox and prints basic information about the game configuration.
+func startControlServer(p golParams) {
+ e := termbox.Init()
+ check(e)
+
+ fmt.Println("Threads:", p.threads)
+ fmt.Println("Width:", p.imageWidth)
+ fmt.Println("Height:", p.imageHeight)
+}
+
+// stopControlServer closes termbox.
+// If the program is terminated without closing termbox the terminal window may misbehave.
+func StopControlServer() {
+ termbox.Close()
+}
diff --git a/submission/stage5/gameoflife b/submission/stage5/gameoflife
new file mode 100755
index 0000000..2ec3cfe
Binary files /dev/null and b/submission/stage5/gameoflife differ
diff --git a/submission/stage5/go.mod b/submission/stage5/go.mod
new file mode 100644
index 0000000..0fcbfa1
--- /dev/null
+++ b/submission/stage5/go.mod
@@ -0,0 +1,10 @@
+module uk.ac.bris.cs/gameoflife
+
+go 1.12
+
+require (
+ github.com/ChrisGora/semaphore v1.0.0
+ github.com/mattn/go-runewidth v0.0.4 // indirect
+ github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e
+ github.com/stretchr/testify v1.3.0
+)
diff --git a/submission/stage5/go.sum b/submission/stage5/go.sum
new file mode 100644
index 0000000..215cd8c
--- /dev/null
+++ b/submission/stage5/go.sum
@@ -0,0 +1,14 @@
+github.com/ChrisGora/semaphore v1.0.0 h1:+8iNL5i426tqjHJwlqsMaH7D9eEiU5vmk00eEuqFdWo=
+github.com/ChrisGora/semaphore v1.0.0/go.mod h1:wwAuK+zqnPweFwSjwiQNtOsZua9khcA6q4/R04KQzxA=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
+github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e h1:Vbib8wJAaMEF9jusI/kMSYMr/LtRzM7+F9MJgt/nH8k=
+github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
diff --git a/submission/stage5/gol.go b/submission/stage5/gol.go
new file mode 100644
index 0000000..661ac97
--- /dev/null
+++ b/submission/stage5/gol.go
@@ -0,0 +1,439 @@
+package main
+
+import (
+ "encoding/gob"
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type workerData struct {
+ outputWorld chan [][]byte
+ distributorOutput chan int
+ encoder *gob.Encoder
+ index int
+}
+
+const (
+ pause = iota
+ ping = iota
+ resume = iota
+ quit = iota
+ save = iota
+)
+
+func positiveModulo(x, m int) int {
+ for x < 0 {
+ x += m
+ }
+ return x % m
+}
+
+// Makes a matrix slice
+func makeMatrix(width, height int) [][]byte {
+ M := make([][]byte, height)
+ for i := range M {
+ M[i] = make([]byte, width)
+ }
+ return M
+}
+
+// Sends world to output
+func outputWorld(p golParams, state int, d distributorChans, world [][]byte) {
+ d.io.command <- ioOutput
+ d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x") + "_state_" + strconv.Itoa(state)
+ for i := range world {
+ for j := range world[i] {
+ d.io.world <- world[i][j]
+ }
+ }
+}
+
+// initialise worker channels
+func initialiseChannels(workerChannels []workerData, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight int, p golParams) {
+ for i := 0; i < threadsSmall; i++ {
+ workerChannels[i].outputWorld = make(chan [][]byte, 1)
+
+ workerChannels[i].distributorOutput = make(chan int, 1)
+ }
+
+ for i := 0; i < threadsLarge; i++ {
+ workerChannels[i+threadsSmall].outputWorld = make(chan [][]byte, 1)
+
+ workerChannels[i+threadsSmall].distributorOutput = make(chan int, 1)
+ }
+}
+
+type controllerData struct {
+ Index, Data int
+}
+
+func encodeData(worker workerData, data int) {
+ p := controllerData{worker.index, data}
+ err := worker.encoder.Encode(&p)
+ if err != nil {
+ fmt.Println(err)
+ }
+}
+
+type clientData struct {
+ encoder *gob.Encoder
+ decoder *gob.Decoder
+ ip string
+}
+
+func pauseWorkers(workerData []workerData, stopAtTurn *int) {
+ // Pause and get current turns
+ for _, worker := range workerData {
+ encodeData(worker, pause)
+ }
+ for _, worker := range workerData {
+ t := <-worker.distributorOutput
+ if t > *stopAtTurn {
+ *stopAtTurn = t
+ }
+ }
+
+ // Tell all workers to stop after turn stopAtTurn
+ for _, worker := range workerData {
+ encodeData(worker, *stopAtTurn)
+ }
+ for _, channel := range workerData {
+ r := <-channel.distributorOutput
+ if r != pause {
+ fmt.Println("Something has gone wrong, r =", r)
+ }
+ }
+}
+
+func receiveWorld(world [][]byte, workerData []workerData, threadsSmall, threadsSmallHeight, threadsLargeHeight int) {
+ startX := 0
+ endX := threadsSmallHeight
+ for i, worker := range workerData {
+ tw := <-worker.outputWorld
+ for i := range tw {
+ copy(world[startX+i], tw[i])
+ }
+
+ // New startX, endX
+ startX = endX
+ if i < threadsSmall-1 {
+ endX += threadsSmallHeight
+ } else {
+ endX += threadsLargeHeight
+ }
+ }
+}
+
+// Sends data over all worker channels
+func sendToWorkers(workerData []workerData, data int) {
+ for _, worker := range workerData {
+ encodeData(worker, data)
+ }
+}
+
+func workerController(p golParams, world [][]byte, workerData []workerData, d distributorChans, keyChan <-chan rune, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight int) {
+ stopAtTurn := 0
+ paused := false
+ timer := time.NewTimer(2 * time.Second)
+ for q := false; q != true; {
+ select {
+ case <-timer.C:
+ // Get alive cells
+ alive := 0
+ if !paused {
+ pauseWorkers(workerData, &stopAtTurn)
+ // Ping unpauses workers
+ sendToWorkers(workerData, ping)
+ for _, channel := range workerData {
+ alive += <-channel.distributorOutput
+ }
+ fmt.Println("There are", alive, "alive cells in the world.")
+ }
+
+ timer = time.NewTimer(2 * time.Second)
+ case k := <-keyChan:
+ if k == 'p' || k == 's' || k == 'q' {
+ // If not already paused
+ if !paused {
+ pauseWorkers(workerData, &stopAtTurn)
+ // Paused until resume
+ if k == 'p' {
+ fmt.Println("Pausing. The turn number", stopAtTurn+1, "is currently being processed.")
+ }
+ } else if k == 'p' { // If this was a pause command and we are already paused, resume
+ // Resume all workers
+ sendToWorkers(workerData, resume)
+ fmt.Println("Continuing.")
+ }
+ // If this was a save or quit command
+ if k == 's' || k == 'q' {
+ if k == 's' {
+ fmt.Println("Saving on turn", stopAtTurn)
+ } else {
+ fmt.Println("Saving and quitting on turn", stopAtTurn)
+ }
+ sendToWorkers(workerData, save)
+
+ // If paused just to save, unpause. If quit, don't unpause
+ if !paused && k == 's' {
+ sendToWorkers(workerData, resume)
+ }
+
+ // Receive and output world
+ receiveWorld(world, workerData, threadsSmall, threadsSmallHeight, threadsLargeHeight)
+ outputWorld(p, stopAtTurn, d, world)
+
+ // Quit workers
+ if k == 'q' {
+ q = true
+ sendToWorkers(workerData, quit)
+ }
+ }
+ // If this was a pause command, actually pause
+ if k == 'p' {
+ paused = !paused
+ }
+ }
+ case o := <-workerData[0].distributorOutput: // Workers are starting to finish
+ if o != -1 {
+ fmt.Println("Something has gone wrong, o =", o)
+ }
+ for i := 1; i < p.threads; i++ {
+ <-workerData[i].distributorOutput
+ }
+ // Receive the world and quit
+ receiveWorld(world, workerData, threadsSmall, threadsSmallHeight, threadsLargeHeight)
+ q = true
+ }
+ }
+}
+
+type initPackage struct {
+ Clients int
+ Workers int
+ IpBefore, IpAfter string
+ Turns int
+ Width int
+}
+
+type workerPackage struct {
+ StartX int
+ EndX int
+ World [][]byte
+ Index int
+}
+
+type distributorPackage struct {
+ Index int
+ Type int
+ Data int
+ OutputWorld [][]byte
+}
+
+func listenToWorker(decoder *gob.Decoder, channel []workerData, workersServed int) {
+ for i := 0; i < workersServed; {
+ var p distributorPackage
+ err := decoder.Decode(&p)
+ if err != nil {
+ fmt.Println("Err", err)
+ }
+
+ if p.Type == 1 {
+ channel[p.Index].outputWorld <- p.OutputWorld
+ } else if p.Type == 0 {
+ channel[p.Index].distributorOutput <- p.Data
+ } else if p.Type == -1 {
+ channel[p.Index].distributorOutput <- p.Data
+ i++
+ }
+ }
+}
+
+const (
+ INIT = 0
+)
+
+func startWorkers(client clientData, initP initPackage, workerP []workerPackage, workerData []workerData) {
+ // The next packet is an init package
+ err := client.encoder.Encode(INIT)
+ if err != nil {
+ fmt.Println("Err", err)
+ }
+
+ // Send the init package
+ err = client.encoder.Encode(initP)
+ if err != nil {
+ fmt.Println("Err", err)
+ }
+ // Send worker packages
+ for i, p := range workerP {
+ workerData[i].encoder = client.encoder
+ workerData[i].index = i
+ err = client.encoder.Encode(p)
+ if err != nil {
+ fmt.Println("Err", err)
+ }
+ }
+
+ if err != nil {
+ fmt.Println("Err", err)
+ }
+}
+
+// distributor divides the work between workers and interacts with other goroutines.
+func distributor(p golParams, d distributorChans, alive chan []cell, keyChan <-chan rune, clients []clientData, clientNumber int) {
+
+ // Create the 2D slice to store the world.
+ world := makeMatrix(p.imageWidth, p.imageHeight)
+
+ // Request the io goroutine to read in the image with the given filename.
+ d.io.command <- ioInput
+ d.io.filename <- strings.Join([]string{strconv.Itoa(p.imageWidth), strconv.Itoa(p.imageHeight)}, "x")
+
+ // The io goroutine sends the requested image byte by byte, in rows.
+ for y := 0; y < p.imageHeight; y++ {
+ for x := 0; x < p.imageWidth; x++ {
+ val := <-d.io.inputVal
+ if val != 0 {
+ fmt.Println("Alive cell at", x, y)
+ world[y][x] = val
+ }
+ }
+ }
+
+ // Make channels
+ var chans = make([]chan [][]byte, p.threads)
+ for i := 0; i < p.threads; i++ {
+ chans[i] = make(chan [][]byte)
+ }
+
+ // Thread calculations
+ // 16x16 with 10 threads: 6 large threads with 2 height + 4 small threads with 1 height
+ threadsLarge := p.imageHeight % p.threads
+ threadsSmall := p.threads - p.imageHeight%p.threads
+ threadsLargeHeight := p.imageHeight/p.threads + 1
+ threadsSmallHeight := p.imageHeight / p.threads
+
+ // Worker channels
+ workerData := make([]workerData, p.threads)
+ initialiseChannels(workerData, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight, p)
+
+ // Threads per client
+ //clientLarge := p.threads % clientNumber
+ clientSmall := clientNumber - p.threads%clientNumber
+
+ clientLargeWorkers := p.threads/clientNumber + 1
+ clientSmallWorkers := p.threads / clientNumber
+
+ workerBounds := make([]workerPackage, p.threads)
+ t := 0
+
+ // Copy of world, but with extra 2 lines (one at the start, one at the end)
+ borderedWorld := make([][]byte, p.imageHeight+2)
+ for i := range world {
+ borderedWorld[i+1] = world[i]
+ }
+ borderedWorld[0] = world[p.imageHeight-1]
+ borderedWorld[p.imageHeight+1] = world[0]
+
+ // start workers
+ for i := 0; i < threadsSmall; i++ {
+ workerBounds[t] = workerPackage{
+ threadsSmallHeight * i,
+ threadsSmallHeight * (i + 1),
+ borderedWorld[(threadsSmallHeight)*i : (threadsSmallHeight)*(i+1)+2],
+ t,
+ }
+ t++
+ }
+ for i := 0; i < threadsLarge; i++ {
+ workerBounds[t] = workerPackage{
+ threadsSmallHeight*threadsSmall + threadsLargeHeight*i,
+ threadsSmallHeight*threadsSmall + threadsLargeHeight*(i+1),
+ borderedWorld[threadsSmallHeight*threadsSmall+threadsLargeHeight*i : threadsSmallHeight*threadsSmall+threadsLargeHeight*(i+1)+2],
+ t,
+ }
+ t++
+ }
+
+ t = 0
+ // Start workers on remote machines
+ for i := 0; i < clientNumber; i++ {
+ host0 := clients[positiveModulo(i-1, clientNumber)].ip
+ host1 := clients[positiveModulo(i+1, clientNumber)].ip
+ if i < clientSmall {
+ fmt.Println(clientSmallWorkers, "Workers started on client", i)
+ startWorkers(clients[i], initPackage{clientNumber, clientSmallWorkers, host0, host1, p.turns, p.imageWidth},
+ workerBounds[t:t+clientSmallWorkers], workerData[t:t+clientSmallWorkers])
+ t += clientSmallWorkers
+ } else {
+ fmt.Println(clientLargeWorkers, "Workers started on client", i)
+ startWorkers(clients[i], initPackage{clientNumber, clientLargeWorkers, host0, host1, p.turns, p.imageWidth},
+ workerBounds[t:t+clientLargeWorkers], workerData[t:t+clientLargeWorkers])
+ t += clientLargeWorkers
+ }
+ }
+
+ for i := 0; i < clientNumber; i++ {
+ var p int
+ err := clients[i].decoder.Decode(&p)
+
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ if p != 1 {
+ fmt.Println("Ready package mismatch")
+ }
+ }
+ for i := 0; i < clientNumber; i++ {
+ p := 1
+ err := clients[i].encoder.Encode(&p)
+
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ if i < clientSmall {
+ go listenToWorker(clients[i].decoder, workerData, clientSmallWorkers)
+ } else {
+ go listenToWorker(clients[i].decoder, workerData, clientLargeWorkers)
+ }
+ }
+
+ // Process IO and control workers
+ workerController(p, world, workerData, d, keyChan, threadsSmall, threadsSmallHeight, threadsLarge, threadsLargeHeight)
+
+ // Create an empty slice to store coordinates of cells that are still alive after p.turns are done.
+ var finalAlive []cell
+ // Go through the world and append the cells that are still alive.
+ for y := 0; y < p.imageHeight; y++ {
+ for x := 0; x < p.imageWidth; x++ {
+ if world[y][x] != 0 {
+ finalAlive = append(finalAlive, cell{x: x, y: y})
+ }
+ }
+ }
+
+ // Tell workers to exit listening functions
+ for i := 0; i < clientNumber; i++ {
+ err := clients[i].encoder.Encode(controllerData{
+ Index: -1,
+ Data: 0,
+ })
+ if err != nil {
+ fmt.Println(err)
+ }
+ }
+
+ //outputWorld(p, p.turns, d, world)
+
+ // Make sure that the Io has finished any output before exiting.
+ d.io.command <- ioCheckIdle
+ <-d.io.idle
+ // Return the coordinates of cells that are still alive.
+ alive <- finalAlive
+
+}
diff --git a/submission/stage5/images/128x128.pgm b/submission/stage5/images/128x128.pgm
new file mode 100644
index 0000000..c187b5d
Binary files /dev/null and b/submission/stage5/images/128x128.pgm differ
diff --git a/submission/stage5/images/16x16.pgm b/submission/stage5/images/16x16.pgm
new file mode 100644
index 0000000..945b809
Binary files /dev/null and b/submission/stage5/images/16x16.pgm differ
diff --git a/submission/stage5/images/256x256.pgm b/submission/stage5/images/256x256.pgm
new file mode 100644
index 0000000..4d24b49
Binary files /dev/null and b/submission/stage5/images/256x256.pgm differ
diff --git a/submission/stage5/images/512x512.pgm b/submission/stage5/images/512x512.pgm
new file mode 100644
index 0000000..fd0b54f
Binary files /dev/null and b/submission/stage5/images/512x512.pgm differ
diff --git a/submission/stage5/images/64x64.pgm b/submission/stage5/images/64x64.pgm
new file mode 100644
index 0000000..8fd5ab8
Binary files /dev/null and b/submission/stage5/images/64x64.pgm differ
diff --git a/submission/stage5/main.go b/submission/stage5/main.go
new file mode 100644
index 0000000..60683ef
--- /dev/null
+++ b/submission/stage5/main.go
@@ -0,0 +1,175 @@
+package main
+
+import (
+ "encoding/gob"
+ "flag"
+ "fmt"
+ "net"
+)
+
+const clientNumber = 1
+
+// golParams provides the details of how to run the Game of Life and which image to load.
+type golParams struct {
+ turns int
+ threads int
+ imageWidth int
+ imageHeight int
+}
+
+// ioCommand allows requesting behaviour from the io (pgm) goroutine.
+type ioCommand uint8
+
+// This is a way of creating enums in Go.
+// It will evaluate to:
+// ioOutput = 0
+// ioInput = 1
+// ioCheckIdle = 2
+const (
+ ioOutput ioCommand = iota
+ ioInput
+ ioCheckIdle
+)
+
+// cell is used as the return type for the testing framework.
+type cell struct {
+ x, y int
+}
+
+// distributorToIo defines all chans that the distributor goroutine will have to communicate with the io goroutine.
+// Note the restrictions on chans being send-only or receive-only to prevent bugs.
+type distributorToIo struct {
+ command chan<- ioCommand
+ idle <-chan bool
+
+ filename chan<- string
+ inputVal <-chan uint8
+ world chan<- byte
+}
+
+// ioToDistributor defines all chans that the io goroutine will have to communicate with the distributor goroutine.
+// Note the restrictions on chans being send-only or receive-only to prevent bugs.
+type ioToDistributor struct {
+ command <-chan ioCommand
+ idle chan<- bool
+
+ filename <-chan string
+ inputVal chan<- uint8
+ world <-chan byte
+}
+
+// distributorChans stores all the chans that the distributor goroutine will use.
+type distributorChans struct {
+ io distributorToIo
+}
+
+// ioChans stores all the chans that the io goroutine will use.
+type ioChans struct {
+ distributor ioToDistributor
+}
+
+// gameOfLife is the function called by the testing framework.
+// It makes some channels and starts relevant goroutines.
+// It places the created channels in the relevant structs.
+// It returns an array of alive cells returned by the distributor.
+func gameOfLife(p golParams, keyChan <-chan rune, clientNumber int, clients []clientData) []cell {
+ var dChans distributorChans
+ var ioChans ioChans
+
+ ioCommand := make(chan ioCommand)
+ dChans.io.command = ioCommand
+ ioChans.distributor.command = ioCommand
+
+ ioIdle := make(chan bool)
+ dChans.io.idle = ioIdle
+ ioChans.distributor.idle = ioIdle
+
+ ioFilename := make(chan string)
+ dChans.io.filename = ioFilename
+ ioChans.distributor.filename = ioFilename
+
+ inputVal := make(chan uint8)
+ dChans.io.inputVal = inputVal
+ ioChans.distributor.inputVal = inputVal
+
+ worldChan := make(chan byte)
+ dChans.io.world = worldChan
+ ioChans.distributor.world = worldChan
+
+ aliveCells := make(chan []cell)
+
+ if p.threads < clientNumber {
+ p.threads = clientNumber
+ }
+
+ go distributor(p, dChans, aliveCells, keyChan, clients, clientNumber)
+ go pgmIo(p, ioChans)
+
+ alive := <-aliveCells
+ return alive
+}
+
+func processClients(clientNumber int) []clientData {
+
+ clients := make([]clientData, clientNumber)
+
+ ln, err := net.Listen("tcp4", ":4000")
+ if err != nil {
+ fmt.Println("Could not listen to port 4000", err)
+ }
+
+ if ln != nil {
+ for i := 0; i < clientNumber; i++ {
+ conn, err := ln.Accept()
+ if err != nil {
+ fmt.Println(err)
+ }
+ fmt.Println("Client number", i, "/", clientNumber, "connected")
+
+ clients[i].encoder = gob.NewEncoder(conn)
+ clients[i].decoder = gob.NewDecoder(conn)
+ clients[i].ip, _, _ = net.SplitHostPort(conn.RemoteAddr().String())
+ }
+ }
+
+ return clients
+}
+
+// main is the function called when starting Game of Life with 'make gol'
+// Do not edit until Stage 2.
+func main() {
+ var params golParams
+
+ flag.IntVar(
+ ¶ms.threads,
+ "t",
+ 4,
+ "Specify the number of worker threads to use. Defaults to 8.")
+
+ flag.IntVar(
+ ¶ms.imageWidth,
+ "w",
+ 512,
+ "Specify the width of the image. Defaults to 512.")
+
+ flag.IntVar(
+ ¶ms.imageHeight,
+ "h",
+ 512,
+ "Specify the height of the image. Defaults to 512.")
+
+ flag.Parse()
+
+ params.turns = 5000
+
+ fmt.Println("Waiting for", clientNumber, "clients to connect.")
+ clients := processClients(clientNumber)
+
+ startControlServer(params)
+ keyChan := make(chan rune)
+ go getKeyboardCommand(keyChan)
+
+ gameOfLife(params, keyChan, clientNumber, clients)
+
+ StopControlServer()
+}
diff --git a/submission/stage5/main_test.go b/submission/stage5/main_test.go
new file mode 100644
index 0000000..e385db8
--- /dev/null
+++ b/submission/stage5/main_test.go
@@ -0,0 +1,478 @@
+package main
+
+import (
+ "fmt"
+ "github.com/stretchr/testify/assert"
+ "os"
+ "testing"
+ "time"
+)
+
+var clients []clientData
+
+func Test(t *testing.T) {
+ type args struct {
+ p golParams
+ expectedAlive []cell
+ }
+ tests := []struct {
+ name string
+ args args
+ }{
+ {"16x16x2-0", args{
+ p: golParams{
+ turns: 0,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 4, y: 5},
+ {x: 5, y: 6},
+ {x: 3, y: 7},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ },
+ }},
+
+ {"16x16x4-0", args{
+ p: golParams{
+ turns: 0,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 4, y: 5},
+ {x: 5, y: 6},
+ {x: 3, y: 7},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ },
+ }},
+
+ {"16x16x8-0", args{
+ p: golParams{
+ turns: 0,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 4, y: 5},
+ {x: 5, y: 6},
+ {x: 3, y: 7},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ },
+ }},
+
+ {"16x16x2-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x4-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x8-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x2-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x4-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x8-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ // New 6, 10, 12 thread tests
+ {"16x16x6-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 6,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x10-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 10,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x12-1", args{
+ p: golParams{
+ turns: 1,
+ threads: 12,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 3, y: 6},
+ {x: 5, y: 6},
+ {x: 4, y: 7},
+ {x: 5, y: 7},
+ {x: 4, y: 8},
+ },
+ }},
+
+ {"16x16x6-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 6,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x10-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 10,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ {"16x16x12-100", args{
+ p: golParams{
+ turns: 100,
+ threads: 12,
+ imageWidth: 16,
+ imageHeight: 16,
+ },
+ expectedAlive: []cell{
+ {x: 12, y: 0},
+ {x: 13, y: 0},
+ {x: 14, y: 0},
+ {x: 13, y: 14},
+ {x: 14, y: 15},
+ },
+ }},
+
+ // Special test to be used to generate traces - not a real test
+ //{"trace", args{
+ // p: golParams{
+ // turns: 10,
+ // threads: 4,
+ // imageWidth: 64,
+ // imageHeight: 64,
+ // },
+ //}},
+ }
+
+ start := time.Now()
+ // Networking
+ fmt.Println("Waiting for", clientNumber, "clients to connect.")
+ clients = processClients(clientNumber)
+ fmt.Println("Waited for", time.Since(start))
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ alive := gameOfLife(test.args.p, nil, clientNumber, clients)
+ //fmt.Println("Ran test:", test.name)
+ if test.name != "trace" {
+ assert.ElementsMatch(t, alive, test.args.expectedAlive)
+ }
+ })
+ }
+}
+
+const benchLength = 1000
+
+func Benchmark(b *testing.B) {
+ benchmarks := []struct {
+ name string
+ p golParams
+ }{
+ /*{
+ "16x16x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 16,
+ imageHeight: 16,
+ }},
+
+ {
+ "16x16x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 16,
+ imageHeight: 16,
+ }},
+
+ {
+ "16x16x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 16,
+ imageHeight: 16,
+ }},
+
+ {
+ "64x64x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 64,
+ imageHeight: 64,
+ }},
+
+ {
+ "64x64x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 64,
+ imageHeight: 64,
+ }},
+
+ {
+ "64x64x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 64,
+ imageHeight: 64,
+ }},
+
+ {
+ "128x128x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 128,
+ imageHeight: 128,
+ }},
+
+ {
+ "128x128x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 128,
+ imageHeight: 128,
+ }},
+
+ {
+ "128x128x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 128,
+ imageHeight: 128,
+ }},
+
+ {
+ "256x256x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 256,
+ imageHeight: 256,
+ }},
+
+ {
+ "256x256x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 256,
+ imageHeight: 256,
+ }},
+
+ {
+ "256x256x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 256,
+ imageHeight: 256,
+ }},
+
+ {
+ "512x512x2", golParams{
+ turns: benchLength,
+ threads: 2,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x4", golParams{
+ turns: benchLength,
+ threads: 4,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x8", golParams{
+ turns: benchLength,
+ threads: 8,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ */
+
+ {
+ "512x512x12", golParams{
+ turns: benchLength,
+ threads: 12,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x16", golParams{
+ turns: benchLength,
+ threads: 16,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x32", golParams{
+ turns: benchLength,
+ threads: 32,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x64", golParams{
+ turns: benchLength,
+ threads: 64,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x96", golParams{
+ turns: benchLength,
+ threads: 96,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+
+ {
+ "512x512x128", golParams{
+ turns: benchLength,
+ threads: 128,
+ imageWidth: 512,
+ imageHeight: 512,
+ }},
+ }
+
+ for _, bm := range benchmarks {
+ os.Stdout = nil // Disable all program output apart from benchmark results
+ b.Run(bm.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ gameOfLife(bm.p, nil, clientNumber, clients)
+ //fmt.Println("Ran bench:", bm.name)
+ }
+ })
+ }
+}
diff --git a/submission/stage5/pgm.go b/submission/stage5/pgm.go
new file mode 100644
index 0000000..e9d78cf
--- /dev/null
+++ b/submission/stage5/pgm.go
@@ -0,0 +1,111 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+)
+
+func check(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
+
+// writePgmImage receives an array of bytes and writes it to a pgm file.
+// Note that this function is incomplete. Use the commented-out for loop to receive data from the distributor.
+func writePgmImage(p golParams, i ioChans) {
+ _ = os.Mkdir("out", os.ModePerm)
+
+ filename := <-i.distributor.filename
+ file, ioError := os.Create("out/" + filename + ".pgm")
+ check(ioError)
+ defer file.Close()
+
+ _, _ = file.WriteString("P5\n")
+ //_, _ = file.WriteString("# PGM file writer by pnmmodules (https://github.com/owainkenwayucl/pnmmodules).\n")
+ _, _ = file.WriteString(strconv.Itoa(p.imageWidth))
+ _, _ = file.WriteString(" ")
+ _, _ = file.WriteString(strconv.Itoa(p.imageHeight))
+ _, _ = file.WriteString("\n")
+ _, _ = file.WriteString(strconv.Itoa(255))
+ _, _ = file.WriteString("\n")
+
+ world := make([][]byte, p.imageHeight)
+ for i := range world {
+ world[i] = make([]byte, p.imageWidth)
+ }
+
+ // Receives the world from the distributor
+ for x := 0; x < p.imageHeight; x++ {
+ for y := 0; y < p.imageWidth; y++ {
+ world[x][y] = <-i.distributor.world
+ }
+ }
+
+ for y := 0; y < p.imageHeight; y++ {
+ for x := 0; x < p.imageWidth; x++ {
+ _, ioError = file.Write([]byte{world[y][x]})
+ check(ioError)
+ }
+ }
+
+ ioError = file.Sync()
+ check(ioError)
+
+ fmt.Println("File", filename, "output done!")
+}
+
+// readPgmImage opens a pgm file and sends its data as an array of bytes.
+func readPgmImage(p golParams, i ioChans) {
+ filename := <-i.distributor.filename
+ data, ioError := ioutil.ReadFile("images/" + filename + ".pgm")
+ check(ioError)
+
+ fields := strings.Fields(string(data))
+
+ if fields[0] != "P5" {
+ panic("Not a pgm file")
+ }
+
+ width, _ := strconv.Atoi(fields[1])
+ if width != p.imageWidth {
+ panic("Incorrect width")
+ }
+
+ height, _ := strconv.Atoi(fields[2])
+ if height != p.imageHeight {
+ panic("Incorrect height")
+ }
+
+ maxval, _ := strconv.Atoi(fields[3])
+ if maxval != 255 {
+ panic("Incorrect maxval/bit depth")
+ }
+
+ image := []byte(fields[4])
+
+ for _, b := range image {
+ i.distributor.inputVal <- b
+ }
+
+ fmt.Println("File", filename, "input done!")
+}
+
+func pgmIo(p golParams, i ioChans) {
+ for {
+ select {
+ case command := <-i.distributor.command:
+ switch command {
+ case ioInput:
+ readPgmImage(p, i)
+ case ioOutput:
+ writePgmImage(p, i)
+ case ioCheckIdle:
+ i.distributor.idle <- true
+ }
+ }
+ }
+}
diff --git a/submission/stage5/worker/worker b/submission/stage5/worker/worker
new file mode 100755
index 0000000..3cce026
Binary files /dev/null and b/submission/stage5/worker/worker differ
diff --git a/submission/stage5/worker/worker.go b/submission/stage5/worker/worker.go
new file mode 100644
index 0000000..e2f56dd
--- /dev/null
+++ b/submission/stage5/worker/worker.go
@@ -0,0 +1,602 @@
+package main
+
+import (
+ "encoding/gob"
+ "flag"
+ "fmt"
+ "io"
+ "net"
+)
+
+const (
+ INIT = 0
+)
+
+const (
+ pause = iota
+ ping = iota
+ resume = iota
+ quit = iota
+ save = iota
+)
+
+type initPackage struct {
+ Clients int
+ Workers int
+ IpBefore, IpAfter string
+ Turns int
+ Width int
+}
+
+type workerPackage struct {
+ StartX int
+ EndX int
+ World [][]byte
+ Index int
+}
+
+type workerChannel struct {
+ inputHalo [2]chan byte
+ outputHalo [2]chan byte
+ distributorInput chan int
+ localDistributor chan byte
+}
+
+type distributorPackage struct {
+ Index int
+ Type int
+ Data int
+ OutputWorld [][]byte
+}
+
+type controllerData struct {
+ Index, Data int
+}
+
+// Returns the new state of a cell from the number of alive neighbours and current state
+func getNewState(numberOfAlive int, cellState bool) int {
+ if cellState == true {
+ if numberOfAlive < 2 {
+ return -1
+ }
+ if numberOfAlive <= 3 {
+ return 0
+ }
+ if numberOfAlive > 3 {
+ return -1
+ }
+ } else {
+ if numberOfAlive == 3 {
+ return 1
+ }
+ }
+ return 0
+}
+
+func worker(p initPackage, channels workerChannel, wp workerPackage, encoder *gob.Encoder) {
+ endX := wp.EndX
+ startX := wp.StartX
+ endY := p.Width
+ startY := 0
+
+ world := make([][]byte, endX-startX+2)
+ for i := range world {
+ world[i] = make([]byte, endY-startY)
+ }
+
+ newWorld := make([][]byte, endX-startX+2)
+ for i := range world {
+ newWorld[i] = make([]byte, endY-startY)
+ }
+
+ for i := range world {
+ for j := 0; j < endY; j++ {
+ newWorld[i][j] = wp.World[i][j]
+ }
+ }
+
+ for i := range world {
+ for j := range world[i] {
+ world[i][j] = newWorld[i][j]
+ }
+ }
+
+ halo0 := true
+ halo1 := true
+ stopAtTurn := -2
+
+ for turn := 0; turn < p.Turns; {
+
+ if turn == stopAtTurn+1 {
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 0,
+ Data: pause,
+ OutputWorld: nil,
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ for {
+ r := <-channels.distributorInput
+ if r == resume {
+ break
+ } else if r == save {
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 1,
+ Data: 0,
+ OutputWorld: newWorld[1 : endX-startX+1],
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ } else if r == quit {
+ channels.localDistributor <- 1
+ return
+ } else if r == ping {
+ alive := 0
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ if newWorld[i][j] == 0xFF {
+ alive++
+ }
+ }
+ }
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 0,
+ Data: alive,
+ OutputWorld: nil,
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ break
+ } else {
+ fmt.Println("Something went wrong, r = ", r)
+ }
+ }
+ }
+
+ // Process something
+ if turn != 0 {
+ if !halo0 {
+ select {
+ case c := <-channels.inputHalo[0]:
+ world[0][0] = c
+ for j := 1; j < endY; j++ {
+ world[0][j] = <-channels.inputHalo[0]
+ }
+ halo0 = true
+ case <-channels.distributorInput:
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 0,
+ Data: turn,
+ OutputWorld: nil,
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ stopAtTurn = <-channels.distributorInput
+ }
+ }
+ if !halo1 {
+ select {
+ case c := <-channels.inputHalo[1]:
+ world[endX-startX+1][0] = c
+ for j := 1; j < endY; j++ {
+ world[endX-startX+1][j] = <-channels.inputHalo[1]
+ }
+ halo1 = true
+ case <-channels.distributorInput:
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 0,
+ Data: turn,
+ OutputWorld: nil,
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ stopAtTurn = <-channels.distributorInput
+ }
+ }
+ }
+
+ // Move on to next turn
+ if halo0 && halo1 {
+
+ for i := 1; i < endX-startX+1; i++ {
+ for j := startY; j < endY; j++ {
+ // Compute alive neighbours
+ aliveNeighbours := 0
+
+ yp1 := (j + 1) % endY
+ ym1 := j - 1
+ if ym1 < 0 {
+ ym1 += endY
+ }
+
+ aliveNeighbours = int(world[i+1][j]) + int(world[i-1][j]) +
+ int(world[i][yp1]) + int(world[i][ym1]) +
+ int(world[i+1][yp1]) + int(world[i+1][ym1]) +
+ int(world[i-1][yp1]) + int(world[i-1][ym1])
+
+ switch getNewState(aliveNeighbours/255, world[i][j] == 0xFF) {
+ case -1:
+ newWorld[i][j] = 0x00
+ case 1:
+ newWorld[i][j] = 0xFF
+ case 0:
+ newWorld[i][j] = world[i][j]
+ }
+ }
+ }
+ halo0 = false
+ halo1 = false
+ turn++
+
+ out0 := false
+ out1 := false
+ for !(out0 && out1) {
+ if !out0 {
+ select {
+ case channels.outputHalo[0] <- newWorld[1][0]:
+ for j := 1; j < endY; j++ {
+ channels.outputHalo[0] <- newWorld[1][j]
+ }
+ out0 = true
+ case <-channels.distributorInput:
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 0,
+ Data: turn,
+ OutputWorld: nil,
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ stopAtTurn = <-channels.distributorInput
+ }
+ }
+ if !out1 {
+ select {
+ case channels.outputHalo[1] <- newWorld[endX-startX][0]:
+ for j := 1; j < endY; j++ {
+ channels.outputHalo[1] <- newWorld[endX-startX][j]
+ }
+ out1 = true
+ case <-channels.distributorInput:
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 0,
+ Data: turn,
+ OutputWorld: nil,
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ stopAtTurn = <-channels.distributorInput
+ }
+ }
+ }
+
+ for i := range world {
+ for j := range world[i] {
+ world[i][j] = newWorld[i][j]
+ }
+ }
+ }
+
+ }
+
+ err := encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: 1,
+ Data: 0,
+ OutputWorld: newWorld[1 : endX-startX+1],
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+
+ // Done
+ err = encoder.Encode(distributorPackage{
+ Index: wp.Index,
+ Type: -1,
+ Data: -1,
+ OutputWorld: nil,
+ })
+ if err != nil {
+ fmt.Println("err", err)
+ }
+
+ channels.localDistributor <- 0
+}
+
+func initialiseChannels(workerChannels []workerChannel, workers, clients, imageWidth, endX, startX, i int) {
+ workerChannels[i].inputHalo[0] = make(chan byte, imageWidth)
+ workerChannels[i].inputHalo[1] = make(chan byte, imageWidth)
+ workerChannels[i].localDistributor = make(chan byte)
+ workerChannels[i].distributorInput = make(chan int, 1)
+
+ if workers == 1 && clients > 1 {
+ // Just one client
+ workerChannels[0].outputHalo[0] = make(chan byte, imageWidth)
+ workerChannels[0].outputHalo[1] = make(chan byte, imageWidth)
+ } else if workers == 1 {
+ workerChannels[0].outputHalo[0] = workerChannels[0].inputHalo[1]
+ workerChannels[0].outputHalo[1] = workerChannels[0].inputHalo[0]
+ } else if i == 0 {
+ workerChannels[0].outputHalo[0] = make(chan byte, imageWidth)
+ workerChannels[i+1].outputHalo[0] = workerChannels[i].inputHalo[1]
+ } else {
+ if i == workers-1 {
+ workerChannels[workers-1].outputHalo[1] = make(chan byte, imageWidth)
+ workerChannels[i-1].outputHalo[1] = workerChannels[i].inputHalo[0]
+ } else {
+ workerChannels[i-1].outputHalo[1] = workerChannels[i].inputHalo[0]
+ workerChannels[i+1].outputHalo[0] = workerChannels[i].inputHalo[1]
+ }
+
+ }
+
+}
+
+func receiveFromDistributor(decoder *gob.Decoder, channels []workerChannel, exit chan byte) {
+ for {
+ var p controllerData
+
+ err := decoder.Decode(&p)
+ if err != nil {
+ fmt.Println("err", err)
+ break
+ }
+
+ // Exit
+ if p.Index == -1 {
+ break
+ }
+ channels[p.Index].distributorInput <- p.Data
+ }
+ exit <- 1
+}
+
+func syncWithOtherClients(encoder *gob.Encoder, decoder *gob.Decoder) {
+ // This client is ready to receive
+ err := encoder.Encode(1)
+ if err != nil {
+ fmt.Println("err", err)
+ }
+
+ var p int
+ err = decoder.Decode(&p)
+ // All clients are ready to receive
+ if err != nil {
+ fmt.Println("err", err)
+ }
+ if p != 1 {
+ fmt.Println("Error from distributor, p =", p)
+ }
+
+}
+
+type haloPacket struct {
+ Index int
+ Data []byte
+}
+
+func distributor(encoder *gob.Encoder, decoder *gob.Decoder, exitThread []chan byte) int {
+ var haloClients = make([]net.Conn, 2)
+ var done = make(chan net.Listener)
+ var listening = make(chan byte)
+
+ var initP initPackage
+ err := decoder.Decode(&initP)
+ if err != nil {
+ fmt.Println("err", err)
+ }
+
+ if initP.Clients != 1 {
+ go waitForClients(haloClients, done, listening)
+ }
+
+ workerChannel := make([]workerChannel, initP.Workers)
+ workerPackages := make([]workerPackage, initP.Workers)
+ for i := 0; i < initP.Workers; i++ {
+ var w workerPackage
+ err = decoder.Decode(&w)
+ //fmt.Println(w)
+ if err != nil {
+ fmt.Println("err", err)
+ break
+ }
+ //
+ //fmt.Println("Received worker package,", w.StartX, w.EndX)
+
+ workerPackages[i] = w
+ initialiseChannels(workerChannel, initP.Workers, initP.Clients, initP.Width, w.EndX, w.StartX, i)
+ }
+
+ // Wait until the program binds to port, then sync to this point with all clients. At this point all of them are listening and ready for connections
+ if initP.Clients > 1 {
+ <-listening
+ }
+ syncWithOtherClients(encoder, decoder)
+
+ var ln net.Listener
+ // Connect to external halo sockets
+ if initP.Clients > 1 {
+ go receiveFromClient(initP.IpBefore, [2]chan byte{workerChannel[0].inputHalo[0], workerChannel[initP.Workers-1].inputHalo[1]}, initP.Width, initP.Turns, exitThread[1])
+ go receiveFromClient(initP.IpAfter, [2]chan byte{workerChannel[0].inputHalo[0], workerChannel[initP.Workers-1].inputHalo[1]}, initP.Width, initP.Turns, exitThread[2])
+
+ ln = <-done
+ }
+
+ go receiveFromDistributor(decoder, workerChannel, exitThread[0])
+
+ if initP.Clients != 1 {
+ ip0, _, _ := net.SplitHostPort(haloClients[0].RemoteAddr().String())
+ ip1, _, _ := net.SplitHostPort(haloClients[1].RemoteAddr().String())
+ if ip0 == initP.IpBefore && ip1 == initP.IpAfter {
+ go serveToClient(haloClients[0], 1, workerChannel[0].outputHalo[0], initP.Width, initP.Turns, exitThread[3])
+ go serveToClient(haloClients[1], 0, workerChannel[initP.Workers-1].outputHalo[1], initP.Width, initP.Turns, exitThread[4])
+ } else if ip0 == initP.IpAfter && ip1 == initP.IpBefore {
+ go serveToClient(haloClients[1], 1, workerChannel[0].outputHalo[0], initP.Width, initP.Turns, exitThread[3])
+ go serveToClient(haloClients[0], 0, workerChannel[initP.Workers-1].outputHalo[1], initP.Width, initP.Turns, exitThread[4])
+ } else {
+ fmt.Println("IPs are mismatched")
+ }
+ } else {
+ // Only local workers
+ workerChannel[initP.Workers-1].outputHalo[1] = workerChannel[0].inputHalo[0]
+ workerChannel[0].outputHalo[0] = workerChannel[initP.Workers-1].inputHalo[1]
+ }
+
+ for i := 0; i < initP.Workers; i++ {
+ go worker(initP, workerChannel[i], workerPackages[i], encoder)
+ }
+
+ fmt.Println("All workers active")
+ var r byte
+ for i := 0; i < initP.Workers; i++ {
+ r = <-workerChannel[i].localDistributor
+ }
+
+ if initP.Clients != 1 {
+ err = ln.Close()
+ if err != nil {
+ fmt.Println(err)
+ }
+ }
+ fmt.Println("Done")
+ if r == 1 {
+ return -1 // Quit command
+ }
+ if initP.Clients == 1 {
+ return 1 // Just local client, no halo sockets
+ }
+ return 0 // Normal termination
+}
+
+func serveToClient(conn net.Conn, index int, c chan byte, width int, turns int, exit chan byte) {
+ enc := gob.NewEncoder(conn)
+ for i := 0; i < turns; i++ {
+ var haloData = make([]byte, width)
+
+ for i := 0; i < width; i++ {
+ haloData[i] = <-c
+ }
+
+ err := enc.Encode(haloPacket{index, haloData})
+
+ if err != nil {
+ fmt.Println("err", err)
+ break
+ }
+ }
+
+ exit <- 1
+}
+
+func waitForClients(clients []net.Conn, done chan net.Listener, listening chan byte) {
+ ln, err := net.Listen("tcp4", ":4001")
+ if err != nil {
+ fmt.Println("err", err)
+ return
+ }
+ listening <- 1
+
+ if ln != nil {
+ for i := 0; i < 2; i++ {
+ conn, _ := ln.Accept()
+ clients[i] = conn
+ }
+ done <- ln
+ }
+}
+
+func receiveFromClient(ip string, c [2]chan byte, width int, turns int, exit chan byte) {
+ conn, err := net.Dial("tcp4", ip+":4001")
+ if err != nil {
+ fmt.Println("err", err)
+ exit <- 1
+ return
+ }
+
+ dec := gob.NewDecoder(conn)
+ for i := 0; i < turns; i++ {
+ var haloP haloPacket
+ err := dec.Decode(&haloP)
+ //fmt.Println("Received from socket,", haloData)
+
+ if err != nil {
+ fmt.Println("err", err)
+ return
+ }
+
+ // Take bytes from haloData row slice and put them in the channel
+ for _, b := range haloP.Data {
+ c[haloP.Index] <- b
+ }
+ }
+
+ exit <- 1
+}
+
+func main() {
+ defaultHostname := "127.0.0.1"
+ var hostname string
+
+ flag.StringVar(&hostname, "hostname", defaultHostname, "The hostname of the server.")
+ flag.Parse()
+
+ fmt.Println("Connecting to", hostname)
+
+ conn, err := net.Dial("tcp4", hostname+":4000")
+ if err != nil {
+ fmt.Println("Server is offline")
+ return
+ }
+ fmt.Println("Connected to server")
+
+ exitThread := make([]chan byte, 5)
+ for i := 0; i < 5; i++ {
+ exitThread[i] = make(chan byte, 1)
+ }
+
+ dec := gob.NewDecoder(conn)
+ enc := gob.NewEncoder(conn)
+
+ for {
+ var packetType int = 0
+ err := dec.Decode(&packetType)
+ if err != nil {
+ if err == io.EOF {
+ fmt.Println("Connection closed, exiting worker.")
+ return
+ }
+ fmt.Println("err", err)
+ break
+ }
+
+ if packetType == INIT {
+ fmt.Println("Starting workers..")
+ result := distributor(enc, dec, exitThread)
+ waitForX := 5
+
+ if result == -1 || result == 1 {
+ waitForX = 1
+ }
+ for i := 0; i < waitForX; i++ {
+ <-exitThread[i]
+ }
+
+ if result == -1 { // If quit command, quit worker program
+ return
+ }
+ }
+ }
+}
diff --git a/submission/submission.zip b/submission/submission.zip
new file mode 100644
index 0000000..bddf3c2
Binary files /dev/null and b/submission/submission.zip differ