content: module Data.Index
    ( ZeroBased,
      first,
      second,
      third,
      next,
      toMachine,
      toHuman,
      indexedMap,
      indexedTraverse,
      indexedForA,
      VerifiedList (..),
      indexedZipWith,
      indexedZipWithA,
    )
  where
  
  import Control.Monad (liftM)
  import Data.Binary
  
  -- ZERO BASED
  
  newtype ZeroBased = ZeroBased Int
    deriving (Eq, Ord, Show)
  
  first :: ZeroBased
  first =
    ZeroBased 0
  
  second :: ZeroBased
  second =
    ZeroBased 1
  
  third :: ZeroBased
  third =
    ZeroBased 2
  
  next :: ZeroBased -> ZeroBased
  next (ZeroBased i) =
    ZeroBased (i + 1)
  
  -- DESTRUCT
  
  toMachine :: ZeroBased -> Int
  toMachine (ZeroBased index) =
    index
  
  toHuman :: ZeroBased -> Int
  toHuman (ZeroBased index) =
    index + 1
  
  -- INDEXED MAP
  
  indexedMap :: (ZeroBased -> a -> b) -> [a] -> [b]
  indexedMap func xs =
    zipWith func (map ZeroBased [0 .. length xs]) xs
  
  indexedTraverse :: (Applicative f) => (ZeroBased -> a -> f b) -> [a] -> f [b]
  indexedTraverse func xs =
    sequenceA (indexedMap func xs)
  
  indexedForA :: (Applicative f) => [a] -> (ZeroBased -> a -> f b) -> f [b]
  indexedForA xs func =
    sequenceA (indexedMap func xs)
  
  -- VERIFIED/INDEXED ZIP
  
  data VerifiedList a
    = LengthMatch [a]
    | LengthMismatch Int Int
  
  indexedZipWith :: (ZeroBased -> a -> b -> c) -> [a] -> [b] -> VerifiedList c
  indexedZipWith func listX listY =
    indexedZipWithHelp func 0 listX listY []
  
  indexedZipWithHelp :: (ZeroBased -> a -> b -> c) -> Int -> [a] -> [b] -> [c] -> VerifiedList c
  indexedZipWithHelp func index listX listY revListZ =
    case (listX, listY) of
      ([], []) ->
        LengthMatch (reverse revListZ)
      (x : xs, y : ys) ->
        indexedZipWithHelp func (index + 1) xs ys $
          func (ZeroBased index) x y : revListZ
      (_, _) ->
        LengthMismatch (index + length listX) (index + length listY)
  
  indexedZipWithA :: (Applicative f) => (ZeroBased -> a -> b -> f c) -> [a] -> [b] -> f (VerifiedList c)
  indexedZipWithA func listX listY =
    case indexedZipWith func listX listY of
      LengthMatch xs ->
        LengthMatch <$> sequenceA xs
      LengthMismatch x y ->
        pure (LengthMismatch x y)
  
  -- BINARY
  
  instance Binary ZeroBased where
    get = liftM ZeroBased get
    put (ZeroBased n) = put n
  