{- |
European Article Number

<https://en.wikipedia.org/wiki/International_Article_Number>
-}
module Math.Checksum.EAN (checksum, valid) where

import qualified Math.Checksum.Utility as Util

import Control.Monad.Exception.Synchronous (Exceptional)

import qualified Data.List as List


{- |
> checksum "400638129240" == Success 5
-}
checksum :: String -> Exceptional String Int
checksum :: String -> Exceptional String Int
checksum String
xs = do
   [Int]
ws <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Char -> Exceptional String Int
Util.intFromDigit String
xs
   forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int
9 forall a. Num a => a -> a -> a
- [Int] -> Int
remainder ([Int]
wsforall a. [a] -> [a] -> [a]
++[Int
9])

{- |
> valid "4006381292405" == Nothing
> valid "4006381292406" == Just "check sum does not match"
-}
valid :: String -> Maybe String
valid :: String -> Maybe String
valid String
xs = Exceptional String Bool -> Maybe String
Util.processValid forall a b. (a -> b) -> a -> b
$ do
   [Int]
ws <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Char -> Exceptional String Int
Util.intFromDigit String
xs
   forall (m :: * -> *) a. Monad m => a -> m a
return (Int
0 forall a. Eq a => a -> a -> Bool
== [Int] -> Int
remainder [Int]
ws)

{-
Not stream-friendly way, but more declarative.
-}
_remainder :: [Int] -> Int
_remainder :: [Int] -> Int
_remainder [Int]
ws = forall a. Integral a => a -> a -> a
mod (forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith forall a. Num a => a -> a -> a
(*) (forall a. [a] -> [a]
cycle [Int
1,Int
3]) (forall a. [a] -> [a]
reverse [Int]
ws)) Int
10

remainder :: [Int] -> Int
remainder :: [Int] -> Int
remainder [Int]
ws =
   let (Int
x,Int
y) = forall a. Num a => [a] -> (a, a)
sum2 [Int]
ws
   in  forall a. Integral a => a -> a -> a
mod (Int
3forall a. Num a => a -> a -> a
*Int
xforall a. Num a => a -> a -> a
+Int
y) Int
10

sum2 :: (Num a) => [a] -> (a,a)
sum2 :: forall a. Num a => [a] -> (a, a)
sum2 = forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
List.foldl' (\(a
a,a
b) a
x -> forall a b. a -> b -> (a, b)
strictPair a
b (a
aforall a. Num a => a -> a -> a
+a
x)) (a
0,a
0)

strictPair :: a -> b -> (a,b)
strictPair :: forall a b. a -> b -> (a, b)
strictPair a
a b
b = ((,) forall a b. (a -> b) -> a -> b
$! a
a) forall a b. (a -> b) -> a -> b
$! b
b