packages feed

falsify-0.4.0: src/Data/Falsify/Permutation.hs

-- | Permutations
--
-- Intended for qualified import.
--
-- > import Data.Falsify.Permutation (Permutation)
-- > import qualified Data.Falsify.Permutation as Permutation
module Data.Falsify.Permutation (
    Permutation -- opaque
  , invariant
  , toSwaps
    -- * Construction
  , identity
  , fromSwaps
    -- * Properties
  , size
    -- * Using permutations
  , apply
  ) where

import Control.Monad
import Control.Monad.ST

import qualified Data.Vector         as V
import qualified Data.Vector.Mutable as VM

{-------------------------------------------------------------------------------
  Definition
-------------------------------------------------------------------------------}

-- | Permutation is a sequence of swaps
newtype Permutation = Permutation {
      -- | Individual swaps executed by this permutation
      toSwaps :: [(Word, Word)]
    }
  deriving stock (Show)

-- | Permutation invariant
--
-- For every swap @(i, j)@ in the permutation we must have @i > j@.
invariant :: Permutation -> Bool
invariant = all checkSwap . toSwaps
  where
    checkSwap :: (Word, Word) -> Bool
    checkSwap (i, j) = i > j

{-------------------------------------------------------------------------------
  Construction
-------------------------------------------------------------------------------}

-- | Identity permutation
identity :: Permutation
identity = fromSwaps []

-- | From swaps
--
-- Any identity swaps are filtered out.
fromSwaps :: [(Word, Word)] -> Permutation
fromSwaps = Permutation . filter (\(i, j) -> i /= j)

{-------------------------------------------------------------------------------
  Properties
-------------------------------------------------------------------------------}

-- | Number of swaps
size :: Permutation -> Int
size = length . toSwaps

{-------------------------------------------------------------------------------
  Using permutations
-------------------------------------------------------------------------------}

-- | Apply permutation
apply :: Permutation -> [a] -> [a]
apply (Permutation p) xs =
    V.toList $ V.modify (forM_ (map conv p) . swap) (V.fromList xs)
  where
    swap :: V.MVector s a -> (Int, Int) -> ST s ()
    swap vec (i, j) = do
        x <- VM.read vec i
        y <- VM.read vec j
        VM.write vec i y
        VM.write vec j x

    conv :: (Word, Word) -> (Int, Int)
    conv (i, j) = (fromIntegral i, fromIntegral j)