From 46daa16e3c8b8b31ef28186079444f1f8832aea4 Mon Sep 17 00:00:00 2001 From: norangebit Date: Wed, 27 Mar 2019 17:56:58 +0100 Subject: [PATCH] add mergesort - add point sorter - fix typo in BranchAndMerge --- .../algorithms/exercises/BranchAndMerge.kt | 4 +- .../algorithms/exercises/PointSorter.kt | 84 +++++++++++++++++ .../norangeb/algorithms/sorting/Mergesort.kt | 88 ++++++++++++++++++ .../it/norangeb/algorithms/sorting/Sorter.kt | 32 +++++++ .../exercises/BranchAndMergeTest.kt | 8 +- .../algorithms/sorting/MergesortTest.kt | 90 +++++++++++++++++++ 6 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/it/norangeb/algorithms/exercises/PointSorter.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/sorting/Mergesort.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/sorting/Sorter.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/sorting/MergesortTest.kt diff --git a/src/main/kotlin/it/norangeb/algorithms/exercises/BranchAndMerge.kt b/src/main/kotlin/it/norangeb/algorithms/exercises/BranchAndMerge.kt index c1cd5ff..2afb4c2 100644 --- a/src/main/kotlin/it/norangeb/algorithms/exercises/BranchAndMerge.kt +++ b/src/main/kotlin/it/norangeb/algorithms/exercises/BranchAndMerge.kt @@ -37,10 +37,10 @@ class BranchAndMerge( companion object { private const val TMP_FILE_PATH_1 = "/tmp/file1" private const val TMP_FILE_PATH_2 = "/tmp/file2" + private val GSON = Gson() } - private val GSON = Gson() - fun run(source: File) { + fun sort(source: File) { tryBranch(source) while (!isSorted()) { diff --git a/src/main/kotlin/it/norangeb/algorithms/exercises/PointSorter.kt b/src/main/kotlin/it/norangeb/algorithms/exercises/PointSorter.kt new file mode 100644 index 0000000..befcf3f --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/exercises/PointSorter.kt @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.exercises + +import it.norangeb.algorithms.sorting.Mergesort + +data class Point(val x: Double, val y: Double) { + + constructor(x: Int, y: Int) : this(x.toDouble(), y.toDouble()) + + operator fun minus(other: Point): Point { + val x = this.x - other.x + val y = this.y - other.y + + return Point(x, y) + } + + fun theta(): Double = Math.atan2(y, x) + + companion object { + fun thetaComparator( + p1: Point, + p2: Point, + origin: Point = Point(0, 0) + ): Int { + var theta1 = (p1 - origin).theta() + var theta2 = (p2 - origin).theta() + + if (theta1 < 0) theta1 += 2 * Math.PI + if (theta2 < 0) theta2 += 2 * Math.PI + + return theta1.compareTo(theta2) + } + } +} + +typealias P = Point + +fun main(args: Array) { + if (args.size < 4 || args.size % 2 != 0) return + + val origin = P(args[0].toDouble(), args[1].toDouble()) + + val list = ArrayList() + + for (i in 2 until args.size step 2) + list.add(P(args[i].toDouble(), args[i + 1].toDouble())) + + val points = list.toTypedArray() + + val compare = { p1: Point, p2: Point -> + Point.thetaComparator(p1, p2, origin) + } + + Mergesort.sortWith(points, compare) + + println("origin in $origin") + points.forEach { + println(it) + } +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/sorting/Mergesort.kt b/src/main/kotlin/it/norangeb/algorithms/sorting/Mergesort.kt new file mode 100644 index 0000000..d0cfcdd --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/sorting/Mergesort.kt @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.sorting + +object Mergesort : Sorter { + override fun > sort(array: Array) { + sort(array) { t1, t2 -> t1 < t2 } + } + + override fun > sortBy(array: Array, compareBy: (T) -> C) { + sort(array) { t1, t2 -> compareBy(t1) < compareBy(t2) } + } + + override fun sortWith(array: Array, compare: (T, T) -> Int) { + sort(array) { t1, t2 -> compare(t1, t2) < 0 } + } + + private fun sort(array: Array, isLess: (T, T) -> Boolean) { + val auxiliary = arrayOfNulls(array.size) as Array + sort(array, auxiliary, 0, array.size - 1, isLess) + } + + private fun sort( + array: Array, + auxiliary: Array, + low: Int, + high: Int, + isLess: (T, T) -> Boolean + ) { + if (high <= low) return + + val mid = low + (high - low) / 2 + + sort(array, auxiliary, low, mid, isLess) + sort(array, auxiliary, mid + 1, high, isLess) + + if (!isLess(array[mid+1], array[mid])) + return + + merge(array, auxiliary, low, mid, high, isLess) + } + + private inline fun merge( + array: Array, + auxiliary: Array, + low: Int, + mid: Int, + high: Int, + isLess: (T, T) -> Boolean + ) { + for (i in low..high) + auxiliary[i] = array[i] + + var i = low + var j = mid + 1 + + for (k in low..high) + when { + i > mid -> array[k] = auxiliary[j++] + j > high -> array[k] = auxiliary[i++] + isLess(auxiliary[j], auxiliary[i]) -> array[k] = auxiliary[j++] + else -> array[k] = auxiliary[i++] + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/sorting/Sorter.kt b/src/main/kotlin/it/norangeb/algorithms/sorting/Sorter.kt new file mode 100644 index 0000000..19d9dee --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/sorting/Sorter.kt @@ -0,0 +1,32 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.sorting + +interface Sorter { + fun > sort(array: Array) + fun > sortBy(array: Array, compareBy: (T) -> C) + fun sortWith(array: Array, compare: (T, T) -> Int) +} \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/exercises/BranchAndMergeTest.kt b/src/test/kotlin/it/norangeb/algorithms/exercises/BranchAndMergeTest.kt index 3f048ad..34097a9 100644 --- a/src/test/kotlin/it/norangeb/algorithms/exercises/BranchAndMergeTest.kt +++ b/src/test/kotlin/it/norangeb/algorithms/exercises/BranchAndMergeTest.kt @@ -281,7 +281,7 @@ class BranchAndMergeTest { ps.close() BranchAndMerge(Int::class.java, this::defaultComparator) - .run(File(filePath)) + .sort(File(filePath)) File(filePath).readLines() .map { it.toInt() } `should equal` testSet @@ -297,7 +297,7 @@ class BranchAndMergeTest { ps.close() BranchAndMerge(Int::class.java, this::defaultComparator) - .run(File(filePath)) + .sort(File(filePath)) File(filePath).readLines() .map { it.toInt() } `should equal` testSet.sorted() @@ -313,7 +313,7 @@ class BranchAndMergeTest { ps.close() BranchAndMerge(Int::class.java) { it1, it2 -> it2.compareTo(it1) } - .run(File(filePath)) + .sort(File(filePath)) File(filePath).readLines() .map { it.toInt() } `should equal` testSet.sorted().reversed() @@ -343,7 +343,7 @@ class BranchAndMergeTest { } BranchAndMerge(Person::class.java, sortByAge) - .run(File(filePath)) + .sort(File(filePath)) File(filePath).readLines() .map { gson.fromJson(it, Person::class.java) } `should equal` diff --git a/src/test/kotlin/it/norangeb/algorithms/sorting/MergesortTest.kt b/src/test/kotlin/it/norangeb/algorithms/sorting/MergesortTest.kt new file mode 100644 index 0000000..4cbd68f --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/sorting/MergesortTest.kt @@ -0,0 +1,90 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.sorting + +import org.amshove.kluent.`should equal` +import org.junit.jupiter.api.Test +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.jvm.isAccessible + +class MergesortTest { + + @Test + fun testMerge() { + val merge = Mergesort::class.memberFunctions + .find { it.name == "merge" } + .also { it?.isAccessible = true } + + val array = arrayOf(3, 7, 9, 12, 1, 4, 6, 10) + val low = 0 + val mid = 3 + val high = array.size - 1 + val auxiliary = arrayOfNulls(array.size) + + merge?.call( + Mergesort, + array, + auxiliary, + low, + mid, + high, + { int1: Int, int2: Int -> + int1 < int2 + } + ) + + array `should equal` array.sortedArray() + } + + @Test + fun testSort() { + val array = arrayOf(5, 2, 3, 1, 4) + + Mergesort.sort(array) + + array `should equal` array.sortedArray() + } + + @Test + fun testSortBy() { + val array = arrayOf(5, 2, 1, 4, 3) + + Mergesort.sortBy(array) { it } + + array `should equal` array.sortedArray() + } + + @Test + fun testSortWith() { + val array = arrayOf(4, 5, 3, 2, 1) + + Mergesort.sortWith(array) { t1, t2 -> + t1.compareTo(t2) + } + + array `should equal` array.sortedArray() + } +} \ No newline at end of file