module Prep where

import Data.Bifunctor (Bifunctor (bimap))
import Data.Bool (bool)

separate :: [Int] -> ([Int], [Int])
separate [] = ([], [])
separate [x] = ([x], [])
separate (x1 : x2 : xs) =
  let (next1, next2) = separate xs
   in (x1 : next1, x2 : next2)

-- >>> separate [1..5]
-- ([1,3,5],[2,4])

numToStr :: Int -> Int -> String
numToStr n radix
  | n < radix = [chars !! n]
  | otherwise = numToStr (n `div` radix) radix ++ [chars !! (n `mod` radix)]
  where
    chars = "0123456789ABCDEF"

-- >>> numToStr 52 10
-- >>> numToStr 5 2
-- >>> numToStr 255 16
-- "52"
-- "101"
-- "FF"

split :: Int -> [a] -> [[a]]
split n xs =
  let prefix = take n xs
      suffix = drop n xs
   in if length prefix < n || null suffix
        then [prefix]
        else prefix : split n suffix

-- >>> split 3 [1..10]
-- >>> split 3 [1, 2]
-- >>> split 3 [1, 2, 3]
-- [[1,2,3],[4,5,6],[7,8,9],[10]]
-- [[1,2]]
-- [[1,2,3]]

averageN :: Int -> [Int] -> [Float]
averageN n xs = [fromIntegral s / fromIntegral l | x <- split n xs, let s = sum x, let l = length x]

-- >>> averageN 3 [-1, 0, 1, 2, 3]
-- [0.0,2.5]

-- TASKS (but good luck... :dennis:)

copy :: Int -> String -> String
copy 0 _ = ""
copy n s = s ++ copy (n - 1) s

-- >>> copy 3 "abc"
-- "abcabcabc"

luhnDouble :: Int -> Int
luhnDouble = ((> 9) >>= bool id (subtract 9)) . (2 *)

-- >>> luhnDouble <$> [0..9]
-- [0,2,4,6,8,1,3,5,7,9]

luhn :: [Int] -> Bool
luhn = (== 0) . (`mod` 10) . uncurry (+) . bimap sum sum . fmap (fmap luhnDouble) . separate . reverse

-- >>> luhn [1,7,8,4]
-- >>> luhn [4,7,8,3]
-- True
-- False
