packages feed

code-conjure-0.4.2: src/Conjure/Utils.hs

-- |
-- Module      : Conjure.Utils
-- Copyright   : (c) 2021 Rudy Matela
-- License     : 3-Clause BSD  (see the file LICENSE)
-- Maintainer  : Rudy Matela <rudy@matela.com.br>
--
-- An internal module of 'Conjure'.
-- This exports 'Data.List', 'Data.Maybe', 'Data.Function'
-- and a few other simple utitilites.
{-# LANGUAGE CPP #-}
module Conjure.Utils
  ( module Data.List
  , module Data.Function
  , module Data.Maybe
  , module Data.Monoid
  , module Data.Tuple
  , module Data.Typeable

  , count
  , nubOn
  , nubSort
  , iterateUntil
  , mzip
  , groupOn
#if __GLASGOW_HASKELL__ < 710
  , sortOn
#endif
  , takeUntil
  , takeNextWhile
  , takeNextUntil
  , deconstructions
  , isDeconstruction
  , idIO
  , mapHead
  , sets
  , headOr
  , allEqual
  , choices
  , choicesThat
  , filterAnd
  )
where

import Data.List
import Data.Function
import Data.Maybe
import Data.Monoid
import Data.Tuple
import Data.Typeable

import System.IO.Unsafe

allEqual :: Eq a => [a] -> Bool
allEqual []  =  False
allEqual [x]  =  False
allEqual [x,y]  =  x == y
allEqual (x:y:xs)  =  x == y && allEqual (y:xs)

count :: (a -> Bool) -> [a] -> Int
count p  =  length . filter p

nubOn :: Eq b => (a -> b) -> [a] -> [a]
nubOn f  =  nubBy ((==) `on` f)

nubSort :: Ord a => [a] -> [a]
nubSort  =  nnub . sort
  where
  -- linear nub of adjacent values
  nnub [] = []
  nnub [x] = [x]
  nnub (x:xs) = x : nnub (dropWhile (==x) xs)

iterateUntil :: (a -> a -> Bool) -> (a -> a) -> a -> a
iterateUntil (?) f  =  iu
  where
  iu x | x ? fx     =  x
       | otherwise  =  iu fx
    where
    fx  =  f x

mzip :: Monoid a => [a] -> [a] -> [a]
mzip [] []  =  []
mzip [] ys  =  ys
mzip xs []  =  xs
mzip (x:xs) (y:ys)  =  x <> y : mzip xs ys

groupOn :: Eq b => (a -> b) -> [a] -> [[a]]
groupOn f = groupBy ((==) `on` f)

#if __GLASGOW_HASKELL__ < 710
sortOn :: Ord b => (a -> b) -> [a] -> [a]
sortOn f = sortBy (compare `on` f)
#endif

takeUntil :: (a -> Bool) -> [a] -> [a]
takeUntil p  =  takeWhile (not . p)

takeNextWhile :: (a -> a -> Bool) -> [a] -> [a]
takeNextWhile (?)  =  t
  where
  t (x:y:xs) | x ? y  =  x : t (y:xs)
             | otherwise  =  [x]
  t xs  =  xs

takeNextUntil :: (a -> a -> Bool) -> [a] -> [a]
takeNextUntil (?)  =  takeNextWhile (not .: (?))
  where
  (.:)  =  (.) . (.)

deconstructions :: (a -> Bool) -> (a -> a) -> a -> [a]
deconstructions z d x  =  takeUntil z
                       $  iterate d x

-- | The deconstruction is considered valid if it converges
--   for more than half of the given values.
isDeconstruction :: [a] -> (a -> Bool) -> (a -> a) -> Bool
isDeconstruction xs z d  =  count is xs >= l `div` 2
  where
  is x  =  length (take l $ deconstructions z d x) < l
  l  =  length xs

-- | __WARNING:__
--   uses 'unsafePerformIO' and should only be used for debugging!
--
-- > > idIO print 10
-- > 10
-- > 10
idIO :: (a -> IO ()) -> a -> a
idIO action x  =  unsafePerformIO $ do
  action x
  return x

mapHead :: (a -> a) -> [a] -> [a]
mapHead f (x:xs)  =  f x : xs

sets :: [a] -> [[a]]
sets []  =  [[]]
sets (x:xs)  =  map (x:) ss ++ ss
  where
  ss  =  sets xs

headOr :: a -> [a] -> a
headOr x []  =  x
headOr _ (x:xs)  =  x

choices :: [a] -> [(a,[a])]
choices []  =  []
choices (x:xs)  =  (x,xs) : map (mapSnd (x:)) (choices xs)
  where
  mapSnd f (x,y)  =  (x,f y)

choicesThat :: (a -> [a] -> Bool) -> [a] -> [(a,[a])]
choicesThat (?)  =  filter (uncurry (?)) . choices

filterAnd :: (a -> Bool) -> [a] -> ([a],Bool)
filterAnd p xs  =  (xs', and ps)
  where
  xps  =  [(x,p x) | x <- xs]
  xs'  =  [x | (x,True) <- xps]
  ps   =  [p | (_,p) <- xps]