├── project
├── build.properties
└── plugins.sbt
├── .gitignore
├── src
└── main
│ └── scala
│ └── lambda
│ ├── stream
│ ├── data
│ │ └── Main.scala
│ └── basics.scala
│ ├── io
│ ├── priority
│ │ ├── HighLowPriorityRunner.scala
│ │ └── Main.scala
│ └── basics.scala
│ └── intro.scala
├── README.md
└── fahrenheit.txt
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.7.1
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.4.1")
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # editors specific
2 | *.swp
3 | *~*
4 | *#*
5 | .cache-main
6 | .classpath
7 | .project
8 | .settings/
9 | .*.md.html
10 |
11 | # scala/java specific
12 | *.class
13 | *.log
14 |
15 | # sbt/mvn specific
16 | .cache
17 | .history
18 | .lib/
19 | dist/*
20 | target/
21 | lib_managed/
22 | src_managed/
23 | project/boot/
24 | project/plugins/project/
25 | /bin/
26 |
--------------------------------------------------------------------------------
/src/main/scala/lambda/stream/data/Main.scala:
--------------------------------------------------------------------------------
1 | package lambda.stream.data
2 |
3 | import cats.effect.{IO, IOApp}
4 | import cats.syntax.all._
5 | import fs2.io.file.{Files, Path}
6 |
7 | object Main extends IOApp.Simple {
8 | def fahrenheitToCelsius(f: Double): Double =
9 | (f - 32.0) * (5.0 / 9.0)
10 |
11 | override final val run: IO[Unit] =
12 | Files[IO]
13 | .readAll(Path("fahrenheit.txt"))
14 | .through(fs2.text.utf8.decode)
15 | .through(fs2.text.lines)
16 | .tail
17 | .mapFilter(line => line.toDoubleOption)
18 | .map(fahrenheitToCelsius)
19 | .map(celsius => celsius.toString)
20 | .intersperse("\n")
21 | .through(fs2.text.utf8.encode)
22 | .through(Files[IO].writeAll(Path("celsius.txt")))
23 | .compile
24 | .drain
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/lambda/io/priority/HighLowPriorityRunner.scala:
--------------------------------------------------------------------------------
1 | package lambda.io.priority
2 |
3 | import cats.effect.IO
4 | import cats.effect.std.{Queue, Semaphore, Supervisor}
5 | import scala.concurrent.duration._
6 |
7 | object HighLowPriorityRunner {
8 | final case class Config(
9 | highPriorityJobs: Queue[IO, IO[Unit]],
10 | lowPriorityJobs: Queue[IO, IO[Unit]],
11 | rateLimiter: Semaphore[IO]
12 | )
13 |
14 | def apply(config: Config): IO[Unit] =
15 | Supervisor[IO].use { supervisor =>
16 | val nextJob =
17 | config.highPriorityJobs.tryTake.flatMap {
18 | case Some(hpJob) => hpJob
19 | case None => config.lowPriorityJobs.tryTake.flatMap {
20 | case Some(lpJob) => lpJob
21 | case None => IO.sleep(100.millis)
22 | }
23 | }
24 |
25 | val processOneJob =
26 | config.rateLimiter.acquire >>
27 | supervisor.supervise(nextJob.guarantee(config.rateLimiter.release))
28 |
29 | processOneJob.foreverM.void
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/scala/lambda/io/priority/Main.scala:
--------------------------------------------------------------------------------
1 | package lambda.io.priority
2 |
3 | import cats.effect.{IO, IOApp}
4 | import cats.effect.std.{Queue, Semaphore}
5 | import cats.syntax.all._
6 | import scala.concurrent.duration._
7 |
8 | object Main extends IOApp.Simple {
9 | private def createJob(id: Int, highPriority: Boolean = true): IO[Unit] =
10 | IO.println(s"Starting (${if (highPriority) "High" else "Low"} priority) job ${id}") >>
11 | IO.sleep(1.second) >>
12 | IO.println(s"Finished job ${id}!")
13 |
14 | override final val run: IO[Unit] =
15 | (
16 | Queue.unbounded[IO, IO[Unit]],
17 | Queue.unbounded[IO, IO[Unit]],
18 | Semaphore[IO](n = 2)
19 | ).mapN(HighLowPriorityRunner.Config.apply).flatMap { config =>
20 | HighLowPriorityRunner(config).background.surround {
21 | List.range(0, 10).traverse_(id => config.lowPriorityJobs.offer(createJob(id, highPriority = false))) >>
22 | IO.sleep(100.millis) >>
23 | List.range(10, 15).traverse_(id => config.highPriorityJobs.offer(createJob(id))) >>
24 | IO.sleep(4.seconds) >>
25 | List.range(15, 20).traverse_(id => config.highPriorityJobs.offer(createJob(id))) >>
26 | IO.sleep(4.seconds)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/scala/lambda/intro.scala:
--------------------------------------------------------------------------------
1 | package lambda
2 |
3 | sealed trait Program[+A] {
4 | import Program.{Continue, Halt}
5 |
6 | def transformOutput[B](f: A => B): Program[B] = this match {
7 | case Continue(value) => Continue(f(value))
8 | case Halt => Halt
9 | }
10 |
11 | def zip[B](that: Program[B]): Program[(A, B)] = this match {
12 | case Continue(a) => that.transformOutput(b => (a, b))
13 | case Halt => Halt
14 | }
15 |
16 | def chainProgram[B](f: A => Program[B]): Program[B] = this match {
17 | case Continue(value) => f(value)
18 | case Halt => Halt
19 | }
20 |
21 | def recover[B >: A](fallback: Program[B]): Program[B] = this match {
22 | case Continue(value) => Continue(value)
23 | case Halt => fallback
24 | }
25 |
26 | def resultOr[B >: A](default: B): B = this match {
27 | case Continue(value) => value
28 | case Halt => default
29 | }
30 | }
31 |
32 | object Program {
33 | private final case class Continue[+A](value: A) extends Program[A]
34 | private final case object Halt extends Program[Nothing]
35 |
36 | def continue[A](value: A): Program[A] = Continue(value)
37 | def halt: Program[Nothing] = Halt
38 | }
39 |
40 | object Example {
41 | def num(n: Int): Program[Int] =
42 | Program.continue(n)
43 |
44 | def duplicate(p: Program[Int]): Program[Int] =
45 | p.transformOutput(n => n * 2)
46 |
47 | def divide(p1: Program[Int], p2: Program[Int]): Program[Int] =
48 | p1.zip(p2).chainProgram {
49 | case (n1, n2) =>
50 | if (n2 != 0) Program.continue(n1 / n2)
51 | else Program.halt
52 | }
53 |
54 | def main(args: Array[String]): Unit = {
55 | // ((x / y) / z) * 2
56 | def equation(x: Int, y: Int, z: Int): Int =
57 | duplicate(divide(
58 | divide(num(x), num(y)),
59 | num(z)
60 | )).resultOr(-1)
61 |
62 | println(s"The result is: ${equation(100, 10, 5)}")
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/scala/lambda/io/basics.scala:
--------------------------------------------------------------------------------
1 | package lambda.io
2 |
3 | import cats.effect.{IO, IOApp, Resource}
4 | import cats.syntax.all._
5 | import scala.concurrent.duration._ // Provides duration units.
6 | import scala.io.Source
7 |
8 | /** Program 1: Sequential composition. */
9 | object Basics1_1 extends IOApp.Simple {
10 | val myIO: IO[Unit] =
11 | IO.println("Hello, World!")
12 |
13 | override final val run: IO[Unit] =
14 | myIO.flatMap(_ => myIO)
15 | }
16 |
17 | object Basics1_2 extends IOApp.Simple {
18 | val myIO: IO[Unit] =
19 | IO.println("Hello, World!")
20 |
21 | override final val run: IO[Unit] =
22 | myIO >> myIO >> myIO >> myIO >> myIO
23 | }
24 | // ----------------------------------------------
25 |
26 |
27 | /** Program 2: Async & Cancellable operations. */
28 | object Basics2 extends IOApp.Simple {
29 | val tick: IO[Unit] =
30 | (IO.sleep(1.second) >> IO.println("Tick")).foreverM
31 |
32 | val cancelToken: IO[Unit] =
33 | IO.readLine.void
34 |
35 | override final val run: IO[Unit] =
36 | tick.start.flatMap { tickFiber =>
37 | cancelToken >> tickFiber.cancel
38 | }
39 | }
40 | // ----------------------------------------------
41 |
42 |
43 | /** Program 3: Parallel operations. */
44 | object Basics3_1 extends IOApp.Simple {
45 | val ioA: IO[Int] =
46 | IO.sleep(1.second) >> IO.println("Running ioA").as(1)
47 |
48 | val ioB: IO[String] =
49 | IO.sleep(1.second) >> IO.println("Running ioB").as("Balmung")
50 |
51 | val ioC: IO[Boolean] =
52 | IO.sleep(1.second) >> IO.println("Running ioC").as(true)
53 |
54 | override final val run: IO[Unit] =
55 | (ioA, ioB, ioC).parTupled.flatMap {
56 | case (a, b, c) =>
57 | IO.println(s"a: ${a} | b: ${b} | c: ${c}")
58 | }
59 | }
60 |
61 | object Basics3_2 extends IOApp.Simple {
62 | override final val run: IO[Unit] =
63 | List.range(start = 0, end = 11).parTraverse { i =>
64 | IO.sleep(1.second) >> IO.println(i) >> IO.delay((i * 10) + 5)
65 | } flatMap { data =>
66 | IO.println(s"Final data: ${data}")
67 | }
68 | }
69 | // ----------------------------------------------
70 |
71 |
72 | /** Program 4: Error handling and resource management. */
73 | object Basics4 extends IOApp.Simple {
74 | def fahrenheitToCelsius(f: Double): Double =
75 | (f - 32.0) * (5.0 / 9.0)
76 |
77 | def process(lines: List[String]): List[Double] =
78 | lines.filter { line =>
79 | !line.trim.isEmpty && !line.startsWith("//")
80 | } map { line =>
81 | fahrenheitToCelsius(f = line.toDouble)
82 | }
83 |
84 | override final val run: IO[Unit] =
85 | Resource
86 | .fromAutoCloseable(IO(Source.fromFile("fahrenheit.txt")))
87 | .use(file => IO(file.getLines().toList))
88 | .map(process)
89 | .attempt
90 | .flatMap {
91 | case Right(data) =>
92 | IO.println(s"Output: ${data.take(5).mkString("[", ", ", ", ...]")}")
93 |
94 | case Left(ex) =>
95 | IO.println(s"Error: ${ex.getMessage}")
96 | }
97 | }
98 | // ----------------------------------------------
99 |
--------------------------------------------------------------------------------
/src/main/scala/lambda/stream/basics.scala:
--------------------------------------------------------------------------------
1 | package lambda.stream
2 |
3 | import cats.effect.{IO, IOApp, Ref, Resource}
4 | import cats.effect.std.Random
5 | import fs2.Stream
6 | import scala.concurrent.duration._
7 |
8 | /** Program 1: Infinite data. */
9 | object Basics1_1 extends IOApp.Simple {
10 | override final val run: IO[Unit] =
11 | Stream(1, 2, 3)
12 | .covary[IO]
13 | .compile
14 | .toList
15 | .flatMap(IO.println)
16 | }
17 |
18 | object Basics1_2 extends IOApp.Simple {
19 | override final val run: IO[Unit] =
20 | Stream
21 | .iterate(0)(x => x + 1)
22 | .evalTap(IO.println)
23 | .take(10)
24 | .compile
25 | .toList
26 | .flatMap(IO.println)
27 | }
28 |
29 | object Basics1_3 extends IOApp.Simple {
30 | override final val run: IO[Unit] =
31 | Random.scalaUtilRandomSeedInt[IO](3).flatMap { random =>
32 | Stream
33 | .repeatEval(random.nextInt)
34 | .take(10)
35 | .compile
36 | .toList
37 | .flatMap(IO.println)
38 | }
39 | }
40 | // ----------------------------------------------
41 |
42 |
43 | /** Program 2: Resource and effects management. */
44 | object Basics2 extends IOApp.Simple {
45 | final class DBService {
46 | val streamAllData: Stream[IO, Int] =
47 | Stream.range(start = 0, stopExclusive = 21, step = 2)
48 | }
49 | object DBService {
50 | final val instance: Resource[IO, DBService] =
51 | Resource.make(
52 | IO.println("Connecting to the database").as(new DBService)
53 | )(_ =>
54 | IO.println("Closing the database connection")
55 | )
56 | }
57 |
58 | override final val run: IO[Unit] =
59 | Stream.resource(DBService.instance).flatMap { db =>
60 | Stream.eval(IO.println("Before query the db")) ++
61 | db.streamAllData.evalMap(IO.println) ++
62 | Stream.eval(IO.println("After query the db"))
63 | }.compile.drain
64 | }
65 | // ----------------------------------------------
66 |
67 |
68 | /** Program 3: Error handling. */
69 | object Basics3 extends IOApp.Simple {
70 | final case object Failure extends Throwable("Faliure")
71 |
72 | override final val run: IO[Unit] =
73 | Stream(1, 2, 3).evalMap {
74 | case 1 => IO.pure("Foo")
75 | case 2 => IO.raiseError(Failure)
76 | case _ => IO.raiseError(new Exception("Fatal"))
77 | }.handleErrorWith {
78 | case Failure => Stream("Bar", "Baz")
79 | case fatal => Stream.exec(IO.println(s"Fatal error ${fatal}"))
80 | }.evalMap(IO.println).compile.drain
81 | }
82 | // ----------------------------------------------
83 |
84 |
85 | /** Program 4: Control flow & concurrency. */
86 | object Basics4 extends IOApp.Simple {
87 | def greetStream(nameRef: Ref[IO, String]): Stream[IO, Nothing] =
88 | Stream.fixedDelay[IO](1.second).foreach { _ =>
89 | nameRef.get.flatMap { name =>
90 | IO.println(s"Hello ${name}!")
91 | }
92 | }
93 |
94 | def updatesStream(nameRef: Ref[IO, String]): Stream[IO, Unit] =
95 | Stream.repeatEval(IO.readLine).evalMap { line =>
96 | if (line.isBlank) IO.none[Unit]
97 | else nameRef.set(line.trim).option
98 | }.unNoneTerminate
99 |
100 | override final val run: IO[Unit] =
101 | IO.ref("Luis").flatMap { nameRef =>
102 | updatesStream(nameRef)
103 | .concurrently(greetStream(nameRef))
104 | .compile
105 | .drain
106 | }
107 | }
108 | // ----------------------------------------------
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Programs as Values
2 |
3 | Source code of the _"Programs as Values: The foundations of next-gen concurrent programming"_ tech talk.
4 |
5 | ## Links
6 |
7 | + **Slides**: https://perficient-my.sharepoint.com/:p:/p/luis_mejias/EUqvic_6kUJCr8q0ogTRjp8BCBROZstVkaosgd1WlEIPjA
8 | + **Talk recording** _(in Spanish)_: https://www.youtube.com/watch?v=iJ5jmMeAZwE&t=5652s&ab_channel=Josekell
9 | + **Programs as Values series** _(Fabio Labella)_: https://systemfw.org/archive.html
10 | + **The case for effect systems** _(Daniel Spiewak)_: https://www.youtube.com/watch?v=qgfCmQ-2tW0
11 | + **Streams - Your New Favorite Primitive** _(Ryan Peters)_: https://www.youtube.com/watch?v=BZ8O6T7Y1UE
12 | + **Functional Programming with Effects** _(Rob Norris)_: https://www.youtube.com/watch?v=30q6BkBv5MY&ab_channel=ScalaDaysConferences
13 | + **What is an Effect?** _(Adam Rosien)_: https://www.inner-product.com/posts/what-is-an-effect/
14 | + **Why FP** _(Luis Miguel Mejía Suárez)_: https://gist.github.com/BalmungSan/bdb163a080af54d3713e9e7c4a37ff51
15 |
16 | ## Extras
17 |
18 | ### Referential Transparency
19 |
20 | Referential transparency is a property of expressions,
21 | which dictates that you can always replace a variable with the expression it refers,
22 | without altering in any way the behaviour of the program.
23 | In the same way, you can always give a name to any expression,
24 | and use this new variable in all the places where the same expression was used;
25 | and, again, the behaviour of the program must remain the same.
26 |
27 | Let's see in practice what does that means:
28 |
29 | ```scala
30 | val data = List(1, 2, 3)
31 | val first = data.head
32 | val result = first + first
33 | println(result)
34 | ```
35 |
36 | This little program will print `2` since `first` refers to the `head` of `data`; which is `1`
37 | Now, let's see what happens if we replace the `first` variable with its expression.
38 |
39 | ```scala
40 | val data = List(1, 2, 3)
41 | val result = data.head + data.head
42 | println(result)
43 | ```
44 |
45 | The result will be the same since `data.head` will always return `1`
46 | Thus, we can say that `List#head` is referentially transparent.
47 |
48 | Let's now see and example that breaks the property:
49 |
50 | ```scala
51 | val data = List(1, 2, 3).iterator
52 | val first = data.next()
53 | val result = first + first
54 | println(result)
55 | ```
56 |
57 | Again, the above program will print `2`;
58 | because, `first` will evaluate to `data.next()`, which on its first call will return `1`
59 | But, this time the result will change if we replace `first` with the expression it refers to:
60 |
61 | ```scala
62 | val data = List(1, 2, 3).iterator
63 | val result = data.next() + data.next()
64 | println(result)
65 | ```
66 |
67 | In this case, the program will print `3`;
68 | because, the first `data.next()` will return `1` but the second call will return `2`, so `result` will be `3`
69 | As such, we can say that `Iterator#next()` is NOT referentially transparent.
70 |
71 | > **Note**: Expressions that satisfy this property will be called _"values"_.
72 | > Additionally, if a program is made up entirely of referentially transparent expressions _(values)_,
73 | > then you may evaluate it using the _"substitution model"_.
74 |
75 | ### Composition
76 |
77 | Composition is a property of systems,
78 | where you can build complex systems by composing simpler ones.
79 | In consequence, it also means that you can understand complex systems
80 | by understating its parts and the way the compose.
81 |
82 | > **Note**: Composition is not a binary attribute like Referential Transparency, but rather an spectrum;
83 | > the more compositional our programs are then they will be easier to refactor.
84 |
85 | ### Monads
86 |
87 | The (_in_)famous _"M"_ word,
88 | Monads are a mechanism used to solve a fundamental problem that you will find
89 | when using the _"Programs as Values"_ paradigm.
90 | To understand them, let's first understand the problem.
91 |
92 | When our programs only manipulate plain values using functions _(which are also values)_,
93 | it is very simple to compose those functions together into bigger ones.
94 | For example, if we had a function `f: A => B` and a function: `g: B => C`
95 | then creating a function `h: A => C` is as simple as `h = a => g(f(a))`
96 |
97 | However, what happens when we now have effectual values like `Option[A]` or `IO[A]`,
98 | and effectual functions like `f: A => F[B]` and `g: B => F[C]`;
99 | we can not longer use traditional function composition to create `h: A => F[C]`
100 | Although, we can do this:
101 |
102 | ```scala
103 | val h: A => F[C] = { a: A =>
104 | f(a).flatMap(g)
105 | }
106 | ```
107 |
108 | Nevertheless, this requires the assumption that such `flatMap` function exists and has the type signature we want,
109 | that is what Monads are, a Monad is just a triplet of a:
110 | + A type constructor _(`F[_]`)_
111 | + A `flatMap(fa: F[A])(f: A => F[B]): F[B]` implementation for that type constructor _(and also `pure(a: A): F[A]`)_
112 | + A proof that such implementation satisfies some laws
113 |
114 | More importantly, such laws guarantee that such `flatMap` function somehow represents the concept of sequence.
115 | Meaning that for `IO` `flatMap` always means do this and then do that, just like a `;` on imperative languages.
116 |
--------------------------------------------------------------------------------
/fahrenheit.txt:
--------------------------------------------------------------------------------
1 | // this file contains a list of temperatures in degrees fahrenheit
2 | 18.0
3 | 17.9
4 | 25.3
5 | 34.2
6 | 55.3
7 | 44.2
8 | 22.0
9 | 19.0
10 | 44.7
11 | 33.2
12 | 55.7
13 | 66
14 | 78
15 | 72
16 | 64.0
17 | 99
18 | 18.0
19 | 17.9
20 | 25.3
21 | 34.2
22 | 55.3
23 | 44.2
24 | 22.0
25 | 19.0
26 | 44.7
27 | 33.2
28 | 55.7
29 | 66
30 | 78
31 | 72
32 | 64.0
33 | 99
34 | 18.0
35 | 17.9
36 | 25.3
37 | 34.2
38 | 55.3
39 | 44.2
40 | 22.0
41 | 18.0
42 | 17.9
43 | 25.3
44 | 34.2
45 | 55.3
46 | 44.2
47 | 22.0
48 | 19.0
49 | 44.7
50 | 33.2
51 | 55.7
52 | 66
53 | 78
54 | 72
55 | 64.0
56 | 99
57 | 18.0
58 | 17.9
59 | 18.0
60 | 17.9
61 | 25.3
62 | 34.2
63 | 55.3
64 | 44.2
65 | 22.0
66 | 19.0
67 | 44.7
68 | 33.2
69 | 55.7
70 | 66
71 | 78
72 | 72
73 | 64.0
74 | 99
75 | 18.0
76 | 17.9
77 | 25.3
78 | 34.2
79 | 55.3
80 | 44.2
81 | 22.0
82 | 19.0
83 | 44.7
84 | 33.2
85 | 55.7
86 | 66
87 | 78
88 | 72
89 | 64.0
90 | 99
91 | 18.0
92 | 17.9
93 | 25.3
94 | 34.2
95 | 55.3
96 | 44.2
97 | 22.0
98 | 19.0
99 | 44.7
100 | 33.2
101 | 55.7
102 | 66
103 | 78
104 | 72
105 | 64.0
106 | 99
107 | 18.0
108 | 17.9
109 | 25.3
110 | 34.2
111 | 55.3
112 | 44.2
113 | 22.0
114 | 19.0
115 | 44.7
116 | 33.2
117 | 55.7
118 | 66
119 | 78
120 | 72
121 | 64.0
122 | 18.0
123 | 17.9
124 | 25.3
125 | 34.2
126 | 55.3
127 | 44.2
128 | 22.0
129 | 19.0
130 | 44.7
131 | 33.2
132 | 55.7
133 | 66
134 | 78
135 | 72
136 | 64.0
137 | 99
138 | 18.0
139 | 17.9
140 | 25.3
141 | 34.2
142 | 55.3
143 | 44.2
144 | 22.0
145 | 19.0
146 | 44.7
147 | 33.2
148 | 55.7
149 | 66
150 | 78
151 | 72
152 | 64.0
153 | 99
154 | 18.0
155 | 17.9
156 | 25.3
157 | 34.2
158 | 55.3
159 | 44.2
160 | 22.0
161 | 19.0
162 | 44.7
163 | 33.2
164 | 55.7
165 | 66
166 | 78
167 | 72
168 | 64.0
169 | 99
170 | 18.0
171 | 17.9
172 | 25.3
173 | 34.2
174 | 55.3
175 | 44.2
176 | 22.0
177 | 19.0
178 | 44.7
179 | 33.2
180 | 55.7
181 | 66
182 | 78
183 | 72
184 | 64.0
185 | 99
186 | 18.0
187 | 17.9
188 | 25.3
189 | 34.2
190 | 55.3
191 | 44.2
192 | 22.0
193 | 19.0
194 | 44.7
195 | 33.2
196 | 55.7
197 | 66
198 | 78
199 | 72
200 | 64.0
201 | 99
202 | 99
203 | 18.0
204 | 17.9
205 | 25.3
206 | 34.2
207 | 55.3
208 | 44.2
209 | 22.0
210 | 19.0
211 | 44.7
212 | 33.2
213 | 55.7
214 | 66
215 | 78
216 | 72
217 | 64.0
218 | 99
219 | 25.3
220 | 34.2
221 | 55.3
222 | 44.2
223 | 22.0
224 | 19.0
225 | 44.7
226 | 33.2
227 | 55.7
228 | 66
229 | 78
230 | 72
231 | 64.0
232 | 99
233 | 18.0
234 | 17.9
235 | 25.3
236 | 34.2
237 | 55.3
238 | 44.2
239 | 22.0
240 | 19.0
241 | 44.7
242 | 33.2
243 | 55.7
244 | 66
245 | 78
246 | 72
247 | 64.0
248 | 99
249 | 18.0
250 | 17.9
251 | 25.3
252 | 34.2
253 | 55.3
254 | 44.2
255 | 22.0
256 | 19.0
257 | 44.7
258 | 33.2
259 | 55.7
260 | 66
261 | 78
262 | 72
263 | 64.0
264 | 99
265 | 18.0
266 | 17.9
267 | 25.3
268 | 34.2
269 | 55.3
270 | 44.2
271 | 22.0
272 | 19.0
273 | 44.7
274 | 33.2
275 | 55.7
276 | 66
277 | 78
278 | 72
279 | 64.0
280 | 99
281 | 19.0
282 | 44.7
283 | 33.2
284 | 55.7
285 | 66
286 | 78
287 | 72
288 | 64.0
289 | 99
290 | 18.0
291 | 17.9
292 | 25.3
293 | 34.2
294 | 55.3
295 | 44.2
296 | 22.0
297 | 19.0
298 | 44.7
299 | 33.2
300 | 55.7
301 | 66
302 | 78
303 | 72
304 | 64.0
305 | 99
306 | 18.0
307 | 17.9
308 | 25.3
309 | 34.2
310 | 55.3
311 | 44.2
312 | 22.0
313 | 19.0
314 | 44.7
315 | 33.2
316 | 55.7
317 | 66
318 | 78
319 | 72
320 | 64.0
321 | 99
322 | 18.0
323 | 17.9
324 | 25.3
325 | 34.2
326 | 55.3
327 | 44.2
328 | 22.0
329 | 19.0
330 | 44.7
331 | 33.2
332 | 55.7
333 | 66
334 | 78
335 | 72
336 | 64.0
337 | 99
338 | 18.0
339 | 17.9
340 | 25.3
341 | 34.2
342 | 55.3
343 | 44.2
344 | 22.0
345 | 19.0
346 | 44.7
347 | 33.2
348 | 55.7
349 | 66
350 | 78
351 | 72
352 | 64.0
353 | 99
354 | 18.0
355 | 17.9
356 | 25.3
357 | 34.2
358 | 55.3
359 | 44.2
360 | 22.0
361 | 19.0
362 | 44.7
363 | 33.2
364 | 55.7
365 | 66
366 | 78
367 | 72
368 | 64.0
369 | 99
370 | 18.0
371 | 17.9
372 | 25.3
373 | 34.2
374 | 55.3
375 | 44.2
376 | 22.0
377 | 19.0
378 | 44.7
379 | 33.2
380 | 55.7
381 | 66
382 | 78
383 | 72
384 | 64.0
385 | 99
386 | 18.0
387 | 17.9
388 | 25.3
389 | 34.2
390 | 55.3
391 | 44.2
392 | 22.0
393 | 19.0
394 | 44.7
395 | 33.2
396 | 55.7
397 | 66
398 | 78
399 | 72
400 | 64.0
401 | 99
402 | 44.7
403 | 33.2
404 | 55.7
405 | 66
406 | 78
407 | 72
408 | 64.0
409 | 99
410 | 18.0
411 | 17.9
412 | 25.3
413 | 34.2
414 | 55.3
415 | 44.2
416 | 22.0
417 | 19.0
418 | 44.7
419 | 33.2
420 | 55.7
421 | 66
422 | 78
423 | 72
424 | 64.0
425 | 99
426 | 18.0
427 | 17.9
428 | 25.3
429 | 34.2
430 | 55.3
431 | 44.2
432 | 22.0
433 | 19.0
434 | 44.7
435 | 33.2
436 | 55.7
437 | 66
438 | 78
439 | 72
440 | 64.0
441 | 99
442 | 18.0
443 | 17.9
444 | 25.3
445 | 34.2
446 | 55.3
447 | 44.2
448 | 22.0
449 | 19.0
450 | 44.7
451 | 33.2
452 | 55.7
453 | 66
454 | 78
455 | 72
456 | 64.0
457 | 99
458 | 18.0
459 | 17.9
460 | 25.3
461 | 34.2
462 | 55.3
463 | 44.2
464 | 22.0
465 | 19.0
466 | 44.7
467 | 33.2
468 | 55.7
469 | 66
470 | 78
471 | 72
472 | 64.0
473 | 99
474 | 99
475 | 18.0
476 | 17.9
477 | 25.3
478 | 34.2
479 | 55.3
480 | 44.2
481 | 22.0
482 | 19.0
483 | 44.7
484 | 33.2
485 | 55.7
486 | 66
487 | 78
488 | 72
489 | 64.0
490 | 99
491 | 25.3
492 | 34.2
493 | 55.3
494 | 44.2
495 | 22.0
496 | 19.0
497 | 44.7
498 | 33.2
499 | 55.7
500 | 66
501 | 78
502 | 72
503 | 64.0
504 | 99
505 | 18.0
506 | 17.9
507 | 25.3
508 | 34.2
509 | 55.3
510 | 44.2
511 | 22.0
512 | 19.0
513 | 44.7
514 | 33.2
515 | 55.7
516 | 66
517 | 78
518 | 72
519 | 64.0
520 | 99
521 | 18.0
522 | 17.9
523 | 25.3
524 | 34.2
525 | 55.3
526 | 44.2
527 | 22.0
528 | 19.0
529 | 44.7
530 | 33.2
531 | 55.7
532 | 66
533 | 78
534 | 72
535 | 64.0
536 | 99
537 | 18.0
538 | 17.9
539 | 25.3
540 | 34.2
541 | 55.3
542 | 44.2
543 | 22.0
544 | 19.0
545 | 44.7
546 | 33.2
547 | 55.7
548 | 66
549 | 78
550 | 72
551 | 64.0
552 | 99
553 | 19.0
554 | 44.7
555 | 33.2
556 | 55.7
557 | 66
558 | 78
559 | 72
560 | 64.0
561 | 99
562 | 18.0
563 | 17.9
564 | 25.3
565 | 34.2
566 | 55.3
567 | 44.2
568 | 22.0
569 | 19.0
570 | 44.7
571 | 33.2
572 | 55.7
573 | 66
574 | 78
575 | 72
576 | 64.0
577 | 99
578 | 18.0
579 | 17.9
580 | 25.3
581 | 34.2
582 | 55.3
583 | 44.2
584 | 22.0
585 | 19.0
586 | 44.7
587 | 33.2
588 | 55.7
589 | 66
590 | 78
591 | 72
592 | 64.0
593 | 99
594 | 18.0
595 | 17.9
596 | 25.3
597 | 34.2
598 | 55.3
599 | 44.2
600 | 22.0
601 | 19.0
602 | 44.7
603 | 33.2
604 | 55.7
605 | 66
606 | 78
607 | 72
608 | 64.0
609 | 99
610 | 18.0
611 | 17.9
612 | 25.3
613 | 34.2
614 | 55.3
615 | 44.2
616 | 22.0
617 | 19.0
618 | 44.7
619 | 33.2
620 | 55.7
621 | 66
622 | 78
623 | 72
624 | 64.0
625 | 99
626 | 18.0
627 | 17.9
628 | 25.3
629 | 34.2
630 | 55.3
631 | 44.2
632 | 22.0
633 | 19.0
634 | 44.7
635 | 33.2
636 | 55.7
637 | 66
638 | 78
639 | 72
640 | 64.0
641 | 99
642 | 18.0
643 | 17.9
644 | 25.3
645 | 34.2
646 | 55.3
647 | 44.2
648 | 22.0
649 | 19.0
650 | 44.7
651 | 33.2
652 | 55.7
653 | 66
654 | 78
655 | 72
656 | 64.0
657 | 99
658 | 18.0
659 | 17.9
660 | 25.3
661 | 34.2
662 | 55.3
663 | 44.2
664 | 22.0
665 | 19.0
666 | 44.7
667 | 33.2
668 | 55.7
669 | 66
670 | 78
671 | 72
672 | 64.0
673 | 99
674 |
--------------------------------------------------------------------------------