From 6f285664739acf20356adc3436a9c9471981ec71 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Thu, 18 Feb 2016 11:55:36 -0600 Subject: [PATCH 01/12] Started writing the task TODO descriptions so that make more sense to me. Will provide a sample for a PR to see if the original author concurs. --- .../_0_Hello_World/HelloWorld.kt | 29 ++++++++++++++++--- .../JavaToKotlinConverter.kt | 13 +++++++-- .../_2_Named_Arguments/NamedArguments.kt | 10 +++++-- .../DefaultAndNamedParams.kt | 20 ++++++++----- src/i_introduction/_4_Lambdas/Lambdas.kt | 9 ++++-- .../_5_String_Templates/StringTemplates.kt | 11 +++++-- 6 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index 09c9ac27a..aa4a41616 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -7,10 +7,31 @@ fun todoTask0(): Nothing = TODO( """ Introduction: - Workshop tasks are usually to change the function 'taskN' by replacing its body - (which starts out as the function invocation 'todoTaskN()'), with the correct code according to the problem. - The function 'todoTaskN()' throws an exception, so you usually have to replace that invocation with - meaningful code. + The Kotlin Koans Workshop aims to teach you Kotlin by solving a writing code + to solve problems. For each task, there is an associated unit test that + checks your solution. You can run all of the tests (check all of the work + so far) by executing the "All tests" target. + + Individual workshop tasks (this one, task0, is the first of many) generally + require you to change the function 'taskN' by completely replacing the body + of the function in order to solve the problem and allow the associated unit + test to pass. If you run the unit test for a task that is not correct, the + unit test results will be displayed. If you have not yet made any changes + to a task, the task's TODO message will be displayed and exception will be + thrown. This message will provide guidance on the task to be performed. + + In this example (this file), this means replacing the code below + + fun task0(): String { + return todoTask0() + } + + with the correct, meaningful code in order to solve the problem and allow the associated + unit test to pass, such as: + + fun task0(): String { + return "OK" + } Using 'documentation' argument you can open the related part of Kotlin online documentation. Press 'F1' (Quick Documentation) on 'doc0()', "See also" section gives you a link. diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt index b2ec96386..b23363d10 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt @@ -5,9 +5,16 @@ import util.TODO fun todoTask1(collection: Collection): Nothing = TODO( """ Task 1. - Rewrite JavaCode1.task1 in Kotlin. - In IntelliJ, you can just copy-paste the code and agree to automatically convert it to Kotlin, - but only for this task! + The logic for this task exists in java class JavaCode1, which is in this same folder. + The goal of this task is to replace the body of task1(collection) with the logic + found in JavaCode1.task1(collection), but you need to re-write the logic in Kotlin. + + Note: If you copy the JavaCode1.task1(collection) logic and paste it into this + file's task1(collection) function, IntelliJ will offer to automatically convert + the code from Java to Kotlin for you. Give the automatic conversion a try! + In the future, you will want to reject the automatic conversion and manually + translate the code from Java to Kotlin so you don't cheat yourself out of + the learning opportunity. """, references = { JavaCode1().task1(collection) }) diff --git a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt index 2729d42ef..197d7d3ee 100644 --- a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt +++ b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt @@ -15,8 +15,14 @@ fun usage() { fun todoTask2(): Nothing = TODO( """ Task 2. - Implement the same logic as in 'task1' again through the library method 'joinToString()'. - Specify only two of the 'joinToString' arguments. + The logic defined in 'task1' is re-inventing the wheel. The Kotlin standard library + contains a method named 'joinToString() that can provide the exact same functionality, + if you provide the correct arguments. + + Replace the body of the function task2(collection) with code that will + return the result of collection.joinToString() with appropriate arguments + as to provide the same result as task1's JavaCode1.task1(collection). + Your solution should pass only two arguments to joinToString(). """, documentation = doc2(), references = { collection: Collection -> task1(collection); collection.joinToString() }) diff --git a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt index 11b9e179a..aad90e993 100644 --- a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt +++ b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt @@ -7,9 +7,13 @@ fun todoTask3(): Nothing = TODO( """ Task 3. Several overloads of 'JavaCode3.foo()' can be replaced with one function in Kotlin. - Change the declaration of the function 'foo' in a way that makes the code using 'foo' compile. - You have to add parameters and replace 'todoTask3()' with a real body. - Uncomment the commented code and make it compile. + + The task3() function contents must be modified to remove the line "return todoTask3()" + and un-comment the four lines starting with "return (foo("A") +". + + The goal of this task is to change the list of arguments for the function 'foo', provided + just below, and replace the function implementation such that the code in task3() will return + the String "a42b1C42D2". """, documentation = doc2(), references = { name: String -> JavaCode3().foo(name); foo(name) }) @@ -17,9 +21,9 @@ fun todoTask3(): Nothing = TODO( fun foo(name: String): String = todoTask3() fun task3(): String { - todoTask3() -// return (foo("a") + -// foo("b", number = 1) + -// foo("c", toUpperCase = true) + -// foo(name = "d", number = 2, toUpperCase = true)) + return todoTask3() + //return (foo("a") + + // foo("b", number = 1) + + // foo("c", toUpperCase = true) + + // foo(name = "d", number = 2, toUpperCase = true)) } diff --git a/src/i_introduction/_4_Lambdas/Lambdas.kt b/src/i_introduction/_4_Lambdas/Lambdas.kt index 8fa56135f..197445837 100644 --- a/src/i_introduction/_4_Lambdas/Lambdas.kt +++ b/src/i_introduction/_4_Lambdas/Lambdas.kt @@ -14,9 +14,12 @@ fun example() { fun todoTask4(collection: Collection): Nothing = TODO( """ Task 4. - Rewrite 'JavaCode4.task4()' in Kotlin using lambdas. - You can find the appropriate function to call on 'collection' through IntelliJ's code completion feature. - (Don't use the class 'Iterables'). + Replace the implementation of task4(collection) to provide the same logic as + found in JavaCode4.task4(collection). Your Kotlin solution should use + lambdas. + + You can find the appropriate function to call on 'collection' through + IntelliJ's code completion feature. (Don't use the class 'Iterables'). """, documentation = doc4(), references = { JavaCode4().task4(collection) }) diff --git a/src/i_introduction/_5_String_Templates/StringTemplates.kt b/src/i_introduction/_5_String_Templates/StringTemplates.kt index 13cd01234..a0daf36fb 100644 --- a/src/i_introduction/_5_String_Templates/StringTemplates.kt +++ b/src/i_introduction/_5_String_Templates/StringTemplates.kt @@ -28,9 +28,14 @@ val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" fun todoTask5(): Nothing = TODO( """ Task 5. - Copy the body of 'getPattern()' to the 'task5()' function - and rewrite it in such a way that it matches '13 JUN 1992'. - Use the 'month' variable. + The goal of this task is to replace the task5() function implementation + to return a regular expression string that can be used to match dates + formatted such as '13 JUN 1992' (excluding the quotes). + + The getPattern() function (above) is an example regular expression string + that nearly solves this task, but not quite. + + The solution of your task should use the provided 'month' variable (above). """, documentation = doc5(), references = { getPattern(); month }) From e2dc66b874ddf4664062a95b6c2f80281caf22e6 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Thu, 18 Feb 2016 12:05:55 -0600 Subject: [PATCH 02/12] minor typo --- src/i_introduction/_0_Hello_World/HelloWorld.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index aa4a41616..ecab78c36 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -7,8 +7,8 @@ fun todoTask0(): Nothing = TODO( """ Introduction: - The Kotlin Koans Workshop aims to teach you Kotlin by solving a writing code - to solve problems. For each task, there is an associated unit test that + The Kotlin Koans Workshop aims to teach you Kotlin by writing code + to solve tasks. For each task, there is an associated unit test that checks your solution. You can run all of the tests (check all of the work so far) by executing the "All tests" target. From 8bd52cc1a6b0485fee27361c1a6a3dfc4f1cba6e Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Thu, 18 Feb 2016 22:56:06 -0600 Subject: [PATCH 03/12] Text for exercise 13. --- src/ii_collections/_13_Introduction_.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ii_collections/_13_Introduction_.kt b/src/ii_collections/_13_Introduction_.kt index 651a84e3a..7968348f2 100644 --- a/src/ii_collections/_13_Introduction_.kt +++ b/src/ii_collections/_13_Introduction_.kt @@ -7,8 +7,17 @@ import java.util.* * https://github.com/goldmansachs/gs-collections-kata */ -/* - * There are many operations that help to transform one collection into another, starting with 'to' +/** + * Task 13. + * + * In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. + * + * The goal of this exercise is to replace the entire body (blow) of function Shop.getSetOfCustomers() + * to return a set of all customers of Shop. + * + * This function, Shop.getSetOfCustomers(), adds a new function getSetOfCustomers() to the existing Shop class. + * You can find the Shop.kt class in the same folder as this exercise (and all of the other + * section ii_collections exercises). */ fun example0(list: List) { list.toSet() From 708db2607a13804552d1df8d2dda668c749e65e3 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Fri, 19 Feb 2016 10:08:31 -0600 Subject: [PATCH 04/12] Improved KDoc and some structure, mostly for the section i exercises. Developing an general structure to follow. --- .../_0_Hello_World/HelloWorld.kt | 13 +++++- .../_11_SAM_Conversions/SAMConversions.kt | 3 +- .../ExtensionsOnCollections.kt | 3 +- .../JavaToKotlinConverter.kt | 11 ++++- .../_2_Named_Arguments/NamedArguments.kt | 13 +++++- .../DefaultAndNamedParams.kt | 20 ++++++++- src/i_introduction/_4_Lambdas/Lambdas.kt | 13 +++++- .../_5_String_Templates/StringTemplates.kt | 38 +++++++++++++++-- .../_6_Data_Classes/DataClasses.kt | 12 +++++- .../_7_Nullable_Types/NullableTypes.kt | 3 +- .../_8_Smart_Casts/SmartCasts.kt | 33 +++++++++++---- .../ExtensionFunctions.kt | 27 ++++++++++-- src/ii_collections/_13_Introduction_.kt | 42 +++++++++++++------ 13 files changed, 193 insertions(+), 38 deletions(-) diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index ecab78c36..8085d5a85 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -3,9 +3,15 @@ package i_introduction._0_Hello_World.Hello import util.TODO import util.doc0 +/** + * Description of task. + */ fun todoTask0(): Nothing = TODO( """ - Introduction: + + Task 0 + + Introduction The Kotlin Koans Workshop aims to teach you Kotlin by writing code to solve tasks. For each task, there is an associated unit test that @@ -44,6 +50,11 @@ fun todoTask0(): Nothing = TODO( references = { task0(); "OK" } ) +/** + * Replace the function body of this method as described in todoTask0(). + * + * @return the string literal "OK". + */ fun task0(): String { return todoTask0() } diff --git a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt index 03cff9ccf..dd3dec0e2 100644 --- a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt +++ b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt @@ -6,7 +6,8 @@ import java.util.* fun todoTask11(): Nothing = TODO( """ - Task 11. + Task 11 + When an object implements a SAM interface (one with a Single Abstract Method), you can pass a lambda instead. Read more about SAM conversions in the blog posts about Kotlin. diff --git a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt index 7bd11d3d9..7149897b1 100644 --- a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt +++ b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt @@ -5,7 +5,8 @@ import util.doc12 fun todoTask12(): Nothing = TODO( """ - Task 12. + Task 12 + In Kotlin standard library there are lots of extension functions that make the work with collections more convenient. Rewrite the previous example once more using an extension function 'sortedDescending'. diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt index b23363d10..7b888d30d 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt @@ -2,9 +2,13 @@ package i_introduction._1_Java_To_Kotlin_Converter import util.TODO +/** + * Description of task. + */ fun todoTask1(collection: Collection): Nothing = TODO( """ - Task 1. + Task 1 + The logic for this task exists in java class JavaCode1, which is in this same folder. The goal of this task is to replace the body of task1(collection) with the logic found in JavaCode1.task1(collection), but you need to re-write the logic in Kotlin. @@ -19,6 +23,11 @@ fun todoTask1(collection: Collection): Nothing = TODO( references = { JavaCode1().task1(collection) }) +/** + * Replace the function body of this method as described in todoTask1(). + * + * @return the string literal "OK". + */ fun task1(collection: Collection): String { todoTask1(collection) } diff --git a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt index 197d7d3ee..c895828da 100644 --- a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt +++ b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt @@ -12,9 +12,13 @@ fun usage() { bar(1, b = false) } +/** + * Description of task. + */ fun todoTask2(): Nothing = TODO( """ - Task 2. + Task 2 + The logic defined in 'task1' is re-inventing the wheel. The Kotlin standard library contains a method named 'joinToString() that can provide the exact same functionality, if you provide the correct arguments. @@ -27,7 +31,12 @@ fun todoTask2(): Nothing = TODO( documentation = doc2(), references = { collection: Collection -> task1(collection); collection.joinToString() }) +/** + * Replace the function body of this method as described in todoTask2(). + * + * @return the string literal "OK". + */ fun task2(collection: Collection): String { todoTask2() - return collection.joinToString() + //return collection.joinToString(/*some arguments*/) } diff --git a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt index aad90e993..ac5fee696 100644 --- a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt +++ b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt @@ -3,9 +3,13 @@ package i_introduction._3_Default_Arguments import util.TODO import util.doc2 +/** + * Description of task. + */ fun todoTask3(): Nothing = TODO( """ - Task 3. + Task 3 + Several overloads of 'JavaCode3.foo()' can be replaced with one function in Kotlin. The task3() function contents must be modified to remove the line "return todoTask3()" @@ -18,8 +22,20 @@ fun todoTask3(): Nothing = TODO( documentation = doc2(), references = { name: String -> JavaCode3().foo(name); foo(name) }) -fun foo(name: String): String = todoTask3() +/** + * Function to create string that contains a specified string, possibly as upper case (default is lowercase), + * followed by a specified (or default) number. If the number is not provided, the default number should be 42. + * + * Optionally, complete the KDoc, here. + * @return a string of (string, possibly upper case) with a number concatenated to it + */ +fun foo(name: String /* more arguments will go here*/): String = todoTask3() +/** + * Replace the function body of this method as described in todoTask3(). + * + * @return the string literal "OK". + */ fun task3(): String { return todoTask3() //return (foo("a") + diff --git a/src/i_introduction/_4_Lambdas/Lambdas.kt b/src/i_introduction/_4_Lambdas/Lambdas.kt index 197445837..bcd753086 100644 --- a/src/i_introduction/_4_Lambdas/Lambdas.kt +++ b/src/i_introduction/_4_Lambdas/Lambdas.kt @@ -4,16 +4,19 @@ import util.TODO import util.doc4 fun example() { - val sum = { x: Int, y: Int -> x + y } val square: (Int) -> Int = { x -> x * x } sum(1, square(2)) == 5 } +/** + * Description of task. + */ fun todoTask4(collection: Collection): Nothing = TODO( """ - Task 4. + Task 4 + Replace the implementation of task4(collection) to provide the same logic as found in JavaCode4.task4(collection). Your Kotlin solution should use lambdas. @@ -24,6 +27,12 @@ fun todoTask4(collection: Collection): Nothing = TODO( documentation = doc4(), references = { JavaCode4().task4(collection) }) +/** + * Replace the function body of this method as described in todoTask4(). + * + * @param input a collection of Int's + * @return true of any of the elements of collection are divisible by 42 + */ fun task4(collection: Collection): Boolean = todoTask4(collection) diff --git a/src/i_introduction/_5_String_Templates/StringTemplates.kt b/src/i_introduction/_5_String_Templates/StringTemplates.kt index a0daf36fb..74cb25cfd 100644 --- a/src/i_introduction/_5_String_Templates/StringTemplates.kt +++ b/src/i_introduction/_5_String_Templates/StringTemplates.kt @@ -3,14 +3,26 @@ package i_introduction._5_String_Templates import util.TODO import util.doc5 +/** + * Example function that demonstrates simple string interpolation. + */ fun example1(a: Any, b: Any) = "This is some text in which variables ($a, $b) appear." +/** + * Example function using normal Java string concatenation (not using interpolation). + */ fun example2(a: Any, b: Any) = "You can write it in a Java way as well. Like this: " + a + ", " + b + "!" +/** + * Example function demonstrates a string interpolation where the interpolation is a expression. + */ fun example3(c: Boolean, x: Int, y: Int) = "Any expression can be used: ${if (c) x else y}" +/** + * Example function using raw strings covering multiple lines with embedded string interpolation. + */ fun example4() = """ You can use raw strings to write multiline text. @@ -19,15 +31,24 @@ you don't need to escape a backslash by a backslash. String template entries (${42}) are allowed here. """ +/** + * Example function showing raw strings to create a regular expression string. + */ fun getPattern() = """\d{2}\.\d{2}\.\d{4}""" +/** + * Example function demonstrating the ability to use the getPattern() matcher to + * match a date in the getPattern() format. + */ fun example() = "13.06.1992".matches(getPattern().toRegex()) //true -val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" - +/** + * Description of task. + */ fun todoTask5(): Nothing = TODO( """ - Task 5. + Task 5 + The goal of this task is to replace the task5() function implementation to return a regular expression string that can be used to match dates formatted such as '13 JUN 1992' (excluding the quotes). @@ -40,4 +61,15 @@ fun todoTask5(): Nothing = TODO( documentation = doc5(), references = { getPattern(); month }) +/** + * A regular expression string is capable of matching the months in 3 letter uppercase. + * Use this variable in your task solution. + */ +val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" + +/** + * Replace the function body of this method as described in todoTask5(). + * + * @return A regular expression string to match dates formatted such as '13 JUN 1992' + */ fun task5(): String = todoTask5() diff --git a/src/i_introduction/_6_Data_Classes/DataClasses.kt b/src/i_introduction/_6_Data_Classes/DataClasses.kt index 53e90a108..db42b0e6f 100644 --- a/src/i_introduction/_6_Data_Classes/DataClasses.kt +++ b/src/i_introduction/_6_Data_Classes/DataClasses.kt @@ -5,6 +5,8 @@ import util.doc6 fun todoTask6(): Nothing = TODO( """ + Task 6 + Convert 'JavaCode6.Person' class to Kotlin. Then add an annotation `data` to the resulting class. This annotation means the compiler will generate a bunch of useful methods in this class: `equals`/`hashCode`, `toString` and some others. @@ -14,10 +16,18 @@ fun todoTask6(): Nothing = TODO( references = { JavaCode6.Person("Alice", 29) } ) +/** + * The class to create as described in todoTask6() + */ class Person +/** + * Replace the function body of this method as described in todoTask6(). + * + * @return a list of two Person objects, Alice and Bob. + */ fun task6(): List { todoTask6() - return listOf(/*Person("Alice", 29), Person("Bob", 31)*/) + //return listOf(/*Person("Alice", 29), Person("Bob", 31)*/) } diff --git a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt index 2bb3171b7..0296c292c 100644 --- a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt +++ b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt @@ -14,7 +14,8 @@ fun test() { fun todoTask7(client: Client?, message: String?, mailer: Mailer): Nothing = TODO( """ - Task 7. + Task 7 + Rewrite JavaCode7.sendMessageToClient in Kotlin, using only one 'if' expression. Declarations of Client, PersonalInfo and Mailer are given below. """, diff --git a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt index d61eccc2c..1053d034b 100644 --- a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt +++ b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt @@ -3,10 +3,36 @@ package i_introduction._8_Smart_Casts import util.TODO import util.doc8 +fun todoTask8(expr: Expr): Nothing = TODO( + """ + Task 8 + + Rewrite 'JavaCode8.eval()' in Kotlin using smart casts and 'when' expression. + """, + documentation = doc8(), + references = { JavaCode8().eval(expr) }) + +/** + * An interface for defining pieces of an expression. + */ interface Expr + +/** + * A number literal expression. + */ class Num(val value: Int) : Expr + +/** + * A sum expression, which represents the sum of the two specified Expr expression elements. + */ class Sum(val left: Expr, val right: Expr) : Expr +/** + * Replace the function body of this method as described in todoTask8(). + * + * @param e a Expr object (such as a Num or Sum object) + * @return the solution to the expression e + */ fun eval(e: Expr): Int = when (e) { is Num -> todoTask8(e) @@ -14,11 +40,4 @@ fun eval(e: Expr): Int = else -> throw IllegalArgumentException("Unknown expression") } -fun todoTask8(expr: Expr): Nothing = TODO( - """ - Task 8. - Rewrite 'JavaCode8.eval()' in Kotlin using smart casts and 'when' expression. - """, - documentation = doc8(), - references = { JavaCode8().eval(expr) }) diff --git a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt index 056170fb8..fb171b1ae 100644 --- a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt +++ b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt @@ -17,7 +17,8 @@ fun use() { fun todoTask9(): Nothing = TODO( """ - Task 9. + Task 9 + Implement the extension functions Int.r(), Pair.r() to support the following manner of creating rational numbers: 1.r(), Pair(1, 2).r() @@ -25,9 +26,29 @@ fun todoTask9(): Nothing = TODO( documentation = doc9(), references = { 1.r(); Pair(1, 2).r(); RationalNumber(1, 9) }) +/** + * A class defining a RationalNumber. Given that this class is a "data" class, + * .toString(), .equals(), and .hashcode() will be created automatically for this class. + */ data class RationalNumber(val numerator: Int, val denominator: Int) +/** + * Replace the function body of this method as described in todoTask9(). + * + * Extend the class Int to add a function r() which returns a RationalNumber + * with the numerator having the Int's value and a denominator being 1. + * + * @return the solution to the expression e + */ fun Int.r(): RationalNumber = todoTask9() -fun Pair.r(): RationalNumber = todoTask9() - +/** + * Replace the function body of this method as described in todoTask9(). + * + * Extend the class Pair of Int, int to add a function r() which returns a RationalNumber + * setting the numerator to the Pair's .first value and setting the denominator + * to the Pair's .second value. + * + * @return the solution to the expression e + */ +fun Pair.r(): RationalNumber = todoTask9() diff --git a/src/ii_collections/_13_Introduction_.kt b/src/ii_collections/_13_Introduction_.kt index 7968348f2..a0bb90f5f 100644 --- a/src/ii_collections/_13_Introduction_.kt +++ b/src/ii_collections/_13_Introduction_.kt @@ -8,16 +8,7 @@ import java.util.* */ /** - * Task 13. - * - * In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. - * - * The goal of this exercise is to replace the entire body (blow) of function Shop.getSetOfCustomers() - * to return a set of all customers of Shop. - * - * This function, Shop.getSetOfCustomers(), adds a new function getSetOfCustomers() to the existing Shop class. - * You can find the Shop.kt class in the same folder as this exercise (and all of the other - * section ii_collections exercises). + * Example function demonstrating conversion of a list to a set. */ fun example0(list: List) { list.toSet() @@ -26,9 +17,34 @@ fun example0(list: List) { list.to(set) } +/** + * Description of work. + */ +fun todoTask13(): Nothing = util.TODO( + """ + + Task 13 + + In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. + + The goal of this exercise is to replace the entire body (blow) of function Shop.getSetOfCustomers() + to return a set of all customers of Shop. + + This function, Shop.getSetOfCustomers(), adds a new function getSetOfCustomers() to the existing Shop class. + You can find the Shop.kt class in the same folder as this exercise (and all of the other + section ii_collections exercises). + """, + references = { shop: Shop -> shop.customers } +) + +/** + * Augment the Shop class, obtaining the set of customers. + * + * @return a set (not a list) containing all the customers of this shop + */ fun Shop.getSetOfCustomers(): Set { - // Return a set containing all the customers of this shop - todoCollectionTask() -// return this.customers + // + todoTask13() + //return this.customers } From 039cbbaccd3cd531030fe8363d27a53dc0bad63b Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Fri, 19 Feb 2016 10:21:54 -0600 Subject: [PATCH 05/12] Improved JavaDoc and layout of classes. --- .../_10_Object_Expressions/ObjectExpressions.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt index 88b138aec..4697cb518 100644 --- a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt +++ b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt @@ -6,7 +6,8 @@ import java.util.* fun todoTask10(): Nothing = TODO( """ - Task 10. + Task 10 + Read about object expressions that play the same role in Kotlin as anonymous classes do in Java. Add an object expression that provides a comparator to sort a list in a descending order using java.util.Collections class. @@ -16,6 +17,14 @@ fun todoTask10(): Nothing = TODO( documentation = doc10() ) +/** + * MODIFY the function body of this method as described in todoTask10(). + * The ideal solution to this will be to replace the text of "todoTask10()" in the function body + * with an inline object expression that creates a "Comparator of Int" object that overrides the + * compare(l: Int, r: Int) function to provide a descending sort. + * + * @return a List of Int where the list is a pre-specified list of Int's sorted in descending order. + */ fun task10(): List { val arrayList = arrayListOf(1, 5, 2) Collections.sort(arrayList, todoTask10()) From ec47960d90c25b6298d4c5971aefa88830a0ce40 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Tue, 10 May 2016 15:37:22 -0500 Subject: [PATCH 06/12] Revised test --- .gitignore | 5 +- build.gradle | 36 ++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53638 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 160 ++++++++++++++++++ gradlew.bat | 90 ++++++++++ kotlin-koans.iml | 6 +- .../_0_Hello_World/HelloWorld.kt | 9 +- .../ObjectExpressions.kt | 20 ++- .../_11_SAM_Conversions/SAMConversions.kt | 39 ++++- .../ExtensionsOnCollections.kt | 30 +++- .../JavaCode1.java | 4 + .../JavaToKotlinConverter.kt | 11 +- .../_2_Named_Arguments/NamedArguments.kt | 13 +- .../DefaultAndNamedParams.kt | 13 +- .../_3_Default_Arguments/JavaCode3.java | 4 + src/i_introduction/_4_Lambdas/JavaCode4.java | 4 + src/i_introduction/_4_Lambdas/Lambdas.kt | 8 +- .../_5_String_Templates/StringTemplates.kt | 8 +- .../_6_Data_Classes/DataClasses.kt | 30 ++-- .../_6_Data_Classes/JavaCode6.java | 4 + .../_7_Nullable_Types/JavaCode7.java | 4 + .../_7_Nullable_Types/NullableTypes.kt | 43 +++-- .../_8_Smart_Casts/JavaCode8.java | 4 + .../_8_Smart_Casts/SmartCasts.kt | 13 +- .../ExtensionFunctions.kt | 20 ++- .../_9_Extension_Functions/JavaCode9.java | 4 + .../{_24_JavaCode.java => JavaCode24.java} | 6 +- src/ii_collections/_13_Introduction_.kt | 5 +- src/ii_collections/_14_FilterMap.kt | 21 ++- .../_15_AllAnyAndOtherPredicates.kt | 30 ++-- src/ii_collections/_16_FlatMap.kt | 20 ++- src/ii_collections/_17_MaxMin.kt | 18 +- src/ii_collections/_18_Sort_.kt | 17 +- src/ii_collections/_19_Sum_.kt | 16 +- src/ii_collections/_20_GroupBy.kt | 17 +- src/ii_collections/_21_Partition_.kt | 16 +- src/ii_collections/_22_Fold_.kt | 19 ++- src/ii_collections/_23_CompoundTasks.kt | 20 ++- .../_24_ExtensionsOnCollections.kt | 14 +- src/ii_collections/shop.kt | 4 + src/ii_collections/todoUtil.kt | 13 -- src/iii_conventions/MyDate.kt | 5 + src/iii_conventions/MyDateUtil.kt | 5 + 44 files changed, 691 insertions(+), 143 deletions(-) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat rename src/ii_collections/{_24_JavaCode.java => JavaCode24.java} (90%) delete mode 100644 src/ii_collections/todoUtil.kt diff --git a/.gitignore b/.gitignore index 9b5695c52..c9ee75a04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -out -workspace.xml \ No newline at end of file +out/ +build/ +workspace.xml diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..f12c48c7c --- /dev/null +++ b/build.gradle @@ -0,0 +1,36 @@ +group 'org.kotlinlang' +version '1.0-SNAPSHOT' + +buildscript { + // So we can use $kotlin_version in specifying dependencies. + ext.kotlin_version = '1.0.0' + + repositories { + mavenCentral() + } + + dependencies { + // Kotlin support for Gradle + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + // Kotlin library and reflection stuff + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + // Kotlin testing + testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" +} + +// For ease of startup +task wrapper(type: Wrapper) { + gradleVersion = '2.11' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5ccda13e9cb94678ba179b32452cf3d60dc36353 GIT binary patch literal 53638 zcmafaW0a=B^559DjdyI@wr$%scWm3Xy<^+Pj_sKpY&N+!|K#4>Bz;ajPk*RBjZ;RV75EK*;j-;d{(BB5~-#>pF^k0$_Qx&35mhPeng zP5V`%P1S)(UiPcRczm!G=Nud7!TH`9_!bdReTmO0lO(Zfn zfMqP~+s2VTE#?sl=$9e(CuBOAH2n}=c4idsipAKKQIO=pSsMiScd0TvKs^d1xMCym`IZxg&Xd3ii(^3$M#K)myV2qM z{o5&z?1rtP{gnX6zegV2 z$03xe*g2pGA^BqfBE}XDD-CN?H&?w?kE0wG3~``ie?T+IRmsT;*cpLZ)OnaXBtFzX zMcc97L%tQqGz+E@0i)gy&0g_7PV@3~zaE{g-2zQ|T9d>EL&JiD+E8t?H^#-Hv8!mV z-r%F^bl8v}j19B}`M^zJ>hD+Tg?Fg%no2GYmKkbR`2=|`@o`~1&VbKUJ@ZSojd|ihP|{9&8F<6 zcAvtwl6yo{Js7UQ{l~MjA7Rz8(sYEcRXX(n*(Mp75OBEijo(25zwC)pc=#%f_xV93 z`&d+DI*TAtd3KBd(^964h=X;uks5gf3MU}cR#X_wnk6_crcAVLsTV2SzsGM$h~aq~ z6oX|v?jSq%txd-SHwHy`7x4*jJqH^;0*1`Sztp*aYi4tRMBD|Ryyl%F{IC{(=Y{Y5 zhSQPvqZN~4uvCi*h``E|llqjfc4rnRQPs3@(MnB9jto`dtz!CB-ojaReyN}7BwMp! zU@jEkH2hS%NUB|wE0d;=hS4^M^dTz`r=^`7LNsQkU26nlt4o?Ki13cwDXkQH+)w#uNVQo2o@pEJOAZV3Uf z8WWqpN|lDuGdkokHkKLwmo@qCdV6}M=~DGq+P3}@$$yqQssE{3|BxxM*q?tD3oiW6 z^!W)Iau1CDv+;dTH4Lbb;*)+mGrKg;g)4tHB;h~=3QsCF)I|E{`=jp;ArQuy&zUzA zlz$NoIhz7h@;Sw+#%u~;!w56XV3JkGLOHaVlvs1eSSck_-2#zs%EynXvEnsUsO3{@ z=2B!(Gdra;oKm@A@~#LeoDFC2&V->;dgCP}x`Qm{yZA&ULeNnWvNIGzcgjx2?Rx#m z_I4lu^j~)hR_VQ?`&Yk|{^}Rqf8MFY|1el;E@sY>4t8d;4h}YMj{n$ntcs2Tju6_n zc%t6wvvLifwar=wOlL#;T5V}~s_KU-6cMz7X&7`JeYdHW?WaaBnYH!e82^(58{d#J z&3H)nMCXi0pUcVg^sRt^KZxdFRj|_ZglEw{Ri0EN6_laAxbE8zB=H8KgU;Xtpk5?z zC2?g-xj`9d8MtJf-!H#~s0}tJ>Ksa+7KP;J(%hHwUBewO);ZZ&ry8oYXI%5+YQgNQeyS*ViKL>Yy2`MsK? zB7Y$Zk@YAy#-Kwyo5KSK$lcvER(OV>qrW1VXPo7Ih%dEJZ<|5sEmeC)do0(dJ;7Fc z#v{T#df-92-StcUzRO7OjZ?g-Ik?9eGEDWsUL(f2jUmS9_ajH?wV&{0Aj)-0IP36} z4!4}CW2D{v(ZmPjB$#&;fps(Hvph<>^IORq|0^=eDhYiA%W5!rM_K_y(bsu@*)m3P++I=?)h!HA@uUc{zxJ0ibvxU%Ke8OQ+KDRndS#XDA4T zto-I$zC-%q0v8ZL`!Z;MMK0`Irsn?gZwiTbsJAr&4g~c3FEn8J&tfX(X=3ZOyEhpng#DDSOc6XLr%uGtB2|0=_Au$%heH3&*ID*ZPhs8iJyw{$c z)`ySqPVndS_Rnv2f$xtMcp${1WTLjhW)S3(;l*PK4#cXz09;@vNj*6?$Q>%5jIboV2fgAyb?c(W#K{@rj#6OKF&J#QQ9 zboB4HeJ?hXrHp)H9rx@Rta#*PdvkHJ<<2Asc#ClKA;st5qadT0NZHEA111(&qsaLb zkeTH_h(yr92XkyqqslQgTo(|RN$hhQ*IL7<12W?$+q6R2jtnWadKrIyeA>bj=;9mM zaPK&0{1#djnc2d@4fR{7K872i*IvH0mK#eqf4=iu8F5=2P#fG-GBZ|`J2MyJ(^^*5 z$tQaAS;Y(2k!j10=adaH9^!>^k+iBMVWD9#+F=&Q(yZ5NVJ>A}t>1R@32TZ0JTMas z%42sos08y0NMkb$BsDnQo8nVhd#ksaq8UyBjAO1FHRfW*u`ojc^y3)=(f&PTM`k@F zMoZFS>HCeNX1c@G{<=x`IQr>{11kPK#7AZWy_q&GQYwk*t|uTE9H*TVi|@g8P95wBlwf};`RANPqd z@rf3B=~Q8%Wgl5i2t$W?Ns1WgZ1t0sFVJF73Rc!d@X---3W@e+Dbvpj;l~8r`F9Sc zKd8G>dR2>61(|Bw&XdTlG}}fnu~6{2xsz6Efmc;nRDupK!KI-q=^*h{`b$W=VCBWe|mhK3YN$PO28ZaH@2V;Nbgpwl8Fig6xxkWN7UhWjM%G z<^O`4VX45jgsIIO-R$7F$`uCa6O>(WOZ>i>Gor3X^yySAwSB;0*X`pWy<(jya4!HO zYDvrso1n6V3G<>TnrSv{+unA;cSAWuH!9k`z#^j306@cy?0{jP-p4NUCSRP<_wNHG6^axCR zMECIg`Vz^ja7F}a@eRp)X%2>D5;2HR8kL?^&npLgqU9<#pY)7;V<(`jjbZL5j>8!3 zLF`9aB2GDCO#Q|6P6_x!My0QO@k9M-2f2-|AF33CZ))eL<;OOi;76DiE|3}S)dWs znAG6VC4EKe9MedTp6d0%J409Iu^T12e)n(N?^s^DlM+cUS5h4SEqq4NjK+%c6KPr%EUeiN1v_&WkfIr9Do+Q{2=Ap?{FS|6D&*Au8=PW}~Rt~+3EJpNK(;7R!k z%&9kpQ;0c6#VF?+W0D~mUp&bc96HK5g~~9ch$%)z5Z@Wq`!V zLK|>+`YZuZ?H_46y1zmqL~@TLC0lsiaSZzy-7!W4m2VsY*S@u zW}zeWzr7N1oL)qzKa8J;I?@RxaLmPEowX2BlSD2hnYW}WyvQ$FK%;PTPl4|ftNkVB-&5MvFWP;Yx2`zBS8o_7QfB#MLS!;fRlOU$t z%iCYD|0>%`e)v|$e?d9+U&O=spApZ$`@51x_J1P_|AafS>X$C4YG{A#vQ3BFr-~J& zrNf{=tbRuZqT9ky&r6pgk}1*#cgZ;@Dve26=%&Eu887bdn12Lfj~ET zV5IjzrKV8JQNK?MSC0X660d`nnw55z4~eTJnzix7U0S>nhTZ)+f}uOuozz3gz(ZMj zYs1J$Dy2L`8>*UTBLriGUKTY410qsp)+K??D$!Mr_7=C*Ec`Q^X$b{2>?+1_7Ka7f zd2{rtBr%g2PbF_kHb0yF5ypGWmJ(ftYt&YJ_ynIc9aWyTF~dXbrN!iNKZ_(_u>s(I z2*X|;szamJz{2dl(P2I;%foJLF51)WO50&h!EXP{5|gzy^b(4+nn<;j$NCt?UP_cL zEU1-XN`f*1Gl}Fht4&Pn5xo)Ma$kZyBt2qju1yuSTysFNG^pi%vvU=hqPhQ-Cpl`! zSt+cpY2TVYkvTflcqwieu*7MHVIVSQSivl850|@O!h+yWxUM;@nYhP*y8k$MMX>^Vzs= zqdctvR$e(}|H4~b=2=Dszgy^xqjuY6alG6sJ&J~#*>K6$Dl|Pf&Y~sh$@07*amh*T z_wIHl$BbJK)7B2V0P=;_iRjD@X9TGO4h+mK7laU=qy zmy|9(xNb7=49$9mLHL1jK#9O+t+CJ;3IDtkK4I0H_{C zMZMuxRK)|E4155y$!RGIcAVHwpQK;2>ssTa8^Y0_8A*sk}a3gZD5Il8pD}4GR1;_@6*%Y^za=h*V zu5|)zJI%1zR^G!TWc5ioA9xPwtfcqbul(N(r%J#%9D&&zkuV4xj0VXfymNc> zXa2;S1FBKAgNu0n(1&iyT^XFajIG9aRR$`eVEejDBr(KR%NpK5M4^XoP2r^{5!QII z=({hFfVnU7w+W~i2BF-t(|u6~1LVuM`kfH|v*hfA+X=o<%CrInhQid%%Pn@a#zcB{ z^o7+|r0o0_JFn1}AG;)N{NfS8LI&fnSX*e@KcPEe6OV6U-=oiHjXw;D&;Ui32rr=^ zeI$9fsv_3NJU2zXlqE0HwYjRkF^(*=dmpb#bAGI-iGF7mAYYyfIwG8Z;|+==LK(t5 z=LlsTsJKX@6c-VZ+w{_}$Z-T9i*wIN-d5;hIXRENbV;h#&hIG%+U%XScSeP_M`St_ zW*|AQg18~yjh5x7_;)cRFt3fbcf={_ULTh(DfkEq%mVu$EcpgdN}OOAmZBAc2`mzY-_S)k2M=`*UssiSqyy?xVKo=MbGuFS6XRVx2Djkn-AEcIgSQvVAF zfc`p)g#X!O$~sv5mqQqq{Nb>uh&I-rK1N;7H0mguftm{=rV;MIL=kQZjZ6q_PVrHj zl3gcbRfrb*Cn_KeXJnZ06ZEq<=ClnSMYA~}FVE$cEB}!?;QRYG{63OGvgE!wNV+3U z&{hS7QV6Z6UL=q3cB&(hP~yB{WPCY<2lhJj&?d_e^Y$rRxGZ6c1-iXZdJo;c_d!-U7axq4RNZah&+JQde7@OZf;An_X_9 z;&omnq3Vb#o!)t8oY6U5q9V@2gy>2y91VhMexMd;_^7e(hpL;mFYj^Ms7`BGG-h5q zK%Q)s!X(^S6HkOCW#c?l`3P0RZTN{hKdkGb8X4YR z`Q>!^OJIGZmB9N^htzi$VRW^@G$NKIzjI7BcO^B;MhbLW^3e|uMwSeIP|Zs8x`x5{ zeL>F`oNu5Vl>~_AJi)C>nNM&Fhx!XDe?wEG`x8B+)*W-m%b^U0@g03H+h=q%dPTeRj5TVw;2U@yvqaijNc-yOdE0sdE!oOQm8LWcwb zvPS>{qWI4usc7VCDdgf}W@r3gcXz7#y9ura^7ra0x>qu1l*@W+h%sd>?FNRF3P1|= zZbh`V{x`M!W`~UpsRQ+GS9kSrxHBr?)ej6Lo3uG_H zt<~-{2g_b|`=9T*FAm_G(f&ius6m395oJ6G`(dhHA`zwVV}R^Nn`tA;NVLNfW^Hs9AWg~k&`b-e$0W2lP^ww;)sP<7yihp z>9;T5*j*ExLF8eyk}p*_!`MPt{qUWd(sve|0cQ3d(s?$MugrLsb=F0U=Z>3QNKb~xQiuR|wEjrfU z+ zcuJ(5)AJajIhR#qf<4<5IRe+i9ySGsxMdU+)OPV`G3%LV?Y&rh6sS?3B$pjiq)Tp> z7}ce0mnh`&H|iOQ1Gb0uRJX^%qxxmUZ=$BivYy9aXQU8){*Y>2BZ9l-I9bd_Cb^=f%{ zmiWjT=K4anJYD<1{Uv&xtE!H~-PThqR_6Xk6JGHrBqqGQKH?tN;Y{bu{vvRtU8UsW z(llN(rJar!7lEQU6Og+kl{x(`fYfSOcD-A(iW^ymja51?n_e*XdzPU6$KMxkZcJK` zJda&tRCKm42Sb&*e*F*DU{{L^KV#|D}q2fz@SkOgh!`P8>llzU)uQmjI>w+ojmYH8DUYso9od6~Q z;4`O#*N}<@^x4|bmeg+8V z#FzePXp9j-$0LxzgYFM1c8Sfb>{Ps6=4+{2OSxf{aUT^)eNk{Itv=A6UH-NWAk zbZSac^MG}0zqXgc*4EY)bsLAv>)zhim))%o>em2scaCgRq(g@OFY^hHi1@3ZH?0RyOJwn?nXpt|EK zC7C$;BzYJGJRO=@=P;0~`pP4Xo506(sb4GFpOQ5bebMSZAyb9jZC0r^8@4#6@ zN1(csX24ZP&hIq|Wy)DqPP#K}FbsE(!eN*bF(bfPb223XmHFutE}fA+X)(F@1;%yH zk1$?!wEHfeYJqddk074aDxPkEX(5=S7o}DSWlDe=WSG+rui7V8D&L^N{0Eoj{52^F znZVhj=S5qnyRaUmgH@o~<9q*IvbmW>Rd7#O2emhnur1TYHlW(mYhdexX!Kp=0`-Hf z!SuIx<@XNREb6dlpjM~u!9pOrQ+I)gFWa|Hkzd!e`}FK$)l*+RDtXAr6jhG2g7^(& zS#O*-Cp0BefE>7adHa;nq#sma9#q??1@@V^r}|p+lu`QF`_*sO?>*YP{B-=C+6Oin z!SJy*7wjAkT`h%`ZcC$OE7=@uwGN%GB0FWRvSBmnm)%cUkp!1DR;?)JH7?*wZ@)ch ztfdr-ApH==lQ4l~Yl$%t!PHH1zsCEliydl^cX*{BK4Bwl&; zJx=3~4w4j}SylJRk^&{HD^znI={^oh5?NHd2d1H_jh;qml_=-(7Web$GL)idHHN(A!9a;z33kdFv;y;z&UxF)%AeR4wcX*@Dd`TbAx)+%j<6 z7<;WW)i;(q6bO+@sG3~TKii83s*~Hn6H8`K%#A+`Q1P~mlINmHgw45@lEne3jJ17P zZh;hz8;0&UQZnl9P!Lf~yjjLORIsk_d{Ii_YQEQ%aR@^GyDuwHeJ}ihLhY_)AdGHg zhFSd5RVy#l^BEgfnr@AE7^Ft-Psf|qCcFrebXbLZme1nEm7HrU@Z!uVjNSN%-b3af z7qZUkhP=v4wwvDaxCFp{JDGra@o7D-oGogt?x{pR{FrAwaZ3wj-{uzAmKMqt2&XDh ze1?H=?{VpKPF0hrr{{u7|~@}3h?pBn#AQcs3}tn(ld_+ zqfibU?{%p}DH=QlEiIzWnzdn6rJbfL=v^pfoHN&UQod6xHxUy033s>27|xW2!iAIt zRMTOs@W ztWJX=uuAhKLfXd7Xin6FH>PB>VwRXl4F(L&>kRYIq}#{TDkrvZbm;tKBkE*r5OWJg ze$J|ti%j?`N}(DXhW1uC21DuyX5QdoV|XVRKtk$BO(Z z!r{tOk9B6*;^R}Wbbq0T8n^5h^;eF6;UW=V@uJ%kc4}Rrjhc5Tf!euvA3@BRs^etQ zTvbc9z2dp|@0!7GwZ0$+)pv^B;=9vdN9L&x2Rdrsyn9jC@59nFpfyrwenQ6#59tMB zbxBZXtF3No6)I@oO_M(F>|pLUp*fD=$lgK+TWWnF{Y4KN@A9dy_j=>>H)bJ&9@U?0 z-(8chcmAoj#}1M%`IE2FD2NrGL|<^YKj#l)D>KZ*V{He`yRLk7DCyeO=TFkfPHhf4 z5|@pgWJH9l&&_3*O#Cv^2K;f;@ziVC$~X*WqHORbe`PI1`*AOp(@oD_R*Ppc4F~vr z9G|JbXZs4my(CQT9e&C3$OA;PJWyy4wV#ru3Lb_&?NV=uGtP(tB#tEv(Qle)=vYk4 z_2_mBJ16D~hlCE6WQ@rh*$0;0&BE>^ZBhShUv^)=m zh*`f{oNA2|<*ho;N|huZ(U*r;ql>cY94QNLs#2$sN*iX*R7#Fg49~f#84)>us9SYM z8@F_Gk7c(OJeB~Djj!~LB-D6=PR&OfBRuiHS{2D`rca#zAoHu@6%XY^snOZ`Pe{_!OQ!llj z(vX$_-^MCRyV?lYDAQT~lA!9eCC;-*J^9Q>{{U0rLXqySl({*)}PenaIaXeRr|kqvv>R|%fO}InxZ~VOfnRIr--(j~C;1hh zD*m7*eDdTK57}_}{UZVr*caO06;H%}gb&))zDo;^G(P1=7mFuZQv=`$A05DWSR}Yj zz8ZepdD~0R;-d)k(Aaw^lnN|J!2D4^-n)OM?w|7@Y0vZ25q!@+PfU=QX}7$BJdUxj zdp&-GJ8nv;ER~ac$O(wj00ZDTP2$N1C|hle^8}xhQXJzom#1|R)U+mBjk=y|-iY!q z#0nokHC_*isP`5cvj%!?Ooi>_cRXO*x2mYm_L4oGpC8HQUTzGFv%d@~!e9%SuPlpj z{+Ksf!<#*U0k`m-)`OUxR@_@EbDXB26M2=)J>8l$jiL)8FEthy`J}zQnE^>+4bdYbk){#$Ae;MY{p}>NLh`~ z%kMR=#_oMtE+mw(R^lc%8j^q`_5VQoF zdsg1*Je;fRn1)0{M!eq2XZ@bD?`m0tNP`8$^5dk~b~$&7lVu};YMm&UAe?26^F0+) zQ#M%s*_)O?gbLFsB}@In8QSnvrhwl_hj}eQ#Dg$d!fkT2Bpahm|HITp`fg&o5lq3; zIhE8y*U&7&SRM}j)U^6QBW7u5?zLzorLgcxF7fF%IbQblWkITm@JWRX`9%5#5AFm3 z?qVtRQf9en5;}Gc7cg0+4t*tiyZN8fM3#oZS>Ty_0y23t91&jU(~bv4MIo%-OCEV7 zdAXNo%|)mFOFE?n)-|vp2eT+$NF-%FZ)ZJTBUbSk05b~^3%&W1_W4{Qt~t9odd5QM z`Gc4TcYEVcVve}3n#zgRpAFF!s0U_MxOA0)q)trM{)o)oLggxismMzF;OEj^Q5kik z8U%h>Fvp&;ufkk5TM9Wz*OYP7p7KApanC7QeD2)v0iO3c8vKIb$02BzG$mvFyE6cnmvjQ_D+w=-K2%xW?Oqod6 zB7Pl(^a*8csuMhcKhn@tK~z-am+?uA#K$Hy!*QpDIFp;!7GbNxPhhS)*SU3=9qWo2 zlrRVt`EqCX$@&P93(B0audju)9=r1FK+l*9&iT1wn$MJGx^nFLKC#uj7$2`SLY%5e z5ZVhH0FiQ_$qNYobPI9S))@hsvAm~!l+P2Ld$W0r?(zDtGFM~l&o`%k#PGW1`c9uT z>??a$mVcqFF?ro?X7rTop_TB;- z-W57af`0;N3i0G!+UI{3p4KevSam`(@cxay(;!sWX!1I>p<3VQ>!lK81nJ%b+ zaC{l?Bb+#xHQjN8jlax_iYvts#}cHKzm8Gms}{A*qiP!gaY6SXmN-qK^hnk2{Y7k zpFB~8I{Ao7f!d|!hCoNV8t=r5DH_O%vQ-`O3Ij(>ItU^Rdpg~c^&*XyHm`-oj#j$< zs@56Irzq;y_xL50pG(TO$lB~^%`SH3y71v^^PHT<)Fe!in!K<@`4L#QKo1UWCIb=t zsSrf|{N{PF6+M&c%}mwiq0W$O579{QuZikvDg70zm`~43QGO-r5Pk{^lf0W@cj{ei)J8*ALTs_Cpj#}WC zi~dg`S)L3Gmwe@?Qcu2X?ANb%K6(0%i&Xuf^x$WIkRrJ)2)z*Dm7R3N4yu5;J@DJ% z7QO8HEGp|}R620#Xlf#k_WQ$EfvsI1ws3xNbliM)pTUuFnkB3bYRZLSJ}s3GFL*ww zxCqqp2--kYmf9t(woiP*gSs97*`>%XM1f~pqciQDv}yl|R>f0=Y3Ec#{H^n0b*cUf zT?z`b6|cYwVav`F|FlPc8)QB8n0b#WsS^%EKR8`vz8C8L6UjM}H(wCJX}}#Cy?6)l zat}6oCqnMj-)GcV)Q|4apKY~;bkGOsbd9*rK}hyiVwg7);0g8OkJUmj^nu#&Wb_FS z5;q1k>;crXcO>Jx1@HJmgwwCUFnFk!9`{KFcT?=lYbe}3PuIMZW7h5sWj$Zr^BlSB z%4zYVvbVE{aarM@2NJO7Gw;HP>m9}(Upz3!lm*cu2Wc%DVucXK>X1q?uj8(HJQ3!V zH5Zbg+|b!G7#Cs98{?Lz)=b?9ouh9*RPDS7)fF4rXSx;gGFo9;pdPe&uUvoVIw}%D(mbE03&j@<0#ahS#Y}6C zzg;$sg8j!*_paDJnNWjA0=H@ZrE9_p`T0i5lkMTLb2za(w`2TOvGGEQGJny0X}+=G zIc^9V2J;y%k<5M`x8M=}etGa@Bj}g|W?6U1l0!fE*BF9O zF}^F8q0pPw3rnD`Cu$zYPicTy1ByF1g1U6(!G#XLV)}q zAn5=>`!7;UzfkT=LZNd~RiuDfibAh->%HQ=!8V?TnQw$@@{!w_O(zDwgk8>;;Hm+8 zl#60Sp%qZH%iosK?uTL>B>OK|n=X$T9`1z&gVyfS11B_L)WxTSP-; z**~VIOErBGO|wcRK?Z##|U}R@;6~V7UR}1!CHv0ERDFhD zS$yjwLc7+Zqang$sdZ(wtE9>m%h$g6=5H?YkXxGFY|&C@Bekzio2iik-la8wIYYD? zd07T!tEv{~#%ZUUkyPkHnTeUT;?8!xt5DA(X<25(AnGPQoq;pz{QFozFr->fl}l7KL=wwU0c$$0u6|Tn_0ut2k%t6$hP)9U8FA)$YI>KADk7I00%L1I zYcZ~iyUCV8VSB-!$*VTizC66F^K@f~Y^$AZX_?R=SDnRP}HCj$}I|n5d4*6S5u5RX2efSh`aD zC&D6%JG$k1@||c5Ek6vkOqA2j_uxgC>~=dUOb!t z)k|iO1Ez1pL-pzrsG4hNmHDd15X9VKIwubXrwo6&@`@dHvzzuz84AM>0`s;8f~^=J zZ-p?3-Hb z(WDr+^=6inA%7u{Z#8R-&)(D^n&^RbVqjksyK+cLGE`Td?i{WWKzBukjHI{d0YW$L zyP0A~m8zHpd!;NwkYLNY^aUAtn~9FOib#_*o((EbtnS?Eao=%w;f3qjtYqG!g$ZI3`1M80Xw1433 zYDuT%h~5S7xgT!7BmQdwUvjmbZ0T*t0K%+9$P9QPGmyEzXEd?WB=Pso;#S*e+%jtxnvqYOP;|Zfp=h!2kf%MR+7= zqGGk}Lgx^XfkhZ2xVInt_k*Agcds+D?9E$t@BvrZuf4Cmw{L!9J|Dka5Cvcidr=;a z==`^l2XsOJKXd)J(M7QlAPV>GwK~V*+rb^{2^|m*@jWe&&^s6+rSYDQ^n_H848ghf z=!SeuImw26j-NhEJ^LGci2@NPUqzZ8j57Vm05goac}pbcX)}pAEqx_0{oc6 z8=P1J$q!$?Kn76z(ZIe`fees$sX%?yQws0*twdd+*Ow1p;cyCShuLpwlJ9MVd(cIg zd+6kQu!kRSK^ZSYME;?KkoTyctbzGd6?==g_}DksIQZxIc{ObIb|s5xX-)2y6ESs$W_f{ivzGA^!rv!~r{nB%Cs92! zXKk}PxU;-A98m+b+z6F5D3dnrRIuE_ACKCfjd-^`g|YBr=)6-s`VJpTJ3e+`EkKEw^^iEn=}HPap7lN6VM~4Fn$X zutC%X2g+Hcm1c95+mf}-GN=fN`3h;OEzTc`$%qs~2*Wfado zPzd<{tWY>h!$b`Jnc73EI=}cSth`#km4MnUlg+@_MUIxdOhZSKN!zt%hJ|sxRQADk zFUbNBn_vbW%%oMMYqxL88&xk7gJTD!|rdWQ_?n zcG*h4WcftfU7B*^ zyY;4$6>n_*L)?0fo}9@|C@S7{4d^Xh>|vOp5wG(Zj!RjtDxCHSve@dHdvl9c7BjTt zIC$wdXJZa(ys>*f2KSn+F}3)p-H*(uM;l~DN0mvWTB$P3 z9XjJ@R28*M3U$A7Ej1-T2Hh0_%c#3^9?yo^x6^+>DLZe~#MnLu0&OP8>q;|((mR>R zJ5cfFwZus71_JM?CeQoIW7#+QSWIP;)}&MK$|`$1RpSAHhAU^P*K_W9S6MzDu-kVT zwS%Q3g^RFVH76n$0&tc#C0D&$Z+A zV)?!i&p;#EX1K%24(pd_RiUjSdzbcw@h>av$Iixg@X}!=hrvzbITP@5uTpmfYx5M$ zw!ElwFZ1IsSS{^@hZh-XZWfVM-KSj3Q?*2R5HuMal(y6{efA5~ z1T&LnfJ0_jz-Suospc$;Ad73` zU*~dB@=2(oS@R*|EEY{oBTPB27Hv@+i<{SaOLxumq#~@R6R&mBJr8g+X={q1hNdUC zK#4cHD0yiVNLDS5+pk?E@ zs=i@E$z?;yV`F6OE{##0JED?1BA)FO+uXLQWz)2lg;;1*JQq%SIIuwCxcp(h@UZ^n zY65>V$ftP<()hOq-99k>a)lGWl`}+10G0B9wk{_GYL>hexp!TZlYMB|s{{B{MVzu! zH_*q4xaq77m^Wm8JNmc-E?tE{NEk(hJ*AnwB8o2=j=IqT#DVJ67uz|dV$@6i1qWKw zhEU1Y2jtoPgf}pYcC&NwC*0&=OJ-V_VYscGPURO#MT;vN{XJOALTgdXJ6|k~Wti0D zm_ycwy8NrV4?NPkNa3@VEoX443umrYw?Br(^x-tNwwt;U0=&|(d)ZlJt+JbF2X${0 zGnd#L^TzQnc*+lx=$3y}1#1QCo}v;{Hbov+$L zIy3nf)T*-#h8F%Tu{n9=)>ZZ12n>H{ z(3K&i=jM(hT8Ctl@Z;WeeO)<+kv*udON*kb5&nl;GdIaQ@E2TNIO#j zqsR@_F$d5C4A3F=bZla?E@R9+-RvJahY`JR_;wm_TQV@7nv_1ub8HLY{L)Q zX?Jd&ASuon4BbfjVo&Hk?tG-m#&h~1YY(;w=6jNgzFa_L|BbP?ii^9;wzUba!QCOa zyF+ky*Wm8%5TtN-cXxMpcc*Z73l<0;Z+Gu=y5GIe_uChBR~IE~t^b%~jrq(!ZybBv zwQF=k-wQqN&xDpW!dg-ze)0}Ndum9c=kfnMg6BR7Wx#%)c>UctYj zI2usyXd~!5>?j;+R ztcEQ})Fp88$?c(Ka#E~?%U+;S$hmo5Pn~x0yqLMUa0BUtesm*FU3PlBcX?bR$n)Rd z6o4#8L{T^I(xcH;(AOy3NTEZZ==iHsaqf>)yt_wjcBu`W+qV!tJ>Pml*eNKI z*NxwZdAQevbL>vRZsq6Cf9X*j`r0Xb=iQ;RMV#VR-immwbYlx~eY@`q<5?4a=$;es zRG|@!SgHYNfF)2+ByHOaL8N|;B}6PQ9STt`WFqb%KfH_8A$T+zkBKS0*+*{kQ|aSE zCmb@OCKwq}@y5$hxRvD1+fjz)uQFuGR=LIYUXGcOOrQCE3jy&XG3Q5oi2T?9gS6PE zSVkW^sqpod?OQ3La~nIv_1&cR>p2~1QSrvzR=m*_=%xtkso8^i&eQQN+#7ig3(wgz zgY~V>N9;i=UH8as>Z;hc_p=-MInd$R&hz!@;{5z#jRxt2yEtcdfQgSE<*Er~?s*jB zXFNMgcH=`UTkePw;5%hZXIDW@Q$s}o-#|&f=-T%7+FZ&{`V5FEQtC~dDDf&AP`L9m zC~$9^BgUd67t#IUt;JPj(zz1CFl;4Bmi!UO&x_(K-P{?RC~}Bp*R-&>8wnx8Xzbkt z7$5IVi6FQ24MDS&D)D{RagO01KgnD|?qr*b&cl%JDsDJ26m+LMeg%Cm3q)oc;K^qS zEGgQDNeEF}l#mMbk7tyv;FH?yci@X-eXiryo{^x%v1J(biDGOz?kqhSM%{&MD zndH(?z~n^%R>5tE7o~oXyK+427*`?N9C_Ey%POchfv>+=+y4&5?Ik}#_pTylu=i{v zqy3U4iH)h+K9A0#|1F+f?65I1v%2HdqAsWG-Q+euLw=j1&}Gw<{nbQ#C)@@tgk>w_iX~Pb z9ggj=EKc+;xmp37?l**~E?XzxBBq@n zX%vahMtu8cBAEVr71W(-rG;b!E_RNdbW0Sv5UqZ>NeR@H3uT7cc)oFm0y4iWTBwjL zWn}4%GMi%1hK5+RK`Sa7=+N3XA@remIit2z`6zXfFmfr;G?H<)Itg$z#k?@7It^rL zRzeg1}G&58g5hLuSGb5qO?ncqDnuu71PVYObpw4o7wpYII3oA1>GW< zkcHw>vt^n91MdgI}HISUKD#lo8X7i21k*=5)OK@xmFYr4YvPm1^qRo|t zx#WkfD8|}opr-7wi&myQ?PGB7b`FUs*~MNGW3b3Sj@X6X<2=Z4M5VXCzOZfyEi64b z&fMyM#o$|TagMHE+-GJWSW2i0i(5=ZZlN#K0@P%}M2m?l!RS@8d6;l)W3Ao6UX&Si zFS|!WXd1iD8PVV{RbEtU@B!=Ypdf~_jKHhY#S2FF`o~*~_{|tXN)wl;EA(>dF;j-C zyKg83O*}B6oOn8vt6aV0keW}c24fH>JQg}K4&`YZE~idCU+QiML@^J)QU&R9P#D#y zF#*5w%A2YBYWo|ms?8FIbk+|m2Q5VL&UCg1(;e^KckoO=AP~?xbzIYvE698mMW396 zlLx$bn3y$Y@7Opq=TSDK&@I+Jo;eyXq71vsq!dVzj0H})+`|^EFskziD5d{k2eV1I z9IT65*0e^dgu5)hPuJx0fl7$fC9%xA2*QeXOK)D{_9F{9SqA4o`w9F|sZ!aA8%&;S;<%gqRHjwi^5O`VaVmhQ${Rk#EYrMQY_K^ z$?dT~5p2_3S8Al!U=54VTFhN%3^oz&%cr6}#?fF3OG1T~HCR%Z75KS?+UgF=cF;r~ z)x?r~E#`lY!|h3DQ&lu@rWg!KD^5y3#C;*e^0;85Zp7EsfDD;eQVY{!$YLuFrx#Gg z8Ax-BrDby+#X~@{rO4wQbH~Lc6@#%$Eb>Aod`Co>T%aMr^|TK;7}`WQ!UW*#^5yHY zjh80o(vda?g*9n%pu`W(nPeLv7l+_JiN|kNl*UtQjg#vA!)=ONG>%zMbHmKh5I$Bt z_u|G*(Zk(vSF!pCH@bHOAEAa>Gp@!N(Rp~L+1SKCEX#u>g}(q`i_+9 z1kda0UH~h5v4aIpN>QD~j1T2$djC=0FpQ2vG_w|{_WY_RVmTS3ebF_tHLlj`EKk@a zs{1mRdfGCKj>~egs@%5Dm1TVZv%g+HB=n^)*=rhipiF*+haaM8r}|hdF#I^SJ5%yr zhd0K)$D6LBoqq}mgP18t6NRR@G2?O z^5vnT`obE9M@V}>x0BaJLs#bs-|?sqS@Ue){Fu!nLv7aB_S7}-vBqcy@)h-sI{%1_ z_KT`aVUo-cPO%fI4{mWvjs9W_qm3Jj!4soqQ4~Vrtc0{($z{D%(;kF7@h%OF4|0MA zQbQ_%dmVnkw=4V0Ewd8(_f;fHDWh#*sdIc%#=8YxEPsy3wah`*MI$9~aFNVM8oY+V z58N?ee-{1I6#dgY)kB;G44F)>07s1NZ^+iBk==7Yi35$FJzf7V zIMhF*=l?}r%Tl*;!(BoLuHa~f^Gf1<`JxyC&PYVFDg_VoO-=}v$6t_%SZ;4vR(p7! zD263lQJj<_4>7m1pL7bH>~;r@K{pX^^}#0*UZVccJSZb%W1~@CEky_9hCkeNP29gY8&>G4bZb>-K0PgFCJ8gq8z{Hv{Z% z&;l5@_GmGouX9+w0V9U)I(w0OVU*dCdsL^{NVkoXe&k|}Gh#P7;1s0pdU-zMwLCT) zR;vYSyK)ok=xWB~kyNBMZ5G&1RE=S>)6}nE$tCuhHV2wUWZRBeVA*^eV@&T4KYao- z8>J@etM7A@7(4*0RL&+k3^~(YV$bu9)@#Rx?= ze3jW(AYMZ^vk}*kf`#Mc!y~SdQi!|QxZ~^%Ew*}6dZp4A*w@^B#sC|%2y(WS&wldh zD4iBDGg=j|`ii=4Cck<{^>+oi@J&M(Y8?VB=HITKvm9-(E2#I913ZSFKVKY2Se=Hx zJe3-Ch{*{?p`=}7FdrpaBru39>m8F11i)gUN8&E9PYLndHdk*H#r-vY296s+4#_nZCzlQW4W3f}v`#qflyLh3FkP(s)bMd@6~${pwe2gSWmhZr**B{T z5En}1-PoKH;=5FCS$31_wiaX0pv?A7=y5rlOxGN88=X>E^ zQwDj#0*=q0i%R{1f3Rp(89KMi@xGG>xVdzOD06R(Kq0@Wja=V&;S=s5a{mckK=v-& zmw$EwCp=F@7BIOuHel3WFJ_RFz$IV&6}ID?EB&ThJKvZ1~x6MmPge5 zt(SYdXt(59X5{!;Xk>fv(4V&5h>9RVHi}I}qrn&%#?f~i&jPKw+a51q%CmSk1MDEX zKe-r6bb+qh+%7o2ekA2s)~|-R7IaU9lYp6gN^E_a&~$HO*Y`}0or?{lth{q)u=E;| zI`}+cO0H^nzhsK~>!OtU&k2N0OLVYInlPthjkzZ2?zWYiku)sQ47LbfGme|WDV;l* zb>mOd&2kDRZSsW(mrUaRVkLmJONrT*|Hs&Hz=dkdo6?%J&MdaFQHRVK`qY_&J%MA* z_bu1cSSTyp`fE@g{Xd~mM@Q{e2?*+Q0g zVIH8a>a3_Qfq!l3jX8{A+0iQ!cZm2NOV?bksI0G3Rp0dotW5Q7al~fl7a-JGHl9n% z;oE>%4dION#<$t49%%DllWnO8T5z?RpbziccFCUVExL|4hwyOGgT(ixrM1zBV+8!d z^X4{oDIEy<8`?G=1FBkjj!DT+Iz@+~qsPvnA3m$YYa44D5f~z?Td7(CU`g#BkaT5N z*g;A#HjrA>$k~FNN#(p?3L%cbt2&LNuOsx5&k0 zi`vIXe2K4EbSAVsA**>T=5S)3*c1CSl;av6fEaX3VUx=A;q+f7gC%w8uw>&q111fv zZ_bP?e{e|Fsm${(@fT7x=0DJ#{)DtoXz-Enn>m;)G5VqlC>JHg<*H(Yt{#e#jGs(-y04=5aS>Vo$1;BY%!$&ez*ZtGli<{b0rA5}r9)af6 z>H94&tGZUGoI89eu}dOeSbH=dzRfAM3u-hI)p$zs?nqeek+n_bh1t+8m z)Hb4(LrOb-pe5*2>HcVG!fJtDU#w>qFKY3{eoZdw1yHgW!0E(yWylc8FqYQA1TEXD zbf~B#yG!7!%30~Ut7m9o(CKG@2tdj|R&9;>!RGlD3(LDB$M;FGK#^s$JHht}O6NdN z5j`8AY_jR?2}2T60dlmdKv23$I4Du{j`3BsJ8Ex32ISc(jt3{Hr{-@x%a%l+Y?3b^ zAZlPBAe{fRv-2N4%im<|j0~(rEIuP47KSeW7qFA0q%Dp6Ne7%Z1ui*PI5ah{Yd3m? zTAi=&X-f;DfD<Q_M{%F8}X;?4MUJ}8nu+pvwssN^tipM>X-sA+cApuG5Qmea`I+!?Wz zULdjKq+Jc-)2)X^`dQ3fOXK??zua!=6M0RESPpv-s+8Zw1a+M~+)B=kS>MO*EY~-G zqh(d07C`WlE$e}SMClh>Mj6YS9Y5I5w~9o;u&J4TY_k07lvEV7LnK>NO%_n(VeD4k zG){-&tGycc@kilbQR7!qRoa)@uMNncI^CHCej*vD~KpBG?G4FFP`b=h3c<5 zL3cO5n2TX8Ns;w10gjIPBve?{hVeUA_R9O5-CF_W@8-PrpMl)(WyZAYU+qv3Uowv` zc<@@i8N{_o6&`Tq9z27D9!$ZXBG12K(1xTbPA-}(BGM8IhS8Xq8qs3B1V%+B8<7^+ zHWC<^RHEO@de4X)Q)d1leX>^6?%(m$myvMyrdaKQ;co-G*a_oPU!!}z9L&OeLum*7 zPmeF&ND3Dl8U#cd5d?(ze_jdyNx=+O5A#Da!}c|8=wRW2=fXhIpo}d>u)ZRJjc;81 z7VEmf+D8kctKd-2vqlS?v^Fz^QL*VN|7F`mpjxPht{s!yY)v`4r>erMRi&o=hWUoM zc$@*eKvtV%c!_@cy|wR^bL@3Ik>r=-2hr>LrfgyqTnolEx5{gqRP6DN}YZqiH^9hZ1}POOQ87awyCk^DBpT` z3l3r4lFjkr7P(P=KPd$5rY|V&t1lX0jgKn$b(q*dDD$8`!MP{PJ zE5l~1+~7lOtKRTIY^&dZLCB9i=Y=mk$3>Qy@RIJ2pb!a0q!k;)n7WKG;lbeFqDEWM zUbIwY8gyU}?4CS_?RN6mrP`90btKc}IeaW0;4A;$yCp4flat@16AYKs;ImANuE@Ch zgjeDskVwH)h!u|vKg!~VjeNZ*;rLlKir#Nb$sHWzp0HE zKRf85#yP2R6x6LR0UblQ6mtru36wz`)a5ZMZllv_vKg+$cXf8K8lG(R`W7itv%jz9 zv?A4R`t4b~3Ju5+WhP@p(!*%A3iYs7i5Z);*uc`Bg?t}Bdp&KIIwJcE@45+Nsj^<}B6FhfzPXOMZwm$|zrP`#no437g$~jT=c@NXr4r3@7zb;@7=b}XQ@+T0An$s3 zy-I}GgwuAIcFneODJl1gC!5vLa4zl^6=rI})~MKOwK&^^*Mp~{|Avzd_dePHu_OxR zWb)10*H+H|oQJnRcJi%1k+|P(d`(SDgv0VMSW)SSUaRS@I;h!^H^GA=#VyTK_(!gb zfYYG+%8EPLgbXQFz~R>)fb%Mo_f*X)0&>nVZ#B25(?d$K$|%>Rf;cJv@dw2r0QP_k zOmz*loRlwq%RV0tfGOWV zh1Ja{Gc3yusN*N6APmm1UxMQcFeAHY`&woD=fh3C)mv^D8)0T20-M2-Ga>*$mbt(f z1%u6C!5IU0aO@~2&ig$|g^hi%FkNU#y1!3!u9J2j9UkFJap6$er-4nKm)0;T+aZzzSI<)c~I+d3DgJ zBgQ(WOG%eh-479Awz40?Ic#qbcB9V~pVpl$g5StF&L3iwtG-RYO7M2I^rT*EYB@}5 zlr_g!Sw!cg0-U<5zV5mO>=Wnpf~@J-rwvuM1B40%d&fu0Q`&tx1iO7|^cpZmgR%yk z3R=5%0-k1ulIeTxr1o<2aCk%Rd4Trb91M(hfeW=!m(`+T%a$6+4Xmp63wOl+%y~JpiZ=VoB60)9h8{km2x5%$kR_#)es^mlcUlVs=*0cHly>6$+gT)P zh0gTZSy#^7GE9M#%m)`!ff}C}tp13BHR<3_=&N zhGv3Ic(b|b)`N`6#z8|8f5>iIN_kVdk@>3U3O0t2@!QapkmnpkH*md(+p zeb;W_Ru~AoJ_jTh!CuE~h&ESGm1Q@S!L83OCoMmdp1|3!FIQ_3KGq|gPHihQzCKSV zg;&`PH!e@vqQY+5n$J|QnYWd|K3A{+YYvAe)1X?2DDx2HFN^FgdwCser(jP74#!{E&2X00Yr`cdQbzgR(vimiMd zNA_!FBf*@-xW%lsWt^?AddX)l4RS4u`z}JI?_+2-`t*%_hFGZ=Kr#Eu%MP%k)L($G zmJ4d3&3jkMSjX|y*e+@=vvSxMI=NLgzw|}!zLAC4^qxh<=~(Ozou9>_&b@!|%YIaA z!KkG}X@w_4p(I*&!v^|5IA;yZ25P;#XjY0hN}IVag2>$kgZ$Bbcr_#;B*Go^li_D( z@pm&ocQqt+jYGXAm+EL^9-TvKv&W6w9thfs2KUHIKz)is_}cbNwDt_G{6cBKDQU_p zYRfCknnMP{ps>$tQlsP=T)#r;6N26xZw zKx7F=It;ntA6NaNhJ93Fl3J6j|F}!XFB~2F72h3((74h!`%-q2WUMX!@V^_rJG`Nz?Eck!gZGL3RRbAlc8PT{kyZ{bF%57iM2edLIrX5&C?+2PYI%I zXt99XAiTJJm?DmB^RJI}#5Q}!H_^xAW!E=&)3di-GJ>FVyjz|GE^vr#Nh25g#3JFv z;XjkU;_aN_Dfh|qxh!lASsQ>JbwyBB+W|-gsv>tNKR_GVL`ua{xA-x#D^v6cF*-SA zgy^IqVrH*F($Qbn1$&o1{-!ImiF^qZKJ|p$=j$(U{XdcTbZ-A-HPZcqRB!)@#i6(V zKP-3U=_z@tDM?icN!f|X3EHvAnSG_lsTpN^X;~%4S_Q`1MrOuZW_AU8R|G{yW=7V1 zYRd5$DOq}|W?2R)nK8*F+3B$ZYU(MPu}RscL(qSt34nd_zT?-~nPxse2;Qgc{k+cv zF?6x8Hl}klF*32RcmB6e!51TFH$Vg*eEpVG2E70o50kTr=lIgy6AqZLFN$?e<0)rt9fNOYGuj}+R!Ob5B z>a#gOe&>t)>d!@gDVMA8>96f(EjrrupF#rtS)(#1^q9F`VLKt~oQ}h(Ntw*GrQ*F% zNmvfD^=MC8BKh2eqbIo0+itJM@4)_GFL%kHP=t&qL%JK4{a!*JA}6P=Na=+yHxCHF zt^HMZU`>NPwoAy1&-40ky8o{q;q!a{pIHl^g6|gNK(z{uL-Kc)}-_4e4&hh*MQ z+LrPsZHwu@zg^JquZXyjv#_0w%_o;o#=_R*6T<2GN#_5ruy~S+?k9#HjSmbif~Gmt zrfPc@@vy*ogFj730(#C|s2q8IaKr?A#YR5`Ubw)oe>1R7IIWHU2UEG)%1~beL^mhq*kFr#ttMPg&%1fPIIwM)17ddY(dM{ zm|$7$c*Jfo;UWY`M3CXl*Efd-R7=iV(J%4Qjz&GvG3^8xSn2gn_H9Ekp>>@kCSg}n z1)p>MY^Jm@X4Lbk^Z3!;%`sayVqbB}olO=JP<^3q!Ja=3Qe-KJ z<-$Q;E<*?*5BcqYTTs~5X=TR$b>6UVlCe{Vv+>0vo~VIESk&zgxe7I2)8d1b<}1y& z??NZr$i&i9d2Es$IGSSYYONr@1?AA(%%h<6acCOJN4{u?hF<`sCDLgNrmGaQ^M8w} zk#@Ym#l>KTW}``wng=U61g&mC-(D}k(ijVRz*H{yl9sAq9m`Qu*=23j_IAk22jwff z`-db}Ovk}+vQr#mjR>R<3lu+799RYM4mEuD1Z3Bx45{h2{5xgqucgTR!z8Boa|xb& zzWx@{{cGj^+Y(eTaQx)h|CcWt94YwMGz`%X^Cm4g*TeQuE8!UhG;q;A)Irx$Itx(A zU-tC8{vjyJI_>uS_X32bTj%fYprc43(83}?C)T#jJ2q9RwWG2dQo2`GQxq2X8!n{R z@t^gm5VrdHSub!Yk=Qh-s4l6rk`_HeXld`*BMJF?sGC;9RFZQvKJjUskfd(de$j3| zY2k;2JDsw?cv(JVNXOn*=F_LuU7xd9aK4#e4tImyciMp9F6T66vLjC>80~na( zSo`z?)DbJw*nrCO7~2e;x5K#aM0yhWq__QuJ0Z;3C3H@>(!;e}M3E zgV52L4pavfLlaoZmMW-GQj^U@sis7jaEI6+ht^#Xq(zuU7#~&>a_l$eE)h~XxC0-* zLj(0#+V)Sr4(P1aR}7U;(G=@#GDYpBt5!HDQsD|cy^}|OPo^3VCx(B*7!YbE{BD~- zTyLsMnImY?+O58^d|BeXQJAn>-!w2KYm2Ld_?7euE^r>-!mplJ zT%MXtb>FVVW!`$lPh7G7Fy#%MAzeo$0=HV$cA||Az}Qm3+(XKM9iMD2XzlXd+5d6n zJ&0_X&H;;hj(K}YuFVZ`0f)#Wj69(uU#Q8@Oq5)or#TL_8c_TdK4ZYQo3WW5gdX+fWum-4aLQo-t7dCMT!enzWjL%H7HNP zV3~Z}`VNqJa%GeuFyizrFakKW3YT--=qjfenAY<#M4mMn))d2~5Cf;MEnfYHh}A7L z8h zh^<&eLd+I{lpnykh;*~L!yYL4S?^IILpVG5|M0g3-6@kH%*Em-Y)v#}nB>gs0ggx-FUGem-(sOAidI2#zY zh&d;jIr0lCjRE)_rdq$~Jj;U!_2a)q^8ecHot3i8e^H+UKOa1N|C=4~&!w;YX;0aj zeeMr?gHH_ff6ao)QknUrK%@OJZ8tHn$-67iZwk=LQ!u6?`iz(qhQZV!qr!)66<-#^ zYr2kIB|q2nj%7f3-GYXZ4>$NF-cGqz30YZkI&Wn>b$eWI*lj(G%rI<&pxsXPA{`-M zS!^k>IyfreOR5+j~VywzG;xQb0w z%vugs{rpJTD!(a9AwuTOB(Af-=UpDbhvy*Y&(y(tt2UUPr6OTg*NX)iA>l947mYoQfXk^i!S#*U@1J@ z2QxQ8j~Mw)cm)o6Wb1vaQpc)Mov+?fu#6`k=k{qr-AjZkiMVJ53n}^h_sdXxd(h?w z2sYr+WB^%EIjsH{L;2MA^&>*EmiFM)Vx{5kjR(tRxH4Nd;F7H*z7)|SOkwfFzHwu zjyQb=&j52A)7Pv%WBN0lYwa?HW( zarYIRMDIjG1c=2P^h@kN8wb9$KO$>cd);0G+mrbMnewtor?uCd4zr1?o4SR(Cg_nF zkUVpjEWC662=|IJO^(DK?x!B2j^0b6y`ZUikbi4jw#7kK+WJ;36wlKtOhDmSF}g!P zH$f8Obzo73Pm&!K+EXYKY3-PIga1nMPS3b{_VSCxM!!Teo>DGkDn2nojggH_aaXpU zH$LH4G7AvW8Fsi#Kl+Y|_v>v#Dx3|0kueJzq0pCt13sdNMIxa77x~y1i2v)r{k7Il zND=PDKSinZ=X(DiMApBZ_Ma!ai_Pbtqt7uICjU6<|9QkG#Z5_pF`)&^zp@lGHEY?> zob_KdszR+K1%w7Lw$>K?cE%}=OA}#cIkRe`ZoT>9P01uFjZPP!xp|Pi`5TCO_viK% zatD421$LF$U_%rr8raKq98kg+@S5i*PgsVji0t{U;(+WL0{{<}<}w9W&4F{x7$Pl( zbVjo%I-2ko6E1HZJ2oWFx(V7DOrd@d5*` z$^w|?A0wYs&yY_5+5hSUlD7Sv!}ZT&=${JY-yXt14J&uFLv-IB;{;7|FhDTKPqmWt zSYs!|FA=ki#QBAQ@3=CNjq3((GLRfB8)<9c7ei9omq5K)b~Ud$=ylJZQYb>5i+Z*B zzn1gmKOQ|^d3|2DbE^2vOkEo?fVE))zmGHSv#vM0oqkVW4Si($efiF_l~f1eR-H>m zT!3nXB7;q=5h;0h=b>ki`3J>AN^bpM0t?`p#cS6RwMP{$3 zf5YpWx9YH`H(#WeiS>XF^CvIoZmh-lGlgi-e+{_h8{Yi!^Lihu^G%^Y=J}bEP=IJ3 ziy-mF2-~H${~5-Q!o!0yGUx8e06S*KOy>7J>>KEl^fOt7hf06Vr(Ld4S!ktk$I!o38En7`E1Gm*Q%|uEZ}mZi#3G(m+FYjfWOvb#E__Z- z@(F2jsA4hk!V}px!d;J|`8r=x_bYB$J1ll8Ve2jmxjaLGP=xs4h~P@22`UkOuxu$Wok0kqIKZY z7v{k&6b-io3)$3e)lbt}Z_~fO@<4lyx#RLPgJs(e+HrP2uvELL0%L{k5$EjyTCw_+ ze))JVuA@XZtJY(UUm@n~7IQSIt*5nZU*U$mCqwT1D3z8{4@xE$)w^EVdEw%s?L;?S z2|Izmbm=3AO)bgLcxt0pe<;d7iUbn__kCyk1MyTdvmGeC8&7J}*?2$SH-VwY?XfoQ zqYq<9sSl<$9(~Gzm3ef2XRgI*4*Qs^-iOc{mq!7#_VR<*pu?P9q(kH6HYv$S-z zs5+&xfW0mB?!dqQgwI_$GFrdABguYE1tGtW{WA+#TpmLPEWl=W5pr5Aw28uX;H%y; zShKW7UZO*hqu~tPrf!AcpmE0#hMhK)=uhlz&ZpnD;{0lol*8qGnCqv4KG-m|@U3b4 zl?Xtc+Q$Xc06T3Y*XQIe)_)JH*Bb%rJJNTuo*~r=xnwEUULbeU+->~Au=|AaCwC|2 zB2ZvgDEj*SPVt7h<9)_mv`@5?y4II?s5&t&^ql?IDp)~#`3~D#aLNhVyyKm%5s< zL&hyL{c)Pza*a=VISCsC*4=Tp=uQtG2X4&bwB(@~G@9X0<6#s;1YfU8ceDfM>1HP@ zBy+SOJ62?4+$-QM&((;$^prmdeX|xh66wMGkkKp(BhH)FWT^D$W>Xk4W6R9ON7z@% zVRY~IWw$uox{cpl(D*&pG(%7Kh}q0AA?!&l`stAjp6n;qfF)3#d6PKfdL*zVS>}wq z$k(Z_&pyty-RR4EK022?p=*_MgQjvXnxjs<8w|nxC^pBs8a0=({ZRGv?T7#LmUdbo z5r#Hl!!S!-O;@J)d>IFHl5f20y!@7DrF^ z_z?7%h9EOc?w4A3|Xj1uxkVSJ`K>#xa_TW471++r*d$j(!vspAXkqQ`^ zoZD|(BIil5=7YRnGxHTWM533mjo-NK;i4vPo| zn?ra*s~AYnANQ59amix31-PTaLuxI)j@uiXoOo2?&_`tlv)X@EpUk}*8n&pEBNV@j zfuXm{97;!lkNX6+v9zp?5+KP7qR1!wDyh@V6iji)=-R7v3v}szoJ4rNwSbSc0fn-6 z(%(nYl;3B!l$3VIQB5c{^=Z(cU<^xg(h!)O2HJD0|Dx{h)-S(T7;?(y9>PQ`{RE6a zN^$cR<(fZBWlxS$nRU8TMF?c>_K?{Wy8Giv%3+vXj7lz=Xr6dgwdby=-Tc90f7Rmn z^RwmZ}Z^0gA} zpDV7;h)ustN7vN)e6%NhRNEoJ_;&(`T^2pSMT}J*hGEJ`LU99Th$e4vERN_Cl7g#1fFt#NO)B_sw+ciHwEO-DTLU;d;C;dye z;aOqSO?_&%jn68b<9}bH{JlC!Qnis^P(bswU71tl#@~x8%r(>Y)Y<4)?fVX?&>J1e zdSv`?r;hj;u^UCIqcnnP3%jix85v47(NBMO6yqQ~NFgO$8%!~D(h#Rsy^ znD7kV2Yq1PEh7pH_)>|VT8Le`$~LvRf){u#Ty)~YrURJVsxwRhFE305zP=MK(trR9WN;qn5XYoiV7$sVI!-sf{?Fc5ip&cf@ zGudnOY3pS~zMc(WnAgRHe8MTkv!F<~v{cir@{BfEiuf5tq};~7zttC~cORaG;|u%A z8FeY29j1eTQTh7pT&GEc=CMS64QhN+jpHJHpUzv5^nu@Wie3C#1CM*Fx&F-9i<)v5-{o*hh{<{gKx zd=UIIR`8%zBdo!+hVyj&mt4`*Z~b^{sH4B1EheAOX$fYz1wCYxsp=EgFdIn{NTQ?! z>J?uF%QS{ge*9-;b6~hcvHQ70Y(I6h!2f24sM=cn|ETCL)d{7~I-ZYj?3)4ecW9*B z;84mjBN46j zdtzrh_wdA5IPhahW^WKPnw#v7R@qkTkoZGzAsT?)f=Pb)y67CDIEV%IE>TE6pUgTg zL9ZdRW$gY5xw!Ci462Hx`Qj`7ql{#Fy#ut?rfFT}i$MKVFUxodF!_t)=KFcy*2A>u z_moV!M0=Ff>}`{iEgwBS$0#Yu7Ct48thL-)BjwNg@UxAr_*gIIEQ=QTa3jL`EsI>+ zjYuZFeNxBqt$JrBLk_Ion==8{S`XJPyZS~xDjB_0DuZHdF|98P6U&+5+OyYb`R`>$ zT&R0d)Shz`lbzwV-&7um%7g?{!VTcX>T>tUF$!9T7@uYq`WTncHb52^h>~r@jbC@! zF~bd5ftDhUhb}_yZU8OJ2*Xn2&1ia$Rn;Sm7sb%p)Pb+^?Qaz*iRYMB z{HOnwh5+*Sxe|YKy#HBs{>$%G({@@=L;915HBl#u0zbG*K;&xLx*mU+% zHqg$khwqNi@8cEMKjqd2kB5=Wh&!5H#zmiJ>!cUnH29&|eUi)`>>g%wknYx9dUyk7 z6F_YwD8?T>eO49_%SzluTA1%nJDfXWFHX4~)rVA0=CF*0C@m&A?1f@nad48XnLNwp zE8bOcL>+B=P|RwkAvBVx$EsRlXod2)+=E|ci?Bp{RE($15ivmFA*6U|Xd=mybqYmB zu#QxV!?x&{MxKG=LW=FctT;RFq3zZuAprm?YtBkFC#wgKRa#~&N`;=AcGrK;T9x2{ zZa3FXnyoEKCn}04|aPM{QB&kb#R?2`EaZAdyVs z4NDNhxCE(jNZ3uV>6F;=aw^FftX^83sJ&I4mXoE;^pDxhmL3*Or|-UBbQG$UN9wWJ z_Gr&8`m-^ODr@p$q=n>t?3;VRW@%$E@F%O+)E0^L7Bk0GL2V=ugD4wo6jw;B4HfBf z9$QHbDDg?Ad$rCSd7b5Atz^Z2FIX`vgL0-hQrrvRe!D7O@;)!aZEA8H(l^x>vYSzH zF35#}be?z1YA86ZtzpZPL0PsKsJ()yYc7G}B+!0WSBI8hz}D*h*3E68CD?Du(Q-3L zwfLiSYE_|iz$(4oxcw(>fV}v^$3TkoAXSCoanpT!u9y7iS?o>u4w3bIBgSsNmYy*h z1W%o`nC8#GF4)IVaWQjh*sHzl$ltI8`Z9gLpT7&a8Oa-am+p{yPkvq2i8(iE=2^<$ zfPFOR36Q@j3jh;(h0lq&#C%}-VI{P!Psq@u4Lgsezk6)uhVSyjP^C}(S*wqO#C1D@ zS~bMmaW^{IO-E_6a?kb0qz1PaVOn;FXEYz|4+s>)b~lTLRcW%R!an^43kyD5MPZhRfWFGU)S%1LxsUp2 zE;Us$5)4>8;;&AzRRzfgNC~-xa1+bPl^r;)_xBxv+pm=I0VetdtyTryvPe_gPxm>Q z_J?3wcRi!xZ1W~0RUF-rRWG7hZKtOXPpeMOs9HYA}f$?TK=>Aff z@~*63NyAagJAJClb{y!OE`fH6#4%WiTMHeu%x(UjE6ke>r+;+=D#;049D6mD``YoG z&czj|I5U=`*T2rGnx~>8-_;gEt>-kGvt!5mgZR9IqqNN_j4}M1cGl@?_vG#0PW6A?WBxxZ#Uv%&zmm6n4Vn$Eva*fodv1|bVpO(hKfaNee@6>|jpKc0 zmjqmnnyQ&nkbM;N6v_wlKZCs%guAhSL58z%C790YIL@BnbNlo5{tmx`^uyb@_HUue}m7G$vd zI5w&QbjX4RGH=e-Y&Izgagbzx3!ogC(g+bO^w;IE3jJuu$#Z`eX#9;(yw!S zpqe!4ErF|esQfMRrgi`X06a6k9B2myu~agt{0Whb(z^ZbHdNFoP1U`Rw+iEw`Z*Lh zc-Q<@l@U!(xBZegoF3uD1t$e-JVD%mQkJBR%(I{VA|=?!j6|KU9U{n>$@I~$??v)w z;98=w&&N+hv|};w=V$2FMt0i5)=+sWdIPSZ{3%RYClgv0faav`D_>A_k9tv--8&ZBr9J#qbF7|M*D%$3JfTZ-)QB{C)K` zKU{V6KVloB^CESsBhf%|)dkLE6E48FS@*JteR5g64Jrpodo5U0D-!uwv&6a4Fl{O6 zyxHKQB8c>4AC`!pg{&~-0?8cDx6`0ER% z(2ZLil$_5P#|*_SXgAVG1ao)lP8|c;Y;=mTH`h;FEC+AV&L7eH=(oPA=AGnnzHDJN zyY(8rx**=d!{-j2ao-WU(*`r#rBA%2dAsQd?8cs>gDy?imSqbZjXjo|oeM^@$|BC< zxiy-8&F?g75yZ_hS70J6RcoaOB}DxY2bxH-g$L62jwV{5Lq#NOQCbAvllj~@ER~xF z!#Fu8vcW31=Vdw4Mn@uZsWvx;o337|70o?Ynkpam4QGJ_evK`nOPAK_gOajPx0c3l z311T3b1G>1Y9>_!ebR;7%-$Crbq27Fkvy4Prpl~klji#|FZMbu5h{}kA?rSKV_F_@ z%ytfQD=XwVo~bTvrXr1LW!Ertm`NTalpqU3>{Ao;$bF4x)NFB^q2&7P43-oNqy|O~ zJWOA#xsyxq7&4qBfdI+9&&Exo9v_JS-<(9XP-HJzQOdHAaG)7)hIn4(~N< z(mX#}|4=|llvZ6>9**iSEQ8ArOdxb-R=bTgQ7?tQs&H+yls>WKwl6GCflZgFs#K3Y zRSVXd=Zag)y#b|hcAjYe`KOt-G)ar^a+KZbVJ>4CVyU8S!L}rhhya%(*@~j0-UWik zAX#d?bD)K@tIHUpq>-%Z;K@w9wmwK_Cqx#M5V{!~@3jp<mvau-CC&*inT)s~J(4bQwef5NKwgT8Q!5-TWq+{Q14#0wUy=9Y&Ypv{g%UCpcdp)X5E#mp<_EW} z=2<&j9^Fa z3EXedVfxY5Pe70wdS^%p+8aT|)B!sa1#u?2OPp=CV&*`*TT`71PI~dxxHQMLqlV+_ zWUMw+Ruw)|rszJSTi7>|fUNVG>}E*etC>g}1Y86wHL@tqh2Qqg*qt5}vw^D+3tB2+ zOlY&s^1h6tuT%NS6X}W?gx*ns*7yHkXhnJ+UWZ{Ae>r|Qbk_)fB06SPL%>VMWpwm+x zP?NliTb!q@+a~(@Q&ktpE>{)L@>Q;Wgg8=R663fb>J2Q!78L#C@8y9LY2?-4vk z_VD8&5wvW2OdCi?KW4s%6jHk~mEnZx$`Hrv=UxVFN3|Te!P+28e*l_yVLT?&m0tzF zmVCLhb12&m4(bWEq&P>sQiougHCV+$&>Ci9;0S1h?h-E;Zi(Cvg;uYtg2|5aE7~sA zlE;v1(-5Hja}8O%R<7?(FE7NU;?vf~)EeP)K62hF4l^F`dlP53jXB(ANaPMf(LA zfmVcBTft7P)(yoHv{zG1JXx74PpyZbHDuCh3Mb>T=t*v!oOq@r?DC-d_s}%(rFs^^ zZ%d6I;6Giz^0sukzltv_A#7%@4AKf&qE=|ox(x0C`?lw{Z^gvhO?*eU;u_V^d6E9W zaC>^PN#!|DT>$3Zp&}#PJ}eydoid0XvIwPB3pE)2kBgEmre!NXABiU*HxMV7`Uz$yH)SRse@1O zoS+s^kzqW=Bmk$B17sAN(<_%zh&u=%w9Bp3=QzeRq`5~r^rb{D=12B3v5=xPUc1&8 zCLh@^od>R{eDUR3l*po`zs;uh_X)JBHCGHG)wgq$QMZp10IY zY2=XbVB_DLnONUkW5OQ*=0V-wY%*MU)o$!<{o_(bY2-BxVcMdMSYM5)5F15&N}G z|I&m%7V5&f_J#mGGD9nSQwLKg!{7TN=Bk*{ZvfixElr(VcAT1vR{dY%M0Tljq9WV}=Z$6IiTT;#oFAJFftdiMJ#wEMeD@ z51w^Pmnw7ll|jVNT-J<1oIp)}pY^abX z$(&%+DJ@cldZ?NZnM|!;*O=buWpVUW))kWmtz4NgS%^jN&Ywi!&&4)DLbp!JEbFytdRjmC-honQklfu%p{-bM^z#od$M*bQzSw5j zX3^Lp$w2swMHoF}`Z9WIzG8}u;B1R!=LYOf1}#jITe~~nQ;Uw&c+fa3Lgwn6)nstW z)1$tR#Q}yfd>bWF+Ob&g+BT(j=L_^e7|fj#j6cl=k0M`f(ftD-n>(#4Nl@}rN~tYS`Skee3!%|*_nJ(j6D=FB{b~2vk%t2&mxW2z+zjLWk+q27P_>9h-);iNanhX7PBDbcujL#~5 z&?(YirpKTZD!9x6l*G3Iph2-*)mo%M7O15Y?K^}Ss$rFWCN3IIg9>mn9T@G zzu)<~9Bo<%^_osbyA{t=qjdLDcdWLqyImc2r20f(BX$ETg5YLvVam~48XGJG7W5fN zeWs9lWApcxW-#tWmXVqY?yLp20j9M$T>My(y|^v)LKO%s%ODWLrLz*#LpXDdEQUUd z+D6kuG2t%RG2}8P=+solX{al2{qh6tDmV_>7 zZ>CY~Nt`JNL&QdxG8121=3=w47?gNkfrjE=GvVnB4cFHzeYMiDr zF5_|pv(dl>#!gc}Y4?VQ?1O6TUQNUqR0*hUlY5Q4E~dlc>lW+{v_{@P5fZYFa1c#X z=!TAp#_q{|jfLfaxnemk*gTcZFm4I4BIwKCaX7oX z6_wEK72gdI5+u7z4z_teh3zKKJ{7F9Y3tIuRr_Ri@Bvf7W=@p9;Bjmp&Ktwe1go@g z{G?eWDaFAruknsF)IB5n9^l#&E2dmJK4$G4!9U`y0i_Q|(feC(gNFEr!T9rk;$TvS zqh@V8#8Bth!us%G-sNs{bO&Os3lgsVUB}zcmS08vt@n65^4Xp|reP<{!%*rXNk~aK)LSlNoJNI(_qdG2Jodh6AlOv+{ z;JW)ApZ^)A@XJnTtgl4;2A{h<2G$v-cp><^qRl*d)+?YIEFckv*!>B&DkwlnP&>%I z+A}uR>lB9l{8NnJYkY7UV&6}?r1g&u1J2qohUe>(H(k~rmv5R|-%=k|EDZVRiDG+tP<5~o z(OJW^xW0D_;s0>w3YAjcdIyc7-{DI_UM2>*Sf4_S)}oEi zS*jSwX~y7w)XP%PZ-fS^-)jT~H9o1m>_H)Z8@SZ+!=i9q?d)pND0RQcBS}y7^0)A!t86RLC=*8Zt!3gCklci zCWm;JEelpqZpMib)>0@$T6o&hMwL;38r(T`wNj0t*bXH`>4`_=8;)Ce1p}eMf_!&& zM;&EckhL~k#kMf|v|%ZDuXyBE#h?gsErc=rIFo@re{kVy&K2n{o6VKG3}4gh!VYnG z$T0Z}F(zjh8}9uHtKO4jLFt|bESh#3O@;GbEg3M*LBwNyi14t_fM>W zCHt(icYV<*C*4#5LG4lqo|u{WdFTkUAXiF^wuX?kMivYwG;cUv&>EMm`fy5DY_GEY znMK9$!aj|hES+Z<83-j~skhCI;Pu%H%+>^Bz%{6~ z>}YpJ(w89-R@khjmJ>;l(@rr{&Ut7oxQ4ROI#n&FwiPsAf@tHpW)*0}`|%SE1dzds@`VuDdk_DPCuFR*g))jsMvKP(mSkc zWo>iNM5cWmg-V{(>-AG8A$LSX1>5fmn2oSeZn%(_a0~7T8&|LOPYoH6-pY3oV-UAG z>$w!2>A-)ojaB{TUre9i&ZqFm*+jc}1Bec>7Hc z)^d17RH6xWWS;#2-z8jH`e`9#2wDjwYAJiX_=FtYQNC8?RGJ(08CKIcTk6nC8|x<4 zInb7n1cgS>8AxTxduWGD6vjTII3%LBmVM%od2z(4Bqn3?jQ1bxhKHi31_*Gjl)}nX z4;pA9m94^*U+@Y=>peo zNo?*$Q9)7N$K6y$*p)D{cc%Zu`DTK%K-lB8n^%GL_$`in;M$b1F~KI~*UTFnhF5T3{g*2a#kgwo$-jYFM0vcd$qn9xB+JyI$&7jpv=+%4#Xh0b<3g`3 zhdF_vA*~t{>b+!2>!`@0Elcl!sxDEbBBGAVD}8c_0)Eg^8|ej6d_yHd_!=@JmoYjU zDmq^-Q-Y^4Iaf={Hd9=CS%Sxv^kma=dNjj<{4BrZ7U*O{+?0VoYOUXdkY9f8yred3 ztpN8Hv^myWp5ub>K;@BkrH^pxge&e5<$+hC(#oJ)^}zggRWkm-)(xahJjQ~#K#-+7 zGw=m_285~N{?R4b0iN&}e;|6(xLHZ+m7tYXuBueU)KvCVwtw4faK<;_SvQmp^E)m# z2wuc@7d(|cOp;f4&di7t?qK&Dvz;BHR`}wHayxGn2zepXZYY_=6nL{@%h-F`d>*0Q zn0&eInMklJ-Zc>ySO7s;tT*FI;YoqMpa<82%uwHk)3aqzTi)x`BaHh-o(A5M(-c%C z>xbJ`o88`;vnf)yqQ#QGGlmJtz6c!pA6!rAUmxa%Af&ZuA6pl#o} zi;9zZ1L7ofenGj%Fj-a-nA*sqtn6mXc1h9sIIkyAemj!SNmgZ#+E!dS6+GY#zxqki zb%?-K82eoIzTOgfj^;CH>KDy#wY5d9Nn_w1vmh!-Y{1}0ZU?PFVQB5w6L!hDS!2Nm zFFx3+QkhQ!b)#H!Qnv*Nwz8+e29Cm1DoBJxbWja$>oKZ_j?7xTXnI@*OLCl)7N~te z^!mihr{LC84r@yM>a9}%1)_{R)=SSrg^r5K4bCz@v=5aU^X*cuVK=&yFmrD^Pd;58 zc{+qj?;Dd4ZD*S#@P<3-KttrOkR6cMPKCYnlb`7~wXB2OAeMfC-YoxO7+49pwP7q54W_vapya&dxE)%o2JOs4eiSpYf}8%s{8$4xxWR-`$?m$BIN3E zQoj|Q14<@0uqS?Ca)WJUljz837q%vSxmIWj)I!Phz!PwNGaWWFenr)_BF$zXoB&x3 z5AKB{|Ju|RZhRi1@rkc3yozo8Lm%0PcvIh1fczcQot9UMmk7TY%I%%{Jv6J$ghe~# z;;NX{@iF*)kraz$Dwl~RuXp0)#U7Bd<}3rF(S-|X?-p-f{tJfT(EGh4lN?CxX?fMo z6r(nG$vg9Hdaau3(&+jEObS+Bh2&`$3W9=Vps}yJSHN7y8hLvl)nB19>mWMwUr(xB z`cjbCljUKIR{#?{A!NG}3S4ASfj_~%Te3Fc^2~0n0$T)oW%Z5r8}9Y4I6v?`CyjTc zW1ibtcRdK^@IHShDdckCr;LN_ihXbY*`Rwwplb1h2Iv^eU_*mB%Aq4r%EZRv717gB z#EEi&Sa+vLapDq6h49;lqPn6=`dFaMzOCt1X+b5q=+_c-Qxn1)-O#BL z?4w>BKo5Oh8p~w)#te?LkZ8AgTQ_-ON;EC+bWR0OfLCGR@EB%TLDu*uOzv5Tz6qzw zd;41Q!&&Wk=*46P=F>+7yRpw?PlVqlbeRx^nUAjur^`NQj3Pzy`#W8}LQ^~C z6SA6PQBuL;kvli0x~wfGh6oEV+a7p%`B+Nk8B&T7fhVfnSP`m+nY$9nqsg%d@Wc~# zM_=SMx$)~a9@i=R+US4X?quG8RXz`Q(BSkHT zzZL%YCBF43|FhH0ZxHz=-vbP*)PRj}mVaTYA`>(M#1r_#4Z{8pyH(k{|M*PzKQ-VU z?L**Fg?xhb6M#*~$qivr@PHZ=L3IdiWpXyhpDAZ^7Ck4u)%G9`mC6kz>_jwt*--Mn zI@XU#*6kD5&GH)8-m3jlR!93cpjUli5a?4l)yW5xlB!H4#F4J>6c|bg%5==zhh8;< zyd|41?AFey>+YZ$aGVh4n4mH6TB)adN zYK+1QTX*9O8#m{gG5r@PhWvu{NdH>wcM?lXya3nbw$IihoXQR|ZpYSOOQuu&(b}=T@mEqbXCPJXqLOIyr3b!WAfOaIeN;` z-7LGUIB@FC#~qNa$tcN zX5}GhmZSvke@P$`rZPL;e>k2T2(loybV{`eh9reDyw4RH3)L+0{_^!}LY`8%ZSv(X zrUV3G;^Cqgrl1<+E#$K7eJT&}`=%UkF)5?@n**RqY!GS7IfP0i;CzQDvcT<&My#AO zF*fr!GY?U*wL&wyAZP=1+Qi~nVslG71q?od^V8Vqh^&c7-pUnF?Di1DTMGeFr%jIJnq3Q=CtEJBZ_*;5gE(xykw7*}hyR9nX9@#V3r8;{bg= z9k7}3uloES-TjX}gdbiYiYId4y`QHL>l5m+*$u)umJtKqa7I!B zrxZZFnHOBz$eMEvzmLF>{uWRmRIz35G5{1bhdJrWmsOl9dOYRFs%(mZ4TJfS3GUoY z8%!{#xmMt_#(CV~1B|S-+k=;OYwgZ>zdOx{bV!sg7PlQ%>~3rn9h{y{k%j&?l)QCzmpt zHxxOZhMRAamdjbDet~6M;}H_T<)05-)m@PTUhZTgox1US@22rBt{ z-J$qMz3nMW5lg-gSZYiiq_NTjy6dn7UP4QJVp2i=3C6W}#NW{4pV))OEIfENSc%N5 zbbE!*rXyV#@6d8HG0N;1ObGGvQsuc!K@Wsp*` zj5Feo3Uo)L`d(V)_%z0LSuP*wifuNhm>MU`mP|5@&}jDLHOFDCobqak;&7M-#@@sJ zeFwsYVpHiYKvWb576^#v*J1t7STX-*6BG~_&>2S=?GXV^hB;-|FNckyOcD+q1iq}^ zl%7>;YWnSDtZ(>_2##64LRp>! z*9*pH*4_{4p0`^SZ@eLG!1*OOgO^AW$vzk|~XXb*oA8s&FrVSWL}fi3lc5@!SyzO+s%&`?Oe4 zkR;*QmG35AhhA2SfnDHMst&Gbu8v2CbziPhoC3y$#s^&wx>%s65fF~LQNgVrv@mxR zUq4}YMRP*IJc$7-3xj3VGR{d6j^BbJ%WPJ!5>-JxBeLC-&aBEt1x+U_>6S}iMZJ^S z{JJ8w*pMp*q;3Av#JW3}di-p3a0qT$C*YE=iO|fgLuudeK7z#s& zNv`(!Jnv6-HodjGbW|B!i*4|(Ys5zBJ~*>wcA0o%dipNK0kmzf#N-#>oxdnB9C@qJ zqmO+ij2xS!AfV7k_eP~hiZHVz>MJGKryBS=Jt9|x%z6QXsaUE)u?4@0oV~6XtKPH!#1vpK* z)jy-Us#vP8CUT(F$k5$gTT-(9BIt=Sr2MEW=D5G!{LNB#?;~qb<#@@alHw*Y(rGCW z_UNXnRikDXV|HCJ)w{NGc>Ec*^~BeMOa69Pq~D7xb5A}Z)SD|_#A(FgMFKXqvAD$jkUTW(k@wWxE*Am-Wev1Tj7 zx-)GB`2%V-{OznB{Oz0`D>^RL{NtQC5J+7-m_u0m9x_QZ-baTi4OCwin2G4vRVd|b z#uKAfml%wjM@rWl#jJ3<4tO|rB7EHt-EWh~20>YsLjEjp3>!_lsi{&A`x9vIP&8bv0rcuU>SbE>yjv-bF98a?n5Z z<&ufd_I~;=Do5YX@x^DfqPGI(W+K4cO!w=+^@~&HUr$>^Dr){Gv`Bpe{Rv6F7MdUx zW)A&O7-s9M5-=uW)1EvCe`{eL^`P&QGCi_4%qNQW+w4%O7!gmvmj0*X7x`$C9w`}I z4;g13Yg{ZHPaaReZ;@%K4PL!ckRkRHLcC|p%H0VHOOvF_i}A_qqo<>=)$}_1&`8^H zvt6CH*&YT7gW^77VXG=wP(6H|y!s`eNHySyo_4s*1 z=f!O8)JUBi;6FWAAFRSw+n8{(KDOC1urUIu`~PH6oZe|(s}4- zFv%F83l3UnrivGQB0Y$6A3I}%UjsuSS5lrv!I7+fmE1qKI@|b3G{lEhwV^4k#)6@t z``RT+{ekO=nQlNxk%qv`1uX6*PM4%>`2;iX1goo$1GGk$e)G!}Iktpzk#0q5lk2BG z*C0cE_oTTG!AWCLL8l?KtKe;#h3|rB#j94i?up(xh?~CE_J?^IY%l53HfL?~l$lH$ z9QV)l9egqA>?4N^MgwjDis2eFsWsyx@54jl)hHan_28D?XK6RS)qj$rJ24+Lc?&)l__&M~~Eks$7NHRVjM8kB= zM2*Q*(ao%}v5^$}0_WY+$FEo4Abng*DPwxz;km27m4?S>tJ|l^o=!_A>N7_qX-!bEf>kUTDZId`-pC}2>~VC`+6y6aEnRr{w%^6Ww_2d$q?v1ErT)c*nRj2k zD4R5}HmXK#oGFBb+e%-lH#dr$u0(C3DvT{C@YPPFaXWQA-NxOH3Y!EA+zqUB9S+RLG+TB1#P43ER_cn=mVZa!pWn!m&=jF`ZEUFd8K z)@|48>!9(1S2XvPNpz4&_1*w2GKmzsrOcT>_AxDqm>1!MydeFD#4mJ+k)uStModCg za62D@jzzP-k+G5VxI(`NzJR$yL1!uG5V#xP2p23@t_r7|-3Z-aE`OD|LDL74$qQGj zAiBkpE5as_=qY@R&kXs>IiHec+Vv~%Ye

i1O>I_eGKr^&y;^hm-V94&lC5U~@}s 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 GfBiq \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..5f192121e --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/kotlin-koans.iml b/kotlin-koans.iml index 772215cd1..3539cc0a7 100644 --- a/kotlin-koans.iml +++ b/kotlin-koans.iml @@ -3,8 +3,9 @@ + - + @@ -28,5 +29,4 @@ - - + \ No newline at end of file diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index 8085d5a85..1c4837081 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -7,8 +7,7 @@ import util.doc0 * Description of task. */ fun todoTask0(): Nothing = TODO( - """ - + """ Task 0 Introduction @@ -45,9 +44,9 @@ fun todoTask0(): Nothing = TODO( Using 'references' you can usually navigate and see the code mentioned in the task description. To start please make the function 'task0' return "OK". - """, - documentation = doc0(), - references = { task0(); "OK" } + """, + documentation = doc0(), + references = { task0(); "OK" } ) /** diff --git a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt index 4697cb518..7e8bee001 100644 --- a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt +++ b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt @@ -4,17 +4,23 @@ import util.TODO import util.doc10 import java.util.* +/** + * Description of task. + */ fun todoTask10(): Nothing = TODO( - """ + """ Task 10 - Read about object expressions that play the same role in Kotlin as anonymous classes do in Java. + Object expressions that play the same role in Kotlin as anonymous classes do in Java. + + The goal of this task is to sort the List of Int using an object expression that + provides a comparator to sort a list of Int in a descending order using java.util.Collections + class. - Add an object expression that provides a comparator to sort a list in a descending order using java.util.Collections class. - In Kotlin you use Kotlin library extensions instead of java.util.Collections, - but this example is still a good demonstration of mixing Kotlin and Java code. - """, - documentation = doc10() + In Kotlin you use Kotlin library extensions instead of java.util.Collections, but this example + is still a good demonstration of mixing Kotlin and Java code. + """, + documentation = doc10() ) /** diff --git a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt index dd3dec0e2..3bb405df3 100644 --- a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt +++ b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt @@ -4,20 +4,47 @@ import util.TODO import util.doc11 import java.util.* +/** + * Description of task. + */ fun todoTask11(): Nothing = TODO( - """ + """ Task 11 - When an object implements a SAM interface (one with a Single Abstract Method), you can pass a lambda instead. + A Java class is considered to implement the SAM (Single Abstract Method) interface when it provides + a single worker method (you can ignore methods like toString(), equals(), hashCode()). Looking + at Java's Collection interface + + https://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html + + We see that Comparator (ignoring the equals() method) only requires implementation + a of single methodL: compare(T, T). Although compare(T, T) isn't Abstract (for that + matter Comparator is an Interface, not an abstract class), none the less, Comparator + is still consdered to be a SAM. + + When the parameter to a function is of a type that implements the SAM pattern, + you can replace that parameter with a simple lamba that contains the correct number + of arguments. + Read more about SAM conversions in the blog posts about Kotlin. - Rewrite the previous example changing an object expression to a lambda. - """, - documentation = doc11() + The goal of this task is to rewrite the previous example changing an object expression + to a single lambda. The bulk of the code is already in place, just replace, within + task11(), the text "todoTask11()" with the appropriate expression or logic. + """, + documentation = doc11() ) +/** + * MODIFY the function body of this method as described in todoTask11(). + * The ideal solution to this will be to replace the text of "todoTask11()" in the function body + * with an expression like that found in the body of the comparator(x Int: y:Int) { } function + * you wrote in task10. + * + * @return a List of Int where the list is a pre-specified list of Int's sorted in descending order. + */ fun task11(): List { val arrayList = arrayListOf(1, 5, 2) - Collections.sort(arrayList, { x, y -> todoTask11() }) + Collections.sort(arrayList, { l, r -> todoTask11() }) return arrayList } diff --git a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt index 7149897b1..b422ab3b0 100644 --- a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt +++ b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt @@ -3,20 +3,36 @@ package i_introduction._12_Extensions_On_Collections import util.TODO import util.doc12 +/** + * Description of task. + */ fun todoTask12(): Nothing = TODO( - """ + """ Task 12 - In Kotlin standard library there are lots of extension functions that make the work with collections more convenient. - Rewrite the previous example once more using an extension function 'sortedDescending'. + The Kotlin standard library includes many extension functions, making the Java standard library + easier to use and more convenient. + + One of the additions Kotlin makes to the Java standard library is adding the sortedDescending() + to Java's Collection object. + + Rewrite the previous example once more using Kotin's Collection extension function 'sortedDescending()'. + + Because Kotlin can extend existing Java classes, it wasn't necessary for Kotin to introduce their + own Collection-related classes. Kotlin mere using the existing Java classes but improves them. - Kotlin code can be easily mixed with Java code. - Thus in Kotlin we don't introduce our own collections, but use standard Java ones (slightly improved). Read about read-only and mutable views on Java collections. - """, - documentation = doc12() + """, + documentation = doc12() ) +/** + * MODIFY the function body of this method as described in todoTask11(). + * The ideal solution to this will be to replace the text of "todoTask11()" in the function body + * with an expression must like the body of the comparator(x Int: y:Int) function you write in task10. + * + * @return a List of Int where the list is a pre-specified list of Int's sorted in descending order. + */ fun task12(): List { todoTask12() return arrayListOf(1, 5, 2) diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java index f373204aa..314cd8f3d 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java @@ -5,6 +5,10 @@ import java.util.Collection; import java.util.Iterator; +/** + * Example class for task 1. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode1 extends JavaCode { public String task1(Collection collection) { StringBuilder sb = new StringBuilder(); diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt index 7b888d30d..8c297f68a 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt @@ -6,7 +6,7 @@ import util.TODO * Description of task. */ fun todoTask1(collection: Collection): Nothing = TODO( - """ + """ Task 1 The logic for this task exists in java class JavaCode1, which is in this same folder. @@ -19,14 +19,15 @@ fun todoTask1(collection: Collection): Nothing = TODO( In the future, you will want to reject the automatic conversion and manually translate the code from Java to Kotlin so you don't cheat yourself out of the learning opportunity. - """, - references = { JavaCode1().task1(collection) }) + """, + references = { JavaCode1().task1(collection) }) /** - * Replace the function body of this method as described in todoTask1(). + * Modify the function body of this method as described in todoTask1(). * - * @return the string literal "OK". + * @param collection a collection of Int + * @return If the incoming collection is [1, 12, 3] the output should be "{1, 12, 3}". */ fun task1(collection: Collection): String { todoTask1(collection) diff --git a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt index c895828da..a757ca59b 100644 --- a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt +++ b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt @@ -16,7 +16,7 @@ fun usage() { * Description of task. */ fun todoTask2(): Nothing = TODO( - """ + """ Task 2 The logic defined in 'task1' is re-inventing the wheel. The Kotlin standard library @@ -27,14 +27,15 @@ fun todoTask2(): Nothing = TODO( return the result of collection.joinToString() with appropriate arguments as to provide the same result as task1's JavaCode1.task1(collection). Your solution should pass only two arguments to joinToString(). - """, - documentation = doc2(), - references = { collection: Collection -> task1(collection); collection.joinToString() }) + """, + documentation = doc2(), + references = { collection: Collection -> task1(collection); collection.joinToString() }) /** - * Replace the function body of this method as described in todoTask2(). + * Modify the function body of this method as described in todoTask2(). * - * @return the string literal "OK". + * @param collection a collection of Int + * @return If the incoming collection is [1, 12, 3] the output should be "{1, 12, 3}". */ fun task2(collection: Collection): String { todoTask2() diff --git a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt index ac5fee696..5134b6ec7 100644 --- a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt +++ b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt @@ -7,7 +7,7 @@ import util.doc2 * Description of task. */ fun todoTask3(): Nothing = TODO( - """ + """ Task 3 Several overloads of 'JavaCode3.foo()' can be replaced with one function in Kotlin. @@ -18,9 +18,9 @@ fun todoTask3(): Nothing = TODO( The goal of this task is to change the list of arguments for the function 'foo', provided just below, and replace the function implementation such that the code in task3() will return the String "a42b1C42D2". - """, - documentation = doc2(), - references = { name: String -> JavaCode3().foo(name); foo(name) }) + """, + documentation = doc2(), + references = { name: String -> JavaCode3().foo(name); foo(name) }) /** * Function to create string that contains a specified string, possibly as upper case (default is lowercase), @@ -32,9 +32,10 @@ fun todoTask3(): Nothing = TODO( fun foo(name: String /* more arguments will go here*/): String = todoTask3() /** - * Replace the function body of this method as described in todoTask3(). + * Modify the function body of this method as described in todoTask3(). * - * @return the string literal "OK". + * @return the literal string "a42b1C42D2" as computed by this function with multiple + * calls to the foo(...) function. */ fun task3(): String { return todoTask3() diff --git a/src/i_introduction/_3_Default_Arguments/JavaCode3.java b/src/i_introduction/_3_Default_Arguments/JavaCode3.java index 40ecb16e2..8897f9246 100644 --- a/src/i_introduction/_3_Default_Arguments/JavaCode3.java +++ b/src/i_introduction/_3_Default_Arguments/JavaCode3.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 3. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode3 extends JavaCode { private int defaultNumber = 42; diff --git a/src/i_introduction/_4_Lambdas/JavaCode4.java b/src/i_introduction/_4_Lambdas/JavaCode4.java index 7938be547..7b522ce26 100644 --- a/src/i_introduction/_4_Lambdas/JavaCode4.java +++ b/src/i_introduction/_4_Lambdas/JavaCode4.java @@ -6,6 +6,10 @@ import java.util.Collection; +/** + * Example class for task 4. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode4 extends JavaCode { public boolean task4(Collection collection) { return Iterables.any(collection, new Predicate() { diff --git a/src/i_introduction/_4_Lambdas/Lambdas.kt b/src/i_introduction/_4_Lambdas/Lambdas.kt index bcd753086..800ad8a73 100644 --- a/src/i_introduction/_4_Lambdas/Lambdas.kt +++ b/src/i_introduction/_4_Lambdas/Lambdas.kt @@ -14,7 +14,7 @@ fun example() { * Description of task. */ fun todoTask4(collection: Collection): Nothing = TODO( - """ + """ Task 4 Replace the implementation of task4(collection) to provide the same logic as @@ -23,9 +23,9 @@ fun todoTask4(collection: Collection): Nothing = TODO( You can find the appropriate function to call on 'collection' through IntelliJ's code completion feature. (Don't use the class 'Iterables'). - """, - documentation = doc4(), - references = { JavaCode4().task4(collection) }) + """, + documentation = doc4(), + references = { JavaCode4().task4(collection) }) /** * Replace the function body of this method as described in todoTask4(). diff --git a/src/i_introduction/_5_String_Templates/StringTemplates.kt b/src/i_introduction/_5_String_Templates/StringTemplates.kt index 74cb25cfd..d1d67f09a 100644 --- a/src/i_introduction/_5_String_Templates/StringTemplates.kt +++ b/src/i_introduction/_5_String_Templates/StringTemplates.kt @@ -46,7 +46,7 @@ fun example() = "13.06.1992".matches(getPattern().toRegex()) //true * Description of task. */ fun todoTask5(): Nothing = TODO( - """ + """ Task 5 The goal of this task is to replace the task5() function implementation @@ -57,9 +57,9 @@ fun todoTask5(): Nothing = TODO( that nearly solves this task, but not quite. The solution of your task should use the provided 'month' variable (above). - """, - documentation = doc5(), - references = { getPattern(); month }) + """, + documentation = doc5(), + references = { getPattern(); month }) /** * A regular expression string is capable of matching the months in 3 letter uppercase. diff --git a/src/i_introduction/_6_Data_Classes/DataClasses.kt b/src/i_introduction/_6_Data_Classes/DataClasses.kt index db42b0e6f..04cc3b970 100644 --- a/src/i_introduction/_6_Data_Classes/DataClasses.kt +++ b/src/i_introduction/_6_Data_Classes/DataClasses.kt @@ -3,17 +3,26 @@ package i_introduction._6_Data_Classes import util.TODO import util.doc6 +/** + * Description of task. + */ fun todoTask6(): Nothing = TODO( - """ + """ Task 6 - Convert 'JavaCode6.Person' class to Kotlin. - Then add an annotation `data` to the resulting class. - This annotation means the compiler will generate a bunch of useful methods in this class: `equals`/`hashCode`, `toString` and some others. - The `task6` function should return a list of persons. - """, - documentation = doc6(), - references = { JavaCode6.Person("Alice", 29) } + The goal of this class is to learn out to simplify the Person class found in JavaCode6 + by writing it in Kotlin and placing the new simplified Person class here in this file by + completing the definition of "class Person" found just below. + + To-do: + * Complete the Person class (just below), your definition should fit on a single line + * Make the class a "data" class to automatically have the compiler generate a bunch of + useful methods in this class: `equals`/`hashCode`, `toString` and some others. + * Modify the task6() function should return a list of Person. Uncomment the //return line + and remove the todoTask6() line. + """, + documentation = doc6(), + references = { JavaCode6.Person("Alice", 29) } ) /** @@ -22,9 +31,10 @@ fun todoTask6(): Nothing = TODO( class Person /** - * Replace the function body of this method as described in todoTask6(). + * Modify the function body of this method as described in todoTask6(). + * The //return line within task6() * - * @return a list of two Person objects, Alice and Bob. + * @return a list of two Person objects, one for Alice and one for Bob. */ fun task6(): List { todoTask6() diff --git a/src/i_introduction/_6_Data_Classes/JavaCode6.java b/src/i_introduction/_6_Data_Classes/JavaCode6.java index 7d18a0f7a..a03ad0344 100644 --- a/src/i_introduction/_6_Data_Classes/JavaCode6.java +++ b/src/i_introduction/_6_Data_Classes/JavaCode6.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 6. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode6 extends JavaCode { public static class Person { diff --git a/src/i_introduction/_7_Nullable_Types/JavaCode7.java b/src/i_introduction/_7_Nullable_Types/JavaCode7.java index df176b6e6..827fc0469 100644 --- a/src/i_introduction/_7_Nullable_Types/JavaCode7.java +++ b/src/i_introduction/_7_Nullable_Types/JavaCode7.java @@ -4,6 +4,10 @@ import org.jetbrains.annotations.Nullable; import util.JavaCode; +/** + * Example class for task 7. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode7 extends JavaCode { public void sendMessageToClient(@Nullable Client client, @Nullable String message, @NotNull Mailer mailer) { if (client == null || message == null) return; diff --git a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt index 0296c292c..4dd423720 100644 --- a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt +++ b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt @@ -12,22 +12,32 @@ fun test() { val j: Int = q?.length ?: 0 // 0 } +/** + * Description of task. + */ fun todoTask7(client: Client?, message: String?, mailer: Mailer): Nothing = TODO( - """ + """ Task 7 - Rewrite JavaCode7.sendMessageToClient in Kotlin, using only one 'if' expression. - Declarations of Client, PersonalInfo and Mailer are given below. - """, - documentation = doc7(), - references = { JavaCode7().sendMessageToClient(client, message, mailer) } -) + The goal of this task is to introduce you to Nullable Types, which Kotin decorates + with the "?" symbol to the right of (but connected to) the type. For example + the type "Int" must contain a valid Int value (such as 15), but the type + "Int?" may contain a normal Int value OR it might be null -fun sendMessageToClient( - client: Client?, message: String?, mailer: Mailer -) { - todoTask7(client, message, mailer) -} + When a val (value) or var (variable) is of a nullable type, before obtaining the + value of that value or variable, Kotlin forces you to verify that the value is not null. + + Replace the body of the below function sendMessageToClient(...) such that + it uses the same logic found in JavaCode7.sendMessageToClient(...). + Use only one 'if' expression. Declarations of Client, PersonalInfo and Mailer are given below. + + Make sure you look at the test() function above and understand the functioning of the + various operators used. Of special interest is the "?:" operator, which is commonly + referred to as the "Elvis Operator". + """, + documentation = doc7(), + references = { JavaCode7().sendMessageToClient(client, message, mailer) } +) class Client (val personalInfo: PersonalInfo?) class PersonalInfo (val email: String?) @@ -35,3 +45,12 @@ class PersonalInfo (val email: String?) interface Mailer { fun sendMessage(email: String, message: String) } + +/** + * Replace the function body of this method as described in todoTask7(). + * + * @return + */ +fun sendMessageToClient(client: Client?, message: String?, mailer: Mailer) { + todoTask7(client, message, mailer) +} diff --git a/src/i_introduction/_8_Smart_Casts/JavaCode8.java b/src/i_introduction/_8_Smart_Casts/JavaCode8.java index 4a40edc0c..8a9548270 100644 --- a/src/i_introduction/_8_Smart_Casts/JavaCode8.java +++ b/src/i_introduction/_8_Smart_Casts/JavaCode8.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 8. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode8 extends JavaCode { public int eval(Expr expr) { if (expr instanceof Num) { diff --git a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt index 1053d034b..05b3c35d6 100644 --- a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt +++ b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt @@ -3,11 +3,20 @@ package i_introduction._8_Smart_Casts import util.TODO import util.doc8 +/** + * Description of task. + */ fun todoTask8(expr: Expr): Nothing = TODO( """ Task 8 - Rewrite 'JavaCode8.eval()' in Kotlin using smart casts and 'when' expression. + The goal of this task is to replace both instances of the code "todoTask8(e)" + within the below function eval(e: Eval) with the appropriate code to perform + the same logic as found in JavCode8.eval(Expr expr). + + As show in the partial solution below in the eval(e: Eval) function, your + solution should use the Kotin "when" control structure. Additionally, make sure + to include an "else" clause. """, documentation = doc8(), references = { JavaCode8().eval(expr) }) @@ -28,7 +37,7 @@ class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Expr /** - * Replace the function body of this method as described in todoTask8(). + * MODIFY the function body of this method as described in todoTask8(). * * @param e a Expr object (such as a Num or Sum object) * @return the solution to the expression e diff --git a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt index fb171b1ae..1b365c1fc 100644 --- a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt +++ b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt @@ -3,6 +3,7 @@ package i_introduction._9_Extension_Functions import util.TODO import util.doc9 +// 'lastChar' is compiled to a static function in the class ExtensionFunctionsKt (see JavaCode9.useExtension) fun String.lastChar() = this.get(this.length - 1) // 'this' can be omitted @@ -13,18 +14,19 @@ fun use() { "abc".lastChar() } -// 'lastChar' is compiled to a static function in the class ExtensionFunctionsKt (see JavaCode9.useExtension) - +/** + * Description of task. + */ fun todoTask9(): Nothing = TODO( - """ + """ Task 9 - Implement the extension functions Int.r(), Pair.r() - to support the following manner of creating rational numbers: - 1.r(), Pair(1, 2).r() - """, - documentation = doc9(), - references = { 1.r(); Pair(1, 2).r(); RationalNumber(1, 9) }) + The goal of this task is to implement the extension functions Int.r(), Pair.r() + to support the following manner of creating rational numbers: 1.r(), Pair(1, 2).r(). + If a denominator is not provided (as in the case of Int.r()), use a denominator of 1. + """, + documentation = doc9(), + references = { 1.r(); Pair(1, 2).r(); RationalNumber(1, 9) }) /** * A class defining a RationalNumber. Given that this class is a "data" class, diff --git a/src/i_introduction/_9_Extension_Functions/JavaCode9.java b/src/i_introduction/_9_Extension_Functions/JavaCode9.java index 764be041e..62af7d5f9 100644 --- a/src/i_introduction/_9_Extension_Functions/JavaCode9.java +++ b/src/i_introduction/_9_Extension_Functions/JavaCode9.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 9. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode9 extends JavaCode { public void useExtension() { char c = ExtensionFunctionsKt.lastChar("abc"); diff --git a/src/ii_collections/_24_JavaCode.java b/src/ii_collections/JavaCode24.java similarity index 90% rename from src/ii_collections/_24_JavaCode.java rename to src/ii_collections/JavaCode24.java index 0dc0e703e..4d187171c 100644 --- a/src/ii_collections/_24_JavaCode.java +++ b/src/ii_collections/JavaCode24.java @@ -8,7 +8,11 @@ import java.util.List; import java.util.Map; -public class _24_JavaCode extends JavaCode { +/** + * Example class for task 24. + * DO NOT CHANGE THIS FILE. + */ +public class JavaCode24 extends JavaCode { public Collection doSomethingStrangeWithCollection(Collection collection) { Map> groupsByLength = Maps.newHashMap(); for (String s : collection) { diff --git a/src/ii_collections/_13_Introduction_.kt b/src/ii_collections/_13_Introduction_.kt index a0bb90f5f..5162f8ecb 100644 --- a/src/ii_collections/_13_Introduction_.kt +++ b/src/ii_collections/_13_Introduction_.kt @@ -3,7 +3,7 @@ package ii_collections import java.util.* /* - * This part of workshop was inspired by: + * This task was inspired by: * https://github.com/goldmansachs/gs-collections-kata */ @@ -18,11 +18,10 @@ fun example0(list: List) { } /** - * Description of work. + * Description of task. */ fun todoTask13(): Nothing = util.TODO( """ - Task 13 In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. diff --git a/src/ii_collections/_14_FilterMap.kt b/src/ii_collections/_14_FilterMap.kt index c547b44b6..ddb032b49 100644 --- a/src/ii_collections/_14_FilterMap.kt +++ b/src/ii_collections/_14_FilterMap.kt @@ -1,21 +1,32 @@ package ii_collections fun example1(list: List) { - // If a lambda has exactly one parameter, that parameter can be accessed as 'it' val positiveNumbers = list.filter { it > 0 } val squares = list.map { it * it } } +/** + * Description of task. + */ +fun todoTask14(): Nothing = util.TODO( + """ + Task 14 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCitiesCustomersAreFrom(): Set { // Return the set of cities the customers are from - todoCollectionTask() + todoTask14() } fun Shop.getCustomersFrom(city: City): List { // Return a list of the customers who live in the given city - todoCollectionTask() + todoTask14() } - - diff --git a/src/ii_collections/_15_AllAnyAndOtherPredicates.kt b/src/ii_collections/_15_AllAnyAndOtherPredicates.kt index 788d64bfa..225d394d7 100644 --- a/src/ii_collections/_15_AllAnyAndOtherPredicates.kt +++ b/src/ii_collections/_15_AllAnyAndOtherPredicates.kt @@ -1,39 +1,49 @@ package ii_collections fun example2(list: List) { - val isZero: (Int) -> Boolean = { it == 0 } - val hasZero: Boolean = list.any(isZero) - val allZeros: Boolean = list.all(isZero) - val numberOfZeros: Int = list.count(isZero) - val firstPositiveNumber: Int? = list.firstOrNull { it > 0 } } +/** + * Description of task. + */ +fun todoTask15(): Nothing = util.TODO( + """ + Task 15 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + + """, + references = { shop: Shop -> shop.customers } +) + fun Customer.isFrom(city: City): Boolean { // Return true if the customer is from the given city - todoCollectionTask() + todoTask15() } fun Shop.checkAllCustomersAreFrom(city: City): Boolean { // Return true if all customers are from the given city - todoCollectionTask() + todoTask15() } fun Shop.hasCustomerFrom(city: City): Boolean { // Return true if there is at least one customer from the given city - todoCollectionTask() + todoTask15() } fun Shop.countCustomersFrom(city: City): Int { // Return the number of customers from the given city - todoCollectionTask() + todoTask15() } fun Shop.findAnyCustomerFrom(city: City): Customer? { // Return a customer who lives in the given city, or null if there is none - todoCollectionTask() + todoTask15() } diff --git a/src/ii_collections/_16_FlatMap.kt b/src/ii_collections/_16_FlatMap.kt index 85523c0a5..7977dcccc 100644 --- a/src/ii_collections/_16_FlatMap.kt +++ b/src/ii_collections/_16_FlatMap.kt @@ -1,18 +1,30 @@ package ii_collections fun example() { - val result = listOf("abc", "12").flatMap { it.toList() } - result == listOf('a', 'b', 'c', '1', '2') } +/** + * Description of task. + */ +fun todoTask16(): Nothing = util.TODO( + """ + Task 16 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + val Customer.orderedProducts: Set get() { // Return all products ordered by customer - todoCollectionTask() + todoTask16() } val Shop.allOrderedProducts: Set get() { // Return all products that were ordered by at least one customer - todoCollectionTask() + todoTask16() } diff --git a/src/ii_collections/_17_MaxMin.kt b/src/ii_collections/_17_MaxMin.kt index 78b09060b..88425d008 100644 --- a/src/ii_collections/_17_MaxMin.kt +++ b/src/ii_collections/_17_MaxMin.kt @@ -5,12 +5,26 @@ fun example4() { val longestString = listOf("a", "b").maxBy { it.length } } +/** + * Description of task. + */ +fun todoTask17(): Nothing = util.TODO( + """ + Task 17 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomerWithMaximumNumberOfOrders(): Customer? { // Return a customer whose order count is the highest among all customers - todoCollectionTask() + todoTask17() } fun Customer.getMostExpensiveOrderedProduct(): Product? { // Return the most expensive product which has been ordered - todoCollectionTask() + todoTask17() } diff --git a/src/ii_collections/_18_Sort_.kt b/src/ii_collections/_18_Sort_.kt index 1bbbe4887..89385d934 100644 --- a/src/ii_collections/_18_Sort_.kt +++ b/src/ii_collections/_18_Sort_.kt @@ -2,11 +2,24 @@ package ii_collections fun example5() { val result = listOf("a", "bbb", "cc").sortedBy { it.length } - result == listOf("a", "cc", "bbb") } +/** + * Description of task. + */ +fun todoTask18(): Nothing = util.TODO( + """ + Task 18 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomersSortedByNumberOfOrders(): List { // Return a list of customers, sorted by the ascending number of orders they made - todoCollectionTask() + todoTask18() } diff --git a/src/ii_collections/_19_Sum_.kt b/src/ii_collections/_19_Sum_.kt index f7c969f23..9c2480288 100644 --- a/src/ii_collections/_19_Sum_.kt +++ b/src/ii_collections/_19_Sum_.kt @@ -4,8 +4,22 @@ fun example6() { val sum = listOf(1, 5, 3).sum() } +/** + * Description of task. + */ +fun todoTask19(): Nothing = util.TODO( + """ + Task 19 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Customer.getTotalOrderPrice(): Double { // Return the sum of prices of all products that a customer has ordered. // Note: a customer may order the same product for several times. - todoCollectionTask() + todoTask19() } diff --git a/src/ii_collections/_20_GroupBy.kt b/src/ii_collections/_20_GroupBy.kt index dd364ea93..ee8396d6b 100644 --- a/src/ii_collections/_20_GroupBy.kt +++ b/src/ii_collections/_20_GroupBy.kt @@ -2,11 +2,24 @@ package ii_collections fun example7() { val result = listOf("a", "b", "ba", "ccc", "ad").groupBy { it.length } - result == mapOf(1 to listOf("a", "b"), 2 to listOf("ba", "ad"), 3 to listOf("ccc")) } +/** + * Description of task. + */ +fun todoTask20(): Nothing = util.TODO( + """ + Task 20 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.groupCustomersByCity(): Map> { // Return a map of the customers living in each city - todoCollectionTask() + todoTask20() } diff --git a/src/ii_collections/_21_Partition_.kt b/src/ii_collections/_21_Partition_.kt index f95b0da35..80c6849af 100644 --- a/src/ii_collections/_21_Partition_.kt +++ b/src/ii_collections/_21_Partition_.kt @@ -10,7 +10,21 @@ fun example8() { negative == listOf(-4, -11) } +/** + * Description of task. + */ +fun todoTask21(): Nothing = util.TODO( + """ + Task 21 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set { // Return customers who have more undelivered orders than delivered - todoCollectionTask() + todoTask21() } diff --git a/src/ii_collections/_22_Fold_.kt b/src/ii_collections/_22_Fold_.kt index aa1a6d29c..07f9dfdc8 100644 --- a/src/ii_collections/_22_Fold_.kt +++ b/src/ii_collections/_22_Fold_.kt @@ -12,10 +12,23 @@ fun whatFoldDoes(): Int { return result } +/** + * Description of task. + */ +fun todoTask22(): Nothing = util.TODO( + """ + Task 22 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set { // Return the set of products ordered by every customer - return customers.fold(allOrderedProducts, { - orderedByAll, customer -> - todoCollectionTask() + return customers.fold(/*allOrderedProducts*/ todoTask22(), { orderedByAll, customer -> + todoTask22() }) } diff --git a/src/ii_collections/_23_CompoundTasks.kt b/src/ii_collections/_23_CompoundTasks.kt index 35bd7eb7b..78740f6cb 100644 --- a/src/ii_collections/_23_CompoundTasks.kt +++ b/src/ii_collections/_23_CompoundTasks.kt @@ -1,18 +1,32 @@ package ii_collections +/** + * Description of task. + */ +fun todoTask23(): Nothing = util.TODO( + """ + Task 23 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomersWhoOrderedProduct(product: Product): Set { // Return the set of customers who ordered the specified product - todoCollectionTask() + todoTask23() } fun Customer.getMostExpensiveDeliveredProduct(): Product? { // Return the most expensive product among all delivered products // (use the Order.isDelivered flag) - todoCollectionTask() + todoTask23() } fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int { // Return the number of times the given product was ordered. // Note: a customer may order the same product for several times. - todoCollectionTask() + todoTask23() } diff --git a/src/ii_collections/_24_ExtensionsOnCollections.kt b/src/ii_collections/_24_ExtensionsOnCollections.kt index 4311e0d64..67d817e65 100644 --- a/src/ii_collections/_24_ExtensionsOnCollections.kt +++ b/src/ii_collections/_24_ExtensionsOnCollections.kt @@ -2,13 +2,17 @@ package ii_collections import util.TODO +/** + * Description of task. + */ fun todoTask24(): Nothing = TODO( - """ - Task 24. - The function should behave the same as '_24_JavaCode.doSomethingStrangeWithCollection' + """ + Task 24 + + The function should behave the same as 'JavaCode24.doSomethingStrangeWithCollection' Replace all invocations of 'todoTask24()' with the appropriate code. - """, - references = { c: Collection -> _24_JavaCode().doSomethingStrangeWithCollection(c) } + """, + references = { c: Collection -> JavaCode24().doSomethingStrangeWithCollection(c) } ) fun doSomethingStrangeWithCollection(collection: Collection): Collection? { diff --git a/src/ii_collections/shop.kt b/src/ii_collections/shop.kt index 4d999e6ed..2aba781ce 100644 --- a/src/ii_collections/shop.kt +++ b/src/ii_collections/shop.kt @@ -1,5 +1,9 @@ package ii_collections +/** + * Pre-defined classes for tasks in ii_collections. + * DO NOT CHANGE THIS FILE. + */ data class Shop(val name: String, val customers: List) data class Customer(val name: String, val city: City, val orders: List) { diff --git a/src/ii_collections/todoUtil.kt b/src/ii_collections/todoUtil.kt deleted file mode 100644 index a497b66be..000000000 --- a/src/ii_collections/todoUtil.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ii_collections - -import util.TODO - -fun todoCollectionTask(): Nothing = TODO( - """ - Task for working with collections. - Look through the 'Shop' API; all tasks are connected with it. - Return what is described in the name and the comment. - - """, - references = { shop: Shop -> shop.customers } -) diff --git a/src/iii_conventions/MyDate.kt b/src/iii_conventions/MyDate.kt index 147cc3e78..7954cc35d 100644 --- a/src/iii_conventions/MyDate.kt +++ b/src/iii_conventions/MyDate.kt @@ -1,5 +1,10 @@ package iii_conventions +/** + * Pre-defined classes for tasks in iii_conventions + * DO NOT CHANGE THIS FILE. + */ + data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) operator fun MyDate.rangeTo(other: MyDate): DateRange = todoTask27() diff --git a/src/iii_conventions/MyDateUtil.kt b/src/iii_conventions/MyDateUtil.kt index 6594e5b3b..08cb78a9d 100644 --- a/src/iii_conventions/MyDateUtil.kt +++ b/src/iii_conventions/MyDateUtil.kt @@ -3,6 +3,11 @@ package iii_conventions import iii_conventions.TimeInterval.* import java.util.* +/** + * Pre-defined extension functions for the MyDate class for tasks in iii_conventions + * DO NOT CHANGE THIS FILE. + */ + fun MyDate.nextDay() = addTimeIntervals(DAY, 1) fun MyDate.addTimeIntervals(timeInterval: TimeInterval, number: Int): MyDate { From 263e211a98abf52bc2a32729418d4dd83672b335 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Thu, 18 Feb 2016 11:55:36 -0600 Subject: [PATCH 07/12] Started writing the task TODO descriptions so that make more sense to me. Will provide a sample for a PR to see if the original author concurs. --- .../_0_Hello_World/HelloWorld.kt | 29 ++++++++++++++++--- .../JavaToKotlinConverter.kt | 13 +++++++-- .../_2_Named_Arguments/NamedArguments.kt | 10 +++++-- .../DefaultAndNamedParams.kt | 20 ++++++++----- src/i_introduction/_4_Lambdas/Lambdas.kt | 9 ++++-- .../_5_String_Templates/StringTemplates.kt | 11 +++++-- 6 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index 70138b52a..afe2c166a 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -7,10 +7,31 @@ fun todoTask0(): Nothing = TODO( """ Introduction: - Workshop tasks are usually to change the function 'taskN' by replacing its body - (which starts out as the function invocation 'todoTaskN()'), with the correct code according to the problem. - The function 'todoTaskN()' throws an exception, so you usually have to replace that invocation with - meaningful code. + The Kotlin Koans Workshop aims to teach you Kotlin by solving a writing code + to solve problems. For each task, there is an associated unit test that + checks your solution. You can run all of the tests (check all of the work + so far) by executing the "All tests" target. + + Individual workshop tasks (this one, task0, is the first of many) generally + require you to change the function 'taskN' by completely replacing the body + of the function in order to solve the problem and allow the associated unit + test to pass. If you run the unit test for a task that is not correct, the + unit test results will be displayed. If you have not yet made any changes + to a task, the task's TODO message will be displayed and exception will be + thrown. This message will provide guidance on the task to be performed. + + In this example (this file), this means replacing the code below + + fun task0(): String { + return todoTask0() + } + + with the correct, meaningful code in order to solve the problem and allow the associated + unit test to pass, such as: + + fun task0(): String { + return "OK" + } Using 'documentation' argument you can open the related part of Kotlin online documentation. Press 'F1' (Quick Documentation) on 'doc0()', "See also" section gives you a link. diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt index b5a750492..b23363d10 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt @@ -5,9 +5,16 @@ import util.TODO fun todoTask1(collection: Collection): Nothing = TODO( """ Task 1. - Rewrite JavaCode1.task1 in Kotlin. - In IntelliJ IDEA, you can just copy-paste the code and agree to automatically convert it to Kotlin, - but only for this task! + The logic for this task exists in java class JavaCode1, which is in this same folder. + The goal of this task is to replace the body of task1(collection) with the logic + found in JavaCode1.task1(collection), but you need to re-write the logic in Kotlin. + + Note: If you copy the JavaCode1.task1(collection) logic and paste it into this + file's task1(collection) function, IntelliJ will offer to automatically convert + the code from Java to Kotlin for you. Give the automatic conversion a try! + In the future, you will want to reject the automatic conversion and manually + translate the code from Java to Kotlin so you don't cheat yourself out of + the learning opportunity. """, references = { JavaCode1().task1(collection) }) diff --git a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt index 2729d42ef..197d7d3ee 100644 --- a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt +++ b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt @@ -15,8 +15,14 @@ fun usage() { fun todoTask2(): Nothing = TODO( """ Task 2. - Implement the same logic as in 'task1' again through the library method 'joinToString()'. - Specify only two of the 'joinToString' arguments. + The logic defined in 'task1' is re-inventing the wheel. The Kotlin standard library + contains a method named 'joinToString() that can provide the exact same functionality, + if you provide the correct arguments. + + Replace the body of the function task2(collection) with code that will + return the result of collection.joinToString() with appropriate arguments + as to provide the same result as task1's JavaCode1.task1(collection). + Your solution should pass only two arguments to joinToString(). """, documentation = doc2(), references = { collection: Collection -> task1(collection); collection.joinToString() }) diff --git a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt index 11b9e179a..aad90e993 100644 --- a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt +++ b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt @@ -7,9 +7,13 @@ fun todoTask3(): Nothing = TODO( """ Task 3. Several overloads of 'JavaCode3.foo()' can be replaced with one function in Kotlin. - Change the declaration of the function 'foo' in a way that makes the code using 'foo' compile. - You have to add parameters and replace 'todoTask3()' with a real body. - Uncomment the commented code and make it compile. + + The task3() function contents must be modified to remove the line "return todoTask3()" + and un-comment the four lines starting with "return (foo("A") +". + + The goal of this task is to change the list of arguments for the function 'foo', provided + just below, and replace the function implementation such that the code in task3() will return + the String "a42b1C42D2". """, documentation = doc2(), references = { name: String -> JavaCode3().foo(name); foo(name) }) @@ -17,9 +21,9 @@ fun todoTask3(): Nothing = TODO( fun foo(name: String): String = todoTask3() fun task3(): String { - todoTask3() -// return (foo("a") + -// foo("b", number = 1) + -// foo("c", toUpperCase = true) + -// foo(name = "d", number = 2, toUpperCase = true)) + return todoTask3() + //return (foo("a") + + // foo("b", number = 1) + + // foo("c", toUpperCase = true) + + // foo(name = "d", number = 2, toUpperCase = true)) } diff --git a/src/i_introduction/_4_Lambdas/Lambdas.kt b/src/i_introduction/_4_Lambdas/Lambdas.kt index e2685400f..197445837 100644 --- a/src/i_introduction/_4_Lambdas/Lambdas.kt +++ b/src/i_introduction/_4_Lambdas/Lambdas.kt @@ -14,9 +14,12 @@ fun example() { fun todoTask4(collection: Collection): Nothing = TODO( """ Task 4. - Rewrite 'JavaCode4.task4()' in Kotlin using lambdas. - You can find the appropriate function to call on 'collection' through IntelliJ IDEA's code completion feature. - (Don't use the class 'Iterables'). + Replace the implementation of task4(collection) to provide the same logic as + found in JavaCode4.task4(collection). Your Kotlin solution should use + lambdas. + + You can find the appropriate function to call on 'collection' through + IntelliJ's code completion feature. (Don't use the class 'Iterables'). """, documentation = doc4(), references = { JavaCode4().task4(collection) }) diff --git a/src/i_introduction/_5_String_Templates/StringTemplates.kt b/src/i_introduction/_5_String_Templates/StringTemplates.kt index 13cd01234..a0daf36fb 100644 --- a/src/i_introduction/_5_String_Templates/StringTemplates.kt +++ b/src/i_introduction/_5_String_Templates/StringTemplates.kt @@ -28,9 +28,14 @@ val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" fun todoTask5(): Nothing = TODO( """ Task 5. - Copy the body of 'getPattern()' to the 'task5()' function - and rewrite it in such a way that it matches '13 JUN 1992'. - Use the 'month' variable. + The goal of this task is to replace the task5() function implementation + to return a regular expression string that can be used to match dates + formatted such as '13 JUN 1992' (excluding the quotes). + + The getPattern() function (above) is an example regular expression string + that nearly solves this task, but not quite. + + The solution of your task should use the provided 'month' variable (above). """, documentation = doc5(), references = { getPattern(); month }) From e24649e3c2ac311b05ef666c9f0d0ae3bdaefc75 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Thu, 18 Feb 2016 12:05:55 -0600 Subject: [PATCH 08/12] minor typo --- src/i_introduction/_0_Hello_World/HelloWorld.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index afe2c166a..610f71083 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -7,8 +7,8 @@ fun todoTask0(): Nothing = TODO( """ Introduction: - The Kotlin Koans Workshop aims to teach you Kotlin by solving a writing code - to solve problems. For each task, there is an associated unit test that + The Kotlin Koans Workshop aims to teach you Kotlin by writing code + to solve tasks. For each task, there is an associated unit test that checks your solution. You can run all of the tests (check all of the work so far) by executing the "All tests" target. From dfd2f7504c0258c92caf6414fd042dfbe1dec270 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Thu, 18 Feb 2016 22:56:06 -0600 Subject: [PATCH 09/12] Text for exercise 13. --- src/ii_collections/_13_Introduction_.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ii_collections/_13_Introduction_.kt b/src/ii_collections/_13_Introduction_.kt index 651a84e3a..7968348f2 100644 --- a/src/ii_collections/_13_Introduction_.kt +++ b/src/ii_collections/_13_Introduction_.kt @@ -7,8 +7,17 @@ import java.util.* * https://github.com/goldmansachs/gs-collections-kata */ -/* - * There are many operations that help to transform one collection into another, starting with 'to' +/** + * Task 13. + * + * In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. + * + * The goal of this exercise is to replace the entire body (blow) of function Shop.getSetOfCustomers() + * to return a set of all customers of Shop. + * + * This function, Shop.getSetOfCustomers(), adds a new function getSetOfCustomers() to the existing Shop class. + * You can find the Shop.kt class in the same folder as this exercise (and all of the other + * section ii_collections exercises). */ fun example0(list: List) { list.toSet() From 7e54c45122d076a290586b59c6ac15134770dde4 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Fri, 19 Feb 2016 10:08:31 -0600 Subject: [PATCH 10/12] Improved KDoc and some structure, mostly for the section i exercises. Developing an general structure to follow. --- .../_0_Hello_World/HelloWorld.kt | 13 +++++- .../_11_SAM_Conversions/SAMConversions.kt | 3 +- .../ExtensionsOnCollections.kt | 3 +- .../JavaToKotlinConverter.kt | 11 ++++- .../_2_Named_Arguments/NamedArguments.kt | 13 +++++- .../DefaultAndNamedParams.kt | 20 ++++++++- src/i_introduction/_4_Lambdas/Lambdas.kt | 13 +++++- .../_5_String_Templates/StringTemplates.kt | 38 +++++++++++++++-- .../_6_Data_Classes/DataClasses.kt | 12 +++++- .../_7_Nullable_Types/NullableTypes.kt | 3 +- .../_8_Smart_Casts/SmartCasts.kt | 33 +++++++++++---- .../ExtensionFunctions.kt | 27 ++++++++++-- src/ii_collections/_13_Introduction_.kt | 42 +++++++++++++------ 13 files changed, 193 insertions(+), 38 deletions(-) diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index 610f71083..f018f00a7 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -3,9 +3,15 @@ package i_introduction._0_Hello_World import util.TODO import util.doc0 +/** + * Description of task. + */ fun todoTask0(): Nothing = TODO( """ - Introduction: + + Task 0 + + Introduction The Kotlin Koans Workshop aims to teach you Kotlin by writing code to solve tasks. For each task, there is an associated unit test that @@ -44,6 +50,11 @@ fun todoTask0(): Nothing = TODO( references = { task0(); "OK" } ) +/** + * Replace the function body of this method as described in todoTask0(). + * + * @return the string literal "OK". + */ fun task0(): String { return todoTask0() } diff --git a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt index 03cff9ccf..dd3dec0e2 100644 --- a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt +++ b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt @@ -6,7 +6,8 @@ import java.util.* fun todoTask11(): Nothing = TODO( """ - Task 11. + Task 11 + When an object implements a SAM interface (one with a Single Abstract Method), you can pass a lambda instead. Read more about SAM conversions in the blog posts about Kotlin. diff --git a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt index 7bd11d3d9..7149897b1 100644 --- a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt +++ b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt @@ -5,7 +5,8 @@ import util.doc12 fun todoTask12(): Nothing = TODO( """ - Task 12. + Task 12 + In Kotlin standard library there are lots of extension functions that make the work with collections more convenient. Rewrite the previous example once more using an extension function 'sortedDescending'. diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt index b23363d10..7b888d30d 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt @@ -2,9 +2,13 @@ package i_introduction._1_Java_To_Kotlin_Converter import util.TODO +/** + * Description of task. + */ fun todoTask1(collection: Collection): Nothing = TODO( """ - Task 1. + Task 1 + The logic for this task exists in java class JavaCode1, which is in this same folder. The goal of this task is to replace the body of task1(collection) with the logic found in JavaCode1.task1(collection), but you need to re-write the logic in Kotlin. @@ -19,6 +23,11 @@ fun todoTask1(collection: Collection): Nothing = TODO( references = { JavaCode1().task1(collection) }) +/** + * Replace the function body of this method as described in todoTask1(). + * + * @return the string literal "OK". + */ fun task1(collection: Collection): String { todoTask1(collection) } diff --git a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt index 197d7d3ee..c895828da 100644 --- a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt +++ b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt @@ -12,9 +12,13 @@ fun usage() { bar(1, b = false) } +/** + * Description of task. + */ fun todoTask2(): Nothing = TODO( """ - Task 2. + Task 2 + The logic defined in 'task1' is re-inventing the wheel. The Kotlin standard library contains a method named 'joinToString() that can provide the exact same functionality, if you provide the correct arguments. @@ -27,7 +31,12 @@ fun todoTask2(): Nothing = TODO( documentation = doc2(), references = { collection: Collection -> task1(collection); collection.joinToString() }) +/** + * Replace the function body of this method as described in todoTask2(). + * + * @return the string literal "OK". + */ fun task2(collection: Collection): String { todoTask2() - return collection.joinToString() + //return collection.joinToString(/*some arguments*/) } diff --git a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt index aad90e993..ac5fee696 100644 --- a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt +++ b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt @@ -3,9 +3,13 @@ package i_introduction._3_Default_Arguments import util.TODO import util.doc2 +/** + * Description of task. + */ fun todoTask3(): Nothing = TODO( """ - Task 3. + Task 3 + Several overloads of 'JavaCode3.foo()' can be replaced with one function in Kotlin. The task3() function contents must be modified to remove the line "return todoTask3()" @@ -18,8 +22,20 @@ fun todoTask3(): Nothing = TODO( documentation = doc2(), references = { name: String -> JavaCode3().foo(name); foo(name) }) -fun foo(name: String): String = todoTask3() +/** + * Function to create string that contains a specified string, possibly as upper case (default is lowercase), + * followed by a specified (or default) number. If the number is not provided, the default number should be 42. + * + * Optionally, complete the KDoc, here. + * @return a string of (string, possibly upper case) with a number concatenated to it + */ +fun foo(name: String /* more arguments will go here*/): String = todoTask3() +/** + * Replace the function body of this method as described in todoTask3(). + * + * @return the string literal "OK". + */ fun task3(): String { return todoTask3() //return (foo("a") + diff --git a/src/i_introduction/_4_Lambdas/Lambdas.kt b/src/i_introduction/_4_Lambdas/Lambdas.kt index 197445837..bcd753086 100644 --- a/src/i_introduction/_4_Lambdas/Lambdas.kt +++ b/src/i_introduction/_4_Lambdas/Lambdas.kt @@ -4,16 +4,19 @@ import util.TODO import util.doc4 fun example() { - val sum = { x: Int, y: Int -> x + y } val square: (Int) -> Int = { x -> x * x } sum(1, square(2)) == 5 } +/** + * Description of task. + */ fun todoTask4(collection: Collection): Nothing = TODO( """ - Task 4. + Task 4 + Replace the implementation of task4(collection) to provide the same logic as found in JavaCode4.task4(collection). Your Kotlin solution should use lambdas. @@ -24,6 +27,12 @@ fun todoTask4(collection: Collection): Nothing = TODO( documentation = doc4(), references = { JavaCode4().task4(collection) }) +/** + * Replace the function body of this method as described in todoTask4(). + * + * @param input a collection of Int's + * @return true of any of the elements of collection are divisible by 42 + */ fun task4(collection: Collection): Boolean = todoTask4(collection) diff --git a/src/i_introduction/_5_String_Templates/StringTemplates.kt b/src/i_introduction/_5_String_Templates/StringTemplates.kt index a0daf36fb..74cb25cfd 100644 --- a/src/i_introduction/_5_String_Templates/StringTemplates.kt +++ b/src/i_introduction/_5_String_Templates/StringTemplates.kt @@ -3,14 +3,26 @@ package i_introduction._5_String_Templates import util.TODO import util.doc5 +/** + * Example function that demonstrates simple string interpolation. + */ fun example1(a: Any, b: Any) = "This is some text in which variables ($a, $b) appear." +/** + * Example function using normal Java string concatenation (not using interpolation). + */ fun example2(a: Any, b: Any) = "You can write it in a Java way as well. Like this: " + a + ", " + b + "!" +/** + * Example function demonstrates a string interpolation where the interpolation is a expression. + */ fun example3(c: Boolean, x: Int, y: Int) = "Any expression can be used: ${if (c) x else y}" +/** + * Example function using raw strings covering multiple lines with embedded string interpolation. + */ fun example4() = """ You can use raw strings to write multiline text. @@ -19,15 +31,24 @@ you don't need to escape a backslash by a backslash. String template entries (${42}) are allowed here. """ +/** + * Example function showing raw strings to create a regular expression string. + */ fun getPattern() = """\d{2}\.\d{2}\.\d{4}""" +/** + * Example function demonstrating the ability to use the getPattern() matcher to + * match a date in the getPattern() format. + */ fun example() = "13.06.1992".matches(getPattern().toRegex()) //true -val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" - +/** + * Description of task. + */ fun todoTask5(): Nothing = TODO( """ - Task 5. + Task 5 + The goal of this task is to replace the task5() function implementation to return a regular expression string that can be used to match dates formatted such as '13 JUN 1992' (excluding the quotes). @@ -40,4 +61,15 @@ fun todoTask5(): Nothing = TODO( documentation = doc5(), references = { getPattern(); month }) +/** + * A regular expression string is capable of matching the months in 3 letter uppercase. + * Use this variable in your task solution. + */ +val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" + +/** + * Replace the function body of this method as described in todoTask5(). + * + * @return A regular expression string to match dates formatted such as '13 JUN 1992' + */ fun task5(): String = todoTask5() diff --git a/src/i_introduction/_6_Data_Classes/DataClasses.kt b/src/i_introduction/_6_Data_Classes/DataClasses.kt index 474490bc8..ababbd748 100644 --- a/src/i_introduction/_6_Data_Classes/DataClasses.kt +++ b/src/i_introduction/_6_Data_Classes/DataClasses.kt @@ -5,6 +5,8 @@ import util.doc6 fun todoTask6(): Nothing = TODO( """ + Task 6 + Convert 'JavaCode6.Person' class to Kotlin. Then add a modifier `data` to the resulting class. This annotation means the compiler will generate a bunch of useful methods in this class: `equals`/`hashCode`, `toString` and some others. @@ -14,10 +16,18 @@ fun todoTask6(): Nothing = TODO( references = { JavaCode6.Person("Alice", 29) } ) +/** + * The class to create as described in todoTask6() + */ class Person +/** + * Replace the function body of this method as described in todoTask6(). + * + * @return a list of two Person objects, Alice and Bob. + */ fun task6(): List { todoTask6() - return listOf(/*Person("Alice", 29), Person("Bob", 31)*/) + //return listOf(/*Person("Alice", 29), Person("Bob", 31)*/) } diff --git a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt index 2bb3171b7..0296c292c 100644 --- a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt +++ b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt @@ -14,7 +14,8 @@ fun test() { fun todoTask7(client: Client?, message: String?, mailer: Mailer): Nothing = TODO( """ - Task 7. + Task 7 + Rewrite JavaCode7.sendMessageToClient in Kotlin, using only one 'if' expression. Declarations of Client, PersonalInfo and Mailer are given below. """, diff --git a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt index d61eccc2c..1053d034b 100644 --- a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt +++ b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt @@ -3,10 +3,36 @@ package i_introduction._8_Smart_Casts import util.TODO import util.doc8 +fun todoTask8(expr: Expr): Nothing = TODO( + """ + Task 8 + + Rewrite 'JavaCode8.eval()' in Kotlin using smart casts and 'when' expression. + """, + documentation = doc8(), + references = { JavaCode8().eval(expr) }) + +/** + * An interface for defining pieces of an expression. + */ interface Expr + +/** + * A number literal expression. + */ class Num(val value: Int) : Expr + +/** + * A sum expression, which represents the sum of the two specified Expr expression elements. + */ class Sum(val left: Expr, val right: Expr) : Expr +/** + * Replace the function body of this method as described in todoTask8(). + * + * @param e a Expr object (such as a Num or Sum object) + * @return the solution to the expression e + */ fun eval(e: Expr): Int = when (e) { is Num -> todoTask8(e) @@ -14,11 +40,4 @@ fun eval(e: Expr): Int = else -> throw IllegalArgumentException("Unknown expression") } -fun todoTask8(expr: Expr): Nothing = TODO( - """ - Task 8. - Rewrite 'JavaCode8.eval()' in Kotlin using smart casts and 'when' expression. - """, - documentation = doc8(), - references = { JavaCode8().eval(expr) }) diff --git a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt index 056170fb8..fb171b1ae 100644 --- a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt +++ b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt @@ -17,7 +17,8 @@ fun use() { fun todoTask9(): Nothing = TODO( """ - Task 9. + Task 9 + Implement the extension functions Int.r(), Pair.r() to support the following manner of creating rational numbers: 1.r(), Pair(1, 2).r() @@ -25,9 +26,29 @@ fun todoTask9(): Nothing = TODO( documentation = doc9(), references = { 1.r(); Pair(1, 2).r(); RationalNumber(1, 9) }) +/** + * A class defining a RationalNumber. Given that this class is a "data" class, + * .toString(), .equals(), and .hashcode() will be created automatically for this class. + */ data class RationalNumber(val numerator: Int, val denominator: Int) +/** + * Replace the function body of this method as described in todoTask9(). + * + * Extend the class Int to add a function r() which returns a RationalNumber + * with the numerator having the Int's value and a denominator being 1. + * + * @return the solution to the expression e + */ fun Int.r(): RationalNumber = todoTask9() -fun Pair.r(): RationalNumber = todoTask9() - +/** + * Replace the function body of this method as described in todoTask9(). + * + * Extend the class Pair of Int, int to add a function r() which returns a RationalNumber + * setting the numerator to the Pair's .first value and setting the denominator + * to the Pair's .second value. + * + * @return the solution to the expression e + */ +fun Pair.r(): RationalNumber = todoTask9() diff --git a/src/ii_collections/_13_Introduction_.kt b/src/ii_collections/_13_Introduction_.kt index 7968348f2..a0bb90f5f 100644 --- a/src/ii_collections/_13_Introduction_.kt +++ b/src/ii_collections/_13_Introduction_.kt @@ -8,16 +8,7 @@ import java.util.* */ /** - * Task 13. - * - * In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. - * - * The goal of this exercise is to replace the entire body (blow) of function Shop.getSetOfCustomers() - * to return a set of all customers of Shop. - * - * This function, Shop.getSetOfCustomers(), adds a new function getSetOfCustomers() to the existing Shop class. - * You can find the Shop.kt class in the same folder as this exercise (and all of the other - * section ii_collections exercises). + * Example function demonstrating conversion of a list to a set. */ fun example0(list: List) { list.toSet() @@ -26,9 +17,34 @@ fun example0(list: List) { list.to(set) } +/** + * Description of work. + */ +fun todoTask13(): Nothing = util.TODO( + """ + + Task 13 + + In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. + + The goal of this exercise is to replace the entire body (blow) of function Shop.getSetOfCustomers() + to return a set of all customers of Shop. + + This function, Shop.getSetOfCustomers(), adds a new function getSetOfCustomers() to the existing Shop class. + You can find the Shop.kt class in the same folder as this exercise (and all of the other + section ii_collections exercises). + """, + references = { shop: Shop -> shop.customers } +) + +/** + * Augment the Shop class, obtaining the set of customers. + * + * @return a set (not a list) containing all the customers of this shop + */ fun Shop.getSetOfCustomers(): Set { - // Return a set containing all the customers of this shop - todoCollectionTask() -// return this.customers + // + todoTask13() + //return this.customers } From 98c1ec81f0e767580699a889a202e9f52dd9d828 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Fri, 19 Feb 2016 10:21:54 -0600 Subject: [PATCH 11/12] Improved JavaDoc and layout of classes. --- .../_10_Object_Expressions/ObjectExpressions.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt index 88b138aec..4697cb518 100644 --- a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt +++ b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt @@ -6,7 +6,8 @@ import java.util.* fun todoTask10(): Nothing = TODO( """ - Task 10. + Task 10 + Read about object expressions that play the same role in Kotlin as anonymous classes do in Java. Add an object expression that provides a comparator to sort a list in a descending order using java.util.Collections class. @@ -16,6 +17,14 @@ fun todoTask10(): Nothing = TODO( documentation = doc10() ) +/** + * MODIFY the function body of this method as described in todoTask10(). + * The ideal solution to this will be to replace the text of "todoTask10()" in the function body + * with an inline object expression that creates a "Comparator of Int" object that overrides the + * compare(l: Int, r: Int) function to provide a descending sort. + * + * @return a List of Int where the list is a pre-specified list of Int's sorted in descending order. + */ fun task10(): List { val arrayList = arrayListOf(1, 5, 2) Collections.sort(arrayList, todoTask10()) From 2bdacc1f47f42f592f308d54520e2d0ebdf6ccb8 Mon Sep 17 00:00:00 2001 From: Kevin Dorff Date: Tue, 10 May 2016 15:37:22 -0500 Subject: [PATCH 12/12] Revised test --- .gitignore | 5 +- build.gradle | 36 ++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53638 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 160 ++++++++++++++++++ gradlew.bat | 90 ++++++++++ kotlin-koans.iml | 6 +- .../_0_Hello_World/HelloWorld.kt | 9 +- .../ObjectExpressions.kt | 20 ++- .../_11_SAM_Conversions/SAMConversions.kt | 39 ++++- .../ExtensionsOnCollections.kt | 30 +++- .../JavaCode1.java | 4 + .../JavaToKotlinConverter.kt | 11 +- .../_2_Named_Arguments/NamedArguments.kt | 13 +- .../DefaultAndNamedParams.kt | 13 +- .../_3_Default_Arguments/JavaCode3.java | 4 + src/i_introduction/_4_Lambdas/JavaCode4.java | 4 + src/i_introduction/_4_Lambdas/Lambdas.kt | 8 +- .../_5_String_Templates/StringTemplates.kt | 8 +- .../_6_Data_Classes/DataClasses.kt | 30 ++-- .../_6_Data_Classes/JavaCode6.java | 4 + .../_7_Nullable_Types/JavaCode7.java | 4 + .../_7_Nullable_Types/NullableTypes.kt | 43 +++-- .../_8_Smart_Casts/JavaCode8.java | 4 + .../_8_Smart_Casts/SmartCasts.kt | 13 +- .../ExtensionFunctions.kt | 20 ++- .../_9_Extension_Functions/JavaCode9.java | 4 + .../{_24_JavaCode.java => JavaCode24.java} | 6 +- src/ii_collections/_13_Introduction_.kt | 5 +- src/ii_collections/_14_FilterMap.kt | 21 ++- .../_15_AllAnyAndOtherPredicates.kt | 30 ++-- src/ii_collections/_16_FlatMap.kt | 20 ++- src/ii_collections/_17_MaxMin.kt | 18 +- src/ii_collections/_18_Sort_.kt | 17 +- src/ii_collections/_19_Sum_.kt | 16 +- src/ii_collections/_20_GroupBy.kt | 17 +- src/ii_collections/_21_Partition_.kt | 16 +- src/ii_collections/_22_Fold_.kt | 19 ++- src/ii_collections/_23_CompoundTasks.kt | 20 ++- .../_24_ExtensionsOnCollections.kt | 14 +- src/ii_collections/shop.kt | 4 + src/ii_collections/todoUtil.kt | 13 -- src/iii_conventions/MyDate.kt | 5 + src/iii_conventions/MyDateUtil.kt | 5 + 44 files changed, 691 insertions(+), 143 deletions(-) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat rename src/ii_collections/{_24_JavaCode.java => JavaCode24.java} (90%) delete mode 100644 src/ii_collections/todoUtil.kt diff --git a/.gitignore b/.gitignore index 9b5695c52..c9ee75a04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -out -workspace.xml \ No newline at end of file +out/ +build/ +workspace.xml diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..f12c48c7c --- /dev/null +++ b/build.gradle @@ -0,0 +1,36 @@ +group 'org.kotlinlang' +version '1.0-SNAPSHOT' + +buildscript { + // So we can use $kotlin_version in specifying dependencies. + ext.kotlin_version = '1.0.0' + + repositories { + mavenCentral() + } + + dependencies { + // Kotlin support for Gradle + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + // Kotlin library and reflection stuff + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + // Kotlin testing + testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" +} + +// For ease of startup +task wrapper(type: Wrapper) { + gradleVersion = '2.11' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5ccda13e9cb94678ba179b32452cf3d60dc36353 GIT binary patch literal 53638 zcmafaW0a=B^559DjdyI@wr$%scWm3Xy<^+Pj_sKpY&N+!|K#4>Bz;ajPk*RBjZ;RV75EK*;j-;d{(BB5~-#>pF^k0$_Qx&35mhPeng zP5V`%P1S)(UiPcRczm!G=Nud7!TH`9_!bdReTmO0lO(Zfn zfMqP~+s2VTE#?sl=$9e(CuBOAH2n}=c4idsipAKKQIO=pSsMiScd0TvKs^d1xMCym`IZxg&Xd3ii(^3$M#K)myV2qM z{o5&z?1rtP{gnX6zegV2 z$03xe*g2pGA^BqfBE}XDD-CN?H&?w?kE0wG3~``ie?T+IRmsT;*cpLZ)OnaXBtFzX zMcc97L%tQqGz+E@0i)gy&0g_7PV@3~zaE{g-2zQ|T9d>EL&JiD+E8t?H^#-Hv8!mV z-r%F^bl8v}j19B}`M^zJ>hD+Tg?Fg%no2GYmKkbR`2=|`@o`~1&VbKUJ@ZSojd|ihP|{9&8F<6 zcAvtwl6yo{Js7UQ{l~MjA7Rz8(sYEcRXX(n*(Mp75OBEijo(25zwC)pc=#%f_xV93 z`&d+DI*TAtd3KBd(^964h=X;uks5gf3MU}cR#X_wnk6_crcAVLsTV2SzsGM$h~aq~ z6oX|v?jSq%txd-SHwHy`7x4*jJqH^;0*1`Sztp*aYi4tRMBD|Ryyl%F{IC{(=Y{Y5 zhSQPvqZN~4uvCi*h``E|llqjfc4rnRQPs3@(MnB9jto`dtz!CB-ojaReyN}7BwMp! zU@jEkH2hS%NUB|wE0d;=hS4^M^dTz`r=^`7LNsQkU26nlt4o?Ki13cwDXkQH+)w#uNVQo2o@pEJOAZV3Uf z8WWqpN|lDuGdkokHkKLwmo@qCdV6}M=~DGq+P3}@$$yqQssE{3|BxxM*q?tD3oiW6 z^!W)Iau1CDv+;dTH4Lbb;*)+mGrKg;g)4tHB;h~=3QsCF)I|E{`=jp;ArQuy&zUzA zlz$NoIhz7h@;Sw+#%u~;!w56XV3JkGLOHaVlvs1eSSck_-2#zs%EynXvEnsUsO3{@ z=2B!(Gdra;oKm@A@~#LeoDFC2&V->;dgCP}x`Qm{yZA&ULeNnWvNIGzcgjx2?Rx#m z_I4lu^j~)hR_VQ?`&Yk|{^}Rqf8MFY|1el;E@sY>4t8d;4h}YMj{n$ntcs2Tju6_n zc%t6wvvLifwar=wOlL#;T5V}~s_KU-6cMz7X&7`JeYdHW?WaaBnYH!e82^(58{d#J z&3H)nMCXi0pUcVg^sRt^KZxdFRj|_ZglEw{Ri0EN6_laAxbE8zB=H8KgU;Xtpk5?z zC2?g-xj`9d8MtJf-!H#~s0}tJ>Ksa+7KP;J(%hHwUBewO);ZZ&ry8oYXI%5+YQgNQeyS*ViKL>Yy2`MsK? zB7Y$Zk@YAy#-Kwyo5KSK$lcvER(OV>qrW1VXPo7Ih%dEJZ<|5sEmeC)do0(dJ;7Fc z#v{T#df-92-StcUzRO7OjZ?g-Ik?9eGEDWsUL(f2jUmS9_ajH?wV&{0Aj)-0IP36} z4!4}CW2D{v(ZmPjB$#&;fps(Hvph<>^IORq|0^=eDhYiA%W5!rM_K_y(bsu@*)m3P++I=?)h!HA@uUc{zxJ0ibvxU%Ke8OQ+KDRndS#XDA4T zto-I$zC-%q0v8ZL`!Z;MMK0`Irsn?gZwiTbsJAr&4g~c3FEn8J&tfX(X=3ZOyEhpng#DDSOc6XLr%uGtB2|0=_Au$%heH3&*ID*ZPhs8iJyw{$c z)`ySqPVndS_Rnv2f$xtMcp${1WTLjhW)S3(;l*PK4#cXz09;@vNj*6?$Q>%5jIboV2fgAyb?c(W#K{@rj#6OKF&J#QQ9 zboB4HeJ?hXrHp)H9rx@Rta#*PdvkHJ<<2Asc#ClKA;st5qadT0NZHEA111(&qsaLb zkeTH_h(yr92XkyqqslQgTo(|RN$hhQ*IL7<12W?$+q6R2jtnWadKrIyeA>bj=;9mM zaPK&0{1#djnc2d@4fR{7K872i*IvH0mK#eqf4=iu8F5=2P#fG-GBZ|`J2MyJ(^^*5 z$tQaAS;Y(2k!j10=adaH9^!>^k+iBMVWD9#+F=&Q(yZ5NVJ>A}t>1R@32TZ0JTMas z%42sos08y0NMkb$BsDnQo8nVhd#ksaq8UyBjAO1FHRfW*u`ojc^y3)=(f&PTM`k@F zMoZFS>HCeNX1c@G{<=x`IQr>{11kPK#7AZWy_q&GQYwk*t|uTE9H*TVi|@g8P95wBlwf};`RANPqd z@rf3B=~Q8%Wgl5i2t$W?Ns1WgZ1t0sFVJF73Rc!d@X---3W@e+Dbvpj;l~8r`F9Sc zKd8G>dR2>61(|Bw&XdTlG}}fnu~6{2xsz6Efmc;nRDupK!KI-q=^*h{`b$W=VCBWe|mhK3YN$PO28ZaH@2V;Nbgpwl8Fig6xxkWN7UhWjM%G z<^O`4VX45jgsIIO-R$7F$`uCa6O>(WOZ>i>Gor3X^yySAwSB;0*X`pWy<(jya4!HO zYDvrso1n6V3G<>TnrSv{+unA;cSAWuH!9k`z#^j306@cy?0{jP-p4NUCSRP<_wNHG6^axCR zMECIg`Vz^ja7F}a@eRp)X%2>D5;2HR8kL?^&npLgqU9<#pY)7;V<(`jjbZL5j>8!3 zLF`9aB2GDCO#Q|6P6_x!My0QO@k9M-2f2-|AF33CZ))eL<;OOi;76DiE|3}S)dWs znAG6VC4EKe9MedTp6d0%J409Iu^T12e)n(N?^s^DlM+cUS5h4SEqq4NjK+%c6KPr%EUeiN1v_&WkfIr9Do+Q{2=Ap?{FS|6D&*Au8=PW}~Rt~+3EJpNK(;7R!k z%&9kpQ;0c6#VF?+W0D~mUp&bc96HK5g~~9ch$%)z5Z@Wq`!V zLK|>+`YZuZ?H_46y1zmqL~@TLC0lsiaSZzy-7!W4m2VsY*S@u zW}zeWzr7N1oL)qzKa8J;I?@RxaLmPEowX2BlSD2hnYW}WyvQ$FK%;PTPl4|ftNkVB-&5MvFWP;Yx2`zBS8o_7QfB#MLS!;fRlOU$t z%iCYD|0>%`e)v|$e?d9+U&O=spApZ$`@51x_J1P_|AafS>X$C4YG{A#vQ3BFr-~J& zrNf{=tbRuZqT9ky&r6pgk}1*#cgZ;@Dve26=%&Eu887bdn12Lfj~ET zV5IjzrKV8JQNK?MSC0X660d`nnw55z4~eTJnzix7U0S>nhTZ)+f}uOuozz3gz(ZMj zYs1J$Dy2L`8>*UTBLriGUKTY410qsp)+K??D$!Mr_7=C*Ec`Q^X$b{2>?+1_7Ka7f zd2{rtBr%g2PbF_kHb0yF5ypGWmJ(ftYt&YJ_ynIc9aWyTF~dXbrN!iNKZ_(_u>s(I z2*X|;szamJz{2dl(P2I;%foJLF51)WO50&h!EXP{5|gzy^b(4+nn<;j$NCt?UP_cL zEU1-XN`f*1Gl}Fht4&Pn5xo)Ma$kZyBt2qju1yuSTysFNG^pi%vvU=hqPhQ-Cpl`! zSt+cpY2TVYkvTflcqwieu*7MHVIVSQSivl850|@O!h+yWxUM;@nYhP*y8k$MMX>^Vzs= zqdctvR$e(}|H4~b=2=Dszgy^xqjuY6alG6sJ&J~#*>K6$Dl|Pf&Y~sh$@07*amh*T z_wIHl$BbJK)7B2V0P=;_iRjD@X9TGO4h+mK7laU=qy zmy|9(xNb7=49$9mLHL1jK#9O+t+CJ;3IDtkK4I0H_{C zMZMuxRK)|E4155y$!RGIcAVHwpQK;2>ssTa8^Y0_8A*sk}a3gZD5Il8pD}4GR1;_@6*%Y^za=h*V zu5|)zJI%1zR^G!TWc5ioA9xPwtfcqbul(N(r%J#%9D&&zkuV4xj0VXfymNc> zXa2;S1FBKAgNu0n(1&iyT^XFajIG9aRR$`eVEejDBr(KR%NpK5M4^XoP2r^{5!QII z=({hFfVnU7w+W~i2BF-t(|u6~1LVuM`kfH|v*hfA+X=o<%CrInhQid%%Pn@a#zcB{ z^o7+|r0o0_JFn1}AG;)N{NfS8LI&fnSX*e@KcPEe6OV6U-=oiHjXw;D&;Ui32rr=^ zeI$9fsv_3NJU2zXlqE0HwYjRkF^(*=dmpb#bAGI-iGF7mAYYyfIwG8Z;|+==LK(t5 z=LlsTsJKX@6c-VZ+w{_}$Z-T9i*wIN-d5;hIXRENbV;h#&hIG%+U%XScSeP_M`St_ zW*|AQg18~yjh5x7_;)cRFt3fbcf={_ULTh(DfkEq%mVu$EcpgdN}OOAmZBAc2`mzY-_S)k2M=`*UssiSqyy?xVKo=MbGuFS6XRVx2Djkn-AEcIgSQvVAF zfc`p)g#X!O$~sv5mqQqq{Nb>uh&I-rK1N;7H0mguftm{=rV;MIL=kQZjZ6q_PVrHj zl3gcbRfrb*Cn_KeXJnZ06ZEq<=ClnSMYA~}FVE$cEB}!?;QRYG{63OGvgE!wNV+3U z&{hS7QV6Z6UL=q3cB&(hP~yB{WPCY<2lhJj&?d_e^Y$rRxGZ6c1-iXZdJo;c_d!-U7axq4RNZah&+JQde7@OZf;An_X_9 z;&omnq3Vb#o!)t8oY6U5q9V@2gy>2y91VhMexMd;_^7e(hpL;mFYj^Ms7`BGG-h5q zK%Q)s!X(^S6HkOCW#c?l`3P0RZTN{hKdkGb8X4YR z`Q>!^OJIGZmB9N^htzi$VRW^@G$NKIzjI7BcO^B;MhbLW^3e|uMwSeIP|Zs8x`x5{ zeL>F`oNu5Vl>~_AJi)C>nNM&Fhx!XDe?wEG`x8B+)*W-m%b^U0@g03H+h=q%dPTeRj5TVw;2U@yvqaijNc-yOdE0sdE!oOQm8LWcwb zvPS>{qWI4usc7VCDdgf}W@r3gcXz7#y9ura^7ra0x>qu1l*@W+h%sd>?FNRF3P1|= zZbh`V{x`M!W`~UpsRQ+GS9kSrxHBr?)ej6Lo3uG_H zt<~-{2g_b|`=9T*FAm_G(f&ius6m395oJ6G`(dhHA`zwVV}R^Nn`tA;NVLNfW^Hs9AWg~k&`b-e$0W2lP^ww;)sP<7yihp z>9;T5*j*ExLF8eyk}p*_!`MPt{qUWd(sve|0cQ3d(s?$MugrLsb=F0U=Z>3QNKb~xQiuR|wEjrfU z+ zcuJ(5)AJajIhR#qf<4<5IRe+i9ySGsxMdU+)OPV`G3%LV?Y&rh6sS?3B$pjiq)Tp> z7}ce0mnh`&H|iOQ1Gb0uRJX^%qxxmUZ=$BivYy9aXQU8){*Y>2BZ9l-I9bd_Cb^=f%{ zmiWjT=K4anJYD<1{Uv&xtE!H~-PThqR_6Xk6JGHrBqqGQKH?tN;Y{bu{vvRtU8UsW z(llN(rJar!7lEQU6Og+kl{x(`fYfSOcD-A(iW^ymja51?n_e*XdzPU6$KMxkZcJK` zJda&tRCKm42Sb&*e*F*DU{{L^KV#|D}q2fz@SkOgh!`P8>llzU)uQmjI>w+ojmYH8DUYso9od6~Q z;4`O#*N}<@^x4|bmeg+8V z#FzePXp9j-$0LxzgYFM1c8Sfb>{Ps6=4+{2OSxf{aUT^)eNk{Itv=A6UH-NWAk zbZSac^MG}0zqXgc*4EY)bsLAv>)zhim))%o>em2scaCgRq(g@OFY^hHi1@3ZH?0RyOJwn?nXpt|EK zC7C$;BzYJGJRO=@=P;0~`pP4Xo506(sb4GFpOQ5bebMSZAyb9jZC0r^8@4#6@ zN1(csX24ZP&hIq|Wy)DqPP#K}FbsE(!eN*bF(bfPb223XmHFutE}fA+X)(F@1;%yH zk1$?!wEHfeYJqddk074aDxPkEX(5=S7o}DSWlDe=WSG+rui7V8D&L^N{0Eoj{52^F znZVhj=S5qnyRaUmgH@o~<9q*IvbmW>Rd7#O2emhnur1TYHlW(mYhdexX!Kp=0`-Hf z!SuIx<@XNREb6dlpjM~u!9pOrQ+I)gFWa|Hkzd!e`}FK$)l*+RDtXAr6jhG2g7^(& zS#O*-Cp0BefE>7adHa;nq#sma9#q??1@@V^r}|p+lu`QF`_*sO?>*YP{B-=C+6Oin z!SJy*7wjAkT`h%`ZcC$OE7=@uwGN%GB0FWRvSBmnm)%cUkp!1DR;?)JH7?*wZ@)ch ztfdr-ApH==lQ4l~Yl$%t!PHH1zsCEliydl^cX*{BK4Bwl&; zJx=3~4w4j}SylJRk^&{HD^znI={^oh5?NHd2d1H_jh;qml_=-(7Web$GL)idHHN(A!9a;z33kdFv;y;z&UxF)%AeR4wcX*@Dd`TbAx)+%j<6 z7<;WW)i;(q6bO+@sG3~TKii83s*~Hn6H8`K%#A+`Q1P~mlINmHgw45@lEne3jJ17P zZh;hz8;0&UQZnl9P!Lf~yjjLORIsk_d{Ii_YQEQ%aR@^GyDuwHeJ}ihLhY_)AdGHg zhFSd5RVy#l^BEgfnr@AE7^Ft-Psf|qCcFrebXbLZme1nEm7HrU@Z!uVjNSN%-b3af z7qZUkhP=v4wwvDaxCFp{JDGra@o7D-oGogt?x{pR{FrAwaZ3wj-{uzAmKMqt2&XDh ze1?H=?{VpKPF0hrr{{u7|~@}3h?pBn#AQcs3}tn(ld_+ zqfibU?{%p}DH=QlEiIzWnzdn6rJbfL=v^pfoHN&UQod6xHxUy033s>27|xW2!iAIt zRMTOs@W ztWJX=uuAhKLfXd7Xin6FH>PB>VwRXl4F(L&>kRYIq}#{TDkrvZbm;tKBkE*r5OWJg ze$J|ti%j?`N}(DXhW1uC21DuyX5QdoV|XVRKtk$BO(Z z!r{tOk9B6*;^R}Wbbq0T8n^5h^;eF6;UW=V@uJ%kc4}Rrjhc5Tf!euvA3@BRs^etQ zTvbc9z2dp|@0!7GwZ0$+)pv^B;=9vdN9L&x2Rdrsyn9jC@59nFpfyrwenQ6#59tMB zbxBZXtF3No6)I@oO_M(F>|pLUp*fD=$lgK+TWWnF{Y4KN@A9dy_j=>>H)bJ&9@U?0 z-(8chcmAoj#}1M%`IE2FD2NrGL|<^YKj#l)D>KZ*V{He`yRLk7DCyeO=TFkfPHhf4 z5|@pgWJH9l&&_3*O#Cv^2K;f;@ziVC$~X*WqHORbe`PI1`*AOp(@oD_R*Ppc4F~vr z9G|JbXZs4my(CQT9e&C3$OA;PJWyy4wV#ru3Lb_&?NV=uGtP(tB#tEv(Qle)=vYk4 z_2_mBJ16D~hlCE6WQ@rh*$0;0&BE>^ZBhShUv^)=m zh*`f{oNA2|<*ho;N|huZ(U*r;ql>cY94QNLs#2$sN*iX*R7#Fg49~f#84)>us9SYM z8@F_Gk7c(OJeB~Djj!~LB-D6=PR&OfBRuiHS{2D`rca#zAoHu@6%XY^snOZ`Pe{_!OQ!llj z(vX$_-^MCRyV?lYDAQT~lA!9eCC;-*J^9Q>{{U0rLXqySl({*)}PenaIaXeRr|kqvv>R|%fO}InxZ~VOfnRIr--(j~C;1hh zD*m7*eDdTK57}_}{UZVr*caO06;H%}gb&))zDo;^G(P1=7mFuZQv=`$A05DWSR}Yj zz8ZepdD~0R;-d)k(Aaw^lnN|J!2D4^-n)OM?w|7@Y0vZ25q!@+PfU=QX}7$BJdUxj zdp&-GJ8nv;ER~ac$O(wj00ZDTP2$N1C|hle^8}xhQXJzom#1|R)U+mBjk=y|-iY!q z#0nokHC_*isP`5cvj%!?Ooi>_cRXO*x2mYm_L4oGpC8HQUTzGFv%d@~!e9%SuPlpj z{+Ksf!<#*U0k`m-)`OUxR@_@EbDXB26M2=)J>8l$jiL)8FEthy`J}zQnE^>+4bdYbk){#$Ae;MY{p}>NLh`~ z%kMR=#_oMtE+mw(R^lc%8j^q`_5VQoF zdsg1*Je;fRn1)0{M!eq2XZ@bD?`m0tNP`8$^5dk~b~$&7lVu};YMm&UAe?26^F0+) zQ#M%s*_)O?gbLFsB}@In8QSnvrhwl_hj}eQ#Dg$d!fkT2Bpahm|HITp`fg&o5lq3; zIhE8y*U&7&SRM}j)U^6QBW7u5?zLzorLgcxF7fF%IbQblWkITm@JWRX`9%5#5AFm3 z?qVtRQf9en5;}Gc7cg0+4t*tiyZN8fM3#oZS>Ty_0y23t91&jU(~bv4MIo%-OCEV7 zdAXNo%|)mFOFE?n)-|vp2eT+$NF-%FZ)ZJTBUbSk05b~^3%&W1_W4{Qt~t9odd5QM z`Gc4TcYEVcVve}3n#zgRpAFF!s0U_MxOA0)q)trM{)o)oLggxismMzF;OEj^Q5kik z8U%h>Fvp&;ufkk5TM9Wz*OYP7p7KApanC7QeD2)v0iO3c8vKIb$02BzG$mvFyE6cnmvjQ_D+w=-K2%xW?Oqod6 zB7Pl(^a*8csuMhcKhn@tK~z-am+?uA#K$Hy!*QpDIFp;!7GbNxPhhS)*SU3=9qWo2 zlrRVt`EqCX$@&P93(B0audju)9=r1FK+l*9&iT1wn$MJGx^nFLKC#uj7$2`SLY%5e z5ZVhH0FiQ_$qNYobPI9S))@hsvAm~!l+P2Ld$W0r?(zDtGFM~l&o`%k#PGW1`c9uT z>??a$mVcqFF?ro?X7rTop_TB;- z-W57af`0;N3i0G!+UI{3p4KevSam`(@cxay(;!sWX!1I>p<3VQ>!lK81nJ%b+ zaC{l?Bb+#xHQjN8jlax_iYvts#}cHKzm8Gms}{A*qiP!gaY6SXmN-qK^hnk2{Y7k zpFB~8I{Ao7f!d|!hCoNV8t=r5DH_O%vQ-`O3Ij(>ItU^Rdpg~c^&*XyHm`-oj#j$< zs@56Irzq;y_xL50pG(TO$lB~^%`SH3y71v^^PHT<)Fe!in!K<@`4L#QKo1UWCIb=t zsSrf|{N{PF6+M&c%}mwiq0W$O579{QuZikvDg70zm`~43QGO-r5Pk{^lf0W@cj{ei)J8*ALTs_Cpj#}WC zi~dg`S)L3Gmwe@?Qcu2X?ANb%K6(0%i&Xuf^x$WIkRrJ)2)z*Dm7R3N4yu5;J@DJ% z7QO8HEGp|}R620#Xlf#k_WQ$EfvsI1ws3xNbliM)pTUuFnkB3bYRZLSJ}s3GFL*ww zxCqqp2--kYmf9t(woiP*gSs97*`>%XM1f~pqciQDv}yl|R>f0=Y3Ec#{H^n0b*cUf zT?z`b6|cYwVav`F|FlPc8)QB8n0b#WsS^%EKR8`vz8C8L6UjM}H(wCJX}}#Cy?6)l zat}6oCqnMj-)GcV)Q|4apKY~;bkGOsbd9*rK}hyiVwg7);0g8OkJUmj^nu#&Wb_FS z5;q1k>;crXcO>Jx1@HJmgwwCUFnFk!9`{KFcT?=lYbe}3PuIMZW7h5sWj$Zr^BlSB z%4zYVvbVE{aarM@2NJO7Gw;HP>m9}(Upz3!lm*cu2Wc%DVucXK>X1q?uj8(HJQ3!V zH5Zbg+|b!G7#Cs98{?Lz)=b?9ouh9*RPDS7)fF4rXSx;gGFo9;pdPe&uUvoVIw}%D(mbE03&j@<0#ahS#Y}6C zzg;$sg8j!*_paDJnNWjA0=H@ZrE9_p`T0i5lkMTLb2za(w`2TOvGGEQGJny0X}+=G zIc^9V2J;y%k<5M`x8M=}etGa@Bj}g|W?6U1l0!fE*BF9O zF}^F8q0pPw3rnD`Cu$zYPicTy1ByF1g1U6(!G#XLV)}q zAn5=>`!7;UzfkT=LZNd~RiuDfibAh->%HQ=!8V?TnQw$@@{!w_O(zDwgk8>;;Hm+8 zl#60Sp%qZH%iosK?uTL>B>OK|n=X$T9`1z&gVyfS11B_L)WxTSP-; z**~VIOErBGO|wcRK?Z##|U}R@;6~V7UR}1!CHv0ERDFhD zS$yjwLc7+Zqang$sdZ(wtE9>m%h$g6=5H?YkXxGFY|&C@Bekzio2iik-la8wIYYD? zd07T!tEv{~#%ZUUkyPkHnTeUT;?8!xt5DA(X<25(AnGPQoq;pz{QFozFr->fl}l7KL=wwU0c$$0u6|Tn_0ut2k%t6$hP)9U8FA)$YI>KADk7I00%L1I zYcZ~iyUCV8VSB-!$*VTizC66F^K@f~Y^$AZX_?R=SDnRP}HCj$}I|n5d4*6S5u5RX2efSh`aD zC&D6%JG$k1@||c5Ek6vkOqA2j_uxgC>~=dUOb!t z)k|iO1Ez1pL-pzrsG4hNmHDd15X9VKIwubXrwo6&@`@dHvzzuz84AM>0`s;8f~^=J zZ-p?3-Hb z(WDr+^=6inA%7u{Z#8R-&)(D^n&^RbVqjksyK+cLGE`Td?i{WWKzBukjHI{d0YW$L zyP0A~m8zHpd!;NwkYLNY^aUAtn~9FOib#_*o((EbtnS?Eao=%w;f3qjtYqG!g$ZI3`1M80Xw1433 zYDuT%h~5S7xgT!7BmQdwUvjmbZ0T*t0K%+9$P9QPGmyEzXEd?WB=Pso;#S*e+%jtxnvqYOP;|Zfp=h!2kf%MR+7= zqGGk}Lgx^XfkhZ2xVInt_k*Agcds+D?9E$t@BvrZuf4Cmw{L!9J|Dka5Cvcidr=;a z==`^l2XsOJKXd)J(M7QlAPV>GwK~V*+rb^{2^|m*@jWe&&^s6+rSYDQ^n_H848ghf z=!SeuImw26j-NhEJ^LGci2@NPUqzZ8j57Vm05goac}pbcX)}pAEqx_0{oc6 z8=P1J$q!$?Kn76z(ZIe`fees$sX%?yQws0*twdd+*Ow1p;cyCShuLpwlJ9MVd(cIg zd+6kQu!kRSK^ZSYME;?KkoTyctbzGd6?==g_}DksIQZxIc{ObIb|s5xX-)2y6ESs$W_f{ivzGA^!rv!~r{nB%Cs92! zXKk}PxU;-A98m+b+z6F5D3dnrRIuE_ACKCfjd-^`g|YBr=)6-s`VJpTJ3e+`EkKEw^^iEn=}HPap7lN6VM~4Fn$X zutC%X2g+Hcm1c95+mf}-GN=fN`3h;OEzTc`$%qs~2*Wfado zPzd<{tWY>h!$b`Jnc73EI=}cSth`#km4MnUlg+@_MUIxdOhZSKN!zt%hJ|sxRQADk zFUbNBn_vbW%%oMMYqxL88&xk7gJTD!|rdWQ_?n zcG*h4WcftfU7B*^ zyY;4$6>n_*L)?0fo}9@|C@S7{4d^Xh>|vOp5wG(Zj!RjtDxCHSve@dHdvl9c7BjTt zIC$wdXJZa(ys>*f2KSn+F}3)p-H*(uM;l~DN0mvWTB$P3 z9XjJ@R28*M3U$A7Ej1-T2Hh0_%c#3^9?yo^x6^+>DLZe~#MnLu0&OP8>q;|((mR>R zJ5cfFwZus71_JM?CeQoIW7#+QSWIP;)}&MK$|`$1RpSAHhAU^P*K_W9S6MzDu-kVT zwS%Q3g^RFVH76n$0&tc#C0D&$Z+A zV)?!i&p;#EX1K%24(pd_RiUjSdzbcw@h>av$Iixg@X}!=hrvzbITP@5uTpmfYx5M$ zw!ElwFZ1IsSS{^@hZh-XZWfVM-KSj3Q?*2R5HuMal(y6{efA5~ z1T&LnfJ0_jz-Suospc$;Ad73` zU*~dB@=2(oS@R*|EEY{oBTPB27Hv@+i<{SaOLxumq#~@R6R&mBJr8g+X={q1hNdUC zK#4cHD0yiVNLDS5+pk?E@ zs=i@E$z?;yV`F6OE{##0JED?1BA)FO+uXLQWz)2lg;;1*JQq%SIIuwCxcp(h@UZ^n zY65>V$ftP<()hOq-99k>a)lGWl`}+10G0B9wk{_GYL>hexp!TZlYMB|s{{B{MVzu! zH_*q4xaq77m^Wm8JNmc-E?tE{NEk(hJ*AnwB8o2=j=IqT#DVJ67uz|dV$@6i1qWKw zhEU1Y2jtoPgf}pYcC&NwC*0&=OJ-V_VYscGPURO#MT;vN{XJOALTgdXJ6|k~Wti0D zm_ycwy8NrV4?NPkNa3@VEoX443umrYw?Br(^x-tNwwt;U0=&|(d)ZlJt+JbF2X${0 zGnd#L^TzQnc*+lx=$3y}1#1QCo}v;{Hbov+$L zIy3nf)T*-#h8F%Tu{n9=)>ZZ12n>H{ z(3K&i=jM(hT8Ctl@Z;WeeO)<+kv*udON*kb5&nl;GdIaQ@E2TNIO#j zqsR@_F$d5C4A3F=bZla?E@R9+-RvJahY`JR_;wm_TQV@7nv_1ub8HLY{L)Q zX?Jd&ASuon4BbfjVo&Hk?tG-m#&h~1YY(;w=6jNgzFa_L|BbP?ii^9;wzUba!QCOa zyF+ky*Wm8%5TtN-cXxMpcc*Z73l<0;Z+Gu=y5GIe_uChBR~IE~t^b%~jrq(!ZybBv zwQF=k-wQqN&xDpW!dg-ze)0}Ndum9c=kfnMg6BR7Wx#%)c>UctYj zI2usyXd~!5>?j;+R ztcEQ})Fp88$?c(Ka#E~?%U+;S$hmo5Pn~x0yqLMUa0BUtesm*FU3PlBcX?bR$n)Rd z6o4#8L{T^I(xcH;(AOy3NTEZZ==iHsaqf>)yt_wjcBu`W+qV!tJ>Pml*eNKI z*NxwZdAQevbL>vRZsq6Cf9X*j`r0Xb=iQ;RMV#VR-immwbYlx~eY@`q<5?4a=$;es zRG|@!SgHYNfF)2+ByHOaL8N|;B}6PQ9STt`WFqb%KfH_8A$T+zkBKS0*+*{kQ|aSE zCmb@OCKwq}@y5$hxRvD1+fjz)uQFuGR=LIYUXGcOOrQCE3jy&XG3Q5oi2T?9gS6PE zSVkW^sqpod?OQ3La~nIv_1&cR>p2~1QSrvzR=m*_=%xtkso8^i&eQQN+#7ig3(wgz zgY~V>N9;i=UH8as>Z;hc_p=-MInd$R&hz!@;{5z#jRxt2yEtcdfQgSE<*Er~?s*jB zXFNMgcH=`UTkePw;5%hZXIDW@Q$s}o-#|&f=-T%7+FZ&{`V5FEQtC~dDDf&AP`L9m zC~$9^BgUd67t#IUt;JPj(zz1CFl;4Bmi!UO&x_(K-P{?RC~}Bp*R-&>8wnx8Xzbkt z7$5IVi6FQ24MDS&D)D{RagO01KgnD|?qr*b&cl%JDsDJ26m+LMeg%Cm3q)oc;K^qS zEGgQDNeEF}l#mMbk7tyv;FH?yci@X-eXiryo{^x%v1J(biDGOz?kqhSM%{&MD zndH(?z~n^%R>5tE7o~oXyK+427*`?N9C_Ey%POchfv>+=+y4&5?Ik}#_pTylu=i{v zqy3U4iH)h+K9A0#|1F+f?65I1v%2HdqAsWG-Q+euLw=j1&}Gw<{nbQ#C)@@tgk>w_iX~Pb z9ggj=EKc+;xmp37?l**~E?XzxBBq@n zX%vahMtu8cBAEVr71W(-rG;b!E_RNdbW0Sv5UqZ>NeR@H3uT7cc)oFm0y4iWTBwjL zWn}4%GMi%1hK5+RK`Sa7=+N3XA@remIit2z`6zXfFmfr;G?H<)Itg$z#k?@7It^rL zRzeg1}G&58g5hLuSGb5qO?ncqDnuu71PVYObpw4o7wpYII3oA1>GW< zkcHw>vt^n91MdgI}HISUKD#lo8X7i21k*=5)OK@xmFYr4YvPm1^qRo|t zx#WkfD8|}opr-7wi&myQ?PGB7b`FUs*~MNGW3b3Sj@X6X<2=Z4M5VXCzOZfyEi64b z&fMyM#o$|TagMHE+-GJWSW2i0i(5=ZZlN#K0@P%}M2m?l!RS@8d6;l)W3Ao6UX&Si zFS|!WXd1iD8PVV{RbEtU@B!=Ypdf~_jKHhY#S2FF`o~*~_{|tXN)wl;EA(>dF;j-C zyKg83O*}B6oOn8vt6aV0keW}c24fH>JQg}K4&`YZE~idCU+QiML@^J)QU&R9P#D#y zF#*5w%A2YBYWo|ms?8FIbk+|m2Q5VL&UCg1(;e^KckoO=AP~?xbzIYvE698mMW396 zlLx$bn3y$Y@7Opq=TSDK&@I+Jo;eyXq71vsq!dVzj0H})+`|^EFskziD5d{k2eV1I z9IT65*0e^dgu5)hPuJx0fl7$fC9%xA2*QeXOK)D{_9F{9SqA4o`w9F|sZ!aA8%&;S;<%gqRHjwi^5O`VaVmhQ${Rk#EYrMQY_K^ z$?dT~5p2_3S8Al!U=54VTFhN%3^oz&%cr6}#?fF3OG1T~HCR%Z75KS?+UgF=cF;r~ z)x?r~E#`lY!|h3DQ&lu@rWg!KD^5y3#C;*e^0;85Zp7EsfDD;eQVY{!$YLuFrx#Gg z8Ax-BrDby+#X~@{rO4wQbH~Lc6@#%$Eb>Aod`Co>T%aMr^|TK;7}`WQ!UW*#^5yHY zjh80o(vda?g*9n%pu`W(nPeLv7l+_JiN|kNl*UtQjg#vA!)=ONG>%zMbHmKh5I$Bt z_u|G*(Zk(vSF!pCH@bHOAEAa>Gp@!N(Rp~L+1SKCEX#u>g}(q`i_+9 z1kda0UH~h5v4aIpN>QD~j1T2$djC=0FpQ2vG_w|{_WY_RVmTS3ebF_tHLlj`EKk@a zs{1mRdfGCKj>~egs@%5Dm1TVZv%g+HB=n^)*=rhipiF*+haaM8r}|hdF#I^SJ5%yr zhd0K)$D6LBoqq}mgP18t6NRR@G2?O z^5vnT`obE9M@V}>x0BaJLs#bs-|?sqS@Ue){Fu!nLv7aB_S7}-vBqcy@)h-sI{%1_ z_KT`aVUo-cPO%fI4{mWvjs9W_qm3Jj!4soqQ4~Vrtc0{($z{D%(;kF7@h%OF4|0MA zQbQ_%dmVnkw=4V0Ewd8(_f;fHDWh#*sdIc%#=8YxEPsy3wah`*MI$9~aFNVM8oY+V z58N?ee-{1I6#dgY)kB;G44F)>07s1NZ^+iBk==7Yi35$FJzf7V zIMhF*=l?}r%Tl*;!(BoLuHa~f^Gf1<`JxyC&PYVFDg_VoO-=}v$6t_%SZ;4vR(p7! zD263lQJj<_4>7m1pL7bH>~;r@K{pX^^}#0*UZVccJSZb%W1~@CEky_9hCkeNP29gY8&>G4bZb>-K0PgFCJ8gq8z{Hv{Z% z&;l5@_GmGouX9+w0V9U)I(w0OVU*dCdsL^{NVkoXe&k|}Gh#P7;1s0pdU-zMwLCT) zR;vYSyK)ok=xWB~kyNBMZ5G&1RE=S>)6}nE$tCuhHV2wUWZRBeVA*^eV@&T4KYao- z8>J@etM7A@7(4*0RL&+k3^~(YV$bu9)@#Rx?= ze3jW(AYMZ^vk}*kf`#Mc!y~SdQi!|QxZ~^%Ew*}6dZp4A*w@^B#sC|%2y(WS&wldh zD4iBDGg=j|`ii=4Cck<{^>+oi@J&M(Y8?VB=HITKvm9-(E2#I913ZSFKVKY2Se=Hx zJe3-Ch{*{?p`=}7FdrpaBru39>m8F11i)gUN8&E9PYLndHdk*H#r-vY296s+4#_nZCzlQW4W3f}v`#qflyLh3FkP(s)bMd@6~${pwe2gSWmhZr**B{T z5En}1-PoKH;=5FCS$31_wiaX0pv?A7=y5rlOxGN88=X>E^ zQwDj#0*=q0i%R{1f3Rp(89KMi@xGG>xVdzOD06R(Kq0@Wja=V&;S=s5a{mckK=v-& zmw$EwCp=F@7BIOuHel3WFJ_RFz$IV&6}ID?EB&ThJKvZ1~x6MmPge5 zt(SYdXt(59X5{!;Xk>fv(4V&5h>9RVHi}I}qrn&%#?f~i&jPKw+a51q%CmSk1MDEX zKe-r6bb+qh+%7o2ekA2s)~|-R7IaU9lYp6gN^E_a&~$HO*Y`}0or?{lth{q)u=E;| zI`}+cO0H^nzhsK~>!OtU&k2N0OLVYInlPthjkzZ2?zWYiku)sQ47LbfGme|WDV;l* zb>mOd&2kDRZSsW(mrUaRVkLmJONrT*|Hs&Hz=dkdo6?%J&MdaFQHRVK`qY_&J%MA* z_bu1cSSTyp`fE@g{Xd~mM@Q{e2?*+Q0g zVIH8a>a3_Qfq!l3jX8{A+0iQ!cZm2NOV?bksI0G3Rp0dotW5Q7al~fl7a-JGHl9n% z;oE>%4dION#<$t49%%DllWnO8T5z?RpbziccFCUVExL|4hwyOGgT(ixrM1zBV+8!d z^X4{oDIEy<8`?G=1FBkjj!DT+Iz@+~qsPvnA3m$YYa44D5f~z?Td7(CU`g#BkaT5N z*g;A#HjrA>$k~FNN#(p?3L%cbt2&LNuOsx5&k0 zi`vIXe2K4EbSAVsA**>T=5S)3*c1CSl;av6fEaX3VUx=A;q+f7gC%w8uw>&q111fv zZ_bP?e{e|Fsm${(@fT7x=0DJ#{)DtoXz-Enn>m;)G5VqlC>JHg<*H(Yt{#e#jGs(-y04=5aS>Vo$1;BY%!$&ez*ZtGli<{b0rA5}r9)af6 z>H94&tGZUGoI89eu}dOeSbH=dzRfAM3u-hI)p$zs?nqeek+n_bh1t+8m z)Hb4(LrOb-pe5*2>HcVG!fJtDU#w>qFKY3{eoZdw1yHgW!0E(yWylc8FqYQA1TEXD zbf~B#yG!7!%30~Ut7m9o(CKG@2tdj|R&9;>!RGlD3(LDB$M;FGK#^s$JHht}O6NdN z5j`8AY_jR?2}2T60dlmdKv23$I4Du{j`3BsJ8Ex32ISc(jt3{Hr{-@x%a%l+Y?3b^ zAZlPBAe{fRv-2N4%im<|j0~(rEIuP47KSeW7qFA0q%Dp6Ne7%Z1ui*PI5ah{Yd3m? zTAi=&X-f;DfD<Q_M{%F8}X;?4MUJ}8nu+pvwssN^tipM>X-sA+cApuG5Qmea`I+!?Wz zULdjKq+Jc-)2)X^`dQ3fOXK??zua!=6M0RESPpv-s+8Zw1a+M~+)B=kS>MO*EY~-G zqh(d07C`WlE$e}SMClh>Mj6YS9Y5I5w~9o;u&J4TY_k07lvEV7LnK>NO%_n(VeD4k zG){-&tGycc@kilbQR7!qRoa)@uMNncI^CHCej*vD~KpBG?G4FFP`b=h3c<5 zL3cO5n2TX8Ns;w10gjIPBve?{hVeUA_R9O5-CF_W@8-PrpMl)(WyZAYU+qv3Uowv` zc<@@i8N{_o6&`Tq9z27D9!$ZXBG12K(1xTbPA-}(BGM8IhS8Xq8qs3B1V%+B8<7^+ zHWC<^RHEO@de4X)Q)d1leX>^6?%(m$myvMyrdaKQ;co-G*a_oPU!!}z9L&OeLum*7 zPmeF&ND3Dl8U#cd5d?(ze_jdyNx=+O5A#Da!}c|8=wRW2=fXhIpo}d>u)ZRJjc;81 z7VEmf+D8kctKd-2vqlS?v^Fz^QL*VN|7F`mpjxPht{s!yY)v`4r>erMRi&o=hWUoM zc$@*eKvtV%c!_@cy|wR^bL@3Ik>r=-2hr>LrfgyqTnolEx5{gqRP6DN}YZqiH^9hZ1}POOQ87awyCk^DBpT` z3l3r4lFjkr7P(P=KPd$5rY|V&t1lX0jgKn$b(q*dDD$8`!MP{PJ zE5l~1+~7lOtKRTIY^&dZLCB9i=Y=mk$3>Qy@RIJ2pb!a0q!k;)n7WKG;lbeFqDEWM zUbIwY8gyU}?4CS_?RN6mrP`90btKc}IeaW0;4A;$yCp4flat@16AYKs;ImANuE@Ch zgjeDskVwH)h!u|vKg!~VjeNZ*;rLlKir#Nb$sHWzp0HE zKRf85#yP2R6x6LR0UblQ6mtru36wz`)a5ZMZllv_vKg+$cXf8K8lG(R`W7itv%jz9 zv?A4R`t4b~3Ju5+WhP@p(!*%A3iYs7i5Z);*uc`Bg?t}Bdp&KIIwJcE@45+Nsj^<}B6FhfzPXOMZwm$|zrP`#no437g$~jT=c@NXr4r3@7zb;@7=b}XQ@+T0An$s3 zy-I}GgwuAIcFneODJl1gC!5vLa4zl^6=rI})~MKOwK&^^*Mp~{|Avzd_dePHu_OxR zWb)10*H+H|oQJnRcJi%1k+|P(d`(SDgv0VMSW)SSUaRS@I;h!^H^GA=#VyTK_(!gb zfYYG+%8EPLgbXQFz~R>)fb%Mo_f*X)0&>nVZ#B25(?d$K$|%>Rf;cJv@dw2r0QP_k zOmz*loRlwq%RV0tfGOWV zh1Ja{Gc3yusN*N6APmm1UxMQcFeAHY`&woD=fh3C)mv^D8)0T20-M2-Ga>*$mbt(f z1%u6C!5IU0aO@~2&ig$|g^hi%FkNU#y1!3!u9J2j9UkFJap6$er-4nKm)0;T+aZzzSI<)c~I+d3DgJ zBgQ(WOG%eh-479Awz40?Ic#qbcB9V~pVpl$g5StF&L3iwtG-RYO7M2I^rT*EYB@}5 zlr_g!Sw!cg0-U<5zV5mO>=Wnpf~@J-rwvuM1B40%d&fu0Q`&tx1iO7|^cpZmgR%yk z3R=5%0-k1ulIeTxr1o<2aCk%Rd4Trb91M(hfeW=!m(`+T%a$6+4Xmp63wOl+%y~JpiZ=VoB60)9h8{km2x5%$kR_#)es^mlcUlVs=*0cHly>6$+gT)P zh0gTZSy#^7GE9M#%m)`!ff}C}tp13BHR<3_=&N zhGv3Ic(b|b)`N`6#z8|8f5>iIN_kVdk@>3U3O0t2@!QapkmnpkH*md(+p zeb;W_Ru~AoJ_jTh!CuE~h&ESGm1Q@S!L83OCoMmdp1|3!FIQ_3KGq|gPHihQzCKSV zg;&`PH!e@vqQY+5n$J|QnYWd|K3A{+YYvAe)1X?2DDx2HFN^FgdwCser(jP74#!{E&2X00Yr`cdQbzgR(vimiMd zNA_!FBf*@-xW%lsWt^?AddX)l4RS4u`z}JI?_+2-`t*%_hFGZ=Kr#Eu%MP%k)L($G zmJ4d3&3jkMSjX|y*e+@=vvSxMI=NLgzw|}!zLAC4^qxh<=~(Ozou9>_&b@!|%YIaA z!KkG}X@w_4p(I*&!v^|5IA;yZ25P;#XjY0hN}IVag2>$kgZ$Bbcr_#;B*Go^li_D( z@pm&ocQqt+jYGXAm+EL^9-TvKv&W6w9thfs2KUHIKz)is_}cbNwDt_G{6cBKDQU_p zYRfCknnMP{ps>$tQlsP=T)#r;6N26xZw zKx7F=It;ntA6NaNhJ93Fl3J6j|F}!XFB~2F72h3((74h!`%-q2WUMX!@V^_rJG`Nz?Eck!gZGL3RRbAlc8PT{kyZ{bF%57iM2edLIrX5&C?+2PYI%I zXt99XAiTJJm?DmB^RJI}#5Q}!H_^xAW!E=&)3di-GJ>FVyjz|GE^vr#Nh25g#3JFv z;XjkU;_aN_Dfh|qxh!lASsQ>JbwyBB+W|-gsv>tNKR_GVL`ua{xA-x#D^v6cF*-SA zgy^IqVrH*F($Qbn1$&o1{-!ImiF^qZKJ|p$=j$(U{XdcTbZ-A-HPZcqRB!)@#i6(V zKP-3U=_z@tDM?icN!f|X3EHvAnSG_lsTpN^X;~%4S_Q`1MrOuZW_AU8R|G{yW=7V1 zYRd5$DOq}|W?2R)nK8*F+3B$ZYU(MPu}RscL(qSt34nd_zT?-~nPxse2;Qgc{k+cv zF?6x8Hl}klF*32RcmB6e!51TFH$Vg*eEpVG2E70o50kTr=lIgy6AqZLFN$?e<0)rt9fNOYGuj}+R!Ob5B z>a#gOe&>t)>d!@gDVMA8>96f(EjrrupF#rtS)(#1^q9F`VLKt~oQ}h(Ntw*GrQ*F% zNmvfD^=MC8BKh2eqbIo0+itJM@4)_GFL%kHP=t&qL%JK4{a!*JA}6P=Na=+yHxCHF zt^HMZU`>NPwoAy1&-40ky8o{q;q!a{pIHl^g6|gNK(z{uL-Kc)}-_4e4&hh*MQ z+LrPsZHwu@zg^JquZXyjv#_0w%_o;o#=_R*6T<2GN#_5ruy~S+?k9#HjSmbif~Gmt zrfPc@@vy*ogFj730(#C|s2q8IaKr?A#YR5`Ubw)oe>1R7IIWHU2UEG)%1~beL^mhq*kFr#ttMPg&%1fPIIwM)17ddY(dM{ zm|$7$c*Jfo;UWY`M3CXl*Efd-R7=iV(J%4Qjz&GvG3^8xSn2gn_H9Ekp>>@kCSg}n z1)p>MY^Jm@X4Lbk^Z3!;%`sayVqbB}olO=JP<^3q!Ja=3Qe-KJ z<-$Q;E<*?*5BcqYTTs~5X=TR$b>6UVlCe{Vv+>0vo~VIESk&zgxe7I2)8d1b<}1y& z??NZr$i&i9d2Es$IGSSYYONr@1?AA(%%h<6acCOJN4{u?hF<`sCDLgNrmGaQ^M8w} zk#@Ym#l>KTW}``wng=U61g&mC-(D}k(ijVRz*H{yl9sAq9m`Qu*=23j_IAk22jwff z`-db}Ovk}+vQr#mjR>R<3lu+799RYM4mEuD1Z3Bx45{h2{5xgqucgTR!z8Boa|xb& zzWx@{{cGj^+Y(eTaQx)h|CcWt94YwMGz`%X^Cm4g*TeQuE8!UhG;q;A)Irx$Itx(A zU-tC8{vjyJI_>uS_X32bTj%fYprc43(83}?C)T#jJ2q9RwWG2dQo2`GQxq2X8!n{R z@t^gm5VrdHSub!Yk=Qh-s4l6rk`_HeXld`*BMJF?sGC;9RFZQvKJjUskfd(de$j3| zY2k;2JDsw?cv(JVNXOn*=F_LuU7xd9aK4#e4tImyciMp9F6T66vLjC>80~na( zSo`z?)DbJw*nrCO7~2e;x5K#aM0yhWq__QuJ0Z;3C3H@>(!;e}M3E zgV52L4pavfLlaoZmMW-GQj^U@sis7jaEI6+ht^#Xq(zuU7#~&>a_l$eE)h~XxC0-* zLj(0#+V)Sr4(P1aR}7U;(G=@#GDYpBt5!HDQsD|cy^}|OPo^3VCx(B*7!YbE{BD~- zTyLsMnImY?+O58^d|BeXQJAn>-!w2KYm2Ld_?7euE^r>-!mplJ zT%MXtb>FVVW!`$lPh7G7Fy#%MAzeo$0=HV$cA||Az}Qm3+(XKM9iMD2XzlXd+5d6n zJ&0_X&H;;hj(K}YuFVZ`0f)#Wj69(uU#Q8@Oq5)or#TL_8c_TdK4ZYQo3WW5gdX+fWum-4aLQo-t7dCMT!enzWjL%H7HNP zV3~Z}`VNqJa%GeuFyizrFakKW3YT--=qjfenAY<#M4mMn))d2~5Cf;MEnfYHh}A7L z8h zh^<&eLd+I{lpnykh;*~L!yYL4S?^IILpVG5|M0g3-6@kH%*Em-Y)v#}nB>gs0ggx-FUGem-(sOAidI2#zY zh&d;jIr0lCjRE)_rdq$~Jj;U!_2a)q^8ecHot3i8e^H+UKOa1N|C=4~&!w;YX;0aj zeeMr?gHH_ff6ao)QknUrK%@OJZ8tHn$-67iZwk=LQ!u6?`iz(qhQZV!qr!)66<-#^ zYr2kIB|q2nj%7f3-GYXZ4>$NF-cGqz30YZkI&Wn>b$eWI*lj(G%rI<&pxsXPA{`-M zS!^k>IyfreOR5+j~VywzG;xQb0w z%vugs{rpJTD!(a9AwuTOB(Af-=UpDbhvy*Y&(y(tt2UUPr6OTg*NX)iA>l947mYoQfXk^i!S#*U@1J@ z2QxQ8j~Mw)cm)o6Wb1vaQpc)Mov+?fu#6`k=k{qr-AjZkiMVJ53n}^h_sdXxd(h?w z2sYr+WB^%EIjsH{L;2MA^&>*EmiFM)Vx{5kjR(tRxH4Nd;F7H*z7)|SOkwfFzHwu zjyQb=&j52A)7Pv%WBN0lYwa?HW( zarYIRMDIjG1c=2P^h@kN8wb9$KO$>cd);0G+mrbMnewtor?uCd4zr1?o4SR(Cg_nF zkUVpjEWC662=|IJO^(DK?x!B2j^0b6y`ZUikbi4jw#7kK+WJ;36wlKtOhDmSF}g!P zH$f8Obzo73Pm&!K+EXYKY3-PIga1nMPS3b{_VSCxM!!Teo>DGkDn2nojggH_aaXpU zH$LH4G7AvW8Fsi#Kl+Y|_v>v#Dx3|0kueJzq0pCt13sdNMIxa77x~y1i2v)r{k7Il zND=PDKSinZ=X(DiMApBZ_Ma!ai_Pbtqt7uICjU6<|9QkG#Z5_pF`)&^zp@lGHEY?> zob_KdszR+K1%w7Lw$>K?cE%}=OA}#cIkRe`ZoT>9P01uFjZPP!xp|Pi`5TCO_viK% zatD421$LF$U_%rr8raKq98kg+@S5i*PgsVji0t{U;(+WL0{{<}<}w9W&4F{x7$Pl( zbVjo%I-2ko6E1HZJ2oWFx(V7DOrd@d5*` z$^w|?A0wYs&yY_5+5hSUlD7Sv!}ZT&=${JY-yXt14J&uFLv-IB;{;7|FhDTKPqmWt zSYs!|FA=ki#QBAQ@3=CNjq3((GLRfB8)<9c7ei9omq5K)b~Ud$=ylJZQYb>5i+Z*B zzn1gmKOQ|^d3|2DbE^2vOkEo?fVE))zmGHSv#vM0oqkVW4Si($efiF_l~f1eR-H>m zT!3nXB7;q=5h;0h=b>ki`3J>AN^bpM0t?`p#cS6RwMP{$3 zf5YpWx9YH`H(#WeiS>XF^CvIoZmh-lGlgi-e+{_h8{Yi!^Lihu^G%^Y=J}bEP=IJ3 ziy-mF2-~H${~5-Q!o!0yGUx8e06S*KOy>7J>>KEl^fOt7hf06Vr(Ld4S!ktk$I!o38En7`E1Gm*Q%|uEZ}mZi#3G(m+FYjfWOvb#E__Z- z@(F2jsA4hk!V}px!d;J|`8r=x_bYB$J1ll8Ve2jmxjaLGP=xs4h~P@22`UkOuxu$Wok0kqIKZY z7v{k&6b-io3)$3e)lbt}Z_~fO@<4lyx#RLPgJs(e+HrP2uvELL0%L{k5$EjyTCw_+ ze))JVuA@XZtJY(UUm@n~7IQSIt*5nZU*U$mCqwT1D3z8{4@xE$)w^EVdEw%s?L;?S z2|Izmbm=3AO)bgLcxt0pe<;d7iUbn__kCyk1MyTdvmGeC8&7J}*?2$SH-VwY?XfoQ zqYq<9sSl<$9(~Gzm3ef2XRgI*4*Qs^-iOc{mq!7#_VR<*pu?P9q(kH6HYv$S-z zs5+&xfW0mB?!dqQgwI_$GFrdABguYE1tGtW{WA+#TpmLPEWl=W5pr5Aw28uX;H%y; zShKW7UZO*hqu~tPrf!AcpmE0#hMhK)=uhlz&ZpnD;{0lol*8qGnCqv4KG-m|@U3b4 zl?Xtc+Q$Xc06T3Y*XQIe)_)JH*Bb%rJJNTuo*~r=xnwEUULbeU+->~Au=|AaCwC|2 zB2ZvgDEj*SPVt7h<9)_mv`@5?y4II?s5&t&^ql?IDp)~#`3~D#aLNhVyyKm%5s< zL&hyL{c)Pza*a=VISCsC*4=Tp=uQtG2X4&bwB(@~G@9X0<6#s;1YfU8ceDfM>1HP@ zBy+SOJ62?4+$-QM&((;$^prmdeX|xh66wMGkkKp(BhH)FWT^D$W>Xk4W6R9ON7z@% zVRY~IWw$uox{cpl(D*&pG(%7Kh}q0AA?!&l`stAjp6n;qfF)3#d6PKfdL*zVS>}wq z$k(Z_&pyty-RR4EK022?p=*_MgQjvXnxjs<8w|nxC^pBs8a0=({ZRGv?T7#LmUdbo z5r#Hl!!S!-O;@J)d>IFHl5f20y!@7DrF^ z_z?7%h9EOc?w4A3|Xj1uxkVSJ`K>#xa_TW471++r*d$j(!vspAXkqQ`^ zoZD|(BIil5=7YRnGxHTWM533mjo-NK;i4vPo| zn?ra*s~AYnANQ59amix31-PTaLuxI)j@uiXoOo2?&_`tlv)X@EpUk}*8n&pEBNV@j zfuXm{97;!lkNX6+v9zp?5+KP7qR1!wDyh@V6iji)=-R7v3v}szoJ4rNwSbSc0fn-6 z(%(nYl;3B!l$3VIQB5c{^=Z(cU<^xg(h!)O2HJD0|Dx{h)-S(T7;?(y9>PQ`{RE6a zN^$cR<(fZBWlxS$nRU8TMF?c>_K?{Wy8Giv%3+vXj7lz=Xr6dgwdby=-Tc90f7Rmn z^RwmZ}Z^0gA} zpDV7;h)ustN7vN)e6%NhRNEoJ_;&(`T^2pSMT}J*hGEJ`LU99Th$e4vERN_Cl7g#1fFt#NO)B_sw+ciHwEO-DTLU;d;C;dye z;aOqSO?_&%jn68b<9}bH{JlC!Qnis^P(bswU71tl#@~x8%r(>Y)Y<4)?fVX?&>J1e zdSv`?r;hj;u^UCIqcnnP3%jix85v47(NBMO6yqQ~NFgO$8%!~D(h#Rsy^ znD7kV2Yq1PEh7pH_)>|VT8Le`$~LvRf){u#Ty)~YrURJVsxwRhFE305zP=MK(trR9WN;qn5XYoiV7$sVI!-sf{?Fc5ip&cf@ zGudnOY3pS~zMc(WnAgRHe8MTkv!F<~v{cir@{BfEiuf5tq};~7zttC~cORaG;|u%A z8FeY29j1eTQTh7pT&GEc=CMS64QhN+jpHJHpUzv5^nu@Wie3C#1CM*Fx&F-9i<)v5-{o*hh{<{gKx zd=UIIR`8%zBdo!+hVyj&mt4`*Z~b^{sH4B1EheAOX$fYz1wCYxsp=EgFdIn{NTQ?! z>J?uF%QS{ge*9-;b6~hcvHQ70Y(I6h!2f24sM=cn|ETCL)d{7~I-ZYj?3)4ecW9*B z;84mjBN46j zdtzrh_wdA5IPhahW^WKPnw#v7R@qkTkoZGzAsT?)f=Pb)y67CDIEV%IE>TE6pUgTg zL9ZdRW$gY5xw!Ci462Hx`Qj`7ql{#Fy#ut?rfFT}i$MKVFUxodF!_t)=KFcy*2A>u z_moV!M0=Ff>}`{iEgwBS$0#Yu7Ct48thL-)BjwNg@UxAr_*gIIEQ=QTa3jL`EsI>+ zjYuZFeNxBqt$JrBLk_Ion==8{S`XJPyZS~xDjB_0DuZHdF|98P6U&+5+OyYb`R`>$ zT&R0d)Shz`lbzwV-&7um%7g?{!VTcX>T>tUF$!9T7@uYq`WTncHb52^h>~r@jbC@! zF~bd5ftDhUhb}_yZU8OJ2*Xn2&1ia$Rn;Sm7sb%p)Pb+^?Qaz*iRYMB z{HOnwh5+*Sxe|YKy#HBs{>$%G({@@=L;915HBl#u0zbG*K;&xLx*mU+% zHqg$khwqNi@8cEMKjqd2kB5=Wh&!5H#zmiJ>!cUnH29&|eUi)`>>g%wknYx9dUyk7 z6F_YwD8?T>eO49_%SzluTA1%nJDfXWFHX4~)rVA0=CF*0C@m&A?1f@nad48XnLNwp zE8bOcL>+B=P|RwkAvBVx$EsRlXod2)+=E|ci?Bp{RE($15ivmFA*6U|Xd=mybqYmB zu#QxV!?x&{MxKG=LW=FctT;RFq3zZuAprm?YtBkFC#wgKRa#~&N`;=AcGrK;T9x2{ zZa3FXnyoEKCn}04|aPM{QB&kb#R?2`EaZAdyVs z4NDNhxCE(jNZ3uV>6F;=aw^FftX^83sJ&I4mXoE;^pDxhmL3*Or|-UBbQG$UN9wWJ z_Gr&8`m-^ODr@p$q=n>t?3;VRW@%$E@F%O+)E0^L7Bk0GL2V=ugD4wo6jw;B4HfBf z9$QHbDDg?Ad$rCSd7b5Atz^Z2FIX`vgL0-hQrrvRe!D7O@;)!aZEA8H(l^x>vYSzH zF35#}be?z1YA86ZtzpZPL0PsKsJ()yYc7G}B+!0WSBI8hz}D*h*3E68CD?Du(Q-3L zwfLiSYE_|iz$(4oxcw(>fV}v^$3TkoAXSCoanpT!u9y7iS?o>u4w3bIBgSsNmYy*h z1W%o`nC8#GF4)IVaWQjh*sHzl$ltI8`Z9gLpT7&a8Oa-am+p{yPkvq2i8(iE=2^<$ zfPFOR36Q@j3jh;(h0lq&#C%}-VI{P!Psq@u4Lgsezk6)uhVSyjP^C}(S*wqO#C1D@ zS~bMmaW^{IO-E_6a?kb0qz1PaVOn;FXEYz|4+s>)b~lTLRcW%R!an^43kyD5MPZhRfWFGU)S%1LxsUp2 zE;Us$5)4>8;;&AzRRzfgNC~-xa1+bPl^r;)_xBxv+pm=I0VetdtyTryvPe_gPxm>Q z_J?3wcRi!xZ1W~0RUF-rRWG7hZKtOXPpeMOs9HYA}f$?TK=>Aff z@~*63NyAagJAJClb{y!OE`fH6#4%WiTMHeu%x(UjE6ke>r+;+=D#;049D6mD``YoG z&czj|I5U=`*T2rGnx~>8-_;gEt>-kGvt!5mgZR9IqqNN_j4}M1cGl@?_vG#0PW6A?WBxxZ#Uv%&zmm6n4Vn$Eva*fodv1|bVpO(hKfaNee@6>|jpKc0 zmjqmnnyQ&nkbM;N6v_wlKZCs%guAhSL58z%C790YIL@BnbNlo5{tmx`^uyb@_HUue}m7G$vd zI5w&QbjX4RGH=e-Y&Izgagbzx3!ogC(g+bO^w;IE3jJuu$#Z`eX#9;(yw!S zpqe!4ErF|esQfMRrgi`X06a6k9B2myu~agt{0Whb(z^ZbHdNFoP1U`Rw+iEw`Z*Lh zc-Q<@l@U!(xBZegoF3uD1t$e-JVD%mQkJBR%(I{VA|=?!j6|KU9U{n>$@I~$??v)w z;98=w&&N+hv|};w=V$2FMt0i5)=+sWdIPSZ{3%RYClgv0faav`D_>A_k9tv--8&ZBr9J#qbF7|M*D%$3JfTZ-)QB{C)K` zKU{V6KVloB^CESsBhf%|)dkLE6E48FS@*JteR5g64Jrpodo5U0D-!uwv&6a4Fl{O6 zyxHKQB8c>4AC`!pg{&~-0?8cDx6`0ER% z(2ZLil$_5P#|*_SXgAVG1ao)lP8|c;Y;=mTH`h;FEC+AV&L7eH=(oPA=AGnnzHDJN zyY(8rx**=d!{-j2ao-WU(*`r#rBA%2dAsQd?8cs>gDy?imSqbZjXjo|oeM^@$|BC< zxiy-8&F?g75yZ_hS70J6RcoaOB}DxY2bxH-g$L62jwV{5Lq#NOQCbAvllj~@ER~xF z!#Fu8vcW31=Vdw4Mn@uZsWvx;o337|70o?Ynkpam4QGJ_evK`nOPAK_gOajPx0c3l z311T3b1G>1Y9>_!ebR;7%-$Crbq27Fkvy4Prpl~klji#|FZMbu5h{}kA?rSKV_F_@ z%ytfQD=XwVo~bTvrXr1LW!Ertm`NTalpqU3>{Ao;$bF4x)NFB^q2&7P43-oNqy|O~ zJWOA#xsyxq7&4qBfdI+9&&Exo9v_JS-<(9XP-HJzQOdHAaG)7)hIn4(~N< z(mX#}|4=|llvZ6>9**iSEQ8ArOdxb-R=bTgQ7?tQs&H+yls>WKwl6GCflZgFs#K3Y zRSVXd=Zag)y#b|hcAjYe`KOt-G)ar^a+KZbVJ>4CVyU8S!L}rhhya%(*@~j0-UWik zAX#d?bD)K@tIHUpq>-%Z;K@w9wmwK_Cqx#M5V{!~@3jp<mvau-CC&*inT)s~J(4bQwef5NKwgT8Q!5-TWq+{Q14#0wUy=9Y&Ypv{g%UCpcdp)X5E#mp<_EW} z=2<&j9^Fa z3EXedVfxY5Pe70wdS^%p+8aT|)B!sa1#u?2OPp=CV&*`*TT`71PI~dxxHQMLqlV+_ zWUMw+Ruw)|rszJSTi7>|fUNVG>}E*etC>g}1Y86wHL@tqh2Qqg*qt5}vw^D+3tB2+ zOlY&s^1h6tuT%NS6X}W?gx*ns*7yHkXhnJ+UWZ{Ae>r|Qbk_)fB06SPL%>VMWpwm+x zP?NliTb!q@+a~(@Q&ktpE>{)L@>Q;Wgg8=R663fb>J2Q!78L#C@8y9LY2?-4vk z_VD8&5wvW2OdCi?KW4s%6jHk~mEnZx$`Hrv=UxVFN3|Te!P+28e*l_yVLT?&m0tzF zmVCLhb12&m4(bWEq&P>sQiougHCV+$&>Ci9;0S1h?h-E;Zi(Cvg;uYtg2|5aE7~sA zlE;v1(-5Hja}8O%R<7?(FE7NU;?vf~)EeP)K62hF4l^F`dlP53jXB(ANaPMf(LA zfmVcBTft7P)(yoHv{zG1JXx74PpyZbHDuCh3Mb>T=t*v!oOq@r?DC-d_s}%(rFs^^ zZ%d6I;6Giz^0sukzltv_A#7%@4AKf&qE=|ox(x0C`?lw{Z^gvhO?*eU;u_V^d6E9W zaC>^PN#!|DT>$3Zp&}#PJ}eydoid0XvIwPB3pE)2kBgEmre!NXABiU*HxMV7`Uz$yH)SRse@1O zoS+s^kzqW=Bmk$B17sAN(<_%zh&u=%w9Bp3=QzeRq`5~r^rb{D=12B3v5=xPUc1&8 zCLh@^od>R{eDUR3l*po`zs;uh_X)JBHCGHG)wgq$QMZp10IY zY2=XbVB_DLnONUkW5OQ*=0V-wY%*MU)o$!<{o_(bY2-BxVcMdMSYM5)5F15&N}G z|I&m%7V5&f_J#mGGD9nSQwLKg!{7TN=Bk*{ZvfixElr(VcAT1vR{dY%M0Tljq9WV}=Z$6IiTT;#oFAJFftdiMJ#wEMeD@ z51w^Pmnw7ll|jVNT-J<1oIp)}pY^abX z$(&%+DJ@cldZ?NZnM|!;*O=buWpVUW))kWmtz4NgS%^jN&Ywi!&&4)DLbp!JEbFytdRjmC-honQklfu%p{-bM^z#od$M*bQzSw5j zX3^Lp$w2swMHoF}`Z9WIzG8}u;B1R!=LYOf1}#jITe~~nQ;Uw&c+fa3Lgwn6)nstW z)1$tR#Q}yfd>bWF+Ob&g+BT(j=L_^e7|fj#j6cl=k0M`f(ftD-n>(#4Nl@}rN~tYS`Skee3!%|*_nJ(j6D=FB{b~2vk%t2&mxW2z+zjLWk+q27P_>9h-);iNanhX7PBDbcujL#~5 z&?(YirpKTZD!9x6l*G3Iph2-*)mo%M7O15Y?K^}Ss$rFWCN3IIg9>mn9T@G zzu)<~9Bo<%^_osbyA{t=qjdLDcdWLqyImc2r20f(BX$ETg5YLvVam~48XGJG7W5fN zeWs9lWApcxW-#tWmXVqY?yLp20j9M$T>My(y|^v)LKO%s%ODWLrLz*#LpXDdEQUUd z+D6kuG2t%RG2}8P=+solX{al2{qh6tDmV_>7 zZ>CY~Nt`JNL&QdxG8121=3=w47?gNkfrjE=GvVnB4cFHzeYMiDr zF5_|pv(dl>#!gc}Y4?VQ?1O6TUQNUqR0*hUlY5Q4E~dlc>lW+{v_{@P5fZYFa1c#X z=!TAp#_q{|jfLfaxnemk*gTcZFm4I4BIwKCaX7oX z6_wEK72gdI5+u7z4z_teh3zKKJ{7F9Y3tIuRr_Ri@Bvf7W=@p9;Bjmp&Ktwe1go@g z{G?eWDaFAruknsF)IB5n9^l#&E2dmJK4$G4!9U`y0i_Q|(feC(gNFEr!T9rk;$TvS zqh@V8#8Bth!us%G-sNs{bO&Os3lgsVUB}zcmS08vt@n65^4Xp|reP<{!%*rXNk~aK)LSlNoJNI(_qdG2Jodh6AlOv+{ z;JW)ApZ^)A@XJnTtgl4;2A{h<2G$v-cp><^qRl*d)+?YIEFckv*!>B&DkwlnP&>%I z+A}uR>lB9l{8NnJYkY7UV&6}?r1g&u1J2qohUe>(H(k~rmv5R|-%=k|EDZVRiDG+tP<5~o z(OJW^xW0D_;s0>w3YAjcdIyc7-{DI_UM2>*Sf4_S)}oEi zS*jSwX~y7w)XP%PZ-fS^-)jT~H9o1m>_H)Z8@SZ+!=i9q?d)pND0RQcBS}y7^0)A!t86RLC=*8Zt!3gCklci zCWm;JEelpqZpMib)>0@$T6o&hMwL;38r(T`wNj0t*bXH`>4`_=8;)Ce1p}eMf_!&& zM;&EckhL~k#kMf|v|%ZDuXyBE#h?gsErc=rIFo@re{kVy&K2n{o6VKG3}4gh!VYnG z$T0Z}F(zjh8}9uHtKO4jLFt|bESh#3O@;GbEg3M*LBwNyi14t_fM>W zCHt(icYV<*C*4#5LG4lqo|u{WdFTkUAXiF^wuX?kMivYwG;cUv&>EMm`fy5DY_GEY znMK9$!aj|hES+Z<83-j~skhCI;Pu%H%+>^Bz%{6~ z>}YpJ(w89-R@khjmJ>;l(@rr{&Ut7oxQ4ROI#n&FwiPsAf@tHpW)*0}`|%SE1dzds@`VuDdk_DPCuFR*g))jsMvKP(mSkc zWo>iNM5cWmg-V{(>-AG8A$LSX1>5fmn2oSeZn%(_a0~7T8&|LOPYoH6-pY3oV-UAG z>$w!2>A-)ojaB{TUre9i&ZqFm*+jc}1Bec>7Hc z)^d17RH6xWWS;#2-z8jH`e`9#2wDjwYAJiX_=FtYQNC8?RGJ(08CKIcTk6nC8|x<4 zInb7n1cgS>8AxTxduWGD6vjTII3%LBmVM%od2z(4Bqn3?jQ1bxhKHi31_*Gjl)}nX z4;pA9m94^*U+@Y=>peo zNo?*$Q9)7N$K6y$*p)D{cc%Zu`DTK%K-lB8n^%GL_$`in;M$b1F~KI~*UTFnhF5T3{g*2a#kgwo$-jYFM0vcd$qn9xB+JyI$&7jpv=+%4#Xh0b<3g`3 zhdF_vA*~t{>b+!2>!`@0Elcl!sxDEbBBGAVD}8c_0)Eg^8|ej6d_yHd_!=@JmoYjU zDmq^-Q-Y^4Iaf={Hd9=CS%Sxv^kma=dNjj<{4BrZ7U*O{+?0VoYOUXdkY9f8yred3 ztpN8Hv^myWp5ub>K;@BkrH^pxge&e5<$+hC(#oJ)^}zggRWkm-)(xahJjQ~#K#-+7 zGw=m_285~N{?R4b0iN&}e;|6(xLHZ+m7tYXuBueU)KvCVwtw4faK<;_SvQmp^E)m# z2wuc@7d(|cOp;f4&di7t?qK&Dvz;BHR`}wHayxGn2zepXZYY_=6nL{@%h-F`d>*0Q zn0&eInMklJ-Zc>ySO7s;tT*FI;YoqMpa<82%uwHk)3aqzTi)x`BaHh-o(A5M(-c%C z>xbJ`o88`;vnf)yqQ#QGGlmJtz6c!pA6!rAUmxa%Af&ZuA6pl#o} zi;9zZ1L7ofenGj%Fj-a-nA*sqtn6mXc1h9sIIkyAemj!SNmgZ#+E!dS6+GY#zxqki zb%?-K82eoIzTOgfj^;CH>KDy#wY5d9Nn_w1vmh!-Y{1}0ZU?PFVQB5w6L!hDS!2Nm zFFx3+QkhQ!b)#H!Qnv*Nwz8+e29Cm1DoBJxbWja$>oKZ_j?7xTXnI@*OLCl)7N~te z^!mihr{LC84r@yM>a9}%1)_{R)=SSrg^r5K4bCz@v=5aU^X*cuVK=&yFmrD^Pd;58 zc{+qj?;Dd4ZD*S#@P<3-KttrOkR6cMPKCYnlb`7~wXB2OAeMfC-YoxO7+49pwP7q54W_vapya&dxE)%o2JOs4eiSpYf}8%s{8$4xxWR-`$?m$BIN3E zQoj|Q14<@0uqS?Ca)WJUljz837q%vSxmIWj)I!Phz!PwNGaWWFenr)_BF$zXoB&x3 z5AKB{|Ju|RZhRi1@rkc3yozo8Lm%0PcvIh1fczcQot9UMmk7TY%I%%{Jv6J$ghe~# z;;NX{@iF*)kraz$Dwl~RuXp0)#U7Bd<}3rF(S-|X?-p-f{tJfT(EGh4lN?CxX?fMo z6r(nG$vg9Hdaau3(&+jEObS+Bh2&`$3W9=Vps}yJSHN7y8hLvl)nB19>mWMwUr(xB z`cjbCljUKIR{#?{A!NG}3S4ASfj_~%Te3Fc^2~0n0$T)oW%Z5r8}9Y4I6v?`CyjTc zW1ibtcRdK^@IHShDdckCr;LN_ihXbY*`Rwwplb1h2Iv^eU_*mB%Aq4r%EZRv717gB z#EEi&Sa+vLapDq6h49;lqPn6=`dFaMzOCt1X+b5q=+_c-Qxn1)-O#BL z?4w>BKo5Oh8p~w)#te?LkZ8AgTQ_-ON;EC+bWR0OfLCGR@EB%TLDu*uOzv5Tz6qzw zd;41Q!&&Wk=*46P=F>+7yRpw?PlVqlbeRx^nUAjur^`NQj3Pzy`#W8}LQ^~C z6SA6PQBuL;kvli0x~wfGh6oEV+a7p%`B+Nk8B&T7fhVfnSP`m+nY$9nqsg%d@Wc~# zM_=SMx$)~a9@i=R+US4X?quG8RXz`Q(BSkHT zzZL%YCBF43|FhH0ZxHz=-vbP*)PRj}mVaTYA`>(M#1r_#4Z{8pyH(k{|M*PzKQ-VU z?L**Fg?xhb6M#*~$qivr@PHZ=L3IdiWpXyhpDAZ^7Ck4u)%G9`mC6kz>_jwt*--Mn zI@XU#*6kD5&GH)8-m3jlR!93cpjUli5a?4l)yW5xlB!H4#F4J>6c|bg%5==zhh8;< zyd|41?AFey>+YZ$aGVh4n4mH6TB)adN zYK+1QTX*9O8#m{gG5r@PhWvu{NdH>wcM?lXya3nbw$IihoXQR|ZpYSOOQuu&(b}=T@mEqbXCPJXqLOIyr3b!WAfOaIeN;` z-7LGUIB@FC#~qNa$tcN zX5}GhmZSvke@P$`rZPL;e>k2T2(loybV{`eh9reDyw4RH3)L+0{_^!}LY`8%ZSv(X zrUV3G;^Cqgrl1<+E#$K7eJT&}`=%UkF)5?@n**RqY!GS7IfP0i;CzQDvcT<&My#AO zF*fr!GY?U*wL&wyAZP=1+Qi~nVslG71q?od^V8Vqh^&c7-pUnF?Di1DTMGeFr%jIJnq3Q=CtEJBZ_*;5gE(xykw7*}hyR9nX9@#V3r8;{bg= z9k7}3uloES-TjX}gdbiYiYId4y`QHL>l5m+*$u)umJtKqa7I!B zrxZZFnHOBz$eMEvzmLF>{uWRmRIz35G5{1bhdJrWmsOl9dOYRFs%(mZ4TJfS3GUoY z8%!{#xmMt_#(CV~1B|S-+k=;OYwgZ>zdOx{bV!sg7PlQ%>~3rn9h{y{k%j&?l)QCzmpt zHxxOZhMRAamdjbDet~6M;}H_T<)05-)m@PTUhZTgox1US@22rBt{ z-J$qMz3nMW5lg-gSZYiiq_NTjy6dn7UP4QJVp2i=3C6W}#NW{4pV))OEIfENSc%N5 zbbE!*rXyV#@6d8HG0N;1ObGGvQsuc!K@Wsp*` zj5Feo3Uo)L`d(V)_%z0LSuP*wifuNhm>MU`mP|5@&}jDLHOFDCobqak;&7M-#@@sJ zeFwsYVpHiYKvWb576^#v*J1t7STX-*6BG~_&>2S=?GXV^hB;-|FNckyOcD+q1iq}^ zl%7>;YWnSDtZ(>_2##64LRp>! z*9*pH*4_{4p0`^SZ@eLG!1*OOgO^AW$vzk|~XXb*oA8s&FrVSWL}fi3lc5@!SyzO+s%&`?Oe4 zkR;*QmG35AhhA2SfnDHMst&Gbu8v2CbziPhoC3y$#s^&wx>%s65fF~LQNgVrv@mxR zUq4}YMRP*IJc$7-3xj3VGR{d6j^BbJ%WPJ!5>-JxBeLC-&aBEt1x+U_>6S}iMZJ^S z{JJ8w*pMp*q;3Av#JW3}di-p3a0qT$C*YE=iO|fgLuudeK7z#s& zNv`(!Jnv6-HodjGbW|B!i*4|(Ys5zBJ~*>wcA0o%dipNK0kmzf#N-#>oxdnB9C@qJ zqmO+ij2xS!AfV7k_eP~hiZHVz>MJGKryBS=Jt9|x%z6QXsaUE)u?4@0oV~6XtKPH!#1vpK* z)jy-Us#vP8CUT(F$k5$gTT-(9BIt=Sr2MEW=D5G!{LNB#?;~qb<#@@alHw*Y(rGCW z_UNXnRikDXV|HCJ)w{NGc>Ec*^~BeMOa69Pq~D7xb5A}Z)SD|_#A(FgMFKXqvAD$jkUTW(k@wWxE*Am-Wev1Tj7 zx-)GB`2%V-{OznB{Oz0`D>^RL{NtQC5J+7-m_u0m9x_QZ-baTi4OCwin2G4vRVd|b z#uKAfml%wjM@rWl#jJ3<4tO|rB7EHt-EWh~20>YsLjEjp3>!_lsi{&A`x9vIP&8bv0rcuU>SbE>yjv-bF98a?n5Z z<&ufd_I~;=Do5YX@x^DfqPGI(W+K4cO!w=+^@~&HUr$>^Dr){Gv`Bpe{Rv6F7MdUx zW)A&O7-s9M5-=uW)1EvCe`{eL^`P&QGCi_4%qNQW+w4%O7!gmvmj0*X7x`$C9w`}I z4;g13Yg{ZHPaaReZ;@%K4PL!ckRkRHLcC|p%H0VHOOvF_i}A_qqo<>=)$}_1&`8^H zvt6CH*&YT7gW^77VXG=wP(6H|y!s`eNHySyo_4s*1 z=f!O8)JUBi;6FWAAFRSw+n8{(KDOC1urUIu`~PH6oZe|(s}4- zFv%F83l3UnrivGQB0Y$6A3I}%UjsuSS5lrv!I7+fmE1qKI@|b3G{lEhwV^4k#)6@t z``RT+{ekO=nQlNxk%qv`1uX6*PM4%>`2;iX1goo$1GGk$e)G!}Iktpzk#0q5lk2BG z*C0cE_oTTG!AWCLL8l?KtKe;#h3|rB#j94i?up(xh?~CE_J?^IY%l53HfL?~l$lH$ z9QV)l9egqA>?4N^MgwjDis2eFsWsyx@54jl)hHan_28D?XK6RS)qj$rJ24+Lc?&)l__&M~~Eks$7NHRVjM8kB= zM2*Q*(ao%}v5^$}0_WY+$FEo4Abng*DPwxz;km27m4?S>tJ|l^o=!_A>N7_qX-!bEf>kUTDZId`-pC}2>~VC`+6y6aEnRr{w%^6Ww_2d$q?v1ErT)c*nRj2k zD4R5}HmXK#oGFBb+e%-lH#dr$u0(C3DvT{C@YPPFaXWQA-NxOH3Y!EA+zqUB9S+RLG+TB1#P43ER_cn=mVZa!pWn!m&=jF`ZEUFd8K z)@|48>!9(1S2XvPNpz4&_1*w2GKmzsrOcT>_AxDqm>1!MydeFD#4mJ+k)uStModCg za62D@jzzP-k+G5VxI(`NzJR$yL1!uG5V#xP2p23@t_r7|-3Z-aE`OD|LDL74$qQGj zAiBkpE5as_=qY@R&kXs>IiHec+Vv~%Ye

i1O>I_eGKr^&y;^hm-V94&lC5U~@}s zTa5yXP^(8uV=BE6Dt+3? zLaE_7J031uGhDE7|b&w1(^tMyMTy4Ezl&wqN)pF3smtPe#|`O&uE&FOFx;Lv>2r%dWXcsINN|d3 zzSwY&aG}}M#$$|eJ8DI()+D|nV{jSt3X)A(IZ~?lt!A^rpo2z1YmxIU1%lU1G1LL~ z*NFxe{FkBI2+QE9yNI%iZ4g#m1ZMFxYZ10u>`lpV;=aIeh-*?bt~GKz2fOafq^Y$g zM%cM+$NDj_M%^@usUdiHr=2peP3Y;}A%428!?wZ1t|ZvKyDc#F=ivQGFk$n$Auf_1 z)(P76l5Zj$=3GpYqa$Aox}1I>2ghWjQ1cu{;^*$HRyLhx?bh*pJ)4^KC^Ti5mx3`! zvq54tCQ8wFggOQH(f{j65`l?f+7=HujTDJq%0}^CEPFKPxXlgtM$%#TJB|{_HY7WM zd$9`r31iNC3?q53j|V!gg-2DPTYgyV?leMeIyCC^Np)_#+#JG5N3RiYT!$iODzD5A z6YnL^J_$q67@&_DHO|XTjV7qNFUdl}?SlKC>tG$*#ANykA3n~@zH4~pwrK89kqYs~ zSO`7+bxlDW-G#QxDvwT)N*#jp0`2Cv3kbvct-1AYh_@X*vgtjvB$wGRQ#=vi{<*DF z1Bh84t}k!gqK!CAPYZQId%Xi0FyxOvKCz5?x@0X*=|yS_6KV?iX!k=J;1;lT=_hz} zV=L5=<93TT&R^bhK#YsG)gu;R$yf4TTK0PRo?+jke){Nn3knK58FEQES$;Py4-j^G z(PxSif~@1(9Q{#mzxI-64DH)s7Yo=8Z4%`WBkwkcvPME0UF!K%y(a+o`}UBc@#`*3by8Z}nAp3?M6pH77=*ri%#`G z1QtkLqKP)tWpQF`2<_A6X&3Kj6}edvVlZF^2D?8dS3ObqWNwC zOIenicvYA65wda#o|w6m)lF2wmrdDsn*hSj7*D6e69S?j~z zk(0vUKo34jm&;RR0=U1()qpAHGe6dN0{eTD(xaRBK?0hy1z;><{QXNjG8ek|8)3)_iqnw}LZMK$v> zcP>Dw%~pt!=i@kesT1!3flkCj`90M=pS#q`TcWS1IWH~c(~TT<0|S<;Ugeay#fHqM zJQ3xWaJNGVCn0LC2t=mGq6n`!LbUHW_?@c_v%j(>0%{`@K^ll2$dZF9Bt}qbpR27XI#7 z;U+c(3^sbw9~!92lm}5O2ccjQ zWP+fnk`Y);H#FHdqQ1i4ozrAYV;T`diRyj;tH8UHfkT-Ix!6wNNo0_jkx3`w-eb56PVT}m! z1Lohml~_I04KHA|F$Dcv`{S?O>Ob2`|7Edp^m^(Q(xaRvR35Mtcn^=BVK#j&IC7;6 zwepMCRBECVZ*qEYsG#E^&e1)9)tfu>s&RL^@i2C- zY2oU4^msrZ3KB&g4Pl)`L7(sok*F4A2(ppxtn^vZVxbZ0O!nac=@WIal@ADc03m>q zV4O~~o)#h+8qb)F)FLw;C~+l^8!d^=Yp)_UFQHae#4vcDjb&R+pvpGW+Rr$1Wbs%L zNUlR)6%L3w?Gkri+%?_>exZp=8+NXwGZ^2KHCbclCCRST&H%%^tQ}O}W3I?Vq5S@> zQN2o74BTWy0ks%2111cNdL-vP7AHdR=A{uLeYB*kD;b<@b0U*rw~TdaaXGXEbM0cQ zCpxukm)k;`M*P}PY-{Nb3HH>hI;~c%$-QG&Z1%hL#7I1OEo=f$u9a%aLMb6>Ov9P> zHcM2@&oRJoN5)?^GP=XndpF!Lccdd2{Na2x#+X8|J%iMsr zBE*WMg3U}K_*z&RWY`E(;yLc7IZ3h+#;7TT>kAZAX&28F;W7`=bw*n`uO3woyC?-kF~99-?(=qf@bQg@*mw8t;f@}$ zFUn4>$|}JG@pYra1v+m$O1NV#np*C1*N1RlgXts+LJo3Eh+lI?hx_zS7?5Chqc@L= zee;2E38C=ITE7R8zok9Yy7j%kd(8TLCa8uGTR=SEG|^ZCX`8Zi^05UECSKxjO_luw zuW@tF=a@@l0k*cr5Nw`yI3wG0!j}hHOPr(WV-s#q6V*NyNF#JZ^!rN;Z5c(AuX%5K zM-cSO_ktIMpc`#~USvnFC-;Jvec-|rnjbRj2xRhs6``a89lhK}}SIMA??Kul-qE0}hn{(s5eKb02g3JKp&&xim%gn-Wv834dh_x}$G z07k`MB>;B*7Wf|+1Pv4*Tn+^Vgafef0eJr}>OEj|_;Dctod|yaDl4iaNG~ZT1~{ny z_hiPcx7GrHe^3T|&;Xx5uRP!HZ;StzOjb}%QcP4)>4mJ=Ul9kQyS`Xl-G zTdglXVEy$yuRoao1o%@H>d#2)&vVqf8aRsvnCEQ(CI8<1e_pk|z<|I06X5ez9bkRu z;wtFqU;>DJYpVYkP=lZx|*2EW;l!na}aSiriMT z0C`maq^AEHo-V*v{2M%RJ6!_{LuEktc?oMvLm6vpb4Q!sL*s2~FZBXI?g72@`-bqJ zSFJCg&;AY?5cAi<(H?NFOw826P*7jr(B58F*Gkv;Po;(Dc^`q-5&IrN`wn2w`+MFA z0Gg)1$-C+w${YYwhmonVBOrV>Ae8*?^RD2DZ@L4h5^O-;8UM^U;9l!{kMvvU=j|y0 z5R0(*ZyC>bWo%XfWE==kJKsl)Kd)L}dW_$q%UB!#*IF2}I|@Sw_?cJ%%KZIU)Sp+a zZzn(l@aFu)k;e_Z9J6 za`#W@THgeKhRkm&;zzaqx8dix#Nkgo2f%>-8@#_Y{eKfYd|n&R<@0{Bc&YxD<)25K z=c+kB;b!0b-{kpY0RHU+`q%t@-`skxdh-*|`0an!z@pBQGKad+*WUQO z-kwWv{6s}D{1>SIAjt7N|Ieikelnq%{0pYPItl?wXY*I#gXdJwBhCM$0y6(6s{ayq z{yF^fkm*0+6D|G;{zpsvQvmhnRL^6t{-kPj_!m@vRNDWluIJ$^f08UZ{|l0T4rckh zM9-uB{UpP3|0lA)?nXbyd>(7+C+4ZoKVkl8kUyaR`0*sP<1z(dhrp`TODO zxlip+%+ERh8}m0Fw$C&D+=1~Y!FJxiAo%Mj_4^V$cOUyn&|mlq!Jo6lzcKtiFUfOv zkDo+7rN0pU(p~@QaeKan{u4Q<;uqv!JJTP~zu!=QUWn&gmp`dotN)4Wf6S%NckF(` zF4X)J?2iHCANKE_7vQ;F|0ffE<1b9l-yi-`cmJb6&uz^=X+AgoLh~2c9|ij3_77v< zbED%=eDT(Qf&br4kk9MuxgF&v(F(vY@W-1uU_tnMtI8{BaKKyOcXb&iAS6KOxwoC) GfBiq \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..5f192121e --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/kotlin-koans.iml b/kotlin-koans.iml index 772215cd1..3539cc0a7 100644 --- a/kotlin-koans.iml +++ b/kotlin-koans.iml @@ -3,8 +3,9 @@ + - + @@ -28,5 +29,4 @@ - - + \ No newline at end of file diff --git a/src/i_introduction/_0_Hello_World/HelloWorld.kt b/src/i_introduction/_0_Hello_World/HelloWorld.kt index f018f00a7..595adfbcc 100644 --- a/src/i_introduction/_0_Hello_World/HelloWorld.kt +++ b/src/i_introduction/_0_Hello_World/HelloWorld.kt @@ -7,8 +7,7 @@ import util.doc0 * Description of task. */ fun todoTask0(): Nothing = TODO( - """ - + """ Task 0 Introduction @@ -45,9 +44,9 @@ fun todoTask0(): Nothing = TODO( Using 'references' you can usually navigate and see the code mentioned in the task description. To start please make the function 'task0' return "OK". - """, - documentation = doc0(), - references = { task0(); "OK" } + """, + documentation = doc0(), + references = { task0(); "OK" } ) /** diff --git a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt index 4697cb518..7e8bee001 100644 --- a/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt +++ b/src/i_introduction/_10_Object_Expressions/ObjectExpressions.kt @@ -4,17 +4,23 @@ import util.TODO import util.doc10 import java.util.* +/** + * Description of task. + */ fun todoTask10(): Nothing = TODO( - """ + """ Task 10 - Read about object expressions that play the same role in Kotlin as anonymous classes do in Java. + Object expressions that play the same role in Kotlin as anonymous classes do in Java. + + The goal of this task is to sort the List of Int using an object expression that + provides a comparator to sort a list of Int in a descending order using java.util.Collections + class. - Add an object expression that provides a comparator to sort a list in a descending order using java.util.Collections class. - In Kotlin you use Kotlin library extensions instead of java.util.Collections, - but this example is still a good demonstration of mixing Kotlin and Java code. - """, - documentation = doc10() + In Kotlin you use Kotlin library extensions instead of java.util.Collections, but this example + is still a good demonstration of mixing Kotlin and Java code. + """, + documentation = doc10() ) /** diff --git a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt index dd3dec0e2..3bb405df3 100644 --- a/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt +++ b/src/i_introduction/_11_SAM_Conversions/SAMConversions.kt @@ -4,20 +4,47 @@ import util.TODO import util.doc11 import java.util.* +/** + * Description of task. + */ fun todoTask11(): Nothing = TODO( - """ + """ Task 11 - When an object implements a SAM interface (one with a Single Abstract Method), you can pass a lambda instead. + A Java class is considered to implement the SAM (Single Abstract Method) interface when it provides + a single worker method (you can ignore methods like toString(), equals(), hashCode()). Looking + at Java's Collection interface + + https://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html + + We see that Comparator (ignoring the equals() method) only requires implementation + a of single methodL: compare(T, T). Although compare(T, T) isn't Abstract (for that + matter Comparator is an Interface, not an abstract class), none the less, Comparator + is still consdered to be a SAM. + + When the parameter to a function is of a type that implements the SAM pattern, + you can replace that parameter with a simple lamba that contains the correct number + of arguments. + Read more about SAM conversions in the blog posts about Kotlin. - Rewrite the previous example changing an object expression to a lambda. - """, - documentation = doc11() + The goal of this task is to rewrite the previous example changing an object expression + to a single lambda. The bulk of the code is already in place, just replace, within + task11(), the text "todoTask11()" with the appropriate expression or logic. + """, + documentation = doc11() ) +/** + * MODIFY the function body of this method as described in todoTask11(). + * The ideal solution to this will be to replace the text of "todoTask11()" in the function body + * with an expression like that found in the body of the comparator(x Int: y:Int) { } function + * you wrote in task10. + * + * @return a List of Int where the list is a pre-specified list of Int's sorted in descending order. + */ fun task11(): List { val arrayList = arrayListOf(1, 5, 2) - Collections.sort(arrayList, { x, y -> todoTask11() }) + Collections.sort(arrayList, { l, r -> todoTask11() }) return arrayList } diff --git a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt index 7149897b1..b422ab3b0 100644 --- a/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt +++ b/src/i_introduction/_12_Extensions_On_Collections/ExtensionsOnCollections.kt @@ -3,20 +3,36 @@ package i_introduction._12_Extensions_On_Collections import util.TODO import util.doc12 +/** + * Description of task. + */ fun todoTask12(): Nothing = TODO( - """ + """ Task 12 - In Kotlin standard library there are lots of extension functions that make the work with collections more convenient. - Rewrite the previous example once more using an extension function 'sortedDescending'. + The Kotlin standard library includes many extension functions, making the Java standard library + easier to use and more convenient. + + One of the additions Kotlin makes to the Java standard library is adding the sortedDescending() + to Java's Collection object. + + Rewrite the previous example once more using Kotin's Collection extension function 'sortedDescending()'. + + Because Kotlin can extend existing Java classes, it wasn't necessary for Kotin to introduce their + own Collection-related classes. Kotlin mere using the existing Java classes but improves them. - Kotlin code can be easily mixed with Java code. - Thus in Kotlin we don't introduce our own collections, but use standard Java ones (slightly improved). Read about read-only and mutable views on Java collections. - """, - documentation = doc12() + """, + documentation = doc12() ) +/** + * MODIFY the function body of this method as described in todoTask11(). + * The ideal solution to this will be to replace the text of "todoTask11()" in the function body + * with an expression must like the body of the comparator(x Int: y:Int) function you write in task10. + * + * @return a List of Int where the list is a pre-specified list of Int's sorted in descending order. + */ fun task12(): List { todoTask12() return arrayListOf(1, 5, 2) diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java index f373204aa..314cd8f3d 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java @@ -5,6 +5,10 @@ import java.util.Collection; import java.util.Iterator; +/** + * Example class for task 1. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode1 extends JavaCode { public String task1(Collection collection) { StringBuilder sb = new StringBuilder(); diff --git a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt index 7b888d30d..8c297f68a 100644 --- a/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt +++ b/src/i_introduction/_1_Java_To_Kotlin_Converter/JavaToKotlinConverter.kt @@ -6,7 +6,7 @@ import util.TODO * Description of task. */ fun todoTask1(collection: Collection): Nothing = TODO( - """ + """ Task 1 The logic for this task exists in java class JavaCode1, which is in this same folder. @@ -19,14 +19,15 @@ fun todoTask1(collection: Collection): Nothing = TODO( In the future, you will want to reject the automatic conversion and manually translate the code from Java to Kotlin so you don't cheat yourself out of the learning opportunity. - """, - references = { JavaCode1().task1(collection) }) + """, + references = { JavaCode1().task1(collection) }) /** - * Replace the function body of this method as described in todoTask1(). + * Modify the function body of this method as described in todoTask1(). * - * @return the string literal "OK". + * @param collection a collection of Int + * @return If the incoming collection is [1, 12, 3] the output should be "{1, 12, 3}". */ fun task1(collection: Collection): String { todoTask1(collection) diff --git a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt index c895828da..a757ca59b 100644 --- a/src/i_introduction/_2_Named_Arguments/NamedArguments.kt +++ b/src/i_introduction/_2_Named_Arguments/NamedArguments.kt @@ -16,7 +16,7 @@ fun usage() { * Description of task. */ fun todoTask2(): Nothing = TODO( - """ + """ Task 2 The logic defined in 'task1' is re-inventing the wheel. The Kotlin standard library @@ -27,14 +27,15 @@ fun todoTask2(): Nothing = TODO( return the result of collection.joinToString() with appropriate arguments as to provide the same result as task1's JavaCode1.task1(collection). Your solution should pass only two arguments to joinToString(). - """, - documentation = doc2(), - references = { collection: Collection -> task1(collection); collection.joinToString() }) + """, + documentation = doc2(), + references = { collection: Collection -> task1(collection); collection.joinToString() }) /** - * Replace the function body of this method as described in todoTask2(). + * Modify the function body of this method as described in todoTask2(). * - * @return the string literal "OK". + * @param collection a collection of Int + * @return If the incoming collection is [1, 12, 3] the output should be "{1, 12, 3}". */ fun task2(collection: Collection): String { todoTask2() diff --git a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt index ac5fee696..5134b6ec7 100644 --- a/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt +++ b/src/i_introduction/_3_Default_Arguments/DefaultAndNamedParams.kt @@ -7,7 +7,7 @@ import util.doc2 * Description of task. */ fun todoTask3(): Nothing = TODO( - """ + """ Task 3 Several overloads of 'JavaCode3.foo()' can be replaced with one function in Kotlin. @@ -18,9 +18,9 @@ fun todoTask3(): Nothing = TODO( The goal of this task is to change the list of arguments for the function 'foo', provided just below, and replace the function implementation such that the code in task3() will return the String "a42b1C42D2". - """, - documentation = doc2(), - references = { name: String -> JavaCode3().foo(name); foo(name) }) + """, + documentation = doc2(), + references = { name: String -> JavaCode3().foo(name); foo(name) }) /** * Function to create string that contains a specified string, possibly as upper case (default is lowercase), @@ -32,9 +32,10 @@ fun todoTask3(): Nothing = TODO( fun foo(name: String /* more arguments will go here*/): String = todoTask3() /** - * Replace the function body of this method as described in todoTask3(). + * Modify the function body of this method as described in todoTask3(). * - * @return the string literal "OK". + * @return the literal string "a42b1C42D2" as computed by this function with multiple + * calls to the foo(...) function. */ fun task3(): String { return todoTask3() diff --git a/src/i_introduction/_3_Default_Arguments/JavaCode3.java b/src/i_introduction/_3_Default_Arguments/JavaCode3.java index 40ecb16e2..8897f9246 100644 --- a/src/i_introduction/_3_Default_Arguments/JavaCode3.java +++ b/src/i_introduction/_3_Default_Arguments/JavaCode3.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 3. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode3 extends JavaCode { private int defaultNumber = 42; diff --git a/src/i_introduction/_4_Lambdas/JavaCode4.java b/src/i_introduction/_4_Lambdas/JavaCode4.java index 7938be547..7b522ce26 100644 --- a/src/i_introduction/_4_Lambdas/JavaCode4.java +++ b/src/i_introduction/_4_Lambdas/JavaCode4.java @@ -6,6 +6,10 @@ import java.util.Collection; +/** + * Example class for task 4. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode4 extends JavaCode { public boolean task4(Collection collection) { return Iterables.any(collection, new Predicate() { diff --git a/src/i_introduction/_4_Lambdas/Lambdas.kt b/src/i_introduction/_4_Lambdas/Lambdas.kt index bcd753086..800ad8a73 100644 --- a/src/i_introduction/_4_Lambdas/Lambdas.kt +++ b/src/i_introduction/_4_Lambdas/Lambdas.kt @@ -14,7 +14,7 @@ fun example() { * Description of task. */ fun todoTask4(collection: Collection): Nothing = TODO( - """ + """ Task 4 Replace the implementation of task4(collection) to provide the same logic as @@ -23,9 +23,9 @@ fun todoTask4(collection: Collection): Nothing = TODO( You can find the appropriate function to call on 'collection' through IntelliJ's code completion feature. (Don't use the class 'Iterables'). - """, - documentation = doc4(), - references = { JavaCode4().task4(collection) }) + """, + documentation = doc4(), + references = { JavaCode4().task4(collection) }) /** * Replace the function body of this method as described in todoTask4(). diff --git a/src/i_introduction/_5_String_Templates/StringTemplates.kt b/src/i_introduction/_5_String_Templates/StringTemplates.kt index 74cb25cfd..d1d67f09a 100644 --- a/src/i_introduction/_5_String_Templates/StringTemplates.kt +++ b/src/i_introduction/_5_String_Templates/StringTemplates.kt @@ -46,7 +46,7 @@ fun example() = "13.06.1992".matches(getPattern().toRegex()) //true * Description of task. */ fun todoTask5(): Nothing = TODO( - """ + """ Task 5 The goal of this task is to replace the task5() function implementation @@ -57,9 +57,9 @@ fun todoTask5(): Nothing = TODO( that nearly solves this task, but not quite. The solution of your task should use the provided 'month' variable (above). - """, - documentation = doc5(), - references = { getPattern(); month }) + """, + documentation = doc5(), + references = { getPattern(); month }) /** * A regular expression string is capable of matching the months in 3 letter uppercase. diff --git a/src/i_introduction/_6_Data_Classes/DataClasses.kt b/src/i_introduction/_6_Data_Classes/DataClasses.kt index ababbd748..04cc3b970 100644 --- a/src/i_introduction/_6_Data_Classes/DataClasses.kt +++ b/src/i_introduction/_6_Data_Classes/DataClasses.kt @@ -3,17 +3,26 @@ package i_introduction._6_Data_Classes import util.TODO import util.doc6 +/** + * Description of task. + */ fun todoTask6(): Nothing = TODO( - """ + """ Task 6 - Convert 'JavaCode6.Person' class to Kotlin. - Then add a modifier `data` to the resulting class. - This annotation means the compiler will generate a bunch of useful methods in this class: `equals`/`hashCode`, `toString` and some others. - The `task6` function should return a list of persons. - """, - documentation = doc6(), - references = { JavaCode6.Person("Alice", 29) } + The goal of this class is to learn out to simplify the Person class found in JavaCode6 + by writing it in Kotlin and placing the new simplified Person class here in this file by + completing the definition of "class Person" found just below. + + To-do: + * Complete the Person class (just below), your definition should fit on a single line + * Make the class a "data" class to automatically have the compiler generate a bunch of + useful methods in this class: `equals`/`hashCode`, `toString` and some others. + * Modify the task6() function should return a list of Person. Uncomment the //return line + and remove the todoTask6() line. + """, + documentation = doc6(), + references = { JavaCode6.Person("Alice", 29) } ) /** @@ -22,9 +31,10 @@ fun todoTask6(): Nothing = TODO( class Person /** - * Replace the function body of this method as described in todoTask6(). + * Modify the function body of this method as described in todoTask6(). + * The //return line within task6() * - * @return a list of two Person objects, Alice and Bob. + * @return a list of two Person objects, one for Alice and one for Bob. */ fun task6(): List { todoTask6() diff --git a/src/i_introduction/_6_Data_Classes/JavaCode6.java b/src/i_introduction/_6_Data_Classes/JavaCode6.java index 7d18a0f7a..a03ad0344 100644 --- a/src/i_introduction/_6_Data_Classes/JavaCode6.java +++ b/src/i_introduction/_6_Data_Classes/JavaCode6.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 6. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode6 extends JavaCode { public static class Person { diff --git a/src/i_introduction/_7_Nullable_Types/JavaCode7.java b/src/i_introduction/_7_Nullable_Types/JavaCode7.java index df176b6e6..827fc0469 100644 --- a/src/i_introduction/_7_Nullable_Types/JavaCode7.java +++ b/src/i_introduction/_7_Nullable_Types/JavaCode7.java @@ -4,6 +4,10 @@ import org.jetbrains.annotations.Nullable; import util.JavaCode; +/** + * Example class for task 7. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode7 extends JavaCode { public void sendMessageToClient(@Nullable Client client, @Nullable String message, @NotNull Mailer mailer) { if (client == null || message == null) return; diff --git a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt index 0296c292c..4dd423720 100644 --- a/src/i_introduction/_7_Nullable_Types/NullableTypes.kt +++ b/src/i_introduction/_7_Nullable_Types/NullableTypes.kt @@ -12,22 +12,32 @@ fun test() { val j: Int = q?.length ?: 0 // 0 } +/** + * Description of task. + */ fun todoTask7(client: Client?, message: String?, mailer: Mailer): Nothing = TODO( - """ + """ Task 7 - Rewrite JavaCode7.sendMessageToClient in Kotlin, using only one 'if' expression. - Declarations of Client, PersonalInfo and Mailer are given below. - """, - documentation = doc7(), - references = { JavaCode7().sendMessageToClient(client, message, mailer) } -) + The goal of this task is to introduce you to Nullable Types, which Kotin decorates + with the "?" symbol to the right of (but connected to) the type. For example + the type "Int" must contain a valid Int value (such as 15), but the type + "Int?" may contain a normal Int value OR it might be null -fun sendMessageToClient( - client: Client?, message: String?, mailer: Mailer -) { - todoTask7(client, message, mailer) -} + When a val (value) or var (variable) is of a nullable type, before obtaining the + value of that value or variable, Kotlin forces you to verify that the value is not null. + + Replace the body of the below function sendMessageToClient(...) such that + it uses the same logic found in JavaCode7.sendMessageToClient(...). + Use only one 'if' expression. Declarations of Client, PersonalInfo and Mailer are given below. + + Make sure you look at the test() function above and understand the functioning of the + various operators used. Of special interest is the "?:" operator, which is commonly + referred to as the "Elvis Operator". + """, + documentation = doc7(), + references = { JavaCode7().sendMessageToClient(client, message, mailer) } +) class Client (val personalInfo: PersonalInfo?) class PersonalInfo (val email: String?) @@ -35,3 +45,12 @@ class PersonalInfo (val email: String?) interface Mailer { fun sendMessage(email: String, message: String) } + +/** + * Replace the function body of this method as described in todoTask7(). + * + * @return + */ +fun sendMessageToClient(client: Client?, message: String?, mailer: Mailer) { + todoTask7(client, message, mailer) +} diff --git a/src/i_introduction/_8_Smart_Casts/JavaCode8.java b/src/i_introduction/_8_Smart_Casts/JavaCode8.java index 4a40edc0c..8a9548270 100644 --- a/src/i_introduction/_8_Smart_Casts/JavaCode8.java +++ b/src/i_introduction/_8_Smart_Casts/JavaCode8.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 8. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode8 extends JavaCode { public int eval(Expr expr) { if (expr instanceof Num) { diff --git a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt index 1053d034b..05b3c35d6 100644 --- a/src/i_introduction/_8_Smart_Casts/SmartCasts.kt +++ b/src/i_introduction/_8_Smart_Casts/SmartCasts.kt @@ -3,11 +3,20 @@ package i_introduction._8_Smart_Casts import util.TODO import util.doc8 +/** + * Description of task. + */ fun todoTask8(expr: Expr): Nothing = TODO( """ Task 8 - Rewrite 'JavaCode8.eval()' in Kotlin using smart casts and 'when' expression. + The goal of this task is to replace both instances of the code "todoTask8(e)" + within the below function eval(e: Eval) with the appropriate code to perform + the same logic as found in JavCode8.eval(Expr expr). + + As show in the partial solution below in the eval(e: Eval) function, your + solution should use the Kotin "when" control structure. Additionally, make sure + to include an "else" clause. """, documentation = doc8(), references = { JavaCode8().eval(expr) }) @@ -28,7 +37,7 @@ class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Expr /** - * Replace the function body of this method as described in todoTask8(). + * MODIFY the function body of this method as described in todoTask8(). * * @param e a Expr object (such as a Num or Sum object) * @return the solution to the expression e diff --git a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt index fb171b1ae..1b365c1fc 100644 --- a/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt +++ b/src/i_introduction/_9_Extension_Functions/ExtensionFunctions.kt @@ -3,6 +3,7 @@ package i_introduction._9_Extension_Functions import util.TODO import util.doc9 +// 'lastChar' is compiled to a static function in the class ExtensionFunctionsKt (see JavaCode9.useExtension) fun String.lastChar() = this.get(this.length - 1) // 'this' can be omitted @@ -13,18 +14,19 @@ fun use() { "abc".lastChar() } -// 'lastChar' is compiled to a static function in the class ExtensionFunctionsKt (see JavaCode9.useExtension) - +/** + * Description of task. + */ fun todoTask9(): Nothing = TODO( - """ + """ Task 9 - Implement the extension functions Int.r(), Pair.r() - to support the following manner of creating rational numbers: - 1.r(), Pair(1, 2).r() - """, - documentation = doc9(), - references = { 1.r(); Pair(1, 2).r(); RationalNumber(1, 9) }) + The goal of this task is to implement the extension functions Int.r(), Pair.r() + to support the following manner of creating rational numbers: 1.r(), Pair(1, 2).r(). + If a denominator is not provided (as in the case of Int.r()), use a denominator of 1. + """, + documentation = doc9(), + references = { 1.r(); Pair(1, 2).r(); RationalNumber(1, 9) }) /** * A class defining a RationalNumber. Given that this class is a "data" class, diff --git a/src/i_introduction/_9_Extension_Functions/JavaCode9.java b/src/i_introduction/_9_Extension_Functions/JavaCode9.java index 764be041e..62af7d5f9 100644 --- a/src/i_introduction/_9_Extension_Functions/JavaCode9.java +++ b/src/i_introduction/_9_Extension_Functions/JavaCode9.java @@ -2,6 +2,10 @@ import util.JavaCode; +/** + * Example class for task 9. + * DO NOT CHANGE THIS FILE. + */ public class JavaCode9 extends JavaCode { public void useExtension() { char c = ExtensionFunctionsKt.lastChar("abc"); diff --git a/src/ii_collections/_24_JavaCode.java b/src/ii_collections/JavaCode24.java similarity index 90% rename from src/ii_collections/_24_JavaCode.java rename to src/ii_collections/JavaCode24.java index 0dc0e703e..4d187171c 100644 --- a/src/ii_collections/_24_JavaCode.java +++ b/src/ii_collections/JavaCode24.java @@ -8,7 +8,11 @@ import java.util.List; import java.util.Map; -public class _24_JavaCode extends JavaCode { +/** + * Example class for task 24. + * DO NOT CHANGE THIS FILE. + */ +public class JavaCode24 extends JavaCode { public Collection doSomethingStrangeWithCollection(Collection collection) { Map> groupsByLength = Maps.newHashMap(); for (String s : collection) { diff --git a/src/ii_collections/_13_Introduction_.kt b/src/ii_collections/_13_Introduction_.kt index a0bb90f5f..5162f8ecb 100644 --- a/src/ii_collections/_13_Introduction_.kt +++ b/src/ii_collections/_13_Introduction_.kt @@ -3,7 +3,7 @@ package ii_collections import java.util.* /* - * This part of workshop was inspired by: + * This task was inspired by: * https://github.com/goldmansachs/gs-collections-kata */ @@ -18,11 +18,10 @@ fun example0(list: List) { } /** - * Description of work. + * Description of task. */ fun todoTask13(): Nothing = util.TODO( """ - Task 13 In Kotlin, there are many operations that help to transform one collection into another, starting with 'to'. diff --git a/src/ii_collections/_14_FilterMap.kt b/src/ii_collections/_14_FilterMap.kt index c547b44b6..ddb032b49 100644 --- a/src/ii_collections/_14_FilterMap.kt +++ b/src/ii_collections/_14_FilterMap.kt @@ -1,21 +1,32 @@ package ii_collections fun example1(list: List) { - // If a lambda has exactly one parameter, that parameter can be accessed as 'it' val positiveNumbers = list.filter { it > 0 } val squares = list.map { it * it } } +/** + * Description of task. + */ +fun todoTask14(): Nothing = util.TODO( + """ + Task 14 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCitiesCustomersAreFrom(): Set { // Return the set of cities the customers are from - todoCollectionTask() + todoTask14() } fun Shop.getCustomersFrom(city: City): List { // Return a list of the customers who live in the given city - todoCollectionTask() + todoTask14() } - - diff --git a/src/ii_collections/_15_AllAnyAndOtherPredicates.kt b/src/ii_collections/_15_AllAnyAndOtherPredicates.kt index 788d64bfa..225d394d7 100644 --- a/src/ii_collections/_15_AllAnyAndOtherPredicates.kt +++ b/src/ii_collections/_15_AllAnyAndOtherPredicates.kt @@ -1,39 +1,49 @@ package ii_collections fun example2(list: List) { - val isZero: (Int) -> Boolean = { it == 0 } - val hasZero: Boolean = list.any(isZero) - val allZeros: Boolean = list.all(isZero) - val numberOfZeros: Int = list.count(isZero) - val firstPositiveNumber: Int? = list.firstOrNull { it > 0 } } +/** + * Description of task. + */ +fun todoTask15(): Nothing = util.TODO( + """ + Task 15 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + + """, + references = { shop: Shop -> shop.customers } +) + fun Customer.isFrom(city: City): Boolean { // Return true if the customer is from the given city - todoCollectionTask() + todoTask15() } fun Shop.checkAllCustomersAreFrom(city: City): Boolean { // Return true if all customers are from the given city - todoCollectionTask() + todoTask15() } fun Shop.hasCustomerFrom(city: City): Boolean { // Return true if there is at least one customer from the given city - todoCollectionTask() + todoTask15() } fun Shop.countCustomersFrom(city: City): Int { // Return the number of customers from the given city - todoCollectionTask() + todoTask15() } fun Shop.findAnyCustomerFrom(city: City): Customer? { // Return a customer who lives in the given city, or null if there is none - todoCollectionTask() + todoTask15() } diff --git a/src/ii_collections/_16_FlatMap.kt b/src/ii_collections/_16_FlatMap.kt index 85523c0a5..7977dcccc 100644 --- a/src/ii_collections/_16_FlatMap.kt +++ b/src/ii_collections/_16_FlatMap.kt @@ -1,18 +1,30 @@ package ii_collections fun example() { - val result = listOf("abc", "12").flatMap { it.toList() } - result == listOf('a', 'b', 'c', '1', '2') } +/** + * Description of task. + */ +fun todoTask16(): Nothing = util.TODO( + """ + Task 16 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + val Customer.orderedProducts: Set get() { // Return all products ordered by customer - todoCollectionTask() + todoTask16() } val Shop.allOrderedProducts: Set get() { // Return all products that were ordered by at least one customer - todoCollectionTask() + todoTask16() } diff --git a/src/ii_collections/_17_MaxMin.kt b/src/ii_collections/_17_MaxMin.kt index 78b09060b..88425d008 100644 --- a/src/ii_collections/_17_MaxMin.kt +++ b/src/ii_collections/_17_MaxMin.kt @@ -5,12 +5,26 @@ fun example4() { val longestString = listOf("a", "b").maxBy { it.length } } +/** + * Description of task. + */ +fun todoTask17(): Nothing = util.TODO( + """ + Task 17 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomerWithMaximumNumberOfOrders(): Customer? { // Return a customer whose order count is the highest among all customers - todoCollectionTask() + todoTask17() } fun Customer.getMostExpensiveOrderedProduct(): Product? { // Return the most expensive product which has been ordered - todoCollectionTask() + todoTask17() } diff --git a/src/ii_collections/_18_Sort_.kt b/src/ii_collections/_18_Sort_.kt index 1bbbe4887..89385d934 100644 --- a/src/ii_collections/_18_Sort_.kt +++ b/src/ii_collections/_18_Sort_.kt @@ -2,11 +2,24 @@ package ii_collections fun example5() { val result = listOf("a", "bbb", "cc").sortedBy { it.length } - result == listOf("a", "cc", "bbb") } +/** + * Description of task. + */ +fun todoTask18(): Nothing = util.TODO( + """ + Task 18 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomersSortedByNumberOfOrders(): List { // Return a list of customers, sorted by the ascending number of orders they made - todoCollectionTask() + todoTask18() } diff --git a/src/ii_collections/_19_Sum_.kt b/src/ii_collections/_19_Sum_.kt index f7c969f23..9c2480288 100644 --- a/src/ii_collections/_19_Sum_.kt +++ b/src/ii_collections/_19_Sum_.kt @@ -4,8 +4,22 @@ fun example6() { val sum = listOf(1, 5, 3).sum() } +/** + * Description of task. + */ +fun todoTask19(): Nothing = util.TODO( + """ + Task 19 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Customer.getTotalOrderPrice(): Double { // Return the sum of prices of all products that a customer has ordered. // Note: a customer may order the same product for several times. - todoCollectionTask() + todoTask19() } diff --git a/src/ii_collections/_20_GroupBy.kt b/src/ii_collections/_20_GroupBy.kt index dd364ea93..ee8396d6b 100644 --- a/src/ii_collections/_20_GroupBy.kt +++ b/src/ii_collections/_20_GroupBy.kt @@ -2,11 +2,24 @@ package ii_collections fun example7() { val result = listOf("a", "b", "ba", "ccc", "ad").groupBy { it.length } - result == mapOf(1 to listOf("a", "b"), 2 to listOf("ba", "ad"), 3 to listOf("ccc")) } +/** + * Description of task. + */ +fun todoTask20(): Nothing = util.TODO( + """ + Task 20 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.groupCustomersByCity(): Map> { // Return a map of the customers living in each city - todoCollectionTask() + todoTask20() } diff --git a/src/ii_collections/_21_Partition_.kt b/src/ii_collections/_21_Partition_.kt index f95b0da35..80c6849af 100644 --- a/src/ii_collections/_21_Partition_.kt +++ b/src/ii_collections/_21_Partition_.kt @@ -10,7 +10,21 @@ fun example8() { negative == listOf(-4, -11) } +/** + * Description of task. + */ +fun todoTask21(): Nothing = util.TODO( + """ + Task 21 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set { // Return customers who have more undelivered orders than delivered - todoCollectionTask() + todoTask21() } diff --git a/src/ii_collections/_22_Fold_.kt b/src/ii_collections/_22_Fold_.kt index aa1a6d29c..07f9dfdc8 100644 --- a/src/ii_collections/_22_Fold_.kt +++ b/src/ii_collections/_22_Fold_.kt @@ -12,10 +12,23 @@ fun whatFoldDoes(): Int { return result } +/** + * Description of task. + */ +fun todoTask22(): Nothing = util.TODO( + """ + Task 22 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set { // Return the set of products ordered by every customer - return customers.fold(allOrderedProducts, { - orderedByAll, customer -> - todoCollectionTask() + return customers.fold(/*allOrderedProducts*/ todoTask22(), { orderedByAll, customer -> + todoTask22() }) } diff --git a/src/ii_collections/_23_CompoundTasks.kt b/src/ii_collections/_23_CompoundTasks.kt index 35bd7eb7b..78740f6cb 100644 --- a/src/ii_collections/_23_CompoundTasks.kt +++ b/src/ii_collections/_23_CompoundTasks.kt @@ -1,18 +1,32 @@ package ii_collections +/** + * Description of task. + */ +fun todoTask23(): Nothing = util.TODO( + """ + Task 23 + + Task for working with collections. + Look through the 'Shop' API; all tasks are connected with it. + Return what is described in the name and the comment. + """, + references = { shop: Shop -> shop.customers } +) + fun Shop.getCustomersWhoOrderedProduct(product: Product): Set { // Return the set of customers who ordered the specified product - todoCollectionTask() + todoTask23() } fun Customer.getMostExpensiveDeliveredProduct(): Product? { // Return the most expensive product among all delivered products // (use the Order.isDelivered flag) - todoCollectionTask() + todoTask23() } fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int { // Return the number of times the given product was ordered. // Note: a customer may order the same product for several times. - todoCollectionTask() + todoTask23() } diff --git a/src/ii_collections/_24_ExtensionsOnCollections.kt b/src/ii_collections/_24_ExtensionsOnCollections.kt index 4311e0d64..67d817e65 100644 --- a/src/ii_collections/_24_ExtensionsOnCollections.kt +++ b/src/ii_collections/_24_ExtensionsOnCollections.kt @@ -2,13 +2,17 @@ package ii_collections import util.TODO +/** + * Description of task. + */ fun todoTask24(): Nothing = TODO( - """ - Task 24. - The function should behave the same as '_24_JavaCode.doSomethingStrangeWithCollection' + """ + Task 24 + + The function should behave the same as 'JavaCode24.doSomethingStrangeWithCollection' Replace all invocations of 'todoTask24()' with the appropriate code. - """, - references = { c: Collection -> _24_JavaCode().doSomethingStrangeWithCollection(c) } + """, + references = { c: Collection -> JavaCode24().doSomethingStrangeWithCollection(c) } ) fun doSomethingStrangeWithCollection(collection: Collection): Collection? { diff --git a/src/ii_collections/shop.kt b/src/ii_collections/shop.kt index 4d999e6ed..2aba781ce 100644 --- a/src/ii_collections/shop.kt +++ b/src/ii_collections/shop.kt @@ -1,5 +1,9 @@ package ii_collections +/** + * Pre-defined classes for tasks in ii_collections. + * DO NOT CHANGE THIS FILE. + */ data class Shop(val name: String, val customers: List) data class Customer(val name: String, val city: City, val orders: List) { diff --git a/src/ii_collections/todoUtil.kt b/src/ii_collections/todoUtil.kt deleted file mode 100644 index a497b66be..000000000 --- a/src/ii_collections/todoUtil.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ii_collections - -import util.TODO - -fun todoCollectionTask(): Nothing = TODO( - """ - Task for working with collections. - Look through the 'Shop' API; all tasks are connected with it. - Return what is described in the name and the comment. - - """, - references = { shop: Shop -> shop.customers } -) diff --git a/src/iii_conventions/MyDate.kt b/src/iii_conventions/MyDate.kt index 147cc3e78..7954cc35d 100644 --- a/src/iii_conventions/MyDate.kt +++ b/src/iii_conventions/MyDate.kt @@ -1,5 +1,10 @@ package iii_conventions +/** + * Pre-defined classes for tasks in iii_conventions + * DO NOT CHANGE THIS FILE. + */ + data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) operator fun MyDate.rangeTo(other: MyDate): DateRange = todoTask27() diff --git a/src/iii_conventions/MyDateUtil.kt b/src/iii_conventions/MyDateUtil.kt index 6594e5b3b..08cb78a9d 100644 --- a/src/iii_conventions/MyDateUtil.kt +++ b/src/iii_conventions/MyDateUtil.kt @@ -3,6 +3,11 @@ package iii_conventions import iii_conventions.TimeInterval.* import java.util.* +/** + * Pre-defined extension functions for the MyDate class for tasks in iii_conventions + * DO NOT CHANGE THIS FILE. + */ + fun MyDate.nextDay() = addTimeIntervals(DAY, 1) fun MyDate.addTimeIntervals(timeInterval: TimeInterval, number: Int): MyDate {