Skip to content

Commit

Permalink
luhn 1.0.0.2: only test isValid, use (most) x-common cases
Browse files Browse the repository at this point in the history
  • Loading branch information
petertseng committed May 9, 2017
1 parent 381d339 commit 610ba69
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 108 deletions.
31 changes: 8 additions & 23 deletions exercises/luhn/examples/success-standard/src/Luhn.hs
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
2 changes: 1 addition & 1 deletion exercises/luhn/package.yaml
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
Expand Down
16 changes: 2 additions & 14 deletions exercises/luhn/src/Luhn.hs
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."
138 changes: 68 additions & 70 deletions exercises/luhn/test/Tests.hs
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
}
]

0 comments on commit 610ba69

Please sign in to comment.