├── gradle.properties ├── settings.gradle.kts ├── assets ├── algo_logo.png └── algo_ru.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── test │ └── kotlin │ │ ├── other │ │ ├── SqrtTest.kt │ │ ├── FactorialWithCacheTest.kt │ │ ├── FactorialBigWithCacheTest.kt │ │ ├── StringEqualsHashAlgorithmTest.kt │ │ ├── ReverseArrayAlgorithmTest.kt │ │ ├── LevenshteinLengthAlgorithmTest.kt │ │ ├── FizzBuzzTest.kt │ │ ├── MaxAlgorithmTest.kt │ │ ├── MinAlgorithmTest.kt │ │ ├── BinaryDigitsCounterTest.kt │ │ ├── FactorialTest.kt │ │ ├── SieveOfEratosthenesAlgorithmTest.kt │ │ ├── SwapAlgorithmTest.kt │ │ ├── EuclidAlgorithmTest.kt │ │ ├── ParenthesisCheckAlgorithmTest.kt │ │ ├── KnuthMorrisPrattAlgorithmTest.kt │ │ ├── PalindromeAlgorithmTest.kt │ │ └── FactorialAdvancedTest.kt │ │ ├── TestUtils.kt │ │ ├── design_patterns │ │ ├── DecoratorTest.kt │ │ ├── SingletonTest.kt │ │ ├── AdapterTest.kt │ │ ├── IteratorTest.kt │ │ ├── InterpreterTest.kt │ │ ├── ObserverTest.kt │ │ ├── MementoTest.kt │ │ ├── ProxyTest.kt │ │ ├── AbstractFactoryTest.kt │ │ ├── MediatorTest.kt │ │ ├── TemplateMethodTest.kt │ │ ├── FacadeTest.kt │ │ ├── FlyweightTest.kt │ │ ├── CompositeTest.kt │ │ ├── PrototypeTest.kt │ │ ├── ChainOfResponsibilitiesTest.kt │ │ ├── BridgeTest.kt │ │ ├── StateTest.kt │ │ ├── StrategyTest.kt │ │ ├── VisitorTest.kt │ │ ├── FactoryMethodTest.kt │ │ ├── CommandTest.kt │ │ └── BuilderTest.kt │ │ ├── search │ │ ├── LinearSearchTest.kt │ │ └── BinarySearchTest.kt │ │ ├── structures │ │ ├── Stack1Test.kt │ │ ├── Stack2Test.kt │ │ ├── QueueTest.kt │ │ ├── DynamicArrayTest.kt │ │ ├── MinHeapTest.kt │ │ ├── MaxHeapTest.kt │ │ ├── GraphTest.kt │ │ ├── GraphWithWeightsTest.kt │ │ └── SingleLinkedListTest.kt │ │ └── sorting │ │ ├── TimSortTest.kt │ │ ├── RadixSortTest.kt │ │ ├── QuickSortTest.kt │ │ ├── SelectionSortTest.kt │ │ ├── InsertionSortTest.kt │ │ ├── CountingSortTest.kt │ │ ├── BubbleSortTest.kt │ │ └── MergeSortTest.kt └── main │ └── kotlin │ ├── other │ ├── ReverseArrayAlgorithm.kt │ ├── Sqrt.kt │ ├── FizzBuzz.kt │ ├── FactorialWithCache.kt │ ├── PalindromeAlgorithm.kt │ ├── FactorialBigWithCache.kt │ ├── LevenshteinLengthAlgorithm.kt │ ├── SieveOfEratosthenesAlgorithm.kt │ ├── EuclidAlgorithm.kt │ ├── MinAlgorithm.kt │ ├── Factorial.kt │ ├── MaxAlgorithm.kt │ ├── StringEqualsHashAlgorithm.kt │ ├── ParenthesisCheckAlgorithm.kt │ ├── KnuthMorrisPrattAlgorithm.kt │ ├── BinaryDigitsCounter.kt │ └── SwapAlgorithm.kt │ ├── search │ ├── LinearSearch.kt │ └── BinarySearch.kt │ ├── design_patterns │ ├── Adapter.kt │ ├── Iterator.kt │ ├── Abstract Factory.kt │ ├── Singleton.kt │ ├── Observer.kt │ ├── Memento.kt │ ├── Template Method.kt │ ├── Flyweight.kt │ ├── Decorator.kt │ ├── Сhain Of Responsibilities.kt │ ├── Composite.kt │ ├── Interpreter.kt │ ├── Prototype.kt │ ├── Strategy.kt │ ├── Factory Method.kt │ ├── State.kt │ ├── Bridge.kt │ ├── Facade.kt │ ├── Mediator.kt │ ├── Proxy.kt │ ├── Visitor.kt │ ├── Command.kt │ └── Builder.kt │ ├── sorting │ ├── InsertionSort.kt │ ├── SelectionSort.kt │ ├── RadixSort.kt │ ├── CountingSort.kt │ ├── QuickSort.kt │ ├── BubbleSort.kt │ ├── MergeSort.kt │ └── TimSort.kt │ └── structures │ ├── Queue.kt │ ├── Stack1.kt │ ├── Stack2.kt │ ├── MaxHeap.kt │ ├── MinHeap.kt │ ├── Graph.kt │ ├── DynamicArray.kt │ ├── GraphWithWeights.kt │ └── Matrix.kt ├── LICENSE.txt ├── .gitignore └── gradlew.bat /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "Kotlin-Algorithms-and-Design-Patterns" -------------------------------------------------------------------------------- /assets/algo_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DmitryTsyvtsyn/Kotlin-Algorithms-and-Design-Patterns/HEAD/assets/algo_logo.png -------------------------------------------------------------------------------- /assets/algo_ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DmitryTsyvtsyn/Kotlin-Algorithms-and-Design-Patterns/HEAD/assets/algo_ru.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DmitryTsyvtsyn/Kotlin-Algorithms-and-Design-Patterns/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/test/kotlin/other/SqrtTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class SqrtTest { 7 | 8 | @Test 9 | fun test() { 10 | val sqrt = Sqrt() 11 | assertEquals(3.0, sqrt.compute(9.0), 0.0) 12 | assertEquals(2.0, sqrt.compute(4.0), 0.0) 13 | assertEquals(6.0, sqrt.compute(36.0), 0.0) 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/test/kotlin/TestUtils.kt: -------------------------------------------------------------------------------- 1 | 2 | import kotlin.random.Random 3 | 4 | object TestUtils { 5 | 6 | fun randomArray(size: Int) = List(size) { Random.nextInt(100) }.toTypedArray() 7 | fun sortedArray(size: Int) = List(size) { it }.toTypedArray() 8 | 9 | fun mutableRandomList(size: Int) = List(size) { Random.nextInt(100) }.toMutableList() 10 | fun list(size: Int) = List(size) { it } 11 | 12 | 13 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/FactorialWithCacheTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class FactorialWithCacheTest { 7 | 8 | private val factorial = FactorialWithCache() 9 | 10 | @Test 11 | fun test() { 12 | assertEquals(6, factorial.compute(3)) 13 | assertEquals(120, factorial.compute(5)) 14 | assertEquals(720, factorial.compute(6)) 15 | assertEquals(3628800, factorial.compute(10)) 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/ReverseArrayAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for reversing the order of an array 6 | * 7 | * complexity: O(n/2) + O(n) 8 | * 9 | */ 10 | 11 | class ReverseArrayAlgorithm { 12 | 13 | fun compute(array: Array) { 14 | val size = array.size 15 | for (index in 0 until size / 2) { 16 | array[index] = array[size - index - 1].apply { 17 | array[size - index - 1] = array[index] 18 | } 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/search/LinearSearch.kt: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | /** 4 | * 5 | * Linear search algorithm 6 | * 7 | * best time: 1 8 | * worst time: n 9 | * amount of memory: 1 10 | * 11 | */ 12 | 13 | class LinearSearch { 14 | 15 | fun > search(array: Array, element: T) : Int { 16 | var index = 0 17 | while (index < array.size) { 18 | if (element == array[index]) { 19 | return index 20 | } 21 | index++ 22 | } 23 | return -1 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/DecoratorTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class DecoratorTest { 7 | 8 | @Test 9 | fun test() { 10 | val printer = ExclamationPrinter( 11 | WorldPrinter( 12 | SpacePrinter( 13 | CommaPrinter( 14 | HelloPrinter() 15 | ) 16 | ) 17 | ) 18 | ) 19 | assertEquals("Hello, World!", printer.printedText()) 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/SingletonTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class SingletonTest { 7 | 8 | @Test 9 | fun test() { 10 | // we have one instance of SQLiteDatabase class 11 | SQLiteDatabase.openConnection() 12 | 13 | val actual = SQLiteDatabase.execSQL("select * from names") 14 | val expected = listOf("Rick", "Morty", "Jerry", "Beth") 15 | 16 | assertEquals(expected, actual) 17 | 18 | SQLiteDatabase.closeConnection() 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/Sqrt.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for finding the square root of a number 6 | * 7 | * read the wikipedia article: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Heron's_method 8 | * 9 | */ 10 | 11 | class Sqrt { 12 | 13 | // calculates the root of a number and returns result 14 | fun compute(number: Double) : Double { 15 | var value = number / 2 16 | // the number of loop iterations is selected depending on the required accuracy 17 | for (i in 1..100) { 18 | value = (value + number / value) / 2 19 | } 20 | return value 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/AdapterTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class AdapterTest { 7 | 8 | @Test 9 | fun test() { 10 | // we don't understand EnglishSpeaker interface 11 | val englishSpeaker = EnglishSpeakerImpl() 12 | // but we know about SpainSpeaker interface 13 | val speakerAdapter = SpainSpeakerAdapter(englishSpeaker) 14 | 15 | // EnglishSpeaker interface was changed to SpainSpeaker interface 16 | val actual = speakerAdapter.speakSpanish() 17 | 18 | assertEquals("Hola, amigo!", actual) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/IteratorTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class IteratorTest { 7 | 8 | @Test 9 | fun test() { 10 | val presents = arrayOf("chocolate cake", "snowboard", "snowman") 11 | val giftBox = GiftBox(presents) 12 | val giftBoxIterator = giftBox.iterator() 13 | val actualPresents = mutableListOf() 14 | while (giftBoxIterator.hasNext()) { 15 | val present = giftBoxIterator.next() 16 | actualPresents.add(present) 17 | } 18 | assertEquals(presents.toList(), actualPresents) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/FactorialBigWithCacheTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class FactorialBigWithCacheTest { 7 | 8 | private val factorial = FactorialBigWithCache() 9 | 10 | @Test 11 | fun test() { 12 | assertEquals("30414093201713378043612608166064768844377641568960512000000000000", factorial.compute(50).toString()) 13 | assertEquals("265252859812191058636308480000000", factorial.compute(30).toString()) 14 | assertEquals("11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000", factorial.compute(70).toString()) 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/StringEqualsHashAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class StringEqualsHashAlgorithmTest { 7 | 8 | private val stringEqualsHashAlgorithm = StringEqualsHashAlgorithm() 9 | 10 | @Test 11 | fun test_two_the_same_strings() { 12 | val str1 = "Twilight Sparkle" 13 | val str2 = "Twilight Sparkle" 14 | 15 | assertEquals(true, stringEqualsHashAlgorithm.equals(str1, str2)) 16 | } 17 | 18 | @Test 19 | fun test_two_different_strings() { 20 | val greeting = "How are you?" 21 | val pattern = "Happy birthday to me!" 22 | 23 | assertEquals(false, stringEqualsHashAlgorithm.equals(greeting, pattern)) 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Adapter.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Adapter is a structural design pattern that allows objects with incompatible interfaces to work together 6 | * 7 | */ 8 | 9 | interface EnglishSpeaker { 10 | fun speakEnglish(): String 11 | } 12 | 13 | class EnglishSpeakerImpl : EnglishSpeaker { 14 | override fun speakEnglish(): String { 15 | return "Hello, friend!" 16 | } 17 | } 18 | 19 | interface SpainSpeaker { 20 | fun speakSpanish(): String 21 | } 22 | 23 | class SpainSpeakerAdapter(private val englishSpeaker: EnglishSpeaker) : SpainSpeaker { 24 | 25 | override fun speakSpanish(): String = 26 | when (englishSpeaker.speakEnglish()) { 27 | "Hello, friend!" -> "Hola, amigo!" 28 | else -> "No te entiendo" 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/InterpreterTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class InterpreterTest { 7 | 8 | @Test 9 | fun test() { 10 | // x = 10, y = 20, d = 5 11 | // sum = x + y 12 | // result = sum * d 13 | val expression = PerformExpression( 14 | SetIntVariableExpression("x", 10), 15 | SetIntVariableExpression("y", 20), 16 | AddVariablesExpression("x", "y", "sum"), 17 | SetIntVariableExpression("d", 5), 18 | MultipleVariablesExpression("sum", "d", "result") 19 | ) 20 | 21 | val context = InterpreterContext() 22 | 23 | expression.interpret(context) 24 | 25 | assertEquals(150, context.fetchVariable("result")) 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/FizzBuzz.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * The simplest FizzBuzz game 5 | * 6 | * description: the player says numbers in sequence, provided: 7 | * - if the number is divisible by 3 it says fizz 8 | * - if the number is divisible by 5 it says buzz 9 | * - if the number is divisible by both 3 and 5, it says FizzBuzz 10 | * - otherwise, the player calls the number itself 11 | */ 12 | 13 | class FizzBuzz { 14 | 15 | // determines what to say for a number in the FizzBuzz game and returns Fizz, Buzz, FizzBuzz or number 16 | fun compute(number: Int) : String { 17 | return when { 18 | number % 3 == 0 && number % 5 == 0 -> "FizzBuzz" 19 | number % 3 == 0 -> "Fizz" 20 | number % 5 == 0 -> "Buzz" 21 | else -> number.toString() 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/FactorialWithCache.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for finding the factorial of a positive number n 6 | * 7 | * optimization: caching previous factorial values 8 | * 9 | * worst time: O(n) 10 | * the best time: O(1) 11 | * amount of memory: O(n) 12 | * 13 | */ 14 | 15 | class FactorialWithCache { 16 | 17 | private val cache = hashMapOf() 18 | 19 | fun compute(number: Int) : Int { 20 | if (number <= 1) { 21 | return 1 22 | } 23 | 24 | val cachedResult = cache[number] 25 | if (cachedResult != null) { 26 | return cachedResult 27 | } 28 | 29 | var result = 1 30 | for (i in 2..number) { 31 | result *= i 32 | cache[i] = result 33 | } 34 | return result 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /src/test/kotlin/search/LinearSearchTest.kt: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class LinearSearchTest { 7 | 8 | @Test 9 | fun test() { 10 | val searchAlgo = LinearSearch() 11 | 12 | val array = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 13 | 14 | assertEquals(-1, searchAlgo.search(emptyArray(), 0)) 15 | assertEquals(-1, searchAlgo.search(array, 0)) 16 | assertEquals(-1, searchAlgo.search(array, 11)) 17 | assertEquals(0, searchAlgo.search(array, 1)) 18 | assertEquals(2, searchAlgo.search(array, 3)) 19 | assertEquals(4, searchAlgo.search(array, 5)) 20 | assertEquals(6, searchAlgo.search(array, 7)) 21 | assertEquals(8, searchAlgo.search(array, 9)) 22 | assertEquals(9, searchAlgo.search(array, 10)) 23 | } 24 | 25 | 26 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/ReverseArrayAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import junit.framework.TestCase.assertEquals 5 | 6 | internal class ReverseArrayAlgorithmTest { 7 | 8 | private val reverseArrayAlgorithm = ReverseArrayAlgorithm() 9 | 10 | @Test 11 | fun `test numbers`() { 12 | val array = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 13 | 14 | reverseArrayAlgorithm.compute(array) 15 | 16 | assertEquals(listOf(10, 9, 8, 7, 6, 5, 4, 3, 2, 1), array.toList()) 17 | } 18 | 19 | @Test 20 | fun `test strings`() { 21 | val array = arrayOf("Twillight Sparkle", "Pinky Pie", "Apple Jack", "Rainbow Dash", "Fluttershy", "Rarity") 22 | 23 | reverseArrayAlgorithm.compute(array) 24 | 25 | assertEquals(listOf("Rarity", "Fluttershy", "Rainbow Dash", "Apple Jack", "Pinky Pie", "Twillight Sparkle"), array.toList()) 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/LevenshteinLengthAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class LevenshteinLengthAlgorithmTest { 7 | 8 | @Test 9 | fun test() { 10 | val levenshteinLengthAlgorithm = LevenshteinLengthAlgorithm() 11 | 12 | val str1 = "hello" 13 | val str2 = "hello," 14 | assertEquals(1, levenshteinLengthAlgorithm.compute(str1, str2)) 15 | 16 | val str3 = "hello, world!" 17 | val str4 = "hello, friend" 18 | assertEquals(6, levenshteinLengthAlgorithm.compute(str3, str4)) 19 | 20 | assertEquals(0, levenshteinLengthAlgorithm.compute(str1, str1)) 21 | assertEquals(0, levenshteinLengthAlgorithm.compute(str2, str2)) 22 | assertEquals(0, levenshteinLengthAlgorithm.compute(str3, str3)) 23 | assertEquals(0, levenshteinLengthAlgorithm.compute(str4, str4)) 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/PalindromeAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for checking a string for a palindrome 6 | * 7 | * P.S. A palindrome is a word, number, phrase, or other sequence of symbols 8 | * that reads the same backwards as forwards, such as "madam" 9 | */ 10 | 11 | class PalindromeAlgorithm { 12 | 13 | // checks a string for a palindrome and returns true if the string is a palindrome 14 | fun isPalindrome(text: String): Boolean { 15 | if (text.length <= 1) { 16 | return true 17 | } 18 | 19 | val length = text.length 20 | for (index in 0 until length / 2) { 21 | if (text[index] != text[length - index - 1]) { 22 | return false 23 | } 24 | } 25 | 26 | return true 27 | } 28 | 29 | fun isPalindromeSimplifiedVersion(text: String): Boolean { 30 | return text == text.reversed() 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/FizzBuzzTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class FizzBuzzTest { 7 | 8 | private val fizzBuzz = FizzBuzz() 9 | 10 | @Test 11 | fun test_fizz() { 12 | val actual = fizzBuzz.compute(3) 13 | val expected = "Fizz" 14 | assertEquals(expected, actual) 15 | } 16 | 17 | @Test 18 | fun test_buzz() { 19 | val actual = fizzBuzz.compute(5) 20 | val expected = "Buzz" 21 | assertEquals(expected, actual) 22 | } 23 | 24 | @Test 25 | fun test_fizz_buzz() { 26 | val actual = fizzBuzz.compute(15) 27 | val expected = "FizzBuzz" 28 | assertEquals(expected, actual) 29 | } 30 | 31 | @Test 32 | fun test_no_fizz_buzz() { 33 | val actual = fizzBuzz.compute(1) 34 | val expected = "1" 35 | assertEquals(expected, actual) 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/sorting/InsertionSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | /** 4 | * 5 | * Insertion sort is a simple sorting algorithm that works similar to the way you sort playing cards in your hands. 6 | * 7 | * The array is virtually split into a sorted and an unsorted part. 8 | * 9 | * Values from the unsorted part are picked and placed at the correct position in the sorted part. 10 | * 11 | * worst time: n² 12 | * best time: n 13 | * average time: n² 14 | * 15 | * amount of time: 1 16 | * 17 | */ 18 | 19 | class InsertionSort { 20 | 21 | fun > sort(array: Array) { 22 | val arraySize = array.size 23 | for (i in 1 until arraySize) { 24 | val current = array[i] 25 | var j = i - 1 26 | while (j >= 0 && array[j] > current) { 27 | array[j + 1] = array[j] 28 | j-- 29 | } 30 | array[j + 1] = current 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/ObserverTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class ObserverTest { 7 | 8 | @Test 9 | fun test() { 10 | val ponyList = PonyList() 11 | 12 | val observer1 = PonyObserver { items -> 13 | assertEquals(listOf("Twillight Sparkle"), items) 14 | } 15 | ponyList.addObserver(observer1) 16 | // we add the first item, observer1 is triggered 17 | ponyList.add("Twillight Sparkle") 18 | // we remove observer1 so that it is not called again 19 | ponyList.removeObserver(observer1) 20 | 21 | val observer2 = PonyObserver { items -> 22 | assertEquals(listOf("Twillight Sparkle", "Starlight Glimmer"), items) 23 | } 24 | ponyList.addObserver(observer2) 25 | // we add the second item, observer2 is triggered 26 | ponyList.add("Starlight Glimmer") 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/Stack1Test.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class Stack1Test { 7 | 8 | @Test 9 | fun test() { 10 | val stack = Stack1() 11 | 12 | stack.push(1) 13 | stack.push(2) 14 | stack.push(3) 15 | 16 | assertEquals(false, stack.isEmpty) 17 | assertEquals(3, stack.size) 18 | 19 | assertEquals(3, stack.pop()) 20 | assertEquals(2, stack.pop()) 21 | assertEquals(1, stack.pop()) 22 | 23 | assertEquals(true, stack.isEmpty) 24 | assertEquals(0, stack.size) 25 | 26 | stack.push(10) 27 | stack.push(20) 28 | stack.push(30) 29 | 30 | assertEquals(3, stack.size) 31 | assertEquals(30, stack.peek()) 32 | assertEquals(3, stack.size) 33 | 34 | stack.clear() 35 | 36 | assertEquals(true, stack.isEmpty) 37 | assertEquals(0, stack.size) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/Stack2Test.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class Stack2Test { 7 | 8 | @Test 9 | fun test() { 10 | val stack = Stack2() 11 | 12 | stack.push(1) 13 | stack.push(2) 14 | stack.push(3) 15 | 16 | assertEquals(false, stack.isEmpty) 17 | assertEquals(3, stack.size) 18 | 19 | assertEquals(3, stack.pop()) 20 | assertEquals(2, stack.pop()) 21 | assertEquals(1, stack.pop()) 22 | 23 | assertEquals(true, stack.isEmpty) 24 | assertEquals(0, stack.size) 25 | 26 | stack.push(10) 27 | stack.push(20) 28 | stack.push(30) 29 | 30 | assertEquals(3, stack.size) 31 | assertEquals(30, stack.peek()) 32 | assertEquals(3, stack.size) 33 | 34 | stack.clear() 35 | 36 | assertEquals(true, stack.isEmpty) 37 | assertEquals(0, stack.size) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Iterator.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Iterator is a behavioral design pattern that provides a mechanism for sequentially enumerating the elements 6 | * 7 | * of a collection without revealing its internal representation. 8 | * 9 | */ 10 | 11 | interface BoxIterator { 12 | fun hasNext(): Boolean 13 | fun next(): T 14 | } 15 | 16 | interface BoxIterable { 17 | fun iterator(): BoxIterator 18 | } 19 | 20 | class GiftBox(private val presents: Array) : BoxIterable { 21 | 22 | override fun iterator() = object : BoxIterator { 23 | private var current = -1 24 | 25 | override fun next(): String { 26 | return presents[current] 27 | } 28 | 29 | override fun hasNext(): Boolean { 30 | if (current < presents.size - 1) { 31 | current++ 32 | return true 33 | } 34 | return false 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Abstract Factory.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Abstract factory is a generative design pattern that allows 6 | * 7 | * you to create families of related objects without being tied to 8 | * 9 | * the specific classes of objects you create. 10 | * 11 | */ 12 | 13 | interface Button { 14 | fun draw() {} 15 | } 16 | 17 | class AndroidButton : Button 18 | class IOSButton : Button 19 | 20 | interface Text { 21 | fun draw() {} 22 | } 23 | 24 | class AndroidText : Text 25 | class IOSText : Text 26 | 27 | interface ButtonFactory { 28 | fun createButton() : Button 29 | fun createText() : Text 30 | } 31 | 32 | class AndroidButtonFactory : ButtonFactory { 33 | override fun createButton() : Button = AndroidButton() 34 | override fun createText(): Text = AndroidText() 35 | } 36 | 37 | class IOSButtonFactory : ButtonFactory { 38 | override fun createButton() : Button = IOSButton() 39 | override fun createText(): Text = IOSText() 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/sorting/SelectionSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | /** 4 | * 5 | * Selection sort is a simple and efficient sorting algorithm that works by repeatedly selecting 6 | * 7 | * the smallest (or largest) element from the unsorted portion of the list and 8 | * 9 | * moving it to the sorted portion of the list. 10 | * 11 | * worst time: n² 12 | * best time: n² 13 | * average time: n² 14 | * 15 | * amount of memory: 1 16 | * 17 | */ 18 | 19 | class SelectionSort { 20 | 21 | fun > sort(array: Array) { 22 | val arraySize = array.size 23 | for (i in 0 until arraySize - 1) { 24 | var min = i 25 | for (j in i + 1 until arraySize) { 26 | if (array[min] > array[j]) { 27 | min = j 28 | } 29 | } 30 | if (min != i) { 31 | val tmp = array[i] 32 | array[i] = array[min] 33 | array[min] = tmp 34 | } 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/other/FactorialBigWithCache.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import java.math.BigInteger 4 | 5 | /** 6 | * 7 | * Algorithm for finding the factorial of a positive number n 8 | * 9 | * optimization: caching previous factorial values 10 | * 11 | * also adding large numbers 12 | * 13 | * worst time: O(n) 14 | * the best time: O(1) 15 | * amount of memory: O(n) 16 | * problem: creating a huge number of BigInteger objects 17 | * 18 | */ 19 | 20 | class FactorialBigWithCache { 21 | 22 | private val cache = hashMapOf() 23 | 24 | fun compute(number: Int) : BigInteger { 25 | if (number <= 1) { 26 | return BigInteger.ONE 27 | } 28 | 29 | val cachedResult = cache[number] 30 | if (cachedResult != null) { 31 | return cachedResult 32 | } 33 | 34 | var result = BigInteger.ONE 35 | for (i in 2..number) { 36 | result = result.multiply(i.toBigInteger()) 37 | cache[i] = result 38 | } 39 | return result 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/LevenshteinLengthAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import java.lang.Integer.min 4 | 5 | /** 6 | * 7 | * Algorithm for determining the Levenshtein distance 8 | * 9 | */ 10 | 11 | class LevenshteinLengthAlgorithm { 12 | 13 | fun compute(str1: String, str2: String) : Int { 14 | val matrix = Array(str1.length + 1) { 15 | Array(str2.length + 1) { 0 } 16 | } 17 | 18 | for (i in 0..str1.length) { 19 | matrix[i][0] = i 20 | } 21 | 22 | for (i in 0..str2.length) { 23 | matrix[0][i] = i 24 | } 25 | 26 | for (i in 1..str1.length) { 27 | for (j in 1..str2.length) { 28 | if (str1[i - 1] == str2[j - 1]) { 29 | matrix[i][j] = matrix[i - 1][j - 1] 30 | } else { 31 | matrix[i][j] = min(min(matrix[i - 1][j], matrix[i][j - 1]), matrix[i - 1][j - 1]) + 1 32 | } 33 | } 34 | } 35 | 36 | return matrix[str1.length][str2.length] 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/SieveOfEratosthenesAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * The sieve of Eratosthenes allows you to efficiently calculate a series of prime numbers 6 | * 7 | * algorithm complexity: nlog(logn) operations 8 | * 9 | */ 10 | 11 | class SieveOfEratosthenesAlgorithm { 12 | 13 | // computes a series of primes up to the maximum value and returns a list of prime numbers 14 | fun compute(maxNumber: Int) : List { 15 | val numbers = Array(maxNumber + 1) { index -> index >= 2 } 16 | 17 | var x = 2 18 | while (x * x <= maxNumber) { 19 | if (numbers[x]) { 20 | for (y in x * x..maxNumber step x) { 21 | numbers[y] = false 22 | } 23 | } 24 | x++ 25 | } 26 | 27 | val primes = mutableListOf() 28 | 29 | numbers.forEachIndexed { number, isPrime -> 30 | if (isPrime) { 31 | primes.add(number) 32 | } 33 | } 34 | 35 | return primes 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/MementoTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class MementoTest { 7 | 8 | @Test 9 | fun test() { 10 | // start Android system 11 | val androidOS = AndroidSystem() 12 | 13 | val greetingText = "Hello, World!" 14 | val greetingView = TextView() 15 | greetingView.setText(greetingText) 16 | greetingView.draw() 17 | 18 | // rotating Android device (recreating application components) 19 | // Android system saves the states of running applications 20 | androidOS.saveBundle(greetingView.onSaveInstanceState()) 21 | 22 | // the state of the text was lost, but we saved it 23 | greetingView.setText("") 24 | 25 | // Android device has already rotated 26 | // The system restores the states of running applications 27 | greetingView.onRestoreInstanceState(androidOS.restoreBundle()) 28 | 29 | assertEquals(greetingText, greetingView.text()) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Singleton.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Singleton is a generative design pattern that guarantees the existence of one instance of a class 6 | * 7 | * and provides a global access point to it 8 | * 9 | */ 10 | 11 | object SQLiteDatabase { 12 | 13 | // SQLiteDatabase instance state 14 | private var connectionId = -1 15 | 16 | // These methods provide a global access point to SQLiteDatabase instance state 17 | fun openConnection() { 18 | if (connectionId < 0) { 19 | // open connection... 20 | connectionId = 1 21 | } 22 | } 23 | 24 | fun execSQL(sql: String): List { 25 | if (connectionId < 0) return emptyList() 26 | return when (sql) { 27 | "select * from names" -> listOf("Rick", "Morty", "Jerry", "Beth") 28 | else -> emptyList() 29 | } 30 | } 31 | 32 | fun closeConnection() { 33 | if (connectionId > 0) { 34 | // close connection... 35 | connectionId = -1 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/TimSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import kotlin.random.Random 5 | import org.junit.Assert.assertArrayEquals 6 | 7 | class TimSortTest { 8 | 9 | @Test 10 | fun `test sort`() { 11 | val timSort = TimSort() 12 | 13 | val expected1 = Array(1_000_000) { it } 14 | val actual1 = expected1.reversedArray() 15 | timSort.sort(actual1) 16 | assertArrayEquals(expected1, actual1) 17 | 18 | val actual2 = Array(1_000_000) { Random.nextInt(1_000_000) } 19 | val expected2 = actual2.sortedArray() 20 | timSort.sort(actual2) 21 | assertArrayEquals(expected2, actual2) 22 | 23 | val expected3 = Array(1_000_000) { it } 24 | val actual3 = expected3.copyOf() 25 | actual3.shuffle() 26 | timSort.sort(actual3) 27 | assertArrayEquals(expected3, actual3) 28 | 29 | val expected4 = Array(1_000_000) { it } 30 | val actual4 = expected3.copyOf() 31 | timSort.sort(actual3) 32 | assertArrayEquals(expected4, actual4) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/EuclidAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Euclid's algorithm for finding the greatest common divisor 6 | * 7 | */ 8 | 9 | class EuclidAlgorithm { 10 | 11 | fun computeByDivisionWithRemainder(number1: Int, number2: Int) : Int { 12 | var copyNumber1 = number1 13 | var copyNumber2 = number2 14 | while (copyNumber1 != 0 && copyNumber2 != 0) { 15 | if (copyNumber1 > copyNumber2) { 16 | copyNumber1 %= copyNumber2 17 | } else { 18 | copyNumber2 %= copyNumber1 19 | } 20 | } 21 | return copyNumber1 + copyNumber2 22 | } 23 | 24 | fun computeBySubtraction(number1: Int, number2: Int): Int { 25 | var copyNumber1 = number1 26 | var copyNumber2 = number2 27 | while (copyNumber1 != copyNumber2) { 28 | if (copyNumber1 > copyNumber2) { 29 | copyNumber1 -= copyNumber2 30 | } else { 31 | copyNumber2 -= copyNumber1 32 | } 33 | } 34 | return copyNumber1 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/RadixSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertArrayEquals 5 | import kotlin.random.Random 6 | 7 | class RadixSortTest { 8 | 9 | @Test 10 | fun test() { 11 | val radixSort = RadixSort() 12 | 13 | val expected1 = Array(1_000_000) { it } 14 | val actual1 = expected1.reversedArray() 15 | radixSort.sort(actual1) 16 | assertArrayEquals(expected1, actual1) 17 | 18 | val actual2 = Array(1_000_000) { Random.nextInt(1_000_000) } 19 | val expected2 = actual2.sortedArray() 20 | radixSort.sort(actual2) 21 | assertArrayEquals(expected2, actual2) 22 | 23 | val expected3 = Array(1_000_000) { it } 24 | val actual3 = expected3.copyOf() 25 | actual3.shuffle() 26 | radixSort.sort(actual3) 27 | assertArrayEquals(expected3, actual3) 28 | 29 | val expected4 = Array(1_000_000) { it } 30 | val actual4 = expected3.copyOf() 31 | radixSort.sort(actual3) 32 | assertArrayEquals(expected4, actual4) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/QuickSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import kotlin.random.Random 5 | import org.junit.Assert.assertArrayEquals 6 | 7 | class QuickSortTest { 8 | 9 | @Test 10 | fun `test sort`() { 11 | val quickSort = QuickSort() 12 | 13 | val expected1 = Array(1_000_000) { it } 14 | val actual1 = expected1.reversedArray() 15 | quickSort.sort(actual1) 16 | assertArrayEquals(expected1, actual1) 17 | 18 | val actual2 = Array(1_000_000) { Random.nextInt(1_000_000) } 19 | val expected2 = actual2.sortedArray() 20 | quickSort.sort(actual2) 21 | assertArrayEquals(expected2, actual2) 22 | 23 | val expected3 = Array(1_000_000) { it } 24 | val actual3 = expected3.copyOf() 25 | actual3.shuffle() 26 | quickSort.sort(actual3) 27 | assertArrayEquals(expected3, actual3) 28 | 29 | val expected4 = Array(1_000_000) { it } 30 | val actual4 = expected3.copyOf() 31 | quickSort.sort(actual3) 32 | assertArrayEquals(expected4, actual4) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/SelectionSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertArrayEquals 5 | import kotlin.random.Random 6 | 7 | class SelectionSortTest { 8 | 9 | @Test 10 | fun test() { 11 | val selectionSort = SelectionSort() 12 | 13 | val expected1 = Array(1000) { it } 14 | val actual1 = expected1.reversedArray() 15 | selectionSort.sort(actual1) 16 | assertArrayEquals(expected1, actual1) 17 | 18 | val actual2 = Array(1000) { Random.nextInt(1000) } 19 | val expected2 = actual2.sortedArray() 20 | selectionSort.sort(actual2) 21 | assertArrayEquals(expected2, actual2) 22 | 23 | val expected3 = Array(1000) { it } 24 | val actual3 = expected3.copyOf() 25 | actual3.shuffle() 26 | selectionSort.sort(actual3) 27 | assertArrayEquals(expected3, actual3) 28 | 29 | val expected4 = Array(1000) { it } 30 | val actual4 = expected3.copyOf() 31 | selectionSort.sort(actual3) 32 | assertArrayEquals(expected4, actual4) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/MinAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import java.lang.IllegalArgumentException 4 | 5 | /** 6 | * 7 | * Algorithm for finding the minimum value from a list 8 | * 9 | */ 10 | 11 | class MinAlgorithm { 12 | 13 | fun > compute(items: List) : T { 14 | if (items.isEmpty()) { 15 | throw IllegalArgumentException("items list is empty!") 16 | } 17 | var min = items[0] 18 | for (i in 1 until items.size) { 19 | if (min > items[i]) { 20 | min = items[i] 21 | } 22 | } 23 | return min 24 | } 25 | 26 | fun > computeRecursive(items: List) : T { 27 | if (items.isEmpty()) { 28 | throw IllegalArgumentException("items list is empty!") 29 | } 30 | if (items.size == 1) { 31 | return items.first() 32 | } 33 | val first = items.first() 34 | val others = items.subList(1, items.size) 35 | 36 | val min = computeRecursive(others) 37 | 38 | return if (first < min) first else min 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/InsertionSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertArrayEquals 5 | import kotlin.random.Random 6 | 7 | internal class InsertionSortTest { 8 | 9 | @Test 10 | fun `test sort`() { 11 | val insertionSort = InsertionSort() 12 | 13 | val expected1 = Array(1000) { it } 14 | val actual1 = expected1.reversedArray() 15 | insertionSort.sort(actual1) 16 | assertArrayEquals(expected1, actual1) 17 | 18 | val actual2 = Array(1000) { Random.nextInt(1000) } 19 | val expected2 = actual2.sortedArray() 20 | insertionSort.sort(actual2) 21 | assertArrayEquals(expected2, actual2) 22 | 23 | val expected3 = Array(1000) { it } 24 | val actual3 = expected3.copyOf() 25 | actual3.shuffle() 26 | insertionSort.sort(actual3) 27 | assertArrayEquals(expected3, actual3) 28 | 29 | val expected4 = Array(1000) { it } 30 | val actual4 = expected3.copyOf() 31 | insertionSort.sort(actual3) 32 | assertArrayEquals(expected4, actual4) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Dmitry Tsyvtsyn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/test/kotlin/other/MaxAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class MaxAlgorithmTest { 7 | 8 | private val maxAlgorithm = MaxAlgorithm() 9 | 10 | @Test 11 | fun `test iterative algorithm`() { 12 | val actual1 = maxAlgorithm.compute(listOf(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)) 13 | assertEquals(10, actual1) 14 | 15 | val actual2 = maxAlgorithm.compute(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) 16 | assertEquals(10, actual2) 17 | 18 | val actual3 = maxAlgorithm.compute(listOf(-50, 10, 6, -100, -9, 110)) 19 | assertEquals(110, actual3) 20 | } 21 | 22 | @Test 23 | fun `test recursive algorithm`() { 24 | val actual1 = maxAlgorithm.computeRecursive(listOf(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)) 25 | assertEquals(10, actual1) 26 | 27 | val actual2 = maxAlgorithm.computeRecursive(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) 28 | assertEquals(10, actual2) 29 | 30 | val actual3 = maxAlgorithm.computeRecursive(listOf(-50, 10, 6, -100, -9, 110)) 31 | assertEquals(110, actual3) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/MinAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class MinAlgorithmTest { 7 | 8 | private val minAlgorithm = MinAlgorithm() 9 | 10 | @Test 11 | fun `test iterative algorithm`() { 12 | val actual1 = minAlgorithm.compute(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) 13 | assertEquals(1, actual1) 14 | 15 | val actual2 = minAlgorithm.compute(listOf(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)) 16 | assertEquals(1, actual2) 17 | 18 | val actual3 = minAlgorithm.compute(listOf(-50, 10, 6, -100, -9, 110)) 19 | assertEquals(-100, actual3) 20 | } 21 | 22 | @Test 23 | fun `test recursive algorithm`() { 24 | val actual1 = minAlgorithm.computeRecursive(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) 25 | assertEquals(1, actual1) 26 | 27 | val actual2 = minAlgorithm.computeRecursive(listOf(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)) 28 | assertEquals(1, actual2) 29 | 30 | val actual3 = minAlgorithm.computeRecursive(listOf(-50, 10, 6, -100, -9, 110)) 31 | assertEquals(-100, actual3) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/ProxyTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class ProxyTest { 7 | 8 | @Test 9 | fun test() { 10 | // we use the proxy object instead of a real one 11 | val mediaPlayer = AudioPlayerProxy() 12 | 13 | mediaPlayer.play("track_0.mp3") 14 | assertEquals(""" 15 | current audio: track_0.mp3 16 | current status: RUNNING 17 | """.trimIndent(), mediaPlayer.currentState) 18 | 19 | mediaPlayer.pause() 20 | assertEquals(""" 21 | current audio: track_0.mp3 22 | current status: PAUSED 23 | """.trimIndent(), mediaPlayer.currentState) 24 | 25 | mediaPlayer.resume() 26 | assertEquals(""" 27 | current audio: track_0.mp3 28 | current status: RUNNING 29 | """.trimIndent(), mediaPlayer.currentState) 30 | 31 | mediaPlayer.stop() 32 | assertEquals(""" 33 | current audio: 34 | current status: NOT_INITIALIZED 35 | """.trimIndent(), mediaPlayer.currentState) 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/AbstractFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.hamcrest.MatcherAssert.assertThat 4 | import org.hamcrest.core.IsInstanceOf.instanceOf 5 | import org.junit.Test 6 | 7 | internal class AbstractFactoryTest { 8 | 9 | @Test 10 | fun test() { 11 | val iosFactory = IOSButtonFactory() 12 | 13 | val iosButton = iosFactory.createButton() 14 | val iosText = iosFactory.createText() 15 | 16 | assertThat(iosButton, instanceOf(IOSButton::class.java)) 17 | assertThat(iosText, instanceOf(IOSText::class.java)) 18 | 19 | val androidFactory = AndroidButtonFactory() 20 | 21 | val androidButton = androidFactory.createButton() 22 | val androidText = androidFactory.createText() 23 | 24 | assertThat(androidButton, instanceOf(AndroidButton::class.java)) 25 | assertThat(androidText, instanceOf(AndroidText::class.java)) 26 | 27 | // we can draw iOS and Android components regardless of their implementation 28 | listOf(iosButton, androidButton).forEach { it.draw() } 29 | listOf(iosText, androidText).forEach { it.draw() } 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/Factorial.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for finding the factorial of a positive number n 6 | * 7 | */ 8 | 9 | class Factorial { 10 | 11 | /** 12 | * iterative method 13 | * worst time: O(n) 14 | * amount of memory: O(1) 15 | */ 16 | fun compute(number: Int) : Int { 17 | if (number <= 1) { 18 | return 1 19 | } 20 | 21 | var result = 1 22 | for (i in 2..number) { 23 | result *= i 24 | } 25 | return result 26 | } 27 | 28 | /** 29 | * recursive method 30 | * worst time: O(n) 31 | * amount of memory: O(n) - stack for recursion 32 | */ 33 | fun computeRecursive(number: Int) : Int { 34 | return if (number <= 1) { 35 | 1 36 | } else { 37 | number * computeRecursive(number - 1) 38 | } 39 | } 40 | 41 | // read more: https://kotlinlang.org/docs/functions.html#tail-recursive-functions 42 | tailrec fun computeRecursiveWithKotlinOptimization(number: Int, result: Int = 1) : Int = 43 | if (number <= 1) result else computeRecursiveWithKotlinOptimization(number - 1, result * number) 44 | 45 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/MaxAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import java.lang.IllegalArgumentException 4 | 5 | /** 6 | * 7 | * Algorithm for finding the maximum value from a list 8 | * 9 | */ 10 | 11 | class MaxAlgorithm { 12 | 13 | fun > compute(items: List) : T { 14 | if (items.isEmpty()) { 15 | throw IllegalArgumentException("items list is empty!") 16 | } 17 | var max = items[0] 18 | for (i in 1 until items.size) { 19 | if (max < items[i]) { 20 | max = items[i] 21 | } 22 | } 23 | return max 24 | } 25 | 26 | /** 27 | * returns the maximum element from the list recursively 28 | */ 29 | fun > computeRecursive(items: List) : T { 30 | if (items.isEmpty()) { 31 | throw IllegalArgumentException("items list is empty!") 32 | } 33 | if (items.size == 1) { 34 | return items.first() 35 | } 36 | val first = items.first() 37 | val others = items.subList(1, items.size) 38 | 39 | val max = computeRecursive(others) 40 | 41 | return if (first > max) first else max 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/MediatorTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class MediatorTest { 7 | 8 | @Test 9 | fun test() { 10 | val manager = SoftwareDevelopmentManager() 11 | 12 | val customer = CustomerSoftwareDevelopmentMember(manager) 13 | manager.changeCustomer(customer) 14 | val designer = DesignerSoftwareDevelopmentMember(manager) 15 | manager.changeDesigner(designer) 16 | val programmer = ProgrammerSoftwareDevelopmentMember(manager) 17 | manager.changeProgrammer(programmer) 18 | val tester = TesterSoftwareDevelopmentMember(manager) 19 | manager.changeTester(tester) 20 | 21 | customer.finishWork() 22 | designer.finishWork() 23 | programmer.finishWork() 24 | tester.finishWork() 25 | 26 | assertEquals(""" 27 | Designer accepted the work: design development 28 | Programmer accepted the work: writing code 29 | Tester accepted the work: application testing 30 | Customer accepted the work: business valuation 31 | """.trimIndent(), manager.stagesAsString) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/BinaryDigitsCounterTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class BinaryDigitsCounterTest { 7 | 8 | private val counter = BinaryDigitsCounter() 9 | 10 | @Test 11 | fun test_empty_string() { 12 | val result = counter.compute("") 13 | 14 | assertEquals(BinaryDigitsCounter.Result(), result) 15 | } 16 | 17 | @Test 18 | fun test_binary_string_1() { 19 | val result = counter.compute("10101111000") 20 | 21 | assertEquals(BinaryDigitsCounter.Result(6, 5), result) 22 | } 23 | 24 | @Test 25 | fun test_binary_string_2() { 26 | val result = counter.compute("0100000111110010101010") 27 | 28 | assertEquals(BinaryDigitsCounter.Result(10, 12), result) 29 | } 30 | 31 | @Test 32 | fun test_binary_string_3() { 33 | val result = counter.compute("1111111111") 34 | 35 | assertEquals(BinaryDigitsCounter.Result(10, 0), result) 36 | } 37 | 38 | @Test 39 | fun test_binary_string_4() { 40 | val result = counter.compute("0000000000") 41 | 42 | assertEquals(BinaryDigitsCounter.Result(0, 10), result) 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/TemplateMethodTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class TemplateMethodTest { 7 | 8 | @Test 9 | fun `test ChocolateCakeBaker`() { 10 | val baker = ChocolateCakeBaker() 11 | 12 | val actual = baker.makeCake(3) 13 | val expected = Cake( 14 | layers = listOf( 15 | "chocolate cake layer", 16 | "chocolate cake layer", 17 | "chocolate cake layer" 18 | ), 19 | cream = "chocolate cream", 20 | sprinkles = "chocolate chips" 21 | ) 22 | 23 | assertEquals(expected, actual) 24 | } 25 | 26 | @Test 27 | fun `test WaffleCakeBaker`() { 28 | val baker = WaffleCakeBaker() 29 | 30 | val actual = baker.makeCake(3) 31 | val expected = Cake( 32 | layers = listOf( 33 | "waffle cake layer", 34 | "waffle cake layer", 35 | "waffle cake layer" 36 | ), 37 | cream = "custard cream", 38 | sprinkles = "coconut flakes" 39 | ) 40 | 41 | assertEquals(expected, actual) 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/FacadeTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class FacadeTest { 7 | 8 | @Test 9 | fun test() { 10 | val goodsRepository = GoodsRepository( 11 | GoodsDatabase(), 12 | GoodsNetworkService(), 13 | CategoryDatabase(), 14 | CategoryNetworkService() 15 | ) 16 | 17 | val actual = goodsRepository.goodsAndCategories() 18 | 19 | assertEquals( 20 | GoodsResult( 21 | goods = listOf(GoodsEntity( 22 | id = 1, 23 | name = "Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software 2nd Edition", 24 | description = "You know you don't want to reinvent the wheel, so you look to Design Patterns: the lessons learned by those who've faced the same software design problems.", 25 | price = 41.94 26 | )), 27 | categories = listOf(CategoryEntity( 28 | id = 1, 29 | name = "Books" 30 | )) 31 | ), 32 | actual 33 | ) 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/FlyweightTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class FlyweightTest { 7 | 8 | @Test 9 | fun test() { 10 | val garden = BeautifulGarden() 11 | 12 | garden.placeTree("oak", "", 21, 10, 10) 13 | garden.placeTree("birch", "", 15, 10, 20) 14 | garden.placeTree("birch", "", 16, 10, 30) 15 | garden.placeTree("birch", "", 17, 10, 40) 16 | garden.placeTree("oak", "", 21, 20, 10) 17 | garden.placeTree("oak", "", 21, 20, 20) 18 | garden.placeTree("oak", "", 21, 20, 30) 19 | garden.placeTree("birch", "", 15, 20, 40) 20 | 21 | assertEquals(""" 22 | Beautiful Garden: 23 | name: oak, height: 21, x: 10, y: 10 24 | name: birch, height: 15, x: 10, y: 20 25 | name: birch, height: 16, x: 10, y: 30 26 | name: birch, height: 17, x: 10, y: 40 27 | name: oak, height: 21, x: 20, y: 10 28 | name: oak, height: 21, x: 20, y: 20 29 | name: oak, height: 21, x: 20, y: 30 30 | name: birch, height: 15, x: 20, y: 40 31 | -|-|-|-|-|-|-|-|-|-|- 32 | """.trimIndent(), garden.placedTreesAsString()) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/CompositeTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class CompositeTest { 7 | 8 | @Test 9 | fun test() { 10 | val menu = Menu("Delicious Restaurant") 11 | 12 | val pizzasMenu = Menu("Pizzas") 13 | pizzasMenu.addComponent(MenuItem("Cheese pizza", 10)) 14 | pizzasMenu.addComponent(MenuItem("Pepperoni pizza", 11)) 15 | menu.addComponent(pizzasMenu) 16 | 17 | val cakesMenu = Menu("Cakes") 18 | cakesMenu.addComponent(MenuItem("Chocolate cake", 13)) 19 | cakesMenu.addComponent(MenuItem("Cheese cake", 13)) 20 | menu.addComponent(cakesMenu) 21 | 22 | assertEquals(""" 23 | Menu: Delicious Restaurant 24 | Menu: Pizzas 25 | title: Cheese pizza 26 | price: 10 27 | ------------- 28 | title: Pepperoni pizza 29 | price: 11 30 | ------------- 31 | Menu: Cakes 32 | title: Chocolate cake 33 | price: 13 34 | ------------- 35 | title: Cheese cake 36 | price: 13 37 | ------------- 38 | """.trimIndent(), menu.fetchMenuInformation()) 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/PrototypeTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class PrototypeTest { 7 | 8 | @Test 9 | fun test() { 10 | // we have a list of sweets that need to be made at the confectionery factory 11 | val recipesOfSweets = listOf( 12 | OreoCookies("Original OREO", "Rich chocolate cookies with vanilla creme", 12), 13 | OreoCookies("Golden OREO", "Sweet vanilla cookies with vanilla creme", 13), 14 | OreoCookies("OREO Thins", "Sweet vanilla cookies with vanilla creme", 11), 15 | `M&MsChocolate`("with puffed rice", 10), 16 | `M&MsChocolate`("with peanuts", 11) 17 | ) 18 | 19 | // we produce sweets according to existing recipes 20 | val producedSweets = recipesOfSweets.flatMap { sweets -> List(10) { sweets.copy() } } 21 | 22 | assertEquals(producedSweets.size, 50) 23 | assertEquals(producedSweets[9], recipesOfSweets[0]) 24 | assertEquals(producedSweets[19], recipesOfSweets[1]) 25 | assertEquals(producedSweets[29], recipesOfSweets[2]) 26 | assertEquals(producedSweets[39], recipesOfSweets[3]) 27 | assertEquals(producedSweets[49], recipesOfSweets[4]) 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/QueueTest.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class QueueTest { 7 | 8 | @Test 9 | fun test() { 10 | val queue = Queue() 11 | 12 | assertEquals(true, queue.isEmpty) 13 | assertEquals(0, queue.size) 14 | 15 | queue.enqueue(1) 16 | assertEquals(1, queue.peek()) 17 | 18 | queue.enqueue(2) 19 | assertEquals(1, queue.peek()) 20 | 21 | queue.enqueue(3) 22 | assertEquals(1, queue.peek()) 23 | 24 | assertEquals(false, queue.isEmpty) 25 | assertEquals(3, queue.size) 26 | 27 | assertEquals(1, queue.dequeue()) 28 | assertEquals(2, queue.peek()) 29 | 30 | assertEquals(2, queue.dequeue()) 31 | assertEquals(3, queue.peek()) 32 | 33 | assertEquals(3, queue.dequeue()) 34 | 35 | assertEquals(true, queue.isEmpty) 36 | assertEquals(0, queue.size) 37 | 38 | queue.enqueue(10) 39 | queue.enqueue(20) 40 | queue.enqueue(30) 41 | 42 | assertEquals(false, queue.isEmpty) 43 | assertEquals(3, queue.size) 44 | 45 | queue.clear() 46 | 47 | assertEquals(true, queue.isEmpty) 48 | assertEquals(0, queue.size) 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/CountingSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertArrayEquals 5 | 6 | class CountingSortTest { 7 | 8 | @Test 9 | fun test() { 10 | val countingSort = CountingSort() 11 | 12 | val actual1 = arrayOf( 13 | 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 4, 3, 4, 3, 3, 0, 14 | 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 5, 3, 2, 2, 1, 1, 0 15 | ) 16 | val expected1 = actual1.sortedArray() 17 | countingSort.sort(actual1,0, 5) 18 | assertArrayEquals(expected1, actual1) 19 | 20 | val actual2 = arrayOf(9, 9, 9, 10, 10, 5, 4, 4, 4, 1, 1, 1, 3, 3, 3) 21 | val expected2 = actual2.sortedArray() 22 | countingSort.sort(actual2, 1, 10) 23 | assertArrayEquals(expected2, actual2) 24 | 25 | val actual3 = arrayOf( 26 | 1000, 1000, 555, 555, 555, 333, 222, 222, 1, 1, 1, 222, 222, 555, 587, 587, 1, 587, 27 | 1000, 1000, 1000, 6, 7, 6, 7, 7, 7, 6, 1, 1, 222, 555, 587, 3, 3, 3, 1, 3, 3, 6, 6, 28 | 49, 587, 587, 49, 49, 49, 100, 100, 1000, 100, 1000, 555, 222 29 | ) 30 | val expected3 = actual3.sortedArray() 31 | countingSort.sort(actual3, 1, 1000) 32 | assertArrayEquals(expected3, actual3) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/Queue.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * 7 | * Queue is a data structure that follows the FIFO (first in, first out) principle 8 | * 9 | * LIFO implies that the element that is inserted first, comes out first 10 | * 11 | * The main operations: 12 | * 13 | * 1) enqueue - inserts an element at the end of the queue 14 | * 2) dequeue - removes an element from the beginning of the queue 15 | * 16 | * All these operations performed in O(1) time. 17 | * 18 | */ 19 | class Queue { 20 | 21 | private val data = LinkedList() 22 | 23 | val isEmpty: Boolean 24 | get() = data.isEmpty() 25 | 26 | val size: Int 27 | get() = data.size 28 | 29 | fun enqueue(item: T) { 30 | data.add(item) 31 | } 32 | 33 | fun dequeue(): T { 34 | if (isEmpty) throw IllegalStateException("The queue is empty") 35 | 36 | // LinkedList under the hood stores a link to the first element 37 | // this operation will be completed in time O(1) 38 | return data.removeFirst() 39 | } 40 | 41 | fun peek(): T { 42 | if (isEmpty) throw IllegalStateException("The queue is empty") 43 | 44 | return data.first 45 | } 46 | 47 | fun clear() { 48 | data.clear() 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/StringEqualsHashAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for comparing two strings with a hash 6 | * 7 | */ 8 | 9 | class StringEqualsHashAlgorithm { 10 | 11 | fun equals(source: String, pattern: String) : Boolean = 12 | if (source.length != pattern.length) false else source.hash() == pattern.hash() 13 | 14 | /** 15 | * 16 | * computes the hash of a string according to the formula: 17 | * 18 | * hash(abc) = a.code * primeCoefficient⁰ + b.code * primeCoefficient¹ + c.code * primeCoefficient² 19 | * 20 | */ 21 | private fun String.hash() : Int { 22 | var result = 0 23 | var factor = 1 24 | forEach { symbol -> 25 | result += symbol.code * factor 26 | factor *= PRIME_COEFFICIENT 27 | } 28 | // the hash can exceed the maximum Int value, so we limit it 29 | return result.mod(Int.MAX_VALUE) 30 | } 31 | 32 | companion object { 33 | /** 34 | * 35 | * I chose the nearest prime number for the size of the alphabet 36 | * 37 | * my alphabet is [a-z] [A-Z] ! , . 38 | * 39 | * size of the alphabet = 26 + 26 + 3 = 55 (is not prime) 40 | * 41 | */ 42 | private const val PRIME_COEFFICIENT = 53 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/kotlin/sorting/RadixSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | /** 4 | * 5 | * Radix Sort is a linear sorting algorithm that sorts elements by processing them digit by digit. 6 | * 7 | * It is an efficient sorting algorithm for integers or strings with fixed-size keys. 8 | * 9 | * worst time: * n 10 | * 11 | * amount of memory: 2 * n 12 | */ 13 | 14 | class RadixSort { 15 | 16 | fun sort(array: Array) { 17 | val arraySize = array.size 18 | val arrayOfZeroBits = Array(arraySize) { 0 } 19 | val arrayOfOneBits = Array(arraySize) { 0 } 20 | 21 | val size = Int.SIZE_BITS 22 | 23 | for (radix in 0 until size) { 24 | var size1 = 0 25 | var size2 = 0 26 | for (index in array.indices) { 27 | if (array[index].and(1 shl radix) == 0) { 28 | arrayOfZeroBits[size1++] = array[index] 29 | } else { 30 | arrayOfOneBits[size2++] = array[index] 31 | } 32 | } 33 | 34 | for (index in 0 until size1) { 35 | array[index] = arrayOfZeroBits[index] 36 | } 37 | 38 | for (index in 0 until size2) { 39 | array[size1 + index] = arrayOfOneBits[index] 40 | } 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Observer.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Observer is is a behavioral design pattern that defines a one-to-many relationship between objects 6 | * 7 | * such that when the state of one object changes all dependent objects are automatically notified and updated 8 | * 9 | */ 10 | 11 | fun interface PonyObserver { 12 | fun update(item: List) 13 | } 14 | 15 | interface PonyObservable { 16 | fun addObserver(observer: PonyObserver) 17 | fun removeObserver(observer: PonyObserver) 18 | fun notifyObservers() 19 | } 20 | 21 | // PonyList contains some data and when it changes we will notify observers 22 | class PonyList : PonyObservable { 23 | 24 | private val ponies = mutableListOf() 25 | 26 | private val observers = mutableSetOf() 27 | 28 | fun add(pony: String) { 29 | ponies.add(pony) 30 | // notify observers that the data has changed 31 | notifyObservers() 32 | } 33 | 34 | override fun addObserver(observer: PonyObserver) { 35 | observers.add(observer) 36 | } 37 | 38 | override fun removeObserver(observer: PonyObserver) { 39 | observers.remove(observer) 40 | } 41 | 42 | override fun notifyObservers() { 43 | observers.forEach { observer -> observer.update(ponies) } 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/FactorialTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class FactorialTest { 7 | 8 | private val factorial = Factorial() 9 | 10 | @Test 11 | fun test_iterative() { 12 | assertEquals(1, factorial.compute(0)) 13 | assertEquals(1, factorial.compute(1)) 14 | assertEquals(6, factorial.compute(3)) 15 | assertEquals(120, factorial.compute(5)) 16 | assertEquals(720, factorial.compute(6)) 17 | } 18 | 19 | @Test 20 | fun test_recursive() { 21 | assertEquals(1, factorial.computeRecursive(0)) 22 | assertEquals(1, factorial.computeRecursive(1)) 23 | assertEquals(6, factorial.computeRecursive(3)) 24 | assertEquals(120, factorial.computeRecursive(5)) 25 | assertEquals(720, factorial.computeRecursive(6)) 26 | } 27 | 28 | @Test 29 | fun test_recursive_with_kotlin_optimization() { 30 | assertEquals(1, factorial.computeRecursiveWithKotlinOptimization(0)) 31 | assertEquals(1, factorial.computeRecursiveWithKotlinOptimization(1)) 32 | assertEquals(6, factorial.computeRecursiveWithKotlinOptimization(3)) 33 | assertEquals(120, factorial.computeRecursiveWithKotlinOptimization(5)) 34 | assertEquals(720, factorial.computeRecursiveWithKotlinOptimization(6)) 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/SieveOfEratosthenesAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class SieveOfEratosthenesAlgorithmTest { 7 | 8 | private val eratosthenesAlgorithm = SieveOfEratosthenesAlgorithm() 9 | 10 | @Test 11 | fun tes() { 12 | assertEquals(emptyList(), eratosthenesAlgorithm.compute(1)) 13 | assertEquals(listOf(2, 3, 5, 7), eratosthenesAlgorithm.compute(10)) 14 | assertEquals(listOf(2, 3, 5, 7, 11, 13, 17, 19), eratosthenesAlgorithm.compute(20)) 15 | assertEquals(listOf(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37), eratosthenesAlgorithm.compute(40)) 16 | assertEquals( 17 | listOf(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101), 18 | eratosthenesAlgorithm.compute(102) 19 | ) 20 | assertEquals( 21 | listOf( 22 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 23 | 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 24 | 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293 25 | ), 26 | eratosthenesAlgorithm.compute(300) 27 | ) 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/Stack1.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.lang.IllegalArgumentException 4 | import java.util.ArrayList 5 | 6 | /** 7 | * 8 | * Stack is a linear data structure that follows the LIFO (last in first out) principle 9 | * 10 | * LIFO implies that the element that is inserted last, comes out first. 11 | * 12 | * The main operations: 13 | * 14 | * 1) push() - when we insert an element in a stack then the operation is known as a push 15 | * 2) pop() - when we delete an element from the stack, the operation is known as a pop 16 | * 17 | * All these operations performed in O(1) time. 18 | * 19 | */ 20 | 21 | class Stack1 { 22 | // this implementation uses ArrayList 23 | private val data = ArrayList() 24 | 25 | val isEmpty: Boolean 26 | get() = data.size == 0 27 | 28 | val size: Int 29 | get() = data.size 30 | 31 | fun push(item: T) { 32 | data.add(item) 33 | } 34 | 35 | fun pop() : T { 36 | if (isEmpty) { 37 | throw IllegalArgumentException("Stack is empty!") 38 | } 39 | return data.removeLast() 40 | } 41 | 42 | fun peek() : T { 43 | if (isEmpty) { 44 | throw IllegalArgumentException("Stack is empty!") 45 | } 46 | return data.last() 47 | } 48 | 49 | fun clear() { 50 | data.clear() 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/ChainOfResponsibilitiesTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class ChainOfResponsibilitiesTest { 7 | 8 | @Test 9 | fun test_when_we_have_only_stone_pickaxe() { 10 | val pickaxe = StonePickaxe() 11 | 12 | assertEquals(true, pickaxe.mine(StoneBlock())) 13 | assertEquals(false, pickaxe.mine(DiamondBlock())) 14 | assertEquals(false, pickaxe.mine(ObsidianBlock())) 15 | } 16 | 17 | @Test 18 | fun test_when_we_have_stone_and_iron_pickaxes() { 19 | val pickaxe = StonePickaxe() 20 | pickaxe.changeNextPickaxe(IronPickaxe()) 21 | 22 | assertEquals(true, pickaxe.mine(StoneBlock())) 23 | assertEquals(true, pickaxe.mine(DiamondBlock())) 24 | assertEquals(false, pickaxe.mine(ObsidianBlock())) 25 | } 26 | 27 | @Test 28 | fun test_when_we_have_all_three_pickaxes() { 29 | val ironPickaxe = IronPickaxe() 30 | ironPickaxe.changeNextPickaxe(DiamondPickaxe()) 31 | 32 | val stonePickaxe = StonePickaxe() 33 | stonePickaxe.changeNextPickaxe(ironPickaxe) 34 | 35 | assertEquals(true, stonePickaxe.mine(StoneBlock())) 36 | assertEquals(true, stonePickaxe.mine(DiamondBlock())) 37 | assertEquals(true, stonePickaxe.mine(ObsidianBlock())) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/ParenthesisCheckAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for checking a string for correct placement of parentheses using stack 6 | * 7 | * ([]) - correctly 8 | * ()(){} - correctly 9 | * (() - incorrectly 10 | * (())[][]}{ - incorrectly 11 | * 12 | */ 13 | 14 | class ParenthesisCheckAlgorithm { 15 | 16 | fun check(code: String = DEFAULT_CODE): Boolean { 17 | // we use a regular kotlin list to create a stack 18 | val stack = mutableListOf() 19 | 20 | var index = 0 21 | while (index < code.length) { 22 | 23 | when (val symbol = code[index]) { 24 | '(', '{', '[' -> stack.add(symbol) 25 | ')', '}', ']' -> { 26 | val value = bracketRelations[stack.removeLastOrNull()] 27 | if (symbol != value) { 28 | return false 29 | } 30 | } 31 | } 32 | 33 | index++ 34 | } 35 | 36 | return stack.isEmpty() 37 | } 38 | 39 | companion object { 40 | // the correct C program 41 | private const val DEFAULT_CODE = """ 42 | void main() { 43 | printf("Hello, World!"); 44 | } 45 | """ 46 | 47 | private val bracketRelations = mapOf('(' to ')', '{' to '}', '[' to ']') 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/main/kotlin/sorting/CountingSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | /** 4 | * 5 | * Counting sort is a sorting technique based on keys between a specific range 6 | * 7 | * worst time: n 8 | * best time: n 9 | * average time: n 10 | * 11 | * amount of memory: equals to the size of the range of numbers plus 1 (for example: 1001 for numbers from 0 to 1000) 12 | * 13 | * P.S. The use of counting sort is useful only when the sorted numbers have (or can be mapped to) a range of possible 14 | * values that is small enough compared to the sorted set, for example, a million natural numbers less than 1000 15 | * 16 | */ 17 | 18 | class CountingSort { 19 | 20 | // sorts numbers in the range from start to end 21 | fun sort(array: Array, start: Int, end: Int) { 22 | val arraySize = array.size 23 | val countedNumbers = Array(end + 1) { 0 } 24 | 25 | var index = 0 26 | while (index < arraySize) { 27 | countedNumbers[array[index]]++ 28 | index++ 29 | } 30 | 31 | index = 0 32 | var currentNumber = start 33 | while (currentNumber < countedNumbers.size) { 34 | var frequency = countedNumbers[currentNumber] 35 | while (frequency > 0) { 36 | array[index++] = currentNumber 37 | frequency-- 38 | } 39 | currentNumber++ 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Memento.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Memento is a behavioral design pattern that allows without violating encapsulation to capture 6 | * 7 | * and save the internal state of an object so that it can be restored to this state later 8 | * 9 | */ 10 | 11 | 12 | class Bundle(val str: String) 13 | 14 | // Android system saves the application state in special bundles 15 | // I gave this as an example, Android system’s saving mechanism is much more complicated 16 | class AndroidSystem { 17 | 18 | private var savedBundle: Bundle = Bundle("") 19 | 20 | fun saveBundle(bundle: Bundle) { 21 | savedBundle = bundle 22 | } 23 | 24 | fun restoreBundle() = savedBundle 25 | } 26 | 27 | // TextView is an Android component that draws text on the screen 28 | class TextView { 29 | 30 | private var currentText: String = "" 31 | 32 | fun setText(text: String) { 33 | currentText = text 34 | } 35 | 36 | fun text() = currentText 37 | 38 | fun draw() { 39 | println(currentText) 40 | } 41 | 42 | // saves the current state of TextView before re-creating it 43 | fun onSaveInstanceState(): Bundle { 44 | return Bundle(currentText) 45 | } 46 | 47 | // restores the current state after TextView is recreated 48 | fun onRestoreInstanceState(bundle: Bundle) { 49 | currentText = bundle.str 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/Stack2.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * 7 | * Stack is a linear data structure that follows the LIFO (Last-In-First-Out) principle 8 | * 9 | * LIFO implies that the element that is inserted last, comes out first. 10 | * 11 | * The main operations: 12 | * 13 | * push() - when we insert an element in a stack then the operation is known as a push. 14 | * pop() - when we delete an element from the stack, the operation is known as a pop. 15 | * If the stack is empty means that no element exists in the stack. 16 | * 17 | * All these operations performed in O(1) time. 18 | * 19 | */ 20 | 21 | class Stack2 { 22 | // this implementation uses LinkedList 23 | private val data = LinkedList() 24 | 25 | val isEmpty: Boolean 26 | get() = data.size == 0 27 | 28 | val size: Int 29 | get() = data.size 30 | 31 | fun push(item: T) { 32 | data.add(item) 33 | } 34 | 35 | fun pop(): T { 36 | if (isEmpty) { 37 | throw IllegalArgumentException("Stack is empty!") 38 | } 39 | return data.removeLast() 40 | } 41 | 42 | fun peek(): T { 43 | if (isEmpty) { 44 | throw IllegalArgumentException("Stack is empty!") 45 | } 46 | return data.peekLast() 47 | } 48 | 49 | fun clear() { 50 | data.clear() 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/kotlin/sorting/QuickSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import kotlin.random.Random 4 | 5 | /** 6 | * 7 | * QuickSort is a sorting algorithm based on the Divide and Conquer algorithm 8 | * 9 | * that picks an element as a pivot and partitions the given array around the picked pivot 10 | * 11 | * by placing the pivot in its correct position in the sorted array. 12 | * 13 | * 14 | * worst time: n² 15 | * best time: n * log(n) 16 | * average time: n * log(n) 17 | * 18 | * amount of memory: n 19 | * 20 | */ 21 | 22 | class QuickSort { 23 | 24 | fun > sort(array: Array, start: Int = 0, end: Int = array.size - 1) { 25 | if (array.isEmpty()) return 26 | if (start >= end) return 27 | 28 | val pivotIndex = Random.nextInt(start, end + 1) 29 | val pivot = array[pivotIndex] 30 | 31 | var i = start 32 | var j = end 33 | while (i <= j) { 34 | while (array[i] < pivot) { 35 | i++ 36 | } 37 | while (array[j] > pivot) { 38 | j-- 39 | } 40 | if (i <= j) { 41 | val tmp = array[i] 42 | array[i] = array[j] 43 | array[j] = tmp 44 | i++ 45 | j-- 46 | } 47 | } 48 | 49 | if (i < end) sort(array, i, end) 50 | if (0 < j) sort(array, start, j) 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/BridgeTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class BridgeTest { 7 | 8 | @Test 9 | fun test() { 10 | val windowsSystem = WindowsSystem() 11 | val macOSSystem = MacOSSystem() 12 | 13 | // the line doesn't know how graphics are implemented in different operating systems 14 | val line = GraphicLinePrimitive(0, 0, 100, 100) 15 | line.draw(windowsSystem) 16 | line.draw(macOSSystem) 17 | 18 | // the circle doesn't know how graphics are implemented in different operating systems 19 | val circle = GraphicCirclePrimitive(10, 10, 6) 20 | circle.draw(windowsSystem) 21 | circle.draw(macOSSystem) 22 | 23 | assertEquals(""" 24 | Windows 10 25 | Lines: 26 | Windows graphic subsystem -> startX: 0, startY: 0, endX: 100, endY: 100 27 | Circles: 28 | Windows graphic subsystem -> centerX: 10, centerY: 10, radius: 6 29 | """.trimIndent(), windowsSystem.toString()) 30 | 31 | assertEquals(""" 32 | MacOS 14 33 | Lines: 34 | MacOS graphic subsystem -> startX: 0, startY: 0, endX: 100, endY: 100 35 | Circles: 36 | MacOS graphic subsystem -> centerX: 10, centerY: 10, radius: 6 37 | """.trimIndent(), macOSSystem.toString()) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Template Method.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Template method a behavioral design pattern that defines the basis of an algorithm 6 | * 7 | * and allows subclasses to redefine some steps of the algorithm without changing its overall structure 8 | * 9 | */ 10 | 11 | data class Cake( 12 | val layers: List, 13 | val cream: String, 14 | val sprinkles: String 15 | ) 16 | 17 | abstract class CakeBaker { 18 | 19 | protected abstract fun layer(): String 20 | 21 | protected abstract fun cream(): String 22 | 23 | protected abstract fun sprinkles(): String 24 | 25 | // we have the basis of an algorithm 26 | fun makeCake(numberOfLayers: Int): Cake { 27 | return Cake( 28 | layers = List(numberOfLayers) { layer() }, 29 | cream = cream(), 30 | sprinkles = sprinkles() 31 | ) 32 | } 33 | 34 | } 35 | 36 | class ChocolateCakeBaker : CakeBaker() { 37 | 38 | // subclasses redefines some steps of the algorithm 39 | override fun cream(): String = "chocolate cream" 40 | 41 | override fun layer(): String = "chocolate cake layer" 42 | 43 | override fun sprinkles(): String = "chocolate chips" 44 | 45 | } 46 | 47 | class WaffleCakeBaker : CakeBaker() { 48 | 49 | override fun cream(): String = "custard cream" 50 | 51 | override fun layer(): String = "waffle cake layer" 52 | 53 | override fun sprinkles(): String = "coconut flakes" 54 | 55 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE-specific stuff 2 | idea/ 3 | 4 | # CMake 5 | cmake-build-*/ 6 | 7 | # File-based project format 8 | *.iws 9 | 10 | # IntelliJ 11 | out/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Crashlytics plugin (for Android Studio and IntelliJ) 17 | com_crashlytics_export_strings.xml 18 | crashlytics.properties 19 | crashlytics-build.properties 20 | fabric.properties 21 | 22 | ### Kotlin ### 23 | # Compiled class file 24 | *.class 25 | 26 | # Log file 27 | *.log 28 | 29 | # BlueJ files 30 | *.ctxt 31 | 32 | # Mobile Tools for Java (J2ME) 33 | .mtj.tmp/ 34 | 35 | # Package Files # 36 | *.jar 37 | *.war 38 | *.nar 39 | *.ear 40 | *.zip 41 | *.tar.gz 42 | *.rar 43 | 44 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 45 | hs_err_pid* 46 | replay_pid* 47 | 48 | ### Gradle ### 49 | .gradle 50 | **/build/ 51 | !src/**/build/ 52 | 53 | # Ignore Gradle GUI config 54 | gradle-app.setting 55 | 56 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 57 | !gradle-wrapper.jar 58 | 59 | # Avoid ignore Gradle wrappper properties 60 | !gradle-wrapper.properties 61 | 62 | # Cache of project 63 | .gradletasknamecache 64 | 65 | # Eclipse Gradle plugin generated files 66 | # Eclipse Core 67 | .project 68 | # JDT-specific (Eclipse Java Development Tools) 69 | .classpath 70 | 71 | ### Gradle Patch ### 72 | # Java heap dump 73 | *.hprof 74 | 75 | # End of https://www.toptal.com/developers/gitignore/api/intellij,gradle,kotlin -------------------------------------------------------------------------------- /src/test/kotlin/other/SwapAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class SwapAlgorithmTest { 7 | 8 | private val swapAlgorithm = SwapAlgorithm() 9 | 10 | @Test 11 | fun test_list() { 12 | val list = mutableListOf(1, 2, 3, 4, 5, 6) 13 | 14 | swapAlgorithm.swap(list, 0, 5) 15 | swapAlgorithm.swap(list, 1, 4) 16 | swapAlgorithm.swap(list, 2, 3) 17 | 18 | assertEquals(listOf(6, 5, 4, 3, 2, 1), list) 19 | } 20 | 21 | @Test 22 | fun test_array() { 23 | val array = arrayOf(1, 2, 3, 4, 5, 6) 24 | 25 | swapAlgorithm.swap(array, 0, 5) 26 | swapAlgorithm.swap(array, 1, 4) 27 | swapAlgorithm.swap(array, 2, 3) 28 | 29 | assertEquals(listOf(6, 5, 4, 3, 2, 1), array.toList()) 30 | } 31 | 32 | @Test 33 | fun test_list_kotlin() { 34 | val list = mutableListOf(1, 2, 3, 4, 5, 6) 35 | 36 | swapAlgorithm.swapKotlin(list, 0, 5) 37 | swapAlgorithm.swapKotlin(list, 1, 4) 38 | swapAlgorithm.swapKotlin(list, 2, 3) 39 | 40 | assertEquals(listOf(6, 5, 4, 3, 2, 1), list) 41 | } 42 | 43 | @Test 44 | fun test_array_kotlin() { 45 | val array = arrayOf(1, 2, 3, 4, 5, 6) 46 | 47 | swapAlgorithm.swapKotlin(array, 0, 5) 48 | swapAlgorithm.swapKotlin(array, 1, 4) 49 | swapAlgorithm.swapKotlin(array, 2, 3) 50 | 51 | assertEquals(listOf(6, 5, 4, 3, 2, 1), array.toList()) 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Flyweight.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Flyweight is a structural design pattern that reduces memory costs by reusing a family of objects 6 | * 7 | * and storing mutable state outside the object. 8 | * 9 | */ 10 | 11 | class BeautifulGarden { 12 | 13 | // we reuse objects with immutable state, in this example trees 14 | private val existingTrees = mutableListOf() 15 | 16 | // mutable state (coordinates) are stored outside the object 17 | private val placedTrees = mutableListOf>() 18 | 19 | fun placeTree(name: String, description: String, height: Int, x: Int, y: Int) { 20 | // check if such a tree already exists reuse it otherwise create a new one 21 | val tree = existingTrees.find { tree -> tree.name == name && tree.description == description && tree.height == height } 22 | ?: Tree(name, description, height).apply(existingTrees::add) 23 | 24 | placedTrees.add(Triple(x, y, tree)) 25 | } 26 | 27 | fun placedTreesAsString(): String { 28 | val builder = StringBuilder() 29 | builder.append("Beautiful Garden:\n") 30 | 31 | placedTrees.forEach { (x, y, tree) -> 32 | builder.append("name: ${tree.name}, height: ${tree.height}, x: $x, y: $y\n") 33 | } 34 | 35 | builder.append("-|-|-|-|-|-|-|-|-|-|-") 36 | 37 | return builder.toString() 38 | } 39 | 40 | data class Tree( 41 | val name: String, 42 | val description: String, 43 | val height: Int 44 | ) 45 | 46 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/DynamicArrayTest.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class DynamicArrayTest { 7 | 8 | @Test 9 | fun test() { 10 | val array = DynamicArray() 11 | array.add(1) 12 | array.add(2) 13 | array.add(3) 14 | array.add(4) 15 | 16 | assertEquals(""" 17 | capacity: 10 18 | size: 4 19 | elements: 1, 2, 3, 4 20 | """.trimIndent(), array.toString()) 21 | 22 | assertEquals(1, array.set(0, 10)) 23 | assertEquals(10, array.get(0)) 24 | 25 | assertEquals(2, array.set(1, 20)) 26 | assertEquals(20, array.get(1)) 27 | 28 | assertEquals(3, array.set(2, 30)) 29 | assertEquals(30, array.get(2)) 30 | 31 | assertEquals(4, array.set(3, 40)) 32 | assertEquals(40, array.get(3)) 33 | 34 | assertEquals(true, array.remove(10)) 35 | assertEquals(true, array.remove(20)) 36 | assertEquals(true, array.remove(30)) 37 | assertEquals(true, array.remove(40)) 38 | 39 | array.add(1); array.add(2); array.add(3); array.add(4); array.add(5) 40 | array.add(6); array.add(7); array.add(8); array.add(9); array.add(10) 41 | array.add(11); array.add(12); array.add(13); array.add(14); array.add(15) 42 | 43 | assertEquals(""" 44 | capacity: 20 45 | size: 15 46 | elements: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 47 | """.trimIndent(), array.toString()) 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Decorator.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Decorator is a structural design pattern that adds new functionality to an object: 6 | * 7 | * classes implement a common interface and to extend the functionality of the previous object, 8 | * 9 | * the old object is passed through the constructor 10 | * 11 | */ 12 | 13 | interface MyPrinter { 14 | fun printedText() : String 15 | } 16 | 17 | // just returns "Hello" 18 | class HelloPrinter : MyPrinter { 19 | override fun printedText() : String { 20 | return "Hello" 21 | } 22 | } 23 | 24 | // adds a comma to the previous value of the printedText() function 25 | class CommaPrinter(private val printer: MyPrinter) : MyPrinter { 26 | override fun printedText() : String { 27 | return "${printer.printedText()}," 28 | } 29 | } 30 | 31 | // adds a space to the previous value of the printedText() function 32 | class SpacePrinter(private val printer: MyPrinter) : MyPrinter { 33 | override fun printedText() : String { 34 | return "${printer.printedText()} " 35 | } 36 | } 37 | 38 | // adds the word "World" to the previous value of the printedText() function 39 | class WorldPrinter(private val printer: MyPrinter) : MyPrinter { 40 | override fun printedText() : String { 41 | return "${printer.printedText()}World" 42 | } 43 | } 44 | 45 | // adds an exclamation mark to the previous value of the printedText() function 46 | class ExclamationPrinter(private val printer: MyPrinter) : MyPrinter { 47 | override fun printedText() : String { 48 | return "${printer.printedText()}!" 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Сhain Of Responsibilities.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Chain of responsibility is a behavioral design pattern that allows requests to be sent sequentially 6 | * 7 | * through a chain of handlers 8 | * 9 | */ 10 | 11 | enum class BlockFactor { ONE, TWO, THREE } 12 | 13 | // I decided to give an analogy from the Minecraft game. 14 | // In this game there are blocks that can be broken with a stone pickaxe, iron and diamond. 15 | // For example: diamond may mine by iron and diamond pickaxes unlike cobblestone, which is mined by any pickaxe 16 | abstract class Block(private val factor: BlockFactor) { 17 | fun mayMine(factor: BlockFactor) = this.factor.ordinal <= factor.ordinal 18 | } 19 | 20 | // the blocks are from the game 21 | class StoneBlock: Block(BlockFactor.ONE) 22 | class DiamondBlock: Block(BlockFactor.TWO) 23 | class ObsidianBlock: Block(BlockFactor.THREE) 24 | 25 | abstract class Pickaxe(private val factor: BlockFactor) { 26 | 27 | private var nextPickaxe: Pickaxe? = null 28 | 29 | fun changeNextPickaxe(pickaxe: Pickaxe) { 30 | nextPickaxe = pickaxe 31 | } 32 | 33 | // we mine the block, if it doesn't work, we take another pickaxe, if there is one, returns true if a pickaxe can mine 34 | fun mine(block: Block): Boolean { 35 | if (block.mayMine(factor)) return true 36 | 37 | return nextPickaxe?.mine(block) ?: false 38 | } 39 | 40 | } 41 | 42 | // the pickaxes are from the game 43 | class StonePickaxe: Pickaxe(BlockFactor.ONE) 44 | 45 | class IronPickaxe: Pickaxe(BlockFactor.TWO) 46 | 47 | class DiamondPickaxe: Pickaxe(BlockFactor.THREE) -------------------------------------------------------------------------------- /src/test/kotlin/other/EuclidAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class EuclidAlgorithmTest { 7 | 8 | private val euclidAlgorithm = EuclidAlgorithm() 9 | 10 | @Test 11 | fun `test computeByDivisionWithRemainder`() { 12 | assertEquals(5, euclidAlgorithm.computeByDivisionWithRemainder(10, 5)) 13 | assertEquals(10, euclidAlgorithm.computeByDivisionWithRemainder(10, 100)) 14 | assertEquals(9, euclidAlgorithm.computeByDivisionWithRemainder(9, 27)) 15 | assertEquals(13, euclidAlgorithm.computeByDivisionWithRemainder(26, 39)) 16 | assertEquals(1, euclidAlgorithm.computeByDivisionWithRemainder(135, 13)) 17 | assertEquals(1, euclidAlgorithm.computeByDivisionWithRemainder(27, 19)) 18 | assertEquals(1, euclidAlgorithm.computeByDivisionWithRemainder(2, 17)) 19 | assertEquals(1, euclidAlgorithm.computeByDivisionWithRemainder(4, 9)) 20 | } 21 | 22 | @Test 23 | fun `test computeBySubtraction`() { 24 | assertEquals(5, euclidAlgorithm.computeBySubtraction(10, 5)) 25 | assertEquals(10, euclidAlgorithm.computeBySubtraction(10, 100)) 26 | assertEquals(9, euclidAlgorithm.computeBySubtraction(9, 27)) 27 | assertEquals(13, euclidAlgorithm.computeBySubtraction(26, 39)) 28 | assertEquals(1, euclidAlgorithm.computeBySubtraction(135, 13)) 29 | assertEquals(1, euclidAlgorithm.computeBySubtraction(27, 19)) 30 | assertEquals(1, euclidAlgorithm.computeBySubtraction(2, 17)) 31 | assertEquals(1, euclidAlgorithm.computeBySubtraction(4, 9)) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/MinHeapTest.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class MinHeapTest { 7 | 8 | @Test 9 | fun `test operations`() { 10 | val heap = MinHeap(10) 11 | 12 | assertEquals(true, heap.isEmpty) 13 | 14 | heap.add(10) 15 | heap.add(17) 16 | heap.add(20) 17 | 18 | assertEquals(false, heap.isEmpty) 19 | 20 | assertEquals(10, heap.peekMin()) 21 | assertEquals(10, heap.popMin()) 22 | 23 | heap.add(1) 24 | heap.add(5) 25 | 26 | assertEquals(1, heap.peekMin()) 27 | assertEquals(1, heap.popMin()) 28 | 29 | assertEquals(5, heap.peekMin()) 30 | assertEquals(5, heap.popMin()) 31 | 32 | heap.set(1, 15) 33 | 34 | assertEquals(15, heap.peekMin()) 35 | assertEquals(15, heap.popMin()) 36 | 37 | assertEquals(20, heap.peekMin()) 38 | assertEquals(20, heap.popMin()) 39 | 40 | assertEquals(true, heap.isEmpty) 41 | } 42 | 43 | @Test 44 | fun `test creating from array`() { 45 | val array = intArrayOf(10, 2, 11, 17, 5, -1, 9, 0) 46 | 47 | val heap = MinHeap.create(array) 48 | 49 | assertEquals(-1, heap.popMin()) 50 | assertEquals(0, heap.popMin()) 51 | assertEquals(2, heap.popMin()) 52 | assertEquals(5, heap.popMin()) 53 | assertEquals(9, heap.popMin()) 54 | assertEquals(10, heap.popMin()) 55 | assertEquals(11, heap.popMin()) 56 | assertEquals(17, heap.popMin()) 57 | 58 | assertEquals(true, heap.isEmpty) 59 | 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/MaxHeapTest.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | 4 | import org.junit.Test 5 | import org.junit.Assert.assertEquals 6 | 7 | internal class MaxHeapTest { 8 | 9 | @Test 10 | fun `test operations`() { 11 | val heap = MaxHeap(10) 12 | 13 | assertEquals(true, heap.isEmpty) 14 | 15 | heap.add(10) 16 | heap.add(17) 17 | heap.add(20) 18 | 19 | assertEquals(false, heap.isEmpty) 20 | 21 | assertEquals(20, heap.peekMax()) 22 | assertEquals(20, heap.popMax()) 23 | 24 | heap.add(1) 25 | heap.add(5) 26 | 27 | assertEquals(17, heap.peekMax()) 28 | assertEquals(17, heap.popMax()) 29 | 30 | assertEquals(10, heap.peekMax()) 31 | assertEquals(10, heap.popMax()) 32 | 33 | heap.set(1, 100) 34 | 35 | assertEquals(100, heap.peekMax()) 36 | assertEquals(100, heap.popMax()) 37 | 38 | assertEquals(1, heap.peekMax()) 39 | assertEquals(1, heap.popMax()) 40 | 41 | assertEquals(true, heap.isEmpty) 42 | } 43 | 44 | @Test 45 | fun `test creating from array`() { 46 | val array = intArrayOf(10, 2, 11, 17, 5, -1, 9, 0) 47 | 48 | val heap = MaxHeap.create(array) 49 | 50 | assertEquals(17, heap.popMax()) 51 | assertEquals(11, heap.popMax()) 52 | assertEquals(10, heap.popMax()) 53 | assertEquals(9, heap.popMax()) 54 | assertEquals(5, heap.popMax()) 55 | assertEquals(2, heap.popMax()) 56 | assertEquals(0, heap.popMax()) 57 | assertEquals(-1, heap.popMax()) 58 | 59 | assertEquals(true, heap.isEmpty) 60 | 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/main/kotlin/sorting/BubbleSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | /** 4 | * 5 | * Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm 6 | * 7 | * that repeatedly steps through the input list element by element, comparing the current element 8 | * 9 | * with the one after it, swapping their values if needed 10 | * 11 | */ 12 | 13 | class BubbleSort { 14 | 15 | /** 16 | * worst time: n² 17 | * best time: n² 18 | * average time: n² 19 | * 20 | * amount of memory: 1 21 | */ 22 | fun > sort(array: Array) { 23 | val arraySize = array.size 24 | for (i in 0 until arraySize - 1) { 25 | for (j in 0 until arraySize - 1 - i) { 26 | if (array[j] > array[j + 1]) { 27 | val tmp = array[j + 1] 28 | array[j + 1] = array[j] 29 | array[j] = tmp 30 | } 31 | } 32 | } 33 | } 34 | 35 | /** 36 | * worst time: n² 37 | * best time: n 38 | * average time: n² 39 | * 40 | * amount of memory: 1 41 | */ 42 | fun > sortImproved(array: Array) { 43 | val arraySize = array.size 44 | var isSorted = true 45 | for (i in 0 until arraySize - 1) { 46 | for (j in 0 until arraySize - 1 - i) { 47 | if (array[j] > array[j + 1]) { 48 | val tmp = array[j + 1] 49 | array[j + 1] = array[j] 50 | array[j] = tmp 51 | 52 | isSorted = false 53 | } 54 | } 55 | if (isSorted) break 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/kotlin/other/KnuthMorrisPrattAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Knut Morris Pratt's Algorithm for finding a substring in a string 6 | * 7 | */ 8 | 9 | class KnuthMorrisPrattAlgorithm { 10 | 11 | // returns true if the substring is in the string 12 | fun contains(sub: String, str: String) : Boolean { 13 | val summary = "$sub$SEPARATOR$str" 14 | val prefixFunctionValue = prefixFunction(summary) 15 | return prefixFunctionValue.any { value -> value == sub.length } 16 | } 17 | 18 | // returns the number of occurrences of a substring in a string 19 | fun count(sub: String, str: String) : Int { 20 | val summary = "$sub$SEPARATOR$str" 21 | return prefixFunction(summary).count { value -> value == sub.length } 22 | } 23 | 24 | // returns an array of prefix functions for a string 25 | private fun prefixFunction(str: String) : Array { 26 | val prefixFunctionsValues = Array(str.length) { 0 } 27 | for (index in 1 until str.length) { 28 | var prefixFunctionValue = prefixFunctionsValues[index - 1] 29 | while (prefixFunctionValue > 0 && str[index] != str[prefixFunctionValue]) { 30 | prefixFunctionValue = prefixFunctionsValues[prefixFunctionValue - 1] 31 | } 32 | if (str[index] == str[prefixFunctionValue]) { 33 | prefixFunctionValue++ 34 | } 35 | prefixFunctionsValues[index] = prefixFunctionValue 36 | } 37 | return prefixFunctionsValues 38 | } 39 | 40 | companion object { 41 | // delimiter must not occur in source strings 42 | private const val SEPARATOR = "#" 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Composite.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Composite is a structural design pattern that organizes objects into a tree structure 6 | * 7 | * and allows clients to access individual objects and groups of objects in the same way. 8 | * 9 | */ 10 | 11 | // all components have a common interface 12 | abstract class MenuComponent(val title: String) { 13 | // menus and items can give information about themselves 14 | abstract fun fetchMenuInformation() : String 15 | } 16 | 17 | class Menu(title: String) : MenuComponent(title) { 18 | 19 | // menu can contain other menus and items 20 | private val childMenuComponents = mutableListOf() 21 | 22 | // addComponent/removeComponent operations are only available for the menu 23 | fun addComponent(component: MenuComponent) { 24 | childMenuComponents.add(component) 25 | } 26 | 27 | fun removeComponent(component: MenuComponent) { 28 | childMenuComponents.remove(component) 29 | } 30 | 31 | override fun fetchMenuInformation(): String { 32 | val builder = StringBuilder() 33 | builder.append("Menu: $title") 34 | childMenuComponents.forEach { component -> 35 | builder.append("\n") 36 | builder.append(component.fetchMenuInformation()) 37 | } 38 | return builder.toString() 39 | } 40 | 41 | } 42 | 43 | // the simple component that contains no others 44 | class MenuItem(title: String, private val price: Int) : MenuComponent(title) { 45 | 46 | override fun fetchMenuInformation(): String = 47 | """ 48 | title: $title 49 | price: $price 50 | ------------- 51 | """.trimIndent() 52 | 53 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/BinaryDigitsCounter.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * This algorithm counts the number of ones and zeros in a binary string 6 | * 7 | * and implemented on a finite state machine 8 | * 9 | */ 10 | 11 | class BinaryDigitsCounter { 12 | 13 | data class Result(private val ones: Int = 0, private val zeros: Int = 0) 14 | 15 | // represents two states 16 | private enum class State { 17 | ONE, ZERO 18 | } 19 | 20 | fun compute(binaryString: String): Result { // 1010010011 21 | if (binaryString.isEmpty()) { 22 | return Result() 23 | } 24 | 25 | // define initial state 26 | var currentState = if (binaryString.first() == '1') State.ONE else State.ZERO 27 | 28 | var onesCount = 0 29 | var zerosCount = 0 30 | 31 | binaryString.forEach { symbol -> 32 | // we use 'when' statement to toggle the state 33 | when (currentState) { 34 | State.ONE -> { 35 | if (symbol == '0') { 36 | zerosCount++ 37 | // move to another state 38 | currentState = State.ZERO 39 | } else { 40 | onesCount++ 41 | } 42 | } 43 | State.ZERO -> { 44 | if (symbol == '1') { 45 | onesCount++ 46 | // move to another state 47 | currentState = State.ONE 48 | } else { 49 | zerosCount++ 50 | } 51 | } 52 | } 53 | } 54 | 55 | return Result(onesCount, zerosCount) 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/main/kotlin/other/SwapAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | /** 4 | * 5 | * Algorithm for exchanging two variables without a third additional 6 | * 7 | */ 8 | 9 | class SwapAlgorithm { 10 | 11 | // swaps two list values at the specified indexes 12 | fun swap(list: MutableList, oldIndex: Int, newIndex: Int) { 13 | if (oldIndex in list.indices && newIndex in list.indices) { 14 | list[oldIndex] = list[oldIndex] + list[newIndex] 15 | list[newIndex] = list[oldIndex] - list[newIndex] 16 | list[oldIndex] = list[oldIndex] - list[newIndex] 17 | } 18 | } 19 | 20 | // swaps two array values at the specified indexes 21 | fun swap(array: Array, oldIndex: Int, newIndex: Int) { 22 | if (oldIndex in array.indices && newIndex in array.indices) { 23 | array[oldIndex] = array[oldIndex] + array[newIndex] 24 | array[newIndex] = array[oldIndex] - array[newIndex] 25 | array[oldIndex] = array[oldIndex] - array[newIndex] 26 | } 27 | } 28 | 29 | // swaps two list values using Kotlin language features 30 | fun swapKotlin(list: MutableList, oldIndex: Int, newIndex: Int) { 31 | if (oldIndex in list.indices && newIndex in list.indices) { 32 | list[oldIndex] = list[newIndex].apply { 33 | list[newIndex] = list[oldIndex] 34 | } 35 | } 36 | } 37 | 38 | // swaps two array values using Kotlin language features 39 | fun swapKotlin(array: Array, oldIndex: Int, newIndex: Int) { 40 | if (oldIndex in array.indices && newIndex in array.indices) { 41 | array[oldIndex] = array[newIndex].apply { 42 | array[newIndex] = array[oldIndex] 43 | } 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/StateTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class StateTest { 7 | 8 | @Test 9 | fun test() { 10 | val machine = CocaColaMachine() 11 | 12 | assertEquals("NoDollarCocaColaState", machine.currentState) 13 | machine.pressButton() 14 | assertEquals("NoDollarCocaColaState", machine.currentState) 15 | 16 | machine.insertDollar() 17 | assertEquals("HaveDollarCocaColaState", machine.currentState) 18 | machine.takeBackDollar() 19 | assertEquals("NoDollarCocaColaState", machine.currentState) 20 | machine.takeBackDollar() 21 | assertEquals("NoDollarCocaColaState", machine.currentState) 22 | 23 | machine.insertDollar() 24 | assertEquals("HaveDollarCocaColaState", machine.currentState) 25 | machine.pressButton() 26 | assertEquals("NoDollarCocaColaState", machine.currentState) 27 | 28 | machine.insertDollar() 29 | assertEquals("HaveDollarCocaColaState", machine.currentState) 30 | machine.pressButton() 31 | assertEquals("NoDollarCocaColaState", machine.currentState) 32 | 33 | machine.insertDollar() 34 | assertEquals("HaveDollarCocaColaState", machine.currentState) 35 | machine.pressButton() 36 | assertEquals("EmptyCocaColaState", machine.currentState) 37 | 38 | machine.insertDollar() 39 | assertEquals("EmptyCocaColaState", machine.currentState) 40 | 41 | machine.loadDrinks(1) 42 | machine.insertDollar() 43 | assertEquals("HaveDollarCocaColaState", machine.currentState) 44 | machine.pressButton() 45 | assertEquals("EmptyCocaColaState", machine.currentState) 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/BubbleSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertArrayEquals 5 | import kotlin.random.Random 6 | 7 | internal class BubbleSortTest { 8 | 9 | private val bubbleSort = BubbleSort() 10 | 11 | @Test 12 | fun `test sort`() { 13 | val expected1 = Array(1000) { it } 14 | val actual1 = expected1.reversedArray() 15 | bubbleSort.sort(actual1) 16 | assertArrayEquals(expected1, actual1) 17 | 18 | val actual2 = Array(1000) { Random.nextInt(1000) } 19 | val expected2 = actual2.sortedArray() 20 | bubbleSort.sort(actual2) 21 | assertArrayEquals(expected2, actual2) 22 | 23 | val expected3 = Array(1000) { it } 24 | val actual3 = expected3.copyOf() 25 | actual3.shuffle() 26 | bubbleSort.sort(actual3) 27 | assertArrayEquals(expected3, actual3) 28 | 29 | val expected4 = Array(1000) { it } 30 | val actual4 = expected3.copyOf() 31 | bubbleSort.sort(actual3) 32 | assertArrayEquals(expected4, actual4) 33 | } 34 | 35 | @Test 36 | fun `test sort improved`() { 37 | val expected1 = Array(1000) { it } 38 | val actual1 = expected1.reversedArray() 39 | bubbleSort.sortImproved(actual1) 40 | assertArrayEquals(expected1, actual1) 41 | 42 | val actual2 = Array(1000) { Random.nextInt(1000) } 43 | val expected2 = actual2.sortedArray() 44 | bubbleSort.sortImproved(actual2) 45 | assertArrayEquals(expected2, actual2) 46 | 47 | val expected3 = Array(1000) { it } 48 | val actual3 = expected3.copyOf() 49 | actual3.shuffle() 50 | bubbleSort.sortImproved(actual3) 51 | assertArrayEquals(expected3, actual3) 52 | 53 | val expected4 = Array(1000) { it } 54 | val actual4 = expected3.copyOf() 55 | bubbleSort.sortImproved(actual3) 56 | assertArrayEquals(expected4, actual4) 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/ParenthesisCheckAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class ParenthesisCheckAlgorithmTest { 7 | 8 | private val parenthesisCheckAlgorithm = ParenthesisCheckAlgorithm() 9 | 10 | @Test 11 | fun `test success`() { 12 | // the correct C program 13 | assertEquals(true, parenthesisCheckAlgorithm.check(""" 14 | void main() { 15 | printf("Hello, World!"); 16 | } 17 | """)) 18 | assertEquals(true, parenthesisCheckAlgorithm.check("")) 19 | assertEquals(true, parenthesisCheckAlgorithm.check("()")) 20 | assertEquals(true, parenthesisCheckAlgorithm.check("{}")) 21 | assertEquals(true, parenthesisCheckAlgorithm.check("[]")) 22 | assertEquals(true, parenthesisCheckAlgorithm.check("(([[]]))[[]]")) 23 | assertEquals(true, parenthesisCheckAlgorithm.check("[[[[((()))]]]]{}{}{}")) 24 | assertEquals(true, parenthesisCheckAlgorithm.check("(())()()[][]{{()}}{}")) 25 | } 26 | 27 | @Test 28 | fun `test failed`() { 29 | // the failed C program 30 | assertEquals(false, parenthesisCheckAlgorithm.check(""" 31 | void main({ 32 | printf("Hello, World!"; 33 | } 34 | """)) 35 | assertEquals(false, parenthesisCheckAlgorithm.check("(")) 36 | assertEquals(false, parenthesisCheckAlgorithm.check(")")) 37 | assertEquals(false, parenthesisCheckAlgorithm.check("{")) 38 | assertEquals(false, parenthesisCheckAlgorithm.check("}")) 39 | assertEquals(false, parenthesisCheckAlgorithm.check("[")) 40 | assertEquals(false, parenthesisCheckAlgorithm.check("]")) 41 | assertEquals(false, parenthesisCheckAlgorithm.check("[](){{}")) 42 | assertEquals(false, parenthesisCheckAlgorithm.check("[[[]]]{}{})")) 43 | assertEquals(false, parenthesisCheckAlgorithm.check("{{{}}}({[}]))[]")) 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/KnuthMorrisPrattAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class KnuthMorrisPrattAlgorithmTest { 7 | 8 | @Test 9 | fun test() { 10 | val knuthMorrisPrattAlgorithm = KnuthMorrisPrattAlgorithm() 11 | 12 | val sentence1 = "Twillight Sparkle likes reading books!" 13 | assertEquals(true, knuthMorrisPrattAlgorithm.contains("Twillight Sparkle", sentence1)) 14 | assertEquals(true, knuthMorrisPrattAlgorithm.contains("likes", sentence1)) 15 | assertEquals(true, knuthMorrisPrattAlgorithm.contains("reading", sentence1)) 16 | assertEquals(true, knuthMorrisPrattAlgorithm.contains("books", sentence1)) 17 | 18 | assertEquals(false, knuthMorrisPrattAlgorithm.contains("Apple Jack", sentence1)) 19 | assertEquals(false, knuthMorrisPrattAlgorithm.contains("loves", sentence1)) 20 | assertEquals(false, knuthMorrisPrattAlgorithm.contains("learning", sentence1)) 21 | assertEquals(false, knuthMorrisPrattAlgorithm.contains("articles", sentence1)) 22 | 23 | val sentence2 = """ 24 | Watch the Keynote, livestream replay, and tech talks to hear about the latest updates 25 | in Android development directly from the Android team. 26 | """.trimIndent() 27 | assertEquals(3, knuthMorrisPrattAlgorithm.count("the", sentence2)) 28 | assertEquals(2, knuthMorrisPrattAlgorithm.count("Android", sentence2)) 29 | assertEquals(1, knuthMorrisPrattAlgorithm.count("and", sentence2)) 30 | assertEquals(1, knuthMorrisPrattAlgorithm.count("Keynote", sentence2)) 31 | 32 | assertEquals(0, knuthMorrisPrattAlgorithm.count("Kotlin", sentence2)) 33 | assertEquals(0, knuthMorrisPrattAlgorithm.count("Jetpack Compose", sentence2)) 34 | assertEquals(0, knuthMorrisPrattAlgorithm.count("Android Studio", sentence2)) 35 | assertEquals(0, knuthMorrisPrattAlgorithm.count("developers", sentence2)) 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/test/kotlin/sorting/MergeSortTest.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import org.junit.Test 4 | import kotlin.random.Random 5 | import org.junit.Assert.assertArrayEquals 6 | 7 | class MergeSortTest { 8 | 9 | private val mergeSort = MergeSort() 10 | 11 | @Test 12 | fun `test sort`() { 13 | val expected1 = Array(1_000_000) { it } 14 | val actual1 = expected1.reversedArray() 15 | mergeSort.sort(actual1) 16 | assertArrayEquals(expected1, actual1) 17 | 18 | val actual2 = Array(1_000_000) { Random.nextInt(1_000_000) } 19 | val expected2 = actual2.sortedArray() 20 | mergeSort.sort(actual2) 21 | assertArrayEquals(expected2, actual2) 22 | 23 | val expected3 = Array(1_000_000) { it } 24 | val actual3 = expected3.copyOf() 25 | actual3.shuffle() 26 | mergeSort.sort(actual3) 27 | assertArrayEquals(expected3, actual3) 28 | 29 | val expected4 = Array(1_000_000) { it } 30 | val actual4 = expected3.copyOf() 31 | mergeSort.sort(actual3) 32 | assertArrayEquals(expected4, actual4) 33 | } 34 | 35 | @Test 36 | fun `test sort recursive`() { 37 | val expected1 = Array(1_000_000) { it } 38 | val actual1 = expected1.reversedArray() 39 | mergeSort.sortRecursive(actual1) 40 | assertArrayEquals(expected1, actual1) 41 | 42 | val actual2 = Array(1_000_000) { Random.nextInt(1_000_000) } 43 | val expected2 = actual2.sortedArray() 44 | mergeSort.sortRecursive(actual2) 45 | assertArrayEquals(expected2, actual2) 46 | 47 | val expected3 = Array(1_000_000) { it } 48 | val actual3 = expected3.copyOf() 49 | actual3.shuffle() 50 | mergeSort.sortRecursive(actual3) 51 | assertArrayEquals(expected3, actual3) 52 | 53 | val expected4 = Array(1_000_000) { it } 54 | val actual4 = expected3.copyOf() 55 | mergeSort.sortRecursive(actual3) 56 | assertArrayEquals(expected4, actual4) 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/StrategyTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class StrategyTest { 7 | 8 | @Test 9 | fun test() { 10 | val strategy = OnlyChipsFilterStrategy() 11 | val foodStore = FoodStore(strategy) 12 | 13 | assertEquals(listOf( 14 | FoodEntity( 15 | "Lays Potato Chips Fried Crab Flavor", 16 | 2, 17 | "chips" 18 | ), 19 | FoodEntity( 20 | "Lay's Potato Chips, Classic", 21 | 3, 22 | "chips" 23 | ) 24 | ), foodStore.foodItems()) 25 | 26 | foodStore.changeStrategy(OnlyChocolateFilterStrategy()) 27 | 28 | assertEquals(listOf( 29 | FoodEntity( 30 | "Dove Chocolate", 31 | 3, 32 | "chocolate" 33 | ), 34 | FoodEntity( 35 | "Ritter Sport Chocolate", 36 | 4, 37 | "chocolate" 38 | ) 39 | ), foodStore.foodItems()) 40 | 41 | foodStore.changeStrategy(PriceFilterStrategy(3)) 42 | 43 | assertEquals(listOf( 44 | FoodEntity( 45 | "Lay's Potato Chips, Classic", 46 | 3, 47 | "chips" 48 | ), 49 | FoodEntity( 50 | "Dove Chocolate", 51 | 3, 52 | "chocolate" 53 | ), 54 | FoodEntity( 55 | "Ritter Sport Chocolate", 56 | 4, 57 | "chocolate" 58 | ) 59 | ), foodStore.foodItems()) 60 | 61 | foodStore.changeStrategy(SearchWordFilterStrategy("Ritter Sport")) 62 | 63 | assertEquals(listOf( 64 | FoodEntity( 65 | "Ritter Sport Chocolate", 66 | 4, 67 | "chocolate" 68 | ) 69 | ), foodStore.foodItems()) 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/VisitorTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class VisitorTest { 7 | 8 | private val expectedJson = """ 9 | [{ 10 | "what" : "unicorn", 11 | "name" : "Twilight Sparkle", 12 | "cutie_mark" : "star 13 | }, 14 | { 15 | "what" : "pegasus", 16 | "name" : "Rainbow Dash", 17 | "cutie_mark" : "lightning 18 | }, 19 | { 20 | "what" : "earth pony", 21 | "name" : "Apple Jack", 22 | "cutie_mark" : "apple 23 | }] 24 | """.trimIndent() 25 | 26 | private val expectedXml = """ 27 | 28 | 29 | unicorn 30 | Twilight Sparkle 31 | star 32 | 33 | 34 | pegasus 35 | Rainbow Dash 36 | lightning 37 | 38 | 39 | earth pony 40 | Apple Jack 41 | apple 42 | 43 | 44 | """.trimIndent() 45 | 46 | @Test 47 | fun test_json_visitor() { 48 | val jsonVisitor = JsonVisitor() 49 | val twilightSparkle = Unicorn("Twilight Sparkle", "star") 50 | val rainbowDash = Pegasus("Rainbow Dash", "lightning") 51 | val appleJack = EarthPony("Apple Jack", "apple") 52 | val actual = jsonVisitor.visitPonies(twilightSparkle, rainbowDash, appleJack) 53 | assertEquals(expectedJson, actual) 54 | } 55 | 56 | @Test 57 | fun test_xml_visitor() { 58 | val xmlVisitor = XmlVisitor() 59 | val twilightSparkle = Unicorn("Twilight Sparkle", "star") 60 | val rainbowDash = Pegasus("Rainbow Dash", "lightning") 61 | val appleJack = EarthPony("Apple Jack", "apple") 62 | val actual = xmlVisitor.visitPonies(twilightSparkle, rainbowDash, appleJack) 63 | assertEquals(expectedXml, actual) 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Interpreter.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Interpreter is a behavioral design pattern that defines a simple language grammar for a problem domain, 6 | * 7 | * represents grammatical rules as language sentences and interprets them to solve commonly encountered problems 8 | * 9 | */ 10 | 11 | // contains general information for the interpreter 12 | class InterpreterContext { 13 | private val variables = mutableMapOf() 14 | 15 | fun putVariable(key: String, value: Int) { 16 | variables[key] = value 17 | } 18 | 19 | fun fetchVariable(key: String): Int { 20 | return variables[key] ?: 0 21 | } 22 | } 23 | 24 | // represents a specific interpreter grammar expression 25 | interface InterpreterExpression { 26 | fun interpret(context: InterpreterContext) 27 | } 28 | 29 | class SetIntVariableExpression( 30 | private val key: String, 31 | private val intValue: Int 32 | ) : InterpreterExpression { 33 | override fun interpret(context: InterpreterContext) { 34 | context.putVariable(key = key, value = intValue) 35 | } 36 | } 37 | 38 | class PerformExpression(private vararg val expressions: InterpreterExpression) : InterpreterExpression { 39 | override fun interpret(context: InterpreterContext) { 40 | expressions.forEach { it.interpret(context) } 41 | } 42 | } 43 | 44 | class AddVariablesExpression( 45 | private val key0: String, 46 | private val key1: String, 47 | private val result: String 48 | ) : InterpreterExpression { 49 | override fun interpret(context: InterpreterContext) { 50 | context.putVariable( 51 | key = result, 52 | value = context.fetchVariable(key0) + context.fetchVariable(key1) 53 | ) 54 | } 55 | } 56 | 57 | class MultipleVariablesExpression( 58 | private val key0: String, 59 | private val key1: String, 60 | private val result: String 61 | ) : InterpreterExpression { 62 | override fun interpret(context: InterpreterContext) { 63 | context.putVariable( 64 | key = result, 65 | value = context.fetchVariable(key0) * context.fetchVariable(key1) 66 | ) 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Prototype.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Prototype is a generative design pattern that allows you to copy objects 6 | * 7 | * without going into the details of their implementation. 8 | * 9 | */ 10 | 11 | abstract class Sweets( 12 | protected val title: String, 13 | protected val description: String, 14 | protected val price: Int 15 | ) { 16 | 17 | // we only have a copy interface and don't know about its implementation 18 | abstract fun copy(): Sweets 19 | 20 | } 21 | 22 | class OreoCookies( 23 | title: String, 24 | private val flavor: String, 25 | price: Int 26 | ) : Sweets( 27 | title = title, 28 | price = price, 29 | description = "Take a delicious break with OREO Chocolate Sandwich Cookies, America's favorite sandwich cookie for over 100 years..." 30 | ) { 31 | 32 | // each implementation creates its own copy 33 | override fun copy(): Sweets { 34 | return OreoCookies(title, flavor, price) 35 | } 36 | 37 | override fun equals(other: Any?): Boolean { 38 | if (other == null) return false 39 | if (this === other) return true 40 | if (other !is OreoCookies) return false 41 | 42 | return title == other.title && description == other.description && 43 | flavor == other.flavor && price == other.price 44 | } 45 | 46 | } 47 | 48 | class `M&MsChocolate`( 49 | private val taste: String, 50 | price: Int, 51 | ) : Sweets( 52 | title = "M&M's chocolate", 53 | price = price, 54 | description = "M&M's are a chocolate jelly bean produced by Mars LLC. It first appeared in the United States in 1941 and is now sold in more than 100 countries..." 55 | ) { 56 | 57 | override fun copy(): Sweets { 58 | return `M&MsChocolate`(taste, price) 59 | } 60 | 61 | override fun equals(other: Any?): Boolean { 62 | if (other == null) return false 63 | if (this === other) return true 64 | if (other !is `M&MsChocolate`) return false 65 | 66 | return title == other.title && description == other.description && 67 | taste == other.taste && price == other.price 68 | } 69 | 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/test/kotlin/search/BinarySearchTest.kt: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class BinarySearchTest { 7 | 8 | private val searchAlgo = BinarySearch() 9 | 10 | @Test 11 | fun `test search`() { 12 | val array = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 13 | 14 | assertEquals(-1, searchAlgo.search(emptyArray(), 0)) 15 | assertEquals(-1, searchAlgo.search(array, 0)) 16 | assertEquals(-1, searchAlgo.search(array, 11)) 17 | assertEquals(0, searchAlgo.search(array, 1)) 18 | assertEquals(2, searchAlgo.search(array, 3)) 19 | assertEquals(4, searchAlgo.search(array, 5)) 20 | assertEquals(6, searchAlgo.search(array, 7)) 21 | assertEquals(8, searchAlgo.search(array, 9)) 22 | assertEquals(9, searchAlgo.search(array, 10)) 23 | 24 | assertEquals(-1, searchAlgo.searchRecursive(emptyArray(), 0)) 25 | assertEquals(-1, searchAlgo.searchRecursive(array, 0)) 26 | assertEquals(-1, searchAlgo.searchRecursive(array, 11)) 27 | assertEquals(0, searchAlgo.searchRecursive(array, 1)) 28 | assertEquals(2, searchAlgo.searchRecursive(array, 3)) 29 | assertEquals(4, searchAlgo.searchRecursive(array, 5)) 30 | assertEquals(6, searchAlgo.searchRecursive(array, 7)) 31 | assertEquals(8, searchAlgo.searchRecursive(array, 9)) 32 | assertEquals(9, searchAlgo.searchRecursive(array, 10)) 33 | } 34 | 35 | @Test 36 | fun `test bounds`() { 37 | val array = arrayOf(1, 2, 3, 4, 6, 7, 8, 10) 38 | assertEquals(-1, searchAlgo.leftBound(array, 0)) 39 | assertEquals(-1, searchAlgo.leftBound(array, 1)) 40 | assertEquals(1, searchAlgo.leftBound(array, 3)) 41 | assertEquals(3, searchAlgo.leftBound(array, 5)) 42 | assertEquals(6, searchAlgo.leftBound(array, 10)) 43 | assertEquals(7, searchAlgo.leftBound(array, 11)) 44 | assertEquals(7, searchAlgo.leftBound(array, 100)) 45 | 46 | assertEquals(0, searchAlgo.rightBound(array, 0)) 47 | assertEquals(0, searchAlgo.rightBound(array, 1)) 48 | assertEquals(2, searchAlgo.rightBound(array, 3)) 49 | assertEquals(4, searchAlgo.rightBound(array, 5)) 50 | assertEquals(7, searchAlgo.rightBound(array, 10)) 51 | assertEquals(8, searchAlgo.rightBound(array, 11)) 52 | assertEquals(8, searchAlgo.rightBound(array, 100)) 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/FactoryMethodTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.hamcrest.MatcherAssert.assertThat 4 | import org.hamcrest.core.IsInstanceOf.instanceOf 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | 8 | class FactoryMethodTest { 9 | 10 | @Test 11 | fun `test WoodenHouseCompany`() { 12 | val woodenHouseCompany = WoodenHouseCompany() 13 | 14 | val house1 = woodenHouseCompany.orderHouse("77, Brook Ave", HouseCompany.HouseCompanyCost.CHEAP) 15 | assertThat(house1, instanceOf(WoodenCheapHouse::class.java)) 16 | 17 | val house2 = woodenHouseCompany.orderHouse("77, Brook Ave", HouseCompany.HouseCompanyCost.AVERAGE) 18 | assertThat(house2, instanceOf(WoodenAverageHouse::class.java)) 19 | 20 | val house3 = woodenHouseCompany.orderHouse("77, Brook Ave", HouseCompany.HouseCompanyCost.EXPENSIVE) 21 | assertThat(house3, instanceOf(WoodenExpensiveHouse::class.java)) 22 | 23 | val expected = """ 24 | address = 77, Brook Ave 25 | price = 50000 26 | 27 | address = 77, Brook Ave 28 | price = 250000 29 | 30 | address = 77, Brook Ave 31 | price = 1000000 32 | """.trimIndent() 33 | assertEquals(expected, woodenHouseCompany.examplesAlreadyBuiltHouses) 34 | } 35 | 36 | @Test 37 | fun `test StoneHouseCompany`() { 38 | val stoneHouseCompany = StoneHouseCompany() 39 | 40 | val house1 = stoneHouseCompany.orderHouse("55, Brook Ave", HouseCompany.HouseCompanyCost.CHEAP) 41 | assertThat(house1, instanceOf(StoneCheapHouse::class.java)) 42 | 43 | val house2 = stoneHouseCompany.orderHouse("55, Brook Ave", HouseCompany.HouseCompanyCost.AVERAGE) 44 | assertThat(house2, instanceOf(StoneAverageHouse::class.java)) 45 | 46 | val house3 = stoneHouseCompany.orderHouse("55, Brook Ave", HouseCompany.HouseCompanyCost.EXPENSIVE) 47 | assertThat(house3, instanceOf(StoneExpensiveHouse::class.java)) 48 | 49 | val expected = """ 50 | address = 55, Brook Ave 51 | price = 45000 52 | 53 | address = 55, Brook Ave 54 | price = 230000 55 | 56 | address = 55, Brook Ave 57 | price = 900000 58 | """.trimIndent() 59 | assertEquals(expected, stoneHouseCompany.examplesAlreadyBuiltHouses) 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/CommandTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class CommandTest { 7 | 8 | @Test 9 | fun `test usual commands`() { 10 | val stereoSystem = StereoSystem() 11 | val remoteControl = StereoSystemRemoteControl(mapOf( 12 | "turnOn" to TurnOnCommand(stereoSystem), 13 | "turnOff" to TurnOffCommand(stereoSystem), 14 | "volume+" to IncreaseVolumeCommand(stereoSystem), 15 | "volume-" to DecreaseVolumeCommand(stereoSystem) 16 | )) 17 | 18 | remoteControl.pressButton("turnOn") 19 | 20 | assertEquals(""" 21 | running status: true 22 | volume value: 50 23 | """.trimIndent(), stereoSystem.currentState) 24 | 25 | remoteControl.pressButton("volume+") 26 | remoteControl.pressButton("volume+") 27 | remoteControl.pressButton("volume+") 28 | 29 | assertEquals(""" 30 | running status: true 31 | volume value: 80 32 | """.trimIndent(), stereoSystem.currentState) 33 | 34 | remoteControl.pressUndoButton() 35 | remoteControl.pressUndoButton() 36 | remoteControl.pressUndoButton() 37 | remoteControl.pressUndoButton() 38 | 39 | assertEquals(""" 40 | running status: false 41 | volume value: 50 42 | """.trimIndent(), stereoSystem.currentState) 43 | } 44 | 45 | @Test 46 | fun `test macro command`() { 47 | val stereoSystem = StereoSystem() 48 | val remoteControl = StereoSystemRemoteControl(mapOf( 49 | "party" to MacroCommand( 50 | TurnOnCommand(stereoSystem), 51 | IncreaseVolumeCommand(stereoSystem), 52 | IncreaseVolumeCommand(stereoSystem), 53 | IncreaseVolumeCommand(stereoSystem), 54 | IncreaseVolumeCommand(stereoSystem), 55 | IncreaseVolumeCommand(stereoSystem) 56 | ) 57 | )) 58 | 59 | remoteControl.pressButton("party") 60 | 61 | assertEquals(""" 62 | running status: true 63 | volume value: 100 64 | """.trimIndent(), stereoSystem.currentState) 65 | 66 | remoteControl.pressUndoButton() 67 | 68 | assertEquals(""" 69 | running status: false 70 | volume value: 50 71 | """.trimIndent(), stereoSystem.currentState) 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Strategy.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Strategy is a behavioral design pattern used to define a family of algorithms, 6 | * 7 | * encapsulate each one and make them interchangeable 8 | * 9 | */ 10 | 11 | // encapsulates the filtering algorithm 12 | interface FoodFilterStrategy { 13 | fun filter(items: List): List 14 | } 15 | 16 | class OnlyChipsFilterStrategy : FoodFilterStrategy { 17 | override fun filter(items: List): List { 18 | return items.filter { it.category == "chips" } 19 | } 20 | } 21 | 22 | class OnlyChocolateFilterStrategy : FoodFilterStrategy { 23 | override fun filter(items: List): List { 24 | return items.filter { it.category == "chocolate" } 25 | } 26 | } 27 | 28 | class PriceFilterStrategy(private val price: Int) : FoodFilterStrategy { 29 | override fun filter(items: List): List { 30 | return items.filter { it.price >= price } 31 | } 32 | } 33 | 34 | class SearchWordFilterStrategy(private val search: String) : FoodFilterStrategy { 35 | override fun filter(items: List): List { 36 | return items.filter { it.title.contains(search, ignoreCase = true) } 37 | } 38 | } 39 | 40 | data class FoodEntity( 41 | val title: String, 42 | val price: Int, 43 | val category: String 44 | ) 45 | 46 | // FoodStore returns a list of food filtered by strategy 47 | class FoodStore(private var filterStrategy: FoodFilterStrategy) { 48 | 49 | // we can change strategy 50 | fun changeStrategy(strategy: FoodFilterStrategy) { 51 | filterStrategy = strategy 52 | } 53 | 54 | fun foodItems(): List { 55 | val foodItems = fetchFoodItems() 56 | return filterStrategy.filter(foodItems) 57 | } 58 | 59 | private fun fetchFoodItems() = 60 | listOf( 61 | FoodEntity( 62 | "Lays Potato Chips Fried Crab Flavor", 63 | 2, 64 | "chips" 65 | ), 66 | FoodEntity( 67 | "Lay's Potato Chips, Classic", 68 | 3, 69 | "chips" 70 | ), 71 | FoodEntity( 72 | "Dove Chocolate", 73 | 3, 74 | "chocolate" 75 | ), 76 | FoodEntity( 77 | "Ritter Sport Chocolate", 78 | 4, 79 | "chocolate" 80 | ) 81 | ) 82 | 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Factory Method.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * A factory method is a generic design pattern that defines 6 | * 7 | * a common interface for creating objects in a superclass, 8 | * 9 | * allowing subclasses to change the type of objects they create. 10 | * 11 | */ 12 | 13 | abstract class House(private val address: String, private val price: Int) { 14 | 15 | override fun toString() = """ 16 | address = $address 17 | price = $price 18 | """.trimIndent() 19 | 20 | } 21 | 22 | // the factory method makes sense if we have a hierarchy of objects 23 | class WoodenCheapHouse(address: String) : House(address, 50_000) 24 | class WoodenAverageHouse(address: String) : House(address, 250_000) 25 | class WoodenExpensiveHouse(address: String) : House(address, 1_000_000) 26 | 27 | class StoneCheapHouse(address: String) : House(address, 45_000) 28 | class StoneAverageHouse(address: String) : House(address, 230_000) 29 | class StoneExpensiveHouse(address: String) : House(address, 900_000) 30 | 31 | // we have a common logic for every HouseCompany 32 | abstract class HouseCompany { 33 | 34 | private val alreadyBuiltHouses = mutableListOf() 35 | 36 | val examplesAlreadyBuiltHouses: String 37 | get() = alreadyBuiltHouses.joinToString("\n\n") 38 | 39 | fun orderHouse(address: String, cost: HouseCompanyCost): House { 40 | val house = buildHouse(address, cost) 41 | alreadyBuiltHouses.add(house) 42 | return house 43 | } 44 | 45 | // the subclasses define a specific implementation 46 | protected abstract fun buildHouse(address: String, cost: HouseCompanyCost): House 47 | 48 | enum class HouseCompanyCost { CHEAP, AVERAGE, EXPENSIVE } 49 | 50 | } 51 | 52 | // WoodenHouseCompany only builds wooden houses 53 | class WoodenHouseCompany : HouseCompany() { 54 | 55 | override fun buildHouse(address: String, cost: HouseCompanyCost) = when(cost) { 56 | HouseCompanyCost.CHEAP -> WoodenCheapHouse(address) 57 | HouseCompanyCost.AVERAGE -> WoodenAverageHouse(address) 58 | HouseCompanyCost.EXPENSIVE -> WoodenExpensiveHouse(address) 59 | } 60 | 61 | } 62 | 63 | // StoneHouseCompany only builds stone houses 64 | class StoneHouseCompany : HouseCompany() { 65 | 66 | override fun buildHouse(address: String, cost: HouseCompanyCost) = when(cost) { 67 | HouseCompanyCost.CHEAP -> StoneCheapHouse(address) 68 | HouseCompanyCost.AVERAGE -> StoneAverageHouse(address) 69 | HouseCompanyCost.EXPENSIVE -> StoneExpensiveHouse(address) 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /src/main/kotlin/search/BinarySearch.kt: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | /** 4 | * 5 | * Binary search algorithm only works for sorted lists and arrays 6 | * 7 | * best time: 1 8 | * worst time: log(n) 9 | * amount of memory: 1 10 | * 11 | */ 12 | 13 | class BinarySearch { 14 | 15 | // searches for an element in the array and returns the index of the searched element or -1 16 | fun > search(array: Array, element: T) : Int { 17 | if (array.isEmpty()) return -1 18 | 19 | var left = 0 20 | var right = array.size 21 | while (left < right) { 22 | val middle = left + (right - left) / 2 23 | if (array[middle] < element) { 24 | left = middle + 1 25 | } else if (array[middle] > element) { 26 | right = middle 27 | } else { 28 | return middle 29 | } 30 | } 31 | return -1 32 | } 33 | 34 | // recursive method 35 | // P.S. The tailrec modifier tells the compiler to optimize the recursion and turn it into an iterative version 36 | tailrec fun > searchRecursive(array: Array, element: T, left: Int = 0, right: Int = array.size - 1): Int { 37 | if (left <= right) { 38 | val middle = left + (right - left) / 2 39 | if (array[middle] == element) { 40 | return middle 41 | } 42 | return if (array[middle] > element) { 43 | searchRecursive(array, element, left, middle - 1) 44 | } else { 45 | searchRecursive(array, element, middle + 1, right) 46 | } 47 | } 48 | return -1 49 | } 50 | 51 | // finds the left border index to insert an element into a sorted array 52 | fun > leftBound(array: Array, element: T) : Int { 53 | if (array.isEmpty()) return 0 54 | 55 | var left = -1 56 | var right = array.size 57 | while ((right - left) > 1) { 58 | val middle = left + (right - left) / 2 59 | if (element > array[middle]) { 60 | left = middle 61 | } else { 62 | right = middle 63 | } 64 | } 65 | return left 66 | } 67 | 68 | // finds the right border index to insert an element into a sorted array 69 | fun > rightBound(array: Array, element: T) : Int { 70 | if (array.isEmpty()) return -1 71 | 72 | var left = -1 73 | var right = array.size 74 | while ((right - left) > 1) { 75 | val middle = (right + left) / 2 76 | if (element > array[middle]) { 77 | left = middle 78 | } else { 79 | right = middle 80 | } 81 | } 82 | return right 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/State.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * State is a behavioral design pattern that controls changes in the behavior of an object 6 | * 7 | * when its internal state changes 8 | * 9 | */ 10 | 11 | // the common interface for all states 12 | interface CocaColaState { 13 | fun insertDollar() : Boolean 14 | fun takeBackDollar() : Boolean 15 | fun pressButton(numberOfDrinks: Int) : Boolean 16 | } 17 | 18 | // we don't have drinks in the machine 19 | class EmptyCocaColaState : CocaColaState { 20 | override fun insertDollar() = false 21 | override fun takeBackDollar() = false 22 | override fun pressButton(numberOfDrinks: Int) = false 23 | override fun toString(): String = "EmptyCocaColaState" 24 | } 25 | 26 | // we have drinks, but we don't have a dollar in the machine 27 | class NoDollarCocaColaState(private val machine: CocaColaMachine) : CocaColaState { 28 | override fun insertDollar(): Boolean { 29 | machine.setState(HaveDollarCocaColaState(machine)) 30 | return true 31 | } 32 | 33 | override fun takeBackDollar() = false 34 | override fun pressButton(numberOfDrinks: Int) = false 35 | override fun toString(): String = "NoDollarCocaColaState" 36 | } 37 | 38 | // we have a dollar in the machine 39 | class HaveDollarCocaColaState(private val machine: CocaColaMachine) : CocaColaState { 40 | override fun insertDollar() = false 41 | override fun takeBackDollar(): Boolean { 42 | machine.setState(NoDollarCocaColaState(machine)) 43 | return true 44 | } 45 | override fun pressButton(numberOfDrinks: Int): Boolean { 46 | if (numberOfDrinks == 1) { 47 | machine.setState(EmptyCocaColaState()) 48 | } else { 49 | machine.setState(NoDollarCocaColaState(machine)) 50 | } 51 | return true 52 | } 53 | override fun toString(): String = "HaveDollarCocaColaState" 54 | } 55 | 56 | // CocaColaMachine switches between states 57 | class CocaColaMachine { 58 | 59 | private var numberOfDrinks = 3 60 | private var state: CocaColaState = NoDollarCocaColaState(this) 61 | 62 | val currentState: String 63 | get() = state.toString() 64 | 65 | fun insertDollar() = state.insertDollar() 66 | 67 | fun takeBackDollar() = state.takeBackDollar() 68 | 69 | fun pressButton(): Boolean { 70 | val isSuccess = state.pressButton(numberOfDrinks) 71 | if (isSuccess) { 72 | numberOfDrinks -= 1 73 | } 74 | return isSuccess 75 | } 76 | 77 | fun loadDrinks(count: Int) { 78 | numberOfDrinks += count 79 | if (numberOfDrinks > 0) { 80 | setState(NoDollarCocaColaState(this)) 81 | } else { 82 | setState(EmptyCocaColaState()) 83 | } 84 | } 85 | 86 | fun setState(newState: CocaColaState) { 87 | state = newState 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Bridge.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Bridge is a structural design pattern used to separate abstraction and implementation 6 | * 7 | * so they can change independently 8 | * 9 | */ 10 | 11 | // OperatingSystem implements an internal algorithm for drawing primitives (operating systems are implementations in this example) 12 | abstract class OperatingSystem(private val name: String, private val version: Int) { 13 | 14 | protected val lines = mutableListOf() 15 | protected val circles = mutableListOf() 16 | 17 | abstract fun drawLine(startX: Int, startY: Int, endX: Int, endY: Int) 18 | 19 | abstract fun drawCircle(centerX: Int, centerY: Int, radius: Int) 20 | 21 | override fun toString(): String { 22 | return """ 23 | $name $version 24 | Lines: 25 | ${lines.joinToString("\n")} 26 | Circles: 27 | ${circles.joinToString("\n")} 28 | """.trimIndent() 29 | } 30 | 31 | } 32 | 33 | // different implementations have different algorithms for drawing primitives 34 | class WindowsSystem : OperatingSystem("Windows", 10) { 35 | 36 | override fun drawLine(startX: Int, startY: Int, endX: Int, endY: Int) { 37 | lines.add("Windows graphic subsystem -> startX: $startX, startY: $startY, endX: $endX, endY: $endY") 38 | } 39 | 40 | override fun drawCircle(centerX: Int, centerY: Int, radius: Int) { 41 | circles.add("Windows graphic subsystem -> centerX: $centerX, centerY: $centerY, radius: $radius") 42 | } 43 | 44 | } 45 | 46 | class MacOSSystem : OperatingSystem("MacOS", 14) { 47 | 48 | override fun drawLine(startX: Int, startY: Int, endX: Int, endY: Int) { 49 | lines.add("MacOS graphic subsystem -> startX: $startX, startY: $startY, endX: $endX, endY: $endY") 50 | } 51 | 52 | override fun drawCircle(centerX: Int, centerY: Int, radius: Int) { 53 | circles.add("MacOS graphic subsystem -> centerX: $centerX, centerY: $centerY, radius: $radius") 54 | } 55 | 56 | } 57 | 58 | // we can add new primitives without changing OperatingSystem implementations (primitives are abstractions in this example) 59 | abstract class GraphicPrimitive { 60 | abstract fun draw(system: OperatingSystem) 61 | } 62 | 63 | class GraphicCirclePrimitive( 64 | private val centerX: Int, 65 | private val centerY: Int, 66 | private val radius: Int 67 | ) : GraphicPrimitive() { 68 | 69 | override fun draw(system: OperatingSystem) { 70 | system.drawCircle(centerX, centerY, radius) 71 | } 72 | 73 | } 74 | 75 | class GraphicLinePrimitive( 76 | private val startX: Int, 77 | private val startY: Int, 78 | private val endX: Int, 79 | private val endY: Int 80 | ) : GraphicPrimitive() { 81 | 82 | override fun draw(system: OperatingSystem) { 83 | system.drawLine(startX, startY, endX, endY) 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /src/test/kotlin/design_patterns/BuilderTest.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class BuilderTest { 7 | 8 | @Test 9 | fun test_first_variant() { 10 | val dnsServerAddress = "8.8.8.8" 11 | val callTimeout = 500 12 | val connectTimeout = 3_000 13 | val writeTimeout = 3_000 14 | val readTimeout = 3_000 15 | 16 | val httpClient = HttpConnectionClient1.Builder() 17 | .dnsServerAddress(dnsServerAddress) 18 | .callTimeout(callTimeout) 19 | .connectTimeout(connectTimeout) 20 | .writeTimeout(writeTimeout) 21 | .readTimeout(readTimeout) 22 | .build() 23 | 24 | val expected = """ 25 | dns -> $dnsServerAddress 26 | call timeout -> $callTimeout 27 | connect timeout -> $connectTimeout 28 | read timeout -> $readTimeout 29 | write timeout -> $writeTimeout 30 | """.trimIndent() 31 | 32 | assertEquals(expected, httpClient.toString()) 33 | } 34 | 35 | @Test 36 | fun test_the_second_variant() { 37 | val dnsServerAddress = "8.8.8.8" 38 | val callTimeout = 500 39 | val connectTimeout = 3_000 40 | val writeTimeout = 3_000 41 | val readTimeout = 3_000 42 | 43 | val httpClient = HttpConnectionClient2.newBuilder() 44 | .dnsServerAddress(dnsServerAddress) 45 | .callTimeout(callTimeout) 46 | .connectTimeout(connectTimeout) 47 | .writeTimeout(writeTimeout) 48 | .readTimeout(readTimeout) 49 | .build() 50 | 51 | val expected = """ 52 | dns -> $dnsServerAddress 53 | call timeout -> $callTimeout 54 | connect timeout -> $connectTimeout 55 | read timeout -> $readTimeout 56 | write timeout -> $writeTimeout 57 | """.trimIndent() 58 | 59 | assertEquals(expected, httpClient.toString()) 60 | } 61 | 62 | @Test 63 | fun test_third_variant() { 64 | val dnsServerAddress = "8.8.8.8" 65 | val callTimeout = 500 66 | val connectTimeout = 3_000 67 | val writeTimeout = 3_000 68 | val readTimeout = 3_000 69 | 70 | val httpClient = HttpConnectionClient3( 71 | dnsServerAddress = dnsServerAddress, 72 | callTimeout = callTimeout, 73 | connectTimeout = connectTimeout, 74 | readTimeout = readTimeout, 75 | writeTimeout = writeTimeout 76 | ) 77 | 78 | val expected = """ 79 | dns -> $dnsServerAddress 80 | call timeout -> $callTimeout 81 | connect timeout -> $connectTimeout 82 | read timeout -> $readTimeout 83 | write timeout -> $writeTimeout 84 | """.trimIndent() 85 | 86 | assertEquals(expected, httpClient.toString()) 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/PalindromeAlgorithmTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class PalindromeAlgorithmTest { 7 | 8 | private val palindromeAlgorithm = PalindromeAlgorithm() 9 | 10 | @Test 11 | fun `test empty string`() { 12 | assertEquals(true, palindromeAlgorithm.isPalindrome("")) 13 | 14 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("")) 15 | } 16 | 17 | @Test 18 | fun `test string with one symbol`() { 19 | assertEquals(true, palindromeAlgorithm.isPalindrome("a")) 20 | assertEquals(true, palindromeAlgorithm.isPalindrome("b")) 21 | assertEquals(true, palindromeAlgorithm.isPalindrome("c")) 22 | assertEquals(true, palindromeAlgorithm.isPalindrome("d")) 23 | assertEquals(true, palindromeAlgorithm.isPalindrome("e")) 24 | assertEquals(true, palindromeAlgorithm.isPalindrome("f")) 25 | 26 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("a")) 27 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("b")) 28 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("c")) 29 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("d")) 30 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("e")) 31 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("f")) 32 | 33 | } 34 | 35 | @Test 36 | fun `test string is palindrome`() { 37 | assertEquals(true, palindromeAlgorithm.isPalindrome("tenet")) 38 | assertEquals(true, palindromeAlgorithm.isPalindrome("madam")) 39 | assertEquals(true, palindromeAlgorithm.isPalindrome("racecar")) 40 | assertEquals(true, palindromeAlgorithm.isPalindrome("dad")) 41 | 42 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("tenet")) 43 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("madam")) 44 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("racecar")) 45 | assertEquals(true, palindromeAlgorithm.isPalindromeSimplifiedVersion("dad")) 46 | } 47 | 48 | @Test 49 | fun `test string is not palindrome`() { 50 | assertEquals(false, palindromeAlgorithm.isPalindrome("friend")) 51 | assertEquals(false, palindromeAlgorithm.isPalindrome("success")) 52 | assertEquals(false, palindromeAlgorithm.isPalindrome("mistake")) 53 | assertEquals(false, palindromeAlgorithm.isPalindrome("language")) 54 | 55 | assertEquals(false, palindromeAlgorithm.isPalindromeSimplifiedVersion("friend")) 56 | assertEquals(false, palindromeAlgorithm.isPalindromeSimplifiedVersion("success")) 57 | assertEquals(false, palindromeAlgorithm.isPalindromeSimplifiedVersion("mistake")) 58 | assertEquals(false, palindromeAlgorithm.isPalindromeSimplifiedVersion("language")) 59 | } 60 | 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Facade.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Facade is a structural design pattern that simplifies the interface to a group of interfaces 6 | * 7 | * with a more complex implementation 8 | * 9 | */ 10 | 11 | data class GoodsEntity( 12 | private val id: Long, 13 | private val name: String, 14 | private val description: String, 15 | private val price: Double 16 | ) 17 | 18 | class GoodsDatabase { 19 | private val cachedGoods = mutableListOf() 20 | 21 | fun save(goods: List) { 22 | cachedGoods.addAll(goods) 23 | } 24 | 25 | fun read() = cachedGoods 26 | } 27 | 28 | class GoodsNetworkService { 29 | 30 | fun fetch() = listOf( 31 | GoodsEntity( 32 | id = 1, 33 | name = "Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software 2nd Edition", 34 | description = "You know you don't want to reinvent the wheel, so you look to Design Patterns: the lessons learned by those who've faced the same software design problems.", 35 | price = 41.94 36 | ) 37 | ) 38 | 39 | } 40 | 41 | data class CategoryEntity( 42 | private val id: Long, 43 | private val name: String 44 | ) 45 | 46 | class CategoryDatabase { 47 | private val cachedCategories = mutableListOf() 48 | 49 | fun save(goods: List) { 50 | cachedCategories.addAll(goods) 51 | } 52 | 53 | fun read() = cachedCategories 54 | } 55 | 56 | class CategoryNetworkService { 57 | 58 | fun fetch() = listOf( 59 | CategoryEntity( 60 | id = 1, 61 | name = "Books" 62 | ) 63 | ) 64 | 65 | } 66 | 67 | data class GoodsResult( 68 | val goods: List, 69 | val categories: List 70 | ) 71 | 72 | // we have a group of interfaces (databases and network services) 73 | class GoodsRepository( 74 | private val goodsDatabase: GoodsDatabase, 75 | private val goodsNetworkService: GoodsNetworkService, 76 | private val categoryDatabase: CategoryDatabase, 77 | private val categoryNetworkService: CategoryNetworkService 78 | ) { 79 | 80 | // we need a simpler interface 81 | fun goodsAndCategories() : GoodsResult { 82 | val goods = goodsDatabase.read().toMutableList() 83 | if (goods.isEmpty()) { 84 | val networkGoods = goodsNetworkService.fetch() 85 | goodsDatabase.save(networkGoods) 86 | goods.addAll(networkGoods) 87 | } 88 | 89 | val categories = categoryDatabase.read().toMutableList() 90 | if (categories.isEmpty()) { 91 | val networkCategories = categoryNetworkService.fetch() 92 | categoryDatabase.save(networkCategories) 93 | categories.addAll(networkCategories) 94 | } 95 | 96 | return GoodsResult( 97 | goods = goods, 98 | categories = categories 99 | ) 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/test/kotlin/structures/GraphTest.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class GraphTest { 7 | 8 | @Test 9 | fun `test add and remove vertexes`() { 10 | val graph = Graph() 11 | 12 | graph.addVertex(10) 13 | graph.addVertex(20) 14 | graph.addVertex(30) 15 | 16 | graph.addEdge(10, 20) 17 | graph.addEdge(10, 30) 18 | graph.addEdge(20, 30) 19 | 20 | assertEquals(listOf(20, 30), graph.connectedVertexes(10)) 21 | assertEquals(listOf(10, 30), graph.connectedVertexes(20)) 22 | 23 | graph.removeVertex(10) 24 | 25 | assertEquals(listOf(20), graph.connectedVertexes(30)) 26 | assertEquals(listOf(30), graph.connectedVertexes(20)) 27 | 28 | graph.removeVertex(20) 29 | 30 | assertEquals(listOf(), graph.connectedVertexes(30)) 31 | } 32 | 33 | @Test 34 | fun `test remove edges`() { 35 | val graph = Graph() 36 | 37 | graph.addVertex(10) 38 | graph.addVertex(20) 39 | graph.addVertex(30) 40 | 41 | graph.addEdge(10, 20) 42 | graph.addEdge(10, 30) 43 | graph.addEdge(20, 30) 44 | 45 | assertEquals(listOf(20, 30), graph.connectedVertexes(10)) 46 | 47 | graph.removeEdge(10, 20) 48 | 49 | assertEquals(listOf(30), graph.connectedVertexes(10)) 50 | 51 | graph.removeEdge(10, 30) 52 | 53 | assertEquals(listOf(), graph.connectedVertexes(10)) 54 | assertEquals(listOf(30), graph.connectedVertexes(20)) 55 | 56 | graph.removeEdge(20, 30) 57 | 58 | assertEquals(listOf(), graph.connectedVertexes(20)) 59 | } 60 | 61 | @Test 62 | fun `test depthFirstTraversal`() { 63 | val graph = Graph() 64 | 65 | graph.addVertex(10) 66 | graph.addVertex(20) 67 | graph.addVertex(30) 68 | 69 | graph.addEdge(10, 20) 70 | graph.addEdge(20, 30) 71 | graph.addEdge(30, 10) 72 | 73 | assertEquals(listOf(10, 20, 30), graph.depthFirstTraversal()) 74 | 75 | graph.removeVertex(10) 76 | 77 | assertEquals(listOf(20, 30), graph.depthFirstTraversal()) 78 | 79 | graph.removeVertex(30) 80 | 81 | assertEquals(listOf(20), graph.depthFirstTraversal()) 82 | 83 | graph.removeVertex(20) 84 | 85 | assertEquals(listOf(), graph.depthFirstTraversal()) 86 | } 87 | 88 | @Test 89 | fun `test breadthFirstTraversal`() { 90 | val graph = Graph() 91 | 92 | graph.addVertex(10) 93 | graph.addVertex(20) 94 | graph.addVertex(30) 95 | graph.addVertex(40) 96 | 97 | graph.addEdge(10, 20) 98 | graph.addEdge(20, 30) 99 | graph.addEdge(20, 40) 100 | 101 | assertEquals(listOf(10, 20, 30, 40), graph.breadthFirstTraversal()) 102 | 103 | graph.removeVertex(10) 104 | 105 | assertEquals(listOf(20, 30, 40), graph.breadthFirstTraversal()) 106 | 107 | graph.removeVertex(40) 108 | 109 | assertEquals(listOf(20, 30), graph.breadthFirstTraversal()) 110 | 111 | graph.removeVertex(20) 112 | graph.removeVertex(30) 113 | 114 | assertEquals(listOf(), graph.breadthFirstTraversal()) 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Mediator.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Mediator is a behavioral design pattern that reduces the coupling of many classes among themselves 6 | * 7 | * by moving these couplings into one mediator class 8 | * 9 | */ 10 | 11 | // SoftwareDevelopmentManager is a mediator for the customer, designer, programmer and tester 12 | class SoftwareDevelopmentManager { 13 | 14 | private lateinit var customer: SoftwareDevelopmentMember 15 | private lateinit var designer: SoftwareDevelopmentMember 16 | private lateinit var programmer: SoftwareDevelopmentMember 17 | private lateinit var tester: SoftwareDevelopmentMember 18 | 19 | fun changeCustomer(member: SoftwareDevelopmentMember) { customer = member } 20 | fun changeDesigner(member: SoftwareDevelopmentMember) { designer = member } 21 | fun changeProgrammer(member: SoftwareDevelopmentMember) { programmer = member } 22 | fun changeTester(member: SoftwareDevelopmentMember) { tester = member } 23 | 24 | private val stages = mutableListOf() 25 | val stagesAsString: String 26 | get() = stages.joinToString("\n") 27 | 28 | // members do not interact directly with each other because the mediator does it 29 | // which reduces the coupling between them 30 | fun nextStage(work: String, member: SoftwareDevelopmentMember) { 31 | val finishedStage = when (member) { 32 | 33 | is CustomerSoftwareDevelopmentMember -> designer.receiveWork(work) 34 | 35 | is DesignerSoftwareDevelopmentMember -> programmer.receiveWork(work) 36 | 37 | is ProgrammerSoftwareDevelopmentMember -> tester.receiveWork(work) 38 | 39 | is TesterSoftwareDevelopmentMember -> customer.receiveWork(work) 40 | 41 | else -> "" 42 | } 43 | 44 | stages.add(finishedStage) 45 | } 46 | 47 | } 48 | 49 | // SoftwareDevelopmentMember works with other members through the mediator 50 | abstract class SoftwareDevelopmentMember(protected val mediator: SoftwareDevelopmentManager) { 51 | 52 | abstract fun receiveWork(work: String): String 53 | 54 | abstract fun finishWork() 55 | 56 | } 57 | 58 | class CustomerSoftwareDevelopmentMember(mediator: SoftwareDevelopmentManager) : SoftwareDevelopmentMember(mediator) { 59 | override fun receiveWork(work: String) = "Customer accepted the work: $work" 60 | 61 | override fun finishWork() { 62 | mediator.nextStage("design development", this) 63 | } 64 | } 65 | 66 | class DesignerSoftwareDevelopmentMember(mediator: SoftwareDevelopmentManager) : SoftwareDevelopmentMember(mediator) { 67 | override fun receiveWork(work: String) = "Designer accepted the work: $work" 68 | 69 | override fun finishWork() { 70 | mediator.nextStage("writing code", this) 71 | } 72 | } 73 | 74 | class ProgrammerSoftwareDevelopmentMember(mediator: SoftwareDevelopmentManager) : SoftwareDevelopmentMember(mediator) { 75 | override fun receiveWork(work: String) = "Programmer accepted the work: $work" 76 | 77 | override fun finishWork() { 78 | mediator.nextStage("application testing", this) 79 | } 80 | } 81 | 82 | class TesterSoftwareDevelopmentMember(mediator: SoftwareDevelopmentManager) : SoftwareDevelopmentMember(mediator) { 83 | override fun receiveWork(work: String) = "Tester accepted the work: $work" 84 | 85 | override fun finishWork() { 86 | mediator.nextStage("business valuation", this) 87 | } 88 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/GraphWithWeightsTest.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | class GraphWithWeightsTest { 7 | 8 | @Test 9 | fun `test add and remove vertexes`() { 10 | val graph = GraphWithWeights() 11 | 12 | graph.addVertex(10) 13 | graph.addVertex(20) 14 | graph.addVertex(30) 15 | 16 | graph.addEdge(10, 20, 1) 17 | graph.addEdge(10, 30, 2) 18 | graph.addEdge(20, 30, 3) 19 | 20 | assertEquals(listOf( 21 | "vertex -> 20, cost -> 1", 22 | "vertex -> 30, cost -> 2" 23 | ), graph.connectedVertexesWithWeights(10)) 24 | 25 | graph.removeVertex(10) 26 | 27 | assertEquals(listOf(), graph.connectedVertexesWithWeights(10)) 28 | 29 | assertEquals(listOf("vertex -> 30, cost -> 3"), graph.connectedVertexesWithWeights(20)) 30 | 31 | graph.removeVertex(20) 32 | 33 | assertEquals(listOf(), graph.connectedVertexesWithWeights(10)) 34 | } 35 | 36 | @Test 37 | fun `test remove edges`() { 38 | val graph = GraphWithWeights() 39 | 40 | graph.addVertex(10) 41 | graph.addVertex(20) 42 | graph.addVertex(30) 43 | 44 | graph.addEdge(10, 20, 1) 45 | graph.addEdge(10, 30, 2) 46 | graph.addEdge(20, 30, 3) 47 | 48 | assertEquals(listOf( 49 | "vertex -> 20, cost -> 1", 50 | "vertex -> 30, cost -> 2" 51 | ), graph.connectedVertexesWithWeights(10)) 52 | 53 | graph.removeEdge(10, 20) 54 | 55 | assertEquals(listOf("vertex -> 30, cost -> 2"), graph.connectedVertexesWithWeights(10)) 56 | 57 | graph.removeEdge(10, 30) 58 | 59 | assertEquals(listOf(), graph.connectedVertexesWithWeights(10)) 60 | assertEquals(listOf("vertex -> 30, cost -> 3"), graph.connectedVertexesWithWeights(20)) 61 | 62 | graph.removeEdge(20, 30) 63 | 64 | assertEquals(listOf(), graph.connectedVertexesWithWeights(10)) 65 | } 66 | 67 | @Test 68 | fun test_dijkstraAlgorithm() { 69 | val graph = GraphWithWeights() 70 | 71 | graph.addVertex(1) 72 | graph.addVertex(2) 73 | graph.addVertex(3) 74 | graph.addVertex(4) 75 | graph.addVertex(5) 76 | 77 | graph.addEdge(1, 2, 10) 78 | graph.addEdge(1, 5, 100) 79 | graph.addEdge(2, 3, 20) 80 | graph.addEdge(2, 4, 5) 81 | graph.addEdge(3, 4, 15) 82 | graph.addEdge(4, 5, 5) 83 | 84 | assertEquals(linkedMapOf( 85 | 1 to 0, 86 | 2 to 10, 87 | 3 to 30, 88 | 4 to 15, 89 | 5 to 20 90 | ), graph.dijkstraAlgorithm()) 91 | 92 | graph.removeVertex(4) 93 | 94 | assertEquals(linkedMapOf( 95 | 1 to 0, 96 | 2 to 10, 97 | 3 to 30, 98 | 5 to 100 99 | ), graph.dijkstraAlgorithm()) 100 | 101 | graph.removeVertex(5) 102 | 103 | assertEquals(linkedMapOf( 104 | 1 to 0, 105 | 2 to 10, 106 | 3 to 30 107 | ), graph.dijkstraAlgorithm()) 108 | 109 | graph.removeVertex(1) 110 | graph.removeVertex(2) 111 | graph.removeVertex(3) 112 | 113 | assertEquals(mapOf(), graph.dijkstraAlgorithm()) 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/MaxHeap.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.lang.IllegalArgumentException 4 | 5 | /** 6 | * 7 | * Max-heap is a binary tree in which each parent is greater than its children 8 | * 9 | */ 10 | 11 | class MaxHeap(private val maxSize: Int) { 12 | 13 | private val data = IntArray(maxSize + 1) { Int.MIN_VALUE } 14 | 15 | private val root = 1 16 | private var size = 0 17 | 18 | val isEmpty: Boolean 19 | get() = size == 0 20 | 21 | private val Int.parent 22 | get() = this / 2 23 | 24 | private val Int.leftChild 25 | get() = this * 2 26 | 27 | private val Int.rightChild 28 | get() = this * 2 + 1 29 | 30 | init { 31 | if (maxSize <= 0) throw IllegalArgumentException("The heap must have maxSize larger than zero") 32 | 33 | data[0] = Int.MAX_VALUE 34 | } 35 | 36 | // Complexity: O(logn) 37 | fun add(element: Int) { 38 | if (size >= maxSize) throw IllegalStateException("The heap is full!") 39 | 40 | data[++size] = Int.MIN_VALUE 41 | set(size, element) 42 | } 43 | 44 | // Complexity: O(logn) 45 | fun set(index: Int, newValue: Int) { 46 | if (index < root || index > maxSize) throw IllegalArgumentException("The heap doesn't have the such index: $index!") 47 | if (newValue < data[index]) throw IllegalArgumentException("The new value $newValue is less than the previous: ${data[index]}") 48 | 49 | data[index] = newValue 50 | 51 | var current = index 52 | while (current > root && data[current.parent] < data[current]) { 53 | swap(current, current.parent) 54 | current = current.parent 55 | } 56 | } 57 | 58 | // Complexity: O(1) 59 | fun peekMax() = data[root] 60 | 61 | // Complexity: O(logn) 62 | fun popMax(): Int { 63 | if (size < 1) throw IllegalStateException("The heap is empty!") 64 | 65 | val max = data[root] 66 | data[root] = data[size--] 67 | heapify(root) 68 | return max 69 | } 70 | 71 | private tailrec fun heapify(pos: Int) { 72 | val leftChild = pos.leftChild 73 | val rightChild = pos.rightChild 74 | var largest = pos 75 | 76 | if (leftChild <= size && data[leftChild] > data[largest]) { 77 | largest = leftChild 78 | } 79 | 80 | if (rightChild <= size && data[rightChild] > data[largest]) { 81 | largest = rightChild 82 | } 83 | 84 | if (largest != pos) { 85 | swap(pos, largest) 86 | heapify(largest) 87 | } 88 | } 89 | 90 | private fun swap(index1: Int, index2: Int) { 91 | val tmp = data[index1] 92 | data[index1] = data[index2] 93 | data[index2] = tmp 94 | } 95 | 96 | companion object { 97 | fun create(intArray: IntArray): MaxHeap { 98 | val arraySize = intArray.size 99 | val heap = MaxHeap(arraySize) 100 | heap.size = intArray.size 101 | 102 | var index = 0 103 | while (index < arraySize) { 104 | heap.data[index + 1] = intArray[index] 105 | index++ 106 | } 107 | 108 | var pos = arraySize / 2 109 | while (pos >= 0) { 110 | heap.heapify(pos) 111 | pos-- 112 | } 113 | return heap 114 | } 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/MinHeap.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.lang.IllegalArgumentException 4 | 5 | 6 | /** 7 | * 8 | * Min-heap is a binary tree in which each parent is smaller than its children 9 | * 10 | */ 11 | 12 | class MinHeap(private val maxSize: Int) { 13 | 14 | private val data = IntArray(maxSize + 1) { Int.MAX_VALUE } 15 | 16 | private val root = 1 17 | private var size = 0 18 | 19 | val isEmpty: Boolean 20 | get() = size == 0 21 | 22 | private val Int.parent 23 | get() = this / 2 24 | 25 | private val Int.leftChild 26 | get() = this * 2 27 | 28 | private val Int.rightChild 29 | get() = this * 2 + 1 30 | 31 | init { 32 | if (maxSize <= 0) throw IllegalArgumentException("The heap must have maxSize larger than zero") 33 | 34 | data[0] = Int.MIN_VALUE 35 | } 36 | 37 | // Complexity: O(logn) 38 | fun add(element: Int) { 39 | if (size >= maxSize) throw IllegalStateException("The heap is full!") 40 | 41 | data[++size] = Int.MAX_VALUE 42 | set(size, element) 43 | } 44 | 45 | // Complexity: O(logn) 46 | fun set(index: Int, newValue: Int) { 47 | if (index < root || index > maxSize) throw IllegalArgumentException("The heap doesn't have the such index: $index!") 48 | if (newValue > data[index]) throw IllegalArgumentException("The new value $newValue is more than the previous: ${data[index]}") 49 | 50 | data[index] = newValue 51 | 52 | var current = index 53 | while (current > root && data[current.parent] > data[current]) { 54 | swap(current, current.parent) 55 | current = current.parent 56 | } 57 | } 58 | 59 | // Complexity: O(1) 60 | fun peekMin() = data[root] 61 | 62 | // Complexity: O(logn) 63 | fun popMin(): Int { 64 | if (size < 1) throw IllegalStateException("The heap is empty!") 65 | 66 | val max = data[root] 67 | data[root] = data[size--] 68 | heapify(root) 69 | return max 70 | } 71 | 72 | private tailrec fun heapify(pos: Int) { 73 | val leftChild = pos.leftChild 74 | val rightChild = pos.rightChild 75 | var minimum = pos 76 | 77 | if (leftChild <= size && data[leftChild] < data[minimum]) { 78 | minimum = leftChild 79 | } 80 | 81 | if (rightChild <= size && data[rightChild] < data[minimum]) { 82 | minimum = rightChild 83 | } 84 | 85 | if (minimum != pos) { 86 | swap(pos, minimum) 87 | heapify(minimum) 88 | } 89 | } 90 | 91 | private fun swap(index1: Int, index2: Int) { 92 | val tmp = data[index1] 93 | data[index1] = data[index2] 94 | data[index2] = tmp 95 | } 96 | 97 | companion object { 98 | fun create(intArray: IntArray): MinHeap { 99 | val arraySize = intArray.size 100 | val heap = MinHeap(arraySize) 101 | heap.size = intArray.size 102 | 103 | var index = 0 104 | while (index < arraySize) { 105 | heap.data[index + 1] = intArray[index] 106 | index++ 107 | } 108 | 109 | var pos = arraySize / 2 110 | while (pos >= 0) { 111 | heap.heapify(pos) 112 | pos-- 113 | } 114 | return heap 115 | } 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Proxy.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Proxy is a structural design pattern that provides an object 6 | * 7 | * that controls access to another object by intercepting all calls. 8 | * 9 | * There are several types of this pattern: 10 | * 11 | * 1) Logging proxy - saves all calls to a real object with their parameters to the log (shown in this example) 12 | * 2) Remote proxy - provides communication with an object which is located in a different address space or on a remote machine (Java Remote Procedure Call) 13 | * 3) Virtual proxy - ensures that a real object is created only when it is actually needed (shown in this example) 14 | * 4) Protection proxy - checks whether the caller has the necessary rights to execute the request 15 | * 5) Caching proxy - provides temporary storage of calculation results 16 | * 6) Synchronizing proxy - performs synchronized access control to an object in a multi-threaded environment (Collections.synchronized** methods) 17 | * 7) Smart reference proxy - performs additional actions when a link is created to an object 18 | * 19 | */ 20 | 21 | interface AudioPlayer { 22 | val currentState: String 23 | 24 | fun play(audio: String) 25 | fun pause() 26 | fun resume() 27 | fun stop() 28 | } 29 | 30 | // we have the simple player simulation 31 | class AudioPlayerImpl : AudioPlayer { 32 | 33 | private var currentAudio: String = "" 34 | private var currentStatus = MediaPlayerStatus.NOT_INITIALIZED 35 | 36 | override val currentState: String 37 | get() = """ 38 | current audio: $currentAudio 39 | current status: $currentStatus 40 | """.trimIndent() 41 | 42 | override fun play(audio: String) { 43 | if (audio.isNotBlank()) { 44 | currentAudio = audio 45 | currentStatus = MediaPlayerStatus.RUNNING 46 | } 47 | } 48 | override fun pause() { 49 | if (currentStatus == MediaPlayerStatus.RUNNING) { 50 | currentStatus = MediaPlayerStatus.PAUSED 51 | } 52 | } 53 | 54 | override fun resume() { 55 | if (currentStatus == MediaPlayerStatus.PAUSED) { 56 | currentStatus = MediaPlayerStatus.RUNNING 57 | } 58 | } 59 | 60 | override fun stop() { 61 | if (currentStatus != MediaPlayerStatus.NOT_INITIALIZED) { 62 | currentAudio = "" 63 | currentStatus = MediaPlayerStatus.NOT_INITIALIZED 64 | } 65 | } 66 | 67 | enum class MediaPlayerStatus { 68 | NOT_INITIALIZED, PAUSED, RUNNING 69 | } 70 | 71 | } 72 | 73 | // MediaPlayerProxy defers the creation of a real object (virtual proxy) and logs all its calls (logging proxy) 74 | class AudioPlayerProxy : AudioPlayer { 75 | 76 | // deferred creation of the real player 77 | private val player: AudioPlayer by lazy { AudioPlayerImpl() } 78 | 79 | override val currentState: String 80 | get() = player.currentState 81 | 82 | override fun play(audio: String) { 83 | player.play(audio) 84 | // logs real player calls 85 | println("play audio: $audio") 86 | } 87 | 88 | override fun pause() { 89 | player.pause() 90 | println("pause") 91 | } 92 | 93 | override fun resume() { 94 | player.resume() 95 | println("resume") 96 | } 97 | 98 | override fun stop() { 99 | player.stop() 100 | println("stop") 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/Graph.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.util.LinkedList 4 | import kotlin.collections.LinkedHashSet 5 | 6 | /** 7 | * 8 | * Graph is a non-linear data structure consisting of vertices and edges. 9 | * 10 | * The vertices are sometimes also referred to as nodes and the edges are lines or arcs that connect any two nodes in the graph. 11 | * 12 | * More formally a Graph is composed of a set of vertices V and a set of edges E. The graph is denoted by G(E, V). 13 | * 14 | * Undirected graph is a type of graph where the edges have no specified direction assigned to the them. 15 | * 16 | */ 17 | 18 | class Graph { 19 | 20 | private val data = linkedMapOf, MutableList>>() 21 | 22 | // Complexity: O(1) 23 | fun addVertex(value: T) { 24 | data.putIfAbsent(Vertex(value), mutableListOf()) 25 | } 26 | 27 | // Complexity: O(n) 28 | fun removeVertex(value: T) { 29 | val removingVertex = Vertex(value) 30 | data.values.forEach { list -> 31 | list.remove(removingVertex) 32 | } 33 | data.remove(removingVertex) 34 | } 35 | 36 | // Complexity: O(1) 37 | fun addEdge(value1: T, value2: T) { 38 | val vertex1 = Vertex(value1) 39 | val vertex2 = Vertex(value2) 40 | data[vertex1]?.add(vertex2) 41 | data[vertex2]?.add(vertex1) 42 | } 43 | 44 | // Complexity: O(1) 45 | fun removeEdge(value1: T, value2: T) { 46 | val vertex1 = Vertex(value1) 47 | val vertex2 = Vertex(value2) 48 | data[vertex1]?.remove(vertex2) 49 | data[vertex2]?.remove(vertex1) 50 | } 51 | 52 | // returns the associated vertices with the given vertex value 53 | fun connectedVertexes(value: T) = data[Vertex(value)]?.map { it.value } ?: emptyList() 54 | 55 | /** 56 | * 57 | * Traversal of the graph in depth, 58 | * 59 | * returns all vertices of the graph 60 | * 61 | */ 62 | fun depthFirstTraversal() : List { 63 | val firstVertex = data.keys.firstOrNull() ?: return emptyList() 64 | 65 | val visited = LinkedHashSet() 66 | val queue = LinkedList>() 67 | queue.push(firstVertex) 68 | while (queue.isNotEmpty()) { 69 | val vertex = queue.pollFirst() 70 | if (!visited.contains(vertex.value)) { 71 | visited.add(vertex.value) 72 | queue.addAll(data[vertex] ?: emptyList()) 73 | } 74 | } 75 | return visited.toList() 76 | } 77 | 78 | /** 79 | * 80 | * Traversal of the graph in breadth, 81 | * 82 | * returns all vertices of the graph 83 | * 84 | */ 85 | fun breadthFirstTraversal() : List { 86 | val firstVertex = data.keys.firstOrNull() ?: return emptyList() 87 | 88 | val visited = LinkedHashSet() 89 | val queue = LinkedList>() 90 | queue.add(firstVertex) 91 | visited.add(firstVertex.value) 92 | while (queue.isNotEmpty()) { 93 | val vertex = queue.pollFirst() 94 | data[vertex]?.forEach { connectedVertex -> 95 | if (!visited.contains(connectedVertex.value)) { 96 | visited.add(connectedVertex.value) 97 | queue.add(connectedVertex) 98 | } 99 | } 100 | } 101 | return visited.toList() 102 | } 103 | 104 | private data class Vertex(val value: T) 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Visitor.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Visitor is a behavioral design pattern that allows you to add a new operation 6 | * 7 | * to an entire class hierarchy without changing the code of these classes. 8 | * 9 | */ 10 | 11 | interface PonyVisitor { 12 | fun visitPonies(vararg ponies: LittlePony) : String 13 | fun visitEarthPony(pony: EarthPony) : String 14 | fun visitUnicorn(pony: Unicorn) : String 15 | fun visitPegasus(pony: Pegasus) : String 16 | } 17 | 18 | class JsonVisitor : PonyVisitor { 19 | 20 | override fun visitPonies(vararg ponies: LittlePony) : String { 21 | val poniesString = ponies.joinToString(",\n") { pony -> pony.accept(this) } 22 | return "[$poniesString]" 23 | } 24 | 25 | override fun visitEarthPony(pony: EarthPony): String { 26 | return """ 27 | { 28 | "what" : "earth pony", 29 | "name" : "${pony.name()}", 30 | "cutie_mark" : "${pony.cutie()} 31 | } 32 | """.trimIndent() 33 | } 34 | 35 | override fun visitUnicorn(pony: Unicorn): String { 36 | return """ 37 | { 38 | "what" : "unicorn", 39 | "name" : "${pony.name()}", 40 | "cutie_mark" : "${pony.cutie()} 41 | } 42 | """.trimIndent() 43 | } 44 | 45 | override fun visitPegasus(pony: Pegasus): String { 46 | return """ 47 | { 48 | "what" : "pegasus", 49 | "name" : "${pony.name()}", 50 | "cutie_mark" : "${pony.cutie()} 51 | } 52 | """.trimIndent() 53 | } 54 | 55 | } 56 | 57 | class XmlVisitor : PonyVisitor { 58 | 59 | override fun visitPonies(vararg ponies: LittlePony) : String { 60 | val poniesString = ponies.joinToString("\n") { pony -> pony.accept(this) } 61 | return "\n$poniesString\n" 62 | } 63 | 64 | override fun visitEarthPony(pony: EarthPony): String { 65 | return """ 66 | 67 | earth pony 68 | ${pony.name()} 69 | ${pony.cutie()} 70 | 71 | """.trimIndent() 72 | } 73 | 74 | override fun visitUnicorn(pony: Unicorn): String { 75 | return """ 76 | 77 | unicorn 78 | ${pony.name()} 79 | ${pony.cutie()} 80 | 81 | """.trimIndent() 82 | } 83 | 84 | override fun visitPegasus(pony: Pegasus): String { 85 | return """ 86 | 87 | pegasus 88 | ${pony.name()} 89 | ${pony.cutie()} 90 | 91 | """.trimIndent() 92 | } 93 | 94 | } 95 | 96 | abstract class LittlePony(private val name: String, private val cutieMark: String) { 97 | 98 | fun name() = name 99 | fun cutie() = cutieMark 100 | 101 | abstract fun accept(visitor: PonyVisitor) : String 102 | } 103 | 104 | class EarthPony(name: String, cutieMark: String) : LittlePony(name, cutieMark) { 105 | override fun accept(visitor: PonyVisitor) = visitor.visitEarthPony(this) 106 | } 107 | 108 | class Unicorn(name: String, cutieMark: String) : LittlePony(name, cutieMark) { 109 | override fun accept(visitor: PonyVisitor) = visitor.visitUnicorn(this) 110 | 111 | } 112 | 113 | class Pegasus(name: String, cutieMark: String) : LittlePony(name, cutieMark) { 114 | override fun accept(visitor: PonyVisitor) = visitor.visitPegasus(this) 115 | } 116 | 117 | -------------------------------------------------------------------------------- /src/main/kotlin/structures/DynamicArray.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.lang.IllegalStateException 4 | 5 | /** 6 | * 7 | * Dynamic array or array list is a random access, variable-size list data structure 8 | * 9 | * that allows elements to be added or removed, in Java this is java.util.ArrayList 10 | * 11 | * P.S. Kotlin lists use under the hood java.util.ArrayList on the JVM 12 | * 13 | * Example: 14 | * 15 | * 1) val numbers = listOf(1, 2, 3) // java.util.ArrayList 16 | * 17 | * 2) val symbols = mutableListOf('a', 'b', 'c') // also java.util.ArrayList 18 | * 19 | */ 20 | 21 | class DynamicArray(private var capacity: Int = 10) { 22 | 23 | private var data = arrayOfNulls(capacity) 24 | private var size = 0 25 | 26 | /** 27 | * Complexity: 28 | * worst time - O(n) because increaseSize() is called 29 | * best time - O(1) 30 | * average time - O(1) 31 | */ 32 | fun add(value: T) { 33 | if (size <= data.size - 1) { 34 | data[size] = value 35 | } else { 36 | increaseSize() 37 | data[size] = value 38 | } 39 | size += 1 40 | } 41 | 42 | /** 43 | * Complexity: 44 | * worst time: O(n) 45 | * best time: O(n) 46 | * average time: O(n) 47 | */ 48 | fun remove(value: T) : Boolean { 49 | var foundedIndex = -1 50 | 51 | for (i in data.indices) { 52 | if (data[i] == value) { 53 | foundedIndex = i 54 | break 55 | } 56 | } 57 | 58 | if (foundedIndex == -1) return false 59 | 60 | for (i in foundedIndex until data.size - 1) { 61 | data[i] = data[i + 1] 62 | } 63 | 64 | size -= 1 65 | 66 | return true 67 | } 68 | 69 | /** 70 | * Complexity: 71 | * worst time: O(n) 72 | * best time: O(1) 73 | * average time: O(n) 74 | */ 75 | fun contains(value: T): Boolean { 76 | for (i in data.indices) { 77 | if (data[i] == value) { 78 | return true 79 | } 80 | } 81 | 82 | return false 83 | } 84 | 85 | /** 86 | * Complexity: 87 | * worst time: O(1) 88 | * best time: O(1) 89 | * average time: O(1) 90 | */ 91 | fun set(index: Int, value: T): T? { 92 | if (index !in 0 until size) throw IllegalStateException("The index $index is out of bounds!") 93 | 94 | val oldValue = data[index] 95 | 96 | data[index] = value 97 | 98 | return oldValue as? T 99 | } 100 | 101 | /** 102 | * Complexity: 103 | * worst time: O(1) 104 | * best time: O(1) 105 | * average time: O(1) 106 | */ 107 | fun get(index: Int) : T { 108 | if (index !in data.indices) throw IllegalStateException("The index $index is out of bounds!") 109 | 110 | return data[index] as T 111 | } 112 | 113 | override fun toString(): String { 114 | val builder = StringBuilder() 115 | builder.append("capacity: $capacity\n") 116 | builder.append("size: $size\n") 117 | 118 | builder.append("elements: ") 119 | for (i in 0 until size - 1) { 120 | builder.append("${data[i]}, ") 121 | } 122 | builder.append(data[size - 1]) 123 | 124 | return builder.toString() 125 | } 126 | 127 | private fun increaseSize() { 128 | capacity = (capacity * INCREASE_SIZE_COEFFICIENT).toInt() 129 | val newArray = arrayOfNulls(capacity) 130 | for (i in data.indices) { 131 | newArray[i] = data[i] 132 | } 133 | data = newArray 134 | } 135 | 136 | companion object { 137 | private const val INCREASE_SIZE_COEFFICIENT = 1.5f 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /src/main/kotlin/sorting/MergeSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | /** 4 | * 5 | * Merge sort is a divide-and-conquer algorithm that was invented by John von Neumann in 1945 6 | * 7 | * Conceptually, a merge sort works as follows: 8 | * 9 | * 1) Divide the unsorted list into n sublists, each containing one element (a list of one element is considered sorted). 10 | * 11 | * 2) Repeatedly merge sublists to produce new sorted sublists until there is only one sublist remaining. 12 | * This will be the sorted list. 13 | * 14 | */ 15 | 16 | class MergeSort { 17 | 18 | /** 19 | * worst time: n * log(n) 20 | * best time: n * log(n) 21 | * average time: n * log(n) 22 | * 23 | * amount of memory: n 24 | */ 25 | fun > sort(array: Array) { 26 | val arraySize = array.size 27 | val temporaryArray = array.copyOf() 28 | 29 | var windowSize = 1 30 | while (windowSize < arraySize) { 31 | 32 | var left = 0 33 | while (left + windowSize < arraySize) { 34 | 35 | val middle = left + windowSize 36 | var right = middle + windowSize 37 | if (right > arraySize) right = arraySize 38 | 39 | var i = left 40 | var k = left 41 | var j = middle 42 | while (i < middle && j < right) { 43 | if (array[i] <= array[j]) { 44 | temporaryArray[k] = array[i] 45 | i++ 46 | } else { 47 | temporaryArray[k] = array[j] 48 | j++ 49 | } 50 | k++ 51 | } 52 | 53 | while (i < middle) { 54 | temporaryArray[k] = array[i] 55 | i++ 56 | k++ 57 | } 58 | 59 | while (j < right) { 60 | temporaryArray[k] = array[j] 61 | j++ 62 | k++ 63 | } 64 | 65 | i = left 66 | while (i < right) { 67 | array[i] = temporaryArray[i] 68 | i++ 69 | } 70 | 71 | left += windowSize * 2 72 | } 73 | 74 | windowSize *= 2 75 | } 76 | } 77 | 78 | /** 79 | * worst time: n * log(n) 80 | * best time: n * log(n) 81 | * average time: n * log(n) 82 | * 83 | * amount of memory: n * log(n) 84 | */ 85 | fun sortRecursive(array: Array) { 86 | val arraySize = array.size 87 | 88 | if (arraySize < 2) 89 | return 90 | 91 | val middle = arraySize / 2 92 | 93 | val left = Array(middle) { 0 } 94 | val right = Array(arraySize - middle) { 0 } 95 | 96 | var i = 0 97 | while (i < middle) { 98 | left[i] = array[i] 99 | i++ 100 | } 101 | 102 | while (i < arraySize) { 103 | right[i - middle] = array[i] 104 | i++ 105 | } 106 | 107 | sortRecursive(left) 108 | sortRecursive(right) 109 | 110 | val leftSize = left.size 111 | val rightSize = right.size 112 | 113 | i = 0 114 | var j = 0 115 | var k = 0 116 | while (i < leftSize && j < rightSize) { 117 | if (left[i] <= right[j]) { 118 | array[k] = left[i] 119 | i++ 120 | } else { 121 | array[k] = right[j] 122 | j++ 123 | } 124 | k++ 125 | } 126 | 127 | while (i < leftSize) { 128 | array[k] = left[i] 129 | i++ 130 | k++ 131 | } 132 | 133 | while (j < rightSize) { 134 | array[k] = right[j] 135 | j++ 136 | k++ 137 | } 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/GraphWithWeights.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | 4 | /** 5 | * 6 | * Graph is a non-linear data structure consisting of vertices and edges. 7 | * 8 | * The vertices are sometimes also referred to as nodes and the edges are lines or arcs that connect any two nodes in the graph. 9 | * 10 | * More formally a Graph is composed of a set of vertices V and a set of edges E. The graph is denoted by G(E, V). 11 | * 12 | * Directed graph with weights is a type of graph where the edges have specified direction and weights assigned to the them. 13 | * 14 | */ 15 | 16 | class GraphWithWeights { 17 | 18 | private val data = linkedMapOf, MutableList>>() 19 | 20 | // Complexity: O(1) 21 | fun addVertex(value: T) { 22 | data.putIfAbsent(Vertex(value), mutableListOf()) 23 | } 24 | 25 | // Complexity: O(n) 26 | fun removeVertex(value: T) { 27 | val removingVertex = Vertex(value) 28 | data.values.forEach { connections -> 29 | connections.removeIf { it.vertex == removingVertex } 30 | } 31 | data.remove(removingVertex) 32 | } 33 | 34 | // Complexity: O(1) 35 | fun addEdge(value1: T, value2: T, cost: Int) { 36 | val vertex1 = Vertex(value1) 37 | val vertex2 = Vertex(value2) 38 | data[vertex1]?.add(VertexConnection(vertex2, cost)) 39 | } 40 | 41 | // Complexity: O(n) 42 | fun removeEdge(value1: T, value2: T) { 43 | val vertex1 = Vertex(value1) 44 | val vertex2 = Vertex(value2) 45 | data[vertex1]?.removeIf { it.vertex == vertex2 } 46 | } 47 | 48 | // returns the associated vertices and their weights with the given vertex value 49 | fun connectedVertexesWithWeights(value: T) = data[Vertex(value)]?.map { it.toString() } ?: emptyList() 50 | 51 | /** 52 | * 53 | * Dijkstra's algorithm, 54 | * 55 | * returns pairs of a vertex and the minimum weight needed to get to that vertex, 56 | * 57 | * the starting vertex is the first added 58 | * 59 | */ 60 | fun dijkstraAlgorithm(): Map { 61 | if (data.isEmpty()) return emptyMap() 62 | 63 | val unvisitedVertexes = linkedMapOf, Int>() 64 | data.keys.forEach { vertex -> 65 | unvisitedVertexes[vertex] = Int.MAX_VALUE 66 | } 67 | 68 | val visitedVertexes = linkedMapOf() 69 | var minimumCost = 0 70 | 71 | var currentVertex = unvisitedVertexes.keys.first() 72 | while(unvisitedVertexes.isNotEmpty()) { 73 | val neighbourVertexConnections = data[currentVertex] ?: emptyList() 74 | for (neighbourVertexConnection in neighbourVertexConnections) { 75 | val neighbourVertex = neighbourVertexConnection.vertex 76 | if (!unvisitedVertexes.contains(neighbourVertex)) continue 77 | 78 | val newCost = minimumCost + neighbourVertexConnection.cost 79 | val neighbourVertexCost = unvisitedVertexes[neighbourVertex] ?: Int.MAX_VALUE 80 | if (neighbourVertexCost > newCost) { 81 | unvisitedVertexes[neighbourVertex] = newCost 82 | } 83 | } 84 | visitedVertexes[currentVertex.value] = minimumCost 85 | unvisitedVertexes.remove(currentVertex) 86 | val nextUnvisitedEntry = unvisitedVertexes.entries 87 | .filter { it.value != Int.MAX_VALUE } 88 | .minByOrNull { it.value } ?: return visitedVertexes 89 | currentVertex = nextUnvisitedEntry.key 90 | minimumCost = nextUnvisitedEntry.value 91 | } 92 | return visitedVertexes 93 | } 94 | 95 | private data class Vertex(val value: T) 96 | 97 | // helper class for defining graph weights 98 | private data class VertexConnection(val vertex: Vertex, val cost: Int) { 99 | override fun toString(): String = "vertex -> ${vertex.value}, cost -> $cost" 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /src/main/kotlin/sorting/TimSort.kt: -------------------------------------------------------------------------------- 1 | package sorting 2 | 3 | import kotlin.math.min 4 | 5 | /** 6 | * 7 | * Tim Sort is a hybrid sorting algorithm derived from merge sort and insertion sort 8 | * 9 | * used by Python’s sorted() and list.sort() functions, which was designed to perform well on 10 | * 11 | * many kinds of real-world data. 12 | * 13 | * worst time: n * log(n) 14 | * best time: n 15 | * average time: n * log(n) 16 | * 17 | * amount of memory: n 18 | * 19 | */ 20 | 21 | class TimSort { 22 | 23 | fun sort(array: Array) { 24 | val arraySize = array.size 25 | val minRunSize = minRunSize(arraySize) 26 | 27 | var i = 0 28 | while (i < arraySize) { 29 | insertionSort(array, i, min(i + minRunSize - 1, arraySize - 1)) 30 | i += minRunSize 31 | } 32 | 33 | var mergingSize = minRunSize 34 | while (mergingSize < arraySize) { 35 | var start = 0 36 | while (start < arraySize) { 37 | val middle = start + mergingSize - 1 38 | val end = min(start + 2 * mergingSize - 1, arraySize - 1) 39 | if (middle < end) { 40 | merge(array, start, middle, end) 41 | } 42 | start += mergingSize * 2 43 | } 44 | mergingSize *= 2 45 | } 46 | } 47 | 48 | /** 49 | * Minrun is chosen from the range 32 to 64 inclusive, such that the size of the data, divided by minrun, is equal to, 50 | * or slightly less than, a power of two. The final algorithm takes the six most significant bits of the size of the array, 51 | * adds one if any of the remaining bits are set, and uses that result as the minrun. 52 | * This algorithm works for all arrays, including those smaller than 64; for arrays of size 63 or less, 53 | * this sets minrun equal to the array size and Timsort reduces to an insertion sort 54 | */ 55 | private fun minRunSize(arraySize: Int) : Int { 56 | var result = 0 57 | var size = arraySize 58 | while (size >= 64) { 59 | result = result or (size and 1) 60 | // shift one bit to the right until 6 significant bits remain 61 | size = size shr 1 62 | } 63 | 64 | return size + result 65 | } 66 | 67 | private fun insertionSort(array: Array, start: Int, end: Int) { 68 | var i = start + 1 69 | while (i <= end) { 70 | val current = array[i] 71 | var j = i - 1 72 | while (j >= start && array[j] > current) { 73 | array[j + 1] = array[j] 74 | j-- 75 | } 76 | array[j + 1] = current 77 | i++ 78 | } 79 | } 80 | 81 | private fun merge(array: Array, start: Int, middle: Int, end: Int) { 82 | val leftSize = middle - start + 1 83 | val rightSize = end - middle 84 | 85 | var i = 0 86 | val leftArray = Array(leftSize) { 0 } 87 | while (i < leftSize) { 88 | leftArray[i] = array[start + i] 89 | i++ 90 | } 91 | 92 | i = 0 93 | val rightArray = Array(rightSize) { 0 } 94 | while (i < rightSize) { 95 | rightArray[i] = array[middle + i + 1] 96 | i++ 97 | } 98 | 99 | i = 0 100 | var j = 0 101 | var k = start 102 | while (i < leftSize && j < rightSize) { 103 | if (leftArray[i] <= rightArray[j]) { 104 | array[k] = leftArray[i] 105 | i++ 106 | } else { 107 | array[k] = rightArray[j] 108 | j++ 109 | } 110 | k++ 111 | } 112 | 113 | while (i < leftSize) { 114 | array[k] = leftArray[i] 115 | i++ 116 | k++ 117 | } 118 | 119 | while (j < rightSize) { 120 | array[k] = rightArray[j] 121 | j++ 122 | k++ 123 | } 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Command.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Command is a behavioral pattern that encapsulates a request to perform an action as a separate object 6 | * 7 | */ 8 | 9 | // StereoSystem is the receiver that performs the action 10 | class StereoSystem { 11 | 12 | private var hasTurnedOn = false 13 | private var volume = 50 14 | 15 | val currentState: String 16 | get() = """ 17 | running status: $hasTurnedOn 18 | volume value: $volume 19 | """.trimIndent() 20 | 21 | fun turnOn() { 22 | hasTurnedOn = true 23 | } 24 | 25 | fun turnOff() { 26 | hasTurnedOn = false 27 | } 28 | 29 | fun increaseVolume() { 30 | if (!hasTurnedOn) return 31 | if (volume < 100) { 32 | volume += 10 33 | } 34 | } 35 | 36 | fun decreaseVolume() { 37 | if (!hasTurnedOn) return 38 | if (volume > 0) { 39 | volume -= 10 40 | } 41 | } 42 | 43 | } 44 | 45 | interface StereoSystemCommand { 46 | fun execute() 47 | fun undo() : Boolean 48 | } 49 | 50 | // this command encapsulates a request to increase the volume in StereoSystem 51 | class IncreaseVolumeCommand(private val stereoSystem: StereoSystem) : StereoSystemCommand { 52 | 53 | override fun execute() { 54 | stereoSystem.increaseVolume() 55 | } 56 | 57 | override fun undo(): Boolean { 58 | stereoSystem.decreaseVolume() 59 | return true 60 | } 61 | 62 | } 63 | 64 | // this command encapsulates a request to decrease the volume in StereoSystem 65 | class DecreaseVolumeCommand(private val stereoSystem: StereoSystem) : StereoSystemCommand { 66 | 67 | override fun execute() { 68 | stereoSystem.decreaseVolume() 69 | } 70 | 71 | override fun undo(): Boolean { 72 | stereoSystem.increaseVolume() 73 | return true 74 | } 75 | 76 | } 77 | 78 | // this command encapsulates a request to turn on StereoSystem 79 | class TurnOnCommand(private val stereoSystem: StereoSystem) : StereoSystemCommand { 80 | 81 | override fun execute() { 82 | stereoSystem.turnOn() 83 | } 84 | 85 | override fun undo(): Boolean { 86 | stereoSystem.turnOff() 87 | return true 88 | } 89 | 90 | } 91 | 92 | // this command encapsulates a request to turn off StereoSystem 93 | class TurnOffCommand(private val stereoSystem: StereoSystem) : StereoSystemCommand { 94 | 95 | override fun execute() { 96 | stereoSystem.turnOff() 97 | } 98 | 99 | override fun undo() = false 100 | 101 | } 102 | 103 | // we can also create the command that combines others 104 | class MacroCommand(private vararg val commands: StereoSystemCommand) : StereoSystemCommand { 105 | 106 | override fun execute() { 107 | for (command in commands) { 108 | command.execute() 109 | } 110 | } 111 | 112 | override fun undo(): Boolean { 113 | for (command in commands.reversed()) { 114 | val isNotCancelableCommand = !command.undo() 115 | if (isNotCancelableCommand) return false 116 | } 117 | return true 118 | } 119 | 120 | } 121 | 122 | // StereoSystemRemoteControl executes commands without breaking their encapsulation 123 | class StereoSystemRemoteControl(private val buttons: Map = mapOf()) { 124 | 125 | // we only know about StereoSystemCommand interface 126 | private val commandHistory = mutableListOf() 127 | 128 | fun pressButton(name: String) { 129 | val command = buttons[name] 130 | if (command != null) { 131 | command.execute() 132 | commandHistory.add(command) 133 | } 134 | } 135 | 136 | fun pressUndoButton() { 137 | val lastCommand = commandHistory.removeLastOrNull() 138 | if (lastCommand != null) { 139 | val isNotCancelableCommand = !lastCommand.undo() 140 | if (isNotCancelableCommand) commandHistory.clear() 141 | } 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /src/main/kotlin/design_patterns/Builder.kt: -------------------------------------------------------------------------------- 1 | package design_patterns 2 | 3 | /** 4 | * 5 | * Builder is is a generative design pattern that is used to create complex objects, 6 | * 7 | * separates the construction of an object from its representation 8 | * 9 | */ 10 | 11 | /** 12 | * The first variant 13 | */ 14 | class HttpConnectionClient1 private constructor( 15 | private val dnsServerAddress: String, 16 | private val callTimeout: Int, 17 | private val connectTimeout: Int, 18 | private val readTimeout: Int, 19 | private val writeTimeout: Int, 20 | // class can have many more fields 21 | ) { 22 | 23 | override fun toString() = """ 24 | dns -> $dnsServerAddress 25 | call timeout -> $callTimeout 26 | connect timeout -> $connectTimeout 27 | read timeout -> $readTimeout 28 | write timeout -> $writeTimeout 29 | """.trimIndent() 30 | 31 | class Builder { 32 | private var dnsServerAddress: String = "8.8.8.8" 33 | private var callTimeout: Int = 0 34 | private var connectTimeout: Int = 10_000 35 | private var readTimeout: Int = 10_000 36 | private var writeTimeout: Int = 0 37 | 38 | fun dnsServerAddress(address: String) = apply { 39 | dnsServerAddress = address 40 | } 41 | 42 | fun callTimeout(timeout: Int) = apply { 43 | // we can add some checks such as: 44 | // if (timeout < 0) throw IllegalArgumentException("Uncorrected timeout: $timeout") 45 | callTimeout = timeout 46 | } 47 | 48 | fun connectTimeout(timeout: Int) = apply { 49 | connectTimeout = timeout 50 | } 51 | 52 | fun readTimeout(timeout: Int) = apply { 53 | readTimeout = timeout 54 | } 55 | 56 | fun writeTimeout(timeout: Int) = apply { 57 | writeTimeout = timeout 58 | } 59 | 60 | fun build() = HttpConnectionClient1(dnsServerAddress, callTimeout, connectTimeout, readTimeout, writeTimeout) 61 | 62 | } 63 | 64 | } 65 | 66 | /** 67 | * The second variant 68 | */ 69 | class HttpConnectionClient2 private constructor() { 70 | private var dnsServerAddress: String = "8.8.8.8" 71 | private var callTimeout: Int = 0 72 | private var connectTimeout: Int = 10_000 73 | private var readTimeout: Int = 10_000 74 | private var writeTimeout: Int = 0 75 | 76 | override fun toString() = """ 77 | dns -> $dnsServerAddress 78 | call timeout -> $callTimeout 79 | connect timeout -> $connectTimeout 80 | read timeout -> $readTimeout 81 | write timeout -> $writeTimeout 82 | """.trimIndent() 83 | 84 | companion object { 85 | fun newBuilder() = HttpConnectionClient2().Builder() 86 | } 87 | 88 | inner class Builder { 89 | 90 | fun dnsServerAddress(address: String) = apply { 91 | dnsServerAddress = address 92 | } 93 | 94 | fun callTimeout(timeout: Int) = apply { 95 | // we can add some checks such as: 96 | // if (timeout < 0) throw IllegalArgumentException("Uncorrected timeout: $timeout") 97 | callTimeout = timeout 98 | } 99 | 100 | fun connectTimeout(timeout: Int) = apply { 101 | connectTimeout = timeout 102 | } 103 | 104 | fun readTimeout(timeout: Int) = apply { 105 | readTimeout = timeout 106 | } 107 | 108 | fun writeTimeout(timeout: Int) = apply { 109 | writeTimeout = timeout 110 | } 111 | 112 | fun build() = this@HttpConnectionClient2 113 | } 114 | 115 | } 116 | 117 | /** 118 | * Kotlin variant with default arguments 119 | */ 120 | class HttpConnectionClient3( 121 | private val dnsServerAddress: String = "8.8.8.8", 122 | private val callTimeout: Int = 0, 123 | private val connectTimeout: Int = 10_000, 124 | private val readTimeout: Int = 10_000, 125 | private val writeTimeout: Int = 0 126 | ) { 127 | override fun toString() = """ 128 | dns -> $dnsServerAddress 129 | call timeout -> $callTimeout 130 | connect timeout -> $connectTimeout 131 | read timeout -> $readTimeout 132 | write timeout -> $writeTimeout 133 | """.trimIndent() 134 | } -------------------------------------------------------------------------------- /src/main/kotlin/structures/Matrix.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import java.lang.IllegalArgumentException 4 | 5 | fun matrix(apply: Matrix.() -> Unit): Matrix { 6 | val matrix = Matrix() 7 | matrix.apply() 8 | return matrix 9 | } 10 | 11 | /** 12 | * Matrix is a rectangular table filled with numbers. 13 | * 14 | * For example: 15 | * 1, 2, 3 16 | * 4, 5, 6 17 | * 7, 8, 9 18 | */ 19 | class Matrix() { 20 | 21 | private val elements = mutableListOf() 22 | private var columnCount = 0 23 | private var rowCount = 0 24 | 25 | private constructor(elements: List, columnCount: Int, rowCount: Int) : this() { 26 | this.elements.addAll(elements) 27 | this.columnCount = columnCount 28 | this.rowCount = rowCount 29 | } 30 | 31 | fun row(vararg rowElements: Int) { 32 | if (rowElements.isEmpty()) { 33 | return 34 | } 35 | 36 | if (rowCount == 0) { 37 | columnCount = rowElements.size 38 | } 39 | 40 | if (columnCount != rowElements.size) { 41 | throw IllegalArgumentException("The number of elements in each row must be the same") 42 | } 43 | 44 | elements.addAll(rowElements.toTypedArray()) 45 | 46 | rowCount++ 47 | } 48 | 49 | operator fun plus(value: Int): Matrix { 50 | return Matrix(elements.map { it + value }, columnCount, rowCount) 51 | } 52 | 53 | operator fun plus(value: Matrix): Matrix { 54 | if (rowCount != value.rowCount) throw IllegalArgumentException("The number of rows doesn't match") 55 | if (columnCount != value.columnCount) throw IllegalArgumentException("The number of columns doesn't match") 56 | 57 | return Matrix(elements.zip(value.elements) { element1, element2 -> element1 + element2 }, columnCount, rowCount) 58 | } 59 | 60 | operator fun minus(value: Int): Matrix { 61 | return Matrix(elements.map { it - value }, columnCount, rowCount) 62 | } 63 | 64 | operator fun minus(value: Matrix): Matrix { 65 | if (rowCount != value.rowCount) throw IllegalArgumentException("The number of rows doesn't match") 66 | if (columnCount != value.columnCount) throw IllegalArgumentException("The number of columns doesn't match") 67 | 68 | return Matrix(elements.zip(value.elements) { element1, element2 -> element1 - element2 }, columnCount, rowCount) 69 | } 70 | 71 | operator fun times(value: Int): Matrix { 72 | return Matrix(elements.map { it * value }, columnCount, rowCount) 73 | } 74 | 75 | operator fun times(matrix: Matrix): Matrix { 76 | val columnCountMatrix1 = columnCount 77 | val rowCountMatrix1 = rowCount 78 | val columnCountMatrix2 = matrix.columnCount 79 | val rowCountMatrix2 = matrix.rowCount 80 | 81 | if (columnCountMatrix1 != rowCountMatrix2) throw IllegalArgumentException("The number of columns of the first matrix doesn't match the number of rows of the second matrix") 82 | 83 | val newElements = mutableListOf() 84 | for (rowIndexMatrix1 in 0 until rowCountMatrix1) { 85 | for (columnIndexMatrix2 in 0 until columnCountMatrix2) { 86 | var sum = 0 87 | for (sameSizeIndex in 0 until columnCountMatrix1) { 88 | sum += elements[rowIndexMatrix1 * columnCountMatrix1 + sameSizeIndex] * matrix.elements[sameSizeIndex * columnCountMatrix2 + columnIndexMatrix2] 89 | } 90 | newElements.add(sum) 91 | } 92 | } 93 | 94 | return Matrix(newElements, columnCountMatrix2, rowCountMatrix1) 95 | } 96 | 97 | operator fun div(value: Int): Matrix { 98 | return Matrix(elements.map { it / value }, columnCount, rowCount) 99 | } 100 | 101 | override fun equals(other: Any?): Boolean { 102 | if (other == null) return false 103 | if (other !is Matrix) return false 104 | if (this === other) return true 105 | 106 | return columnCount == other.columnCount && rowCount == other.rowCount && elements == other.elements 107 | } 108 | 109 | override fun hashCode(): Int { 110 | var result = elements.hashCode() 111 | result = 31 * result + columnCount 112 | result = 31 * result + rowCount 113 | return result 114 | } 115 | 116 | override fun toString(): String { 117 | val stringBuilder = StringBuilder("\n") 118 | 119 | elements.forEachIndexed { index, value -> 120 | val floating = if ((index + 1) % columnCount == 0) "" else ", " 121 | stringBuilder.append("$value$floating") 122 | 123 | if ((index + 1) % columnCount == 0) { 124 | stringBuilder.append("\n") 125 | } 126 | } 127 | 128 | return stringBuilder.toString() 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /src/test/kotlin/structures/SingleLinkedListTest.kt: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class SingleLinkedListTest { 7 | 8 | @Test 9 | fun `test is empty`() { 10 | val list = SingleLinkedList() 11 | 12 | assertEquals(true, list.isEmpty) 13 | 14 | list.add(1) 15 | list.add(2) 16 | list.add(3) 17 | 18 | assertEquals(false, list.isEmpty) 19 | 20 | list.remove(1) 21 | list.remove(2) 22 | list.remove(3) 23 | 24 | assertEquals(true, list.isEmpty) 25 | } 26 | 27 | @Test 28 | fun `test add by index`() { 29 | val list = SingleLinkedList() 30 | 31 | list.add(1) 32 | list.add(3) 33 | list.add(5) 34 | 35 | assertEquals(""" 36 | size: 3 37 | elements: 1 - 3 - 5 38 | """.trimIndent(), list.toString()) 39 | 40 | list.add(1, 2) 41 | list.add(3, 4) 42 | 43 | assertEquals(""" 44 | size: 5 45 | elements: 1 - 2 - 3 - 4 - 5 46 | """.trimIndent(), list.toString()) 47 | } 48 | 49 | @Test 50 | fun `test addFirst`() { 51 | val list = SingleLinkedList() 52 | 53 | list.addFirst(100) 54 | list.addFirst(80) 55 | list.addFirst(60) 56 | list.addFirst(40) 57 | list.addFirst(20) 58 | 59 | assertEquals(""" 60 | size: 5 61 | elements: 20 - 40 - 60 - 80 - 100 62 | """.trimIndent(), list.toString()) 63 | 64 | list.remove(80) 65 | list.remove(40) 66 | 67 | assertEquals(""" 68 | size: 3 69 | elements: 20 - 60 - 100 70 | """.trimIndent(), list.toString()) 71 | 72 | list.addFirst(0) 73 | 74 | assertEquals(""" 75 | size: 4 76 | elements: 0 - 20 - 60 - 100 77 | """.trimIndent(), list.toString()) 78 | } 79 | 80 | @Test 81 | fun `test addLast`() { 82 | val list = SingleLinkedList() 83 | 84 | list.addLast(100) 85 | list.addLast(200) 86 | list.addLast(300) 87 | list.addLast(400) 88 | list.addLast(500) 89 | list.addLast(600) 90 | list.addLast(700) 91 | 92 | assertEquals(""" 93 | size: 7 94 | elements: 100 - 200 - 300 - 400 - 500 - 600 - 700 95 | """.trimIndent(), list.toString()) 96 | 97 | list.remove(200) 98 | list.remove(400) 99 | list.remove(600) 100 | 101 | assertEquals(""" 102 | size: 4 103 | elements: 100 - 300 - 500 - 700 104 | """.trimIndent(), list.toString()) 105 | 106 | list.addLast(900) 107 | 108 | assertEquals(""" 109 | size: 5 110 | elements: 100 - 300 - 500 - 700 - 900 111 | """.trimIndent(), list.toString()) 112 | } 113 | 114 | @Test 115 | fun `test contains`() { 116 | val list = SingleLinkedList() 117 | 118 | list.add(1) 119 | list.add(3) 120 | list.add(5) 121 | list.add(7) 122 | list.add(9) 123 | list.add(11) 124 | 125 | assertEquals(true, list.contains(1)) 126 | assertEquals(true, list.contains(5)) 127 | assertEquals(true, list.contains(9)) 128 | assertEquals(true, list.contains(11)) 129 | 130 | assertEquals(false, list.contains(2)) 131 | assertEquals(false, list.contains(4)) 132 | assertEquals(false, list.contains(8)) 133 | assertEquals(false, list.contains(10)) 134 | 135 | list.clear() 136 | 137 | assertEquals(false, list.contains(1)) 138 | assertEquals(false, list.contains(5)) 139 | assertEquals(false, list.contains(9)) 140 | assertEquals(false, list.contains(11)) 141 | } 142 | 143 | @Test 144 | fun `test remove and clear`() { 145 | val list = SingleLinkedList() 146 | 147 | list.add(1) 148 | list.add(2) 149 | list.add(3) 150 | list.add(4) 151 | list.add(5) 152 | 153 | assertEquals(""" 154 | size: 5 155 | elements: 1 - 2 - 3 - 4 - 5 156 | """.trimIndent(), list.toString()) 157 | 158 | assertEquals(true, list.remove(1)) 159 | assertEquals(true, list.remove(3)) 160 | assertEquals(true, list.remove(5)) 161 | 162 | assertEquals(""" 163 | size: 2 164 | elements: 2 - 4 165 | """.trimIndent(), list.toString()) 166 | 167 | assertEquals(true, list.remove(2)) 168 | assertEquals(true, list.remove(4)) 169 | 170 | assertEquals(true, list.isEmpty) 171 | 172 | list.add(6) 173 | list.add(7) 174 | list.add(11) 175 | list.add(13) 176 | 177 | assertEquals(""" 178 | size: 4 179 | elements: 6 - 7 - 11 - 13 180 | """.trimIndent(), list.toString()) 181 | 182 | list.clear() 183 | 184 | assertEquals(true, list.isEmpty) 185 | } 186 | 187 | } -------------------------------------------------------------------------------- /src/test/kotlin/other/FactorialAdvancedTest.kt: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | import org.junit.Test 4 | import org.junit.Assert.assertEquals 5 | 6 | internal class FactorialAdvancedTest { 7 | 8 | private val factorial = FactorialAdvanced() 9 | 10 | @Test 11 | fun test_factorial_30() { 12 | val actual = factorial.compute(30) 13 | assertEquals("265252859812191058636308480000000", actual.toString()) 14 | } 15 | 16 | @Test 17 | fun test_factorial_60() { 18 | val actual = factorial.compute(60) 19 | assertEquals("8320987112741390144276341183223364380754172606361245952449277696409600000000000000", actual.toString()) 20 | } 21 | 22 | @Test 23 | fun test_factorial_90() { 24 | val expected = "93326215443944152681699238856266700490715968264381621468592963" + 25 | "895217599993229915608941463976156518286253697920827223758251185210916" + 26 | "864000000000000000000000000" 27 | 28 | val actual = factorial.compute(100) 29 | assertEquals(expected, actual.toString()) 30 | } 31 | 32 | @Test 33 | fun test_factorial_200() { 34 | val expected = "788657867364790503552363213932185062295135977687173263294742533244359449963403342920304" + 35 | "2840119846239041772121389196388302576427902426371050619266249528299311134628572707633172373969" + 36 | "8894392244562145166424025403329186413122742829485327752424240757390324032125740557956866022603" + 37 | "1904170324062351700858796178922222789623703897374720000000000000000000000000000000000000000000000000" 38 | 39 | val actual = factorial.compute(200) 40 | assertEquals(expected, actual.toString()) 41 | } 42 | 43 | @Test 44 | fun test_factorial_1000() { 45 | val expected = "40238726007709377354370243392300398571937486421071463254379991042993851" + 46 | "2398629020592044208486969404800479988610197196058631666872994808558901323829669" + 47 | "9445909974245040870737599188236277271887325197795059509952761208749754624970436" + 48 | "0141827809464649629105639388743788648733711918104582578364784997701247663288983" + 49 | "5955735432513185323958463075557409114262417474349347553428646576611667797396668" + 50 | "8202912073791438537195882498081268678383745597317461360853795345242215865932019" + 51 | "2809087829730843139284440328123155861103697680135730421616874760967587134831202" + 52 | "5478589320767169132448426236131412508780208000261683151027341827977704784635868" + 53 | "1701643650241536913982812648102130927612448963599287051149649754199093422215668" + 54 | "3257208082133318611681155361583654698404670897560290095053761647584772842188967" + 55 | "9646244945160765353408198901385442487984959953319101723355556602139450399736280" + 56 | "7501378376153071277619268490343526252000158885351473316117021039681759215109077" + 57 | "8801939317811419454525722386554146106289218796022383897147608850627686296714667" + 58 | "4697562911234082439208160153780889893964518263243671616762179168909779911903754" + 59 | "0312746222899880051954444142820121873617459926429565817466283029555702990243241" + 60 | "5318161721046583203678690611726015878352075151628422554026517048330422614397428" + 61 | "6933061690897968482590125458327168226458066526769958652682272807075781391858178" + 62 | "8896522081643483448259932660433676601769996128318607883861502794659551311565520" + 63 | "3609398818061213855860030143569452722420634463179746059468257310379008402443243" + 64 | "8465657245014402821885252470935190620929023136493273497565513958720559654228749" + 65 | "7740114133469627154228458623773875382304838656889764619273838149001407673104466" + 66 | "4025989949022222176590433990188601856652648506179970235619389701786004081188972" + 67 | "9918311021171229845901641921068884387121855646124960798722908519296819372388642" + 68 | "6148396573822911231250241866493531439701374285319266498753372189406942814341185" + 69 | "2015801412334482801505139969429015348307764456909907315243327828826986460278986" + 70 | "4321139083506217095002597389863554277196742822248757586765752344220207573630569" + 71 | "4988250879689281627538488633969099598262809561214509948717012445164612603790293" + 72 | "0912088908694202851064018215439945715680594187274899809425474217358240106367740" + 73 | "4595741785160829230135358081840096996372524230560855903700624271243416909004153" + 74 | "6901059339838357779394109700277534720000000000000000000000000000000000000000000" + 75 | "0000000000000000000000000000000000000000000000000000000000000000000000000000000" + 76 | "0000000000000000000000000000000000000000000000000000000000000000000000000000000" + 77 | "000000000000000000000000000000000000000000000000" 78 | 79 | val actual = factorial.compute(1000) 80 | assertEquals(expected, actual.toString()) 81 | } 82 | 83 | } --------------------------------------------------------------------------------