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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

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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
W?hyXq*~-Acz~JfX=d#Wzp$Pz}kdu1=

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