-
-
Notifications
You must be signed in to change notification settings - Fork 199
luhn 1.0.0.2: only test isValid, use (most) x-common cases #533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,32 +1,17 @@ | ||
| module Luhn (checkDigit, addends, checksum, isValid, create) where | ||
| module Luhn (isValid) where | ||
|
|
||
| revDigits :: Integral a => a -> [a] | ||
| revDigits n = rem10 : digits | ||
| where (quot10, rem10) = n `quotRem` 10 | ||
| digits | quot10 == 0 = [] | ||
| | otherwise = revDigits quot10 | ||
| import Data.Char (digitToInt) | ||
|
|
||
| luhnDouble :: Integral a => a -> a | ||
| luhnDouble n | n < 5 = n * 2 | ||
| | otherwise = n * 2 - 9 | ||
|
|
||
| luhnDigits :: Integral a => a -> [a] | ||
| luhnDigits = zipWith ($) (cycle [id, luhnDouble]) . revDigits | ||
| luhnDigits :: Integral a => [a] -> [a] | ||
| luhnDigits = zipWith ($) (cycle [id, luhnDouble]) . reverse | ||
|
|
||
| checkDigit :: Integral a => a -> a | ||
| checkDigit = head . revDigits | ||
|
|
||
| addends :: Integral a => a -> [a] | ||
| addends = reverse . luhnDigits | ||
|
|
||
| checksum :: Integral a => a -> a | ||
| checksum :: Integral a => [a] -> a | ||
| checksum = (`rem` 10) . sum . luhnDigits | ||
|
|
||
| isValid :: Integral a => a -> Bool | ||
| isValid = (0 ==) . checksum | ||
|
|
||
| create :: Integral a => a -> a | ||
| create n | chk == 0 = n10 | ||
| | otherwise = n10 + (10 - chk) | ||
| where n10 = n * 10 | ||
| chk = checksum n10 `rem` 10 | ||
| isValid :: String -> Bool | ||
| isValid s = length digits > 1 && checksum digits == 0 | ||
| where digits = map digitToInt $ filter (/= ' ') s |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| name: luhn | ||
| version: 0.9.0.1 # 2016-08-04 | ||
| version: 1.0.0.2 | ||
|
|
||
| dependencies: | ||
| - base | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,4 @@ | ||
| module Luhn (addends, checkDigit, checksum, create, isValid) where | ||
| module Luhn (isValid) where | ||
|
|
||
| addends :: Integer -> [Integer] | ||
| addends n = error "You need to implement this function." | ||
|
|
||
| checkDigit :: Integer -> Integer | ||
| checkDigit n = error "You need to implement this function." | ||
|
|
||
| checksum :: Integer -> Integer | ||
| checksum n = error "You need to implement this function." | ||
|
|
||
| create :: Integer -> Integer | ||
| create n = error "You need to implement this function." | ||
|
|
||
| isValid :: Integer -> Bool | ||
| isValid :: String -> Bool | ||
| isValid n = error "You need to implement this function." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,79 +1,77 @@ | ||
| {-# OPTIONS_GHC -fno-warn-type-defaults #-} | ||
| {-# LANGUAGE RecordWildCards #-} | ||
|
|
||
| import Data.Foldable (for_) | ||
| import Test.Hspec (Spec, describe, it, shouldBe) | ||
| import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith) | ||
|
|
||
| import Luhn (addends, checkDigit, checksum, create, isValid) | ||
| import Luhn (isValid) | ||
|
|
||
| main :: IO () | ||
| main = hspecWith defaultConfig {configFastFail = True} specs | ||
|
|
||
| specs :: Spec | ||
| specs = describe "luhn" $ do | ||
| describe "standard tests" $ do | ||
|
|
||
| it "check digit" $ | ||
| checkDigit 34567 `shouldBe` 7 | ||
|
|
||
| it "check digit with input ending in zero" $ | ||
| checkDigit 91370 `shouldBe` 0 | ||
|
|
||
| it "check addends" $ | ||
| addends 12121 `shouldBe` [1, 4, 1, 4, 1] | ||
|
|
||
| it "check too large addends" $ | ||
| addends 8631 `shouldBe` [7, 6, 6, 1] | ||
|
|
||
| -- The reference test cases expect the checksum function to return | ||
| -- the simple sum of the transformed digits, not their `mod 10` sum. | ||
| -- In this track, we insist on the `mod 10`. :) | ||
|
|
||
| it "checksum" $ | ||
| checksum 4913 `shouldBe` 2 -- The reference test expects 22. | ||
|
|
||
| it "checksum of larger number" $ | ||
| checksum 201773 `shouldBe` 1 -- The reference test expects 21. | ||
|
|
||
| it "check invalid number" $ | ||
| isValid 738 `shouldBe` False | ||
|
|
||
| it "check valid number" $ | ||
| isValid 8739567 `shouldBe` True | ||
|
|
||
| it "create valid number" $ | ||
| create 123 `shouldBe` 1230 | ||
|
|
||
| it "create larger valid number" $ | ||
| create 873956 `shouldBe` 8739567 | ||
|
|
||
| it "create even larger valid number" $ | ||
| create 837263756 `shouldBe` 8372637564 | ||
|
|
||
| describe "track-specific tests" $ do | ||
|
|
||
| -- This track has some tests that were not included in the | ||
| -- reference test cases from `exercism/x-common/leap.json`. | ||
|
|
||
| it "checksum 1111" $ | ||
| checksum 1111 `shouldBe` 6 | ||
|
|
||
| it "checksum 8763" $ | ||
| checksum 8763 `shouldBe` 0 | ||
|
|
||
| it "checksum 8739567" $ | ||
| checksum 8739567 `shouldBe` 0 | ||
|
|
||
| it "checksum 2323200577663554" $ | ||
| checksum 2323200577663554 `shouldBe` 0 | ||
|
|
||
| it "isValid 1111" $ | ||
| isValid 1111 `shouldBe` False | ||
|
|
||
| it "isValid 8763" $ | ||
| isValid 8763 `shouldBe` True | ||
|
|
||
| it "isValid 2323200577663554" $ | ||
| isValid 2323200577663554 `shouldBe` True | ||
|
|
||
| it "create 232320057766355" $ | ||
| create 232320057766355 `shouldBe` 2323200577663554 | ||
| specs = describe "valid" $ for_ cases test | ||
| where | ||
| test Case{..} = it description $ isValid input `shouldBe` expected | ||
|
|
||
| data Case = Case { description :: String | ||
| , input :: String | ||
| , expected :: Bool | ||
| } | ||
|
|
||
| cases :: [Case] | ||
| cases = [ Case { description = "single digit strings can not be valid" | ||
| , input = "1" | ||
| , expected = False | ||
| } | ||
| , Case { description = "A single zero is invalid" | ||
| , input = "0" | ||
| , expected = False | ||
| } | ||
| , Case { description = "a simple valid SIN that remains valid if reversed" | ||
| , input = "059" | ||
| , expected = True | ||
| } | ||
| , Case { description = "a simple valid SIN that becomes invalid if reversed" | ||
| , input = "59" | ||
| , expected = True | ||
| } | ||
| , Case { description = "a valid Canadian SIN" | ||
| , input = "055 444 285" | ||
| , expected = True | ||
| } | ||
| , Case { description = "invalid Canadian SIN" | ||
| , input = "055 444 286" | ||
| , expected = False | ||
| } | ||
| , Case { description = "invalid credit card" | ||
| , input = "8273 1232 7352 0569" | ||
| , expected = False | ||
| } | ||
| -- This track is not testing these cases, since we would rather focus on the algorithm, | ||
| -- and because it seems strange to be unable to distinguish between well-formed invalid input and malformed input. | ||
| -- , Case { description = "valid strings with a non-digit included become invalid" | ||
| -- , input = "055a 444 285" | ||
| -- , expected = False | ||
| -- } | ||
| -- , Case { description = "valid strings with punctuation included become invalid" | ||
| -- , input = "055-444-285" | ||
| -- , expected = False | ||
| -- } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here it seems that you suppressed one test: {
"description": "valid strings with symbols included become invalid",
"property": "valid",
"input": "055£ 444$ 285",
"expected": false
},Was it intentional? I don't consider that a problem, because considering what was discussed in exercism/problem-specifications#428, it probably shouldn't exist anyway.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! I guess I'll add it (and comment it) for the purpose of following x-common, but you are right that we expect the case to go away soon. |
||
| -- , Case { description = "valid strings with symbols included become invalid" | ||
| -- , input = "055£ 444$ 285" | ||
| -- , expected = False | ||
| -- } | ||
| , Case { description = "single zero with space is invalid" | ||
| , input = " 0" | ||
| , expected = False | ||
| } | ||
| , Case { description = "more than a single zero is valid" | ||
| , input = "0000 0" | ||
| , expected = True | ||
| } | ||
| , Case { description = "input digit 9 is correctly converted to output digit 9" | ||
| , input = "091" | ||
| , expected = True | ||
| } | ||
| ] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
notice! no luhn!