Newer
Older
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Applicative
import Control.Monad.Reader
import Control.Monad.Trans.Maybe
class (Monad m) => MonadReduce l m | m -> l where
split :: Maybe l -> m i -> m i -> m i
(<|) :: (MonadReduce l r) => r i -> r i -> r i
r1 <| r2 = split Nothing r1 r2
splitOn :: (MonadReduce l r) => l -> r i -> r i -> r i
splitOn l = split (Just l)
data RTree l i
= Split (RTree l i) !(RTree l i)
| SplitOn !l (RTree l i) !(RTree l i)
extract :: RTree l i -> i
extract = \case
Split _ rhs -> extract rhs
SplitOn _ _ rhs -> extract rhs
Split lhs rhs ->
Split (lhs >>= f) (rhs >>= f)
SplitOn l lhs rhs ->
SplitOn l (lhs >>= f) (rhs >>= f)
instance MonadReduce l (RTree l) where
split = \case
Just n -> SplitOn n
Nothing -> Split
(Split lhs rhs) -> (checkgo lhs <|> go rhs)
(SplitOn _ lhs rhs) -> (checkgo lhs <|> go rhs)
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
checkgo rt = p (extract rt) *> go rt
{-# SPECIALIZE reduce :: (i -> MaybeT IO ()) -> RTree l i -> MaybeT IO i #-}
type Valuation l = Map.Map l Bool
extractL :: (Ord l) => Valuation l -> RTree l i -> i
extractL v = \case
Split _ rhs -> extractL v rhs
SplitOn l lhs rhs -> case Map.lookup l v of
Just False -> extractL v lhs
_ -> extractL v rhs
Done i -> i
reduceL
:: forall m l i
. (Alternative m, Ord l)
=> (Valuation l -> i -> m ())
-> Valuation l
-> RTree l i
-> m i
reduceL p = checkgo
where
checkgo v r = p v (extractL v r) *> go v r
go v = \case
Done i -> pure i
SplitOn l lhs rhs -> case Map.lookup l v of
Just True -> checkgo v rhs
Just False -> checkgo v lhs
Nothing -> checkgo (Map.insert l False v) lhs <|> go (Map.insert l True v) rhs
Split lhs rhs -> (checkgo v lhs <|> go v rhs)
{-# INLINE reduceL #-}
data ReState l = ReState ![Bool] !(Valuation l)
{- | A faster version of the RTree which simply reruns the reducer program instead
of building a tree.
-}
newtype IORTree l i = IORTree {runIORTree :: IORef (ReState l) -> IO i}
deriving (Functor, Applicative, Monad) via (ReaderT (IORef (ReState l)) IO)
instance (Ord l) => MonadReduce l (IORTree l) where
split ml r1 r2 = IORTree \ref -> do
test <- case ml of
Nothing -> do
atomicModifyIORef'
ref
( \case
ReState (a : as) v -> (ReState as v, a)
ReState [] v -> (ReState [] v, False)
)
Just l -> do
atomicModifyIORef'
ref
( \case
ReState as v@(Map.lookup l -> Just x) -> (ReState as v, not x)
ReState (a : as) v -> (ReState as (Map.insert l (not a) v), a)
ReState [] v -> (ReState [] (Map.insert l True v), False)
)
if test
:: forall m l i
. (MonadIO m, Ord l)
=> (Valuation l -> i -> MaybeT m ())
-> Valuation l
where
go pth = do
ref <- liftIO $ newIORef (ReState pth v)
i <- liftIO $ ext ref
ReState r v' <- liftIO $ readIORef ref
case r of
[] -> do
t <- isJust <$> runMaybeT (p v' i)
if t
then go (pth <> [True])
else go (pth <> [False])
_
| null pth ->
pure Nothing
| otherwise ->
pure (Just i)
-- Combinators
type MRTree l = MaybeT (RTree l)
instance (MonadReduce l m) => MonadReduce l (MaybeT m) where
split m (MaybeT lhs) (MaybeT rhs) = MaybeT (split m lhs rhs)
-- | Given a list of item try to remove each of them the list.
collect :: (MonadReduce l m) => (a -> MaybeT m b) -> [a] -> m [b]
collect fn = fmap catMaybes . traverse (runMaybeT . fn)
{-# INLINE collect #-}
collectNonEmpty' :: (MonadReduce l m) => (a -> MaybeT m b) -> [a] -> MaybeT m [b]
collectNonEmpty' fn as =
NE.toList <$> collectNonEmpty fn as
{-# INLINE collectNonEmpty' #-}
collectNonEmpty :: (MonadReduce l m) => (a -> MaybeT m b) -> [a] -> MaybeT m (NE.NonEmpty b)
collectNonEmpty fn as = do
as' <- lift . fmap catMaybes . traverse (runMaybeT . fn) $ as
MaybeT . pure $ NE.nonEmpty as'
{-# INLINE collectNonEmpty #-}
-- | Split the world on a fact. False it does not happen, and True it does happen.
{- | A reducer should extract itself
@
extract . red = id
@
-}
{- | Binary reduction on the list assumming suffixes all contain eachother:
@[] < [c] < [b, c] < [a,b,c]@
-}
case res of
[] -> pure []
a : as' -> (a :) <$> rSuffixList as'
{- | Given a progression of inputs that are progressively larger, pick the smallest using
binary search.
-}
where
(NE.fromList -> f, NE.fromList -> l) = NE.splitAt (NE.length d `div` 2) d
{- | Given a progression of inputs that are progressively larger, pick the smallest using
binary search.
-}
exponentialSearch = go 1
where
go n = \case
d
| n >= NE.length d -> binarySearch d
| otherwise -> go (n * 2) l <| binarySearch f
where
(NE.fromList -> f, NE.fromList -> l) = NE.splitAt n d
nonEmptyOr :: String -> [a] -> NE.NonEmpty a
nonEmptyOr msg ls = case NE.nonEmpty ls of
Just a -> a
Nothing -> error msg
linearSearch' is = linearSearch (NE.fromList $ fmap Just is ++ [Nothing])
-- | Given
ddmin = \case
[] -> pure []
[a] -> pure [a]
as -> go 2 as
where
go n lst
| n' <= 0 = pure lst
| otherwise = do
r <- linearSearch' (partitions n' lst ++ composites n' lst)
case r of
Nothing -> go (n * 2) lst <| pure lst -- (for efficiency :D)
Just lst' -> ddmin lst'
where
n' = length lst `div` n
partitions n lst =
case lst of
[] -> []
_ -> let (h, r) = splitAt n lst in h : partitions n r
composites n lst =
case lst of
[] -> []
_ -> let (h, r) = splitAt n lst in r : fmap (h ++) (composites n r)