{-# LANGUAGE NumericUnderscores #-} import Data.List (intercalate) import Text.Printf (printf) import Data.List.Split (chunksOf) isEban :: Int -> Bool isEban n = all (`elem` [0, 2, 4, 6]) z where (b, r1) = n `quotRem` (10 ^ 9) (m, r2) = r1 `quotRem` (10 ^ 6) (t, r3) = r2 `quotRem` (10 ^ 3) z = b : map (\x -> if x >= 30 && x <= 66 then x `mod` 10 else x) [m, t, r3] ebans = map f where f x = (thousands x, thousands $ length $ filter isEban [1..x]) thousands:: Int -> String thousands = reverse . intercalate "," . chunksOf 3 . reverse . show main :: IO () main = do uncurry (printf "eban numbers up to and including 1000: %2s\n%s\n\n") $ r [1..1000] uncurry (printf "eban numbers between 1000 and 4000: %2s\n%s\n\n") $ r [1000..4000] mapM_ (uncurry (printf "eban numbers up and including %13s: %5s\n")) ebanCounts where ebanCounts = ebans [ 10_000 , 100_000 , 1_000_000 , 10_000_000 , 100_000_000 , 1_000_000_000 ] r = ((,) <$> thousands . length <*> show) . filter isEban