diff --git a/exercises/luhn/examples/success-standard/src/Luhn.hs b/exercises/luhn/examples/success-standard/src/Luhn.hs index b3aea79c6..8d27dd0b9 100644 --- a/exercises/luhn/examples/success-standard/src/Luhn.hs +++ b/exercises/luhn/examples/success-standard/src/Luhn.hs @@ -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 diff --git a/exercises/luhn/package.yaml b/exercises/luhn/package.yaml index aecf076ba..bc21ef93d 100644 --- a/exercises/luhn/package.yaml +++ b/exercises/luhn/package.yaml @@ -1,5 +1,5 @@ name: luhn -version: 0.9.0.1 # 2016-08-04 +version: 1.0.0.2 dependencies: - base diff --git a/exercises/luhn/src/Luhn.hs b/exercises/luhn/src/Luhn.hs index e2fc38dfd..bba6d5046 100644 --- a/exercises/luhn/src/Luhn.hs +++ b/exercises/luhn/src/Luhn.hs @@ -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." diff --git a/exercises/luhn/test/Tests.hs b/exercises/luhn/test/Tests.hs index 7e7372d6e..4eee15377 100644 --- a/exercises/luhn/test/Tests.hs +++ b/exercises/luhn/test/Tests.hs @@ -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 + } + ]