module Math.Checksum.ISBN (checksum, valid) where
import qualified Math.Checksum.Utility as Util
import qualified Control.Monad.Exception.Synchronous as ME
import Control.Monad.Exception.Synchronous (Exceptional)
import Control.Monad (zipWithM)
import Control.Applicative ((<$>))
checksum :: String -> Exceptional String Int
checksum xs = remainder [1..] <$> mapM Util.intFromDigit xs
valid :: String -> Maybe String
valid xs = Util.processValid $ do
ds <-
zipWithM id
(replicate 9 (Util.intFromDigit=<<) ++ (intFromCheckdigit=<<) :
[ME.switch
(const $ return 0) (const $ ME.throw "more than 10 characters")])
(map return xs ++ [ME.throw "less than 10 characters"])
return $ 0 == remainder weights ds
intFromCheckdigit :: Char -> Exceptional String Int
intFromCheckdigit 'X' = return 10
intFromCheckdigit c = Util.intFromDigit c
remainder :: [Int] -> [Int] -> Int
remainder ws ds = mod (sum (zipWith (*) ws ds)) 11
weights :: [Int]
weights = [1..10] ++ [-1]