From 74010f4f68eee8f279b24baec0a9fc2e5346e062 Mon Sep 17 00:00:00 2001 From: Stuart Kent Date: Sat, 14 Jan 2017 23:11:57 -0500 Subject: [PATCH] Update Luhn tests and sample implementation Fixes #245. Tracking issue: https://github.com/exercism/x-common/issues/491 --- exercises/luhn/src/example/java/Luhn.java | 90 ------------------ .../luhn/src/example/java/LuhnValidator.java | 40 ++++++++ exercises/luhn/src/test/java/LuhnTest.java | 95 ------------------- .../luhn/src/test/java/LuhnValidatorTest.java | 39 ++++++++ 4 files changed, 79 insertions(+), 185 deletions(-) delete mode 100644 exercises/luhn/src/example/java/Luhn.java create mode 100644 exercises/luhn/src/example/java/LuhnValidator.java delete mode 100644 exercises/luhn/src/test/java/LuhnTest.java create mode 100644 exercises/luhn/src/test/java/LuhnValidatorTest.java diff --git a/exercises/luhn/src/example/java/Luhn.java b/exercises/luhn/src/example/java/Luhn.java deleted file mode 100644 index 3f8cbb242..000000000 --- a/exercises/luhn/src/example/java/Luhn.java +++ /dev/null @@ -1,90 +0,0 @@ -import java.util.stream.IntStream; - -public class Luhn { - - private long number; - private long checkDigit; - private int[] addends; - private int checkSum; - private boolean isValid; - - public Luhn(long number) { - this.number = number; - this.checkDigit = number % 10; - this.addends = generateAddends(); - this.checkSum = IntStream.of(addends).sum(); - this.isValid = checkSum % 10 == 0; - } - - public long getCheckDigit() { - return checkDigit; - } - - public int[] getAddends() { - return addends; - } - - public int getCheckSum() { - return checkSum; - } - - public boolean isValid() { - return isValid; - } - - private int[] generateAddends() { - int[] reversedIntArray = splitToReversedIntArray(number); - - for (int index = 1; index < reversedIntArray.length; index++) { - if (index % 2 != 0) { - reversedIntArray[index] = convertDigitForAddend(reversedIntArray[index]); - } - } - - return reverseIntArray(reversedIntArray); - } - - private static int[] splitToReversedIntArray(long value) { - int[] ints = new StringBuilder(Long.toString(value)) - .reverse() - .toString() - .chars() - .map(x -> Character.getNumericValue(x)) - .toArray(); - - return ints; - } - - private static int convertDigitForAddend(int value) { - int doubled = value * 2; - - return doubled < 10 ? doubled : doubled - 9; - } - - public static long create(long number) { - long zeroCheckDigitNumber = number * 10; - Luhn luhn = new Luhn(zeroCheckDigitNumber); - - if (luhn.isValid()) { - return zeroCheckDigitNumber; - } - - return zeroCheckDigitNumber + createCheckDigit(luhn.getCheckSum()); - } - - private static int createCheckDigit(int value) { - int nearestTen = (int) (Math.ceil(value / 10.0) * 10); - - return nearestTen - value; - } - - private static int[] reverseIntArray(int[] array) { - int[] reversed = new int[array.length]; - - for (int index = 0; index < array.length; index++) { - reversed[index] = array[array.length - 1 - index]; - } - - return reversed; - } -} diff --git a/exercises/luhn/src/example/java/LuhnValidator.java b/exercises/luhn/src/example/java/LuhnValidator.java new file mode 100644 index 000000000..24f6d9f3a --- /dev/null +++ b/exercises/luhn/src/example/java/LuhnValidator.java @@ -0,0 +1,40 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +final class LuhnValidator { + + private static final Pattern SPACE_PATTERN = Pattern.compile("\\s+"); + + boolean isValid(final String candidate) { + final String sanitizedCandidate = SPACE_PATTERN.matcher(candidate).replaceAll(""); + + final List computedDigits = new ArrayList<>(); + + for (int charIndex = 0; charIndex < sanitizedCandidate.length(); charIndex++) { + int inputDigit = Character.getNumericValue(sanitizedCandidate.charAt(charIndex)); + + // Character.getNumericValue returns a negative int if the supplied character does not represent a digit. + if (inputDigit < 0) { + return false; + } + + if (charIndex % 2 == 1) { + /* + * Since our doubled input digit must lie in [2, 18], the operation + * + * "subtract 9 from the doubled input digit if it exceeds 9 in value" + * + * is equivalent to applying the modulo operation below universally. + */ + inputDigit = (2 * inputDigit) % 9; + } + + computedDigits.add(inputDigit); + } + + final int digitSum = computedDigits.stream().mapToInt(Integer::intValue).sum(); + return digitSum > 0 && digitSum % 10 == 0; + } + +} diff --git a/exercises/luhn/src/test/java/LuhnTest.java b/exercises/luhn/src/test/java/LuhnTest.java deleted file mode 100644 index 159b68d0d..000000000 --- a/exercises/luhn/src/test/java/LuhnTest.java +++ /dev/null @@ -1,95 +0,0 @@ -import org.junit.Test; -import org.junit.Ignore; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -public class LuhnTest { - - - @Test - public void checkDigitIsRightMostDigit() { - Luhn luhn = new Luhn(34567); - int expectedOutput = 7; - - assertEquals(expectedOutput, luhn.getCheckDigit()); - } - - @Ignore - @Test - public void addendsDoublesEveryOtherNumberFromRight() { - Luhn luhn = new Luhn(12121); - int[] expectedOutput = new int[]{1, 4, 1, 4, 1}; - - assertArrayEquals(expectedOutput, luhn.getAddends()); - } - - @Ignore - @Test - public void addendsSubtracts9WhenDoubledNumberIsMoreThan9() { - Luhn luhn = new Luhn(8631); - int[] expectedOutput = new int[]{7, 6, 6, 1}; - - assertArrayEquals(expectedOutput, luhn.getAddends()); - } - - @Ignore - @Test - public void checkSumAddsAddendsTogether1() { - Luhn luhn = new Luhn(4913); - int expectedOutput = 22; - - assertEquals(expectedOutput, luhn.getCheckSum()); - } - - @Ignore - @Test - public void checkSumAddsAddendsTogether2() { - Luhn luhn = new Luhn(201773); - int expectedOutput = 21; - - assertEquals(expectedOutput, luhn.getCheckSum()); - } - - @Ignore - @Test - public void numberIsValidWhenChecksumMod10IsZero1() { - Luhn luhn = new Luhn(738); - boolean expectedOutput = false; - - assertEquals(expectedOutput, luhn.isValid()); - } - - @Ignore - @Test - public void numberIsValidWhenChecksumMod10IsZero2() { - Luhn luhn = new Luhn(8739567); - boolean expectedOutput = true; - - assertEquals(expectedOutput, luhn.isValid()); - } - - @Ignore - @Test - public void luhnCanCreateSimpleNumbersWithValidCheckDigit() { - long expectedOutput = 1230; - - assertEquals(expectedOutput, Luhn.create(123)); - } - - @Ignore - @Test - public void luhnCanCreateLargeNumbersWithValidCheckDigit() { - long expectedOutput = 8739567; - - assertEquals(expectedOutput, Luhn.create(873956)); - } - - @Ignore - @Test - public void luhnCanCreateHugeNumbersWithValidCheckDigit() { - long expectedOutput = 8372637564L; - - assertEquals(expectedOutput, Luhn.create(837263756)); - } -} diff --git a/exercises/luhn/src/test/java/LuhnValidatorTest.java b/exercises/luhn/src/test/java/LuhnValidatorTest.java new file mode 100644 index 000000000..4423499e2 --- /dev/null +++ b/exercises/luhn/src/test/java/LuhnValidatorTest.java @@ -0,0 +1,39 @@ +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; + +@RunWith(Parameterized.class) +public class LuhnValidatorTest { + + @Parameterized.Parameters(name="When checking if {0} is valid, the answer should be {1}") + public static Collection data() { + return Arrays.asList(new Object[][]{ + {"1", false}, + {"0", false}, + {"046 454 286", true}, + {"046 454 287", false}, + {"8273 1232 7352 0569", false}, + {"046a 454 286", false}, + }); + } + + private final String candidate; + + private final boolean expectedToBeValid; + + public LuhnValidatorTest(final String candidate, final boolean expectedToBeValid) { + this.candidate = candidate; + this.expectedToBeValid = expectedToBeValid; + } + + @Test + public void test() { + assertEquals(expectedToBeValid, new LuhnValidator().isValid(candidate)); + } + +}