{-# LANGUAGE ExistentialQuantification #-} ----------------------------------------------------------------------------- -- Copyright 2010, Open Universiteit Nederland. This file is distributed -- under the terms of the GNU General Public License. For more information, -- see the file "LICENSE.txt", which is included in the distribution. ----------------------------------------------------------------------------- -- | -- Maintainer : bastiaan.heeren@ou.nl -- Stability : provisional -- Portability : portable (depends on ghc) -- -- A collection of general utility functions -- ----------------------------------------------------------------------------- module Common.Utils where import Control.Monad import Data.Char import Data.List import Data.Ratio import System.Random import Test.QuickCheck import qualified Data.Map as M data Some f = forall a . Some (f a) data ShowString = ShowString { fromShowString :: String } deriving (Eq, Ord) instance Show ShowString where show = fromShowString thoroughCheck :: Testable a => a -> IO () thoroughCheck = quickCheckWith $ stdArgs {maxSize = 500, maxSuccess = 500} readInt :: String -> Maybe Int readInt xs | null xs = Nothing | any (not . isDigit) xs = Nothing | otherwise = Just (foldl' (\a b -> a*10+ord b-48) 0 xs) -- ' readM :: (Monad m, Read a) => String -> m a readM s = case reads s of [(a, xs)] | all isSpace xs -> return a _ -> fail ("no read: " ++ s) stringToHex :: String -> Maybe Int stringToHex = foldl op (Just 0) where op (Just i) c = fmap (\j -> i*16 + j) (charToHex c) op Nothing _ = Nothing charToHex :: Char -> Maybe Int charToHex c | isDigit c = return (ord c - 48) | toUpper c `elem` ['A' .. 'F'] = return (ord (toUpper c) - 55) | otherwise = Nothing subsets :: [a] -> [[a]] subsets = foldr op [[]] where op a list = list ++ map (a:) list isSubsetOf :: Eq a => [a] -> [a] -> Bool isSubsetOf xs ys = all (`elem` ys) xs cartesian :: [a] -> [b] -> [(a, b)] cartesian as bs = [ (a, b) | a <- as, b <- bs ] distinct :: Eq a => [a] -> Bool distinct [] = True distinct (x:xs) = all (/=x) xs && distinct xs safeHead :: [a] -> Maybe a safeHead (x:_) = return x safeHead _ = Nothing fixpoint :: Eq a => (a -> a) -> a -> a fixpoint f = stop . iterate f where stop [] = error "Common.Utils: empty list" stop (x:xs) | x == head xs = x | otherwise = stop xs fixpointM :: (Monad m, Eq a) => (a -> m a) -> a -> m a fixpointM f a = do b <- f a if a==b then return a else fixpointM f b splitAtElem :: Eq a => a -> [a] -> Maybe ([a], [a]) splitAtElem c s = case break (==c) s of (xs, _:ys) -> Just (xs, ys) _ -> Nothing splitsWithElem :: Eq a => a -> [a] -> [[a]] splitsWithElem c s = case splitAtElem c s of Just (xs, ys) -> xs : splitsWithElem c ys Nothing -> [s] -- | Use a fixed standard "random" number generator. This generator is -- accessible by calling System.Random.getStdGen useFixedStdGen :: IO () useFixedStdGen = setStdGen (mkStdGen 280578) {- magic number -} fst3 (x, _, _) = x snd3 (_, x, _) = x thd3 (_, _, x) = x commaList :: [String] -> String commaList = concat . intersperse ", " primes :: [Int] primes = rec [2..] where rec [] = error "Common.Utils: empty list" rec (x:xs) = x : rec (filter (\y -> y `mod` x /= 0) xs) putLabel :: String -> IO () putLabel s = let n = (40 - length s) `max` 3 in putStr (s ++ replicate n ' ') reportTest :: String -> Bool -> IO () reportTest s b = putLabel s >> putStrLn (if b then "OK" else "FAILED") instance Show (a -> b) where show _ = "" {- instance Arbitrary Char where arbitrary = let chars = ['a' .. 'z'] ++ ['A' .. 'Z'] in oneof (map return chars) instance CoArbitrary Char where coarbitrary = coarbitrary . ord -} instance (Ord k, Arbitrary k, Arbitrary a) => Arbitrary (M.Map k a) where arbitrary = liftM M.fromList arbitrary instance (Ord k, CoArbitrary k, CoArbitrary a) => CoArbitrary (M.Map k a) where coarbitrary = coarbitrary . M.toList {- -- Generating arbitrary random rational numbers instance Integral a => Arbitrary (Ratio a) where arbitrary = sized (\n -> ratioGen n (n `div` 4)) instance Integral a => CoArbitrary (Ratio a) where coarbitrary r = f (numerator r) . f (denominator r) where f = variant . fromIntegral -} -- | Prevents a bias towards small numbers ratioGen :: Integral a => Int -> Int -> Gen (Ratio a) ratioGen n m = do a <- choose (-n, n) b <- liftM (succ . abs) (choose (-m, m)) c <- choose (1-b, b-1) return (fromIntegral a + (fromIntegral c / fromIntegral b))