
Functionality to choose random, but unique, numbers.

(Unfortunately, it doesn't appear to be nearly as fast as 
 just checking randoms against a map)

\begin{code}

module RndUniq where

import Random
import Data.Set
-- import Debug.Trace

trace x e = e

-- (Integral a, Random a, RandomGen g) => (a,a) -> g -> Int -> [a]
urandomRs :: RandomGen g => (Int,Int) -> g -> Int -> [Int]
urandomRs (mn,mx) g n = 
    let rs = rnds g (mx-mn+1)
    in trace ("** rs= "++show rs) $ (toList $ foldl (add_kth (mn,mx)) empty $ take n rs)

add_kth :: (Int,Int) -> Set Int -> Int -> Set Int
add_kth range set k = trace ("Adding "++show k++"'th to set "++show (toList set)) $ insert (find_value range k set) set

rnds :: RandomGen g => g -> Int -> [Int]
rnds g mx = if mx == 1 then [1] else 
            let (r,g') = randomR (1,mx) g
            in r : rnds g' (mx-1)


-- find the k'th value *not* in a set
-- (the range is inclusive)
find_value :: (Int,Int) -> Int -> Set Int -> Int
find_value (mn,mx) k set = 
   trace ("**"++show (mn,mx)++" : "++show (toList set)++" : "++show k) $
   if k <= 0 || k > mx-mn+1-(fromIntegral $ size set) then error ("k="++show k++" out of range ("++show mn++","++show mx++"),size="++show (size set))
   else if Data.Set.null set then (mn+k-1)
   else let pivot            = mn+(mx-mn) `div` 2
            (left,piv,right) = splitMember pivot set  -- sizes [mn..pivot-1][pivot][pivot+1..mx]
            empty_left       = pivot - mn - (fromIntegral $ size left)
        in trace ("** pivot: "++show pivot++" empty_left: "++show empty_left) $
          case compare (empty_left+1) k of 
           LT -> if piv then find_value (pivot+1,mx) (k-empty_left) right 
                        else find_value (pivot+1,mx) (k-empty_left-1) right 
           EQ -> if piv then find_value (pivot+1,mx) 1 right
                        else trace ("** Found: "++show pivot++"\n") pivot
           GT -> find_value (mn,pivot-1) k left 
        
\end{code}


