packages feed

set-cover-0.1: example/Random.hs

module Random (intSetFromSetAssigns, shuffle) where

import Mastermind.Utility (attach)

import qualified Math.SetCover.Exact as ESC

import System.Random (StdGen, randomR, )

import qualified Control.Monad.Trans.State as MS
import Control.Applicative ((<$>), )

import qualified Data.Map as Map
import qualified Data.IntSet as IntSet
import qualified Data.Set as Set; import Data.Set (Set, )
import qualified Data.Sequence as Seq; import Data.Sequence (Seq, )
import Data.Tuple.HT (mapSnd, )
import Data.Monoid ((<>), )


{- |
This is a variant of 'ESC.intSetFromSetAssigns'
that includes shuffling of elements.
This way, we can make 'consistentCodesRnd' prefer, say, symbol @z@ to @a@.
-}
intSetFromSetAssigns ::
   (Ord a) =>
   [ESC.Assign label (Set a)] ->
   MS.State StdGen [ESC.Assign label IntSet.IntSet]
intSetFromSetAssigns asns = do
   toMapInt <- mapIntFromSet asns
   let toIntSet = IntSet.fromList . Map.elems . toMapInt
   return $ map (fmap toIntSet) asns

-- cf. ESC
mapIntFromSet ::
   (Ord a) =>
   [ESC.Assign label (Set a)] -> MS.State StdGen (Set a -> Map.Map a Int)
mapIntFromSet asns = do
   mapToInt <-
      fmap (Map.fromList . flip zip [0..]) $
      shuffle $ Set.toList $ ESC.unions $ map ESC.labeledSet asns
   return $ Map.intersection mapToInt . constMap ()

-- SetCover.EnumMap
constMap :: (Ord a) => b -> Set.Set a -> Map.Map a b
constMap a = Map.fromAscList . attach a . Set.toAscList


shuffle :: [a] -> MS.State StdGen [a]
shuffle = shuffleSeq . Seq.fromList

shuffleSeq :: Seq a -> MS.State StdGen [a]
shuffleSeq xs =
   if Seq.null xs
      then return []
      else do
         (y, ys) <- select xs
         (y:) <$> shuffleSeq ys

select :: Seq a -> MS.State StdGen (a, Seq a)
select xs = do
   k <- MS.state $ randomR (0, Seq.length xs - 1)
   case mapSnd Seq.viewl $ Seq.splitAt k xs of
      (_, Seq.EmptyL) -> error "Seq.size must have been zero"
      (ys, z Seq.:< zs) -> return (z, ys <> zs)