diff --git a/exercises/luhn/src/example/kotlin/Luhn.kt b/exercises/luhn/src/example/kotlin/Luhn.kt index 39550342..427abb4b 100644 --- a/exercises/luhn/src/example/kotlin/Luhn.kt +++ b/exercises/luhn/src/example/kotlin/Luhn.kt @@ -1,37 +1,29 @@ -data class Luhn(val number: Long) { +object Luhn { - val checkDigit: Int by lazy { checkDigit(number) } + fun isValid(candidate: String): Boolean = + isValidCandidate(candidate) && checksum(number(candidate)) == 0 - val addends: List by lazy { - digits(number).withIndex().reversed() - .map { if (isOdd(it.index)) dbl(it.value) else it.value } - } + private fun isValidCandidate(candidate: String): Boolean = + candidate.filter(Char::isDigit).length > 1 && + candidate.all { it.isDigit() || Character.isSpaceChar(it) } - val checksum: Int by lazy { addends.sum() } + private fun number(candidate: String) = candidate.filter(Char::isDigit).toLong() - val isValid: Boolean by lazy { checksum % 10 == 0 } + private fun checksum(number: Long) = addends(number).sum() % 10 - val create: Long by lazy { - val zeroCheckDigitNumber = number * 10 - val luhn = Luhn(zeroCheckDigitNumber) + private fun addends(number: Long): List = digits(number).withIndex().reversed() + .map { if (isOdd(it.index)) dbl(it.value) else it.value } - if (luhn.isValid) zeroCheckDigitNumber else zeroCheckDigitNumber + (10 - luhn.checksum % 10) + private fun digits(n: Long): List = when (n) { + 0L -> emptyList() + else -> listOf((n % 10).toInt()) + digits(n / 10) } - companion object { - private fun checkDigit(n: Long) = (n % 10).toInt() - - private fun digits(n: Long): List = when (n) { - 0L -> emptyList() - else -> listOf(checkDigit(n)) + digits(n / 10) - } - - private fun dbl(n: Int): Int { - val dbled = n * 2 - return if (dbled > 10) dbled - 9 else dbled - } - - private fun isOdd(i: Int) = i % 2 == 1 + private fun dbl(n: Int): Int { + val dbled = n * 2 + return if (dbled > 9) dbled - 9 else dbled } + private fun isOdd(i: Int) = i % 2 == 1 + } diff --git a/exercises/luhn/src/test/kotlin/LuhnTest.kt b/exercises/luhn/src/test/kotlin/LuhnTest.kt index a723e3b6..b0764ef3 100644 --- a/exercises/luhn/src/test/kotlin/LuhnTest.kt +++ b/exercises/luhn/src/test/kotlin/LuhnTest.kt @@ -1,86 +1,88 @@ import org.junit.Test import org.junit.Ignore -import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue class LuhnTest { @Test - fun checkDigitIsRightMostDigit() { - val expectedOutput = 7 - - assertEquals(expectedOutput, Luhn(34567).checkDigit) + fun singleDigitStringsCannotBeValid() { + assertFalse(Luhn.isValid("1")) } @Ignore @Test - fun addendsDoublesEveryOtherNumberFromRight() { - val expectedOutput = listOf(1, 4, 1, 4, 1) - - assertEquals(expectedOutput, Luhn(12121).addends) + fun singleZeroIsInvalid() { + assertFalse(Luhn.isValid("0")) } @Ignore @Test - fun addendsSubtracts9WhenDoubledNumberIsMoreThan9() { - val expectedOutput = listOf(7, 6, 6, 1) - - assertEquals(expectedOutput, Luhn(8631).addends) + fun simpleValidSINThatRemainsValidIfReversed() { + assertTrue(Luhn.isValid("059")) + assertTrue(Luhn.isValid("950")) } @Ignore @Test - fun checkSumAddsAddendsTogether1() { - val expectedOutput = 22 - - assertEquals(expectedOutput, Luhn(4913).checksum) + fun simpleValidSINThatBecomesInvalidIfReversed() { + assertTrue(Luhn.isValid("59")) + assertFalse(Luhn.isValid("95")) } @Ignore @Test - fun checkSumAddsAddendsTogether2() { - val expectedOutput = 21 - - assertEquals(expectedOutput, Luhn(201773).checksum) + fun validCanadianSIN() { + assertTrue(Luhn.isValid("055 444 285")) } @Ignore @Test - fun numberIsValidWhenChecksumMod10IsZero1() { - val expectedOutput = false - - assertEquals(expectedOutput, Luhn(738).isValid) + fun invalidCanadianSIN() { + assertFalse(Luhn.isValid("055 444 286")) } @Ignore @Test - fun numberIsValidWhenChecksumMod10IsZero2() { - val expectedOutput = true - - assertEquals(expectedOutput, Luhn(8739567).isValid) + fun invalidCreditCard() { + assertFalse(Luhn.isValid("8273 1232 7352 0569")) } @Ignore @Test - fun luhnCanCreateSimpleNumbersWithValidCheckDigit() { - val expectedOutput = 1230L + fun validStringsWithNonDigitIncludedBecomeInvalid() { + assertFalse(Luhn.isValid("055a 444 285")) + } - assertEquals(expectedOutput, Luhn(123).create) + @Ignore + @Test + fun validStringsWithPunctuationIncludedBecomeInvalid() { + assertFalse(Luhn.isValid("055-444-285")) } @Ignore @Test - fun luhnCanCreateLargeNumbersWithValidCheckDigit() { - val expectedOutput = 8739567L + fun validStringsWithSymbolsIncludedBecomeInvalid() { + assertFalse(Luhn.isValid("055£ 444$ 285")) + } - assertEquals(expectedOutput, Luhn(873956).create) + @Ignore + @Test + fun singleZeroWithSpaceIsInvalid() { + assertFalse(Luhn.isValid(" 0")) } @Ignore @Test - fun luhnCanCreateHugeNumbersWithValidCheckDigit() { - val expectedOutput = 8372637564L + fun moreThanSingleZeroIsValid() { + assertTrue(Luhn.isValid("0000 0")) + } - assertEquals(expectedOutput, Luhn(837263756).create) + @Ignore + @Test + fun inputDigit9IsCorrectlyConvertedToOutputDigit9() { + assertTrue(Luhn.isValid("091")) } + }