From f7b40a6d15f91fec31385aedced0a86e9222a7e5 Mon Sep 17 00:00:00 2001
From: Olivier Blanvillain <olivier.blanvillain@gmail.com>
Date: Mon, 1 Mar 2021 11:28:01 +0100
Subject: [PATCH] Add labs/lab2-reductions-and-prefix-sums

---
 .../lab2-reductions-and-prefix-sums/README.md | 355 ++++++++++++++++++
 .../angle-example-2.svg                       | 209 +++++++++++
 .../angle-example.svg                         | 154 ++++++++
 .../lab2-reductions-and-prefix-sums/angle.png | Bin 0 -> 4134 bytes
 .../angle2.png                                | Bin 0 -> 6122 bytes
 .../terrain.png                               | Bin 0 -> 2863 bytes
 .../terrain.svg                               | 112 ++++++
 .../visibility.png                            | Bin 0 -> 3534 bytes
 .../visibility.svg                            | 131 +++++++
 9 files changed, 961 insertions(+)
 create mode 100755 labs/lab2-reductions-and-prefix-sums/README.md
 create mode 100755 labs/lab2-reductions-and-prefix-sums/angle-example-2.svg
 create mode 100755 labs/lab2-reductions-and-prefix-sums/angle-example.svg
 create mode 100755 labs/lab2-reductions-and-prefix-sums/angle.png
 create mode 100755 labs/lab2-reductions-and-prefix-sums/angle2.png
 create mode 100755 labs/lab2-reductions-and-prefix-sums/terrain.png
 create mode 100755 labs/lab2-reductions-and-prefix-sums/terrain.svg
 create mode 100755 labs/lab2-reductions-and-prefix-sums/visibility.png
 create mode 100755 labs/lab2-reductions-and-prefix-sums/visibility.svg

diff --git a/labs/lab2-reductions-and-prefix-sums/README.md b/labs/lab2-reductions-and-prefix-sums/README.md
new file mode 100755
index 0000000..42240bb
--- /dev/null
+++ b/labs/lab2-reductions-and-prefix-sums/README.md
@@ -0,0 +1,355 @@
+# Reductions and Prefix Sums
+
+Use the following commands to make a fresh clone of your repository:
+
+```
+git clone -b reductions git@gitlab.epfl.ch:lamp/student-repositories-s21/cs206-GASPAR.git cs206-reductions
+```
+
+## Useful links
+
+  * [A guide to the Scala parallel collections](https://docs.scala-lang.org/overviews/parallel-collections/overview.html)
+  * [The API documentation of the Scala parallel collections](https://www.javadoc.io/doc/org.scala-lang.modules/scala-parallel-collections_2.13/latest/scala/collection/index.html)
+  * [The API documentation of the Scala standard library](https://www.scala-lang.org/files/archive/api/2.13.4)
+  * [The API documentation of the Java standard library](https://docs.oracle.com/en/java/javase/15/docs/api/index.html)
+
+**If you have issues with the IDE, try [reimporting the
+build](https://gitlab.epfl.ch/lamp/cs206/-/blob/master/labs/example-lab.md#ide-features-like-type-on-hover-or-go-to-definition-do-not-work),
+if you still have problems, use `compile` in sbt instead.**
+
+## Introduction
+
+In this assignment, you will implement several variants of reduction and prefix
+sum algorithms. Each of the three parts of the assignment will exercise a
+different aspect of parallel programming:
+
+- choosing the right parallelization threshold
+- identifying the correct reduction operator
+- identifying the correct prefix sum operator
+
+We will use the `parallel` construct, defined in the file `package.scala`, as in the lecture to start parallel
+computations. Every `parallel` construct invocation takes two tasks as input and
+outputs the corresponding results as a tuple of two elements. You are not allowed
+to use the `task` construct in this assignment.
+
+## Parallel Counting Change
+
+If you took the course Functional Programming in Scala, you surely recall the
+assignment in which you had to count the number of ways in which you can make
+the change for a given amount of money.
+The text of that assignment was as follows:
+
+> Write a recursive function that counts how many different ways you can
+> make change for an amount, given a list of coin denominations. For
+> example, there are 3 ways to give change for 4 if you have coins with
+> denomination 1 and 2: 1+1+1+1, 1+1+2, 2+2.
+
+In this assignment, you will repeat the same task, but this time, your
+implementation will be parallel.
+Start with the sequential version of this problem once more -- the `countChange`
+function takes the amount of money and the list of different coin denominations.
+It returns the total number of different ways you can give change:
+
+```scala
+def countChange(money: Int, coins: List[Int]): Int
+```
+
+Note that the solution to this problem is recursive.
+In every recursive call, we either decide to continue subtracting the next coin
+in the `coins` list from the `money` amount, or we decide to drop the coin from the list of coins.
+For example, if we have 4 CHF, and coin denominations of 1 and 2, the call
+graph, in which every node depicts one invocation of the `countChange` method,
+is as follows:
+
+                                     4,[1, 2]
+                       3,[1, 2]          +            4,[2]
+               2,[1, 2]    +     3,[2]          2,[2]   +   4,[]
+          1,[1, 2] + 2,[2]   1,[2] + 3,[]    0,[2] + 2,[]    0
+    0,[1, 2] + 1,[2]   1       0      0       1       0
+        1        0
+
+We can take advantage of this recursive structure by evaluating different
+subtrees in parallel.
+This is the next part of the assignment -- implement the method `parCountChange`
+that counts the amount of change in parallel:
+
+```scala
+def parCountChange(money: Int, coins: List[Int], threshold: Threshold): Int
+```
+
+As we learned in the lectures, the `parCountChange` should not spawn parallel
+computations after reaching the leaf in the call graph -- the synchronization
+costs of doing this are way too high.
+Instead, we need to *agglomerate* parts of the computation.
+We do this by calling the sequential `countChange` method when we decide that
+the amount of work is lower than a certain value, called the *threshold*.
+To separate the concern of deciding on the threshold value from the
+implementation of our parallel algorithm, we implement the threshold
+functionality in a separate function, described by the `Threshold` type alias:
+
+```scala
+type Threshold = (Int, List[Int]) => Boolean
+```
+
+When a `threshold` function returns `true` for a given amount of money and the
+given list of coins, the sequential `countChange` implementation must be called.
+
+Implement `parCountChange`!
+
+Now that we have the `parCountChange` method, we ask ourselves what is the right
+implementation of the `threshold` function?
+Recall the examples from the lectures, such as summing the array values and
+computing the norm, where this was easy -- we exactly knew the amount of work
+required to traverse a subrange of the array, so `threshold` could return `true`
+when the length of the subrange was smaller than a certain value.
+
+Sadly, the total amount of work for a given `parCountChange` invocation is hard
+to evaluate from the remaining amount of money and a list of coins.
+In fact, the amount of work directly corresponds to the count that
+`parCountChange` returns, which is the value that we are trying to compute.
+Counting change is a canonical example of a task-parallel problem in which the
+partitioning the workload across processors is *solution-driven* -- to know how
+to optimally partition the work, we would first need to solve the problem
+itself.
+
+For this reason, many parallel algorithms in practice rely on heuristics to
+assess the amount of work in a subtask.
+We will implement several such heuristics in this exercise, and assess the
+effect on performance.
+First, implement the `moneyThreshold` method, which creates a threshold function
+that returns `true` when the amount of money is less than or equal to `2 / 3` of
+the starting amount:
+
+```scala
+def moneyThreshold(startingMoney: Int): Threshold
+```
+
+Remember that `a / b` will return the integer division of `a` and `b` when both
+operands are `Int`s. To avoid this problem, be sure to always do the multiplication of
+`startingMoney` by `2` before doing the division by `3`.
+
+Now run the `ParallelCountChange` application in sbt and observe the speedup:
+
+```
+> runMain reductions.ParallelCountChangeRunner
+```
+
+The previous heuristic did not take into account how many coins were left on the
+coins list, so try two other heuristics.
+Implement the method `totalCoinsThreshold`, which returns a threshold function
+that returns `true` when the number of coins is less than or equal to the `2 / 3` of the
+initial number of coins:
+
+```scala
+def totalCoinsThreshold(totalCoins: Int): Threshold
+```
+
+Again, be careful about the order of operations.
+
+Then, implement the method `combinedThreshold`, which returns a threshold
+function that returns `true` when the amount of money multiplied with the number
+of remaining coins is less than or equal to the starting money multiplied with
+the initial number of coins divided by `2`:
+
+```scala
+def combinedThreshold(startingMoney: Int, allCoins: List[Int]): Threshold
+```
+
+Which of the three threshold heuristics gives the best speedup?
+Can you think of a heuristic that improves performance even more?
+
+
+## Parallel Parentheses Balancing
+
+In this part of the assignment, we recall the Parenthesis Balancing assignment
+that might be familiar to you from the Functional Programming in Scala course.
+Here, the task is to, given an array of characters, decide if the parentheses in
+the array are balanced.
+
+Let us recall a few examples of strings in which parentheses are correctly
+balanced:
+
+```
+(if (zero? x) max (/ 1 x))
+I told him (that it's not (yet) done). (But he wasn't listening)
+```
+
+Similarly, the parentheses in the following strings are not balanced:
+
+```
+(o_()
+:-)
+())(
+```
+
+Implement a sequential function `balance`, which returns `true` iff the
+parentheses in the array are balanced:
+
+```scala
+def balance(chars: Array[Char]): Boolean
+```
+
+Next, you will implement a parallel version of this method.
+By now, you're already an expert at implementing the structure of a reduction
+algorithm, so you should have no problem there.
+The tricky part in parallel parentheses balancing is choosing the reduction
+operator -- you probably implemented `balance` by keeping an integer
+accumulator, incrementing it for left parentheses and decrementing it for the
+right ones, taking care that this accumulator does not drop below zero.
+Parallel parentheses balancing will require a bit more ingenuity on your part,
+so we will give you a hint -- you will need two integer values for the
+accumulator.
+
+Implement the `parBalance` method, which checks if the parentheses in the input
+array are balanced using two helper methods `reduce` and `traverse`.
+These methods implement the parallel reduction and the sequential traversal
+part, respectively:
+
+```scala
+def parBalance(chars: Array[Char], threshold: Int): Boolean = {
+  def traverse(idx: Int, until: Int, _???_: Int, _???_: Int): ???
+
+  def reduce(from: Int, until: Int): ??? = ???
+
+  reduce(0, chars.length) == ???
+}
+```
+
+In this case, we again use the fixed threshold parameter, as we did in the
+lectures. Sections with size smaller or equal to the threshold should be processed sequentially.
+For maximum performance, use a `while` loop in the `traverse` method, or make
+`traverse` tail-recursive -- do not use a `Range`.
+
+Now, run the `ParallelParenthesesBalancing` application from sbt:
+
+```
+> runMain reductions.ParallelParenthesesBalancingRunner
+```
+
+How large was your speedup?
+
+If you are looking for additional challenges, prove that your reduction operator
+is associative!
+
+
+## Line of Sight
+
+In the last part of the exercise, you will be implementing an entirely new
+parallel algorithm -- you will apply the prefix sum algorithm to computing the
+line-of-sight in two-dimensional terrain.
+
+Imagine that you are standing at the zero of a coordinate system.
+The curve to your right describes the terrain that you are facing.
+This is shown in the following figure:
+
+![terrain](terrain.png)
+
+The task of the line-of-sight algorithm is to compute the visibility of each
+point of the terrain, as shown in the following figure, where the visible area
+is above of the full line, and the obscured terrain is shown with a dotted
+line.
+
+![visibility](visibility.png)
+
+What is the necessary and sufficient condition for a point on the terrain to be
+visibile from the zero of the coordinate system, where you are standing?
+Imagine that the terrain heights are represented with an array of numbers.
+We can compute (the tangent of) the viewing angle of each point on the terrain by dividing the
+height of the terrain `xs(i)` with the distance from the viewing point `i`,
+as shown in the following figure:
+
+![angle](angle.png)
+
+It turns out that if the viewing angle of some point B is **lower** than the
+viewing angle of an earlier point A, then the point B is not visible, as shown
+in the following figure:
+
+![angle](angle2.png)
+
+This simple realization allows us to easily compute the line-of-sight on the
+terrain -- if you were a sequential programmer, you would traverse the array of
+height values from the beginning to the end, and write the maximum angle seen so
+far into the output array.
+
+Implement the sequential `lineOfSight` method, which, for each height entry in
+the `input` array (except for input(0) which is the location of the observer and
+is always zero), writes the maximum angle until that point into the `output`
+array (output(0) should be 0):
+
+```scala
+def lineOfSight(input: Array[Float], output: Array[Float]): Unit
+```
+
+We keep things simple -- instead of outputting an array of booleans denoting the
+visibilities, we only output the angles.
+
+Note that what we call an angle in this assignment is actually the tangent
+of the angle. Indeed, `xs(i)` is the opposing side of the angle and `i` the adjacent side.
+The ratio `xs(i) / i` that you compute is in fact the tangent of the angle!
+Since the tangent of an angle is strictly increasing between
+0° and 90°, it is a perfectly good replacement for the actual angle in our use case.
+Keep this in mind and make sure that you do not apply any trigonometic functions on the tangent!
+
+When we see a sequential algorithm that produces a sequence of values by
+traversing the input from left to right, this is an indication that the
+algorithm might have a parallel prefix sum variant.
+So let's try to implement one!
+
+Recall what you learned in the lectures -- the first phase of the parallel
+prefix sum algorithm is the *upsweep* phase.
+Here, the algorithm constructs the reduction tree by traversing parts of the
+input array in parallel.
+Implement the method `upsweepSequential`, which returns the maximum angle in a
+given part of the array, and the method `upsweep`, which returns the reduction
+tree over parts of the input array. If the length of the given part of the input
+array is less than or equal to `threshold`, then `upsweep` calls `upsweepSequential`.
+Note that the part of the input array that needs to traversed is represented
+using indices 'from' (inclusive) and 'until' (or 'end') (exclusive).
+
+```scala
+def upsweepSequential(input: Array[Float], from: Int, until: Int): Float
+```
+
+```scala
+def upsweep(input: Array[Float], from: Int, end: Int, threshold: Int): Tree
+```
+
+The `Tree` data type is either a `Leaf` or an inner `Node`, and it contains the
+maximum angle in the corresponding part of the array.
+Note that when the number of elements in a part of the input array,
+which is `(end - from)`, is smaller or equal to the threshold, the sequential `upsweepSequential`
+has to be invoked, and you should return a `Leaf`.
+Otherwise, you should process the part of the input array in parallel, and return
+a `Node`. Make sure that the work is evenly distributed between the parallel computations.
+
+The second phase is called *downsweep* -- here, the algorithm uses the tree to
+push the maximum angle in the corresponding *prefix* of the array to the leaves
+of the tree, and outputs the values.
+Implement the methods `downsweep` which processes parts of the tree in parallel,
+and the method `downsweepSequential`, which traverses the parts of the array
+corresponding to leaves of the tree and writes the final angles into the
+`output` array:
+
+```scala
+def downsweep(input: Array[Float], output: Array[Float],
+  startingAngle: Float, tree: Tree): Unit
+
+def downsweepSequential(input: Array[Float], output: Array[Float],
+  startingAngle: Float, from: Int, until: Int): Unit
+```
+
+Finally, implement `parLineOfSight` using the `upsweep` and `downsweep` methods:
+
+```scala
+def parLineOfSight(input: Array[Float], output: Array[Float],
+  threshold: Int): Unit
+```
+
+Now, run the `LineOfSight` application in sbt and observe the relative speedups:
+
+```
+> runMain reductions.LineOfSightRunner
+```
+
+How large is the speedup compared to the number of cores in your processor?
+Can you explain your results?
diff --git a/labs/lab2-reductions-and-prefix-sums/angle-example-2.svg b/labs/lab2-reductions-and-prefix-sums/angle-example-2.svg
new file mode 100755
index 0000000..bd47893
--- /dev/null
+++ b/labs/lab2-reductions-and-prefix-sums/angle-example-2.svg
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 744.09448819 1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="angle-example-2.svg"
+   inkscape:export-filename="C:\cygwin\home\axel22\workspaces\scala\progfun\instructions\angle2.png"
+   inkscape:export-xdpi="32.263699"
+   inkscape:export-ydpi="32.263699">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4146"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) translate(12.5,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lend"
+       style="overflow:visible;"
+       inkscape:isstock="true">
+      <path
+         id="path4149"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) rotate(180) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.9899495"
+     inkscape:cx="488.53326"
+     inkscape:cy="701.51987"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1600"
+     inkscape:window-height="877"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4"
+     inkscape:window-maximized="1"
+     inkscape:snap-text-baseline="false" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Lstart)"
+       d="m 142.85714,101.50506 0,298.57143"
+       id="path3336"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3.8496573;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+       d="m 142.85714,399.21935 668.06991,0"
+       id="path3340"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:3,3;stroke-dashoffset:0"
+       d="m 143.44166,399.80366 c 88.89343,-20.20305 105.05587,-39.39595 105.05587,-39.39595"
+       id="path4640"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3,3;stroke-opacity:1;stroke-dashoffset:0"
+       d="m 248.24499,360.5773 c 0,0 34.72027,-23.20876 57.95379,-32.30013 39.28467,-15.37226 57.45614,8.89703 57.45614,8.89703 19.69797,32.32488 43.43656,27.27412 55.55839,8.08122 l 12.12183,-19.1929 0,0 c 40.4061,-86.87311 77.78174,-113.13708 77.78174,-113.13708 0,0 18.22571,-16.58543 45.38508,0.61276 28.26715,17.89967 41.48804,62.0167 41.48804,62.0167"
+       id="path4642"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cscscccsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3,3;stroke-opacity:1;stroke-dashoffset:0"
+       d="m 595.70683,274.55743 c 0,0 12.08157,73.3608 31.09283,77.76906 28.85051,6.68975 48.99239,-25.75889 48.99239,-25.75889 0,0 17.42513,-17.42513 27.27412,-14.89975 9.84899,2.52538 30.50526,33.55149 30.50526,33.55149"
+       id="path4752"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cscsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 142.85714,398.79078 500,221.64792"
+       id="path4832"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:6,3;stroke-dashoffset:0"
+       d="m 501.03566,222.01682 0,176.77669"
+       id="path5388"
+       inkscape:connector-curvature="0" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path5390"
+       d="m 142.43151,219.99652 358.60415,-1e-5"
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="310.11682"
+       y="200.8036"
+       id="text5392"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan5394"
+         x="310.11682"
+         y="200.8036">i</tspan></text>
+    <text
+       sodipodi:linespacing="125%"
+       id="text5396"
+       y="293.73761"
+       x="504.0661"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       xml:space="preserve"><tspan
+         y="293.73761"
+         x="504.0661"
+         id="tspan5398"
+         sodipodi:role="line">xs(i)</tspan></text>
+    <path
+       inkscape:connector-curvature="0"
+       id="path3357"
+       d="m 632.35549,355.35695 0,41.41625"
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6,3;stroke-dashoffset:0;stroke-opacity:1" />
+    <path
+       sodipodi:nodetypes="cc"
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3.48477578;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6.96955149, 3.48477574;stroke-dashoffset:0;stroke-opacity:1"
+       d="m 148.49243,353.35696 483.86306,-1e-5"
+       id="path3359"
+       inkscape:connector-curvature="0" />
+    <text
+       sodipodi:linespacing="125%"
+       id="text3361"
+       y="347.29602"
+       x="458.60925"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       xml:space="preserve"><tspan
+         y="347.29602"
+         x="458.60925"
+         id="tspan3363"
+         sodipodi:role="line">i'</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="637.40619"
+       y="385.66147"
+       id="text3365"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3367"
+         x="637.40619"
+         y="385.66147">xs(i')</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="486.89352"
+       y="201.81377"
+       id="text4181"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4183"
+         x="486.89352"
+         y="201.81377">A</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="625.28442"
+       y="344.24527"
+       id="text4185"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4187"
+         x="625.28442"
+         y="344.24527">B</tspan></text>
+  </g>
+</svg>
diff --git a/labs/lab2-reductions-and-prefix-sums/angle-example.svg b/labs/lab2-reductions-and-prefix-sums/angle-example.svg
new file mode 100755
index 0000000..fd67412
--- /dev/null
+++ b/labs/lab2-reductions-and-prefix-sums/angle-example.svg
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 744.09448819 1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="angle-example.svg"
+   inkscape:export-filename="C:\cygwin\home\axel22\workspaces\scala\progfun\instructions\angle.png"
+   inkscape:export-xdpi="32.263699"
+   inkscape:export-ydpi="32.263699">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4146"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) translate(12.5,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lend"
+       style="overflow:visible;"
+       inkscape:isstock="true">
+      <path
+         id="path4149"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) rotate(180) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.9899495"
+     inkscape:cx="488.53326"
+     inkscape:cy="701.51987"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1600"
+     inkscape:window-height="877"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4"
+     inkscape:window-maximized="1"
+     inkscape:snap-text-baseline="false" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Lstart)"
+       d="m 142.85714,101.50506 0,298.57143"
+       id="path3336"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3.8496573;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+       d="m 142.85714,399.21935 668.06991,0"
+       id="path3340"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:3,3;stroke-dashoffset:0"
+       d="m 143.44166,399.80366 c 88.89343,-20.20305 105.05587,-39.39595 105.05587,-39.39595"
+       id="path4640"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3,3;stroke-opacity:1;stroke-dashoffset:0"
+       d="m 248.24499,360.5773 c 0,0 34.72027,-23.20876 57.95379,-32.30013 39.28467,-15.37226 57.45614,8.89703 57.45614,8.89703 19.69797,32.32488 43.43656,27.27412 55.55839,8.08122 l 12.12183,-19.1929 0,0 c 40.4061,-86.87311 77.78174,-113.13708 77.78174,-113.13708 0,0 18.22571,-16.58543 45.38508,0.61276 28.26715,17.89967 41.48804,62.0167 41.48804,62.0167"
+       id="path4642"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cscscccsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3,3;stroke-opacity:1;stroke-dashoffset:0"
+       d="m 595.70683,274.55743 c 0,0 12.08157,73.3608 31.09283,77.76906 28.85051,6.68975 48.99239,-25.75889 48.99239,-25.75889 0,0 17.42513,-17.42513 27.27412,-14.89975 9.84899,2.52538 30.50526,33.55149 30.50526,33.55149"
+       id="path4752"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cscsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 142.85714,398.79078 500,221.64792"
+       id="path4832"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:6,3;stroke-dashoffset:0"
+       d="m 501.03566,222.01682 0,176.77669"
+       id="path5388"
+       inkscape:connector-curvature="0" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path5390"
+       d="m 142.43151,219.99652 358.60415,-1e-5"
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="310.11682"
+       y="200.8036"
+       id="text5392"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan5394"
+         x="310.11682"
+         y="200.8036">i</tspan></text>
+    <text
+       sodipodi:linespacing="125%"
+       id="text5396"
+       y="326.0625"
+       x="506.0864"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       xml:space="preserve"><tspan
+         y="326.0625"
+         x="506.0864"
+         id="tspan5398"
+         sodipodi:role="line">xs(i)</tspan></text>
+  </g>
+</svg>
diff --git a/labs/lab2-reductions-and-prefix-sums/angle.png b/labs/lab2-reductions-and-prefix-sums/angle.png
new file mode 100755
index 0000000000000000000000000000000000000000..50a8cc00497fa82cbb7442ac40ddd56b88dc9885
GIT binary patch
literal 4134
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
Q=%?qfGQW(hHFb~w9~VT-?EnA(

literal 0
HcmV?d00001

diff --git a/labs/lab2-reductions-and-prefix-sums/angle2.png b/labs/lab2-reductions-and-prefix-sums/angle2.png
new file mode 100755
index 0000000000000000000000000000000000000000..d5b4fccface66a660bcee7539458198423fb674e
GIT binary patch
literal 6122
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

literal 0
HcmV?d00001

diff --git a/labs/lab2-reductions-and-prefix-sums/terrain.png b/labs/lab2-reductions-and-prefix-sums/terrain.png
new file mode 100755
index 0000000000000000000000000000000000000000..11ba1b6f55b18bdd7d93e73c3e751739eaf1be32
GIT binary patch
literal 2863
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

literal 0
HcmV?d00001

diff --git a/labs/lab2-reductions-and-prefix-sums/terrain.svg b/labs/lab2-reductions-and-prefix-sums/terrain.svg
new file mode 100755
index 0000000..d9073e0
--- /dev/null
+++ b/labs/lab2-reductions-and-prefix-sums/terrain.svg
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 744.09448819 1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="terain.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4146"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) translate(12.5,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lend"
+       style="overflow:visible;"
+       inkscape:isstock="true">
+      <path
+         id="path4149"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) rotate(180) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.49497475"
+     inkscape:cx="363.63782"
+     inkscape:cy="644.8613"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1600"
+     inkscape:window-height="877"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Lstart)"
+       d="m 142.85714,101.50506 0,298.57143"
+       id="path3336"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3.8496573;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+       d="m 142.85714,399.21935 668.06991,0"
+       id="path3340"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+       d="m 143.44166,399.80366 c 88.89343,-20.20305 105.05587,-39.39595 105.05587,-39.39595"
+       id="path4640"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 248.24499,360.5773 c 0,0 34.72027,-23.20876 57.95379,-32.30013 39.28467,-15.37226 57.45614,8.89703 57.45614,8.89703 19.69797,32.32488 43.43656,27.27412 55.55839,8.08122 l 12.12183,-19.1929 0,0 c 40.4061,-86.87311 77.78174,-113.13708 77.78174,-113.13708 0,0 18.22571,-16.58543 45.38508,0.61276 28.26715,17.89967 41.48804,62.0167 41.48804,62.0167"
+       id="path4642"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cscscccsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 595.70683,274.55743 c 0,0 12.08157,73.3608 31.09283,77.76906 28.85051,6.68975 48.99239,-25.75889 48.99239,-25.75889 0,0 17.42513,-17.42513 27.27412,-14.89975 9.84899,2.52538 30.50526,33.55149 30.50526,33.55149"
+       id="path4752"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cscsc" />
+  </g>
+</svg>
diff --git a/labs/lab2-reductions-and-prefix-sums/visibility.png b/labs/lab2-reductions-and-prefix-sums/visibility.png
new file mode 100755
index 0000000000000000000000000000000000000000..9a26107e64f77f3a6d217f5ea80e0cffdb8127d8
GIT binary patch
literal 3534
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
IM6N<$f~b&_djJ3c

literal 0
HcmV?d00001

diff --git a/labs/lab2-reductions-and-prefix-sums/visibility.svg b/labs/lab2-reductions-and-prefix-sums/visibility.svg
new file mode 100755
index 0000000..e4f1882
--- /dev/null
+++ b/labs/lab2-reductions-and-prefix-sums/visibility.svg
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 744.09448819 1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="visibility.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Lstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path4146"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) translate(12.5,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Lend"
+       style="overflow:visible;"
+       inkscape:isstock="true">
+      <path
+         id="path4149"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         transform="scale(0.8) rotate(180) translate(12.5,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="450.29691"
+     inkscape:cy="792.75432"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1600"
+     inkscape:window-height="877"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Lstart)"
+       d="m 142.85714,101.50506 0,298.57143"
+       id="path3336"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3.8496573;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+       d="m 142.85714,399.21935 668.06991,0"
+       id="path3340"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 143.44166,399.80366 C 229.1208,389.60061 247.60467,362.19342 247.60467,362.19342"
+       id="path4640"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 247.12126,362.59389 c 0,0 35.844,-25.22535 59.07752,-34.31672 50.7702,-20.12943 91.36602,-35.89748 158.70779,-62.92894 2.54896,-14.01596 48.13888,-55.27993 48.13888,-55.27993"
+       id="path4642"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3, 3;stroke-dashoffset:0;stroke-opacity:1"
+       d="m 594.1916,275.56758 c 0,0 12.08157,73.3608 31.09283,77.76906 28.85051,6.68975 48.99239,-25.75889 48.99239,-25.75889 0,0 17.42513,-17.42513 27.27412,-14.89975 9.84899,2.52538 30.50526,33.55149 30.50526,33.55149"
+       id="path4752"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cscsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 312.07723,326.23693 465.71429,265.21935"
+       id="path4754"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 511.25,211.11221 742.85714,79.505061"
+       id="path4756"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       sodipodi:nodetypes="cscscccsc"
+       inkscape:connector-curvature="0"
+       id="path4758"
+       d="m 245.5023,364.2185 c 0,0 35.844,-25.22535 59.07752,-34.31672 39.28467,-15.37226 57.45614,8.89703 57.45614,8.89703 19.69797,32.32488 43.43656,27.27412 55.55839,8.08122 l 12.12183,-19.1929 0,0 c 40.4061,-86.87311 77.78174,-113.13708 77.78174,-113.13708 0,0 18.22571,-16.58543 45.38508,0.61276 28.26715,17.89967 41.48804,62.0167 41.48804,62.0167"
+       style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3, 3;stroke-dashoffset:0;stroke-opacity:1" />
+  </g>
+</svg>
-- 
GitLab