Skip to content
Snippets Groups Projects
RTree.hs 4.03 KiB
Newer Older
  • Learn to ignore specific revisions
  • chrg's avatar
    chrg committed
    {-# LANGUAGE BlockArguments #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE DeriveFunctor #-}
    {-# LANGUAGE DerivingVia #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE KindSignatures #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE LambdaCase #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE PackageImports #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE RankNTypes #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE TypeSynonymInstances #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE ViewPatterns #-}
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    {- |
    Module: Control.RTree
    -}
    
    chrg's avatar
    chrg committed
    module Control.RTree where
    
    
    chrg's avatar
    chrg committed
    import Control.Applicative (Alternative ((<|>)))
    
    chrg's avatar
    chrg committed
    import Data.Functor
    
    chrg's avatar
    chrg committed
    import Data.Functor.Classes
    
    chrg's avatar
    chrg committed
    import qualified Data.List.NonEmpty as NE
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    import Data.Coerce (coerce)
    import "free" Control.Monad.Free
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    -- | The reduction tree
    data RTreeF f
      = f :<| f
      | Lab String f f
      deriving (Show, Eq, Functor)
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    instance Show1 RTreeF where
      liftShowsPrec = undefined
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    newtype RTree i = RTree {rtreeFree :: Free RTreeF i}
      deriving (Show)
      deriving (Functor, Applicative, Monad) via (Free RTreeF)
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    instance MonadFree RTreeF RTree where
      wrap x = RTree (Free (fmap rtreeFree x))
    
    chrg's avatar
    chrg committed
    
    infixr 3 <|
    
    chrg's avatar
    chrg committed
    infixl 3 |>
    
    (<|) :: (MonadFree RTreeF r) => r i -> r i -> r i
    r1 <| r2 = wrap (r1 :<| r2)
    
    lab :: (MonadFree RTreeF r) => String -> r i -> r i -> r i
    lab l r1 r2 = wrap (Lab l r1 r2)
    
    (|>) :: (MonadFree RTreeF r) => r i -> r i -> r i
    r1 |> r2 = wrap (r2 :<| r1)
    
    foldR :: (RTreeF a -> a) -> RTree a -> a
    foldR fn = coerce $ iter fn
    
    -- | Extract the input from the reducer.
    extract :: RTree i -> i
    extract = foldR \case
      (_ :<| e) -> e
      Lab _ _ e -> e
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    -- | Reduce an input using a monad.
    reduce :: (Alternative m) => (i -> m ()) -> RTree i -> m i
    reduce fn =
      ( foldR \case
          lhs :<| rhs -> lhs <|> rhs
          Lab _ lhs rhs -> lhs <|> rhs
      )
        . fmap (\i -> fn i $> i)
    
    chrg's avatar
    chrg committed
    
    -- | Split the world on a fact. False it does not happen, and True it does happen.
    
    chrg's avatar
    chrg committed
    given :: RTree Bool
    
    chrg's avatar
    chrg committed
    given = pure False <| pure True
    
    
    chrg's avatar
    chrg committed
    {- | A reducer should extract itself
    @
     extract . red = id
    @
    -}
    
    chrg's avatar
    chrg committed
    lawReduceId :: (Eq i) => (i -> RTree i) -> i -> Bool
    
    chrg's avatar
    chrg committed
    lawReduceId red i = extract (red i) == i
    
    
    chrg's avatar
    chrg committed
    -- | Reducing a list one element at a time.
    
    chrg's avatar
    chrg committed
    rList :: [a] -> RTree [a]
    
    chrg's avatar
    chrg committed
    rList = \case
    
    chrg's avatar
    chrg committed
      [] -> pure []
    
    chrg's avatar
    chrg committed
      a : as -> rList as <| (a :) <$> rList as
    
    
    chrg's avatar
    chrg committed
    {- | Binary reduction on the list assumming suffixes all contain eachother:
    @[] < [c] < [b, c] < [a,b,c]@
    -}
    
    chrg's avatar
    chrg committed
    rSuffixList :: [a] -> RTree [a]
    
    chrg's avatar
    chrg committed
    rSuffixList as = do
    
    chrg's avatar
    chrg committed
      res <- exponentialSearch (NE.tails as)
    
    chrg's avatar
    chrg committed
      case res of
        [] -> pure []
        a : as' -> (a :) <$> rSuffixList as'
    
    {- | Given a progression of inputs that are progressively larger, pick the smallest using
    binary search.
    -}
    
    chrg's avatar
    chrg committed
    binarySearch :: NE.NonEmpty i -> RTree i
    
    chrg's avatar
    chrg committed
    binarySearch = \case
    
    chrg's avatar
    chrg committed
      a NE.:| [] -> pure a
      d -> binarySearch l <| binarySearch f
    
    chrg's avatar
    chrg committed
       where
        (NE.fromList -> f, NE.fromList -> l) = NE.splitAt (NE.length d `div` 2) d
    
    
    chrg's avatar
    chrg committed
    {- | Given a progression of inputs that are progressively larger, pick the smallest using
    binary search.
    -}
    exponentialSearch :: NE.NonEmpty i -> RTree i
    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
    
    
    chrg's avatar
    chrg committed
    -- | Given a list of orderd options,  the
    
    chrg's avatar
    chrg committed
    linearSearch :: NE.NonEmpty i -> RTree i
    linearSearch = foldr1 (<|) . fmap pure
    
    chrg's avatar
    chrg committed
    
    -- | Given a list of orderd options,  the
    
    chrg's avatar
    chrg committed
    linearSearch' :: [i] -> RTree (Maybe i)
    
    chrg's avatar
    chrg committed
    linearSearch' is = linearSearch (NE.fromList $ fmap Just is ++ [Nothing])
    
    -- | Given
    
    chrg's avatar
    chrg committed
    ddmin :: [i] -> RTree [i]
    
    chrg's avatar
    chrg committed
    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)