packages feed

radix-tree-1.0.0.0: test/properties/Test/Zebra/Word/Sample.hs

{-# LANGUAGE RankNTypes #-}

module Test.Zebra.Word.Sample
  ( Sample
  , zero
  , one
  , tiny
  , small
  , medium
  , large

  , mkUnary0
  , mkUnary1
  , mkUnary2

  , mkBinaryL
  , mkEqual
  , mkSubset

  , tipsA
  , mkTipTip
  , tipsB
  , mkTipBin
  ) where

import           No.Set.Word (NoSet)
import qualified No.Set.Word as No
import           Test.Kit
import           Test.Random

import           Data.Foldable (foldl')
import           Data.Function
import qualified Data.List as List
import           System.Random



data Sample = Sample
                No.Color           -- ^ Color of negative infinity in the set
                [(Word, No.Color)] -- ^ Keys in the set (colors are arbitrary)
                [(Word, No.Color)] -- ^ Keys not in the set (colors are arbitrary)
              deriving Show

zero, one :: Sample
zero = Sample No.Black []
         [ (0, No.Black), (5824, No.White), (6183, No.Black), (maxBound, No.White)
         ]

one  = Sample No.White [(6593, No.Black)]
         [ (0   , No.Black), (4905, No.White), (6285, No.Black), (6134    , No.White)
         , (6737, No.Black), (6928, No.White), (7513, No.Black), (maxBound, No.White)
         ]



halve :: [a] -> ([a], [a])
halve (a:b:cs) = let ~(xs, ys) = halve cs
                 in (a:xs, b:ys)
halve a        = (a, [])

color :: Bool -> No.Color
color False = No.Black
color True  = No.White

sample :: RandomGen g => (Word, Word) -> Int -> g -> (Sample, g)
sample r n g0 =
  let ~(c0, g1) = uniform g0

      ~(xs, g2) = list (\g' -> let ~(w, g'') = uniformR r g'
                                   ~(c, _)   = uniform g''

                               in ((w, color c), g'')
                       )
                    n g1

      cs = List.nub $ List.sortBy (compare `on` fst) xs

      ~(as, bs) = halve cs

  in (Sample (color c0) as bs, g2)



-- | Function that fills the space in the \((+\infty, k]\) range with the given color.
type FillL set = Word -> No.Color -> set -> set

type FromList set = No.Color
                    -- ^ Color of positive infinity

                 -> (FillL set -> set -> set)
                    -- ^ Application of every other color.

                 -> set

foldrFromList :: FromList set -> No.Color -> [(Word, No.Color)] -> set
foldrFromList f c xs = f c (\g s0 -> List.foldr (uncurry g) s0 xs)

noFromList :: FromList NoSet
noFromList c f = f No.fillL (No.Mono c)

setFromNo :: Show set => FromList set -> NoSet -> set
setFromNo setFromList no =
  case No.foldl (\z r c -> (r, c) : z) [] no of
    []          -> error "Zebra.Sample: empty NoSet"
    (_, c) : ys -> setFromList c $ \f s -> foldl' (\z (No.Range _ b, x) -> f b x z) s ys

tiny, small, medium, large :: Sample
tiny   = fst $ sample (0x1000, 0x80000) 8    (mkStdGen 0)
small  = fst $ sample (0x1000, 0x80000) 64   (mkStdGen 1)
medium = fst $ sample (0x1000, 0x80000) 512  (mkStdGen 2)
large  = fst $ sample (0x1000, 0x80000) 4096 (mkStdGen 3)



mkUnary0 :: FromList set -> Sample -> [Case () set NoSet]
mkUnary0 setFromList (Sample c xs _) =
  [Case () (foldrFromList setFromList c xs) (foldrFromList noFromList c xs)]

mkUnary1 :: FromList set -> Sample -> [Case (Word, No.Color) set NoSet]
mkUnary1 setFromList (Sample c xs ys) =
  let set = foldrFromList setFromList c xs
      no  = foldrFromList noFromList c xs

  in foldr (\x -> (:) (Case x set no)) [] $
       (:) (0, No.Black) . (:) (maxBound, No.White) $ xs <> ys

mkUnary2 :: FromList set -> Sample -> [Case (No.Range, No.Color) set NoSet]
mkUnary2 setFromList (Sample c xs ys) =
  let set = foldrFromList setFromList c xs
      no  = foldrFromList noFromList c xs

      ~(as, bs) = halve xs
      ~(cs, ds) = halve ys

      ones = fmap (\(a, i) -> (No.UnsafeRange a a, i)) $
               (:) (0, No.White) . (:) (maxBound, No.Black) $ as <> cs

      es = List.nub . List.sortBy (compare `on` fst) $ bs <> ds

      twos = (:) (No.UnsafeRange 0       0x65432 , No.Black)
           . (:) (No.UnsafeRange 0x54321 maxBound, No.White)
           . (:) (No.UnsafeRange 0       maxBound, No.White)
           $ unsafeRanges es

  in foldr (\x -> (:) (Case x set no)) [] $ ones <> twos
  where
    -- | Converts an ascending list of integers into a list of ranges.
    unsafeRanges :: [(Word, No.Color)] -> [(No.Range, No.Color)]
    unsafeRanges ((a, x):(b, _):cs) = (No.UnsafeRange a b, x) : unsafeRanges cs
    unsafeRanges _                  = []



mkBinaryL :: FromList set -> Sample -> [Case (set, NoSet) set NoSet]
mkBinaryL setFromList (Sample c xs ys) =
  let set1 = foldrFromList setFromList c xs
      no1  = foldrFromList noFromList c xs

      set2 = foldrFromList setFromList c ys
      no2  = foldrFromList noFromList c ys

  in [Case (set2, no2) set1 no1]


mkEqual :: FromList set -> Sample -> [Case (set, NoSet) set NoSet]
mkEqual setFromList (Sample c xs _) =
  let set = foldrFromList setFromList c xs
      no  = foldrFromList noFromList c xs

  in [Case (set, no) set no]

mkSubset :: Show set => FromList set -> No.Color -> Sample -> [Case (set, NoSet) set NoSet]
mkSubset setFromList x (Sample c xs ys) =
  let set = foldrFromList setFromList c xs

      no  = foldrFromList noFromList c xs
      no' = foldrFromList noFromList c ys

      noI = No.intersection x no no'

  in [Case (setFromNo setFromList noI, noI) set no]



tipA :: RandomGen g => g -> ((No.Color, Word, No.Color, Word), g)
tipA g0 =
  let ~(c1, g1) = uniform g0
      ~(w1, g2) = uniform g1

      ~(c2, g3) = uniform g2
      ~(w2, g4) = uniform g3

  in ((if c1 then No.White else No.Black, w1, if c2 then No.White else No.Black, w2), g4)

tipsA :: [(No.Color, Word, No.Color, Word)]
tipsA = fst $ list tipA 10000 (mkStdGen 0)

mkTipTip :: FromList set -> No.Color -> Word -> No.Color -> Word -> [Case (set, NoSet) set NoSet]
mkTipTip setFromList c1 w1 c2 w2 =
  let set1 = foldrFromList setFromList c1 [(w1, No.other c1)]
      no1  = foldrFromList noFromList c1 [(w1, No.other c1)]

      set2 = foldrFromList setFromList c2 [(w2, No.other c2)]
      no2  = foldrFromList noFromList c2 [(w2, No.other c2)]

  in [Case (set2, no2) set1 no1]



tipB :: RandomGen g => g -> ((No.Color, Word, Sample), g)
tipB g0 =
  let ~(c1, g1) = uniform g0
      ~(w1, g2) = uniform g1

      ~(s, g3) = sample (0, maxBound) 16 g2

  in ((if c1 then No.White else No.Black, w1, s), g3)

tipsB :: [(No.Color, Word, Sample)]
tipsB = fst $ list tipB 1000 (mkStdGen 0)

mkTipBin :: FromList set -> No.Color -> Word -> Sample -> [Case (set, NoSet) set NoSet]
mkTipBin setFromList c1 w1 (Sample c2 xs ys) =
  let set1 = foldrFromList setFromList c1 [(w1, No.other c1)]
      no1  = foldrFromList noFromList c1 [(w1, No.other c1)]

      set2 = foldrFromList setFromList c2 xs
      no2  = foldrFromList noFromList c2 xs

      (setA, noA, setB, noB) | (_, No.Black):_ <- ys = (set2, no2, set1, no1)
                             | otherwise             = (set1, no1, set2, no2)

  in [Case (setB, noB) setA noA]