-
-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
luhn 1.0.0.2: only test isValid, use (most) x-common cases
exercism/problem-specifications#474 exercism/problem-specifications#496 exercism/problem-specifications#522 exercism/problem-specifications#547 In consideration of exercism/problem-specifications#523, we will not be testing the malformed inputs. They have been commented out. Closes #469
- Loading branch information
1 parent
381d339
commit 610ba69
Showing
4 changed files
with
79 additions
and
108 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,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 |
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,5 +1,5 @@ | ||
name: luhn | ||
version: 0.9.0.1 # 2016-08-04 | ||
version: 1.0.0.2 | ||
|
||
dependencies: | ||
- base | ||
|
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,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." |
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,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 | ||
-- } | ||
-- , 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 | ||
} | ||
] |