{-# OPTIONS_GHC -Wno-deprecations #-}

-- |
-- Module      : Streamly.Internal.Data.Stream.Enumeration
-- Copyright   : (c) 2018 Composewell Technologies
--
-- License     : BSD3
-- Maintainer  : streamly@composewell.com
-- Stability   : experimental
-- Portability : GHC
--
-- The functions defined in this module should be rarely needed for direct use,
-- try to use the operations from the 'Enumerable' type class
-- instances instead.
--
-- This module provides an 'Enumerable' type class to enumerate 'Enum' types
-- into a stream. The operations in this type class correspond to similar
-- perations in the 'Enum' type class, the only difference is that they produce
-- a stream instead of a list. These operations cannot be defined generically
-- based on the 'Enum' type class. We provide instances for commonly used
-- types. If instances for other types are needed convenience functions defined
-- in this module can be used to define them. Alternatively, these functions
-- can be used directly.

module Streamly.Internal.Data.Stream.IsStream.Enumeration {-# DEPRECATED "Please use \"Streamly.Data.Stream.*\" instead." #-}
    (
      Enumerable (..)

    -- ** Enumerating 'Bounded' 'Enum' Types
    , enumerate
    , enumerateTo
    , enumerateFromBounded

    -- ** Enumerating 'Enum' Types not larger than 'Int'
    , enumerateFromToSmall
    , enumerateFromThenToSmall
    , enumerateFromThenSmallBounded

    -- ** Enumerating 'Bounded' 'Integral' Types
    , enumerateFromIntegral
    , enumerateFromThenIntegral

    -- ** Enumerating 'Integral' Types
    , enumerateFromToIntegral
    , enumerateFromThenToIntegral

    -- ** Enumerating unbounded 'Integral' Types
    , enumerateFromStepIntegral

    -- ** Enumerating 'Fractional' Types
    , enumerateFromFractional
    , enumerateFromToFractional
    , enumerateFromThenFractional
    , enumerateFromThenToFractional
    )
where

import Data.Fixed
import Data.Int
import Data.Ratio
import Data.Word
import Numeric.Natural
import Data.Functor.Identity (Identity(..))

import Streamly.Internal.Data.Stream.IsStream.Type (IsStream(..), fromStreamD)

import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
import qualified Streamly.Internal.Data.Stream as D
    ( enumerateFromNum
    , enumerateFromThenNum
    , enumerateFromThenToIntegral
    , enumerateFromThenIntegral
    , enumerateFromStepIntegral
    , enumerateFromToIntegral
    , enumerateFromIntegral
    , enumerateFromToFractional
    , enumerateFromThenToFractional
    )
import qualified Streamly.Internal.Data.Stream.Serial as Serial (map)

-- $setup
-- >>> :set -fno-warn-deprecations
-- >>> import Streamly.Prelude as Stream
-- >>> import Streamly.Internal.Data.Stream.IsStream.Enumeration as Stream

-------------------------------------------------------------------------------
-- Enumeration of Integral types
-------------------------------------------------------------------------------
--
-- | @enumerateFromStepIntegral from step@ generates an infinite stream whose
-- first element is @from@ and the successive elements are in increments of
-- @step@.
--
-- CAUTION: This function is not safe for finite integral types. It does not
-- check for overflow, underflow or bounds.
--
-- @
-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2
-- [0,2,4,6]
--
-- >>> Stream.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)
-- [0,-2,-4]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromStepIntegral #-}
enumerateFromStepIntegral
    :: (IsStream t, Monad m, Integral a)
    => a -> a -> t m a
enumerateFromStepIntegral :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Integral a) =>
a -> a -> t m a
enumerateFromStepIntegral a
from a
stride =
    Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> a -> Stream m a
forall a (m :: * -> *).
(Integral a, Monad m) =>
a -> a -> Stream m a
D.enumerateFromStepIntegral a
from a
stride

-- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a
-- stream whose first element is @from@ and the successive elements are in
-- increments of @1@. The stream is bounded by the size of the 'Integral' type.
--
-- @
-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)
-- [0,1,2,3]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromIntegral #-}
enumerateFromIntegral
    :: (IsStream t, Monad m, Integral a, Bounded a)
    => a -> t m a
enumerateFromIntegral :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Integral a, Bounded a) =>
a -> t m a
enumerateFromIntegral a
from = Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> Stream m a
forall (m :: * -> *) a.
(Monad m, Integral a, Bounded a) =>
a -> Stream m a
D.enumerateFromIntegral a
from

-- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from
-- then@ generates a stream whose first element is @from@, the second element
-- is @then@ and the successive elements are in increments of @then - from@.
-- The stream is bounded by the size of the 'Integral' type.
--
-- @
-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2
-- [0,2,4,6]
--
-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)
-- [0,-2,-4,-6]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromThenIntegral #-}
enumerateFromThenIntegral
    :: (IsStream t, Monad m, Integral a, Bounded a)
    => a -> a -> t m a
enumerateFromThenIntegral :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Integral a, Bounded a) =>
a -> a -> t m a
enumerateFromThenIntegral a
from a
next =
    Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> a -> Stream m a
forall (m :: * -> *) a.
(Monad m, Integral a, Bounded a) =>
a -> a -> Stream m a
D.enumerateFromThenIntegral a
from a
next

-- | Enumerate an 'Integral' type up to a given limit.
-- @enumerateFromToIntegral from to@ generates a finite stream whose first
-- element is @from@ and successive elements are in increments of @1@ up to
-- @to@.
--
-- @
-- >>> Stream.toList $ Stream.enumerateFromToIntegral 0 4
-- [0,1,2,3,4]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromToIntegral #-}
enumerateFromToIntegral :: (IsStream t, Monad m, Integral a) => a -> a -> t m a
enumerateFromToIntegral :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Integral a) =>
a -> a -> t m a
enumerateFromToIntegral a
from a
to =
    Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> a -> Stream m a
forall (m :: * -> *) a.
(Monad m, Integral a) =>
a -> a -> Stream m a
D.enumerateFromToIntegral a
from a
to

-- | Enumerate an 'Integral' type in steps up to a given limit.
-- @enumerateFromThenToIntegral from then to@ generates a finite stream whose
-- first element is @from@, the second element is @then@ and the successive
-- elements are in increments of @then - from@ up to @to@.
--
-- @
-- >>> Stream.toList $ Stream.enumerateFromThenToIntegral 0 2 6
-- [0,2,4,6]
--
-- >>> Stream.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)
-- [0,-2,-4,-6]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromThenToIntegral #-}
enumerateFromThenToIntegral
    :: (IsStream t, Monad m, Integral a)
    => a -> a -> a -> t m a
enumerateFromThenToIntegral :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Integral a) =>
a -> a -> a -> t m a
enumerateFromThenToIntegral a
from a
next a
to =
    Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> a -> a -> Stream m a
forall (m :: * -> *) a.
(Monad m, Integral a) =>
a -> a -> a -> Stream m a
D.enumerateFromThenToIntegral a
from a
next a
to

-------------------------------------------------------------------------------
-- Enumeration of Fractional types
-------------------------------------------------------------------------------
--
-- Even though the underlying implementation of enumerateFromFractional and
-- enumerateFromThenFractional works for any 'Num' we have restricted these to
-- 'Fractional' because these do not perform any bounds check, in contrast to
-- integral versions and are therefore not equivalent substitutes for those.
--
-- | Numerically stable enumeration from a 'Fractional' number in steps of size
-- @1@. @enumerateFromFractional from@ generates a stream whose first element
-- is @from@ and the successive elements are in increments of @1@.  No overflow
-- or underflow checks are performed.
--
-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:
--
-- @
-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1
-- [1.1,2.1,3.1,4.1]
--
-- @
--
--
-- @since 0.6.0
{-# INLINE enumerateFromFractional #-}
enumerateFromFractional :: (IsStream t, Monad m, Fractional a) => a -> t m a
enumerateFromFractional :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Fractional a) =>
a -> t m a
enumerateFromFractional a
from = Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> Stream m a
forall (m :: * -> *) a. (Monad m, Num a) => a -> Stream m a
D.enumerateFromNum a
from

-- | Numerically stable enumeration from a 'Fractional' number in steps.
-- @enumerateFromThenFractional from then@ generates a stream whose first
-- element is @from@, the second element is @then@ and the successive elements
-- are in increments of @then - from@.  No overflow or underflow checks are
-- performed.
--
-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)
-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromThenFractional #-}
enumerateFromThenFractional
    :: (IsStream t, Monad m, Fractional a)
    => a -> a -> t m a
enumerateFromThenFractional :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Fractional a) =>
a -> a -> t m a
enumerateFromThenFractional a
from a
next = Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> a -> Stream m a
forall (m :: * -> *) a. (Monad m, Num a) => a -> a -> Stream m a
D.enumerateFromThenNum a
from a
next

-- | Numerically stable enumeration from a 'Fractional' number to a given
-- limit.  @enumerateFromToFractional from to@ generates a finite stream whose
-- first element is @from@ and successive elements are in increments of @1@ up
-- to @to@.
--
-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.toList $ Stream.enumerateFromToFractional 1.1 4
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.toList $ Stream.enumerateFromToFractional 1.1 4.6
-- [1.1,2.1,3.1,4.1,5.1]
--
-- @
--
-- Notice that the last element is equal to the specified @to@ value after
-- rounding to the nearest integer.
--
-- @since 0.6.0
{-# INLINE enumerateFromToFractional #-}
enumerateFromToFractional
    :: (IsStream t, Monad m, Fractional a, Ord a)
    => a -> a -> t m a
enumerateFromToFractional :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Fractional a, Ord a) =>
a -> a -> t m a
enumerateFromToFractional a
from a
to =
    Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> a -> Stream m a
forall (m :: * -> *) a.
(Monad m, Fractional a, Ord a) =>
a -> a -> Stream m a
D.enumerateFromToFractional a
from a
to

-- | Numerically stable enumeration from a 'Fractional' number in steps up to a
-- given limit.  @enumerateFromThenToFractional from then to@ generates a
-- finite stream whose first element is @from@, the second element is @then@
-- and the successive elements are in increments of @then - from@ up to @to@.
--
-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.toList $ Stream.enumerateFromThenToFractional 0.1 2 6
-- [0.1,2.0,3.9,5.799999999999999]
--
-- >>> Stream.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)
-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]
--
-- @
--
--
-- @since 0.6.0
{-# INLINE enumerateFromThenToFractional #-}
enumerateFromThenToFractional
    :: (IsStream t, Monad m, Fractional a, Ord a)
    => a -> a -> a -> t m a
enumerateFromThenToFractional :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Fractional a, Ord a) =>
a -> a -> a -> t m a
enumerateFromThenToFractional a
from a
next a
to =
    Stream m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m) =>
Stream m a -> t m a
fromStreamD (Stream m a -> t m a) -> Stream m a -> t m a
forall a b. (a -> b) -> a -> b
$ a -> a -> a -> Stream m a
forall (m :: * -> *) a.
(Monad m, Fractional a, Ord a) =>
a -> a -> a -> Stream m a
D.enumerateFromThenToFractional a
from a
next a
to

-------------------------------------------------------------------------------
-- Enumeration of Enum types not larger than Int
-------------------------------------------------------------------------------
--
-- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'.
--
-- @since 0.6.0
{-# INLINE enumerateFromToSmall #-}
enumerateFromToSmall :: (IsStream t, Monad m, Enum a) => a -> a -> t m a
enumerateFromToSmall :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Enum a) =>
a -> a -> t m a
enumerateFromToSmall a
from a
to =
    SerialT m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
IsStream t =>
SerialT m a -> t m a
IsStream.fromSerial
        (SerialT m a -> t m a) -> SerialT m a -> t m a
forall a b. (a -> b) -> a -> b
$ (Int -> a) -> SerialT m Int -> SerialT m a
forall (m :: * -> *) a b.
Monad m =>
(a -> b) -> SerialT m a -> SerialT m b
Serial.map Int -> a
forall a. Enum a => Int -> a
toEnum
        (SerialT m Int -> SerialT m a) -> SerialT m Int -> SerialT m a
forall a b. (a -> b) -> a -> b
$ Int -> Int -> SerialT m Int
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Integral a) =>
a -> a -> t m a
enumerateFromToIntegral (a -> Int
forall a. Enum a => a -> Int
fromEnum a
from) (a -> Int
forall a. Enum a => a -> Int
fromEnum a
to)

-- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'.
--
-- @since 0.6.0
{-# INLINE enumerateFromThenToSmall #-}
enumerateFromThenToSmall :: (IsStream t, Monad m, Enum a)
    => a -> a -> a -> t m a
enumerateFromThenToSmall :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Enum a) =>
a -> a -> a -> t m a
enumerateFromThenToSmall a
from a
next a
to =
    SerialT m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
IsStream t =>
SerialT m a -> t m a
IsStream.fromSerial
        (SerialT m a -> t m a) -> SerialT m a -> t m a
forall a b. (a -> b) -> a -> b
$ (Int -> a) -> SerialT m Int -> SerialT m a
forall (m :: * -> *) a b.
Monad m =>
(a -> b) -> SerialT m a -> SerialT m b
Serial.map Int -> a
forall a. Enum a => Int -> a
toEnum
        (SerialT m Int -> SerialT m a) -> SerialT m Int -> SerialT m a
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int -> SerialT m Int
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Integral a) =>
a -> a -> a -> t m a
enumerateFromThenToIntegral
            (a -> Int
forall a. Enum a => a -> Int
fromEnum a
from) (a -> Int
forall a. Enum a => a -> Int
fromEnum a
next) (a -> Int
forall a. Enum a => a -> Int
fromEnum a
to)

-- | 'enumerateFromThen' for 'Enum' types not larger than 'Int'.
--
-- Note: We convert the 'Enum' to 'Int' and enumerate the 'Int'. If a
-- type is bounded but does not have a 'Bounded' instance then we can go on
-- enumerating it beyond the legal values of the type, resulting in the failure
-- of 'toEnum' when converting back to 'Enum'. Therefore we require a 'Bounded'
-- instance for this function to be safely used.
--
-- @since 0.6.0
{-# INLINE enumerateFromThenSmallBounded #-}
enumerateFromThenSmallBounded :: (IsStream t, Monad m, Enumerable a, Bounded a)
    => a -> a -> t m a
enumerateFromThenSmallBounded :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Enumerable a, Bounded a) =>
a -> a -> t m a
enumerateFromThenSmallBounded a
from a
next =
    if a -> Int
forall a. Enum a => a -> Int
fromEnum a
next Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= a -> Int
forall a. Enum a => a -> Int
fromEnum a
from
    then a -> a -> a -> t m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> a -> a -> t m a
enumerateFromThenTo a
from a
next a
forall a. Bounded a => a
maxBound
    else a -> a -> a -> t m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> a -> a -> t m a
enumerateFromThenTo a
from a
next a
forall a. Bounded a => a
minBound

-------------------------------------------------------------------------------
-- Enumerable type class
-------------------------------------------------------------------------------
--
-- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream
-- enumerations like this:
--
-- {-# RULES "fromList enumFrom" [1]
--     forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}
--
-- But this does not work because enumFrom is a class method and GHC rewrites
-- it quickly, so we do not get a chance to have our rule fired.

-- | Types that can be enumerated as a stream. The operations in this type
-- class are equivalent to those in the 'Enum' type class, except that these
-- generate a stream instead of a list. Use the functions in
-- "Streamly.Internal.Data.Stream.Enumeration" module to define new instances.
--
-- @since 0.6.0
class Enum a => Enumerable a where
    -- | @enumerateFrom from@ generates a stream starting with the element
    -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or
    -- generating an infinite stream when the type is not 'Bounded'.
    --
    -- @
    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)
    -- [0,1,2,3]
    --
    -- @
    --
    -- For 'Fractional' types, enumeration is numerically stable. However, no
    -- overflow or underflow checks are performed.
    --
    -- @
    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1
    -- [1.1,2.1,3.1,4.1]
    --
    -- @
    --
    -- @since 0.6.0
    enumerateFrom :: (IsStream t, Monad m) => a -> t m a

    -- | Generate a finite stream starting with the element @from@, enumerating
    -- the type up to the value @to@. If @to@ is smaller than @from@ then an
    -- empty stream is returned.
    --
    -- @
    -- >>> Stream.toList $ Stream.enumerateFromTo 0 4
    -- [0,1,2,3,4]
    --
    -- @
    --
    -- For 'Fractional' types, the last element is equal to the specified @to@
    -- value after rounding to the nearest integral value.
    --
    -- @
    -- >>> Stream.toList $ Stream.enumerateFromTo 1.1 4
    -- [1.1,2.1,3.1,4.1]
    --
    -- >>> Stream.toList $ Stream.enumerateFromTo 1.1 4.6
    -- [1.1,2.1,3.1,4.1,5.1]
    --
    -- @
    --
    -- @since 0.6.0
    enumerateFromTo :: (IsStream t, Monad m) => a -> a -> t m a

    -- | @enumerateFromThen from then@ generates a stream whose first element
    -- is @from@, the second element is @then@ and the successive elements are
    -- in increments of @then - from@.  Enumeration can occur downwards or
    -- upwards depending on whether @then@ comes before or after @from@. For
    -- 'Bounded' types the stream ends when 'maxBound' is reached, for
    -- unbounded types it keeps enumerating infinitely.
    --
    -- @
    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2
    -- [0,2,4,6]
    --
    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)
    -- [0,-2,-4,-6]
    --
    -- @
    --
    -- @since 0.6.0
    enumerateFromThen :: (IsStream t, Monad m) => a -> a -> t m a

    -- | @enumerateFromThenTo from then to@ generates a finite stream whose
    -- first element is @from@, the second element is @then@ and the successive
    -- elements are in increments of @then - from@ up to @to@. Enumeration can
    -- occur downwards or upwards depending on whether @then@ comes before or
    -- after @from@.
    --
    -- @
    -- >>> Stream.toList $ Stream.enumerateFromThenTo 0 2 6
    -- [0,2,4,6]
    --
    -- >>> Stream.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)
    -- [0,-2,-4,-6]
    --
    -- @
    --
    -- @since 0.6.0
    enumerateFromThenTo :: (IsStream t, Monad m) => a -> a -> a -> t m a

-- MAYBE: Sometimes it is more convenient to know the count rather then the
-- ending or starting element. For those cases we can define the folllowing
-- APIs. All of these will work only for bounded types if we represent the
-- count by Int.
--
-- enumerateN
-- enumerateFromN
-- enumerateToN
-- enumerateFromStep
-- enumerateFromStepN

-------------------------------------------------------------------------------
-- Convenient functions for bounded types
-------------------------------------------------------------------------------
--
-- |
-- > enumerate = enumerateFrom minBound
--
-- Enumerate a 'Bounded' type from its 'minBound' to 'maxBound'
--
-- @since 0.6.0
{-# INLINE enumerate #-}
enumerate :: (IsStream t, Monad m, Bounded a, Enumerable a) => t m a
enumerate :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Bounded a, Enumerable a) =>
t m a
enumerate = a -> t m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> t m a
enumerateFrom a
forall a. Bounded a => a
minBound

-- |
-- > enumerateTo = enumerateFromTo minBound
--
-- Enumerate a 'Bounded' type from its 'minBound' to specified value.
--
-- @since 0.6.0
{-# INLINE enumerateTo #-}
enumerateTo :: (IsStream t, Monad m, Bounded a, Enumerable a) => a -> t m a
enumerateTo :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Bounded a, Enumerable a) =>
a -> t m a
enumerateTo = a -> a -> t m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> a -> t m a
enumerateFromTo a
forall a. Bounded a => a
minBound

-- |
-- > enumerateFromBounded = enumerateFromTo from maxBound
--
-- 'enumerateFrom' for 'Bounded' 'Enum' types.
--
-- @since 0.6.0
{-# INLINE enumerateFromBounded #-}
enumerateFromBounded :: (IsStream t, Monad m, Enumerable a, Bounded a)
    => a -> t m a
enumerateFromBounded :: forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(IsStream t, Monad m, Enumerable a, Bounded a) =>
a -> t m a
enumerateFromBounded a
from = a -> a -> t m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> a -> t m a
enumerateFromTo a
from a
forall a. Bounded a => a
maxBound

-------------------------------------------------------------------------------
-- Enumerable Instances
-------------------------------------------------------------------------------
--
-- For Enum types smaller than or equal to Int size.
#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE)           \
instance Enumerable SMALL_TYPE where {                 \
    {-# INLINE enumerateFrom #-};                      \
    enumerateFrom = enumerateFromBounded;              \
    {-# INLINE enumerateFromThen #-};                  \
    enumerateFromThen = enumerateFromThenSmallBounded; \
    {-# INLINE enumerateFromTo #-};                    \
    enumerateFromTo = enumerateFromToSmall;            \
    {-# INLINE enumerateFromThenTo #-};                \
    enumerateFromThenTo = enumerateFromThenToSmall }


ENUMERABLE_BOUNDED_SMALL(())
ENUMERABLE_BOUNDED_SMALL(Bool)
ENUMERABLE_BOUNDED_SMALL(Ordering)
ENUMERABLE_BOUNDED_SMALL(Char)

-- For bounded Integral Enum types, may be larger than Int.
#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE)  \
instance Enumerable INTEGRAL_TYPE where {           \
    {-# INLINE enumerateFrom #-};                   \
    enumerateFrom = enumerateFromIntegral;          \
    {-# INLINE enumerateFromThen #-};               \
    enumerateFromThen = enumerateFromThenIntegral;  \
    {-# INLINE enumerateFromTo #-};                 \
    enumerateFromTo = enumerateFromToIntegral;      \
    {-# INLINE enumerateFromThenTo #-};             \
    enumerateFromThenTo = enumerateFromThenToIntegral }

ENUMERABLE_BOUNDED_INTEGRAL(Int)
ENUMERABLE_BOUNDED_INTEGRAL(Int8)
ENUMERABLE_BOUNDED_INTEGRAL(Int16)
ENUMERABLE_BOUNDED_INTEGRAL(Int32)
ENUMERABLE_BOUNDED_INTEGRAL(Int64)
ENUMERABLE_BOUNDED_INTEGRAL(Word)
ENUMERABLE_BOUNDED_INTEGRAL(Word8)
ENUMERABLE_BOUNDED_INTEGRAL(Word16)
ENUMERABLE_BOUNDED_INTEGRAL(Word32)
ENUMERABLE_BOUNDED_INTEGRAL(Word64)

-- For unbounded Integral Enum types.
#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE)              \
instance Enumerable INTEGRAL_TYPE where {                         \
    {-# INLINE enumerateFrom #-};                                 \
    enumerateFrom from = enumerateFromStepIntegral from 1;        \
    {-# INLINE enumerateFromThen #-};                             \
    enumerateFromThen from next =                                 \
        enumerateFromStepIntegral from (next - from);             \
    {-# INLINE enumerateFromTo #-};                               \
    enumerateFromTo = enumerateFromToIntegral;                    \
    {-# INLINE enumerateFromThenTo #-};                           \
    enumerateFromThenTo = enumerateFromThenToIntegral }

ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)
ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)

#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT)         \
instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where {     \
    {-# INLINE enumerateFrom #-};                                 \
    enumerateFrom = enumerateFromFractional;                      \
    {-# INLINE enumerateFromThen #-};                             \
    enumerateFromThen = enumerateFromThenFractional;              \
    {-# INLINE enumerateFromTo #-};                               \
    enumerateFromTo = enumerateFromToFractional;                  \
    {-# INLINE enumerateFromThenTo #-};                           \
    enumerateFromThenTo = enumerateFromThenToFractional }

ENUMERABLE_FRACTIONAL(Float,)
ENUMERABLE_FRACTIONAL(Double,)
ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)
ENUMERABLE_FRACTIONAL((Ratio a),Integral a)

instance Enumerable a => Enumerable (Identity a) where
    {-# INLINE enumerateFrom #-}
    enumerateFrom :: forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(IsStream t, Monad m) =>
Identity a -> t m (Identity a)
enumerateFrom (Identity a
from) =
        SerialT m (Identity a) -> t m (Identity a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
IsStream t =>
SerialT m a -> t m a
IsStream.fromSerial (SerialT m (Identity a) -> t m (Identity a))
-> SerialT m (Identity a) -> t m (Identity a)
forall a b. (a -> b) -> a -> b
$ (a -> Identity a) -> SerialT m a -> SerialT m (Identity a)
forall (m :: * -> *) a b.
Monad m =>
(a -> b) -> SerialT m a -> SerialT m b
Serial.map a -> Identity a
forall a. a -> Identity a
Identity (SerialT m a -> SerialT m (Identity a))
-> SerialT m a -> SerialT m (Identity a)
forall a b. (a -> b) -> a -> b
$ a -> SerialT m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> t m a
enumerateFrom a
from
    {-# INLINE enumerateFromThen #-}
    enumerateFromThen :: forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(IsStream t, Monad m) =>
Identity a -> Identity a -> t m (Identity a)
enumerateFromThen (Identity a
from) (Identity a
next) =
        SerialT m (Identity a) -> t m (Identity a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
IsStream t =>
SerialT m a -> t m a
IsStream.fromSerial (SerialT m (Identity a) -> t m (Identity a))
-> SerialT m (Identity a) -> t m (Identity a)
forall a b. (a -> b) -> a -> b
$ (a -> Identity a) -> SerialT m a -> SerialT m (Identity a)
forall (m :: * -> *) a b.
Monad m =>
(a -> b) -> SerialT m a -> SerialT m b
Serial.map a -> Identity a
forall a. a -> Identity a
Identity (SerialT m a -> SerialT m (Identity a))
-> SerialT m a -> SerialT m (Identity a)
forall a b. (a -> b) -> a -> b
$ a -> a -> SerialT m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> a -> t m a
enumerateFromThen a
from a
next
    {-# INLINE enumerateFromTo #-}
    enumerateFromTo :: forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(IsStream t, Monad m) =>
Identity a -> Identity a -> t m (Identity a)
enumerateFromTo (Identity a
from) (Identity a
to) =
        SerialT m (Identity a) -> t m (Identity a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
IsStream t =>
SerialT m a -> t m a
IsStream.fromSerial (SerialT m (Identity a) -> t m (Identity a))
-> SerialT m (Identity a) -> t m (Identity a)
forall a b. (a -> b) -> a -> b
$ (a -> Identity a) -> SerialT m a -> SerialT m (Identity a)
forall (m :: * -> *) a b.
Monad m =>
(a -> b) -> SerialT m a -> SerialT m b
Serial.map a -> Identity a
forall a. a -> Identity a
Identity (SerialT m a -> SerialT m (Identity a))
-> SerialT m a -> SerialT m (Identity a)
forall a b. (a -> b) -> a -> b
$ a -> a -> SerialT m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> a -> t m a
enumerateFromTo a
from a
to
    {-# INLINE enumerateFromThenTo #-}
    enumerateFromThenTo :: forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(IsStream t, Monad m) =>
Identity a -> Identity a -> Identity a -> t m (Identity a)
enumerateFromThenTo (Identity a
from) (Identity a
next) (Identity a
to) =
        SerialT m (Identity a) -> t m (Identity a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
IsStream t =>
SerialT m a -> t m a
IsStream.fromSerial
            (SerialT m (Identity a) -> t m (Identity a))
-> SerialT m (Identity a) -> t m (Identity a)
forall a b. (a -> b) -> a -> b
$ (a -> Identity a) -> SerialT m a -> SerialT m (Identity a)
forall (m :: * -> *) a b.
Monad m =>
(a -> b) -> SerialT m a -> SerialT m b
Serial.map a -> Identity a
forall a. a -> Identity a
Identity
            (SerialT m a -> SerialT m (Identity a))
-> SerialT m a -> SerialT m (Identity a)
forall a b. (a -> b) -> a -> b
$ a -> a -> a -> SerialT m a
forall a (t :: (* -> *) -> * -> *) (m :: * -> *).
(Enumerable a, IsStream t, Monad m) =>
a -> a -> a -> t m a
enumerateFromThenTo a
from a
next a
to

-- TODO
{-
instance Enumerable a => Enumerable (Last a)
instance Enumerable a => Enumerable (First a)
instance Enumerable a => Enumerable (Max a)
instance Enumerable a => Enumerable (Min a)
instance Enumerable a => Enumerable (Const a b)
instance Enumerable (f a) => Enumerable (Alt f a)
instance Enumerable (f a) => Enumerable (Ap f a)
-}