module Fold
  (
    {- * Fold types -} Fold (Fold), NonemptyFold (NonemptyFold),
            EffectfulFold (EffectfulFold), ShortcutFold (ShortcutFold),
            ShortcutNonemptyFold (ShortcutNonemptyFold), Vitality (..), Will (..),
    {- * Running -} runFold, runNonemptyFold, runEffectfulFold,
    {- * Search -} element, notElement, find, lookup,
    {- * Arithmetic folds -} sum, product, mean, variance, standardDeviation,
    {- * Working with indices -} index, findIndex, elementIndex,
    {- * Counting inputs -} null, length,
    {- * Boolean folds -} and, or, all, any,
    {- * Min/max -} maximum, minimum, maximumBy, minimumBy,
    {- * First/last -} first, last,
    {- * General folds -} magma, semigroup, monoid, effect, effectMonoid,
    {- * List folds -} list, reverseList, nonemptyList, reverseNonemptyList,
    {- * Fold conversions -} emptyToNonempty, nonemptyToEmpty, pureToEffectful,
            effectfulToPure, nonemptyToEffectful, effectfulToNonempty,
    {- * Hoist -} hoist,
    {- * Duplicate -} duplicateFold, duplicateNonemptyFold, duplicateEffectfulFold,
  )
  where

import Fold.Effectful.Type
import Fold.Nonempty.Type
import Fold.Pure.Type
import Fold.Shortcut.Type
import Fold.ShortcutNonempty.Type

import Fold.Effectful.Examples.Interesting
import Fold.Nonempty.Examples.Interesting hiding (list, reverseList, sum, product)
import Fold.Pure.Examples.Interesting
import Fold.Shortcut.Examples.Interesting
import Fold.ShortcutNonempty.Examples.Interesting

import Fold.Effectful.Utilities

import qualified Fold.Effectful.Conversion as ConvertTo.Effectful
import qualified Fold.Nonempty.Conversion as ConvertTo.Nonempty
import qualified Fold.Pure.Conversion as ConvertTo.Pure

import qualified Fold.Effectful as Effectful
import qualified Fold.Nonempty as Nonempty
import qualified Fold.Pure as Pure

import Control.Applicative (Applicative)
import Control.Monad (Monad)
import Data.Foldable (Foldable)
import Data.Functor.Identity (Identity)
import Data.List.NonEmpty (NonEmpty)
import Data.Maybe (Maybe)

{-| Fold a listlike container to a single summary result -}
runFold :: Foldable f => Fold a b -> f a -> b
runFold :: forall (f :: * -> *) a b. Foldable f => Fold a b -> f a -> b
runFold = forall (f :: * -> *) a b. Foldable f => Fold a b -> f a -> b
Pure.run

{-| Fold a nonempty listlike container to a single summary result -}
runNonemptyFold :: NonemptyFold a b -> NonEmpty a -> b
runNonemptyFold :: forall a b. NonemptyFold a b -> NonEmpty a -> b
runNonemptyFold = forall a b. NonemptyFold a b -> NonEmpty a -> b
Nonempty.run

{-| Fold an listlike container to an action that produces a single summary
result -}
runEffectfulFold :: Foldable f => Monad m => EffectfulFold m a b -> f a -> m b
runEffectfulFold :: forall (f :: * -> *) (m :: * -> *) a b.
(Foldable f, Monad m) =>
EffectfulFold m a b -> f a -> m b
runEffectfulFold = forall (f :: * -> *) (m :: * -> *) a b.
(Foldable f, Monad m) =>
EffectfulFold m a b -> f a -> m b
Effectful.run

{-| Turn a regular fold that allows empty input into a fold that
requires at least one input -}
emptyToNonempty :: Fold a b -> NonemptyFold a b
emptyToNonempty :: forall a b. Fold a b -> NonemptyFold a b
emptyToNonempty = forall a b. Fold a b -> NonemptyFold a b
ConvertTo.Nonempty.fold

{-| Turn an effectful fold into a pure fold that requires at least
one input -}
effectfulToNonempty :: EffectfulFold Identity a b -> NonemptyFold a b
effectfulToNonempty :: forall a b. EffectfulFold Identity a b -> NonemptyFold a b
effectfulToNonempty = forall a b. EffectfulFold Identity a b -> NonemptyFold a b
ConvertTo.Nonempty.effectfulFold

{-| Turn a fold that requires at least one input into a fold that returns
    'Data.Maybe.Nothing' when there are no inputs -}
nonemptyToEmpty :: NonemptyFold a b -> Fold a (Maybe b)
nonemptyToEmpty :: forall a b. NonemptyFold a b -> Fold a (Maybe b)
nonemptyToEmpty = forall a b. NonemptyFold a b -> Fold a (Maybe b)
ConvertTo.Pure.nonemptyFold

{-| Generalize a pure fold to an effectful fold -}
pureToEffectful :: Monad m => Fold a b -> EffectfulFold m a b
pureToEffectful :: forall (m :: * -> *) a b.
Monad m =>
Fold a b -> EffectfulFold m a b
pureToEffectful = forall (m :: * -> *) a b.
Monad m =>
Fold a b -> EffectfulFold m a b
ConvertTo.Effectful.fold

{-| Turn an effectful fold into a pure fold -}
effectfulToPure :: EffectfulFold Identity a b -> Fold a b
effectfulToPure :: forall a b. EffectfulFold Identity a b -> Fold a b
effectfulToPure = forall a b. EffectfulFold Identity a b -> Fold a b
ConvertTo.Pure.effectfulFold

{-| Turn a nonempty fold that requires at least one input into a fold that
    returns 'Data.Maybe.Nothing' when there are no inputs -}
nonemptyToEffectful :: Monad m =>
    NonemptyFold a b -> EffectfulFold m a (Maybe b)
nonemptyToEffectful :: forall (m :: * -> *) a b.
Monad m =>
NonemptyFold a b -> EffectfulFold m a (Maybe b)
nonemptyToEffectful = forall (m :: * -> *) a b.
Monad m =>
NonemptyFold a b -> EffectfulFold m a (Maybe b)
ConvertTo.Effectful.nonemptyFold

{-| All the inputs from a nonempty fold -}
nonemptyList :: NonemptyFold a (NonEmpty a)
nonemptyList :: forall a. NonemptyFold a (NonEmpty a)
nonemptyList = forall a. NonemptyFold a (NonEmpty a)
Nonempty.list

{-| All the inputs from a nonempty fold, in reverse order -}
reverseNonemptyList :: NonemptyFold a (NonEmpty a)
reverseNonemptyList :: forall a. NonemptyFold a (NonEmpty a)
reverseNonemptyList = forall a. NonemptyFold a (NonEmpty a)
Nonempty.reverseList

{-| Allows to continue feeding a fold even after passing it to a function
    that closes it -}
duplicateFold :: Fold a b -> Fold a (Fold a b)
duplicateFold :: forall a b. Fold a b -> Fold a (Fold a b)
duplicateFold = forall a b. Fold a b -> Fold a (Fold a b)
Pure.duplicate

{-| Allows to continue feeding a fold even after passing it to a function
    that closes it -}
duplicateNonemptyFold :: NonemptyFold a b -> NonemptyFold a (Fold a b)
duplicateNonemptyFold :: forall a b. NonemptyFold a b -> NonemptyFold a (Fold a b)
duplicateNonemptyFold = forall a b. NonemptyFold a b -> NonemptyFold a (Fold a b)
Nonempty.duplicate

{-| Allows to continue feeding an effectful fold even after passing it to a
    function that closes it -}
duplicateEffectfulFold :: Applicative m => EffectfulFold m a b -> EffectfulFold m a (EffectfulFold m a b)
duplicateEffectfulFold :: forall (m :: * -> *) a b.
Applicative m =>
EffectfulFold m a b -> EffectfulFold m a (EffectfulFold m a b)
duplicateEffectfulFold = forall (m :: * -> *) a b.
Applicative m =>
EffectfulFold m a b -> EffectfulFold m a (EffectfulFold m a b)
Effectful.duplicate