module Data.Series.Generic.Aggregation ( 
    -- * Grouping
    Grouping,
    groupBy,
    aggregateWith,
    foldWith,

    -- * Windowing
    expanding,
    windowing,

    -- * Folding
    all, any, and, or, sum, product, maximum, maximumOn, minimum, minimumOn,
    argmax, argmin,
) where

import qualified Data.List 
import qualified Data.Map.Strict                as Map
import           Data.Ord                       ( Down(..) )
import           Data.Series.Generic.Definition ( Series(..) )
import qualified Data.Series.Generic.Definition as GSeries
import           Data.Series.Generic.View       ( Range, slice, select )
import qualified Data.Vector                    as Boxed
import           Data.Vector.Generic            ( Vector )
import qualified Data.Vector.Generic            as Vector
import           Prelude                        hiding ( last, null, length, all, any, and, or, sum, product, maximum, minimum )

-- $setup
-- >>> import qualified Data.Series as Series
-- >>> import qualified Data.Set as Set

-- | Group values in a 'Series' by some grouping function (@k -> g@).
-- The provided grouping function is guaranteed to operate on a non-empty 'Series'.
--
-- This function is expected to be used in conjunction with @aggregate@:
-- 
-- >>> import Data.Maybe ( fromMaybe )
-- >>> type Date = (Int, String)
-- >>> month :: (Date -> String) = snd
-- >>> :{ 
--     let xs = Series.fromList [ ((2020, "January") :: Date,  0 :: Int)
--                              , ((2021, "January"), -5)
--                              , ((2020, "June")   , 20)
--                              , ((2021, "June")   , 25) 
--                              ]
--      in xs `groupBy` month `aggregateWith` (fromMaybe 0 . minimum)
-- :}
--     index | values
--     ----- | ------
-- "January" |     -5
--    "June" |     20
groupBy :: Series v k a       -- ^ Input series
        -> (k -> g)           -- ^ Grouping function
        -> Grouping k g v a   -- ^ Grouped series
{-# INLINABLE groupBy #-}
groupBy :: forall {k} (v :: k -> *) k (a :: k) g.
Series v k a -> (k -> g) -> Grouping k g v a
groupBy = Series v k a -> (k -> g) -> Grouping k g v a
forall {k} k g (v :: k -> *) (a :: k).
Series v k a -> (k -> g) -> Grouping k g v a
MkGrouping


-- | Representation of a 'Series' being grouped.
data Grouping k g v a 
    = MkGrouping (Series v k a)  (k -> g)


-- | Aggregate groups resulting from a call to 'groupBy':
-- 
-- >>> import Data.Maybe ( fromMaybe )
-- >>> type Date = (Int, String)
-- >>> month :: (Date -> String) = snd
-- >>> :{ 
--     let xs = Series.fromList [ ((2020, "January") :: Date,  0 :: Int)
--                              , ((2021, "January"), -5)
--                              , ((2020, "June")   , 20)
--                              , ((2021, "June")   , 25) 
--                              ]
--      in xs `groupBy` month `aggregateWith` (fromMaybe 0 . minimum)
-- :}
--     index | values
--     ----- | ------
-- "January" |     -5
--    "June" |     20
--
-- If you want to aggregate groups using a binary function, see 'foldWith' which
-- may be much faster.
aggregateWith :: (Ord g, Vector v a, Vector v b) 
              => Grouping k g v a 
              -> (Series v k a -> b) 
              -> Series v g b
{-# INLINABLE aggregateWith #-}
aggregateWith :: forall g (v :: * -> *) a b k.
(Ord g, Vector v a, Vector v b) =>
Grouping k g v a -> (Series v k a -> b) -> Series v g b
aggregateWith (MkGrouping Series v k a
xs k -> g
by) Series v k a -> b
f
    = Map g b -> Series v g b
forall (v :: * -> *) a k. Vector v a => Map k a -> Series v k a
GSeries.fromStrictMap 
    -- Using `fromDistinctAscList` is predicated on a particular structure
    -- created by the `acc` function below.
    -- This is rather unsafe, and has been the source of bugs in the past
    (Map g b -> Series v g b) -> Map g b -> Series v g b
forall a b. (a -> b) -> a -> b
$ ([(k, a)] -> b) -> Map g [(k, a)] -> Map g b
forall a b. (a -> b) -> Map g a -> Map g b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Series v k a -> b
f (Series v k a -> b) -> ([(k, a)] -> Series v k a) -> [(k, a)] -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(k, a)] -> Series v k a
forall (v :: * -> *) a k. Vector v a => [(k, a)] -> Series v k a
GSeries.fromDistinctAscList)
    -- We're using a list fold to limit the number of 
    -- type constraints. This is about as fast as it is 
    -- with a Vector fold
    (Map g [(k, a)] -> Map g b) -> Map g [(k, a)] -> Map g b
forall a b. (a -> b) -> a -> b
$ (Map g [(k, a)] -> (k, a) -> Map g [(k, a)])
-> Map g [(k, a)] -> [(k, a)] -> Map g [(k, a)]
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
Data.List.foldl' Map g [(k, a)] -> (k, a) -> Map g [(k, a)]
forall {b}. Map g [(k, b)] -> (k, b) -> Map g [(k, b)]
acc Map g [(k, a)]
forall a. Monoid a => a
mempty 
    ([(k, a)] -> Map g [(k, a)]) -> [(k, a)] -> Map g [(k, a)]
forall a b. (a -> b) -> a -> b
$ Series v k a -> [(k, a)]
forall (v :: * -> *) a k. Vector v a => Series v k a -> [(k, a)]
GSeries.toList Series v k a
xs
    where
        acc :: Map g [(k, b)] -> (k, b) -> Map g [(k, b)]
acc !Map g [(k, b)]
m (k
key, b
val) = ([(k, b)] -> [(k, b)] -> [(k, b)])
-> g -> [(k, b)] -> Map g [(k, b)] -> Map g [(k, b)]
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith (([(k, b)] -> [(k, b)] -> [(k, b)])
-> [(k, b)] -> [(k, b)] -> [(k, b)]
forall a b c. (a -> b -> c) -> b -> a -> c
flip [(k, b)] -> [(k, b)] -> [(k, b)]
forall a. Semigroup a => a -> a -> a
(<>)) -- Flipping arguments to ensure that keys are ordered as expected
                                           (k -> g
by k
key) 
                                           ((k, b) -> [(k, b)]
forall a. a -> [a]
Data.List.singleton (k
key, b
val)) 
                                           Map g [(k, b)]
m


-- | Fold over each group in a 'Grouping' using a binary function.
-- While this is not as expressive as 'aggregateWith', users looking for maximum
-- performance should use 'foldWith' as much as possible.
--
-- >>> type Date = (Int, String)
-- >>> month :: (Date -> String) = snd
-- >>> :{ 
--     let xs = Series.fromList [ ((2020, "January") :: Date,  0 :: Int)
--                              , ((2021, "January"), -5)
--                              , ((2020, "June")   , 20)
--                              , ((2021, "June")   , 25) 
--                              ]
--      in xs `groupBy` month `foldWith` min
-- :}
--     index | values
--     ----- | ------
-- "January" |     -5
--    "June" |     20
foldWith :: (Ord g, Vector v a) 
         => Grouping k g v a
         -> (a -> a -> a)
         -> Series v g a
{-# INLINABLE foldWith #-}
foldWith :: forall g (v :: * -> *) a k.
(Ord g, Vector v a) =>
Grouping k g v a -> (a -> a -> a) -> Series v g a
foldWith (MkGrouping Series v k a
xs k -> g
by) a -> a -> a
f 
    = Map g a -> Series v g a
forall (v :: * -> *) a k. Vector v a => Map k a -> Series v k a
GSeries.fromStrictMap 
    -- We're using a list fold to limit the number of 
    -- type constraints. This is about as fast as it is 
    -- with a Vector fold
    (Map g a -> Series v g a) -> Map g a -> Series v g a
forall a b. (a -> b) -> a -> b
$ (Map g a -> (k, a) -> Map g a) -> Map g a -> [(k, a)] -> Map g a
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
Data.List.foldl' Map g a -> (k, a) -> Map g a
acc Map g a
forall a. Monoid a => a
mempty 
    ([(k, a)] -> Map g a) -> [(k, a)] -> Map g a
forall a b. (a -> b) -> a -> b
$ Series v k a -> [(k, a)]
forall (v :: * -> *) a k. Vector v a => Series v k a -> [(k, a)]
GSeries.toList Series v k a
xs
    where
        acc :: Map g a -> (k, a) -> Map g a
acc !Map g a
m (k
key, a
val) = (a -> a -> a) -> g -> a -> Map g a -> Map g a
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith a -> a -> a
f (k -> g
by k
key) a
val Map g a
m


-- | Expanding window aggregation.
--
-- >>> import qualified Data.Series as Series 
-- >>> :{ 
--     let (xs :: Series.Series Int Int) 
--          = Series.fromList [ (1, 0)
--                            , (2, 1)
--                            , (3, 2)
--                            , (4, 3)
--                            , (5, 4)
--                            , (6, 5)
--                            ]
--     in (xs `expanding` sum) :: Series.Series Int Int 
-- :}
-- index | values
-- ----- | ------
--     1 |      0
--     2 |      1
--     3 |      3
--     4 |      6
--     5 |     10
--     6 |     15
expanding :: (Vector v a, Vector v b) 
          => Series v k a        -- ^ Series vector
          -> (Series v k a -> b) -- ^ Aggregation function
          -> Series v k b        -- ^ Resulting vector
{-# INLINABLE expanding #-}
expanding :: forall (v :: * -> *) a b k.
(Vector v a, Vector v b) =>
Series v k a -> (Series v k a -> b) -> Series v k b
expanding Series v k a
vs Series v k a -> b
f = Index k -> v b -> Series v k b
forall {k} (v :: k -> *) k1 (a :: k).
Index k1 -> v a -> Series v k1 a
MkSeries (Series v k a -> Index k
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> Index k2
index Series v k a
vs) (v b -> Series v k b) -> v b -> Series v k b
forall a b. (a -> b) -> a -> b
$ Int -> (Int -> (b, Int)) -> Int -> v b
forall (v :: * -> *) a b.
Vector v a =>
Int -> (b -> (a, b)) -> b -> v a
Vector.unfoldrExactN (Series v k a -> Int
forall (v :: * -> *) a k. Vector v a => Series v k a -> Int
GSeries.length Series v k a
vs) Int -> (b, Int)
go Int
0
    where
        -- Recall that `slice` does NOT include the right index
        go :: Int -> (b, Int)
go Int
ix = (Series v k a -> b
f (Series v k a -> b) -> Series v k a -> b
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Series v k a -> Series v k a
forall (v :: * -> *) a k.
Vector v a =>
Int -> Int -> Series v k a -> Series v k a
slice Int
0 (Int
ix Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Series v k a
vs, Int
ix Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)


-- | General-purpose window aggregation.
--
-- >>> import qualified Data.Series as Series 
-- >>> import           Data.Series ( to )
-- >>> :{ 
--     let (xs :: Series.Series Int Int) 
--          = Series.fromList [ (1, 0)
--                            , (2, 1)
--                            , (3, 2)
--                            , (4, 3)
--                            , (5, 4)
--                            , (6, 5)
--                            ]
--     in windowing (\k -> k `to` (k + 2)) sum xs
-- :}
-- index | values
-- ----- | ------
--     1 |      3
--     2 |      6
--     3 |      9
--     4 |     12
--     5 |      9
--     6 |      5
windowing :: (Ord k, Vector v a, Vector v b)
          => (k -> Range k)
          -> (Series v k a -> b)
          -> Series v k a
          -> Series v k b
{-# INLINABLE windowing #-}
windowing :: forall k (v :: * -> *) a b.
(Ord k, Vector v a, Vector v b) =>
(k -> Range k)
-> (Series v k a -> b) -> Series v k a -> Series v k b
windowing k -> Range k
range Series v k a -> b
agg Series v k a
series 
    = (k -> a -> b) -> Series v k a -> Series v k b
forall (v :: * -> *) a b k.
(Vector v a, Vector v b) =>
(k -> a -> b) -> Series v k a -> Series v k b
GSeries.mapWithKey (\k
k a
_ -> Series v k a -> b
agg (Series v k a -> b) -> Series v k a -> b
forall a b. (a -> b) -> a -> b
$ Series v k a
series Series v k a -> Range k -> Series v k a
forall (s :: * -> *) (v :: * -> *) a k.
(Selection s, Vector v a, Ord k) =>
Series v k a -> s k -> Series v k a
forall (v :: * -> *) a k.
(Vector v a, Ord k) =>
Series v k a -> Range k -> Series v k a
`select` k -> Range k
range k
k) Series v k a
series


-- | \(O(n)\) Check if all elements satisfy the predicate.
all :: Vector v a => (a -> Bool) -> Series v k a -> Bool
{-# INLINABLE all #-}
all :: forall (v :: * -> *) a k.
Vector v a =>
(a -> Bool) -> Series v k a -> Bool
all a -> Bool
f = (a -> Bool) -> v a -> Bool
forall (v :: * -> *) a. Vector v a => (a -> Bool) -> v a -> Bool
Vector.all a -> Bool
f (v a -> Bool) -> (Series v k a -> v a) -> Series v k a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) Check if any element satisfies the predicate.
any :: Vector v a => (a -> Bool) -> Series v k a -> Bool
{-# INLINABLE any #-}
any :: forall (v :: * -> *) a k.
Vector v a =>
(a -> Bool) -> Series v k a -> Bool
any a -> Bool
f = (a -> Bool) -> v a -> Bool
forall (v :: * -> *) a. Vector v a => (a -> Bool) -> v a -> Bool
Vector.any a -> Bool
f (v a -> Bool) -> (Series v k a -> v a) -> Series v k a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) Check if all elements are 'True'.
and :: Vector v Bool => Series v k Bool -> Bool
{-# INLINABLE and #-}
and :: forall (v :: * -> *) k. Vector v Bool => Series v k Bool -> Bool
and = v Bool -> Bool
forall (v :: * -> *). Vector v Bool => v Bool -> Bool
Vector.and (v Bool -> Bool)
-> (Series v k Bool -> v Bool) -> Series v k Bool -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k Bool -> v Bool
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) Check if any element is 'True'.
or :: Vector v Bool => Series v k Bool -> Bool
{-# INLINABLE or #-}
or :: forall (v :: * -> *) k. Vector v Bool => Series v k Bool -> Bool
or = v Bool -> Bool
forall (v :: * -> *). Vector v Bool => v Bool -> Bool
Vector.or (v Bool -> Bool)
-> (Series v k Bool -> v Bool) -> Series v k Bool -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k Bool -> v Bool
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) Compute the sum of the elements.
sum :: (Num a, Vector v a) => Series v k a -> a
{-# INLINABLE sum #-}
sum :: forall a (v :: * -> *) k. (Num a, Vector v a) => Series v k a -> a
sum = v a -> a
forall (v :: * -> *) a. (Vector v a, Num a) => v a -> a
Vector.sum (v a -> a) -> (Series v k a -> v a) -> Series v k a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) Compute the product of the elements.
product :: (Num a, Vector v a) => Series v k a -> a
{-# INLINABLE product #-}
product :: forall a (v :: * -> *) k. (Num a, Vector v a) => Series v k a -> a
product = v a -> a
forall (v :: * -> *) a. (Vector v a, Num a) => v a -> a
Vector.product (v a -> a) -> (Series v k a -> v a) -> Series v k a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


nothingIfEmpty :: Vector v a 
               => (Series v k a -> b) -> (Series v k a -> Maybe b)
nothingIfEmpty :: forall (v :: * -> *) a k b.
Vector v a =>
(Series v k a -> b) -> Series v k a -> Maybe b
nothingIfEmpty Series v k a -> b
f Series v k a
xs = if Series v k a -> Bool
forall (v :: * -> *) a k. Vector v a => Series v k a -> Bool
GSeries.null Series v k a
xs then Maybe b
forall a. Maybe a
Nothing else b -> Maybe b
forall a. a -> Maybe a
Just (Series v k a -> b
f Series v k a
xs) 


-- | \(O(n)\) Yield the maximum element of the series. In case of a tie, the first occurrence wins.
maximum :: (Ord a, Vector v a) => Series v k a -> Maybe a
{-# INLINABLE maximum #-}
maximum :: forall a (v :: * -> *) k.
(Ord a, Vector v a) =>
Series v k a -> Maybe a
maximum = (Series v k a -> a) -> Series v k a -> Maybe a
forall (v :: * -> *) a k b.
Vector v a =>
(Series v k a -> b) -> Series v k a -> Maybe b
nothingIfEmpty ((Series v k a -> a) -> Series v k a -> Maybe a)
-> (Series v k a -> a) -> Series v k a -> Maybe a
forall a b. (a -> b) -> a -> b
$ v a -> a
forall (v :: * -> *) a. (Vector v a, Ord a) => v a -> a
Vector.maximum (v a -> a) -> (Series v k a -> v a) -> Series v k a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) @'maximumOn' f xs@ teturns the maximum element of the series @xs@, as determined by the function @f@.
-- In case of a tie, the first occurrence wins.
-- If the 'Series' is empty, @Nothing@ is returned.
maximumOn :: (Ord b, Vector v a) => (a -> b) -> Series v k a -> Maybe a
{-# INLINABLE maximumOn #-}
maximumOn :: forall b (v :: * -> *) a k.
(Ord b, Vector v a) =>
(a -> b) -> Series v k a -> Maybe a
maximumOn a -> b
f = (Series v k a -> a) -> Series v k a -> Maybe a
forall (v :: * -> *) a k b.
Vector v a =>
(Series v k a -> b) -> Series v k a -> Maybe b
nothingIfEmpty ((Series v k a -> a) -> Series v k a -> Maybe a)
-> (Series v k a -> a) -> Series v k a -> Maybe a
forall a b. (a -> b) -> a -> b
$ (a -> b) -> v a -> a
forall b (v :: * -> *) a.
(Ord b, Vector v a) =>
(a -> b) -> v a -> a
Vector.maximumOn a -> b
f (v a -> a) -> (Series v k a -> v a) -> Series v k a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) Yield the minimum element of the series. In case of a tie, the first occurrence wins.
-- If the 'Series' is empty, @Nothing@ is returned.
minimum :: (Ord a, Vector v a) => Series v k a -> Maybe a
{-# INLINABLE minimum #-}
minimum :: forall a (v :: * -> *) k.
(Ord a, Vector v a) =>
Series v k a -> Maybe a
minimum = (Series v k a -> a) -> Series v k a -> Maybe a
forall (v :: * -> *) a k b.
Vector v a =>
(Series v k a -> b) -> Series v k a -> Maybe b
nothingIfEmpty ((Series v k a -> a) -> Series v k a -> Maybe a)
-> (Series v k a -> a) -> Series v k a -> Maybe a
forall a b. (a -> b) -> a -> b
$ v a -> a
forall (v :: * -> *) a. (Vector v a, Ord a) => v a -> a
Vector.minimum (v a -> a) -> (Series v k a -> v a) -> Series v k a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) @'minimumOn' f xs@ teturns the minimum element of the series @xs@, as determined by the function @f@.
-- In case of a tie, the first occurrence wins.
-- If the 'Series' is empty, @Nothing@ is returned.
minimumOn :: (Ord b, Vector v a) => (a -> b) -> Series v k a -> Maybe a
{-# INLINABLE minimumOn #-}
minimumOn :: forall b (v :: * -> *) a k.
(Ord b, Vector v a) =>
(a -> b) -> Series v k a -> Maybe a
minimumOn a -> b
f = (Series v k a -> a) -> Series v k a -> Maybe a
forall (v :: * -> *) a k b.
Vector v a =>
(Series v k a -> b) -> Series v k a -> Maybe b
nothingIfEmpty ((Series v k a -> a) -> Series v k a -> Maybe a)
-> (Series v k a -> a) -> Series v k a -> Maybe a
forall a b. (a -> b) -> a -> b
$ (a -> b) -> v a -> a
forall b (v :: * -> *) a.
(Ord b, Vector v a) =>
(a -> b) -> v a -> a
Vector.minimumOn a -> b
f (v a -> a) -> (Series v k a -> v a) -> Series v k a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> v a
forall {k1} (v :: k1 -> *) k2 (a :: k1). Series v k2 a -> v a
values


-- | \(O(n)\) Find the index of the maximum element in the input series.
-- If the input series is empty, 'Nothing' is returned.
--
-- The index of the first occurrence of the maximum element is returned.
--
-- >>> import qualified Data.Series as Series 
-- >>> :{ 
--     let (xs :: Series.Series Int Int) 
--          = Series.fromList [ (1, 0)
--                            , (2, 1)
--                            , (3, 2)
--                            , (4, 7)
--                            , (5, 4)
--                            , (6, 5)
--                            ]
--     in argmax xs 
-- :}
-- Just 4
argmax :: (Ord a, Vector v a)
       => Series v k a
       -> Maybe k
{-# INLINABLE argmax #-}
argmax :: forall a (v :: * -> *) k.
(Ord a, Vector v a) =>
Series v k a -> Maybe k
argmax Series v k a
xs | Series v k a -> Bool
forall (v :: * -> *) a k. Vector v a => Series v k a -> Bool
GSeries.null Series v k a
xs = Maybe k
forall a. Maybe a
Nothing
          | Bool
otherwise = k -> Maybe k
forall a. a -> Maybe a
Just 
                      (k -> Maybe k) -> (Series v k a -> k) -> Series v k a -> Maybe k
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (k, a) -> k
forall a b. (a, b) -> a
fst 
                      -- We're forcing the use of boxed vectors in order to
                      -- reduce the constraints on the vector instance
                      ((k, a) -> k) -> (Series v k a -> (k, a)) -> Series v k a -> k
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((k, a) -> a) -> Vector (k, a) -> (k, a)
forall b a. Ord b => (a -> b) -> Vector a -> a
Boxed.maximumOn (k, a) -> a
forall a b. (a, b) -> b
snd 
                      (Vector (k, a) -> (k, a))
-> (Series v k a -> Vector (k, a)) -> Series v k a -> (k, a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series Vector k a -> Vector (k, a)
forall (v :: * -> *) a k.
(Vector v a, Vector v k, Vector v (k, a)) =>
Series v k a -> v (k, a)
GSeries.toVector
                      (Series Vector k a -> Vector (k, a))
-> (Series v k a -> Series Vector k a)
-> Series v k a
-> Vector (k, a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Series v k a -> Series Vector k a
forall (v1 :: * -> *) a (v2 :: * -> *) k.
(Vector v1 a, Vector v2 a) =>
Series v1 k a -> Series v2 k a
GSeries.convert
                      (Series v k a -> Maybe k) -> Series v k a -> Maybe k
forall a b. (a -> b) -> a -> b
$ Series v k a
xs


-- | \(O(n)\) Find the index of the minimum element in the input series.
-- If the input series is empty, 'Nothing' is returned.
--
-- The index of the first occurrence of the minimum element is returned.
--
-- >>> import qualified Data.Series as Series 
-- >>> :{ 
--     let (xs :: Series.Series Int Int) 
--          = Series.fromList [ (1, 1)
--                            , (2, 1)
--                            , (3, 2)
--                            , (4, 0)
--                            , (5, 4)
--                            , (6, 5)
--                            ]
--     in argmin xs 
-- :}
-- Just 4
argmin :: (Ord a, Vector v a, Vector v (Down a))
       => Series v k a
       -> Maybe k
{-# INLINABLE argmin #-}
argmin :: forall a (v :: * -> *) k.
(Ord a, Vector v a, Vector v (Down a)) =>
Series v k a -> Maybe k
argmin = Series v k (Down a) -> Maybe k
forall a (v :: * -> *) k.
(Ord a, Vector v a) =>
Series v k a -> Maybe k
argmax (Series v k (Down a) -> Maybe k)
-> (Series v k a -> Series v k (Down a)) -> Series v k a -> Maybe k
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Down a) -> Series v k a -> Series v k (Down a)
forall (v :: * -> *) a b k.
(Vector v a, Vector v b) =>
(a -> b) -> Series v k a -> Series v k b
GSeries.map a -> Down a
forall a. a -> Down a
Down