From 7233593860ddee0745778a010f7ef0b2f3660933 Mon Sep 17 00:00:00 2001 From: Martin Berg Alstad Date: Fri, 1 Aug 2025 09:24:57 +0200 Subject: [PATCH] Refactor assignment 3 and implement alternative Pareto Principle --- src/assignment/week3/nicestring/NiceString.kt | 27 ++++---- src/assignment/week3/taxipark/TaxiPark.kt | 25 +++---- .../week3/taxipark/TaxiParkParetoPrinciple.kt | 67 +++++++++++++++++++ src/assignment/week3/taxipark/TaxiParkTask.kt | 10 +-- 4 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 src/assignment/week3/taxipark/TaxiParkParetoPrinciple.kt diff --git a/src/assignment/week3/nicestring/NiceString.kt b/src/assignment/week3/nicestring/NiceString.kt index 8e5d0d2..e745e96 100644 --- a/src/assignment/week3/nicestring/NiceString.kt +++ b/src/assignment/week3/nicestring/NiceString.kt @@ -1,25 +1,22 @@ -package nicestring +package assignment.week3.nicestring -fun String.isNice(): Boolean { - return listOf( - ::containsNotNiceSubstring, - ::containsAtLeastThreeVowels, - ::containsDoubleLetter - ) - .count { it() } >= 2 -} +const val VOWELS = "aeiou" -fun String.containsNotNiceSubstring(): Boolean { +fun String.isNice(): Boolean = listOf( + ::containsNotNiceSubstring, + ::containsAtLeastThreeVowels, + ::containsDoubleLetter +).count { it() } >= 2 + +private fun String.containsNotNiceSubstring(): Boolean { val illegalSubStrings = listOf("bu", "ba", "be") return this.zipWithNext() .map { (first, second) -> "$first$second" } .none { it in illegalSubStrings } } -fun String.containsAtLeastThreeVowels(): Boolean { - val vovels = "aeiou".toCharArray() - return this.count { char -> char in vovels } >= 3 -} +private fun String.containsAtLeastThreeVowels(): Boolean = + this.count { char -> char in VOWELS.toCharArray() } >= 3 -fun String.containsDoubleLetter(): Boolean = this.zipWithNext() +private fun String.containsDoubleLetter(): Boolean = this.zipWithNext() .any { (first, second) -> first == second } diff --git a/src/assignment/week3/taxipark/TaxiPark.kt b/src/assignment/week3/taxipark/TaxiPark.kt index 81dbde0..06232f0 100644 --- a/src/assignment/week3/taxipark/TaxiPark.kt +++ b/src/assignment/week3/taxipark/TaxiPark.kt @@ -1,22 +1,23 @@ -package taxipark +package assignment.week3.taxipark data class TaxiPark( - val allDrivers: Set, - val allPassengers: Set, - val trips: List) + val allDrivers: Set, + val allPassengers: Set, + val trips: List +) data class Driver(val name: String) data class Passenger(val name: String) data class Trip( - val driver: Driver, - val passengers: Set, - // the trip duration in minutes - val duration: Int, - // the trip distance in km - val distance: Double, - // the percentage of discount (in 0.0..1.0 if not null) - val discount: Double? = null + val driver: Driver, + val passengers: Set, + // the trip duration in minutes + val duration: Int, + // the trip distance in km + val distance: Double, + // the percentage of discount (in 0.0..1.0 if not null) + val discount: Double? = null ) { // the total cost of the trip val cost: Double diff --git a/src/assignment/week3/taxipark/TaxiParkParetoPrinciple.kt b/src/assignment/week3/taxipark/TaxiParkParetoPrinciple.kt new file mode 100644 index 0000000..d500b9f --- /dev/null +++ b/src/assignment/week3/taxipark/TaxiParkParetoPrinciple.kt @@ -0,0 +1,67 @@ +package assignment.week3.taxipark + +fun TaxiPark.checkParetoPrinciple2(): Boolean { + if (trips.isEmpty()) return false + + val totalIncome = trips.sumOf { trip -> trip.cost } + val sortedDriversIncome: List = trips + .groupBy { it.driver } + .map { (_, tripsByDriver) -> tripsByDriver.sumOf { trip -> trip.cost } } + .sortedDescending() + + val numberOfTopDrivers = (0.2 * allDrivers.size).toInt() + val incomeByTopDrivers = sortedDriversIncome + .take(numberOfTopDrivers) + .sum() + + return incomeByTopDrivers >= 0.8 * totalIncome +} + +fun main() { + taxiPark( + 1..5, 1..4, + trip(1, 1, 20, 20.0), + trip(1, 2, 20, 20.0), + trip(1, 3, 20, 20.0), + trip(1, 4, 20, 20.0), + trip(2, 1, 20, 19.0) + ) + .checkParetoPrinciple() eq true + + taxiPark( + 1..5, 1..4, + trip(1, 1, 20, 20.0), + trip(1, 2, 20, 20.0), + trip(1, 3, 20, 20.0), + trip(1, 4, 20, 20.0), + trip(2, 1, 20, 21.0) + ) + .checkParetoPrinciple2() eq false +} + +private infix fun T.eq(other: T) { + if (this != other) throw AssertionError("Expected $this to equal $other") + println("OK") +} + +private fun taxiPark( + allDrivers: IntProgression, + allPassengers: IntProgression, + vararg trips: Trip +) = TaxiPark( + allDrivers = allDrivers.map { Driver(it.toString()) }.toSet(), + allPassengers = allPassengers.map { Passenger(it.toString()) }.toSet(), + trips = trips.toList() +) + +private fun trip( + driver: Int, + passenger: Int, + duration: Int, + distance: Double +) = Trip( + driver = Driver(driver.toString()), + passengers = setOf(Passenger(passenger.toString())), + duration = duration, + distance = distance +) diff --git a/src/assignment/week3/taxipark/TaxiParkTask.kt b/src/assignment/week3/taxipark/TaxiParkTask.kt index ee8cdef..8aa7440 100644 --- a/src/assignment/week3/taxipark/TaxiParkTask.kt +++ b/src/assignment/week3/taxipark/TaxiParkTask.kt @@ -1,4 +1,4 @@ -package taxipark +package assignment.week3.taxipark /* * Task #1. Find all the drivers who performed no trips. @@ -49,13 +49,13 @@ fun TaxiPark.findSmartPassengers(): Set = this.allPassengers * Return any period if many are the most frequent, return `null` if there're no trips. */ fun TaxiPark.findTheMostFrequentTripDurationPeriod(): IntRange? { - val maxDuration = this.trips + val (maxDuration, _) = this.trips .map { trip -> trip.duration / 10 } .groupBy { it } - .maxByOrNull { it.value.size } + .maxByOrNull { it.value.size } ?: return null - val startRange = maxDuration?.let { it.key * 10 } - return startRange?.let { it.rangeTo(it + 9) } + val startRange = maxDuration * 10 + return startRange.rangeTo(startRange + 9) } /*