-
-
Notifications
You must be signed in to change notification settings - Fork 546
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
luhn: Update Luhn tests and description (#474)
* Update Luhn tests and description I'm addressing a few different things here: 1. The tests exposed functionality that is not needed The `addends` and `check_digit` methods aren't needed by clients of this code, and I didn't see that students got any benefit from explicitly implementing them. The presence of these tests pushed students towards a particular implementation, which we [try to avoid](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md#improving-consistency-by-extracting-shared-test-data) > The test cases should not require people to follow one very specific way to solve the problem, e.g. the tests should avoid checking for helper functions that might not be present in other implementations. To fix this I've removed tests of `addends` and `check_digit`. 2. The tests were duplicative. There's not a way (that I could see, at least) of implementing Luhn in tiny steps. It's either working or its not. Once you have a couple valid tests and a couple invalid tests your are pretty much covered. (note: I guess you could test the cases where Luhn can be wrong, but that seemed outside the scope of this exercise.) Again, from the guide > All tests should be essential to the problem. Ensure people can get the first test passing easily. Then each following test should ideally add a little functionality. People should be able to solve the problem a little at a time. Avoid test cases that don't introduce any new aspect and would already pass anyway. To fix this I've removed most duplicative tests that would pass as soon as the student got a working Luhn implementation. 3. The examples and the description didn't match Luhn can be hard to grasp. At least it can be for me. What helps me is to have explicit, real-world examples. To help with this I've updated the description.md file to have examples of credit cards and Canadian SINs, two places where Luhn is used in the real world. To re-enforce the descriptions, I've used the same examples in the tests. So students can see step-by-step breakdowns of how to get their tests passing.
- Loading branch information
1 parent
94c7e00
commit 1f3fe70
Showing
2 changed files
with
111 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,34 @@ | ||
{ | ||
"#": [ | ||
"The only note about this exercise is to make sure that the methods used", | ||
"to check if a number is valid and to compute its checksum DO NOT modify", | ||
"the value of the internal attributes. ", | ||
"I say this because I've seen for the Python testcases that it needs a last", | ||
"test to call the validation method twice to assure this. ", | ||
"Simply assure that the two methods are const methods (to use a C++ notation)", | ||
"using the tools available in your language. ", | ||
"Here is a pretty universal solution for the validation method: ", | ||
"`return (0 === (this.checksum % 10)) ? true : false;`" | ||
], | ||
"cases": [ | ||
{ | ||
"description": "check digit", | ||
"input": 34567, | ||
"expected": 7 | ||
}, | ||
{ | ||
"description": "check digit with input ending in zero", | ||
"input": 91370, | ||
"expected": 0 | ||
}, | ||
{ | ||
"description": "check addends", | ||
"input": 12121, | ||
"expected": [1, 4, 1, 4, 1] | ||
}, | ||
{ | ||
"description": "check too large addends", | ||
"input": 8631, | ||
"expected": [7, 6, 6, 1] | ||
}, | ||
{ | ||
"description": "checksum", | ||
"input": 4913, | ||
"expected": 22 | ||
}, | ||
{ | ||
"description": "checksum of larger number", | ||
"input": 201773, | ||
"expected": 21 | ||
}, | ||
{ | ||
"description": "check invalid number", | ||
"input": 738, | ||
"expected": false | ||
}, | ||
{ | ||
"description": "check valid number", | ||
"input": 8739567, | ||
"expected": true | ||
}, | ||
{ | ||
"description": "create valid number", | ||
"input": 123, | ||
"expected": 1230 | ||
}, | ||
{ | ||
"description": "create larger valid number", | ||
"input": 873956, | ||
"expected": 8739567 | ||
}, | ||
{ | ||
"description": "create even larger valid number", | ||
"input": 837263756, | ||
"expected": 8372637564 | ||
} | ||
] | ||
"valid": [ | ||
{ | ||
"description": "single digit strings can not be valid", | ||
"input": "1", | ||
"expected": false | ||
}, | ||
{ | ||
"description": "A single zero is invalid", | ||
"input": "0", | ||
"expected": false | ||
}, | ||
{ | ||
"description": "valid Canadian SIN", | ||
"input": "046 454 286", | ||
"expected": true | ||
}, | ||
{ | ||
"description": "invalid Canadian SIN", | ||
"input": "046 454 287", | ||
"expected": false | ||
}, | ||
{ | ||
"description": "invalid credit card", | ||
"input": "8273 1232 7352 0569", | ||
"expected": false | ||
}, | ||
{ | ||
"description": "strings that contain non-digits are not valid", | ||
"input": "827a 1232 7352 0569", | ||
"expected": false | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,80 @@ | ||
The Luhn formula is a simple checksum formula used to validate a variety | ||
of identification numbers, such as credit card numbers and Canadian | ||
Social Insurance Numbers. | ||
|
||
The formula verifies a number against its included check digit, which is | ||
usually appended to a partial number to generate the full number. This | ||
number must pass the following test: | ||
|
||
- Counting from rightmost digit (which is the check digit) and moving | ||
left, double the value of every second digit. | ||
- For any digits that thus become 10 or more, subtract 9 from the | ||
result. | ||
- 1111 becomes 2121. | ||
- 8763 becomes 7733 (from 2×6=12 → 12-9=3 and 2×8=16 → 16-9=7). | ||
- Add all these digits together. | ||
- 1111 becomes 2121 sums as 2+1+2+1 to give a check digit of 6. | ||
- 8763 becomes 7733, and 7+7+3+3 is 20. | ||
|
||
If the total ends in 0 (put another way, if the total modulus 10 is | ||
congruent to 0), then the number is valid according to the Luhn formula; | ||
else it is not valid. So, 1111 is not valid (as shown above, it comes | ||
out to 6), while 8763 is valid (as shown above, it comes out to 20). | ||
|
||
Write a program that, given a number | ||
|
||
- Can check if it is valid per the Luhn formula. This should treat, for | ||
example, "2323 2005 7766 3554" as valid. | ||
- Can return the checksum, or the remainder from using the Luhn method. | ||
- Can add a check digit to make the number valid per the Luhn formula and | ||
return the original number plus that digit. This should give "2323 2005 7766 | ||
3554" in response to "2323 2005 7766 355". | ||
|
||
## About Checksums | ||
|
||
A checksum has to do with error-detection. There are a number of different | ||
ways in which a checksum could be calculated. | ||
|
||
When transmitting data, you might also send along a checksum that says how | ||
many bytes of data are being sent. That means that when the data arrives on | ||
the other side, you can count the bytes and compare it to the checksum. If | ||
these are different, then the data has been garbled in transmission. | ||
|
||
In the Luhn problem the final digit acts as a sanity check for all the prior | ||
digits. Running those prior digits through a particular algorithm should give | ||
you that final digit. | ||
|
||
It doesn't actually tell you if it's a real credit card number, only that it's | ||
a plausible one. It's the same thing with the bytes that get transmitted -- | ||
you could have the right number of bytes and still have a garbled message. So | ||
checksums are a simple sanity-check, not a real in-depth verification of the | ||
authenticity of some data. It's often a cheap first pass, and can be used to | ||
quickly discard obviously invalid things. | ||
The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is | ||
a simple checksum formula used to validate a variety of identification | ||
numbers, such as credit card numbers and Canadian Social Insurance | ||
Numbers. | ||
|
||
The task is to write a function that checks if a given string is valid. | ||
|
||
Validating a Number | ||
------ | ||
|
||
As an example, here is a valid (but fictitious) Canadian Social Insurance | ||
Number. | ||
|
||
``` | ||
046 454 286 | ||
``` | ||
|
||
The first step of the Luhn algorithm is to double every second digit, | ||
starting from the right. We will be doubling | ||
|
||
``` | ||
_4_ 4_4 _8_ | ||
``` | ||
|
||
If doubling the number results in a number greater than 9 then subtract 9 | ||
from the product. The results of our doubling: | ||
|
||
``` | ||
086 858 276 | ||
``` | ||
|
||
Then sum all of the digits | ||
|
||
``` | ||
0+8+6+8+5+8+2+7+6 = 50 | ||
``` | ||
|
||
If the sum is evenly divisible by 10, then the number is valid. This number is valid! | ||
|
||
An example of an invalid Canadian SIN where we've changed the final digit | ||
|
||
``` | ||
046 454 287 | ||
``` | ||
|
||
Double the second digits, starting from the right | ||
|
||
``` | ||
086 858 277 | ||
``` | ||
|
||
Sum the digits | ||
|
||
``` | ||
0+8+6+8+5+8+2+7+7 = 51 | ||
``` | ||
|
||
51 is not evenly divisible by 10, so this number is not valid. | ||
|
||
---- | ||
|
||
An example of an invalid credit card account | ||
|
||
``` | ||
8273 1232 7352 0569 | ||
``` | ||
|
||
Double the second digits, starting from the right | ||
|
||
``` | ||
7253 2262 5312 0539 | ||
``` | ||
|
||
Sum the digits | ||
|
||
``` | ||
7+2+5+3+2+2+5+2+5+3+1+2+0+5+3+9 = 57 | ||
``` | ||
|
||
57 is not evenly divisible by 10, so this number is not valid. |