{-# LANGUAGE BangPatterns          #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE UndecidableInstances  #-}

module Plots.Util
  ( pathFromVertices
  , minMaxOf
  , enumFromToN
  , whenever

    -- * State helpers
  , (&=)
  , (&~~)
  ) where

import           Control.Lens
import           Control.Monad.State
import           Data.Bool

import           Diagrams.Prelude           hiding (diff)

-- | Similar to '(%=)' but takes a state modification instead of a
--   function.
(&=) :: MonadState s m => ASetter' s b -> State b a -> m ()
ASetter' s b
l &= :: forall s (m :: * -> *) b a.
MonadState s m =>
ASetter' s b -> State b a -> m ()
&= State b a
s = ASetter' s b
l ASetter' s b -> (b -> b) -> m ()
forall s (m :: * -> *) a b.
MonadState s m =>
ASetter s s a b -> (a -> b) -> m ()
%= State b a -> b -> b
forall s a. State s a -> s -> s
execState State b a
s
infix 3 &=

-- | Similar to '(&~)' but works with 'StateT' and returns it in @m@.
(&~~) :: Monad m => s -> StateT s m a -> m s
s
l &~~ :: forall (m :: * -> *) s a. Monad m => s -> StateT s m a -> m s
&~~ StateT s m a
s = StateT s m a -> s -> m s
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m s
execStateT StateT s m a
s s
l
infix 1 &~~

-- | @enumFromToN a b n@ calculates a list from @a@ to @b@ in @n@ steps.
enumFromToN :: Fractional n => n -> n -> Int -> [n]
enumFromToN :: forall n. Fractional n => n -> n -> Int -> [n]
enumFromToN n
a n
b Int
n = Int -> n -> [n]
go Int
n n
a
  where
    go :: Int -> n -> [n]
go !Int
i !n
x | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1     = [n
x]
             | Bool
otherwise = n
x n -> [n] -> [n]
forall a. a -> [a] -> [a]
: Int -> n -> [n]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (n
x n -> n -> n
forall a. Num a => a -> a -> a
+ n
diff)
    diff :: n
diff = (n
b n -> n -> n
forall a. Num a => a -> a -> a
- n
a) n -> n -> n
forall a. Fractional a => a -> a -> a
/ Int -> n
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n

-- | Apply a function if the predicate is true.
whenever :: Bool -> (a -> a) -> a -> a
whenever :: forall a. Bool -> (a -> a) -> a -> a
whenever Bool
b a -> a
f = (a -> a) -> (a -> a) -> Bool -> a -> a
forall a. a -> a -> Bool -> a
bool a -> a
forall a. a -> a
id a -> a
f Bool
b

------------------------------------------------------------------------
-- Diagrams
------------------------------------------------------------------------

-- | Type specialised version of 'fromVertices'.
pathFromVertices :: (Metric v, OrderedField n) => [Point v n] -> Path v n
pathFromVertices :: forall (v :: * -> *) n.
(Metric v, OrderedField n) =>
[Point v n] -> Path v n
pathFromVertices = [Point v n] -> Path v n
[Point (V (Path v n)) (N (Path v n))] -> Path v n
forall t. TrailLike t => [Point (V t) (N t)] -> t
fromVertices
{-# INLINE pathFromVertices #-}

-- | Minmax of a getter in the form @V2 min max@. Returns @(V2
--   (-Infinity) Infinity)@ for empty folds.
minMaxOf :: (Fractional a, Ord a) => Getting (Endo (Endo (V2 a))) s a -> s -> (a,a)
minMaxOf :: forall a s.
(Fractional a, Ord a) =>
Getting (Endo (Endo (V2 a))) s a -> s -> (a, a)
minMaxOf Getting (Endo (Endo (V2 a))) s a
l = Getting (Endo (Endo (V2 a))) s a
-> (V2 a -> a -> V2 a) -> V2 a -> s -> V2 a
forall r s a.
Getting (Endo (Endo r)) s a -> (r -> a -> r) -> r -> s -> r
foldlOf' Getting (Endo (Endo (V2 a))) s a
l (\(V2 a
mn a
mx) a
a -> a -> a -> V2 a
forall a. a -> a -> V2 a
V2 (a -> a -> a
forall a. Ord a => a -> a -> a
min a
mn a
a) (a -> a -> a
forall a. Ord a => a -> a -> a
max a
mx a
a)) (a -> a -> V2 a
forall a. a -> a -> V2 a
V2 (a
1a -> a -> a
forall a. Fractional a => a -> a -> a
/a
0) (-a
1a -> a -> a
forall a. Fractional a => a -> a -> a
/a
0))
          (s -> V2 a) -> (V2 a -> (a, a)) -> s -> (a, a)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(V2 a
x a
y) -> (a
x,a
y)
      -- (\acc a -> acc <**> V2 min max ?? a)
-- V2 is used instead of a tuple because V2 is strict.
{-# INLINE minMaxOf #-}