content: ------------------------------------------------------------------------
  -- |
  -- Module           : Data.Parameterized.TraversableFC.WithIndex
  -- Copyright        : (c) Galois, Inc 2021
  -- Maintainer       : Langston Barrett
  -- Description      : 'TraversableFC' classes, but with indices.
  --
  -- As in the package indexed-traversable.
  ------------------------------------------------------------------------
  {-# LANGUAGE PolyKinds #-}
  {-# LANGUAGE RankNTypes #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE TypeOperators #-}
  
  module Data.Parameterized.TraversableFC.WithIndex
    ( FunctorFCWithIndex(..)
    , FoldableFCWithIndex(..)
    , ifoldlMFC
    , ifoldrMFC
    , iallFC
    , ianyFC
    , TraversableFCWithIndex(..)
    , imapFCDefault
    , ifoldMapFCDefault
    ) where
  
  import Data.Functor.Const (Const(Const, getConst))
  import Data.Functor.Identity (Identity(Identity, runIdentity))
  import Data.Kind
  import Data.Monoid (All(..), Any(..), Endo(Endo), appEndo, Dual(Dual, getDual))
  import Data.Profunctor.Unsafe ((#.))
  import GHC.Exts (build)
  
  import Data.Parameterized.Classes
  import Data.Parameterized.TraversableFC
  
  class FunctorFC t => FunctorFCWithIndex (t :: (k -> Type) -> l -> Type) where
    -- | Like 'fmapFC', but with an index.
    --
    -- @
    -- 'fmapFC' f ≡ 'imapFC' ('const' f)
    -- @
    imapFC ::
      forall f g z.
      (forall x. IndexF (t f z) x -> f x -> g x)
      -> t f z
      -> t g z
  
  ------------------------------------------------------------------------
  
  class (FoldableFC t, FunctorFCWithIndex t) => FoldableFCWithIndex (t :: (k -> Type) -> l -> Type) where
  
    -- | Like 'foldMapFC', but with an index.
    --
    -- @
    -- 'foldMapFC' f ≡ 'ifoldMapFC' ('const' f)
    -- @
    ifoldMapFC ::
      forall f m z.
      Monoid m =>
      (forall x. IndexF (t f z) x -> f x -> m) ->
      t f z ->
      m
    ifoldMapFC f = ifoldrFC (\i x -> mappend (f i x)) mempty
  
    -- | Like 'foldrFC', but with an index.
    ifoldrFC ::
      forall z f b.
      (forall x. IndexF (t f z) x -> f x -> b -> b) ->
      b ->
      t f z ->
      b
    ifoldrFC f z t = appEndo (ifoldMapFC (\i x -> Endo (f i x)) t) z
  
    -- | Like 'foldlFC', but with an index.
    ifoldlFC ::
      forall f b z.
      (forall x. IndexF (t f z) x -> b -> f x -> b) ->
      b ->
      t f z ->
      b
    ifoldlFC f z t =
      appEndo (getDual (ifoldMapFC (\i e -> Dual (Endo (\r -> f i r e))) t)) z
  
    -- | Like 'ifoldrFC', but with an index.
    ifoldrFC' ::
      forall f b z.
      (forall x. IndexF (t f z) x -> f x -> b -> b) ->
      b ->
      t f z ->
      b
    ifoldrFC' f0 z0 xs = ifoldlFC (f' f0) id xs z0
      where f' f i k x z = k $! f i x z
  
    -- | Like 'ifoldlFC', but with an index.
    ifoldlFC' :: forall f b. (forall x. b -> f x -> b) -> (forall x. b -> t f x -> b)
    ifoldlFC' f0 z0 xs = foldrFC (f' f0) id xs z0
      where f' f x k z = k $! f z x
  
    -- | Convert structure to list.
    itoListFC ::
      forall f a z.
      (forall x. IndexF (t f z) x -> f x -> a) ->
      t f z ->
      [a]
    itoListFC f t = build (\c n -> ifoldrFC (\i e v -> c (f i e) v) n t)
  
  -- | Like 'foldlMFC', but with an index.
  ifoldlMFC ::
    FoldableFCWithIndex t =>
    Monad m =>
    (forall x. IndexF (t f z) x -> b -> f x -> m b) ->
    b ->
    t f z ->
    m b
  ifoldlMFC f z0 xs = ifoldlFC (\i k x z -> f i z x >>= k) return xs z0
  
  -- | Like 'foldrMFC', but with an index.
  ifoldrMFC ::
    FoldableFCWithIndex t =>
    Monad m =>
    (forall x. IndexF (t f z) x -> f x -> b -> m b) ->
    b ->
    t f z ->
    m b
  ifoldrMFC f z0 xs = ifoldlFC (\i k x z -> f i x z >>= k) return xs z0
  
  -- | Like 'allFC', but with an index.
  iallFC ::
    FoldableFCWithIndex t =>
    (forall x. IndexF (t f z) x -> f x -> Bool) ->
    t f z ->
    Bool
  iallFC p = getAll #. ifoldMapFC (\i x -> All (p i x))
  
  -- | Like 'anyFC', but with an index.
  ianyFC ::
    FoldableFCWithIndex t =>
    (forall x. IndexF (t f z) x -> f x -> Bool) ->
    t f z -> Bool
  ianyFC p = getAny #. ifoldMapFC (\i x -> Any (p i x))
  
  ------------------------------------------------------------------------
  
  class (TraversableFC t, FoldableFCWithIndex t) => TraversableFCWithIndex (t :: (k -> Type) -> l -> Type) where
    -- | Like 'traverseFC', but with an index.
    --
    -- @
    -- 'traverseFC' f ≡ 'itraverseFC' ('const' f)
    -- @
    itraverseFC ::
      forall m z f g.
      Applicative m =>
      (forall x. IndexF (t f z) x -> f x -> m (g x)) ->
      t f z ->
      m (t g z)
  
  imapFCDefault ::
    forall t f g z.
    TraversableFCWithIndex t =>
    (forall x. IndexF (t f z) x -> f x -> g x)
    -> t f z
    -> t g z
  imapFCDefault f = runIdentity #. itraverseFC (\i x -> Identity (f i x))
  {-# INLINEABLE imapFCDefault #-}
  
  ifoldMapFCDefault ::
    forall t m z f.
    TraversableFCWithIndex t =>
    Monoid m =>
    (forall x. IndexF (t f z) x -> f x -> m) ->
    t f z ->
    m
  ifoldMapFCDefault f = getConst #. itraverseFC (\i x -> Const (f i x))
  {-# INLINEABLE ifoldMapFCDefault #-}
  