Skip to content

Commit 11faba6

Browse files
nithiajtigger
authored andcommitted
Changes to Luhn tests and sample implementation. Fixes #24 (#37)
1 parent 7b5f1dc commit 11faba6

File tree

2 files changed

+58
-64
lines changed

2 files changed

+58
-64
lines changed
Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,29 @@
1-
data class Luhn(val number: Long) {
1+
object Luhn {
22

3-
val checkDigit: Int by lazy { checkDigit(number) }
3+
fun isValid(candidate: String): Boolean =
4+
isValidCandidate(candidate) && checksum(number(candidate)) == 0
45

5-
val addends: List<Int> by lazy {
6-
digits(number).withIndex().reversed()
7-
.map { if (isOdd(it.index)) dbl(it.value) else it.value }
8-
}
6+
private fun isValidCandidate(candidate: String): Boolean =
7+
candidate.filter(Char::isDigit).length > 1 &&
8+
candidate.all { it.isDigit() || Character.isSpaceChar(it) }
99

10-
val checksum: Int by lazy { addends.sum() }
10+
private fun number(candidate: String) = candidate.filter(Char::isDigit).toLong()
1111

12-
val isValid: Boolean by lazy { checksum % 10 == 0 }
12+
private fun checksum(number: Long) = addends(number).sum() % 10
1313

14-
val create: Long by lazy {
15-
val zeroCheckDigitNumber = number * 10
16-
val luhn = Luhn(zeroCheckDigitNumber)
14+
private fun addends(number: Long): List<Int> = digits(number).withIndex().reversed()
15+
.map { if (isOdd(it.index)) dbl(it.value) else it.value }
1716

18-
if (luhn.isValid) zeroCheckDigitNumber else zeroCheckDigitNumber + (10 - luhn.checksum % 10)
17+
private fun digits(n: Long): List<Int> = when (n) {
18+
0L -> emptyList()
19+
else -> listOf((n % 10).toInt()) + digits(n / 10)
1920
}
2021

21-
companion object {
22-
private fun checkDigit(n: Long) = (n % 10).toInt()
23-
24-
private fun digits(n: Long): List<Int> = when (n) {
25-
0L -> emptyList()
26-
else -> listOf(checkDigit(n)) + digits(n / 10)
27-
}
28-
29-
private fun dbl(n: Int): Int {
30-
val dbled = n * 2
31-
return if (dbled > 10) dbled - 9 else dbled
32-
}
33-
34-
private fun isOdd(i: Int) = i % 2 == 1
22+
private fun dbl(n: Int): Int {
23+
val dbled = n * 2
24+
return if (dbled > 9) dbled - 9 else dbled
3525
}
3626

27+
private fun isOdd(i: Int) = i % 2 == 1
28+
3729
}
Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,88 @@
11
import org.junit.Test
22
import org.junit.Ignore
3-
import kotlin.test.assertEquals
3+
import kotlin.test.assertFalse
4+
import kotlin.test.assertTrue
45

56
class LuhnTest {
67

78

89
@Test
9-
fun checkDigitIsRightMostDigit() {
10-
val expectedOutput = 7
11-
12-
assertEquals(expectedOutput, Luhn(34567).checkDigit)
10+
fun singleDigitStringsCannotBeValid() {
11+
assertFalse(Luhn.isValid("1"))
1312
}
1413

1514
@Ignore
1615
@Test
17-
fun addendsDoublesEveryOtherNumberFromRight() {
18-
val expectedOutput = listOf(1, 4, 1, 4, 1)
19-
20-
assertEquals(expectedOutput, Luhn(12121).addends)
16+
fun singleZeroIsInvalid() {
17+
assertFalse(Luhn.isValid("0"))
2118
}
2219

2320
@Ignore
2421
@Test
25-
fun addendsSubtracts9WhenDoubledNumberIsMoreThan9() {
26-
val expectedOutput = listOf(7, 6, 6, 1)
27-
28-
assertEquals(expectedOutput, Luhn(8631).addends)
22+
fun simpleValidSINThatRemainsValidIfReversed() {
23+
assertTrue(Luhn.isValid("059"))
24+
assertTrue(Luhn.isValid("950"))
2925
}
3026

3127
@Ignore
3228
@Test
33-
fun checkSumAddsAddendsTogether1() {
34-
val expectedOutput = 22
35-
36-
assertEquals(expectedOutput, Luhn(4913).checksum)
29+
fun simpleValidSINThatBecomesInvalidIfReversed() {
30+
assertTrue(Luhn.isValid("59"))
31+
assertFalse(Luhn.isValid("95"))
3732
}
3833

3934
@Ignore
4035
@Test
41-
fun checkSumAddsAddendsTogether2() {
42-
val expectedOutput = 21
43-
44-
assertEquals(expectedOutput, Luhn(201773).checksum)
36+
fun validCanadianSIN() {
37+
assertTrue(Luhn.isValid("055 444 285"))
4538
}
4639

4740
@Ignore
4841
@Test
49-
fun numberIsValidWhenChecksumMod10IsZero1() {
50-
val expectedOutput = false
51-
52-
assertEquals(expectedOutput, Luhn(738).isValid)
42+
fun invalidCanadianSIN() {
43+
assertFalse(Luhn.isValid("055 444 286"))
5344
}
5445

5546
@Ignore
5647
@Test
57-
fun numberIsValidWhenChecksumMod10IsZero2() {
58-
val expectedOutput = true
59-
60-
assertEquals(expectedOutput, Luhn(8739567).isValid)
48+
fun invalidCreditCard() {
49+
assertFalse(Luhn.isValid("8273 1232 7352 0569"))
6150
}
6251

6352
@Ignore
6453
@Test
65-
fun luhnCanCreateSimpleNumbersWithValidCheckDigit() {
66-
val expectedOutput = 1230L
54+
fun validStringsWithNonDigitIncludedBecomeInvalid() {
55+
assertFalse(Luhn.isValid("055a 444 285"))
56+
}
6757

68-
assertEquals(expectedOutput, Luhn(123).create)
58+
@Ignore
59+
@Test
60+
fun validStringsWithPunctuationIncludedBecomeInvalid() {
61+
assertFalse(Luhn.isValid("055-444-285"))
6962
}
7063

7164
@Ignore
7265
@Test
73-
fun luhnCanCreateLargeNumbersWithValidCheckDigit() {
74-
val expectedOutput = 8739567L
66+
fun validStringsWithSymbolsIncludedBecomeInvalid() {
67+
assertFalse(Luhn.isValid("055£ 444$ 285"))
68+
}
7569

76-
assertEquals(expectedOutput, Luhn(873956).create)
70+
@Ignore
71+
@Test
72+
fun singleZeroWithSpaceIsInvalid() {
73+
assertFalse(Luhn.isValid(" 0"))
7774
}
7875

7976
@Ignore
8077
@Test
81-
fun luhnCanCreateHugeNumbersWithValidCheckDigit() {
82-
val expectedOutput = 8372637564L
78+
fun moreThanSingleZeroIsValid() {
79+
assertTrue(Luhn.isValid("0000 0"))
80+
}
8381

84-
assertEquals(expectedOutput, Luhn(837263756).create)
82+
@Ignore
83+
@Test
84+
fun inputDigit9IsCorrectlyConvertedToOutputDigit9() {
85+
assertTrue(Luhn.isValid("091"))
8586
}
87+
8688
}

0 commit comments

Comments
 (0)