├── .gitignore ├── .idea └── inspectionProfiles │ └── Project_Default.xml ├── License.md ├── README.md ├── input ├── align_in1.txt ├── center_in1.txt ├── chaotic_in1.txt ├── empty.txt ├── expr_in1.txt ├── expr_in2.txt ├── expr_in3.txt ├── expr_in4.txt ├── expr_in5.txt ├── expr_in6.txt ├── markdown_lists.md ├── markdown_simple.md ├── onegin.txt ├── sibilants_in1.txt ├── substrings_in1.txt ├── top20.txt ├── trans_in1.txt └── width_in1.txt ├── pom.xml ├── src ├── lesson1 │ └── task1 │ │ └── Simple.kt ├── lesson10 │ ├── task1 │ │ └── Regex.kt │ └── task2 │ │ └── Html.kt ├── lesson2 │ ├── task1 │ │ └── IfElse.kt │ └── task2 │ │ └── Logical.kt ├── lesson3 │ └── task1 │ │ └── Loop.kt ├── lesson4 │ └── task1 │ │ └── List.kt ├── lesson5 │ └── task1 │ │ └── Map.kt ├── lesson6 │ └── task1 │ │ └── Parse.kt ├── lesson7 │ └── task1 │ │ └── Files.kt ├── lesson8 │ ├── task1 │ │ └── Geometry.kt │ ├── task2 │ │ └── Chess.kt │ └── task3 │ │ └── Graph.kt └── lesson9 │ ├── task1 │ └── Matrix.kt │ └── task2 │ └── Matrices.kt ├── test ├── lesson1 │ └── task1 │ │ └── Tests.kt ├── lesson10 │ ├── task1 │ │ └── Tests.kt │ └── task2 │ │ └── Tests.kt ├── lesson2 │ ├── task1 │ │ └── Tests.kt │ └── task2 │ │ └── Tests.kt ├── lesson3 │ └── task1 │ │ └── Tests.kt ├── lesson4 │ └── task1 │ │ └── Tests.kt ├── lesson5 │ └── task1 │ │ └── Tests.kt ├── lesson6 │ └── task1 │ │ └── Tests.kt ├── lesson7 │ └── task1 │ │ └── Tests.kt ├── lesson8 │ ├── task1 │ │ └── Tests.kt │ ├── task2 │ │ └── Tests.kt │ └── task3 │ │ └── Tests.kt └── lesson9 │ ├── task1 │ └── Tests.kt │ └── task2 │ └── Tests.kt └── tutorial ├── chapter00.adoc ├── chapter01.adoc ├── chapter02.adoc ├── chapter03.adoc ├── chapter04.adoc ├── chapter04_5.adoc ├── chapter05.adoc ├── chapter06.adoc ├── chapter06_5.adoc ├── chapter07.adoc ├── chapter08.adoc ├── chapter08_5.adoc ├── chapter09.adoc └── chapter10.adoc /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | 4 | .idea/*.xml 5 | .idea/copyright 6 | .idea/libraries 7 | .idea/shelf 8 | target 9 | out 10 | 11 | *.iml 12 | *.ipr 13 | *.iws 14 | *.zip -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Котлин как первый язык программирования 2 | 3 | Решите приведённые в проекте задачи, чтобы научиться программировать на Котлине. Сейчас доступны девять групп задач (уроков). 4 | 5 | 1. Простые функции. 6 | 2. Ветвления. 7 | 3. Циклы. 8 | 4. Работа со списками. 9 | 5. Работа с ассоциативными массивами (Maps). 10 | 6. Разбор строк. 11 | 7. Работа с файлами. 12 | 8. Простые классы. 13 | 9. Сложные классы на примере матриц. 14 | 15 | В директории tutorial имеется учебное пособие по Котлину на русском языке. 16 | Оно содержит введение и восемь разделов, каждый из которых относится к соотвествующему уроку в проекте. 17 | Также пособие включает дополнительные разделы, не включающие задач: 18 | 19 | * 4.5 (про организацию памяти) 20 | * 6.5 (про регулярные выражения) 21 | * 8.5 (про графы и графовые алгоритмы) 22 | * 10 (про синтаксический разбор и Kotlin DSL) 23 | 24 | ## Список использованных источников 25 | 26 | Формулировки многих задач взяты из следующей книги: 27 | 28 | * А. Юркин. Задачник по программированию. СПб.: Питер, 2002. - 192 с. ISBN 5-318-00399-0. 29 | 30 | Материалы по языку Kotlin: 31 | 32 | * http://kotlinlang.org/docs/reference 33 | -------------------------------------------------------------------------------- /input/align_in1.txt: -------------------------------------------------------------------------------- 1 | Для написания разных видов программ сейчас применяются разные языки программирования. 2 | Например, в сфере мобильных программ сейчас правят бал языки Swift (мобильные устройства под управлением iOS) 3 | и Java (устройства под управлением Android). 4 | Системные программы, как правило, пишутся на языках C или {cpp}. 5 | Эти же языки долгое время использовались и для создания встраиваемых программ, 6 | но в последние годы в этой области набирает популярность язык Java. 7 | Для написания web-клиентов часто используется JavaScript, а в простых случаях -- язык разметки страниц HTML. 8 | Web-серверы используют опять-таки Java (в сложных случаях), а также Python и PHP (в более простых). 9 | Наконец, простые desktop-программы сейчас могут быть написаны на самых разных языках, 10 | и выбор во многом зависит от сложности программы, области её использования, предполагаемой операционной системы. 11 | В первую очередь следует назвать языки Java, {cpp}, C#, Python, Visual Basic, Ruby, Swift. 12 | 13 | Самым универсальным и одновременно самым распространённым языком программирования 14 | на данный момент следует считать язык Java. 15 | Java в широком смысле -- не только язык, но и платформа для выполнения программ 16 | под самыми разными операционными системами и на разной аппаратуре. 17 | Такая универсальность обеспечивается наличием виртуальной машины Java -- 18 | системной программы, интерпретирующей Java байт-код в машинные коды конкретного компьютера или системы. 19 | Java также включает богатейший набор библиотек для разработки. 20 | -------------------------------------------------------------------------------- /input/center_in1.txt: -------------------------------------------------------------------------------- 1 | Съешь же ещё этих мягких французских булок, да выпей чаю. 2 | Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства. 3 | Тест 4 | 5 | Hello World 6 | Во входном файле с именем inputName содержится некоторый текст. 7 | Вывести его в выходной файл с именем outputName, выровняв по центру. -------------------------------------------------------------------------------- /input/chaotic_in1.txt: -------------------------------------------------------------------------------- 1 | Карминовый 2 | Боязливый 3 | Некрасивый 4 | Остроумный 5 | БелогЛазый 6 | ФиолетОвый 7 | -------------------------------------------------------------------------------- /input/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin-Polytech/KotlinAsFirst2018/4a11d744e49f77c1e39d3dec5a74f334e08d5820/input/empty.txt -------------------------------------------------------------------------------- /input/expr_in1.txt: -------------------------------------------------------------------------------- 1 | 3*x*x - 2 / x + 7 -x -------------------------------------------------------------------------------- /input/expr_in2.txt: -------------------------------------------------------------------------------- 1 | (4 * x - 3) / (x - 2) -------------------------------------------------------------------------------- /input/expr_in3.txt: -------------------------------------------------------------------------------- 1 | alpha -------------------------------------------------------------------------------- /input/expr_in4.txt: -------------------------------------------------------------------------------- 1 | -x *(x *x -x) -------------------------------------------------------------------------------- /input/expr_in5.txt: -------------------------------------------------------------------------------- 1 | (4845 * x - 388) / (x - 264) -------------------------------------------------------------------------------- /input/expr_in6.txt: -------------------------------------------------------------------------------- 1 | 1 - x * x - x -------------------------------------------------------------------------------- /input/markdown_lists.md: -------------------------------------------------------------------------------- 1 | * Утка по-пекински 2 | * Утка 3 | * Соус 4 | * Салат Оливье 5 | 1. Мясо 6 | * Или колбаса 7 | 2. Майонез 8 | 3. Картофель 9 | 4. Что-то там ещё 10 | * Помидоры 11 | * Фрукты 12 | 1. Бананы 13 | 23. Яблоки 14 | 1. Красные 15 | 2. Зелёные 16 | -------------------------------------------------------------------------------- /input/markdown_simple.md: -------------------------------------------------------------------------------- 1 | Lorem ipsum *dolor sit amet*, consectetur **adipiscing** elit. 2 | Vestibulum lobortis. ~~Est vehicula rutrum *suscipit*~~, ipsum ~~lib~~ero *placerat **tortor***. 3 | 4 | Suspendisse ~~et elit in enim tempus iaculis~~. 5 | -------------------------------------------------------------------------------- /input/sibilants_in1.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Простая 3 | * 4 | * В русском языке, как правило, после букв Ж, Ч, Ш, Щ пишется И, А, У, а не Ы, Я, Ю. 5 | * Во входном файле с именем inputName содержытся некоторый текст. 6 | * Проверить текст во входном файле на соблюдение данного правила и вывести в выходной 7 | * файл outputName текст с исправленными ошыбками. 8 | * 9 | * Регистр заменённых букв следует сохранять. 10 | * 11 | * Исключения (жЮри, броШЮра, параШют) в рамках данного задания обрабатывать не нужно 12 | * 13 | * жЫ шЫ ЖИ Ши ЖЯ шЯ Жа ша жу шу жю щю чя шю щю щя жя жи жы жю чю чя 14 | */ -------------------------------------------------------------------------------- /input/substrings_in1.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Средняя 3 | * 4 | * Во входном файле с именем inputName имеется словарь с одним словом в каждой строчке. 5 | * Выбрать из данного словаря наиболее длинное слово, 6 | * в котором все буквы разные, например: Неряшливость, Четырёхдюймовка. 7 | * Вывести его в выходной файл с именем outputName. 8 | * Если во входном файле имеется несколько слов с одинаковой длиной, в которых все буквы разные, 9 | * в выходной файл следует вывести их все через запятую. 10 | * Регистр букв игнорировать, то есть буквы е и Е считать одинаковыми. 11 | * 12 | * Пример входного файла: 13 | * Карминовый 14 | * Боязливый 15 | * Некрасивый 16 | * Остроумный 17 | * БелогЛазый 18 | * ФиолетОвый 19 | 20 | * Соответствующий выходной файл: 21 | * Карминовый, Некрасивый 22 | * 23 | * Обратите внимание: данная функция не имеет возвращаемого значения 24 | */ -------------------------------------------------------------------------------- /input/top20.txt: -------------------------------------------------------------------------------- 1 | Привет, привет42, привет!!! -привет?! 2 | 3 | Всё!!! Ведь все русские буквы очень понятные, кроме одной - и это буква Ё вместо Е. Все остальные хорошие и красивые. 4 | 5 | Чёрные аисты летели по белому небу прямо, прямо, прямо, ... 6 | 7 | Let 4us4 write some english text with 1digits1 to test this side of a function. Let us write some more digits!! 8 | 9 | Ну и все всё наконец поняли?! -------------------------------------------------------------------------------- /input/trans_in1.txt: -------------------------------------------------------------------------------- 1 | Здравствуй, 2 | мир! -------------------------------------------------------------------------------- /input/width_in1.txt: -------------------------------------------------------------------------------- 1 | Простая 2 | 3 | Во входном файле с именем inputName содержится некоторый текст. 4 | Вывести его в выходной файл с именем outputName, выровняв по левому и правому краю относительно 5 | самой длинной строки. 6 | Выравнивание производить, вставляя дополнительные пробелы между словами: равномерно по всей строке 7 | 8 | Слова внутри строки отделяются друг от друга одним или более пробелом. 9 | 10 | Следующие правила должны быть выполнены: 11 | 1) Каждая строка входного и выходного файла не должна заканчиваться пробелом. 12 | 2) Пустые строки или строки из пробелов во входном файле должны превратиться в пустые строки в выходном файле. 13 | 3) Число строк в выходном файле должно быть равно числу строк во входном (в т. ч. пустых). 14 | 15 | Равномерность определяется следующими формальными правилами: 16 | 1) Число пробелов между каждыми двумя парами соседних слов не должно отличаться более, чем на 1. 17 | 2) Число пробелов между более левой парой соседних слов должно быть больше или равно числу пробелов 18 | между более правой парой соседних слов. -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.spbstu 8 | kfirst 9 | 0.1.14 10 | 11 | 12 | 13 | UTF-8 14 | 15 | 16 | UTF-8 17 | 18 | 19 | 1.2.51 20 | 0.6.11 21 | 5.0.0 22 | 4.12.0 23 | 1.0.0 24 | 25 | 26 | 27 | 28 | org.jetbrains.kotlin 29 | kotlin-stdlib 30 | ${kotlin.version} 31 | 32 | 33 | org.jetbrains.kotlin 34 | kotlin-reflect 35 | ${kotlin.version} 36 | 37 | 38 | org.jetbrains.kotlin 39 | kotlin-runtime 40 | ${kotlin.version} 41 | 42 | 43 | 44 | org.junit.jupiter 45 | junit-jupiter-api 46 | ${junit.jupiter.version} 47 | test 48 | 49 | 50 | org.junit.jupiter 51 | junit-jupiter-engine 52 | ${junit.jupiter.version} 53 | test 54 | 55 | 56 | org.junit.vintage 57 | junit-vintage-engine 58 | ${junit.vintage.version} 59 | test 60 | 61 | 62 | org.jetbrains.kotlinx 63 | kotlinx-html-jvm 64 | ${kotlinx.html.version} 65 | 66 | 67 | 68 | src 69 | test 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-compiler-plugin 74 | 3.5.1 75 | 76 | 1.8 77 | 1.8 78 | 1.8 79 | 1.8 80 | 81 | 82 | 83 | maven-surefire-plugin 84 | 2.19.1 85 | 86 | true 87 | 88 | 89 | 90 | org.junit.platform 91 | 92 | junit-platform-surefire-provider 93 | 94 | ${junit.platform.version} 95 | 96 | 97 | 98 | 99 | kotlin-maven-plugin 100 | org.jetbrains.kotlin 101 | ${kotlin.version} 102 | 103 | 104 | compile 105 | process-sources 106 | 107 | compile 108 | 109 | 110 | 111 | test-compile 112 | process-test-sources 113 | 114 | test-compile 115 | 116 | 117 | 118 | 119 | 120 | kfirst-runner-plugin 121 | org.jetbrains.research 122 | 0.1.17 123 | 124 | 125 | lesson1.task1 126 | 127 | lesson2.task1 128 | lesson2.task2 129 | 130 | lesson3.task1 131 | lesson4.task1 132 | lesson5.task1 133 | lesson6.task1 134 | lesson7.task1 135 | 136 | lesson8.task1 137 | lesson8.task2 138 | lesson8.task3 139 | 140 | lesson9.task1 141 | lesson9.task2 142 | 143 | lesson10.task1 144 | lesson10.task2 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | jcenter 154 | jcenter 155 | https://jcenter.bintray.com 156 | 157 | 158 | 159 | 160 | 161 | bintray-vorpal-research-kotlin-maven 162 | vorpal-research-kotlin-maven 163 | http://dl.bintray.com/vorpal-research/kotlin-maven 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /src/lesson1/task1/Simple.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson1.task1 3 | 4 | import kotlin.math.* 5 | 6 | /** 7 | * Пример 8 | * 9 | * Вычисление квадрата целого числа 10 | */ 11 | fun sqr(x: Int) = x * x 12 | 13 | /** 14 | * Пример 15 | * 16 | * Вычисление квадрата вещественного числа 17 | */ 18 | fun sqr(x: Double) = x * x 19 | 20 | /** 21 | * Пример 22 | * 23 | * Вычисление дискриминанта квадратного уравнения 24 | */ 25 | fun discriminant(a: Double, b: Double, c: Double) = sqr(b) - 4 * a * c 26 | 27 | /** 28 | * Пример 29 | * 30 | * Поиск одного из корней квадратного уравнения 31 | */ 32 | fun quadraticEquationRoot(a: Double, b: Double, c: Double) = 33 | (-b + sqrt(discriminant(a, b, c))) / (2 * a) 34 | 35 | /** 36 | * Пример 37 | * 38 | * Поиск произведения корней квадратного уравнения 39 | */ 40 | fun quadraticRootProduct(a: Double, b: Double, c: Double): Double { 41 | val sd = sqrt(discriminant(a, b, c)) 42 | val x1 = (-b + sd) / (2 * a) 43 | val x2 = (-b - sd) / (2 * a) 44 | return x1 * x2 // Результат 45 | } 46 | 47 | /** 48 | * Пример главной функции 49 | */ 50 | fun main(args: Array) { 51 | val x1x2 = quadraticRootProduct(1.0, 13.0, 42.0) 52 | println("Root product: $x1x2") 53 | } 54 | 55 | /** 56 | * Тривиальная 57 | * 58 | * Пользователь задает время в часах, минутах и секундах, например, 8:20:35. 59 | * Рассчитать время в секундах, прошедшее с начала суток (30035 в данном случае). 60 | */ 61 | fun seconds(hours: Int, minutes: Int, seconds: Int): Int = TODO() 62 | 63 | /** 64 | * Тривиальная 65 | * 66 | * Пользователь задает длину отрезка в саженях, аршинах и вершках (например, 8 саженей 2 аршина 11 вершков). 67 | * Определить длину того же отрезка в метрах (в данном случае 18.98). 68 | * 1 сажень = 3 аршина = 48 вершков, 1 вершок = 4.445 см. 69 | */ 70 | fun lengthInMeters(sagenes: Int, arshins: Int, vershoks: Int): Double = TODO() 71 | 72 | /** 73 | * Тривиальная 74 | * 75 | * Пользователь задает угол в градусах, минутах и секундах (например, 36 градусов 14 минут 35 секунд). 76 | * Вывести значение того же угла в радианах (например, 0.63256). 77 | */ 78 | fun angleInRadian(grad: Int, min: Int, sec: Int): Double = TODO() 79 | 80 | /** 81 | * Тривиальная 82 | * 83 | * Найти длину отрезка, соединяющего точки на плоскости с координатами (x1, y1) и (x2, y2). 84 | * Например, расстояние между (3, 0) и (0, 4) равно 5 85 | */ 86 | fun trackLength(x1: Double, y1: Double, x2: Double, y2: Double): Double = TODO() 87 | 88 | /** 89 | * Простая 90 | * 91 | * Пользователь задает целое число, большее 100 (например, 3801). 92 | * Определить третью цифру справа в этом числе (в данном случае 8). 93 | */ 94 | fun thirdDigit(number: Int): Int = TODO() 95 | 96 | /** 97 | * Простая 98 | * 99 | * Поезд вышел со станции отправления в h1 часов m1 минут (например в 9:25) и 100 | * прибыл на станцию назначения в h2 часов m2 минут того же дня (например в 13:01). 101 | * Определите время поезда в пути в минутах (в данном случае 216). 102 | */ 103 | fun travelMinutes(hoursDepart: Int, minutesDepart: Int, hoursArrive: Int, minutesArrive: Int): Int = TODO() 104 | 105 | /** 106 | * Простая 107 | * 108 | * Человек положил в банк сумму в s рублей под p% годовых (проценты начисляются в конце года). 109 | * Сколько денег будет на счету через 3 года (с учётом сложных процентов)? 110 | * Например, 100 рублей под 10% годовых превратятся в 133.1 рубля 111 | */ 112 | fun accountInThreeYears(initial: Int, percent: Int): Double = TODO() 113 | 114 | /** 115 | * Простая 116 | * 117 | * Пользователь задает целое трехзначное число (например, 478). 118 | * Необходимо вывести число, полученное из заданного перестановкой цифр в обратном порядке (например, 874). 119 | */ 120 | fun numberRevert(number: Int): Int = TODO() 121 | -------------------------------------------------------------------------------- /src/lesson10/task1/Regex.kt: -------------------------------------------------------------------------------- 1 | package lesson10.task1 2 | 3 | import lesson10.task1.Expression.Operation.* 4 | import java.io.File 5 | import java.util.regex.Pattern 6 | 7 | /** 8 | * Пример 9 | * 10 | * Во входном файле с именем inputName 11 | * содержится строчка, содержащая описание функции от x, например: 12 | * 13 | * 3*x*x - 2 / x + 7 -x 14 | * 15 | * В списке values содержатся целочисленные значения аргумента x, например, (1, 2, -1) 16 | * 17 | * Вернуть ассоциативный массив (map), содержащий отображение заданных значений аргумента 18 | * в значение заданной в файле функции, в данном случае 19 | * 20 | * (1 to 7, 2 to 16, -1 to 13) 21 | * 22 | * В функции могут присутствовать четыре арифметических действия и круглые скобки. 23 | * Обратите внимание, что функция является целочисленной, 24 | * то есть деление также следует трактовать как целочисленное. 25 | */ 26 | fun parseExpr(inputName: String, values: List): Map { 27 | val expr = File(inputName).readLines().firstOrNull()?.parseExpr() ?: throw IllegalArgumentException() 28 | val result = mutableMapOf() 29 | for (value in values) { 30 | result[value] = expr.calculate(value) 31 | } 32 | return result 33 | } 34 | 35 | fun String.parseExpr(): Expression { 36 | val pattern = Pattern.compile("""x|\+|-|\*|/|\(|\)|\d+| +?|.+?""") 37 | val matcher = pattern.matcher(this) 38 | val groups = mutableListOf() 39 | while (matcher.find()) { 40 | val group = matcher.group() 41 | if (group[0] != ' ') { 42 | groups.add(group) 43 | } 44 | } 45 | return Parser(groups).parse() 46 | } 47 | 48 | sealed class Expression { 49 | object Variable : Expression() 50 | 51 | class Constant(val value: Int) : Expression() 52 | 53 | enum class Operation { 54 | PLUS, 55 | MINUS, 56 | TIMES, 57 | DIV; 58 | } 59 | 60 | class Binary(val left: Expression, val op: Operation, val right: Expression) : Expression() 61 | 62 | class Negate(val arg: Expression) : Expression() 63 | 64 | fun calculate(x: Int): Int = when (this) { 65 | Variable -> x 66 | is Constant -> value 67 | is Binary -> when (op) { 68 | PLUS -> left.calculate(x) + right.calculate(x) 69 | MINUS -> left.calculate(x) - right.calculate(x) 70 | TIMES -> left.calculate(x) * right.calculate(x) 71 | DIV -> left.calculate(x) / right.calculate(x) 72 | } 73 | is Negate -> -arg.calculate(x) 74 | } 75 | } 76 | 77 | class Parser(private val groups: List) { 78 | private var pos = 0 79 | 80 | fun parse(): Expression { 81 | val result = parseExpression() 82 | if (pos < groups.size) { 83 | throw IllegalStateException("Unexpected expression remainder: ${groups.subList(pos, groups.size)}") 84 | } 85 | return result 86 | } 87 | 88 | private fun parseExpression(): Expression { 89 | var left = parseItem() 90 | while (pos < groups.size) { 91 | val op = operationMap[groups[pos]] 92 | when (op) { 93 | PLUS, MINUS -> { 94 | pos++ 95 | val right = parseItem() 96 | left = Expression.Binary(left, op, right) 97 | } 98 | else -> return left 99 | } 100 | } 101 | return left 102 | } 103 | 104 | private fun parseItem(): Expression { 105 | var left = parseFactor() 106 | while (pos < groups.size) { 107 | val op = operationMap[groups[pos]] 108 | when (op) { 109 | TIMES, DIV -> { 110 | pos++ 111 | val right = parseFactor() 112 | left = Expression.Binary(left, op, right) 113 | } 114 | else -> return left 115 | } 116 | } 117 | return left 118 | } 119 | 120 | private fun parseFactor(): Expression = 121 | if (pos >= groups.size) throw IllegalStateException("Unexpected expression end") 122 | else { 123 | val group = groups[pos++] 124 | when (group) { 125 | "x" -> Expression.Variable 126 | "-" -> Expression.Negate(parseFactor()) 127 | "(" -> { 128 | val arg = parseExpression() 129 | val next = groups[pos++] 130 | if (next == ")") arg 131 | else throw IllegalStateException(") expected instead of $next") 132 | } 133 | else -> Expression.Constant(group.toInt()) 134 | } 135 | } 136 | 137 | private val operationMap = mapOf("+" to PLUS, "-" to MINUS, "*" to TIMES, "/" to DIV) 138 | } -------------------------------------------------------------------------------- /src/lesson10/task2/Html.kt: -------------------------------------------------------------------------------- 1 | package lesson10.task2 2 | 3 | import kotlinx.html.* 4 | import kotlinx.html.stream.appendHTML 5 | 6 | /** 7 | * Пример: преобразование списка списков в HTML таблицу 8 | */ 9 | fun List>.convertToHtmlTable(): String { 10 | val sb = StringBuilder() 11 | sb.append("") 12 | sb.append("") 13 | sb.append("") 14 | for (row in this) { 15 | sb.append("") 16 | for (data in row) { 17 | sb.append("") 20 | } 21 | sb.append("") 22 | } 23 | sb.append("
") 18 | sb.append(data) 19 | sb.append("
") 24 | sb.append("") 25 | sb.append("") 26 | return sb.toString() 27 | } 28 | 29 | fun List>.convertToHtmlTableUsingKotlinxHtml(): String { 30 | val inputList = this 31 | val sb = StringBuilder() 32 | sb.appendHTML().html { 33 | body { 34 | table { 35 | for (row in inputList) { 36 | tr { 37 | for (data in row) { 38 | td { +data } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | return sb.toString() 46 | } 47 | 48 | fun generateSimpleHtml(s: String): String { 49 | val sb = StringBuilder() 50 | sb.myHtml { 51 | myBody { 52 | +s 53 | } 54 | } 55 | return sb.toString() 56 | } 57 | 58 | private class HTML(val sb: StringBuilder) { 59 | fun myBody(init: HTMLBody.() -> Unit): HTMLBody { 60 | val body = HTMLBody(sb) 61 | sb.append("") 62 | body.init() 63 | sb.append("") 64 | return body 65 | } 66 | } 67 | 68 | private class HTMLBody(val sb: StringBuilder) { 69 | operator fun String.unaryPlus() { 70 | sb.append(this) 71 | } 72 | } 73 | 74 | private fun StringBuilder.myHtml(init: HTML.() -> Unit): HTML { 75 | val html = HTML(this) 76 | append("") 77 | html.init() 78 | append("") 79 | return html 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/lesson2/task1/IfElse.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson2.task1 3 | 4 | import lesson1.task1.discriminant 5 | import kotlin.math.max 6 | import kotlin.math.sqrt 7 | 8 | /** 9 | * Пример 10 | * 11 | * Найти число корней квадратного уравнения ax^2 + bx + c = 0 12 | */ 13 | fun quadraticRootNumber(a: Double, b: Double, c: Double): Int { 14 | val discriminant = discriminant(a, b, c) 15 | return when { 16 | discriminant > 0.0 -> 2 17 | discriminant == 0.0 -> 1 18 | else -> 0 19 | } 20 | } 21 | 22 | /** 23 | * Пример 24 | * 25 | * Получить строковую нотацию для оценки по пятибалльной системе 26 | */ 27 | fun gradeNotation(grade: Int): String = when (grade) { 28 | 5 -> "отлично" 29 | 4 -> "хорошо" 30 | 3 -> "удовлетворительно" 31 | 2 -> "неудовлетворительно" 32 | else -> "несуществующая оценка $grade" 33 | } 34 | 35 | /** 36 | * Пример 37 | * 38 | * Найти наименьший корень биквадратного уравнения ax^4 + bx^2 + c = 0 39 | */ 40 | fun minBiRoot(a: Double, b: Double, c: Double): Double { 41 | // 1: в главной ветке if выполняется НЕСКОЛЬКО операторов 42 | if (a == 0.0) { 43 | if (b == 0.0) return Double.NaN // ... и ничего больше не делать 44 | val bc = -c / b 45 | if (bc < 0.0) return Double.NaN // ... и ничего больше не делать 46 | return -sqrt(bc) 47 | // Дальше функция при a == 0.0 не идёт 48 | } 49 | val d = discriminant(a, b, c) // 2 50 | if (d < 0.0) return Double.NaN // 3 51 | // 4 52 | val y1 = (-b + sqrt(d)) / (2 * a) 53 | val y2 = (-b - sqrt(d)) / (2 * a) 54 | val y3 = max(y1, y2) // 5 55 | if (y3 < 0.0) return Double.NaN // 6 56 | return -sqrt(y3) // 7 57 | } 58 | 59 | /** 60 | * Простая 61 | * 62 | * Мой возраст. Для заданного 0 < n < 200, рассматриваемого как возраст человека, 63 | * вернуть строку вида: «21 год», «32 года», «12 лет». 64 | */ 65 | fun ageDescription(age: Int): String = TODO() 66 | 67 | /** 68 | * Простая 69 | * 70 | * Путник двигался t1 часов со скоростью v1 км/час, затем t2 часов — со скоростью v2 км/час 71 | * и t3 часов — со скоростью v3 км/час. 72 | * Определить, за какое время он одолел первую половину пути? 73 | */ 74 | fun timeForHalfWay(t1: Double, v1: Double, 75 | t2: Double, v2: Double, 76 | t3: Double, v3: Double): Double = TODO() 77 | 78 | /** 79 | * Простая 80 | * 81 | * Нa шахматной доске стоят черный король и две белые ладьи (ладья бьет по горизонтали и вертикали). 82 | * Определить, не находится ли король под боем, а если есть угроза, то от кого именно. 83 | * Вернуть 0, если угрозы нет, 1, если угроза только от первой ладьи, 2, если только от второй ладьи, 84 | * и 3, если угроза от обеих ладей. 85 | * Считать, что ладьи не могут загораживать друг друга 86 | */ 87 | fun whichRookThreatens(kingX: Int, kingY: Int, 88 | rookX1: Int, rookY1: Int, 89 | rookX2: Int, rookY2: Int): Int = TODO() 90 | 91 | /** 92 | * Простая 93 | * 94 | * На шахматной доске стоят черный король и белые ладья и слон 95 | * (ладья бьет по горизонтали и вертикали, слон — по диагоналям). 96 | * Проверить, есть ли угроза королю и если есть, то от кого именно. 97 | * Вернуть 0, если угрозы нет, 1, если угроза только от ладьи, 2, если только от слона, 98 | * и 3, если угроза есть и от ладьи и от слона. 99 | * Считать, что ладья и слон не могут загораживать друг друга. 100 | */ 101 | fun rookOrBishopThreatens(kingX: Int, kingY: Int, 102 | rookX: Int, rookY: Int, 103 | bishopX: Int, bishopY: Int): Int = TODO() 104 | 105 | /** 106 | * Простая 107 | * 108 | * Треугольник задан длинами своих сторон a, b, c. 109 | * Проверить, является ли данный треугольник остроугольным (вернуть 0), 110 | * прямоугольным (вернуть 1) или тупоугольным (вернуть 2). 111 | * Если такой треугольник не существует, вернуть -1. 112 | */ 113 | fun triangleKind(a: Double, b: Double, c: Double): Int = TODO() 114 | 115 | /** 116 | * Средняя 117 | * 118 | * Даны четыре точки на одной прямой: A, B, C и D. 119 | * Координаты точек a, b, c, d соответственно, b >= a, d >= c. 120 | * Найти длину пересечения отрезков AB и CD. 121 | * Если пересечения нет, вернуть -1. 122 | */ 123 | fun segmentLength(a: Int, b: Int, c: Int, d: Int): Int = TODO() 124 | -------------------------------------------------------------------------------- /src/lesson2/task2/Logical.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson2.task2 3 | 4 | import lesson1.task1.sqr 5 | 6 | /** 7 | * Пример 8 | * 9 | * Лежит ли точка (x, y) внутри окружности с центром в (x0, y0) и радиусом r? 10 | */ 11 | fun pointInsideCircle(x: Double, y: Double, x0: Double, y0: Double, r: Double) = 12 | sqr(x - x0) + sqr(y - y0) <= sqr(r) 13 | 14 | /** 15 | * Простая 16 | * 17 | * Четырехзначное число назовем счастливым, если сумма первых двух ее цифр равна сумме двух последних. 18 | * Определить, счастливое ли заданное число, вернуть true, если это так. 19 | */ 20 | fun isNumberHappy(number: Int): Boolean = TODO() 21 | 22 | /** 23 | * Простая 24 | * 25 | * На шахматной доске стоят два ферзя (ферзь бьет по вертикали, горизонтали и диагоналям). 26 | * Определить, угрожают ли они друг другу. Вернуть true, если угрожают. 27 | * Считать, что ферзи не могут загораживать друг друга. 28 | */ 29 | fun queenThreatens(x1: Int, y1: Int, x2: Int, y2: Int): Boolean = TODO() 30 | 31 | 32 | /** 33 | * Простая 34 | * 35 | * Дан номер месяца (от 1 до 12 включительно) и год (положительный). 36 | * Вернуть число дней в этом месяце этого года по григорианскому календарю. 37 | */ 38 | fun daysInMonth(month: Int, year: Int): Int = TODO() 39 | 40 | /** 41 | * Средняя 42 | * 43 | * Проверить, лежит ли окружность с центром в (x1, y1) и радиусом r1 целиком внутри 44 | * окружности с центром в (x2, y2) и радиусом r2. 45 | * Вернуть true, если утверждение верно 46 | */ 47 | fun circleInside(x1: Double, y1: Double, r1: Double, 48 | x2: Double, y2: Double, r2: Double): Boolean = TODO() 49 | 50 | /** 51 | * Средняя 52 | * 53 | * Определить, пройдет ли кирпич со сторонами а, b, c сквозь прямоугольное отверстие в стене со сторонами r и s. 54 | * Стороны отверстия должны быть параллельны граням кирпича. 55 | * Считать, что совпадения длин сторон достаточно для прохождения кирпича, т.е., например, 56 | * кирпич 4 х 4 х 4 пройдёт через отверстие 4 х 4. 57 | * Вернуть true, если кирпич пройдёт 58 | */ 59 | fun brickPasses(a: Int, b: Int, c: Int, r: Int, s: Int): Boolean = TODO() 60 | -------------------------------------------------------------------------------- /src/lesson3/task1/Loop.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson3.task1 3 | 4 | import kotlin.math.sqrt 5 | 6 | /** 7 | * Пример 8 | * 9 | * Вычисление факториала 10 | */ 11 | fun factorial(n: Int): Double { 12 | var result = 1.0 13 | for (i in 1..n) { 14 | result = result * i // Please do not fix in master 15 | } 16 | return result 17 | } 18 | 19 | /** 20 | * Пример 21 | * 22 | * Проверка числа на простоту -- результат true, если число простое 23 | */ 24 | fun isPrime(n: Int): Boolean { 25 | if (n < 2) return false 26 | if (n == 2) return true 27 | if (n % 2 == 0) return false 28 | for (m in 3..sqrt(n.toDouble()).toInt() step 2) { 29 | if (n % m == 0) return false 30 | } 31 | return true 32 | } 33 | 34 | /** 35 | * Пример 36 | * 37 | * Проверка числа на совершенность -- результат true, если число совершенное 38 | */ 39 | fun isPerfect(n: Int): Boolean { 40 | var sum = 1 41 | for (m in 2..n/2) { 42 | if (n % m > 0) continue 43 | sum += m 44 | if (sum > n) break 45 | } 46 | return sum == n 47 | } 48 | 49 | /** 50 | * Пример 51 | * 52 | * Найти число вхождений цифры m в число n 53 | */ 54 | fun digitCountInNumber(n: Int, m: Int): Int = 55 | when { 56 | n == m -> 1 57 | n < 10 -> 0 58 | else -> digitCountInNumber(n / 10, m) + digitCountInNumber(n % 10, m) 59 | } 60 | 61 | /** 62 | * Тривиальная 63 | * 64 | * Найти количество цифр в заданном числе n. 65 | * Например, число 1 содержит 1 цифру, 456 -- 3 цифры, 65536 -- 5 цифр. 66 | * 67 | * Использовать операции со строками в этой задаче запрещается. 68 | */ 69 | fun digitNumber(n: Int): Int = TODO() 70 | 71 | /** 72 | * Простая 73 | * 74 | * Найти число Фибоначчи из ряда 1, 1, 2, 3, 5, 8, 13, 21, ... с номером n. 75 | * Ряд Фибоначчи определён следующим образом: fib(1) = 1, fib(2) = 1, fib(n+2) = fib(n) + fib(n+1) 76 | */ 77 | fun fib(n: Int): Int = TODO() 78 | 79 | /** 80 | * Простая 81 | * 82 | * Для заданных чисел m и n найти наименьшее общее кратное, то есть, 83 | * минимальное число k, которое делится и на m и на n без остатка 84 | */ 85 | fun lcm(m: Int, n: Int): Int = TODO() 86 | 87 | /** 88 | * Простая 89 | * 90 | * Для заданного числа n > 1 найти минимальный делитель, превышающий 1 91 | */ 92 | fun minDivisor(n: Int): Int = TODO() 93 | 94 | /** 95 | * Простая 96 | * 97 | * Для заданного числа n > 1 найти максимальный делитель, меньший n 98 | */ 99 | fun maxDivisor(n: Int): Int = TODO() 100 | 101 | /** 102 | * Простая 103 | * 104 | * Определить, являются ли два заданных числа m и n взаимно простыми. 105 | * Взаимно простые числа не имеют общих делителей, кроме 1. 106 | * Например, 25 и 49 взаимно простые, а 6 и 8 -- нет. 107 | */ 108 | fun isCoPrime(m: Int, n: Int): Boolean = TODO() 109 | 110 | /** 111 | * Простая 112 | * 113 | * Для заданных чисел m и n, m <= n, определить, имеется ли хотя бы один точный квадрат между m и n, 114 | * то есть, существует ли такое целое k, что m <= k*k <= n. 115 | * Например, для интервала 21..28 21 <= 5*5 <= 28, а для интервала 51..61 квадрата не существует. 116 | */ 117 | fun squareBetweenExists(m: Int, n: Int): Boolean = TODO() 118 | 119 | /** 120 | * Средняя 121 | * 122 | * Гипотеза Коллатца. Рекуррентная последовательность чисел задана следующим образом: 123 | * 124 | * ЕСЛИ (X четное) 125 | * Xслед = X /2 126 | * ИНАЧЕ 127 | * Xслед = 3 * X + 1 128 | * 129 | * например 130 | * 15 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1 4 2 1 4 2 1 ... 131 | * Данная последовательность рано или поздно встречает X == 1. 132 | * Написать функцию, которая находит, сколько шагов требуется для 133 | * этого для какого-либо начального X > 0. 134 | */ 135 | fun collatzSteps(x: Int): Int = TODO() 136 | 137 | /** 138 | * Средняя 139 | * 140 | * Для заданного x рассчитать с заданной точностью eps 141 | * sin(x) = x - x^3 / 3! + x^5 / 5! - x^7 / 7! + ... 142 | * Нужную точность считать достигнутой, если очередной член ряда меньше eps по модулю 143 | */ 144 | fun sin(x: Double, eps: Double): Double = TODO() 145 | 146 | /** 147 | * Средняя 148 | * 149 | * Для заданного x рассчитать с заданной точностью eps 150 | * cos(x) = 1 - x^2 / 2! + x^4 / 4! - x^6 / 6! + ... 151 | * Нужную точность считать достигнутой, если очередной член ряда меньше eps по модулю 152 | */ 153 | fun cos(x: Double, eps: Double): Double = TODO() 154 | 155 | /** 156 | * Средняя 157 | * 158 | * Поменять порядок цифр заданного числа n на обратный: 13478 -> 87431. 159 | * 160 | * Использовать операции со строками в этой задаче запрещается. 161 | */ 162 | fun revert(n: Int): Int = TODO() 163 | 164 | /** 165 | * Средняя 166 | * 167 | * Проверить, является ли заданное число n палиндромом: 168 | * первая цифра равна последней, вторая -- предпоследней и так далее. 169 | * 15751 -- палиндром, 3653 -- нет. 170 | * 171 | * Использовать операции со строками в этой задаче запрещается. 172 | */ 173 | fun isPalindrome(n: Int): Boolean = TODO() 174 | 175 | /** 176 | * Средняя 177 | * 178 | * Для заданного числа n определить, содержит ли оно различающиеся цифры. 179 | * Например, 54 и 323 состоят из разных цифр, а 111 и 0 из одинаковых. 180 | * 181 | * Использовать операции со строками в этой задаче запрещается. 182 | */ 183 | fun hasDifferentDigits(n: Int): Boolean = TODO() 184 | 185 | /** 186 | * Сложная 187 | * 188 | * Найти n-ю цифру последовательности из квадратов целых чисел: 189 | * 149162536496481100121144... 190 | * Например, 2-я цифра равна 4, 7-я 5, 12-я 6. 191 | * 192 | * Использовать операции со строками в этой задаче запрещается. 193 | */ 194 | fun squareSequenceDigit(n: Int): Int = TODO() 195 | 196 | /** 197 | * Сложная 198 | * 199 | * Найти n-ю цифру последовательности из чисел Фибоначчи (см. функцию fib выше): 200 | * 1123581321345589144... 201 | * Например, 2-я цифра равна 1, 9-я 2, 14-я 5. 202 | * 203 | * Использовать операции со строками в этой задаче запрещается. 204 | */ 205 | fun fibSequenceDigit(n: Int): Int = TODO() 206 | -------------------------------------------------------------------------------- /src/lesson4/task1/List.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER", "ConvertCallChainIntoSequence") 2 | 3 | package lesson4.task1 4 | 5 | import lesson1.task1.discriminant 6 | import kotlin.math.sqrt 7 | 8 | /** 9 | * Пример 10 | * 11 | * Найти все корни уравнения x^2 = y 12 | */ 13 | fun sqRoots(y: Double) = 14 | when { 15 | y < 0 -> listOf() 16 | y == 0.0 -> listOf(0.0) 17 | else -> { 18 | val root = sqrt(y) 19 | // Результат! 20 | listOf(-root, root) 21 | } 22 | } 23 | 24 | /** 25 | * Пример 26 | * 27 | * Найти все корни биквадратного уравнения ax^4 + bx^2 + c = 0. 28 | * Вернуть список корней (пустой, если корней нет) 29 | */ 30 | fun biRoots(a: Double, b: Double, c: Double): List { 31 | if (a == 0.0) { 32 | return if (b == 0.0) listOf() 33 | else sqRoots(-c / b) 34 | } 35 | val d = discriminant(a, b, c) 36 | if (d < 0.0) return listOf() 37 | if (d == 0.0) return sqRoots(-b / (2 * a)) 38 | val y1 = (-b + sqrt(d)) / (2 * a) 39 | val y2 = (-b - sqrt(d)) / (2 * a) 40 | return sqRoots(y1) + sqRoots(y2) 41 | } 42 | 43 | /** 44 | * Пример 45 | * 46 | * Выделить в список отрицательные элементы из заданного списка 47 | */ 48 | fun negativeList(list: List): List { 49 | val result = mutableListOf() 50 | for (element in list) { 51 | if (element < 0) { 52 | result.add(element) 53 | } 54 | } 55 | return result 56 | } 57 | 58 | /** 59 | * Пример 60 | * 61 | * Изменить знак для всех положительных элементов списка 62 | */ 63 | fun invertPositives(list: MutableList) { 64 | for (i in 0 until list.size) { 65 | val element = list[i] 66 | if (element > 0) { 67 | list[i] = -element 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * Пример 74 | * 75 | * Из имеющегося списка целых чисел, сформировать список их квадратов 76 | */ 77 | fun squares(list: List) = list.map { it * it } 78 | 79 | /** 80 | * Пример 81 | * 82 | * Из имеющихся целых чисел, заданного через vararg-параметр, сформировать массив их квадратов 83 | */ 84 | fun squares(vararg array: Int) = squares(array.toList()).toTypedArray() 85 | 86 | /** 87 | * Пример 88 | * 89 | * По заданной строке str определить, является ли она палиндромом. 90 | * В палиндроме первый символ должен быть равен последнему, второй предпоследнему и т.д. 91 | * Одни и те же буквы в разном регистре следует считать равными с точки зрения данной задачи. 92 | * Пробелы не следует принимать во внимание при сравнении символов, например, строка 93 | * "А роза упала на лапу Азора" является палиндромом. 94 | */ 95 | fun isPalindrome(str: String): Boolean { 96 | val lowerCase = str.toLowerCase().filter { it != ' ' } 97 | for (i in 0..lowerCase.length / 2) { 98 | if (lowerCase[i] != lowerCase[lowerCase.length - i - 1]) return false 99 | } 100 | return true 101 | } 102 | 103 | /** 104 | * Пример 105 | * 106 | * По имеющемуся списку целых чисел, например [3, 6, 5, 4, 9], построить строку с примером их суммирования: 107 | * 3 + 6 + 5 + 4 + 9 = 27 в данном случае. 108 | */ 109 | fun buildSumExample(list: List) = list.joinToString(separator = " + ", postfix = " = ${list.sum()}") 110 | 111 | /** 112 | * Простая 113 | * 114 | * Найти модуль заданного вектора, представленного в виде списка v, 115 | * по формуле abs = sqrt(a1^2 + a2^2 + ... + aN^2). 116 | * Модуль пустого вектора считать равным 0.0. 117 | */ 118 | fun abs(v: List): Double = TODO() 119 | 120 | /** 121 | * Простая 122 | * 123 | * Рассчитать среднее арифметическое элементов списка list. Вернуть 0.0, если список пуст 124 | */ 125 | fun mean(list: List): Double = TODO() 126 | 127 | /** 128 | * Средняя 129 | * 130 | * Центрировать заданный список list, уменьшив каждый элемент на среднее арифметическое всех элементов. 131 | * Если список пуст, не делать ничего. Вернуть изменённый список. 132 | * 133 | * Обратите внимание, что данная функция должна изменять содержание списка list, а не его копии. 134 | */ 135 | fun center(list: MutableList): MutableList = TODO() 136 | 137 | /** 138 | * Средняя 139 | * 140 | * Найти скалярное произведение двух векторов равной размерности, 141 | * представленные в виде списков a и b. Скалярное произведение считать по формуле: 142 | * C = a1b1 + a2b2 + ... + aNbN. Произведение пустых векторов считать равным 0.0. 143 | */ 144 | fun times(a: List, b: List): Double = TODO() 145 | 146 | /** 147 | * Средняя 148 | * 149 | * Рассчитать значение многочлена при заданном x: 150 | * p(x) = p0 + p1*x + p2*x^2 + p3*x^3 + ... + pN*x^N. 151 | * Коэффициенты многочлена заданы списком p: (p0, p1, p2, p3, ..., pN). 152 | * Значение пустого многочлена равно 0.0 при любом x. 153 | */ 154 | fun polynom(p: List, x: Double): Double = TODO() 155 | 156 | /** 157 | * Средняя 158 | * 159 | * В заданном списке list каждый элемент, кроме первого, заменить 160 | * суммой данного элемента и всех предыдущих. 161 | * Например: 1, 2, 3, 4 -> 1, 3, 6, 10. 162 | * Пустой список не следует изменять. Вернуть изменённый список. 163 | * 164 | * Обратите внимание, что данная функция должна изменять содержание списка list, а не его копии. 165 | */ 166 | fun accumulate(list: MutableList): MutableList = TODO() 167 | 168 | /** 169 | * Средняя 170 | * 171 | * Разложить заданное натуральное число n > 1 на простые множители. 172 | * Результат разложения вернуть в виде списка множителей, например 75 -> (3, 5, 5). 173 | * Множители в списке должны располагаться по возрастанию. 174 | */ 175 | fun factorize(n: Int): List = TODO() 176 | 177 | /** 178 | * Сложная 179 | * 180 | * Разложить заданное натуральное число n > 1 на простые множители. 181 | * Результат разложения вернуть в виде строки, например 75 -> 3*5*5 182 | * Множители в результирующей строке должны располагаться по возрастанию. 183 | */ 184 | fun factorizeToString(n: Int): String = TODO() 185 | 186 | /** 187 | * Средняя 188 | * 189 | * Перевести заданное целое число n >= 0 в систему счисления с основанием base > 1. 190 | * Результат перевода вернуть в виде списка цифр в base-ичной системе от старшей к младшей, 191 | * например: n = 100, base = 4 -> (1, 2, 1, 0) или n = 250, base = 14 -> (1, 3, 12) 192 | */ 193 | fun convert(n: Int, base: Int): List = TODO() 194 | 195 | /** 196 | * Сложная 197 | * 198 | * Перевести заданное целое число n >= 0 в систему счисления с основанием 1 < base < 37. 199 | * Результат перевода вернуть в виде строки, цифры более 9 представлять латинскими 200 | * строчными буквами: 10 -> a, 11 -> b, 12 -> c и так далее. 201 | * Например: n = 100, base = 4 -> 1210, n = 250, base = 14 -> 13c 202 | */ 203 | fun convertToString(n: Int, base: Int): String = TODO() 204 | 205 | /** 206 | * Средняя 207 | * 208 | * Перевести число, представленное списком цифр digits от старшей к младшей, 209 | * из системы счисления с основанием base в десятичную. 210 | * Например: digits = (1, 3, 12), base = 14 -> 250 211 | */ 212 | fun decimal(digits: List, base: Int): Int = TODO() 213 | 214 | /** 215 | * Сложная 216 | * 217 | * Перевести число, представленное цифровой строкой str, 218 | * из системы счисления с основанием base в десятичную. 219 | * Цифры более 9 представляются латинскими строчными буквами: 220 | * 10 -> a, 11 -> b, 12 -> c и так далее. 221 | * Например: str = "13c", base = 14 -> 250 222 | */ 223 | fun decimalFromString(str: String, base: Int): Int = TODO() 224 | 225 | /** 226 | * Сложная 227 | * 228 | * Перевести натуральное число n > 0 в римскую систему. 229 | * Римские цифры: 1 = I, 4 = IV, 5 = V, 9 = IX, 10 = X, 40 = XL, 50 = L, 230 | * 90 = XC, 100 = C, 400 = CD, 500 = D, 900 = CM, 1000 = M. 231 | * Например: 23 = XXIII, 44 = XLIV, 100 = C 232 | */ 233 | fun roman(n: Int): String = TODO() 234 | 235 | /** 236 | * Очень сложная 237 | * 238 | * Записать заданное натуральное число 1..999999 прописью по-русски. 239 | * Например, 375 = "триста семьдесят пять", 240 | * 23964 = "двадцать три тысячи девятьсот шестьдесят четыре" 241 | */ 242 | fun russian(n: Int): String = TODO() -------------------------------------------------------------------------------- /src/lesson5/task1/Map.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER", "ConvertCallChainIntoSequence") 2 | 3 | package lesson5.task1 4 | 5 | /** 6 | * Пример 7 | * 8 | * Для заданного списка покупок `shoppingList` посчитать его общую стоимость 9 | * на основе цен из `costs`. В случае неизвестной цены считать, что товар 10 | * игнорируется. 11 | */ 12 | fun shoppingListCost( 13 | shoppingList: List, 14 | costs: Map): Double { 15 | var totalCost = 0.0 16 | 17 | for (item in shoppingList) { 18 | val itemCost = costs[item] 19 | if (itemCost != null) { 20 | totalCost += itemCost 21 | } 22 | } 23 | 24 | return totalCost 25 | } 26 | 27 | /** 28 | * Пример 29 | * 30 | * Для набора "имя"-"номер телефона" `phoneBook` оставить только такие пары, 31 | * для которых телефон начинается с заданного кода страны `countryCode` 32 | */ 33 | fun filterByCountryCode( 34 | phoneBook: MutableMap, 35 | countryCode: String) { 36 | val namesToRemove = mutableListOf() 37 | 38 | for ((name, phone) in phoneBook) { 39 | if (!phone.startsWith(countryCode)) { 40 | namesToRemove.add(name) 41 | } 42 | } 43 | 44 | for (name in namesToRemove) { 45 | phoneBook.remove(name) 46 | } 47 | } 48 | 49 | /** 50 | * Пример 51 | * 52 | * Для заданного текста `text` убрать заданные слова-паразиты `fillerWords` 53 | * и вернуть отфильтрованный текст 54 | */ 55 | fun removeFillerWords( 56 | text: List, 57 | vararg fillerWords: String): List { 58 | val fillerWordSet = setOf(*fillerWords) 59 | 60 | val res = mutableListOf() 61 | for (word in text) { 62 | if (word !in fillerWordSet) { 63 | res += word 64 | } 65 | } 66 | return res 67 | } 68 | 69 | /** 70 | * Пример 71 | * 72 | * Для заданного текста `text` построить множество встречающихся в нем слов 73 | */ 74 | fun buildWordSet(text: List): MutableSet { 75 | val res = mutableSetOf() 76 | for (word in text) res.add(word) 77 | return res 78 | } 79 | 80 | /** 81 | * Средняя 82 | * 83 | * Объединить два ассоциативных массива `mapA` и `mapB` с парами 84 | * "имя"-"номер телефона" в итоговый ассоциативный массив, склеивая 85 | * значения для повторяющихся ключей через запятую. 86 | * В случае повторяющихся *ключей* значение из mapA должно быть 87 | * перед значением из mapB. 88 | * 89 | * Повторяющиеся *значения* следует добавлять только один раз. 90 | * 91 | * Например: 92 | * mergePhoneBooks( 93 | * mapOf("Emergency" to "112", "Police" to "02"), 94 | * mapOf("Emergency" to "911", "Police" to "02") 95 | * ) -> mapOf("Emergency" to "112, 911", "Police" to "02") 96 | */ 97 | fun mergePhoneBooks(mapA: Map, mapB: Map): Map = TODO() 98 | 99 | /** 100 | * Простая 101 | * 102 | * По заданному ассоциативному массиву "студент"-"оценка за экзамен" построить 103 | * обратный массив "оценка за экзамен"-"список студентов с этой оценкой". 104 | * 105 | * Например: 106 | * buildGrades(mapOf("Марат" to 3, "Семён" to 5, "Михаил" to 5)) 107 | * -> mapOf(5 to listOf("Семён", "Михаил"), 3 to listOf("Марат")) 108 | */ 109 | fun buildGrades(grades: Map): Map> = TODO() 110 | 111 | /** 112 | * Простая 113 | * 114 | * Определить, входит ли ассоциативный массив a в ассоциативный массив b; 115 | * это выполняется, если все ключи из a содержатся в b с такими же значениями. 116 | * 117 | * Например: 118 | * containsIn(mapOf("a" to "z"), mapOf("a" to "z", "b" to "sweet")) -> true 119 | * containsIn(mapOf("a" to "z"), mapOf("a" to "zee", "b" to "sweet")) -> false 120 | */ 121 | fun containsIn(a: Map, b: Map): Boolean = TODO() 122 | 123 | /** 124 | * Средняя 125 | * 126 | * Для заданного списка пар "акция"-"стоимость" вернуть ассоциативный массив, 127 | * содержащий для каждой акции ее усредненную стоимость. 128 | * 129 | * Например: 130 | * averageStockPrice(listOf("MSFT" to 100.0, "MSFT" to 200.0, "NFLX" to 40.0)) 131 | * -> mapOf("MSFT" to 150.0, "NFLX" to 40.0) 132 | */ 133 | fun averageStockPrice(stockPrices: List>): Map = TODO() 134 | 135 | /** 136 | * Средняя 137 | * 138 | * Входными данными является ассоциативный массив 139 | * "название товара"-"пара (тип товара, цена товара)" 140 | * и тип интересующего нас товара. 141 | * Необходимо вернуть название товара заданного типа с минимальной стоимостью 142 | * или null в случае, если товаров такого типа нет. 143 | * 144 | * Например: 145 | * findCheapestStuff( 146 | * mapOf("Мария" to ("печенье" to 20.0), "Орео" to ("печенье" to 100.0)), 147 | * "печенье" 148 | * ) -> "Мария" 149 | */ 150 | fun findCheapestStuff(stuff: Map>, kind: String): String? = TODO() 151 | 152 | /** 153 | * Сложная 154 | * 155 | * Для заданного ассоциативного массива знакомых через одно рукопожатие `friends` 156 | * необходимо построить его максимальное расширение по рукопожатиям, то есть, 157 | * для каждого человека найти всех людей, с которыми он знаком через любое 158 | * количество рукопожатий. 159 | * Считать, что все имена людей являются уникальными, а также что рукопожатия 160 | * являются направленными, то есть, если Марат знает Свету, то это не означает, 161 | * что Света знает Марата. 162 | * 163 | * Например: 164 | * propagateHandshakes( 165 | * mapOf( 166 | * "Marat" to setOf("Mikhail", "Sveta"), 167 | * "Sveta" to setOf("Marat"), 168 | * "Mikhail" to setOf("Sveta") 169 | * ) 170 | * ) -> mapOf( 171 | * "Marat" to setOf("Mikhail", "Sveta"), 172 | * "Sveta" to setOf("Marat", "Mikhail"), 173 | * "Mikhail" to setOf("Sveta", "Marat") 174 | * ) 175 | */ 176 | fun propagateHandshakes(friends: Map>): Map> = TODO() 177 | 178 | /** 179 | * Простая 180 | * 181 | * Удалить из изменяемого ассоциативного массива все записи, 182 | * которые встречаются в заданном ассоциативном массиве. 183 | * Записи считать одинаковыми, если и ключи, и значения совпадают. 184 | * 185 | * ВАЖНО: необходимо изменить переданный в качестве аргумента 186 | * изменяемый ассоциативный массив 187 | * 188 | * Например: 189 | * subtractOf(a = mutableMapOf("a" to "z"), mapOf("a" to "z")) 190 | * -> a changes to mutableMapOf() aka becomes empty 191 | */ 192 | fun subtractOf(a: MutableMap, b: Map): Unit = TODO() 193 | 194 | /** 195 | * Простая 196 | * 197 | * Для двух списков людей найти людей, встречающихся в обоих списках 198 | */ 199 | fun whoAreInBoth(a: List, b: List): List = TODO() 200 | 201 | /** 202 | * Средняя 203 | * 204 | * Для заданного набора символов определить, можно ли составить из него 205 | * указанное слово (регистр символов игнорируется) 206 | * 207 | * Например: 208 | * canBuildFrom(listOf('a', 'b', 'o'), "baobab") -> true 209 | */ 210 | fun canBuildFrom(chars: List, word: String): Boolean = TODO() 211 | 212 | /** 213 | * Средняя 214 | * 215 | * Найти в заданном списке повторяющиеся элементы и вернуть 216 | * ассоциативный массив с информацией о числе повторений 217 | * для каждого повторяющегося элемента. 218 | * Если элемент встречается только один раз, включать его в результат 219 | * не следует. 220 | * 221 | * Например: 222 | * extractRepeats(listOf("a", "b", "a")) -> mapOf("a" to 2) 223 | */ 224 | fun extractRepeats(list: List): Map = TODO() 225 | 226 | /** 227 | * Средняя 228 | * 229 | * Для заданного списка слов определить, содержит ли он анаграммы 230 | * (два слова являются анаграммами, если одно можно составить из второго) 231 | * 232 | * Например: 233 | * hasAnagrams(listOf("тор", "свет", "рот")) -> true 234 | */ 235 | fun hasAnagrams(words: List): Boolean = TODO() 236 | 237 | /** 238 | * Сложная 239 | * 240 | * Для заданного списка неотрицательных чисел и числа определить, 241 | * есть ли в списке пара чисел таких, что их сумма равна заданному числу. 242 | * Если да, верните их индексы в виде Pair; 243 | * если нет, верните пару Pair(-1, -1). 244 | * 245 | * Индексы в результате должны следовать в порядке (меньший, больший). 246 | * 247 | * Постарайтесь сделать ваше решение как можно более эффективным, 248 | * используя то, что вы узнали в данном уроке. 249 | * 250 | * Например: 251 | * findSumOfTwo(listOf(1, 2, 3), 4) -> Pair(0, 2) 252 | * findSumOfTwo(listOf(1, 2, 3), 6) -> Pair(-1, -1) 253 | */ 254 | fun findSumOfTwo(list: List, number: Int): Pair = TODO() 255 | 256 | /** 257 | * Очень сложная 258 | * 259 | * Входными данными является ассоциативный массив 260 | * "название сокровища"-"пара (вес сокровища, цена сокровища)" 261 | * и вместимость вашего рюкзака. 262 | * Необходимо вернуть множество сокровищ с максимальной суммарной стоимостью, 263 | * которые вы можете унести в рюкзаке. 264 | * 265 | * Например: 266 | * bagPacking( 267 | * mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)), 268 | * 850 269 | * ) -> setOf("Кубок") 270 | * bagPacking( 271 | * mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)), 272 | * 450 273 | * ) -> emptySet() 274 | */ 275 | fun bagPacking(treasures: Map>, capacity: Int): Set = TODO() 276 | -------------------------------------------------------------------------------- /src/lesson6/task1/Parse.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER", "ConvertCallChainIntoSequence") 2 | 3 | package lesson6.task1 4 | 5 | /** 6 | * Пример 7 | * 8 | * Время представлено строкой вида "11:34:45", содержащей часы, минуты и секунды, разделённые двоеточием. 9 | * Разобрать эту строку и рассчитать количество секунд, прошедшее с начала дня. 10 | */ 11 | fun timeStrToSeconds(str: String): Int { 12 | val parts = str.split(":") 13 | var result = 0 14 | for (part in parts) { 15 | val number = part.toInt() 16 | result = result * 60 + number 17 | } 18 | return result 19 | } 20 | 21 | /** 22 | * Пример 23 | * 24 | * Дано число n от 0 до 99. 25 | * Вернуть его же в виде двухсимвольной строки, от "00" до "99" 26 | */ 27 | fun twoDigitStr(n: Int) = if (n in 0..9) "0$n" else "$n" 28 | 29 | /** 30 | * Пример 31 | * 32 | * Дано seconds -- время в секундах, прошедшее с начала дня. 33 | * Вернуть текущее время в виде строки в формате "ЧЧ:ММ:СС". 34 | */ 35 | fun timeSecondsToStr(seconds: Int): String { 36 | val hour = seconds / 3600 37 | val minute = (seconds % 3600) / 60 38 | val second = seconds % 60 39 | return String.format("%02d:%02d:%02d", hour, minute, second) 40 | } 41 | 42 | /** 43 | * Пример: консольный ввод 44 | */ 45 | fun main(args: Array) { 46 | println("Введите время в формате ЧЧ:ММ:СС") 47 | val line = readLine() 48 | if (line != null) { 49 | val seconds = timeStrToSeconds(line) 50 | if (seconds == -1) { 51 | println("Введённая строка $line не соответствует формату ЧЧ:ММ:СС") 52 | } 53 | else { 54 | println("Прошло секунд с начала суток: $seconds") 55 | } 56 | } 57 | else { 58 | println("Достигнут <конец файла> в процессе чтения строки. Программа прервана") 59 | } 60 | } 61 | 62 | 63 | /** 64 | * Средняя 65 | * 66 | * Дата представлена строкой вида "15 июля 2016". 67 | * Перевести её в цифровой формат "15.07.2016". 68 | * День и месяц всегда представлять двумя цифрами, например: 03.04.2011. 69 | * При неверном формате входной строки вернуть пустую строку. 70 | * 71 | * Обратите внимание: некорректная с точки зрения календаря дата (например, 30.02.2009) считается неверными 72 | * входными данными. 73 | */ 74 | fun dateStrToDigit(str: String): String = TODO() 75 | 76 | /** 77 | * Средняя 78 | * 79 | * Дата представлена строкой вида "15.07.2016". 80 | * Перевести её в строковый формат вида "15 июля 2016". 81 | * При неверном формате входной строки вернуть пустую строку 82 | * 83 | * Обратите внимание: некорректная с точки зрения календаря дата (например, 30 февраля 2009) считается неверными 84 | * входными данными. 85 | */ 86 | fun dateDigitToStr(digital: String): String = TODO() 87 | 88 | /** 89 | * Средняя 90 | * 91 | * Номер телефона задан строкой вида "+7 (921) 123-45-67". 92 | * Префикс (+7) может отсутствовать, код города (в скобках) также может отсутствовать. 93 | * Может присутствовать неограниченное количество пробелов и чёрточек, 94 | * например, номер 12 -- 34- 5 -- 67 -89 тоже следует считать легальным. 95 | * Перевести номер в формат без скобок, пробелов и чёрточек (но с +), например, 96 | * "+79211234567" или "123456789" для приведённых примеров. 97 | * Все символы в номере, кроме цифр, пробелов и +-(), считать недопустимыми. 98 | * При неверном формате вернуть пустую строку 99 | */ 100 | fun flattenPhoneNumber(phone: String): String = TODO() 101 | 102 | /** 103 | * Средняя 104 | * 105 | * Результаты спортсмена на соревнованиях в прыжках в длину представлены строкой вида 106 | * "706 - % 717 % 703". 107 | * В строке могут присутствовать числа, черточки - и знаки процента %, разделённые пробелами; 108 | * число соответствует удачному прыжку, - пропущенной попытке, % заступу. 109 | * Прочитать строку и вернуть максимальное присутствующее в ней число (717 в примере). 110 | * При нарушении формата входной строки или при отсутствии в ней чисел, вернуть -1. 111 | */ 112 | fun bestLongJump(jumps: String): Int = TODO() 113 | 114 | /** 115 | * Сложная 116 | * 117 | * Результаты спортсмена на соревнованиях в прыжках в высоту представлены строкой вида 118 | * "220 + 224 %+ 228 %- 230 + 232 %%- 234 %". 119 | * Здесь + соответствует удачной попытке, % неудачной, - пропущенной. 120 | * Высота и соответствующие ей попытки разделяются пробелом. 121 | * Прочитать строку и вернуть максимальную взятую высоту (230 в примере). 122 | * При нарушении формата входной строки вернуть -1. 123 | */ 124 | fun bestHighJump(jumps: String): Int = TODO() 125 | 126 | /** 127 | * Сложная 128 | * 129 | * В строке представлено выражение вида "2 + 31 - 40 + 13", 130 | * использующее целые положительные числа, плюсы и минусы, разделённые пробелами. 131 | * Наличие двух знаков подряд "13 + + 10" или двух чисел подряд "1 2" не допускается. 132 | * Вернуть значение выражения (6 для примера). 133 | * Про нарушении формата входной строки бросить исключение IllegalArgumentException 134 | */ 135 | fun plusMinus(expression: String): Int = TODO() 136 | 137 | /** 138 | * Сложная 139 | * 140 | * Строка состоит из набора слов, отделённых друг от друга одним пробелом. 141 | * Определить, имеются ли в строке повторяющиеся слова, идущие друг за другом. 142 | * Слова, отличающиеся только регистром, считать совпадающими. 143 | * Вернуть индекс начала первого повторяющегося слова, или -1, если повторов нет. 144 | * Пример: "Он пошёл в в школу" => результат 9 (индекс первого 'в') 145 | */ 146 | fun firstDuplicateIndex(str: String): Int = TODO() 147 | 148 | /** 149 | * Сложная 150 | * 151 | * Строка содержит названия товаров и цены на них в формате вида 152 | * "Хлеб 39.9; Молоко 62; Курица 184.0; Конфеты 89.9". 153 | * То есть, название товара отделено от цены пробелом, 154 | * а цена отделена от названия следующего товара точкой с запятой и пробелом. 155 | * Вернуть название самого дорогого товара в списке (в примере это Курица), 156 | * или пустую строку при нарушении формата строки. 157 | * Все цены должны быть больше либо равны нуля. 158 | */ 159 | fun mostExpensive(description: String): String = TODO() 160 | 161 | /** 162 | * Сложная 163 | * 164 | * Перевести число roman, заданное в римской системе счисления, 165 | * в десятичную систему и вернуть как результат. 166 | * Римские цифры: 1 = I, 4 = IV, 5 = V, 9 = IX, 10 = X, 40 = XL, 50 = L, 167 | * 90 = XC, 100 = C, 400 = CD, 500 = D, 900 = CM, 1000 = M. 168 | * Например: XXIII = 23, XLIV = 44, C = 100 169 | * 170 | * Вернуть -1, если roman не является корректным римским числом 171 | */ 172 | fun fromRoman(roman: String): Int = TODO() 173 | 174 | /** 175 | * Очень сложная 176 | * 177 | * Имеется специальное устройство, представляющее собой 178 | * конвейер из cells ячеек (нумеруются от 0 до cells - 1 слева направо) и датчик, двигающийся над этим конвейером. 179 | * Строка commands содержит последовательность команд, выполняемых данным устройством, например +>+>+>+>+ 180 | * Каждая команда кодируется одним специальным символом: 181 | * > - сдвиг датчика вправо на 1 ячейку; 182 | * < - сдвиг датчика влево на 1 ячейку; 183 | * + - увеличение значения в ячейке под датчиком на 1 ед.; 184 | * - - уменьшение значения в ячейке под датчиком на 1 ед.; 185 | * [ - если значение под датчиком равно 0, в качестве следующей команды следует воспринимать 186 | * не следующую по порядку, а идущую за соответствующей следующей командой ']' (с учётом вложенности); 187 | * ] - если значение под датчиком не равно 0, в качестве следующей команды следует воспринимать 188 | * не следующую по порядку, а идущую за соответствующей предыдущей командой '[' (с учётом вложенности); 189 | * (комбинация [] имитирует цикл) 190 | * пробел - пустая команда 191 | * 192 | * Изначально все ячейки заполнены значением 0 и датчик стоит на ячейке с номером N/2 (округлять вниз) 193 | * 194 | * После выполнения limit команд или всех команд из commands следует прекратить выполнение последовательности команд. 195 | * Учитываются все команды, в том числе несостоявшиеся переходы ("[" при значении под датчиком не равном 0 и "]" при 196 | * значении под датчиком равном 0) и пробелы. 197 | * 198 | * Вернуть список размера cells, содержащий элементы ячеек устройства после завершения выполнения последовательности. 199 | * Например, для 10 ячеек и командной строки +>+>+>+>+ результат должен быть 0,0,0,0,0,1,1,1,1,1 200 | * 201 | * Все прочие символы следует считать ошибочными и формировать исключение IllegalArgumentException. 202 | * То же исключение формируется, если у символов [ ] не оказывается пары. 203 | * Выход за границу конвейера также следует считать ошибкой и формировать исключение IllegalStateException. 204 | * Считать, что ошибочные символы и непарные скобки являются более приоритетной ошибкой чем выход за границу ленты, 205 | * то есть если в программе присутствует некорректный символ или непарная скобка, то должно быть выброшено 206 | * IllegalArgumentException. 207 | * IllegalArgumentException должен бросаться даже если ошибочная команда не была достигнута в ходе выполнения. 208 | * 209 | */ 210 | fun computeDeviceCells(cells: Int, commands: String, limit: Int): List = TODO() 211 | -------------------------------------------------------------------------------- /src/lesson8/task1/Geometry.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson8.task1 3 | 4 | import lesson1.task1.sqr 5 | import kotlin.math.PI 6 | import kotlin.math.cos 7 | import kotlin.math.sin 8 | import kotlin.math.sqrt 9 | 10 | /** 11 | * Точка на плоскости 12 | */ 13 | data class Point(val x: Double, val y: Double) { 14 | /** 15 | * Пример 16 | * 17 | * Рассчитать (по известной формуле) расстояние между двумя точками 18 | */ 19 | fun distance(other: Point): Double = sqrt(sqr(x - other.x) + sqr(y - other.y)) 20 | } 21 | 22 | /** 23 | * Треугольник, заданный тремя точками (a, b, c, см. constructor ниже). 24 | * Эти три точки хранятся в множестве points, их порядок не имеет значения. 25 | */ 26 | class Triangle private constructor(private val points: Set) { 27 | 28 | private val pointList = points.toList() 29 | 30 | val a: Point get() = pointList[0] 31 | 32 | val b: Point get() = pointList[1] 33 | 34 | val c: Point get() = pointList[2] 35 | 36 | constructor(a: Point, b: Point, c: Point): this(linkedSetOf(a, b, c)) 37 | /** 38 | * Пример: полупериметр 39 | */ 40 | fun halfPerimeter() = (a.distance(b) + b.distance(c) + c.distance(a)) / 2.0 41 | 42 | /** 43 | * Пример: площадь 44 | */ 45 | fun area(): Double { 46 | val p = halfPerimeter() 47 | return sqrt(p * (p - a.distance(b)) * (p - b.distance(c)) * (p - c.distance(a))) 48 | } 49 | 50 | /** 51 | * Пример: треугольник содержит точку 52 | */ 53 | fun contains(p: Point): Boolean { 54 | val abp = Triangle(a, b, p) 55 | val bcp = Triangle(b, c, p) 56 | val cap = Triangle(c, a, p) 57 | return abp.area() + bcp.area() + cap.area() <= area() 58 | } 59 | 60 | override fun equals(other: Any?) = other is Triangle && points == other.points 61 | 62 | override fun hashCode() = points.hashCode() 63 | 64 | override fun toString() = "Triangle(a = $a, b = $b, c = $c)" 65 | } 66 | 67 | /** 68 | * Окружность с заданным центром и радиусом 69 | */ 70 | data class Circle(val center: Point, val radius: Double) { 71 | /** 72 | * Простая 73 | * 74 | * Рассчитать расстояние между двумя окружностями. 75 | * Расстояние между непересекающимися окружностями рассчитывается как 76 | * расстояние между их центрами минус сумма их радиусов. 77 | * Расстояние между пересекающимися окружностями считать равным 0.0. 78 | */ 79 | fun distance(other: Circle): Double = TODO() 80 | 81 | /** 82 | * Тривиальная 83 | * 84 | * Вернуть true, если и только если окружность содержит данную точку НА себе или ВНУТРИ себя 85 | */ 86 | fun contains(p: Point): Boolean = TODO() 87 | } 88 | 89 | /** 90 | * Отрезок между двумя точками 91 | */ 92 | data class Segment(val begin: Point, val end: Point) { 93 | override fun equals(other: Any?) = 94 | other is Segment && (begin == other.begin && end == other.end || end == other.begin && begin == other.end) 95 | 96 | override fun hashCode() = 97 | begin.hashCode() + end.hashCode() 98 | } 99 | 100 | /** 101 | * Средняя 102 | * 103 | * Дано множество точек. Вернуть отрезок, соединяющий две наиболее удалённые из них. 104 | * Если в множестве менее двух точек, бросить IllegalArgumentException 105 | */ 106 | fun diameter(vararg points: Point): Segment = TODO() 107 | 108 | /** 109 | * Простая 110 | * 111 | * Построить окружность по её диаметру, заданному двумя точками 112 | * Центр её должен находиться посередине между точками, а радиус составлять половину расстояния между ними 113 | */ 114 | fun circleByDiameter(diameter: Segment): Circle = TODO() 115 | 116 | /** 117 | * Прямая, заданная точкой point и углом наклона angle (в радианах) по отношению к оси X. 118 | * Уравнение прямой: (y - point.y) * cos(angle) = (x - point.x) * sin(angle) 119 | * или: y * cos(angle) = x * sin(angle) + b, где b = point.y * cos(angle) - point.x * sin(angle). 120 | * Угол наклона обязан находиться в диапазоне от 0 (включительно) до PI (исключительно). 121 | */ 122 | class Line private constructor(val b: Double, val angle: Double) { 123 | init { 124 | require(angle >= 0 && angle < PI) { "Incorrect line angle: $angle" } 125 | } 126 | 127 | constructor(point: Point, angle: Double): this(point.y * cos(angle) - point.x * sin(angle), angle) 128 | 129 | /** 130 | * Средняя 131 | * 132 | * Найти точку пересечения с другой линией. 133 | * Для этого необходимо составить и решить систему из двух уравнений (каждое для своей прямой) 134 | */ 135 | fun crossPoint(other: Line): Point = TODO() 136 | 137 | override fun equals(other: Any?) = other is Line && angle == other.angle && b == other.b 138 | 139 | override fun hashCode(): Int { 140 | var result = b.hashCode() 141 | result = 31 * result + angle.hashCode() 142 | return result 143 | } 144 | 145 | override fun toString() = "Line(${cos(angle)} * y = ${sin(angle)} * x + $b)" 146 | } 147 | 148 | /** 149 | * Средняя 150 | * 151 | * Построить прямую по отрезку 152 | */ 153 | fun lineBySegment(s: Segment): Line = TODO() 154 | 155 | /** 156 | * Средняя 157 | * 158 | * Построить прямую по двум точкам 159 | */ 160 | fun lineByPoints(a: Point, b: Point): Line = TODO() 161 | 162 | /** 163 | * Сложная 164 | * 165 | * Построить серединный перпендикуляр по отрезку или по двум точкам 166 | */ 167 | fun bisectorByPoints(a: Point, b: Point): Line = TODO() 168 | 169 | /** 170 | * Средняя 171 | * 172 | * Задан список из n окружностей на плоскости. Найти пару наименее удалённых из них. 173 | * Если в списке менее двух окружностей, бросить IllegalArgumentException 174 | */ 175 | fun findNearestCirclePair(vararg circles: Circle): Pair = TODO() 176 | 177 | /** 178 | * Сложная 179 | * 180 | * Дано три различные точки. Построить окружность, проходящую через них 181 | * (все три точки должны лежать НА, а не ВНУТРИ, окружности). 182 | * Описание алгоритмов см. в Интернете 183 | * (построить окружность по трём точкам, или 184 | * построить окружность, описанную вокруг треугольника - эквивалентная задача). 185 | */ 186 | fun circleByThreePoints(a: Point, b: Point, c: Point): Circle = TODO() 187 | 188 | /** 189 | * Очень сложная 190 | * 191 | * Дано множество точек на плоскости. Найти круг минимального радиуса, 192 | * содержащий все эти точки. Если множество пустое, бросить IllegalArgumentException. 193 | * Если множество содержит одну точку, вернуть круг нулевого радиуса с центром в данной точке. 194 | * 195 | * Примечание: в зависимости от ситуации, такая окружность может либо проходить через какие-либо 196 | * три точки данного множества, либо иметь своим диаметром отрезок, 197 | * соединяющий две самые удалённые точки в данном множестве. 198 | */ 199 | fun minContainingCircle(vararg points: Point): Circle = TODO() 200 | 201 | -------------------------------------------------------------------------------- /src/lesson8/task2/Chess.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson8.task2 3 | 4 | /** 5 | * Клетка шахматной доски. Шахматная доска квадратная и имеет 8 х 8 клеток. 6 | * Поэтому, обе координаты клетки (горизонталь row, вертикаль column) могут находиться в пределах от 1 до 8. 7 | * Горизонтали нумеруются снизу вверх, вертикали слева направо. 8 | */ 9 | data class Square(val column: Int, val row: Int) { 10 | /** 11 | * Пример 12 | * 13 | * Возвращает true, если клетка находится в пределах доски 14 | */ 15 | fun inside(): Boolean = column in 1..8 && row in 1..8 16 | 17 | /** 18 | * Простая 19 | * 20 | * Возвращает строковую нотацию для клетки. 21 | * В нотации, колонки обозначаются латинскими буквами от a до h, а ряды -- цифрами от 1 до 8. 22 | * Для клетки не в пределах доски вернуть пустую строку 23 | */ 24 | fun notation(): String = TODO() 25 | } 26 | 27 | /** 28 | * Простая 29 | * 30 | * Создаёт клетку по строковой нотации. 31 | * В нотации, колонки обозначаются латинскими буквами от a до h, а ряды -- цифрами от 1 до 8. 32 | * Если нотация некорректна, бросить IllegalArgumentException 33 | */ 34 | fun square(notation: String): Square = TODO() 35 | 36 | /** 37 | * Простая 38 | * 39 | * Определить число ходов, за которое шахматная ладья пройдёт из клетки start в клетку end. 40 | * Шахматная ладья может за один ход переместиться на любую другую клетку 41 | * по вертикали или горизонтали. 42 | * Ниже точками выделены возможные ходы ладьи, а крестиками -- невозможные: 43 | * 44 | * xx.xxххх 45 | * xх.хxххх 46 | * ..Л..... 47 | * xх.хxххх 48 | * xx.xxххх 49 | * xx.xxххх 50 | * xx.xxххх 51 | * xx.xxххх 52 | * 53 | * Если клетки start и end совпадают, вернуть 0. 54 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 55 | * 56 | * Пример: rookMoveNumber(Square(3, 1), Square(6, 3)) = 2 57 | * Ладья может пройти через клетку (3, 3) или через клетку (6, 1) к клетке (6, 3). 58 | */ 59 | fun rookMoveNumber(start: Square, end: Square): Int = TODO() 60 | 61 | /** 62 | * Средняя 63 | * 64 | * Вернуть список из клеток, по которым шахматная ладья может быстрее всего попасть из клетки start в клетку end. 65 | * Описание ходов ладьи см. предыдущую задачу. 66 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start. 67 | * Между ними должны находиться промежуточные клетки, по порядку от start до end. 68 | * Примеры: rookTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 69 | * (здесь возможен ещё один вариант) 70 | * rookTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(3, 3), Square(6, 3)) 71 | * (здесь возможен единственный вариант) 72 | * rookTrajectory(Square(3, 5), Square(8, 5)) = listOf(Square(3, 5), Square(8, 5)) 73 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 74 | */ 75 | fun rookTrajectory(start: Square, end: Square): List = TODO() 76 | 77 | /** 78 | * Простая 79 | * 80 | * Определить число ходов, за которое шахматный слон пройдёт из клетки start в клетку end. 81 | * Шахматный слон может за один ход переместиться на любую другую клетку по диагонали. 82 | * Ниже точками выделены возможные ходы слона, а крестиками -- невозможные: 83 | * 84 | * .xxx.ххх 85 | * x.x.xххх 86 | * xxСxxxxx 87 | * x.x.xххх 88 | * .xxx.ххх 89 | * xxxxx.хх 90 | * xxxxxх.х 91 | * xxxxxхх. 92 | * 93 | * Если клетки start и end совпадают, вернуть 0. 94 | * Если клетка end недостижима для слона, вернуть -1. 95 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 96 | * 97 | * Примеры: bishopMoveNumber(Square(3, 1), Square(6, 3)) = -1; bishopMoveNumber(Square(3, 1), Square(3, 7)) = 2. 98 | * Слон может пройти через клетку (6, 4) к клетке (3, 7). 99 | */ 100 | fun bishopMoveNumber(start: Square, end: Square): Int = TODO() 101 | 102 | /** 103 | * Сложная 104 | * 105 | * Вернуть список из клеток, по которым шахматный слон может быстрее всего попасть из клетки start в клетку end. 106 | * Описание ходов слона см. предыдущую задачу. 107 | * 108 | * Если клетка end недостижима для слона, вернуть пустой список. 109 | * 110 | * Если клетка достижима: 111 | * - список всегда включает в себя клетку start 112 | * - клетка end включается, если она не совпадает со start. 113 | * - между ними должны находиться промежуточные клетки, по порядку от start до end. 114 | * 115 | * Примеры: bishopTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 116 | * bishopTrajectory(Square(3, 1), Square(3, 7)) = listOf(Square(3, 1), Square(6, 4), Square(3, 7)) 117 | * bishopTrajectory(Square(1, 3), Square(6, 8)) = listOf(Square(1, 3), Square(6, 8)) 118 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 119 | */ 120 | fun bishopTrajectory(start: Square, end: Square): List = TODO() 121 | 122 | /** 123 | * Средняя 124 | * 125 | * Определить число ходов, за которое шахматный король пройдёт из клетки start в клетку end. 126 | * Шахматный король одним ходом может переместиться из клетки, в которой стоит, 127 | * на любую соседнюю по вертикали, горизонтали или диагонали. 128 | * Ниже точками выделены возможные ходы короля, а крестиками -- невозможные: 129 | * 130 | * xxxxx 131 | * x...x 132 | * x.K.x 133 | * x...x 134 | * xxxxx 135 | * 136 | * Если клетки start и end совпадают, вернуть 0. 137 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 138 | * 139 | * Пример: kingMoveNumber(Square(3, 1), Square(6, 3)) = 3. 140 | * Король может последовательно пройти через клетки (4, 2) и (5, 2) к клетке (6, 3). 141 | */ 142 | fun kingMoveNumber(start: Square, end: Square): Int = TODO() 143 | 144 | /** 145 | * Сложная 146 | * 147 | * Вернуть список из клеток, по которым шахматный король может быстрее всего попасть из клетки start в клетку end. 148 | * Описание ходов короля см. предыдущую задачу. 149 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start. 150 | * Между ними должны находиться промежуточные клетки, по порядку от start до end. 151 | * Примеры: kingTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 152 | * (здесь возможны другие варианты) 153 | * kingTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(4, 2), Square(5, 2), Square(6, 3)) 154 | * (здесь возможен единственный вариант) 155 | * kingTrajectory(Square(3, 5), Square(6, 2)) = listOf(Square(3, 5), Square(4, 4), Square(5, 3), Square(6, 2)) 156 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 157 | */ 158 | fun kingTrajectory(start: Square, end: Square): List = TODO() 159 | 160 | /** 161 | * Сложная 162 | * 163 | * Определить число ходов, за которое шахматный конь пройдёт из клетки start в клетку end. 164 | * Шахматный конь одним ходом вначале передвигается ровно на 2 клетки по горизонтали или вертикали, 165 | * а затем ещё на 1 клетку под прямым углом, образуя букву "Г". 166 | * Ниже точками выделены возможные ходы коня, а крестиками -- невозможные: 167 | * 168 | * .xxx.xxx 169 | * xxKxxxxx 170 | * .xxx.xxx 171 | * x.x.xxxx 172 | * xxxxxxxx 173 | * xxxxxxxx 174 | * xxxxxxxx 175 | * xxxxxxxx 176 | * 177 | * Если клетки start и end совпадают, вернуть 0. 178 | * Если любая из клеток некорректна, бросить IllegalArgumentException(). 179 | * 180 | * Пример: knightMoveNumber(Square(3, 1), Square(6, 3)) = 3. 181 | * Конь может последовательно пройти через клетки (5, 2) и (4, 4) к клетке (6, 3). 182 | */ 183 | fun knightMoveNumber(start: Square, end: Square): Int = TODO() 184 | 185 | /** 186 | * Очень сложная 187 | * 188 | * Вернуть список из клеток, по которым шахматный конь может быстрее всего попасть из клетки start в клетку end. 189 | * Описание ходов коня см. предыдущую задачу. 190 | * Список всегда включает в себя клетку start. Клетка end включается, если она не совпадает со start. 191 | * Между ними должны находиться промежуточные клетки, по порядку от start до end. 192 | * Примеры: 193 | * 194 | * knightTrajectory(Square(3, 3), Square(3, 3)) = listOf(Square(3, 3)) 195 | * здесь возможны другие варианты) 196 | * knightTrajectory(Square(3, 1), Square(6, 3)) = listOf(Square(3, 1), Square(5, 2), Square(4, 4), Square(6, 3)) 197 | * (здесь возможен единственный вариант) 198 | * knightTrajectory(Square(3, 5), Square(5, 6)) = listOf(Square(3, 5), Square(5, 6)) 199 | * (здесь опять возможны другие варианты) 200 | * knightTrajectory(Square(7, 7), Square(8, 8)) = 201 | * listOf(Square(7, 7), Square(5, 8), Square(4, 6), Square(6, 7), Square(8, 8)) 202 | * 203 | * Если возможно несколько вариантов самой быстрой траектории, вернуть любой из них. 204 | */ 205 | fun knightTrajectory(start: Square, end: Square): List = TODO() 206 | -------------------------------------------------------------------------------- /src/lesson8/task3/Graph.kt: -------------------------------------------------------------------------------- 1 | package lesson8.task3 2 | 3 | import java.util.* 4 | 5 | class Graph { 6 | private data class Vertex(val name: String) { 7 | val neighbors = mutableSetOf() 8 | } 9 | 10 | private val vertices = mutableMapOf() 11 | 12 | private operator fun get(name: String) = vertices[name] ?: throw IllegalArgumentException() 13 | 14 | fun addVertex(name: String) { 15 | vertices[name] = Vertex(name) 16 | } 17 | 18 | private fun connect(first: Vertex, second: Vertex) { 19 | first.neighbors.add(second) 20 | second.neighbors.add(first) 21 | } 22 | 23 | fun connect(first: String, second: String) = connect(this[first], this[second]) 24 | 25 | /** 26 | * Пример 27 | * 28 | * По двум вершинам рассчитать расстояние между ними = число дуг на самом коротком пути между ними. 29 | * Вернуть -1, если пути между вершинами не существует. 30 | * 31 | * Используется поиск в ширину 32 | */ 33 | fun bfs(start: String, finish: String) = bfs(this[start], this[finish]) 34 | 35 | private fun bfs(start: Vertex, finish: Vertex): Int { 36 | val queue = ArrayDeque() 37 | queue.add(start) 38 | val visited = mutableMapOf(start to 0) 39 | while (queue.isNotEmpty()) { 40 | val next = queue.poll() 41 | val distance = visited[next]!! 42 | if (next == finish) return distance 43 | for (neighbor in next.neighbors) { 44 | if (neighbor in visited) continue 45 | visited.put(neighbor, distance + 1) 46 | queue.add(neighbor) 47 | } 48 | } 49 | return -1 50 | } 51 | 52 | /** 53 | * Пример 54 | * 55 | * По двум вершинам рассчитать расстояние между ними = число дуг на самом коротком пути между ними. 56 | * Вернуть -1, если пути между вершинами не существует. 57 | * 58 | * Используется поиск в глубину 59 | */ 60 | fun dfs(start: String, finish: String): Int = dfs(this[start], this[finish], setOf()) ?: -1 61 | 62 | private fun dfs(start: Vertex, finish: Vertex, visited: Set): Int? = 63 | if (start == finish) 0 64 | else { 65 | val min = start.neighbors 66 | .filter { it !in visited } 67 | .mapNotNull { dfs(it, finish, visited + start) } 68 | .min() 69 | if (min == null) null else min + 1 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/lesson9/task1/Matrix.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER", "unused") 2 | package lesson9.task1 3 | 4 | /** 5 | * Ячейка матрицы: row = ряд, column = колонка 6 | */ 7 | data class Cell(val row: Int, val column: Int) 8 | 9 | /** 10 | * Интерфейс, описывающий возможности матрицы. E = тип элемента матрицы 11 | */ 12 | interface Matrix { 13 | /** Высота */ 14 | val height: Int 15 | 16 | /** Ширина */ 17 | val width: Int 18 | 19 | /** 20 | * Доступ к ячейке. 21 | * Методы могут бросить исключение, если ячейка не существует или пуста 22 | */ 23 | operator fun get(row: Int, column: Int): E 24 | operator fun get(cell: Cell): E 25 | 26 | /** 27 | * Запись в ячейку. 28 | * Методы могут бросить исключение, если ячейка не существует 29 | */ 30 | operator fun set(row: Int, column: Int, value: E) 31 | operator fun set(cell: Cell, value: E) 32 | } 33 | 34 | /** 35 | * Простая 36 | * 37 | * Метод для создания матрицы, должен вернуть РЕАЛИЗАЦИЮ Matrix. 38 | * height = высота, width = ширина, e = чем заполнить элементы. 39 | * Бросить исключение IllegalArgumentException, если height или width <= 0. 40 | */ 41 | fun createMatrix(height: Int, width: Int, e: E): Matrix = TODO() 42 | 43 | /** 44 | * Средняя сложность 45 | * 46 | * Реализация интерфейса "матрица" 47 | */ 48 | class MatrixImpl : Matrix { 49 | override val height: Int = TODO() 50 | 51 | override val width: Int = TODO() 52 | 53 | override fun get(row: Int, column: Int): E = TODO() 54 | 55 | override fun get(cell: Cell): E = TODO() 56 | 57 | override fun set(row: Int, column: Int, value: E) { 58 | TODO() 59 | } 60 | 61 | override fun set(cell: Cell, value: E) { 62 | TODO() 63 | } 64 | 65 | override fun equals(other: Any?) = TODO() 66 | 67 | override fun toString(): String = TODO() 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/lesson9/task2/Matrices.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | package lesson9.task2 3 | 4 | import lesson9.task1.Matrix 5 | import lesson9.task1.createMatrix 6 | 7 | // Все задачи в этом файле требуют наличия реализации интерфейса "Матрица" в Matrix.kt 8 | 9 | /** 10 | * Пример 11 | * 12 | * Транспонировать заданную матрицу matrix. 13 | * При транспонировании строки матрицы становятся столбцами и наоборот: 14 | * 15 | * 1 2 3 1 4 6 3 16 | * 4 5 6 ==> 2 5 5 2 17 | * 6 5 4 3 6 4 1 18 | * 3 2 1 19 | */ 20 | fun transpose(matrix: Matrix): Matrix { 21 | if (matrix.width < 1 || matrix.height < 1) return matrix 22 | val result = createMatrix(height = matrix.width, width = matrix.height, e = matrix[0, 0]) 23 | for (i in 0 until matrix.width) { 24 | for (j in 0 until matrix.height) { 25 | result[i, j] = matrix[j, i] 26 | } 27 | } 28 | return result 29 | } 30 | 31 | /** 32 | * Пример 33 | * 34 | * Сложить две заданные матрицы друг с другом. 35 | * Складывать можно только матрицы совпадающего размера -- в противном случае бросить IllegalArgumentException. 36 | * При сложении попарно складываются соответствующие элементы матриц 37 | */ 38 | operator fun Matrix.plus(other: Matrix): Matrix { 39 | if (width != other.width || height != other.height) throw IllegalArgumentException() 40 | if (width < 1 || height < 1) return this 41 | val result = createMatrix(height, width, this[0, 0]) 42 | for (i in 0 until height) { 43 | for (j in 0 until width) { 44 | result[i, j] = this[i, j] + other[i, j] 45 | } 46 | } 47 | return result 48 | } 49 | 50 | /** 51 | * Сложная 52 | * 53 | * Заполнить матрицу заданной высоты height и ширины width 54 | * натуральными числами от 1 до m*n по спирали, 55 | * начинающейся в левом верхнем углу и закрученной по часовой стрелке. 56 | * 57 | * Пример для height = 3, width = 4: 58 | * 1 2 3 4 59 | * 10 11 12 5 60 | * 9 8 7 6 61 | */ 62 | fun generateSpiral(height: Int, width: Int): Matrix = TODO() 63 | 64 | /** 65 | * Сложная 66 | * 67 | * Заполнить матрицу заданной высоты height и ширины width следующим образом. 68 | * Элементам, находящимся на периферии (по периметру матрицы), присвоить значение 1; 69 | * периметру оставшейся подматрицы – значение 2 и так далее до заполнения всей матрицы. 70 | * 71 | * Пример для height = 5, width = 6: 72 | * 1 1 1 1 1 1 73 | * 1 2 2 2 2 1 74 | * 1 2 3 3 2 1 75 | * 1 2 2 2 2 1 76 | * 1 1 1 1 1 1 77 | */ 78 | fun generateRectangles(height: Int, width: Int): Matrix = TODO() 79 | 80 | /** 81 | * Сложная 82 | * 83 | * Заполнить матрицу заданной высоты height и ширины width диагональной змейкой: 84 | * в левый верхний угол 1, во вторую от угла диагональ 2 и 3 сверху вниз, в третью 4-6 сверху вниз и так далее. 85 | * 86 | * Пример для height = 5, width = 4: 87 | * 1 2 4 7 88 | * 3 5 8 11 89 | * 6 9 12 15 90 | * 10 13 16 18 91 | * 14 17 19 20 92 | */ 93 | fun generateSnake(height: Int, width: Int): Matrix = TODO() 94 | 95 | /** 96 | * Средняя 97 | * 98 | * Содержимое квадратной матрицы matrix (с произвольным содержимым) повернуть на 90 градусов по часовой стрелке. 99 | * Если height != width, бросить IllegalArgumentException. 100 | * 101 | * Пример: Станет: 102 | * 1 2 3 7 4 1 103 | * 4 5 6 8 5 2 104 | * 7 8 9 9 6 3 105 | */ 106 | fun rotate(matrix: Matrix): Matrix = TODO() 107 | 108 | /** 109 | * Сложная 110 | * 111 | * Проверить, является ли квадратная целочисленная матрица matrix латинским квадратом. 112 | * Латинским квадратом называется матрица размером n x n, 113 | * каждая строка и каждый столбец которой содержат все числа от 1 до n. 114 | * Если height != width, вернуть false. 115 | * 116 | * Пример латинского квадрата 3х3: 117 | * 2 3 1 118 | * 1 2 3 119 | * 3 1 2 120 | */ 121 | fun isLatinSquare(matrix: Matrix): Boolean = TODO() 122 | 123 | /** 124 | * Средняя 125 | * 126 | * В матрице matrix каждый элемент заменить суммой непосредственно примыкающих к нему 127 | * элементов по вертикали, горизонтали и диагоналям. 128 | * 129 | * Пример для матрицы 4 x 3: (11=2+4+5, 19=1+3+4+5+6, ...) 130 | * 1 2 3 11 19 13 131 | * 4 5 6 ===> 19 31 19 132 | * 6 5 4 19 31 19 133 | * 3 2 1 13 19 11 134 | * 135 | * Поскольку в матрице 1 х 1 примыкающие элементы отсутствуют, 136 | * для неё следует вернуть как результат нулевую матрицу: 137 | * 138 | * 42 ===> 0 139 | */ 140 | fun sumNeighbours(matrix: Matrix): Matrix = TODO() 141 | 142 | /** 143 | * Средняя 144 | * 145 | * Целочисленная матрица matrix состоит из "дырок" (на их месте стоит 0) и "кирпичей" (на их месте стоит 1). 146 | * Найти в этой матрице все ряды и колонки, целиком состоящие из "дырок". 147 | * Результат вернуть в виде Holes(rows = список дырчатых рядов, columns = список дырчатых колонок). 148 | * Ряды и колонки нумеруются с нуля. Любой из спискоов rows / columns может оказаться пустым. 149 | * 150 | * Пример для матрицы 5 х 4: 151 | * 1 0 1 0 152 | * 0 0 1 0 153 | * 1 0 0 0 ==> результат: Holes(rows = listOf(4), columns = listOf(1, 3)): 4-й ряд, 1-я и 3-я колонки 154 | * 0 0 1 0 155 | * 0 0 0 0 156 | */ 157 | fun findHoles(matrix: Matrix): Holes = TODO() 158 | 159 | /** 160 | * Класс для описания местонахождения "дырок" в матрице 161 | */ 162 | data class Holes(val rows: List, val columns: List) 163 | 164 | /** 165 | * Средняя 166 | * 167 | * В целочисленной матрице matrix каждый элемент заменить суммой элементов подматрицы, 168 | * расположенной в левом верхнем углу матрицы matrix и ограниченной справа-снизу данным элементом. 169 | * 170 | * Пример для матрицы 3 х 3: 171 | * 172 | * 1 2 3 1 3 6 173 | * 4 5 6 => 5 12 21 174 | * 7 8 9 12 27 45 175 | * 176 | * К примеру, центральный элемент 12 = 1 + 2 + 4 + 5, элемент в левом нижнем углу 12 = 1 + 4 + 7 и так далее. 177 | */ 178 | fun sumSubMatrix(matrix: Matrix): Matrix = TODO() 179 | 180 | /** 181 | * Сложная 182 | * 183 | * Даны мозаичные изображения замочной скважины и ключа. Пройдет ли ключ в скважину? 184 | * То есть даны две матрицы key и lock, key.height <= lock.height, key.width <= lock.width, состоящие из нулей и единиц. 185 | * 186 | * Проверить, можно ли наложить матрицу key на матрицу lock (без поворота, разрешается только сдвиг) так, 187 | * чтобы каждой единице в матрице lock (штырь) соответствовал ноль в матрице key (прорезь), 188 | * а каждому нулю в матрице lock (дырка) соответствовала, наоборот, единица в матрице key (штырь). 189 | * Ключ при сдвиге не может выходить за пределы замка. 190 | * 191 | * Пример: ключ подойдёт, если его сдвинуть на 1 по ширине 192 | * lock key 193 | * 1 0 1 1 0 194 | * 0 1 0 0 1 195 | * 1 1 1 196 | * 197 | * Вернуть тройку (Triple) -- (да/нет, требуемый сдвиг по высоте, требуемый сдвиг по ширине). 198 | * Если наложение невозможно, то первый элемент тройки "нет" и сдвиги могут быть любыми. 199 | */ 200 | fun canOpenLock(key: Matrix, lock: Matrix): Triple = TODO() 201 | 202 | /** 203 | * Простая 204 | * 205 | * Инвертировать заданную матрицу. 206 | * При инвертировании знак каждого элемента матрицы следует заменить на обратный 207 | */ 208 | operator fun Matrix.unaryMinus(): Matrix = TODO(this.toString()) 209 | 210 | /** 211 | * Средняя 212 | * 213 | * Перемножить две заданные матрицы друг с другом. 214 | * Матрицы можно умножать, только если ширина первой матрицы совпадает с высотой второй матрицы. 215 | * В противном случае бросить IllegalArgumentException. 216 | * Подробно про порядок умножения см. статью Википедии "Умножение матриц". 217 | */ 218 | operator fun Matrix.times(other: Matrix): Matrix = TODO(this.toString()) 219 | 220 | /** 221 | * Сложная 222 | * 223 | * В матрице matrix размером 4х4 дана исходная позиция для игры в 15, например 224 | * 5 7 9 1 225 | * 2 12 14 15 226 | * 3 4 6 8 227 | * 10 11 13 0 228 | * 229 | * Здесь 0 обозначает пустую клетку, а 1-15 – фишки с соответствующими номерами. 230 | * Напомним, что "игра в 15" имеет квадратное поле 4х4, по которому двигается 15 фишек, 231 | * одна клетка всегда остаётся пустой. Цель игры -- упорядочить фишки на игровом поле. 232 | * 233 | * В списке moves задана последовательность ходов, например [8, 6, 13, 11, 10, 3]. 234 | * Ход задаётся номером фишки, которая передвигается на пустое место (то есть, меняется местами с нулём). 235 | * Фишка должна примыкать к пустому месту по горизонтали или вертикали, иначе ход не будет возможным. 236 | * Все номера должны быть в пределах от 1 до 15. 237 | * Определить финальную позицию после выполнения всех ходов и вернуть её. 238 | * Если какой-либо ход является невозможным или список содержит неверные номера, 239 | * бросить IllegalStateException. 240 | * 241 | * В данном случае должно получиться 242 | * 5 7 9 1 243 | * 2 12 14 15 244 | * 0 4 13 6 245 | * 3 10 11 8 246 | */ 247 | fun fifteenGameMoves(matrix: Matrix, moves: List): Matrix = TODO() 248 | 249 | /** 250 | * Очень сложная 251 | * 252 | * В матрице matrix размером 4х4 дана исходная позиция для игры в 15, например 253 | * 5 7 9 2 254 | * 1 12 14 15 255 | * 3 4 6 8 256 | * 10 11 13 0 257 | * 258 | * Здесь 0 обозначает пустую клетку, а 1-15 – фишки с соответствующими номерами. 259 | * Напомним, что "игра в 15" имеет квадратное поле 4х4, по которому двигается 15 фишек, 260 | * одна клетка всегда остаётся пустой. 261 | * 262 | * Цель игры -- упорядочить фишки на игровом поле, приведя позицию к одному из следующих двух состояний: 263 | * 264 | * 1 2 3 4 1 2 3 4 265 | * 5 6 7 8 ИЛИ 5 6 7 8 266 | * 9 10 11 12 9 10 11 12 267 | * 13 14 15 0 13 15 14 0 268 | * 269 | * Можно математически доказать, что РОВНО ОДНО из этих двух состояний достижимо из любой исходной позиции. 270 | * 271 | * Вернуть решение -- список ходов, приводящих исходную позицию к одной из двух упорядоченных. 272 | * Каждый ход -- это перемена мест фишки с заданным номером с пустой клеткой (0), 273 | * при этом заданная фишка должна по горизонтали или по вертикали примыкать к пустой клетке (но НЕ по диагонали). 274 | * К примеру, ход 13 в исходной позиции меняет местами 13 и 0, а ход 11 в той же позиции невозможен. 275 | * 276 | * Одно из решений исходной позиции: 277 | * 278 | * [8, 6, 14, 12, 4, 11, 13, 14, 12, 4, 279 | * 7, 5, 1, 3, 11, 7, 3, 11, 7, 12, 6, 280 | * 15, 4, 9, 2, 4, 9, 3, 5, 2, 3, 9, 281 | * 15, 8, 14, 13, 12, 7, 11, 5, 7, 6, 282 | * 9, 15, 8, 14, 13, 9, 15, 7, 6, 12, 283 | * 9, 13, 14, 15, 12, 11, 10, 9, 13, 14, 284 | * 15, 12, 11, 10, 9, 13, 14, 15] 285 | * 286 | * Перед решением этой задачи НЕОБХОДИМО решить предыдущую 287 | */ 288 | fun fifteenGameSolution(matrix: Matrix): List = TODO() 289 | -------------------------------------------------------------------------------- /test/lesson1/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson1.task1 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | import kotlin.math.PI 7 | 8 | class Tests { 9 | @Test 10 | @Tag("Example") 11 | fun sqr() { 12 | assertEquals(0, sqr(0)) 13 | assertEquals(4, sqr(2)) 14 | assertEquals(9, sqr(-3)) 15 | } 16 | 17 | @Test 18 | @Tag("Example") 19 | fun sqrDouble() { 20 | assertEquals(0.0, sqr(0.0), 1e-13) 21 | assertEquals(4.0, sqr(2.0), 1e-13) 22 | assertEquals(9.0, sqr(-3.0), 1e-13) 23 | } 24 | 25 | @Test 26 | @Tag("Example") 27 | fun discriminant() { 28 | assertEquals(0.0, discriminant(0.0, 0.0, 0.0), 1e-13) 29 | assertEquals(0.0, discriminant(1.0, -2.0, 1.0), 1e-13) 30 | assertEquals(1.0, discriminant(1.0, 3.0, 2.0), 1e-13) 31 | } 32 | 33 | @Test 34 | @Tag("Example") 35 | fun quadraticEquationRoot() { 36 | assertEquals(2.0, quadraticEquationRoot(1.0, -3.0, 2.0), 1e-13) 37 | assertEquals(1.0, quadraticEquationRoot(1.0, -2.0, 1.0), 1e-13) 38 | assertEquals(-3.0, quadraticEquationRoot(1.0, 6.0, 9.0), 1e-13) 39 | } 40 | 41 | @Test 42 | @Tag("Example") 43 | fun quadraticRootProduct() { 44 | assertEquals(1.0, quadraticRootProduct(1.0, -2.0, 1.0), 1e-13) 45 | assertEquals(9.0, quadraticRootProduct(1.0, 6.0, 9.0), 1e-13) 46 | assertEquals(2.0, quadraticRootProduct(1.0, 3.0, 2.0), 1e-13) 47 | } 48 | 49 | @Test 50 | @Tag("Trivial") 51 | fun seconds() { 52 | assertEquals(30035, seconds(8, 20, 35)) 53 | assertEquals(86400, seconds(24, 0, 0)) 54 | assertEquals(13, seconds(0, 0, 13)) 55 | } 56 | 57 | @Test 58 | @Tag("Trivial") 59 | fun lengthInMeters() { 60 | assertEquals(18.98, lengthInMeters(8, 2, 11), 1e-2) 61 | assertEquals(2.13, lengthInMeters(1, 0, 0), 1e-2) 62 | } 63 | 64 | @Test 65 | @Tag("Trivial") 66 | fun angleInRadian() { 67 | assertEquals(0.63256, angleInRadian(36, 14, 35), 1e-5) 68 | assertEquals(PI / 2.0, angleInRadian(90, 0, 0), 1e-5) 69 | } 70 | 71 | @Test 72 | @Tag("Trivial") 73 | fun trackLength() { 74 | assertEquals(5.0, trackLength(3.0, 0.0, 0.0, 4.0), 1e-5) 75 | assertEquals(1.0, trackLength(0.0, 1.0, -1.0, 1.0), 1e-5) 76 | assertEquals(1.41, trackLength(1.0, 1.0, 2.0, 2.0), 1e-2) 77 | } 78 | 79 | @Test 80 | @Tag("Easy") 81 | fun thirdDigit() { 82 | assertEquals(8, thirdDigit(3801)) 83 | assertEquals(1, thirdDigit(100)) 84 | assertEquals(0, thirdDigit(1000)) 85 | } 86 | 87 | @Test 88 | @Tag("Easy") 89 | fun travelMinutes() { 90 | assertEquals(216, travelMinutes(9, 25, 13, 1)) 91 | assertEquals(1, travelMinutes(21, 59, 22, 0)) 92 | } 93 | 94 | @Test 95 | @Tag("Easy") 96 | fun accountInThreeYears() { 97 | assertEquals(133.1, accountInThreeYears(100, 10), 1e-2) 98 | assertEquals(1.0, accountInThreeYears(1, 0), 1e-2) 99 | assertEquals(104.0, accountInThreeYears(13, 100), 1e-2) 100 | } 101 | 102 | @Test 103 | @Tag("Easy") 104 | fun numberRevert() { 105 | assertEquals(874, numberRevert(478)) 106 | assertEquals(201, numberRevert(102)) 107 | } 108 | } -------------------------------------------------------------------------------- /test/lesson10/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson10.task1 2 | 3 | import org.junit.jupiter.api.Tag 4 | import org.junit.jupiter.api.Test 5 | import org.junit.jupiter.api.Assertions.* 6 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun parseExpr() { 11 | assertEquals(mapOf(1 to 7, 2 to 16, -1 to 13), parseExpr("input/expr_in1.txt", listOf(1, 2, -1))) 12 | assertEquals(mapOf(1 to -1, 3 to 9, 4 to 6), parseExpr("input/expr_in2.txt", listOf(1, 3, 4))) 13 | try { 14 | parseExpr("input/expr_in3.txt", listOf(0)) 15 | throw AssertionError("NumberFormatException expected") 16 | } 17 | catch (e: NumberFormatException) {} 18 | assertEquals(mapOf(-2 to 12, -1 to 2, 0 to 0, 1 to 0, 2 to -4), parseExpr("input/expr_in4.txt", listOf(-2, -1, 0, 1, 2))) 19 | assertEquals(mapOf(1 to -16, 3 to -54, 4 to -73), parseExpr("input/expr_in5.txt", listOf(1, 3, 4))) 20 | assertEquals(mapOf(0 to 1, 1 to -1, -1 to 1), parseExpr("input/expr_in6.txt", listOf(0, 1, -1))) 21 | } 22 | } -------------------------------------------------------------------------------- /test/lesson10/task2/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson10.task2 2 | 3 | import org.junit.jupiter.api.Test 4 | 5 | import org.junit.jupiter.api.Assertions.* 6 | import org.junit.jupiter.api.Tag 7 | 8 | class Tests { 9 | @Test 10 | @Tag("Example") 11 | fun convertToHtmlTable() { 12 | val data = listOf(listOf("1", "2", "3"), listOf("4", "5", "6")) 13 | assertEquals("" + 14 | "" + 15 | "" + 16 | "
123
456
", data.convertToHtmlTable()) 17 | } 18 | 19 | @Test 20 | @Tag("Example") 21 | fun convertToHtmlTableUsingKotlinxHTML() { 22 | val data = listOf(listOf("1", "2", "3"), listOf("4", "5", "6")) 23 | assertEquals("" + 24 | "" + 25 | "" + 26 | "
123
456
", data.convertToHtmlTableUsingKotlinxHtml().replace("\\s+".toRegex(), "")) 27 | } 28 | 29 | @Test 30 | @Tag("Example") 31 | fun generateSimpleHtml() { 32 | assertEquals("Hello!", generateSimpleHtml("Hello!")) 33 | } 34 | } -------------------------------------------------------------------------------- /test/lesson2/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson2.task1 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun quadraticRootNumber() { 11 | assertEquals(1, quadraticRootNumber(1.0, 2.0, 1.0)) 12 | assertEquals(2, quadraticRootNumber(1.0, 3.0, 2.0)) 13 | assertEquals(0, quadraticRootNumber(1.0, 4.0, 10.0)) 14 | } 15 | 16 | @Test 17 | @Tag("Example") 18 | fun gradeNotation() { 19 | assertEquals("отлично", gradeNotation(5)) 20 | assertEquals("удовлетворительно", gradeNotation(3)) 21 | } 22 | 23 | @Test 24 | @Tag("Example") 25 | fun minBiRoot() { 26 | assertEquals(Double.NaN, minBiRoot(0.0, 0.0, 1.0), 1e-2) 27 | assertEquals(Double.NaN, minBiRoot(0.0, 1.0, 2.0), 1e-2) 28 | assertEquals(-2.0, minBiRoot(0.0, 1.0, -4.0), 1e-10) 29 | assertEquals(Double.NaN, minBiRoot(1.0, -2.0, 4.0), 1e-2) 30 | assertEquals(Double.NaN, minBiRoot(1.0, 3.0, 2.0), 1e-2) 31 | assertEquals(-1.41, minBiRoot(1.0, -3.0, 2.0), 1e-2) 32 | } 33 | 34 | @Test 35 | @Tag("Easy") 36 | fun ageDescription() { 37 | assertEquals("1 год", ageDescription(1)) 38 | assertEquals("21 год", ageDescription(21)) 39 | assertEquals("132 года", ageDescription(132)) 40 | assertEquals("12 лет", ageDescription(12)) 41 | assertEquals("111 лет", ageDescription(111)) 42 | assertEquals("199 лет", ageDescription(199)) 43 | } 44 | 45 | @Test 46 | @Tag("Easy") 47 | fun timeForHalfWay() { 48 | assertEquals(2.5, timeForHalfWay(1.0, 5.0, 2.0, 4.0, 3.0, 3.0), 1e-2) 49 | assertEquals(3.67, timeForHalfWay(4.0, 3.0, 1.0, 4.0, 1.0, 6.0), 1e-2) 50 | assertEquals(4.4, timeForHalfWay(3.0, 0.0, 1.0, 6.0, 2.0, 5.0), 1e-2) 51 | } 52 | 53 | @Test 54 | @Tag("Easy") 55 | fun whichRookThreatens() { 56 | assertEquals(0, whichRookThreatens(1, 2, 3, 4, 5, 6)) 57 | assertEquals(1, whichRookThreatens(5, 3, 7, 3, 4, 8)) 58 | assertEquals(2, whichRookThreatens(6, 8, 8, 6, 6, 3)) 59 | assertEquals(3, whichRookThreatens(3, 7, 8, 7, 3, 5)) 60 | } 61 | 62 | @Test 63 | @Tag("Easy") 64 | fun rookOrBishopThreatens() { 65 | assertEquals(0, rookOrBishopThreatens(4, 5, 5, 7, 8, 8)) 66 | assertEquals(1, rookOrBishopThreatens(2, 8, 6, 8, 1, 6)) 67 | assertEquals(2, rookOrBishopThreatens(5, 4, 3, 7, 1, 8)) 68 | assertEquals(3, rookOrBishopThreatens(1, 6, 7, 6, 3, 8)) 69 | } 70 | 71 | @Test 72 | @Tag("Easy") 73 | fun triangleKind() { 74 | assertEquals(-1, triangleKind(3.0, 7.5, 4.0)) 75 | assertEquals(1, triangleKind(5.0, 3.0, 4.0)) 76 | assertEquals(2, triangleKind(4.0, 6.0, 8.0)) 77 | assertEquals(0, triangleKind(1.0, 1.5, 1.5)) 78 | } 79 | 80 | @Test 81 | @Tag("Normal") 82 | fun segmentLength() { 83 | assertEquals(-1, segmentLength(1, 2, 3, 4)) 84 | assertEquals(-1, segmentLength(5, 7, 1, 3)) 85 | assertEquals(0, segmentLength(1, 2, 2, 4)) 86 | assertEquals(3, segmentLength(3, 6, 0, 9)) 87 | assertEquals(2, segmentLength(2, 5, 3, 9)) 88 | assertEquals(1, segmentLength(3, 6, 1, 4)) 89 | assertEquals(4, segmentLength(1, 15, 10, 14)) 90 | } 91 | } -------------------------------------------------------------------------------- /test/lesson2/task2/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson2.task2 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun pointInsideCircle() { 11 | // (1, 1) inside circle: center = (0, 0), r = 2 12 | assertTrue(pointInsideCircle(1.0, 1.0, 0.0, 0.0, 2.0)) 13 | // (2, 2) NOT inside circle: center = (0, 0), r = 2 14 | assertFalse(pointInsideCircle(2.0, 2.0, 0.0, 0.0, 2.0)) 15 | } 16 | 17 | @Test 18 | @Tag("Easy") 19 | fun isNumberHappy() { 20 | assertTrue(isNumberHappy(1533)) 21 | assertTrue(isNumberHappy(9009)) 22 | assertFalse(isNumberHappy(3644)) 23 | } 24 | 25 | @Test 26 | @Tag("Easy") 27 | fun queenThreatens() { 28 | assertTrue(queenThreatens(3, 6, 7, 6)) 29 | assertTrue(queenThreatens(8, 1, 1, 8)) 30 | assertFalse(queenThreatens(7, 6, 5, 7)) 31 | } 32 | 33 | @Test 34 | @Tag("Easy") 35 | fun daysInMonth() { 36 | assertEquals(31, daysInMonth(1, 1990)) 37 | assertEquals(28, daysInMonth(2, 1990)) 38 | assertEquals(31, daysInMonth(3, 1990)) 39 | assertEquals(30, daysInMonth(4, 1990)) 40 | assertEquals(31, daysInMonth(5, 1990)) 41 | assertEquals(30, daysInMonth(6, 1990)) 42 | assertEquals(31, daysInMonth(7, 1990)) 43 | assertEquals(31, daysInMonth(8, 1990)) 44 | assertEquals(29, daysInMonth(2, 1992)) 45 | assertEquals(29, daysInMonth(2, 1996)) 46 | assertEquals(28, daysInMonth(2, 1900)) 47 | assertEquals(29, daysInMonth(2, 2000)) 48 | } 49 | 50 | @Test 51 | @Tag("Normal") 52 | fun circleInside() { 53 | assertFalse(circleInside(0.0, 0.0, 6.0, 0.0, 0.0, 5.0)) 54 | assertFalse(circleInside(0.0, 0.0, 1.0, 10.0, 10.0, 9.0)) 55 | assertTrue(circleInside(2.0, 2.0, 2.0, 2.0, 2.0, 2.0)) 56 | assertTrue(circleInside(-2.0, 3.0, 2.0, -2.0, 0.0, 5.0)) 57 | assertFalse(circleInside(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) 58 | } 59 | 60 | @Test 61 | @Tag("Normal") 62 | fun brickPasses() { 63 | assertTrue(brickPasses(2, 10, 5, 6, 3)) 64 | assertTrue(brickPasses(4, 4, 4, 4, 4)) 65 | assertFalse(brickPasses(6, 5, 4, 3, 6)) 66 | assertTrue(brickPasses(3, 2, 1, 1, 2)) 67 | } 68 | } -------------------------------------------------------------------------------- /test/lesson3/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson3.task1 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | import kotlin.math.PI 7 | 8 | class Tests { 9 | @Test 10 | @Tag("Example") 11 | fun factorial() { 12 | assertEquals(1.0, factorial(0), 1e-5) 13 | assertEquals(1.0, factorial(1), 1e-5) 14 | assertEquals(6.0, factorial(3), 1e-5) 15 | assertEquals(120.0, factorial(5), 1e-5) 16 | assertEquals(3628800.0, factorial(10), 1e-5) 17 | assertEquals(2.43290200817664E18, factorial(20), 1E10) 18 | } 19 | 20 | @Test 21 | @Tag("Example") 22 | fun isPrime() { 23 | assertFalse(isPrime(1)) 24 | assertTrue(isPrime(2)) 25 | assertTrue(isPrime(5)) 26 | assertTrue(isPrime(11)) 27 | assertFalse(isPrime(4)) 28 | assertFalse(isPrime(9)) 29 | assertFalse(isPrime(15)) 30 | var count = 0 31 | for (n in 2..7919) { 32 | if (isPrime(n)) { 33 | count++ 34 | } 35 | } 36 | assertEquals(1000, count) 37 | for (n in 2..1000000) { 38 | if (isPrime(n)) { 39 | count++ 40 | } 41 | } 42 | assertEquals(79498, count) 43 | } 44 | 45 | @Test 46 | @Tag("Example") 47 | fun isPerfect() { 48 | assertTrue(isPerfect(6)) 49 | assertTrue(isPerfect(28)) 50 | assertFalse(isPerfect(100)) 51 | } 52 | 53 | @Test 54 | @Tag("Example") 55 | fun digitCountInNumber() { 56 | assertEquals(1, digitCountInNumber(0, 0)) 57 | assertEquals(1, digitCountInNumber(7, 7)) 58 | assertEquals(0, digitCountInNumber(21, 3)) 59 | assertEquals(1, digitCountInNumber(510, 5)) 60 | assertEquals(3, digitCountInNumber(4784041, 4)) 61 | assertEquals(4, digitCountInNumber(5373393, 3)) 62 | } 63 | 64 | @Test 65 | @Tag("Trivial") 66 | fun digitNumber() { 67 | assertEquals(1, digitNumber(0)) 68 | assertEquals(1, digitNumber(7)) 69 | assertEquals(2, digitNumber(10)) 70 | assertEquals(2, digitNumber(99)) 71 | assertEquals(3, digitNumber(123)) 72 | assertEquals(10, digitNumber(Int.MAX_VALUE)) 73 | } 74 | 75 | @Test 76 | @Tag("Easy") 77 | fun fib() { 78 | assertEquals(1, fib(1)) 79 | assertEquals(1, fib(2)) 80 | assertEquals(2, fib(3)) 81 | assertEquals(5, fib(5)) 82 | assertEquals(21, fib(8)) 83 | assertEquals(102334155, fib(40)) 84 | assertEquals(1134903170, fib(45)) 85 | assertEquals(1836311903, fib(46)) 86 | // Just to calculate it 87 | fib(50) 88 | } 89 | 90 | @Test 91 | @Tag("Easy") 92 | fun lcm() { 93 | assertEquals(13, lcm(13, 13)) 94 | assertEquals(8, lcm(2, 8)) 95 | assertEquals(24, lcm(6, 8)) 96 | assertEquals(975, lcm(39, 75)) 97 | assertEquals(13384091, lcm(1357, 9863)) 98 | assertEquals(1339310349, lcm(13579, 98631)) 99 | assertEquals(2089830349, lcm(23579, 88631)) 100 | assertEquals(2022222222, lcm(2, 1011111111)) 101 | assertEquals(2022222222, lcm(1011111111, 2)) 102 | } 103 | 104 | @Test 105 | @Tag("Easy") 106 | fun minDivisor() { 107 | assertEquals(2, minDivisor(2)) 108 | assertEquals(3, minDivisor(75)) 109 | assertEquals(5, minDivisor(75 / 3)) 110 | assertEquals(97, minDivisor(97)) 111 | assertEquals(7, minDivisor(49)) 112 | assertEquals(17, minDivisor(8653)) 113 | assertEquals(2124679 , minDivisor(2124679 )) 114 | assertEquals(1073676287, minDivisor(1073676287)) 115 | assertEquals(Int.MAX_VALUE, minDivisor(Int.MAX_VALUE)) 116 | } 117 | 118 | @Test 119 | @Tag("Easy") 120 | fun maxDivisor() { 121 | assertEquals(1, maxDivisor(17)) 122 | assertEquals(12, maxDivisor(24)) 123 | assertEquals(59, maxDivisor(177)) 124 | assertEquals(17, maxDivisor(34)) 125 | assertEquals(7, maxDivisor(49)) 126 | assertEquals(509, maxDivisor(8653)) 127 | assertEquals(1 , maxDivisor(2124679 )) 128 | assertEquals(1, maxDivisor(1073676287)) 129 | assertEquals(1, maxDivisor(Int.MAX_VALUE)) 130 | } 131 | 132 | @Test 133 | @Tag("Easy") 134 | fun isCoPrime() { 135 | assertTrue(isCoPrime(25, 49)) 136 | assertFalse(isCoPrime(6, 8)) 137 | assertTrue(isCoPrime(17, 97)) 138 | assertFalse(isCoPrime(37, 111)) 139 | assertTrue(isCoPrime(1234567890, 908765431)) 140 | assertTrue(isCoPrime(2109876543, 1234567891)) 141 | } 142 | 143 | @Test 144 | @Tag("Easy") 145 | fun squareBetweenExists() { 146 | assertTrue(squareBetweenExists(1, 1)) 147 | assertTrue(squareBetweenExists(21, 28)) 148 | assertTrue(squareBetweenExists(36, 48)) 149 | assertTrue(squareBetweenExists(50, 64)) 150 | assertFalse(squareBetweenExists(51, 61)) 151 | assertFalse(squareBetweenExists(999, 1001)) 152 | assertTrue(squareBetweenExists(152374337, 152423715)) 153 | assertFalse(squareBetweenExists(2147395601, Int.MAX_VALUE)) 154 | } 155 | 156 | @Test 157 | @Tag("Normal") 158 | fun collatzSteps() { 159 | assertEquals(0, collatzSteps(1)) 160 | assertEquals(1, collatzSteps(2)) 161 | assertEquals(7, collatzSteps(3)) 162 | assertEquals(5, collatzSteps(5)) 163 | assertEquals(6, collatzSteps(10)) 164 | assertEquals(7, collatzSteps(20)) 165 | assertEquals(6, collatzSteps(64)) 166 | assertEquals(25, collatzSteps(100)) 167 | assertEquals(7, collatzSteps(128)) 168 | assertEquals(111, collatzSteps(1000)) 169 | assertEquals(128, collatzSteps(100000)) 170 | } 171 | 172 | @Test 173 | @Tag("Normal") 174 | fun sin() { 175 | assertEquals(0.0, sin(0.0, 1e-5), 1e-5) 176 | assertEquals(1.0, sin(PI / 2.0, 1e-5), 1e-5) 177 | assertEquals(0.0, sin(PI, 1e-5), 1e-5) 178 | assertEquals(-1.0, sin(3.0 * PI / 2.0, 1e-5), 1e-5) 179 | assertEquals(0.0, sin(100 * PI, 1e-5), 1e-5) 180 | } 181 | 182 | @Test 183 | @Tag("Normal") 184 | fun cos() { 185 | assertEquals(1.0, cos(0.0, 1e-5), 1e-5) 186 | assertEquals(0.0, cos(PI / 2.0, 1e-5), 1e-5) 187 | assertEquals(-1.0, cos(PI, 1e-5), 1e-5) 188 | assertEquals(0.0, cos(3.0 * PI / 2.0, 1e-5), 1e-5) 189 | assertEquals(1.0, cos(100 * PI, 1e-5), 1e-5) 190 | } 191 | 192 | @Test 193 | @Tag("Normal") 194 | fun revert() { 195 | assertEquals(87431, revert(13478)) 196 | assertEquals(0, revert(0)) 197 | assertEquals(3, revert(3)) 198 | assertEquals(111, revert(111)) 199 | assertEquals(17571, revert(17571)) 200 | assertEquals(123456789, revert(987654321)) 201 | } 202 | 203 | @Test 204 | @Tag("Normal") 205 | fun isPalindrome() { 206 | assertTrue(isPalindrome(3)) 207 | assertFalse(isPalindrome(3653)) 208 | assertTrue(isPalindrome(15751)) 209 | assertTrue(isPalindrome(24688642)) 210 | } 211 | 212 | @Test 213 | @Tag("Normal") 214 | fun hasDifferentDigits() { 215 | assertTrue(hasDifferentDigits(323)) 216 | assertTrue(hasDifferentDigits(54)) 217 | assertTrue(hasDifferentDigits(222266666)) 218 | assertFalse(hasDifferentDigits(0)) 219 | assertFalse(hasDifferentDigits(777)) 220 | } 221 | 222 | @Test 223 | @Tag("Hard") 224 | fun squareSequenceDigit() { 225 | assertEquals(1, squareSequenceDigit(1)) 226 | assertEquals(4, squareSequenceDigit(2)) 227 | assertEquals(5, squareSequenceDigit(7)) 228 | assertEquals(6, squareSequenceDigit(12)) 229 | assertEquals(0, squareSequenceDigit(17)) 230 | assertEquals(9, squareSequenceDigit(27)) 231 | } 232 | 233 | @Test 234 | @Tag("Hard") 235 | fun fibSequenceDigit() { 236 | assertEquals(1, fibSequenceDigit(1)) 237 | assertEquals(1, fibSequenceDigit(2)) 238 | assertEquals(3, fibSequenceDigit(4)) 239 | assertEquals(2, fibSequenceDigit(9)) 240 | assertEquals(5, fibSequenceDigit(14)) 241 | assertEquals(2, fibSequenceDigit(20)) 242 | } 243 | } -------------------------------------------------------------------------------- /test/lesson4/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson4.task1 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun sqRoots() { 11 | assertEquals(listOf(), sqRoots(-1.0)) 12 | assertArrayEquals(listOf(0.0).toDoubleArray(), sqRoots(0.0).toDoubleArray(), 1e-5) 13 | assertArrayEquals(listOf(-5.0, 5.0).toDoubleArray(), sqRoots(25.0).toDoubleArray(), 1e-5) 14 | } 15 | 16 | @Test 17 | @Tag("Example") 18 | fun biRoots() { 19 | assertEquals(listOf(), biRoots(0.0, 0.0, 1.0)) 20 | assertEquals(listOf(), biRoots(0.0, 1.0, 2.0)) 21 | assertArrayEquals( 22 | listOf(-2.0, 2.0).toDoubleArray(), 23 | biRoots(0.0, 1.0, -4.0).toDoubleArray(), 24 | 1e-5) 25 | assertEquals(listOf(), biRoots(1.0, -2.0, 4.0)) 26 | assertArrayEquals( 27 | listOf(-1.0, 1.0).toDoubleArray(), 28 | biRoots(1.0, -2.0, 1.0).toDoubleArray(), 29 | 1e-5) 30 | assertEquals(listOf(), biRoots(1.0, 3.0, 2.0)) 31 | assertArrayEquals( 32 | listOf(-2.0, -1.0, 1.0, 2.0).toDoubleArray(), 33 | biRoots(1.0, -5.0, 4.0).sorted().toDoubleArray(), 34 | 1e-5) 35 | } 36 | 37 | @Test 38 | @Tag("Example") 39 | fun negativeList() { 40 | assertEquals(listOf(), negativeList(listOf(1, 2, 3))) 41 | assertEquals(listOf(-1, -5), negativeList(listOf(-1, 2, 4, -5))) 42 | } 43 | 44 | @Test 45 | @Tag("Example") 46 | fun invertPositives() { 47 | val list1 = mutableListOf(1, 2, 3) 48 | invertPositives(list1) 49 | assertEquals(listOf(-1, -2, -3), list1) 50 | val list2 = mutableListOf(-1, 2, 4, -5) 51 | invertPositives(list2) 52 | assertEquals(listOf(-1, -2, -4, -5), list2) 53 | } 54 | 55 | @Test 56 | @Tag("Example") 57 | fun squares() { 58 | assertEquals(listOf(0), squares(listOf(0))) 59 | assertEquals(listOf(1, 4, 9), squares(listOf(1, 2, -3))) 60 | } 61 | 62 | @Test 63 | @Tag("Example") 64 | fun squaresVararg() { 65 | assertArrayEquals(arrayOf(0), squares(0)) 66 | assertArrayEquals(arrayOf(1, 4, 9), squares(1, 2, -3)) 67 | } 68 | 69 | @Test 70 | @Tag("Example") 71 | fun isPalindrome() { 72 | assertFalse(isPalindrome("Барабан")) 73 | assertTrue(isPalindrome("А роза упала на лапу Азора")) 74 | assertTrue(isPalindrome("Шалаш")) 75 | } 76 | 77 | @Test 78 | @Tag("Example") 79 | fun buildSumExample() { 80 | assertEquals("42 = 42", buildSumExample(listOf(42))) 81 | assertEquals("3 + 6 + 5 + 4 + 9 = 27", buildSumExample(listOf(3, 6, 5, 4, 9))) 82 | } 83 | 84 | @Test 85 | @Tag("Easy") 86 | fun abs() { 87 | assertEquals(0.0, abs(listOf()), 1e-5) 88 | assertEquals(3.0, abs(listOf(3.0)), 1e-5) 89 | assertEquals(5.0, abs(listOf(3.0, -4.0)), 1e-5) 90 | assertEquals(8.774964, abs(listOf(4.0, -5.0, 6.0)), 1e-5) 91 | } 92 | 93 | @Test 94 | @Tag("Easy") 95 | fun mean() { 96 | assertEquals(0.0, mean(listOf()), 1e-5) 97 | assertEquals(1.0, mean(listOf(1.0)), 1e-5) 98 | assertEquals(2.0, mean(listOf(3.0, 1.0, 2.0)), 1e-5) 99 | assertEquals(3.0, mean(listOf(0.0, 2.0, 7.0, 8.0, -2.0)), 1e-5) 100 | } 101 | 102 | @Test 103 | @Tag("Normal") 104 | fun center() { 105 | assertEquals(listOf(), center(mutableListOf())) 106 | assertArrayEquals( 107 | listOf(0.0).toDoubleArray(), 108 | center(mutableListOf(3.14)).toDoubleArray(), 109 | 1e-5) 110 | assertArrayEquals( 111 | listOf(1.0, -1.0, 0.0).toDoubleArray(), 112 | center(mutableListOf(3.0, 1.0, 2.0)).toDoubleArray(), 113 | 1e-5) 114 | assertArrayEquals( 115 | listOf(-3.0, -1.0, 4.0, 5.0, -5.0).toDoubleArray(), 116 | center(mutableListOf(0.0, 2.0, 7.0, 8.0, -2.0)).toDoubleArray(), 117 | 1e-5) 118 | val toMutate = mutableListOf(-3.0, -1.0, 4.0, 5.0, -5.0) 119 | assertTrue(toMutate === center(toMutate)) { "You should mutate an input list, not create a copy" } 120 | } 121 | 122 | @Test 123 | @Tag("Normal") 124 | fun times() { 125 | assertEquals(0.0, times(listOf(), listOf()), 1e-5) 126 | assertEquals(-5.0, times(listOf(1.0, -4.0), listOf(3.0, 2.0)), 1e-5) 127 | assertEquals(-19.0, times(listOf(-1.0, 2.0, -3.0), listOf(3.0, -2.0, 4.0)), 1e-5) 128 | } 129 | 130 | @Test 131 | @Tag("Normal") 132 | fun polynom() { 133 | assertEquals(0.0, polynom(listOf(), 1000.0), 1e-5) 134 | assertEquals(42.0, polynom(listOf(42.0), -1000.0), 1e-5) 135 | assertEquals(13.0, polynom(listOf(3.0, 2.0), 5.0), 1e-5) 136 | assertEquals(0.0, polynom(listOf(2.0, -3.0, 1.0), 1.0), 1e-5) 137 | assertEquals(45.0, polynom(listOf(-7.0, 6.0, 4.0, -4.0, 1.0), -2.0), 1e-5) 138 | } 139 | 140 | @Test 141 | @Tag("Normal") 142 | fun accumulate() { 143 | assertEquals(listOf(), accumulate(arrayListOf())) 144 | assertArrayEquals( 145 | listOf(3.14).toDoubleArray(), 146 | accumulate(arrayListOf(3.14)).toDoubleArray(), 147 | 1e-5) 148 | assertArrayEquals( 149 | listOf(1.0, 3.0, 6.0, 10.0).toDoubleArray(), 150 | accumulate(arrayListOf(1.0, 2.0, 3.0, 4.0)).toDoubleArray(), 151 | 1e-5) 152 | val toMutate = mutableListOf(-3.0, -1.0, 4.0, 5.0, -5.0) 153 | assertTrue(toMutate === accumulate(toMutate)) { "You should mutate an input list, not create a copy" } 154 | } 155 | 156 | @Test 157 | @Tag("Normal") 158 | fun factorize() { 159 | assertEquals(listOf(2), factorize(2)) 160 | assertEquals(listOf(3, 5, 5), factorize(75)) 161 | assertEquals(listOf(2, 3, 3, 19), factorize(342)) 162 | } 163 | 164 | @Test 165 | @Tag("Hard") 166 | fun factorizeToString() { 167 | assertEquals("2", factorizeToString(2)) 168 | assertEquals("3*5*5", factorizeToString(75)) 169 | assertEquals("2*3*3*19", factorizeToString(342)) 170 | assertEquals("7*7*31*31*151*151", factorizeToString(1073676289)) 171 | assertEquals("1073676287", factorizeToString(1073676287)) 172 | assertEquals(Int.MAX_VALUE.toString(), factorizeToString(Int.MAX_VALUE)) 173 | } 174 | 175 | @Test 176 | @Tag("Normal") 177 | fun convert() { 178 | assertEquals(listOf(1), convert(1, 2)) 179 | assertEquals(listOf(1, 2, 1, 0), convert(100, 4)) 180 | assertEquals(listOf(1, 3, 12), convert(250, 14)) 181 | assertEquals(listOf(2, 14, 12), convert(1000, 19)) 182 | } 183 | 184 | @Test 185 | @Tag("Hard") 186 | fun convertToString() { 187 | assertEquals("1", convertToString(1, 2)) 188 | assertEquals("1210", convertToString(100, 4)) 189 | assertEquals("13c", convertToString(250, 14)) 190 | assertEquals("2ec", convertToString(1000, 19)) 191 | assertEquals("z", convertToString(35, 36)) 192 | assertEquals("a02220281", convertToString(Int.MAX_VALUE, 11)) 193 | } 194 | 195 | @Test 196 | @Tag("Normal") 197 | fun decimal() { 198 | assertEquals(1, decimal(listOf(1), 2)) 199 | assertEquals(100, decimal(listOf(1, 2, 1, 0), 4)) 200 | assertEquals(250, decimal(listOf(1, 3, 12), 14)) 201 | assertEquals(1000, decimal(listOf(2, 14, 12), 19)) 202 | } 203 | 204 | @Test 205 | @Tag("Hard") 206 | fun decimalFromString() { 207 | assertEquals(1, decimalFromString("1", 2)) 208 | assertEquals(100, decimalFromString("1210", 4)) 209 | assertEquals(250, decimalFromString("13c", 14)) 210 | assertEquals(1000, decimalFromString("2ec", 19)) 211 | assertEquals(35, decimalFromString("z", 36)) 212 | assertEquals(Int.MAX_VALUE, decimalFromString("a02220281", 11)) 213 | } 214 | 215 | @Test 216 | @Tag("Hard") 217 | fun roman() { 218 | assertEquals("I", roman(1)) 219 | assertEquals("MMM", roman(3000)) 220 | assertEquals("MCMLXXVIII", roman(1978)) 221 | assertEquals("DCXCIV", roman(694)) 222 | assertEquals("XLIX", roman(49)) 223 | } 224 | 225 | @Test 226 | @Tag("Impossible") 227 | fun russian() { 228 | assertEquals("триста семьдесят пять", russian(375)) 229 | assertEquals("двадцать две тысячи девятьсот шестьдесят четыре", russian(22964)) 230 | assertEquals("сто девятнадцать тысяч пятьсот восемь", russian(119508)) 231 | assertEquals("две тысячи три", russian(2003)) 232 | assertEquals("двести тысяч два", russian(200002)) 233 | assertEquals("девятьсот тысяч", russian(900000)) 234 | assertEquals("двенадцать", russian(12)) 235 | } 236 | } -------------------------------------------------------------------------------- /test/lesson5/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson5.task1 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | 7 | class Tests { 8 | @Test 9 | @Tag("Example") 10 | fun shoppingListCostTest() { 11 | val itemCosts = mapOf( 12 | "Хлеб" to 50.0, 13 | "Молоко" to 100.0 14 | ) 15 | assertEquals( 16 | 150.0, 17 | shoppingListCost( 18 | listOf("Хлеб", "Молоко"), 19 | itemCosts 20 | ) 21 | ) 22 | assertEquals( 23 | 150.0, 24 | shoppingListCost( 25 | listOf("Хлеб", "Молоко", "Кефир"), 26 | itemCosts 27 | ) 28 | ) 29 | assertEquals( 30 | 0.0, 31 | shoppingListCost( 32 | listOf("Хлеб", "Молоко", "Кефир"), 33 | mapOf() 34 | ) 35 | ) 36 | } 37 | 38 | @Test 39 | @Tag("Example") 40 | fun filterByCountryCode() { 41 | val phoneBook = mutableMapOf( 42 | "Quagmire" to "+1-800-555-0143", 43 | "Adam's Ribs" to "+82-000-555-2960", 44 | "Pharmakon Industries" to "+1-800-555-6321" 45 | ) 46 | 47 | filterByCountryCode(phoneBook, "+1") 48 | assertEquals(2, phoneBook.size) 49 | 50 | filterByCountryCode(phoneBook, "+1") 51 | assertEquals(2, phoneBook.size) 52 | 53 | filterByCountryCode(phoneBook, "+999") 54 | assertEquals(0, phoneBook.size) 55 | } 56 | 57 | @Test 58 | @Tag("Example") 59 | fun removeFillerWords() { 60 | assertEquals( 61 | "Я люблю Котлин".split(" "), 62 | removeFillerWords( 63 | "Я как-то люблю Котлин".split(" "), 64 | "как-то" 65 | ) 66 | ) 67 | assertEquals( 68 | "Я люблю Котлин".split(" "), 69 | removeFillerWords( 70 | "Я как-то люблю таки Котлин".split(" "), 71 | "как-то", 72 | "таки" 73 | ) 74 | ) 75 | assertEquals( 76 | "Я люблю Котлин".split(" "), 77 | removeFillerWords( 78 | "Я люблю Котлин".split(" "), 79 | "как-то", 80 | "таки" 81 | ) 82 | ) 83 | } 84 | 85 | @Test 86 | @Tag("Example") 87 | fun buildWordSet() { 88 | assertEquals( 89 | buildWordSet("Я люблю Котлин".split(" ")), 90 | mutableSetOf("Я", "люблю", "Котлин") 91 | ) 92 | assertEquals( 93 | buildWordSet("Я люблю люблю Котлин".split(" ")), 94 | mutableSetOf("Котлин", "люблю", "Я") 95 | ) 96 | assertEquals( 97 | buildWordSet(listOf()), 98 | mutableSetOf() 99 | ) 100 | } 101 | 102 | @Test 103 | @Tag("Normal") 104 | fun mergePhoneBooks() { 105 | assertEquals( 106 | mapOf("Emergency" to "112"), 107 | mergePhoneBooks( 108 | mapOf("Emergency" to "112"), 109 | mapOf("Emergency" to "112") 110 | ) 111 | ) 112 | assertEquals( 113 | mapOf("Emergency" to "112", "Police" to "02"), 114 | mergePhoneBooks( 115 | mapOf("Emergency" to "112"), 116 | mapOf("Emergency" to "112", "Police" to "02") 117 | ) 118 | ) 119 | assertEquals( 120 | mapOf("Emergency" to "112, 911", "Police" to "02"), 121 | mergePhoneBooks( 122 | mapOf("Emergency" to "112"), 123 | mapOf("Emergency" to "911", "Police" to "02") 124 | ) 125 | ) 126 | assertEquals( 127 | mapOf("Emergency" to "112, 911", "Fire department" to "01", "Police" to "02"), 128 | mergePhoneBooks( 129 | mapOf("Emergency" to "112", "Fire department" to "01"), 130 | mapOf("Emergency" to "911", "Police" to "02") 131 | ) 132 | ) 133 | } 134 | 135 | @Test 136 | @Tag("Easy") 137 | fun buildGrades() { 138 | assertEquals( 139 | mapOf>(), 140 | buildGrades(mapOf()) 141 | ) 142 | // TODO: Sort the values here or let the students do it? 143 | assertEquals( 144 | mapOf(5 to listOf("Семён", "Михаил"), 3 to listOf("Марат")), 145 | buildGrades(mapOf("Марат" to 3, "Семён" to 5, "Михаил" to 5)) 146 | ) 147 | assertEquals( 148 | mapOf(3 to listOf("Семён", "Михаил", "Марат")), 149 | buildGrades(mapOf("Марат" to 3, "Семён" to 3, "Михаил" to 3)) 150 | ) 151 | } 152 | 153 | @Test 154 | @Tag("Easy") 155 | fun containsIn() { 156 | assertTrue(containsIn(mapOf("a" to "z"), mapOf("a" to "z", "b" to "sweet"))) 157 | assertFalse(containsIn(mapOf("a" to "z"), mapOf("a" to "zee", "b" to "sweet"))) 158 | } 159 | 160 | @Test 161 | @Tag("Normal") 162 | fun averageStockPrice() { 163 | assertEquals( 164 | mapOf(), 165 | averageStockPrice(listOf()) 166 | ) 167 | assertEquals( 168 | mapOf("MSFT" to 100.0, "NFLX" to 40.0), 169 | averageStockPrice(listOf("MSFT" to 100.0, "NFLX" to 40.0)) 170 | ) 171 | assertEquals( 172 | mapOf("MSFT" to 150.0, "NFLX" to 40.0), 173 | averageStockPrice(listOf("MSFT" to 100.0, "MSFT" to 200.0, "NFLX" to 40.0)) 174 | ) 175 | assertEquals( 176 | mapOf("MSFT" to 150.0, "NFLX" to 45.0), 177 | averageStockPrice(listOf("MSFT" to 100.0, "MSFT" to 200.0, "NFLX" to 40.0, "NFLX" to 50.0)) 178 | ) 179 | } 180 | 181 | @Test 182 | @Tag("Normal") 183 | fun findCheapestStuff() { 184 | assertNull( 185 | findCheapestStuff( 186 | mapOf("Мария" to ("печенье" to 20.0), "Орео" to ("печенье" to 100.0)), 187 | "торт" 188 | ) 189 | ) 190 | assertEquals( 191 | "Мария", 192 | findCheapestStuff( 193 | mapOf("Мария" to ("печенье" to 20.0), "Орео" to ("печенье" to 100.0)), 194 | "печенье" 195 | ) 196 | ) 197 | } 198 | 199 | @Test 200 | @Tag("Hard") 201 | fun propagateHandshakes() { 202 | assertEquals( 203 | mapOf( 204 | "Marat" to setOf("Mikhail", "Sveta"), 205 | "Sveta" to setOf("Mikhail"), 206 | "Mikhail" to setOf() 207 | ), 208 | propagateHandshakes( 209 | mapOf( 210 | "Marat" to setOf("Sveta"), 211 | "Sveta" to setOf("Mikhail") 212 | ) 213 | ) 214 | ) 215 | assertEquals( 216 | mapOf( 217 | "Marat" to setOf("Mikhail", "Sveta"), 218 | "Sveta" to setOf("Marat", "Mikhail"), 219 | "Mikhail" to setOf("Sveta", "Marat") 220 | ), 221 | propagateHandshakes( 222 | mapOf( 223 | "Marat" to setOf("Mikhail", "Sveta"), 224 | "Sveta" to setOf("Marat"), 225 | "Mikhail" to setOf("Sveta") 226 | ) 227 | ) 228 | ) 229 | } 230 | 231 | @Test 232 | @Tag("Easy") 233 | fun subtractOf() { 234 | val from = mutableMapOf("a" to "z", "b" to "c") 235 | 236 | subtractOf(from, mapOf()) 237 | assertEquals(from, mapOf("a" to "z", "b" to "c")) 238 | 239 | subtractOf(from, mapOf("b" to "z")) 240 | assertEquals(from, mapOf("a" to "z", "b" to "c")) 241 | 242 | subtractOf(from, mapOf("a" to "z")) 243 | assertEquals(from, mapOf("b" to "c")) 244 | } 245 | 246 | @Test 247 | @Tag("Easy") 248 | fun whoAreInBoth() { 249 | assertEquals( 250 | emptyList(), 251 | whoAreInBoth(emptyList(), emptyList()) 252 | ) 253 | assertEquals( 254 | listOf("Marat"), 255 | whoAreInBoth(listOf("Marat", "Mikhail"), listOf("Marat", "Kirill")) 256 | ) 257 | assertEquals( 258 | emptyList(), 259 | whoAreInBoth(listOf("Marat", "Mikhail"), listOf("Sveta", "Kirill")) 260 | ) 261 | } 262 | 263 | @Test 264 | @Tag("Normal") 265 | fun canBuildFrom() { 266 | assertFalse(canBuildFrom(emptyList(), "foo")) 267 | assertTrue(canBuildFrom(listOf('a', 'b', 'o'), "baobab")) 268 | assertFalse(canBuildFrom(listOf('a', 'm', 'r'), "Marat")) 269 | } 270 | 271 | @Test 272 | @Tag("Normal") 273 | fun extractRepeats() { 274 | assertEquals( 275 | emptyMap(), 276 | extractRepeats(emptyList()) 277 | ) 278 | assertEquals( 279 | mapOf("a" to 2), 280 | extractRepeats(listOf("a", "b", "a")) 281 | ) 282 | assertEquals( 283 | emptyMap(), 284 | extractRepeats(listOf("a", "b", "c")) 285 | ) 286 | } 287 | 288 | @Test 289 | @Tag("Normal") 290 | fun hasAnagrams() { 291 | assertFalse(hasAnagrams(emptyList())) 292 | assertTrue(hasAnagrams(listOf("рот", "свет", "тор"))) 293 | assertFalse(hasAnagrams(listOf("рот", "свет", "код", "дверь"))) 294 | } 295 | 296 | @Test 297 | @Tag("Hard") 298 | fun findSumOfTwo() { 299 | assertEquals( 300 | Pair(-1, -1), 301 | findSumOfTwo(emptyList(), 1) 302 | ) 303 | assertEquals( 304 | Pair(0, 2), 305 | findSumOfTwo(listOf(1, 2, 3), 4) 306 | ) 307 | assertEquals( 308 | Pair(-1, -1), 309 | findSumOfTwo(listOf(1, 2, 3), 6) 310 | ) 311 | } 312 | 313 | @Test 314 | @Tag("Impossible") 315 | fun bagPacking() { 316 | assertEquals( 317 | setOf("Кубок"), 318 | bagPacking( 319 | mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)), 320 | 850 321 | ) 322 | ) 323 | assertEquals( 324 | emptySet(), 325 | bagPacking( 326 | mapOf("Кубок" to (500 to 2000), "Слиток" to (1000 to 5000)), 327 | 450 328 | ) 329 | ) 330 | } 331 | 332 | // TODO: map task tests 333 | } 334 | -------------------------------------------------------------------------------- /test/lesson6/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson6.task1 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Assertions.assertThrows 5 | import org.junit.jupiter.api.Tag 6 | import org.junit.jupiter.api.Test 7 | 8 | class Tests { 9 | @Test 10 | @Tag("Example") 11 | fun timeStrToSeconds() { 12 | assertEquals(36000, timeStrToSeconds("10:00:00")) 13 | assertEquals(41685, timeStrToSeconds("11:34:45")) 14 | assertEquals(86399, timeStrToSeconds("23:59:59")) 15 | } 16 | 17 | @Test 18 | @Tag("Example") 19 | fun twoDigitStr() { 20 | assertEquals("00", twoDigitStr(0)) 21 | assertEquals("09", twoDigitStr(9)) 22 | assertEquals("10", twoDigitStr(10)) 23 | assertEquals("99", twoDigitStr(99)) 24 | } 25 | 26 | @Test 27 | @Tag("Example") 28 | fun timeSecondsToStr() { 29 | assertEquals("10:00:00", timeSecondsToStr(36000)) 30 | assertEquals("11:34:45", timeSecondsToStr(41685)) 31 | assertEquals("23:59:59", timeSecondsToStr(86399)) 32 | } 33 | 34 | @Test 35 | @Tag("Normal") 36 | fun dateStrToDigit() { 37 | assertEquals("15.07.2016", dateStrToDigit("15 июля 2016")) 38 | assertEquals("", dateStrToDigit("3 мартобря 1918")) 39 | assertEquals("18.11.2018", dateStrToDigit("18 ноября 2018")) 40 | assertEquals("", dateStrToDigit("23")) 41 | assertEquals("03.04.2011", dateStrToDigit("3 апреля 2011")) 42 | assertEquals("", dateStrToDigit("32 сентября 2011")) 43 | assertEquals("", dateStrToDigit("29 февраля 1993")) 44 | } 45 | 46 | @Test 47 | @Tag("Normal") 48 | fun dateDigitToStr() { 49 | assertEquals("15 июля 2016", dateDigitToStr("15.07.2016")) 50 | assertEquals("", dateDigitToStr("01.02.20.19")) 51 | assertEquals("", dateDigitToStr("28.00.2000")) 52 | assertEquals("3 апреля 2011", dateDigitToStr("03.04.2011")) 53 | assertEquals("", dateDigitToStr("ab.cd.ef")) 54 | assertEquals("", dateDigitToStr("32.09.2011")) 55 | assertEquals("", dateDigitToStr("29.02.1993")) 56 | } 57 | 58 | @Test 59 | @Tag("Normal") 60 | fun flattenPhoneNumber() { 61 | assertEquals("+79211234567", flattenPhoneNumber("+7 (921) 123-45-67")) 62 | assertEquals("123456798", flattenPhoneNumber("12 -- 34- 5 -- 67 -98")) 63 | assertEquals("", flattenPhoneNumber("ab-123")) 64 | assertEquals("+12345", flattenPhoneNumber("+12 (3) 4-5")) 65 | assertEquals("", flattenPhoneNumber("134_+874")) 66 | } 67 | 68 | @Test 69 | @Tag("Normal") 70 | fun bestLongJump() { 71 | assertEquals(717, bestLongJump("706 % - 717 - 703")) 72 | assertEquals(-1, bestLongJump("% - - % -")) 73 | assertEquals(754, bestLongJump("700 717 707 % 754")) 74 | assertEquals(-1, bestLongJump("700 + 700")) 75 | 76 | } 77 | 78 | @Test 79 | @Tag("Hard") 80 | fun bestHighJump() { 81 | assertEquals(226, bestHighJump("226 +")) 82 | assertEquals(-1, bestHighJump("???")) 83 | assertEquals(230, bestHighJump("220 + 224 %+ 228 %- 230 + 232 %%- 234 %")) 84 | } 85 | 86 | @Test 87 | @Tag("Hard") 88 | fun plusMinus() { 89 | assertEquals(0, plusMinus("0")) 90 | assertEquals(4, plusMinus("2 + 2")) 91 | assertEquals(6, plusMinus("2 + 31 - 40 + 13")) 92 | assertEquals(-1, plusMinus("0 - 1")) 93 | assertThrows(IllegalArgumentException::class.java) { plusMinus("+2") } 94 | assertThrows(IllegalArgumentException::class.java) { plusMinus("+ 4") } 95 | assertThrows(IllegalArgumentException::class.java) { plusMinus("4 - -2") } 96 | assertThrows(IllegalArgumentException::class.java) { plusMinus("44 - - 12") } 97 | assertThrows(IllegalArgumentException::class.java) { plusMinus("4 - + 12") } 98 | } 99 | 100 | @Test 101 | @Tag("Hard") 102 | fun firstDuplicateIndex() { 103 | assertEquals(-1, firstDuplicateIndex("Привет")) 104 | assertEquals(9, firstDuplicateIndex("Он пошёл в в школу")) 105 | assertEquals(40, firstDuplicateIndex("Яблоко упало на ветку с ветки оно упало на на землю")) 106 | assertEquals(9, firstDuplicateIndex("Мы пошли прямо Прямо располагался магазин")) 107 | } 108 | 109 | @Test 110 | @Tag("Hard") 111 | fun mostExpensive() { 112 | assertEquals("", mostExpensive("")) 113 | assertEquals("Курица", mostExpensive("Хлеб 39.9; Молоко 62.5; Курица 184.0; Конфеты 89.9")) 114 | assertEquals("Вино", mostExpensive("Вино 255.0")) 115 | } 116 | 117 | @Test 118 | @Tag("Hard") 119 | fun fromRoman() { 120 | assertEquals(1, fromRoman("I")) 121 | assertEquals(3000, fromRoman("MMM")) 122 | assertEquals(1978, fromRoman("MCMLXXVIII")) 123 | assertEquals(694, fromRoman("DCXCIV")) 124 | assertEquals(49, fromRoman("XLIX")) 125 | assertEquals(-1, fromRoman("Z")) 126 | } 127 | 128 | @Test 129 | @Tag("Impossible") 130 | fun computeDeviceCells() { 131 | assertEquals(listOf(0, 0, 0, 0, 0, 1, 1, 1, 1, 1), computeDeviceCells(10, "+>+>+>+>+", 10000)) 132 | assertEquals(listOf(-1, -1, -1, -1, -1, 0, 0, 0, 0, 0), computeDeviceCells(10, "<-<-<-<-<-", 10000)) 133 | assertEquals(listOf(1, 1, 1, 1, 1, 0, 0, 0, 0, 0), computeDeviceCells(10, "- <<<<< +[>+]", 10000)) 134 | assertEquals(listOf(0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0), 135 | computeDeviceCells(11, "<<<<< + >>>>>>>>>> --[<-] >+[>+] >++[--< <[<] >+[>+] >++]", 10000)) 136 | 137 | assertEquals(listOf(0, 0, 0, 0, 0, 1, 1, 0, 0, 0), computeDeviceCells(10, "+>+>+>+>+", 4)) 138 | assertEquals(listOf(0, 0, -1, -1, -1, 0, 0, 0, 0, 0), computeDeviceCells(10, "<-<-<-<-<-", 6)) 139 | assertEquals(listOf(1, 1, 1, 0, 0, -1, 0, 0, 0, 0), computeDeviceCells(10, "- <<<<< +[>+]", 17)) 140 | assertEquals(listOf(0, 6, 5, 4, 3, 2, 1, 0, -1, -1, -2), 141 | computeDeviceCells(11, "<<<<< + >>>>>>>>>> --[<-] >+[>+] >++[--< <[<] >+[>+] >++]", 256)) 142 | assertThrows(IllegalArgumentException::class.java) { computeDeviceCells(10, "===", 3) } 143 | assertThrows(IllegalArgumentException::class.java) { computeDeviceCells(10, "+>+>[+>", 3) } 144 | assertThrows(IllegalStateException::class.java) { computeDeviceCells(20, ">>>>>>>>>>>>>", 12) } 145 | } 146 | } -------------------------------------------------------------------------------- /test/lesson8/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson8.task1 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | import java.lang.Math.ulp 7 | import kotlin.math.PI 8 | import kotlin.math.abs 9 | import kotlin.math.sqrt 10 | 11 | class Tests { 12 | @Test 13 | @Tag("Example") 14 | fun pointDistance() { 15 | assertEquals(0.0, Point(0.0, 0.0).distance(Point(0.0, 0.0)), 1e-5) 16 | assertEquals(5.0, Point(3.0, 0.0).distance(Point(0.0, 4.0)), 1e-5) 17 | assertEquals(50.0, Point(0.0, -30.0).distance(Point(-40.0, 0.0)), 1e-5) 18 | } 19 | 20 | @Test 21 | @Tag("Example") 22 | fun halfPerimeter() { 23 | assertEquals(6.0, Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).halfPerimeter(), 1e-5) 24 | assertEquals(2.0, Triangle(Point(0.0, 0.0), Point(0.0, 1.0), Point(0.0, 2.0)).halfPerimeter(), 1e-5) 25 | } 26 | 27 | @Test 28 | @Tag("Example") 29 | fun triangleArea() { 30 | assertEquals(6.0, Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).area(), 1e-5) 31 | assertEquals(0.0, Triangle(Point(0.0, 0.0), Point(0.0, 1.0), Point(0.0, 2.0)).area(), 1e-5) 32 | } 33 | 34 | @Test 35 | @Tag("Example") 36 | fun triangleContains() { 37 | assertTrue(Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).contains(Point(1.5, 1.5))) 38 | assertFalse(Triangle(Point(0.0, 0.0), Point(0.0, 3.0), Point(4.0, 0.0)).contains(Point(2.5, 2.5))) 39 | } 40 | 41 | @Test 42 | @Tag("Example") 43 | fun segmentEquals() { 44 | val first = Segment(Point(1.0, 2.0), Point(3.0, 4.0)) 45 | val second = Segment(Point(1.0, 2.0), Point(3.0, 4.0)) 46 | val third = Segment(Point(3.0, 4.0), Point(1.0, 2.0)) 47 | assertEquals(first, second) 48 | assertEquals(second, third) 49 | assertEquals(third, first) 50 | } 51 | 52 | private fun approxEquals(expected: Line, actual: Line, delta: Double): Boolean = 53 | abs(expected.angle - actual.angle) <= delta && 54 | abs(expected.b - actual.b) <= delta 55 | 56 | private fun assertApproxEquals(expected: Line, actual: Line, delta: Double = ulp(10.0)) { 57 | assertTrue(approxEquals(expected, actual, delta)) 58 | } 59 | 60 | private fun assertApproxNotEquals(expected: Line, actual: Line, delta: Double = ulp(10.0)) { 61 | assertFalse(approxEquals(expected, actual, delta)) 62 | } 63 | 64 | @Test 65 | @Tag("Example") 66 | fun lineEquals() { 67 | run { 68 | val first = Line(Point(0.0, 0.0), 0.0) 69 | val second = Line(Point(3.0, 0.0), 0.0) 70 | val third = Line(Point(-5.0, 0.0), 0.0) 71 | val fourth = Line(Point(3.0, 1.0), 0.0) 72 | assertApproxEquals(first, second) 73 | assertApproxEquals(second, third) 74 | assertApproxEquals(third, first) 75 | assertApproxNotEquals(fourth, first) 76 | } 77 | run { 78 | val first = Line(Point(0.0, 0.0), PI / 2) 79 | val second = Line(Point(0.0, 3.0), PI / 2) 80 | val third = Line(Point(0.0, -5.0), PI / 2) 81 | val fourth = Line(Point(1.0, 3.0), PI / 2) 82 | assertApproxEquals(first, second) 83 | assertApproxEquals(second, third) 84 | assertApproxEquals(third, first) 85 | assertApproxNotEquals(fourth, first) 86 | } 87 | run { 88 | val first = Line(Point(0.0, 0.0), PI / 4) 89 | val second = Line(Point(3.0, 3.0), PI / 4) 90 | val third = Line(Point(-5.0, -5.0), PI / 4) 91 | val fourth = Line(Point(3.00001, 3.0), PI / 4) 92 | assertApproxEquals(first, second) 93 | assertApproxEquals(second, third) 94 | assertApproxEquals(third, first) 95 | assertApproxNotEquals(fourth, first) 96 | } 97 | } 98 | 99 | @Test 100 | @Tag("Example") 101 | fun triangleEquals() { 102 | val first = Triangle(Point(0.0, 0.0), Point(3.0, 0.0), Point(0.0, 4.0)) 103 | val second = Triangle(Point(0.0, 0.0), Point(0.0, 4.0), Point(3.0, 0.0)) 104 | val third = Triangle(Point(0.0, 4.0), Point(0.0, 0.0), Point(3.0, 0.0)) 105 | val fourth = Triangle(Point(0.0, 4.0), Point(0.0, 3.0), Point(3.0, 0.0)) 106 | assertEquals(first, second) 107 | assertEquals(second, third) 108 | assertEquals(third, first) 109 | assertNotEquals(fourth, first) 110 | } 111 | 112 | @Test 113 | @Tag("Easy") 114 | fun circleDistance() { 115 | assertEquals(0.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(1.0, 0.0), 1.0)), 1e-5) 116 | assertEquals(0.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(0.0, 2.0), 1.0)), 1e-5) 117 | assertEquals(1.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(-4.0, 0.0), 2.0)), 1e-5) 118 | assertEquals(2.0 * sqrt(2.0) - 2.0, Circle(Point(0.0, 0.0), 1.0).distance(Circle(Point(2.0, 2.0), 1.0)), 1e-5) 119 | } 120 | 121 | @Test 122 | @Tag("Trivial") 123 | fun circleContains() { 124 | val center = Point(1.0, 2.0) 125 | assertTrue(Circle(center, 1.0).contains(center)) 126 | assertFalse(Circle(center, 2.0).contains(Point(0.0, 0.0))) 127 | assertTrue(Circle(Point(0.0, 3.0), 5.01).contains(Point(-4.0, 0.0))) 128 | } 129 | 130 | @Test 131 | @Tag("Normal") 132 | fun diameter() { 133 | val p1 = Point(0.0, 0.0) 134 | val p2 = Point(1.0, 4.0) 135 | val p3 = Point(-2.0, 2.0) 136 | val p4 = Point(3.0, -1.0) 137 | val p5 = Point(-3.0, -2.0) 138 | val p6 = Point(0.0, 5.0) 139 | assertEquals(Segment(p5, p6), diameter(p1, p2, p3, p4, p5, p6)) 140 | assertEquals(Segment(p4, p6), diameter(p1, p2, p3, p4, p6)) 141 | assertEquals(Segment(p3, p4), diameter(p1, p2, p3, p4)) 142 | assertEquals(Segment(p2, p4), diameter(p1, p2, p4)) 143 | assertEquals(Segment(p1, p4), diameter(p1, p4)) 144 | } 145 | 146 | @Test 147 | @Tag("Easy") 148 | fun circleByDiameter() { 149 | assertEquals(Circle(Point(0.0, 1.0), 1.0), circleByDiameter(Segment(Point(0.0, 0.0), Point(0.0, 2.0)))) 150 | assertEquals(Circle(Point(2.0, 1.5), 2.5), circleByDiameter(Segment(Point(4.0, 0.0), Point(0.0, 3.0)))) 151 | } 152 | 153 | @Test 154 | @Tag("Normal") 155 | fun crossPoint() { 156 | assertTrue(Point(2.0, 3.0).distance(Line(Point(2.0, 0.0), PI / 2).crossPoint(Line(Point(0.0, 3.0), 0.0))) < 1e-5) 157 | assertTrue(Point(2.0, 2.0).distance(Line(Point(0.0, 0.0), PI / 4).crossPoint(Line(Point(0.0, 4.0), 3 * PI / 4))) < 1e-5) 158 | val p = Point(1.0, 3.0) 159 | assertTrue(p.distance(Line(p, 1.0).crossPoint(Line(p, 2.0))) < 1e-5) 160 | } 161 | 162 | @Test 163 | @Tag("Normal") 164 | fun lineBySegment() { 165 | assertApproxEquals(Line(Point(0.0, 0.0), 0.0), lineBySegment(Segment(Point(0.0, 0.0), Point(7.0, 0.0)))) 166 | assertApproxEquals(Line(Point(0.0, 0.0), PI / 2), lineBySegment(Segment(Point(0.0, 0.0), Point(0.0, 8.0)))) 167 | assertApproxEquals(Line(Point(1.0, 1.0), PI / 4), lineBySegment(Segment(Point(1.0, 1.0), Point(3.0, 3.0)))) 168 | } 169 | 170 | @Test 171 | @Tag("Normal") 172 | fun lineByPoint() { 173 | assertApproxEquals(Line(Point(0.0, 0.0), PI / 2), lineByPoints(Point(0.0, 0.0), Point(0.0, 2.0))) 174 | assertApproxEquals(Line(Point(1.0, 1.0), PI / 4), lineByPoints(Point(1.0, 1.0), Point(3.0, 3.0))) 175 | } 176 | 177 | @Test 178 | @Tag("Hard") 179 | fun bisectorByPoints() { 180 | assertApproxEquals(Line(Point(2.0, 0.0), PI / 2), bisectorByPoints(Point(0.0, 0.0), Point(4.0, 0.0))) 181 | assertApproxEquals(Line(Point(1.0, 2.0), 0.0), bisectorByPoints(Point(1.0, 5.0), Point(1.0, -1.0))) 182 | } 183 | 184 | @Test 185 | @Tag("Normal") 186 | fun findNearestCirclePair() { 187 | val c1 = Circle(Point(0.0, 0.0), 1.0) 188 | val c2 = Circle(Point(3.0, 0.0), 5.0) 189 | val c3 = Circle(Point(-5.0, 0.0), 2.0) 190 | val c4 = Circle(Point(0.0, 7.0), 3.0) 191 | val c5 = Circle(Point(0.0, -6.0), 4.0) 192 | assertEquals(Pair(c1, c5), findNearestCirclePair(c1, c3, c4, c5)) 193 | assertEquals(Pair(c2, c4), findNearestCirclePair(c2, c4, c5)) 194 | assertEquals(Pair(c1, c2), findNearestCirclePair(c1, c2, c4, c5)) 195 | } 196 | 197 | @Test 198 | @Tag("Hard") 199 | fun circleByThreePoints() { 200 | val result = circleByThreePoints(Point(5.0, 0.0), Point(3.0, 4.0), Point(0.0, -5.0)) 201 | assertTrue(result.center.distance(Point(0.0, 0.0)) < 1e-5) 202 | assertEquals(5.0, result.radius, 1e-5) 203 | } 204 | 205 | @Test 206 | @Tag("Impossible") 207 | fun minContainingCircle() { 208 | val p1 = Point(0.0, 0.0) 209 | val p2 = Point(1.0, 4.0) 210 | val p3 = Point(-2.0, 2.0) 211 | val p4 = Point(3.0, -1.0) 212 | val p5 = Point(-3.0, -2.0) 213 | val p6 = Point(0.0, 5.0) 214 | val result = minContainingCircle(p1, p2, p3, p4, p5, p6) 215 | assertEquals(4.0, result.radius, 0.02) 216 | for (p in listOf(p1, p2, p3, p4, p5, p6)) { 217 | assertTrue(result.contains(p)) 218 | } 219 | } 220 | } -------------------------------------------------------------------------------- /test/lesson8/task2/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson8.task2 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | import kotlin.math.abs 7 | 8 | class Tests { 9 | @Test 10 | @Tag("Example") 11 | fun inside() { 12 | assertTrue(Square(1, 1).inside()) 13 | assertTrue(Square(8, 8).inside()) 14 | assertTrue(Square(1, 8).inside()) 15 | assertFalse(Square(0, 0).inside()) 16 | assertFalse(Square(0, 1).inside()) 17 | assertFalse(Square(9, 4).inside()) 18 | assertFalse(Square(6, 9).inside()) 19 | assertFalse(Square(100, 1).inside()) 20 | assertFalse(Square(7, -100).inside()) 21 | } 22 | 23 | @Test 24 | @Tag("Easy") 25 | fun notation() { 26 | assertEquals("", Square(1, 0).notation()) 27 | assertEquals("b3", Square(2, 3).notation()) 28 | assertEquals("g6", Square(7, 6).notation()) 29 | assertEquals("a8", Square(1, 8).notation()) 30 | assertEquals("h1", Square(8, 1).notation()) 31 | } 32 | 33 | @Test 34 | @Tag("Easy") 35 | fun square() { 36 | assertEquals(Square(3, 2), square("c2")) 37 | assertEquals(Square(5, 5), square("e5")) 38 | assertEquals(Square(6, 8), square("f8")) 39 | assertEquals(Square(4, 1), square("d1")) 40 | } 41 | 42 | @Test 43 | @Tag("Easy") 44 | fun rookMoveNumber() { 45 | assertEquals(0, rookMoveNumber(square("e3"), square("e3"))) 46 | assertEquals(2, rookMoveNumber(square("c2"), square("b1"))) 47 | assertEquals(2, rookMoveNumber(square("g8"), square("f6"))) 48 | assertEquals(1, rookMoveNumber(square("a8"), square("g8"))) 49 | assertEquals(1, rookMoveNumber(square("h3"), square("h8"))) 50 | } 51 | 52 | private fun List.assertRookTrajectory(start: Square, end: Square, length: Int) { 53 | assertEquals(length + 1, size) 54 | assertEquals(start, first()) 55 | assertEquals(end, last()) 56 | for (i in 0..size - 2) { 57 | val previous = this[i] 58 | val next = this[i + 1] 59 | assertTrue(previous.row == next.row || previous.column == next.column) 60 | } 61 | } 62 | 63 | @Test 64 | @Tag("Normal") 65 | fun rookTrajectory() { 66 | assertEquals(listOf(square("g5")), rookTrajectory(square("g5"), square("g5"))) 67 | rookTrajectory(square("c3"), square("h6")).assertRookTrajectory(square("c3"), square("h6"), 2) 68 | assertEquals(listOf(square("h2"), square("h7")), rookTrajectory(square("h2"), square("h7"))) 69 | } 70 | 71 | @Test 72 | @Tag("Easy") 73 | fun bishopMoveNumber() { 74 | assertEquals(-1, bishopMoveNumber(square("a1"), square("g8"))) 75 | assertEquals(-1, bishopMoveNumber(square("c1"), square("f3"))) 76 | assertEquals(0, bishopMoveNumber(square("d4"), square("d4"))) 77 | assertEquals(1, bishopMoveNumber(square("a3"), square("e7"))) 78 | assertEquals(2, bishopMoveNumber(square("c1"), square("c7"))) 79 | } 80 | 81 | private fun List.assertBishopTrajectory(start: Square, end: Square, length: Int) { 82 | assertEquals(length + 1, size) 83 | assertEquals(start, first()) 84 | assertEquals(end, last()) 85 | for (i in 0..size - 2) { 86 | val previous = this[i] 87 | val next = this[i + 1] 88 | assertTrue(abs(next.row - previous.row) == abs(next.column - previous.column)) 89 | } 90 | } 91 | 92 | @Test 93 | @Tag("Hard") 94 | fun bishopTrajectory() { 95 | assertEquals(listOf(), bishopTrajectory(square("a1"), square("g8"))) 96 | assertEquals(listOf(), bishopTrajectory(square("c1"), square("f3"))) 97 | assertEquals(listOf(square("d4")), bishopTrajectory(square("d4"), square("d4"))) 98 | assertEquals(listOf(square("a3"), square("e7")), bishopTrajectory(square("a3"), square("e7"))) 99 | assertEquals(listOf(square("c1"), square("f4"), square("c7")), bishopTrajectory(square("c1"), square("c7"))) 100 | assertEquals(listOf(square("f1"), square("c4"), square("f7")), bishopTrajectory(square("f1"), square("f7"))) 101 | bishopTrajectory(square("d2"), square("e5")).assertBishopTrajectory(square("d2"), square("e5"), 2) 102 | } 103 | 104 | @Test 105 | @Tag("Normal") 106 | fun kingMoveNumber() { 107 | assertEquals(0, kingMoveNumber(square("e3"), square("e3"))) 108 | assertEquals(1, kingMoveNumber(square("c2"), square("b1"))) 109 | assertEquals(2, kingMoveNumber(square("g8"), square("f6"))) 110 | assertEquals(6, kingMoveNumber(square("a8"), square("g8"))) 111 | assertEquals(7, kingMoveNumber(square("a1"), square("h8"))) 112 | } 113 | 114 | private fun List.assertKingTrajectory(start: Square, end: Square, length: Int) { 115 | assertEquals(length + 1, size) 116 | assertEquals(start, first()) 117 | assertEquals(end, last()) 118 | for (i in 0..size - 2) { 119 | val previous = this[i] 120 | val next = this[i + 1] 121 | assertTrue(abs(next.column - previous.column) <= 1) 122 | assertTrue(abs(next.row - previous.row) <= 1) 123 | } 124 | } 125 | 126 | @Test 127 | @Tag("Hard") 128 | fun kingTrajectory() { 129 | assertEquals(listOf(square("f3")), kingTrajectory(square("f3"), square("f3"))) 130 | kingTrajectory(square("c2"), square("a6")).assertKingTrajectory(square("c2"), square("a6"), 4) 131 | assertEquals(listOf(square("h2"), square("g3"), square("f4"), square("e5"), square("d6"), square("c7")), 132 | kingTrajectory(square("h2"), square("c7"))) 133 | } 134 | 135 | @Test 136 | @Tag("Hard") 137 | fun knightMoveNumber() { 138 | assertEquals(0, knightMoveNumber(square("d3"), square("d3"))) 139 | assertEquals(1, knightMoveNumber(square("e4"), square("d6"))) 140 | assertEquals(2, knightMoveNumber(square("f5"), square("g6"))) 141 | assertEquals(3, knightMoveNumber(square("g6"), square("g3"))) 142 | assertEquals(3, knightMoveNumber(square("d4"), square("a8"))) 143 | assertEquals(4, knightMoveNumber(square("h7"), square("f5"))) 144 | assertEquals(4, knightMoveNumber(square("g7"), square("h8"))) 145 | assertEquals(6, knightMoveNumber(square("a8"), square("h1"))) 146 | } 147 | 148 | private fun List.assertKnightTrajectory(start: Square, end: Square, length: Int) { 149 | assertEquals(length + 1, size) 150 | assertEquals(start, first()) 151 | assertEquals(end, last()) 152 | for (i in 0..size - 2) { 153 | val previous = this[i] 154 | val next = this[i + 1] 155 | assertTrue( 156 | abs(next.column - previous.column) == 2 && abs(next.row - previous.row) == 1 || 157 | abs(next.column - previous.column) == 1 && abs(next.row - previous.row) == 2 158 | ) 159 | } 160 | } 161 | 162 | @Test 163 | @Tag("Impossible") 164 | fun knightTrajectory() { 165 | assertEquals(listOf(square("d3")), knightTrajectory(square("d3"), square("d3"))) 166 | assertEquals(listOf(square("e4"), square("d6")), knightTrajectory(square("e4"), square("d6"))) 167 | knightTrajectory(square("f5"), square("g6")).assertKnightTrajectory(square("f5"), square("g6"), 2) 168 | knightTrajectory(square("g6"), square("g3")).assertKnightTrajectory(square("g6"), square("g3"), 3) 169 | knightTrajectory(square("d4"), square("a8")).assertKnightTrajectory(square("d4"), square("a8"), 3) 170 | knightTrajectory(square("h7"), square("f5")).assertKnightTrajectory(square("h7"), square("f5"), 4) 171 | knightTrajectory(square("g7"), square("h8")).assertKnightTrajectory(square("g7"), square("h8"), 4) 172 | knightTrajectory(square("a1"), square("a8")).assertKnightTrajectory(square("a1"), square("a8"), 5) 173 | knightTrajectory(square("a8"), square("h1")).assertKnightTrajectory(square("a8"), square("h1"), 6) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /test/lesson8/task3/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson8.task3 2 | 3 | import org.junit.jupiter.api.Tag 4 | import org.junit.jupiter.api.Test 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | 7 | class Tests { 8 | 9 | @Test 10 | @Tag("Example") 11 | fun bfs() { 12 | val graph = Graph() 13 | graph.addVertex("A") 14 | graph.addVertex("B") 15 | graph.addVertex("C") 16 | graph.addVertex("D") 17 | graph.addVertex("E") 18 | graph.addVertex("F") 19 | graph.addVertex("G") 20 | graph.addVertex("H") 21 | graph.connect("A", "B") 22 | graph.connect("B", "C") 23 | graph.connect("B", "D") 24 | graph.connect("C", "E") 25 | graph.connect("D", "F") 26 | graph.connect("C", "F") 27 | graph.connect("G", "H") 28 | assertEquals(0, graph.bfs("A", "A")) 29 | assertEquals(3, graph.bfs("A", "F")) 30 | assertEquals(2, graph.bfs("E", "F")) 31 | assertEquals(3, graph.bfs("E", "D")) 32 | assertEquals(1, graph.bfs("H", "G")) 33 | assertEquals(-1, graph.bfs("H", "A")) 34 | } 35 | 36 | 37 | @Test 38 | @Tag("Example") 39 | fun dfs() { 40 | val graph = Graph() 41 | graph.addVertex("A") 42 | graph.addVertex("B") 43 | graph.addVertex("C") 44 | graph.addVertex("D") 45 | graph.addVertex("E") 46 | graph.addVertex("F") 47 | graph.addVertex("G") 48 | graph.addVertex("H") 49 | graph.connect("A", "B") 50 | graph.connect("B", "C") 51 | graph.connect("B", "D") 52 | graph.connect("C", "E") 53 | graph.connect("D", "F") 54 | graph.connect("C", "F") 55 | graph.connect("G", "H") 56 | assertEquals(0, graph.dfs("A", "A")) 57 | assertEquals(1, graph.dfs("A", "B")) 58 | assertEquals(2, graph.dfs("A", "C")) 59 | assertEquals(2, graph.dfs("B", "F")) 60 | assertEquals(3, graph.dfs("A", "F")) 61 | assertEquals(2, graph.dfs("E", "F")) 62 | assertEquals(3, graph.dfs("E", "D")) 63 | assertEquals(1, graph.dfs("H", "G")) 64 | assertEquals(-1, graph.dfs("H", "A")) 65 | } 66 | } -------------------------------------------------------------------------------- /test/lesson9/task1/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson9.task1 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Tag 5 | import org.junit.jupiter.api.Test 6 | 7 | class Tests { 8 | @Test 9 | @Tag("Easy") 10 | fun createMatrix() { 11 | val matrix = createMatrix(4, 6, 0.0) 12 | assertEquals(4, matrix.height) 13 | assertEquals(6, matrix.width) 14 | } 15 | 16 | @Test 17 | @Tag("Normal") 18 | fun getSetInt() { 19 | val matrix = createMatrix(3, 2, 0) 20 | var value = 0 21 | for (row in 0 until matrix.height) { 22 | for (column in 0 until matrix.width) { 23 | matrix[row, column] = value++ 24 | } 25 | } 26 | value = 0 27 | for (row in 0 until matrix.height) { 28 | for (column in 0 until matrix.width) { 29 | assertEquals(value++, matrix[Cell(row, column)]) 30 | } 31 | } 32 | } 33 | 34 | @Test 35 | @Tag("Normal") 36 | fun getSetString() { 37 | val matrix = createMatrix(2, 2, "") 38 | val strings = listOf("alpha", "beta", "gamma", "omega") 39 | var index = 0 40 | for (row in 0 until matrix.height) { 41 | for (column in 0 until matrix.width) { 42 | matrix[Cell(row, column)] = strings[index++] 43 | } 44 | } 45 | index = 0 46 | for (row in 0 until matrix.height) { 47 | for (column in 0 until matrix.width) { 48 | assertEquals(strings[index++], matrix[row, column]) 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /test/lesson9/task2/Tests.kt: -------------------------------------------------------------------------------- 1 | package lesson9.task2 2 | 3 | import lesson9.task1.Matrix 4 | import lesson9.task1.createMatrix 5 | import org.junit.jupiter.api.Assertions.* 6 | import org.junit.jupiter.api.Tag 7 | import org.junit.jupiter.api.Test 8 | 9 | class Tests { 10 | private fun createMatrix(height: Int, width: Int, values: List>): Matrix { 11 | val matrix = createMatrix(height, width, values[0][0]) 12 | for (row in 0 until height) { 13 | for (column in 0 until width) { 14 | matrix[row, column] = values[row][column] 15 | } 16 | } 17 | return matrix 18 | } 19 | 20 | @Test 21 | @Tag("Example") 22 | fun transpose() { 23 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), transpose(createMatrix(1, 1, listOf(listOf(1))))) 24 | assertEquals(createMatrix(3, 4, listOf(listOf(1, 4, 6, 3), listOf(2, 5, 5, 2), listOf(3, 6, 4, 1))), 25 | transpose(createMatrix(4, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(6, 5, 4), listOf(3, 2, 1))))) 26 | } 27 | 28 | @Test 29 | @Tag("Example") 30 | fun plus() { 31 | assertEquals(createMatrix(1, 1, listOf(listOf(3))), 32 | createMatrix(1, 1, listOf(listOf(1))) + createMatrix(1, 1, listOf(listOf(2)))) 33 | assertEquals(createMatrix(2, 2, listOf(listOf(2, 5), listOf(8, 11))), 34 | createMatrix(2, 2, listOf(listOf(1, 2), listOf(3, 4))) + createMatrix(2, 2, listOf(listOf(1, 3), listOf(5, 7)))) 35 | } 36 | 37 | @Test 38 | @Tag("Hard") 39 | fun generateSpiral() { 40 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), generateSpiral(1, 1)) 41 | assertEquals(createMatrix(2, 2, 42 | listOf( 43 | listOf(1, 2), 44 | listOf(4, 3) 45 | )), generateSpiral(2, 2)) 46 | assertEquals(createMatrix(3, 4, 47 | listOf( 48 | listOf(1, 2, 3, 4), 49 | listOf(10, 11, 12, 5), 50 | listOf(9, 8, 7, 6) 51 | )), generateSpiral(3, 4)) 52 | } 53 | 54 | @Test 55 | @Tag("Hard") 56 | fun generateRectangles() { 57 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), generateRectangles(1, 1)) 58 | assertEquals(createMatrix(2, 2, 59 | listOf( 60 | listOf(1, 1), 61 | listOf(1, 1) 62 | )), generateRectangles(2, 2)) 63 | assertEquals(createMatrix(4, 3, 64 | listOf( 65 | listOf(1, 1, 1), 66 | listOf(1, 2, 1), 67 | listOf(1, 2, 1), 68 | listOf(1, 1, 1) 69 | )), generateRectangles(4, 3)) 70 | assertEquals(createMatrix(5, 6, 71 | listOf( 72 | listOf(1, 1, 1, 1, 1, 1), 73 | listOf(1, 2, 2, 2, 2, 1), 74 | listOf(1, 2, 3, 3, 2, 1), 75 | listOf(1, 2, 2, 2, 2, 1), 76 | listOf(1, 1, 1, 1, 1, 1) 77 | )), generateRectangles(5, 6)) 78 | 79 | } 80 | 81 | @Test 82 | @Tag("Hard") 83 | fun generateSnake() { 84 | assertEquals(createMatrix(1, 1, listOf(listOf(1))), generateSnake(1, 1)) 85 | assertEquals(createMatrix(2, 2, 86 | listOf( 87 | listOf(1, 2), 88 | listOf(3, 4) 89 | )), generateSnake(2, 2)) 90 | assertEquals(createMatrix(4, 2, 91 | listOf( 92 | listOf(1, 2), 93 | listOf(3, 4), 94 | listOf(5, 6), 95 | listOf(7, 8) 96 | )), generateSnake(4, 2)) 97 | assertEquals(createMatrix(5, 4, 98 | listOf( 99 | listOf(1, 2, 4, 7), 100 | listOf(3, 5, 8, 11), 101 | listOf(6, 9, 12, 15), 102 | listOf(10, 13, 16, 18), 103 | listOf(14, 17, 19, 20) 104 | )), generateSnake(5, 4)) 105 | 106 | } 107 | 108 | @Test 109 | @Tag("Normal") 110 | fun rotate() { 111 | val m = createMatrix(1, 1, listOf(listOf(("single")))) 112 | assertEquals(m, rotate(m)) 113 | assertEquals(createMatrix(2, 2, listOf(listOf("alpha", "beta"), 114 | listOf("gamma", "delta"))), 115 | rotate(createMatrix(2, 2, listOf(listOf("beta", "delta"), 116 | listOf("alpha", "gamma"))))) 117 | assertEquals(createMatrix(3, 3, listOf(listOf(7, 4, 1), listOf(8, 5, 2), listOf(9, 6, 3))), 118 | rotate(createMatrix(3, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(7, 8, 9))))) 119 | } 120 | 121 | @Test 122 | @Tag("Hard") 123 | fun isLatinSquare() { 124 | assertTrue(isLatinSquare(createMatrix(1, 1, listOf(listOf(1))))) 125 | assertFalse(isLatinSquare(createMatrix(1, 1, listOf(listOf(2))))) 126 | assertTrue(isLatinSquare(createMatrix(2, 2, listOf(listOf(1, 2), listOf(2, 1))))) 127 | assertFalse(isLatinSquare(createMatrix(2, 2, listOf(listOf(1, 2), listOf(1, 2))))) 128 | assertTrue(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 1), listOf(1, 2, 3), listOf(3, 1, 2))))) 129 | assertFalse(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 1), listOf(1, 2, 3), listOf(3, 1, 4))))) 130 | assertFalse(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 1), listOf(1, 2, 3), listOf(1, 3, 2))))) 131 | assertFalse(isLatinSquare(createMatrix(3, 3, listOf(listOf(2, 3, 0), listOf(1, 2, 3), listOf(3, 1, 2))))) 132 | } 133 | 134 | @Test 135 | @Tag("Normal") 136 | fun sumNeighbours() { 137 | assertEquals(createMatrix(1, 1, listOf(listOf(0))), sumNeighbours(createMatrix(1, 1, listOf(listOf(42))))) 138 | assertEquals(createMatrix(2, 2, listOf(listOf(9, 8), listOf(7, 6))), 139 | sumNeighbours(createMatrix(2, 2, listOf(listOf(1, 2), listOf(3, 4))))) 140 | assertEquals(createMatrix(4, 3, listOf(listOf(11, 19, 13), listOf(19, 31, 19), 141 | listOf(19, 31, 19), listOf(13, 19, 11))), 142 | sumNeighbours(createMatrix(4, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6), 143 | listOf(6, 5, 4), listOf(3, 2, 1))))) 144 | } 145 | 146 | @Test 147 | @Tag("Normal") 148 | fun findHoles() { 149 | assertEquals(Holes(listOf(), listOf()), findHoles(createMatrix(1, 1, listOf(listOf(1))))) 150 | assertEquals(Holes(listOf(0), listOf(0)), findHoles(createMatrix(1, 1, listOf(listOf(0))))) 151 | assertEquals(Holes(listOf(), listOf()), findHoles(createMatrix(2, 2, listOf(listOf(0, 1), listOf(1, 0))))) 152 | assertEquals(Holes(listOf(), listOf(0)), findHoles(createMatrix(2, 2, listOf(listOf(0, 1), listOf(0, 1))))) 153 | assertEquals(Holes(listOf(4), listOf(1, 3)), 154 | findHoles(createMatrix(5, 4, listOf(listOf(1, 0, 1, 0), listOf(0, 0, 1, 0), listOf(1, 0, 0, 0), 155 | listOf(0, 0, 1, 0), listOf(0, 0, 0, 0))))) 156 | } 157 | 158 | @Test 159 | @Tag("Normal") 160 | fun sumSubMatrix() { 161 | assertEquals(createMatrix(1, 1, listOf(listOf(0))), sumSubMatrix(createMatrix(1, 1, listOf(listOf(0))))) 162 | assertEquals(createMatrix(1, 5, listOf(listOf(1, 3, 6, 8, 9))), 163 | sumSubMatrix(createMatrix(1, 5, listOf(listOf(1, 2, 3, 2, 1))))) 164 | assertEquals(createMatrix(2, 2, listOf(listOf(2, 6), listOf(3, 10))), 165 | sumSubMatrix(createMatrix(2, 2, listOf(listOf(2, 4), listOf(1, 3))))) 166 | assertEquals(createMatrix(3, 3, listOf(listOf(1, 3, 6), listOf(5, 12, 21), listOf(12, 27, 45))), 167 | sumSubMatrix(createMatrix(3, 3, listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(7, 8, 9))))) 168 | } 169 | 170 | @Test 171 | @Tag("Easy") 172 | fun unaryMinus() { 173 | assertEquals(createMatrix(1, 1, listOf(listOf(3))), -createMatrix(1, 1, listOf(listOf(-3)))) 174 | assertEquals(createMatrix(2, 2, listOf(listOf(-1, -2), listOf(-3, -4))), 175 | -createMatrix(2, 2, listOf(listOf(1, 2), listOf(3, 4)))) 176 | assertEquals(createMatrix(1, 3, listOf(listOf(-4, -6, -8))), -createMatrix(1, 3, listOf(listOf(4, 6, 8)))) 177 | } 178 | 179 | @Test 180 | @Tag("Normal") 181 | fun times() { 182 | assertEquals(createMatrix(1, 1, listOf(listOf(2))), 183 | createMatrix(1, 1, listOf(listOf(1))) * createMatrix(1, 1, listOf(listOf(2)))) 184 | assertEquals(createMatrix(1, 1, listOf(listOf(19))), 185 | createMatrix(1, 3, listOf(listOf(2, 3, 5))) * createMatrix(3, 1, listOf(listOf(4), listOf(2), listOf(1)))) 186 | assertEquals(createMatrix(2, 2, listOf(listOf(23, -2), listOf(8, -18))), 187 | createMatrix(2, 3, listOf(listOf(3, 5, 1), listOf(2, -2, 4))) * 188 | createMatrix(3, 2, listOf(listOf(4, 1), listOf(2, 0), listOf(1, -5)))) 189 | } 190 | 191 | @Test 192 | @Tag("Hard") 193 | fun canOpenLock() { 194 | assertFalse(canOpenLock( 195 | key = createMatrix(1, 1, listOf(listOf(1))), 196 | lock = createMatrix(1, 1, listOf(listOf(1)))).first) 197 | assertEquals(Triple(true, 0, 0), canOpenLock( 198 | key = createMatrix(1, 1, listOf(listOf(1))), 199 | lock = createMatrix(1, 1, listOf(listOf(0))))) 200 | assertEquals(Triple(true, 0, 1), canOpenLock( 201 | key = createMatrix(2, 2, listOf(listOf(1, 0), listOf(0, 1))), 202 | lock = createMatrix(3, 3, listOf(listOf(1, 0, 1), listOf(0, 1, 0), listOf(1, 1, 1))))) 203 | assertFalse(canOpenLock( 204 | key = createMatrix(2, 2, listOf(listOf(1, 1), listOf(1, 0))), 205 | lock = createMatrix(3, 3, listOf(listOf(1, 0, 1), listOf(0, 1, 0), listOf(1, 1, 1)))).first) 206 | } 207 | 208 | @Test 209 | @Tag("Hard") 210 | fun fifteenGameMoves() { 211 | val start = createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8), 212 | listOf(9, 10, 11, 12), listOf(13, 14, 15, 0))) 213 | assertEquals(start, fifteenGameMoves(start, listOf())) 214 | assertEquals(start, fifteenGameMoves(createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8), 215 | listOf(9, 10, 11, 12), listOf(0, 13, 14, 15))), 216 | listOf(13, 14, 15))) 217 | assertEquals(start, fifteenGameMoves(createMatrix(4, 4, listOf(listOf(1, 2, 3, 0), listOf(5, 6, 7, 4), 218 | listOf(9, 10, 11, 8), listOf(13, 14, 15, 12))), 219 | listOf(4, 8, 12))) 220 | assertEquals(createMatrix(4, 4, listOf(listOf(5, 7, 9, 1), listOf(2, 12, 14, 15), 221 | listOf(0, 4, 13, 6), listOf(3, 10, 11, 8))), 222 | fifteenGameMoves(createMatrix(4, 4, listOf(listOf(5, 7, 9, 1), listOf(2, 12, 14, 15), 223 | listOf(3, 4, 6, 8), listOf(10, 11, 13, 0))), 224 | listOf(8, 6, 13, 11, 10, 3))) 225 | try { 226 | fifteenGameMoves(start, listOf(1)) 227 | assert(false) { "Exception expected" } 228 | } 229 | catch (e: IllegalStateException) {} 230 | catch (e: Throwable) { 231 | assert(false) { "IllegalStateException expected" } 232 | } 233 | } 234 | 235 | private fun Matrix.copy(): Matrix { 236 | val result = createMatrix(height, width, this[0, 0]) 237 | for (row in 0 until height) { 238 | for (column in 0 until width) { 239 | result[row, column] = this[row, column] 240 | } 241 | } 242 | return result 243 | } 244 | 245 | private fun Matrix.assertSolution() { 246 | val copy = copy() 247 | val moves = fifteenGameSolution(this) 248 | val start1 = createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8), 249 | listOf(9, 10, 11, 12), listOf(13, 14, 15, 0))) 250 | val start2 = createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8), 251 | listOf(9, 10, 11, 12), listOf(13, 15, 14, 0))) 252 | val result = fifteenGameMoves(copy, moves) 253 | assertTrue(result == start1 || result == start2) { "Result position is not a solution position: $result" } 254 | } 255 | 256 | @Test 257 | @Tag("Impossible") 258 | fun fifteenGameSolution() { 259 | createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8), 260 | listOf(9, 10, 11, 12), listOf(13, 14, 15, 0))).assertSolution() 261 | createMatrix(4, 4, listOf(listOf(1, 2, 3, 4), listOf(5, 6, 7, 8), 262 | listOf(9, 10, 11, 12), listOf(13, 15, 14, 0))).assertSolution() 263 | createMatrix(4, 4, listOf(listOf(5, 7, 9, 2), listOf(1, 12, 14, 15), 264 | listOf(3, 4, 6, 8), listOf(10, 11, 13, 0))).assertSolution() 265 | createMatrix(4, 4, listOf(listOf(0, 1, 2, 3), listOf(4, 5, 6, 7), 266 | listOf(8, 9, 10, 11), listOf(12, 13, 14, 15))).assertSolution() 267 | } 268 | } -------------------------------------------------------------------------------- /tutorial/chapter06_5.adoc: -------------------------------------------------------------------------------- 1 | = 6.5. Доктор RegExp 2 | 3 | __Регулярные выражения (RegExp)__ -- специальный язык для описания множества строк. 4 | Они помогают решать задачу поиска какого-либо текста (из описанного множества) в другом тексте, 5 | описывают интересующий нас текст и работают достаточно эффективно для **быстрого** решения задачи поиска. 6 | 7 | В некоторых случаях количество вариантов искомого текста настолько велико, что перечислять все варианты 8 | становится неудобно. 9 | Иногда все эти варианты могут быть представлены одной строкой -- __регулярным выражением__. 10 | 11 | Примеры регулярных выражений (см. слайды): 12 | 13 | * ```KotlinAsFirst``` 14 | * ```[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}``` 15 | * ```ˆ4[0-9]{12}(?:[0-9]{3})?$``` 16 | * ```[-+]?[0-9]*\.?[0-9]+``` 17 | * ```<([a-z]+)([ˆ<]+)*(?:>(.*)<\/\1>|\s+\/>)``` 18 | 19 | Поиск регулярного выражения осуществляется с помощью автомата с состояниями, или __конечного автомата__. 20 | В данном случае под этим понимается алгоритм, имеющий некоторое количество устойчивых __состояний__. 21 | Для каждого состояния определяются действия, которые алгоритм выполняет в этом состоянии, 22 | а также условия, по которым алгоритм переходит в другие состояния. 23 | 24 | == Возможности языка регулярных выражений 25 | 26 | Регулярное выражение в общем случае -- это строка, в которой часть символов играет специальную роль. 27 | Но большинство символов в регулярном выражении обозначают просто самих себя. Например: 28 | 29 | * `KotlinAsFirst` 30 | * `Трансмогрификация` 31 | * `Мама мыла раму` 32 | * `42` 33 | 34 | Существует однако ряд специальных символов. 35 | Например, __класс символов__ обозначает любой символ из определённого множества: 36 | 37 | * ```[0123456789]``` -- любая цифра 38 | * ```[aeiouy]``` -- любая буква из перечисленных 39 | * ```[~!@#$%^&*+-]``` -- любой символ из перечисленных 40 | 41 | Отрицание класса символов ищет любой символ НЕ из заданного множества: 42 | 43 | * ```[^0123456789]``` -- всё, что угодно, кроме цифры 44 | * ```[^a-z]``` -- всё, что угодно, кроме строчной латинской буквы 45 | * ```[^-az]``` -- всё, что угодно, кроме `-`, `a`, `z` 46 | 47 | Классы и их отрицания, как видим, используют специальные символы `[...]` для обозначения класса, 48 | специальный символ `-` для обозначения интервала символов и 49 | последовательность `[^...]` для обозначения отрицания. 50 | 51 | __Якоря__ позволяют найти начало или конец всей строки: 52 | 53 | * ```^fun``` -- `fun` в начале строки 54 | * ```\.$``` -- точка в конце строки 55 | * ```^Kotlin is great as the first language!$``` -- ВСЯ строка с заданной фразой (и более ничем) 56 | 57 | Здесь `^` используется для обозначения начала строки, а `$` для обозначения конца. 58 | Следует иметь в виду, что якоря никак не учитывают переводы строк -- 59 | имеется в виду начало или конец всего текста, а не одной строки в тексте. 60 | 61 | `\.` использует экранирование для обозначения символа `.`, 62 | поскольку в регулярных выражениях точка является специальным символом (и обозначает любой символ). 63 | Таким образом, `\` в регулярных выражениях экранирует последующий символ, 64 | делая его из специального символа обыкновенным. 65 | Для обозначения символа `\` применяется пара `\\`. 66 | Аналогично, `\^` обозначает символ-шапку, `\$` -- символ доллара, `\[` -- открывающую квадратную скобку, 67 | `\]` -- закрывающую квадратную скобку. 68 | 69 | Особые символы ищут символы по специальным правилам: 70 | 71 | * ``` ..... ``` -- любая последовательность из пяти символов, начинающаяся и заканчивающаяся пробелов 72 | * `\t` -- табуляция, `\n` -- новая строка, `\r` -- возврат каретки (два последних символа унаследованы компьютерами от эпохи пишущих машинок, когда для начала печати с новой строки необходимо было выполнить два действия -- __возврат каретки__ в начало строки и перевод каретки на __новую строку__) 73 | * `\s` -- произвольный вид пробела (пробел, табуляция, новая строка, возврат каретки) 74 | * `\d` -- произвольная цифра, аналог ``[0-9]`` 75 | * `\w` -- произвольная "символ в слове", обычно аналог ``[a-zA-z0-9]``, то есть, латинская буква или цифра 76 | * `\S` -- НЕ пробел, `\D` -- НЕ цифра, `\W` -- НЕ "символ в слове" 77 | 78 | __Шаблон выбора__ `|` ищет одну строку из нескольких, например: 79 | 80 | * ```Марат|Михаил``` -- Марат или Михаил 81 | * ```^\[|\]$``` -- открывающая квадратная скобка в начале строки или закрывающая в конце 82 | * ```for.*(val|var).*``` -- цикл `for` с последующим `val` или `var` 83 | 84 | __Шаблоны количества__ ищут определённое число совпадений: 85 | 86 | * ```.*``` -- любое количество (в том числе ноль) любых символов 87 | * ```(Марат)+``` -- строка Марат один или более раз (но не ноль) 88 | * ```(Михаил)?``` -- строка Михаил ноль или один раз 89 | * ```([0-9]{4})``` -- последовательность из ровно четырёх любых цифр 90 | * ```\w{8,16}``` -- последовательность из 8-16 "символов в слове" 91 | 92 | Круглые скобки `()` задают так называемые __группы поиска__, объединяя несколько символов вместе. 93 | 94 | * ```(Kotlin)+AsFirst``` -- KotlinAsFirst, KotlinKotlinAsFirst, KotlinKotlinKotlinAsFirst, ... 95 | * ```(?:\$\$)+``` -- `$$`, `$$$$`, `$$$$$$`, ... 96 | * ```(\w+)\s\1``` -- слово, за которым следует пробел и то же самое слово. 97 | * ```fun\s+(/w+)\s*\{.*\1.*\}``` -- `fun` с последующими пробелами, произвольным словом в круглых скобках, пробелами и тем же словом в фигурных скобках 98 | 99 | Здесь `\1` (`\2`, `\3`, ...) ищет **уже описанную** группу поиска по её номеру внутри регулярного выражения 100 | (в данном случае -- первую группу). 101 | Комбинация `(?:...)` задаёт группу поиска **без номера**. 102 | В целом, `(?...)` задаёт __группы особого поиска__: 103 | 104 | * ```Марат(?=\sАхин)``` -- Марат, за которым следует пробел и Ахин 105 | * ```(?<=Михаил\s)Глухих``` -- Глухих, перед которым стоит Михаил с пробелом 106 | * ```\d+(?![$\d])``` -- число, после которого НЕ стоит знак доллара 107 | * ```(? previous * 60 + next 157 | } 158 | } 159 | ---- 160 | 161 | Здесь мы разбираем исходную строку вида "12:34:56" с целью найти в ней три одинаковых группы поиска `(\d\d)`. 162 | Каждая из групп поиска включает в себя две цифры. 163 | Убедившись с помощью проверки на **null**, что регулярное выражение успешно найдено, 164 | мы отбрасываем первый элемент `groupValues` с помощью функции `drop(1)`, 165 | оставляя, таким образом, в списке только значения трёх групп поиска. 166 | Далее каждая из пар цифр конвертируется в число. 167 | Результат сворачивается в число секунд, прошедших с начала дня, с помощью функции высшего порядка `fold` -- см. раздел 4. 168 | 169 | == Полезные ссылки 170 | 171 | * http://regexr.com 172 | * https://regex101.com 173 | * https://docs.oracle.com/javase/tutorial/essential/regex -------------------------------------------------------------------------------- /tutorial/chapter07.adoc: -------------------------------------------------------------------------------- 1 | = 7. Файловые операции 2 | 3 | Программы, которые мы пишем, так или иначе должны взаимодействовать с пользователем и внешней средой -- 4 | операционной системой, устройствами компьютера, сетью Интернет. 5 | Простейшим способом взаимодействия является ввод с консоли и вывод на консоль, 6 | но сейчас такой способ применяется крайне редко. 7 | Более совершенным способом взаимодействия являются __файлы__ -- 8 | многие программы берут из них настроечную или входную информацию, 9 | и используют их для сохранения результатов своей работы или различных настроек. 10 | Например, всевозможные __редакторы__ позволяют открывать файлы в определённом формате, 11 | просматривать и/или изменять их, сохранять файлы на диске компьютера или в сети Интернет. 12 | 13 | В библиотеке Java внутри пакета `java.io` имеется ряд типов, обеспечивающих возможность работы с файлами, 14 | а библиотека Котлина дополняет их некоторыми удобными возможностями. 15 | Как обычно, рассмотрим часть этих возможностей на примере. 16 | 17 | [source,kotlin] 18 | ---- 19 | import java.io.File 20 | /** 21 | * Пример 22 | * 23 | * Во входном файле с именем inputName содержится некоторый текст. 24 | * Вывести его в выходной файл с именем outputName, выровняв по левому краю, 25 | * чтобы длина каждой строки не превосходила lineLength. 26 | * Слова в слишком длинных строках следует переносить на следующую строку. 27 | * Слишком короткие строки следует дополнять словами из следующей строки. 28 | * Пустые строки во входном файле обозначают конец абзаца, 29 | * их следует сохранить и в выходном файле 30 | */ 31 | fun alignFile(inputName: String, lineLength: Int, outputName: String) { 32 | val writer = File(outputName).bufferedWriter() 33 | var currentLineLength = 0 34 | for (line in File(inputName).readLines()) { 35 | if (line.isEmpty()) { 36 | writer.newLine() 37 | if (currentLineLength > 0) { 38 | writer.newLine() 39 | currentLineLength = 0 40 | } 41 | } 42 | for (word in line.split(Regex("\\s+"))) { 43 | if (currentLineLength > 0) { 44 | if (word.length + currentLineLength >= lineLength) { 45 | writer.newLine() 46 | currentLineLength = 0 47 | } 48 | else { 49 | writer.write(" ") 50 | currentLineLength++ 51 | } 52 | } 53 | writer.write(word) 54 | currentLineLength += word.length 55 | } 56 | } 57 | writer.close() 58 | } 59 | ---- 60 | 61 | Краеугольный тип, используемый для работы с файлами в Котлине -- тип `java.io.File`. 62 | В соответствии с названием, он предназначен для различных операций с файлами; 63 | объект этого типа соответствует какому-либо реальному файлу, чаще всего находящемуся на жёстком диске. 64 | Для создания файла используется специальная функция-конструктор: `File(inputName)` или `File(outputName)` в примере. 65 | Если в аргументе конструктора указано только имя файла -- поиск файла происходит в текущей директории, 66 | а если аргумент содержим также путь к файлу -- то в директории, указанной этим путём. 67 | Специфика конструктора заключается в том, что его имя совпадает с типом объекта, которую он создаёт, 68 | и он имеет результат соответствующего типа. 69 | Более подробно мы поговорим о конструкторах в следующем уроке. 70 | 71 | Обмен данными с файлом может происходить в режиме чтения либо в режиме записи. 72 | В режиме чтения информации, заданное имя должно соответствовать уже существующему файлу. 73 | Один из способов получения информации из файла -- вызов функции `file.readLines()`. 74 | Результат вызова -- список строк, из которых состоит файл. 75 | Каждый `String` в этом списке соответствует одной строке файла, 76 | строки файла разделяются символом "возврат каретки" и / или "новая строка". 77 | 78 | В режиме записи информации, заданное имя может не соответствовать существующему файлу -- в этом случае он будет создан. 79 | Для записи информации, необходимо создать один из объектов, обеспечивающих такую возможность. 80 | В примере, таким объектом является `val writer = File(outputName).bufferedWriter()` -- 81 | то есть необходимо вызвать функцию `bufferedWriter()` на получателе, соответствующем исходному файлу. 82 | Как видно из текста примера, `writer` (писатель) имеет функции `writer.newLine()` (добавление в файл новой строки), 83 | `writer.write(string)` (добавление в файл заданной строки) и `writer.close()` (закрытие писателя, 84 | выполняется строго ПОСЛЕ выполнения всех остальных действий и фиксирует итоговое состояние файла). 85 | 86 | Мы перечислили все файловые операции, присутствующие в исходном примере. 87 | Внутри цикла `for`, каждая из строк файла разбивается по пробелам на слова, с этой целью используется `Regex("\\s+")`. 88 | В `currentLineLength` накапливается длина текущей строки ВЫХОДНОГО файла. 89 | Если в текущей строке достаточно места для очередного слова ВХОДНОГО файла, слово добавляется в текущую строку, 90 | в противном случае в файл добавляется перевод строки и слово добавляется в новую строку. 91 | Пустые строки входного файла, как и сказано в задании, переносятся в выходной файл без изменений. 92 | 93 | == За занавесом: чтение из файла 94 | 95 | Пакет `java.io` позволяет работать с файлами на трёх разных уровнях: 96 | 97 | 1. Уровень отдельных байт. В этом случае файл воспринимается как массив или, точнее, как поток байт. Поток, в отличие от массива, можно только перебирать, с сильно ограниченными возможностями по возвращению назад. Для этой цели имеется тип `java.io.InputStream`. 98 | 1. Уровень символов. В этом случае файл воспринимается уже как поток символов типа `Char`, то есть каждые несколько байт файла превращаются в определённый символ -- с учётом заданной кодировки файла. Для этой цели имеется тип `java.io.InputStreamReader`, который внутри себя использует `InputStream` для чтения байт. 99 | 1. Уровень строк. На этом уровне файл воспринимается как набор строк `String`, составленных из символов по определённым правилам -- чаще всего используется разделение по отдельным строкам файла. Эту роль выполняет тип `java.io.BufferedReader`, использующий внутри себя `InputStreamReader` для чтения символов. 100 | 101 | При программировании на Java каждый из этих объектов приходится создавать отдельно -- 102 | вначале `InputStream`, потом `InputStreamReader` и, наконец, `BufferedReader`. 103 | Библиотека Котлина позволяет создать любой из этих объектов сразу, используя файл-получатель: 104 | 105 | 1. `file.inputStream()` создаёт байтовый поток. 106 | 1. `file.reader()` создаёт читатель символов, используя кодировку по умолчанию. `file.reader(Charset.forName("CP1251"))` создаёт писатель с заданной кодировкой (в данном случае CP1251). 107 | 1. Наконец, `file.bufferedReader()` создаёт буферизованный читатель строк. Опять-таки, может быть задана нужная кодировка, иначе используется кодировка по умолчанию. 108 | 109 | Набор функций у данных трёх объектов различается. 110 | У всех у них есть функция `close()`, закрывающая исходный файл в конце работы с потоком. 111 | Также, у них имеется функция высшего порядка `use { ... }`, 112 | выполняющая описанные в лямбде действия и закрывающая файл в конце своей работы автоматически. 113 | Скажем, исходный пример можно было бы переписать с помощью `use` так: 114 | 115 | [source,kotlin] 116 | ---- 117 | fun alignFile(inputName: String, lineLength: Int, outputName: String) { 118 | File(outputName).bufferedWriter().use { 119 | var currentLineLength = 0 120 | for (line in File(inputName).readLines()) { 121 | if (line.isEmpty()) { 122 | it.newLine() 123 | if (currentLineLength > 0) { 124 | it.newLine() 125 | currentLineLength = 0 126 | } 127 | continue 128 | } 129 | for (word in line.split(" ")) { 130 | if (currentLineLength > 0) { 131 | if (word.length + currentLineLength >= lineLength) { 132 | it.newLine() 133 | currentLineLength = 0 134 | } else { 135 | it.write(" ") 136 | currentLineLength++ 137 | } 138 | } 139 | it.write(word) 140 | currentLineLength += word.length 141 | } 142 | } 143 | } 144 | } 145 | ---- 146 | 147 | Здесь исходный `BufferedWriter` в лямбде становится параметром `it`. 148 | Заметим, что при использовании `use` исходный файл будет закрыт как при корректном завершении функции, 149 | так и при возникновении исключения. 150 | 151 | Кроме этого, каждый объект обладает своими методами для чтения информации: 152 | 153 | 1. `inputStream.read()` читает из `InputStream` очередной байт, возвращая его в виде результата типа `Int`. Если файл закончен, результат этой функции будет -1. `inputStream.read(byteArray)` читает сразу несколько байт, записывая их в массив байт (число прочитанных байт равно размеру массива). `inputStream.read(byteArray, offset, length)` записывает в `byteArray` `length` байт, начиная с индекса `offset`. 154 | 1. `reader.read()` читает из `InputStreamReader` очередной символ, возвращая его в виде результата типа `Int`. Здесь используется именно `Int`, а не `Char`, так как, во-первых, символ в общем случае может не поместиться в двухбайтовые тип и, во-вторых, чтобы вернуть -1 в случае неудачи. Есть аналогичные методы для чтения символьного массива (НЕ строки) с возможным указанием смещения и числа символов -- см. выше про байтовый массив. 155 | 1. `bufferedReader.readLine()` читает из `BufferedReader` очередную строку (до перевода строки). `bufferedReader.readLines()` читает сразу же все строки. Есть ряд других методов для работы со строками по отдельности. 156 | 157 | Следует отметить, что все функции чтения информации могут бросить исключение `IOException` в том случае, 158 | если чтение по какой-либо причине невозможно (например, если файл не существует или недоступен). 159 | 160 | В примере, мы вообще не создавали `bufferedReader`, а использовали функцию `file.readLines()`. 161 | Она создаёт `bufferedReader` внутри себя и обращается к его функции `readLines()`. 162 | После чтения последней строки файл закрывается. 163 | 164 | == За занавесом: запись в файл 165 | 166 | Запись в файл использует те же три уровня: байты `OutputStream`, символы `OutputStreamWriter` и строки `BufferedWriter`. 167 | Для записи байт либо символов используются функции `write`, 168 | аргументом которых может являться целое число (в котором хранится байт или код символа) или массив (опять-таки байт или символов). 169 | Эти функции не имеют результата и бросают `IOException`, если файл недоступен для записи. 170 | `BufferedWriter` может использовать функцию `write` также для записи строк. 171 | Как и все три вида потоков чтения, 172 | потоки записи необходимо закрывать после использования с помощью `close()` или `use { ... }`. 173 | 174 | Сверх этого, для записи часто используется так называемый поток печати `PrintStream`. 175 | В Котлине его можно создать из файла, используя функцию `file.printStream()`. 176 | Поток печати расширяет обычный байтовый поток рядом дополнительных возможностей: 177 | 178 | 1. `printStream.println(...)` -- вывод заданной строки или строкового представления с последующим переходом на новую строку. 179 | 1. `printStream.print(...)` -- то же, но без перехода на новую строку. 180 | 1. `printStream.format(formatString, ...)` -- форматированный вывод (происходит по принципу, описанном в разделе 6). 181 | 182 | == Упражнения 183 | 184 | Откройте файл `srс/lesson7/task1/Files.kt` в проекте `KotlinAsFirst`. 185 | Он содержит ряд задач, каждая из которых предполагает наличие входного и/или выходного файла. 186 | Решите хотя бы одну-две из имеющихся задач, используя описанные в этом разделе приёмы. 187 | Обратите внимание на задачи, помеченные как "Сложная" или "Очень сложная", попробуйте решить одну из них. 188 | 189 | Протестируйте свою реализацию, используя тесты из `test/lesson7/task1/Tests.kt`. 190 | Обратите внимание, что тесты используют готовые входные файлы, расположенные в директории `input` нашего проекта. 191 | Убедитесь, что тесты успешно проходят, обязательно создайте два-три дополнительных теста. 192 | Постарайтесь внутри этих тестов проверить какие-либо необычные ситуации, 193 | которые могут возникнуть в выбранной вами задаче. 194 | 195 | Поздравляем! Выполнив упражнения по этому разделу, вы успешно завершили базовую часть нашего курса. 196 | Если вас интересует получение сертификата, прочитайте следующий параграф. 197 | 198 | == Получение сертификата / экзамен 199 | 200 | TODO 201 | -------------------------------------------------------------------------------- /tutorial/chapter10.adoc: -------------------------------------------------------------------------------- 1 | = 10. Дополнительные главы 2 | 3 | == Разбор и представление арифметических выражений 4 | 5 | Эта глава развивает содержимое шестого урока, 6 | отвечая на вопрос "А что делать, если простого разбиения строки на части недостаточно?". 7 | Одной из подобных ситуаций является разбор арифметических выражений. 8 | Для этого и других похожих случаев программисты придумали парсеры -- 9 | специальные функции или объекты, выполняющие разбор сложных строк. 10 | Для написания парсеров используется теория формальных языков, 11 | которой мы (вкратце) коснёмся в этой главе. 12 | 13 | Рассмотрим пример. 14 | Пусть во входном файле содержится строчка, содержащая описание функции от `x`, например: 15 | 16 | [source] 17 | ---- 18 | 3*x*x - (2 / x) + 7 -x 19 | ---- 20 | 21 | Необходимо по имеющемуся списку значений `x` (например, 1, 2 и -1) 22 | рассчитать значения данной функции и вернуть их в виде ассоциативного массива 23 | (например, 1 -> 7, 2 -> 16, -1 -> 13). 24 | 25 | Предполагается, что функция является целочисленной, 26 | и в ней могут присутствовать четыре арифметических действия и круглые скобки. 27 | 28 | Как решать подобную задачу? Поэтапно. 29 | 30 | === 1. Лексический анализ 31 | 32 | Лексический анализ -- первый этап решения. 33 | Его цель -- разбиение исходного выражения на атомарные элементы, 34 | которыми в приведённом примере являются: 35 | 36 | [source] 37 | ---- 38 | 3, *, x, *, x, -, (, 2, /, x, ), +, 7, -, x 39 | ---- 40 | 41 | Обратите внимание, что в процессе лексического анализа из выражения выбрасываются пробелы. 42 | 43 | Один из способов лексического анализа -- применение регулярных выражений. 44 | 45 | [source,kotlin] 46 | ---- 47 | fun String.parseExpr(): Expression { 48 | val matchResults = Regex("""x|\+|-|\*|/|\(|\)|\d+?| +?|.+?""").findAll(this) 49 | val groups = matchResults.map { it.value }.filter { it.isNotBlank() }.toList() 50 | return Parser(groups).parse() 51 | } 52 | ---- 53 | 54 | Заданное в этой функции регулярное выражение задаёт описание атомарного элемента. 55 | Это может быть `x`, знаки четырёх арифметических действий, знаки скобок, 56 | пробелы, и все прочие символы. 57 | Лямбда `{ it.isNotBlank() }` убирает из имеющейся последовательности пробелы, 58 | оставляя всё остальное. 59 | После этого происходит переход к следующему этапу -- собственно разбору. 60 | Но перед этим поговорим о том, а что, собственно, мы хотим получить в результате разбора. 61 | 62 | === 2. Представление выражений 63 | 64 | Зададимся вопросом: как представить арифметическое выражение 65 | с помощью структур данных, имеющихся в Котлине? 66 | Перед ответом на этот вопрос стоит ответить на другой: 67 | а что такое (формально) арифметическое выражение в нашей задаче? 68 | В теории формальных языков ответ может выглядеть так: 69 | 70 | * это `x`, 71 | * или же, это целая константа, 72 | * или же, это операция (сложение / вычитание / умножение / деление) над двумя другими выражениями 73 | * или, наконец, это операция "минус выражение" над другим выражением 74 | 75 | Обратите внимание, что определение рекурсивно -- понятие выражения использует в процессе определения себя же. 76 | Это довольно частый случай для подобных формальных описаний. 77 | Подобная рекурсивная структура с вариантами в Котлине может быть описана 78 | так называемым __алгебраическим__ или __запечатанным__ (sealed) классом. 79 | 80 | [source,kotlin] 81 | ---- 82 | sealed class Expression { 83 | object Variable : Expression() 84 | 85 | class Constant(val value: Int) : Expression() 86 | 87 | enum class Operation { 88 | PLUS, 89 | MINUS, 90 | TIMES, 91 | DIV; 92 | } 93 | 94 | class Binary( 95 | val left: Expression, 96 | val op: Operation, 97 | val right: Expression 98 | ) : Expression() 99 | 100 | class Negate(val arg: Expression) : Expression() 101 | } 102 | ---- 103 | 104 | Алгебраический класс используется не сам по себе, а в виде его разновидностей, 105 | причём по правилам Котлина, для алгебраического класса эти разновидности должны быть описаны 106 | __внутри__ определения самого класса (для версии 1.1 и выше -- внутри того же файла). 107 | 108 | Для описания разновидностей `Expression` используется уже знакомый синтаксис `class Something(...) : Expression()`. 109 | Разновидности алгебраического класса содержат дополнительную информацию по сравнению с ним самим: 110 | 111 | * целая константа `Constant` содержит своё значение `value` 112 | * инвертированное выражение `Negate` содержит свой аргумент (например, для `-x` аргумент будет равен `x`) 113 | * выражение с двумя аргументами (бинарное) `Binary` содержит два своих аргумента и выполняемую операцию 114 | 115 | Обратите внимание на `enum class Operation`. 116 | Это не разновидность выражения (нет `: Expression()`), 117 | а просто дополнительный класс для описания операций с двумя аргументами. 118 | Слово `enum` означает __перечисление__ в том смысле, что существует ограниченное (в данном случае -- 4) 119 | количество операций -- сложение, умножение, вычитание, деление. 120 | Все операции перечисляются через запятую, в конце (необязательно) ставится точка с запятой. 121 | 122 | Последняя разновидность выражения `object Variable : Expression()`. 123 | В отличие от прочих разновидностей, эта является не классом, а __объектом__. 124 | В Котлине объект отличается от класса тем, 125 | что для данного типа существует __ровно один__ объект данного типа -- в данном случае это переменная `x`. 126 | У объекта нельзя вызывать конструкторы, а обращаются к нему просто по имени: в данном случае `Variable`. 127 | Если бы нас интересовали выражения с несколькими переменными, 128 | `Variable` тоже превратился бы в класс со свойством `name`: имя конкретной переменной. 129 | 130 | Подобное описание выражения позволяет с лёгкостью рассчитать его значение при заданном `x`: 131 | 132 | [source,kotlin] 133 | ---- 134 | fun Expression.calculate(x: Int): Int = when (this) { 135 | Variable -> x 136 | is Constant -> value 137 | is Binary -> when (op) { 138 | PLUS -> left.calculate(x) + right.calculate(x) 139 | MINUS -> left.calculate(x) - right.calculate(x) 140 | TIMES -> left.calculate(x) * right.calculate(x) 141 | DIV -> left.calculate(x) / right.calculate(x) 142 | } 143 | is Negate -> -arg.calculate(x) 144 | } 145 | ---- 146 | 147 | В данном `when` мы перебираем все возможные варианты выражения-получателя: 148 | 149 | * значение переменной равно заданному `x: Int` 150 | * значение константы равно её свойству `value` 151 | * значение `Negate` равно минус значению её выражения-аргумента 152 | * значение `Binary` рассчитывается аналогично с учётом конкретной операции 153 | 154 | === 3. Синтаксический анализ 155 | 156 | Этот этап иногда называется просто "разбор". 157 | Для его проведения, нам потребуется более подробное формальное описание выражения с учётом приоритетов операций. 158 | Скажем, операции в скобках всегда выполняются в первую очередь, 159 | умножение и деление -- во вторую, сложение и вычитание -- в третью. 160 | На основе этого описания можно составить __формальную грамматику__ выражения, выглядящую примерно так: 161 | 162 | * ВЫРАЖЕНИЕ ::= СЛАГАЕМОЕ +|- СЛАГАЕМОЕ +|- ... +|- СЛАГАЕМОЕ 163 | * СЛАГАЕМОЕ ::= МНОЖИТЕЛЬ *|/ МНОЖИТЕЛЬ *|/ ... *|/ МНОЖИТЕЛЬ 164 | * МНОЖИТЕЛЬ ::= (ВЫРАЖЕНИЕ) | -ВЫРАЖЕНИЕ | КОНСТАНТА | ПЕРЕМЕННАЯ 165 | 166 | То есть, выражение может включать в себя любое (в том числе одно) число слагаемых, 167 | разделённых операторами "плюс" и "минус". 168 | Слагаемое, в свою очередь, может включать в себя любое (в том числе одно) число множителей, 169 | разделённых операторами "умножить" и "разделить". 170 | Наконец, множитель -- это выражение в скобках, или с минусом впереди, или константа, или переменная. 171 | К примеру, в выражении `2 + 3 * x / (1 - x)` имеется два слагаемых -- `2` и `3 * x / (1 - x)`. 172 | Слагаемое `2` состоит из одного множителя, который, в свою очередь, является слагаемым. 173 | Второе слагаемое состоит из трёх множителей -- `3` (константа), `x` (переменная), `(1 - x)`. 174 | Последний из множителей, в свою очередь, является выражением `1 - x` в скобках, состоящим из двух слагаемых. 175 | 176 | Знак `::=` в этом определении приближённо означает "состоит из", то есть, мы описываем одно понятие через другие. 177 | А сама по себе формальная грамматика позволяет определить, является ли заданная строка выражением, 178 | а также разобраться, из каких частей это выражение состоит. Например: 179 | 180 | [source,kotlin] 181 | ---- 182 | class Parser(private val groups: List) { 183 | private var pos = 0 184 | 185 | fun parse(): Expression { 186 | val result = parseExpression() 187 | if (pos < groups.size) { 188 | throw IllegalStateException("Unexpected expression remainder: ${groups.subList(pos, groups.size)}") 189 | } 190 | return result 191 | } 192 | 193 | private fun parseExpression(): Expression { 194 | var left = parseItem() 195 | while (pos < groups.size) { 196 | val op = operationMap[groups[pos]] 197 | when (op) { 198 | PLUS, MINUS -> { 199 | pos++ 200 | val right = parseItem() 201 | left = Expression.Binary(left, op, right) 202 | } 203 | else -> return left 204 | } 205 | } 206 | return left 207 | } 208 | 209 | private val operationMap = mapOf("+" to PLUS, "-" to MINUS, "*" to TIMES, "/" to DIV) 210 | 211 | // ... 212 | } 213 | ---- 214 | 215 | Данный класс `Parser` (разборщик, или просто парсер) имеет свойство `groups` -- список атомарных частей строки, 216 | полученный при лексическом анализе, а также изменяемое свойство `pos` -- 217 | оно указывает, до какой из частей мы дошли в процессе разбора. 218 | Его функция `parse` осуществляет разбор всего выражения и проверяет, 219 | что была разобрана вся найденная строка и не осталось ничего в хвосте. 220 | 221 | Функция `parseExpression` соответствует определению выражения из грамматики. 222 | В соответствии с определением она разбирает первое слагаемое `parseItem`, 223 | затем, если найден плюс или минус -- второе, и составляет из них `Expression.Binary`. 224 | При наличии следующего плюса или минуса процедура повторяется. 225 | Для преобразования обозначения операции в объект перечисления `Operation` используется отображение `operationMap`. 226 | 227 | Результат функции `parseExpression`, как и других функций разбора -- `Expression`, 228 | то есть любая из описанных нами выше разновидностей алгебраического типа `Expression`. 229 | Обратите внимание, что для выражения с тремя и более слагаемыми, например, `2 + x - 1`, 230 | мы получим в итоге следующую структуру: `Binary(Binary(2, +, x), -, 1)`. 231 | То есть первым аргументом внешнего бинарного выражения `2 + x - 1`, 232 | в свою очередь, является бинарное выражение `2 + x`. 233 | 234 | Аналогичным образом устроена функция `parseItem`. 235 | Она соответствует определению слагаемого, для разбора множителей использует `parseFactor`, 236 | и в качестве разделителей ищет знаки умножения и деления: 237 | 238 | [source,kotlin] 239 | ---- 240 | class Parser(val groups: List) { 241 | var pos = 0 242 | 243 | // ... 244 | 245 | private fun parseItem(): Expression { 246 | var left = parseFactor() 247 | while (pos < groups.size) { 248 | val op = operationMap[groups[pos]] 249 | when (op) { 250 | TIMES, DIV -> { 251 | pos++ 252 | val right = parseFactor() 253 | left = Expression.Binary(left, op, right) 254 | } 255 | else -> return left 256 | } 257 | } 258 | return left 259 | } 260 | } 261 | ---- 262 | 263 | Наконец, разбор множителя `parseFactor` анализирует один из четырёх вариантов: 264 | выражение в скобках, выражение с минусом, константа, переменная. 265 | 266 | [source,kotlin] 267 | ---- 268 | class Parser(val groups: List) { 269 | var pos = 0 270 | 271 | // ... 272 | private fun parseFactor(): Expression = 273 | if (pos >= groups.size) throw IllegalStateException("Unexpected expression end") 274 | else { 275 | val group = groups[pos++] 276 | when (group) { 277 | "x" -> Expression.Variable 278 | "-" -> Expression.Negate(parseFactor()) 279 | "(" -> { 280 | val arg = parseExpression() 281 | val next = groups[pos++] 282 | if (next == ")") arg 283 | else throw IllegalStateException(") expected instead of $next") 284 | } 285 | else -> Expression.Constant(group.toInt()) 286 | } 287 | } 288 | } 289 | ---- 290 | 291 | Обратите внимание, что в процессе анализа выражения в скобках мы повторно вызываем `parseExpression`, 292 | то есть, налицо рекурсия. Глубина рекурсии зависит от количества вложенных скобок в выражении. 293 | 294 | В результате действия подобного парсера упомянутое выше выражение `2 + 3 * x / (1 - x)` 295 | превратится в структуру `Binary(2, +, Binary(Binary(3, *, x), /, Binary(1, - x)))`. 296 | 297 | === 4. Объединяем всё вместе 298 | 299 | Наконец, рассмотрим функцию, решающую исходную задачу. 300 | Пусть во входном файле содержится строчка, содержащая описание функции от `x`. 301 | Необходимо по имеющемуся списку значений `x` (например, 1, 2 и -1) 302 | рассчитать значения данной функции и вернуть их в виде ассоциативного массива. 303 | 304 | [source,kotlin] 305 | ---- 306 | fun parseExpr(inputName: String, values: List): Map { 307 | val expr = File(inputName).readLines().firstOrNull()?.parseExpr() ?: throw IllegalArgumentException() 308 | val result = mutableMapOf() 309 | for (value in values) { 310 | result[value] = expr.calculate(value) 311 | } 312 | return result 313 | } 314 | ---- 315 | 316 | Данная функция читает первую строку файла `inputName` и разбирает её с помощью `String.parseExpr()`. 317 | Затем для каждого из значений из списка `values` вызывается функция `Expression.calculate` 318 | и результат кладётся в ассоциативный массив `result`. Таким образом, задача решена. 319 | --------------------------------------------------------------------------------