packages feed

trade-journal-0.0.1: src/Journal/Split.hs

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}

module Journal.Split where

import Control.Lens
import Data.Default
import Data.List (foldl')

data Split a
  = Some
      { _used :: a,
        _kept :: a
      }
  | All a
  | None a
  deriving (Eq, Ord, Show)

makePrisms ''Split

instance Functor Split where
  fmap f (Some u k) = Some (f u) (f k)
  fmap f (All u) = All (f u)
  fmap f (None k) = None (f k)

_Splits :: Traversal (Split a) (Split b) a b
_Splits f (Some u k) = Some <$> f u <*> f k
_Splits f (All u) = All <$> f u
_Splits f (None k) = None <$> f k

_SplitUsed :: Traversal' (Split a) a
_SplitUsed f (Some u k) = Some <$> f u <*> pure k
_SplitUsed f (All u) = All <$> f u
_SplitUsed _ (None k) = pure $ None k

_SplitKept :: Traversal' (Split a) a
_SplitKept f (Some u k) = Some u <$> f k
_SplitKept _ (All u) = pure $ All u
_SplitKept f (None k) = None <$> f k

align ::
  (Eq n, Ord n, Num n) =>
  Lens' a n ->
  Lens' b n ->
  a ->
  b ->
  (Split a, Split b)
align la lb x y
  | xq == 0 && yq == 0 =
    (None x, None y)
  | xq == 0 = (None x, All y)
  | yq == 0 = (All x, None y)
  | xq == yq = (All x, All y)
  | xq < yq =
    ( All x,
      Some
        (y & lb .~ xq)
        (y & lb .~ diff)
    )
  | otherwise =
    ( Some
        (x & la .~ yq)
        (x & la .~ diff),
      All y
    )
  where
    xq = x ^. la
    yq = y ^. lb
    diff = abs (xq - yq)

data Applied v a b = Applied
  { _value :: v,
    _src :: Split a,
    _dest :: Split b
  }
  deriving (Eq, Ord, Show)

makeLenses ''Applied

nothingApplied :: Default v => a -> b -> Applied v a b
nothingApplied x y = Applied def (None x) (None y)

splits :: Default v => Split a -> Split b -> Applied v a b
splits = Applied def

data Considered a b c = Considered
  { _fromList :: [a],
    _newList :: [b],
    _fromElement :: [c],
    _newElement :: Maybe c
  }
  deriving (Eq, Show)

makeLenses ''Considered

consideredList :: Traversal' (Considered a a c) a
consideredList f Considered {..} =
  Considered <$> traverse f _fromList
    <*> traverse f _newList
    <*> pure _fromElement
    <*> pure _newElement

consideredElements :: Traversal' (Considered a b c) c
consideredElements f Considered {..} =
  Considered _fromList _newList
    <$> traverse f _fromElement
    <*> traverse f _newElement

consideredFrom :: Traversal' (Considered a b a) a
consideredFrom f Considered {..} =
  Considered <$> traverse f _fromList
    <*> pure _newList
    <*> traverse f _fromElement
    <*> pure _newElement

consideredNew :: Traversal' (Considered a c c) c
consideredNew f Considered {..} =
  Considered _fromList
    <$> traverse f _newList
    <*> pure _fromElement
    <*> traverse f _newElement

newConsidered :: Considered a b c
newConsidered =
  Considered
    { _fromList = [],
      _newList = [],
      _fromElement = [],
      _newElement = Nothing
    }

-- Given a list, and an element, determine the following three data:
--
-- - A revised version of the input list, based on that element
-- - Elements derived from the input list that become new outputs
-- - The fragments of the original element
consider ::
  (b -> c -> Applied v b c) ->
  (c -> v -> b -> a) ->
  [b] ->
  c ->
  Considered a b c
consider f mk lst el =
  result & fromList %~ reverse
    & newList %~ reverse
    & fromElement %~ reverse
    & newElement .~ remaining
  where
    (remaining, result) = foldl' go (Just el, newConsidered) lst
    go (Nothing, c) x = (Nothing, c & newList %~ (x :))
    go (Just z, c) x =
      ( _dest ^? _SplitKept,
        c & fromList %~ maybe id ((:) . mk z _value) (_src ^? _SplitUsed)
          & newList %~ maybe id (:) (_src ^? _SplitKept)
          & fromElement %~ maybe id (:) (_dest ^? _SplitUsed)
      )
      where
        Applied {..} = f x z