module System.Random.Shuffle.FisherYates (
     shuffle
   ) where

import Control.Monad
import Control.Monad.ST
import Data.STRef
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
import System.Random.TF
import System.Random.TF.Instances

-- The Fisher-Yates shuffle, asymptotically optimal for both space and time:
shuffle :: [x] -> IO [x]
shuffle list = do

   initGen <- newTFGen

   pure $ V.toList $ runST $ do
      genVar <- newSTRef initGen

      v <- V.thaw $ V.fromList list
      let vLen = VM.length v

      forM_ [vLen - 1, vLen - 2 .. 1] $ \n -> do
         indexToSwap <- randSTFromZero genVar n
         -- The "unsafe" means it doesn't have bounds checks, which is fine here:
         VM.unsafeSwap v indexToSwap n

      -- This "unsafe" is also safe here:
      V.unsafeFreeze v



-- | Picks from 0 to maxval, inclusive
randSTFromZero :: STRef s TFGen -> Int -> ST s Int
randSTFromZero genVar maxVal = do
   g0 <- readSTRef genVar
   let (x, g1) = randomR (0, maxVal) g0
   writeSTRef genVar g1
   pure x