{-#LANGUAGE ScopedTypeVariables#-}
module Utils where
import Control.Exception (SomeException, bracket, bracket_, evaluate, try)
import System.IO.Unsafe (unsafePerformIO)
import Debug.Trace (trace)
import Data.Bits ((.&.))
import Data.Char (chr)
import Data.String (IsString, fromString)
import System.Random (Random (..), RandomGen)
import Test.QuickCheck hiding ((.&.))
import Test.QuickCheck.Monadic (assert, monadicIO, run)
import qualified Data.ByteString as B
import Pipes.Text.Internal
-- Ensure that two potentially bottom values (in the sense of crashing
-- for some inputs, not looping infinitely) either both crash, or both
-- give comparable results for some input.
(=^=) :: (Eq a, Show a) => a -> a -> Bool
i =^= j = unsafePerformIO $ do
x <- try (evaluate i)
y <- try (evaluate j)
case (x,y) of
(Left (_ :: SomeException), Left (_ :: SomeException))
-> return True
(Right a, Right b) -> return (a == b)
e -> trace ("*** Divergence: " ++ show e) return False
infix 4 =^=
{-# NOINLINE (=^=) #-}
-- Do two functions give the same answer?
eq :: (Eq a, Show a) => (t -> a) -> (t -> a) -> t -> Bool
eq a b s = a s =^= b s
-- What about with the RHS packed?
-- eqP :: (Eq a, Show a, Stringy s) =>
-- (String -> a) -> (s -> a) -> String -> Word8 -> Bool
-- eqP f g s w = eql "orig" (f s) (g t) &&
-- eql "mini" (f s) (g mini) &&
-- eql "head" (f sa) (g ta) &&
-- eql "tail" (f sb) (g tb)
-- where t = packS s
-- mini = packSChunkSize 10 s
-- (sa,sb) = splitAt m s
-- (ta,tb) = splitAtS m t
-- l = length s
-- m | l == 0 = n
-- | otherwise = n `mod` l
-- n = fromIntegral w
-- eql d a b
-- | a =^= b = True
-- | otherwise = trace (d ++ ": " ++ show a ++ " /= " ++ show b) False
instance Arbitrary B.ByteString where
arbitrary = B.pack `fmap` arbitrary
genUnicode :: IsString a => Gen a
genUnicode = fmap fromString string where
string = sized $ \n ->
do k <- choose (0,n)
sequence [ char | _ <- [1..k] ]
excluding :: [a -> Bool] -> Gen a -> Gen a
excluding bad gen = loop
where
loop = do
x <- gen
if or (map ($ x) bad)
then loop
else return x
reserved = [lowSurrogate, highSurrogate, noncharacter]
lowSurrogate c = c >= 0xDC00 && c <= 0xDFFF
highSurrogate c = c >= 0xD800 && c <= 0xDBFF
noncharacter c = masked == 0xFFFE || masked == 0xFFFF
where
masked = c .&. 0xFFFF
ascii = choose (0,0x7F)
plane0 = choose (0xF0, 0xFFFF)
plane1 = oneof [ choose (0x10000, 0x10FFF)
, choose (0x11000, 0x11FFF)
, choose (0x12000, 0x12FFF)
, choose (0x13000, 0x13FFF)
, choose (0x1D000, 0x1DFFF)
, choose (0x1F000, 0x1FFFF)
]
plane2 = oneof [ choose (0x20000, 0x20FFF)
, choose (0x21000, 0x21FFF)
, choose (0x22000, 0x22FFF)
, choose (0x23000, 0x23FFF)
, choose (0x24000, 0x24FFF)
, choose (0x25000, 0x25FFF)
, choose (0x26000, 0x26FFF)
, choose (0x27000, 0x27FFF)
, choose (0x28000, 0x28FFF)
, choose (0x29000, 0x29FFF)
, choose (0x2A000, 0x2AFFF)
, choose (0x2B000, 0x2BFFF)
, choose (0x2F000, 0x2FFFF)
]
plane14 = choose (0xE0000, 0xE0FFF)
planes = [ascii, plane0, plane1, plane2, plane14]
char = chr `fmap` excluding reserved (oneof planes)