-- | Braids. See eg. -- -- -- Based on: -- -- * Joan S. Birman, Tara E. Brendle: BRAIDS - A SURVEY -- -- -- -- Note: This module GHC 7.8, since we use type-level naturals -- to parametrize the 'Braid' type. -- {-# LANGUAGE CPP, BangPatterns, ScopedTypeVariables, ExistentialQuantification, DataKinds, KindSignatures, Rank2Types, TypeOperators, TypeFamilies, StandaloneDeriving #-} module Math.Combinat.Groups.Braid where -------------------------------------------------------------------------------- import Data.Proxy import GHC.TypeLits import Control.Monad import Data.List ( mapAccumL , foldl' ) import Data.Array.Unboxed import Data.Array.ST import Data.Array.IArray import Data.Array.MArray import Data.Array.Unsafe import Data.Array.Base import Control.Monad.ST import System.Random import Math.Combinat.ASCII import Math.Combinat.Sign import Math.Combinat.Helper import Math.Combinat.TypeLevel import Math.Combinat.Numbers.Series import Math.Combinat.Permutations ( Permutation(..) ) import qualified Math.Combinat.Permutations as P -------------------------------------------------------------------------------- -- * Artin generators -- | A standard Artin generator of a braid: @Sigma i@ represents twisting -- the neighbour strands @i@ and @(i+1)@, such that strand @i@ goes /under/ strand @(i+1)@. -- -- Note: The strands are numbered @1..n@. data BrGen = Sigma !Int -- ^ @i@ goes under @(i+1)@ | SigmaInv !Int -- ^ @i@ goes above @(i+1)@ deriving (Eq,Ord,Show) -- | The strand (more precisely, the first of the two strands) the generator twistes brGenIdx :: BrGen -> Int brGenIdx g = case g of Sigma i -> i SigmaInv i -> i brGenSign :: BrGen -> Sign brGenSign g = case g of Sigma _ -> Plus SigmaInv _ -> Minus brGenSignIdx :: BrGen -> (Sign,Int) brGenSignIdx g = case g of Sigma i -> (Plus ,i) SigmaInv i -> (Minus,i) -- | The inverse of a braid generator invBrGen :: BrGen -> BrGen invBrGen g = case g of Sigma i -> SigmaInv i SigmaInv i -> Sigma i -------------------------------------------------------------------------------- -- * The braid type -- | The braid group @B_n@ on @n@ strands. -- The number @n@ is encoded as a type level natural in the type parameter. -- -- Braids are represented as words in the standard generators and their -- inverses. newtype Braid (n :: Nat) = Braid [BrGen] deriving (Show) -- | The number of strands in the braid numberOfStrands :: KnownNat n => Braid n -> Int numberOfStrands = fromInteger . natVal . braidProxy where braidProxy :: Braid n -> Proxy n braidProxy _ = Proxy -- | Sometimes we want to hide the type-level parameter @n@, for example when -- dynamically creating braids whose size is known only at runtime. data SomeBraid = forall n. KnownNat n => SomeBraid (Braid n) someBraid :: Int -> (forall (n :: Nat). KnownNat n => Braid n) -> SomeBraid someBraid n polyBraid = case snat of SomeNat pxy -> SomeBraid (asProxyTypeOf1 polyBraid pxy) where snat = case someNatVal (fromIntegral n :: Integer) of Just sn -> sn Nothing -> error "someBraid: input is not a natural number" withSomeBraid :: SomeBraid -> (forall n. KnownNat n => Braid n -> a) -> a withSomeBraid sbraid f = case sbraid of SomeBraid braid -> f braid mkBraid :: (forall n. KnownNat n => Braid n -> a) -> Int -> [BrGen] -> a mkBraid f n w = y where sb = someBraid n (Braid w) y = withSomeBraid sb f withBraid :: Int -> (forall (n :: Nat). KnownNat n => Braid n) -> (forall (n :: Nat). KnownNat n => Braid n -> a) -> a withBraid n polyBraid f = case snat of SomeNat pxy -> f (asProxyTypeOf1 polyBraid pxy) where snat = case someNatVal (fromIntegral n :: Integer) of Just sn -> sn Nothing -> error "withBraid: input is not a natural number" -------------------------------------------------------------------------------- braidWord :: Braid n -> [BrGen] braidWord (Braid gs) = gs braidWordLength :: Braid n -> Int braidWordLength (Braid gs) = length gs -- | Embeds a smaller braid group into a bigger braid group extend :: (n1 <= n2) => Braid n1 -> Braid n2 extend (Braid gs) = Braid gs -- | Apply \"free reduction\" to the word, that is, iteratively remove @sigma_i sigma_i^-1@ pairs. -- The resulting braid is clearly equivalent to the original. freeReduceBraidWord :: Braid n -> Braid n freeReduceBraidWord (Braid orig) = Braid (loop orig) where loop w = case reduceStep w of Nothing -> w Just w' -> loop w' reduceStep :: [BrGen] -> Maybe [BrGen] reduceStep = go False where go !changed w = case w of (Sigma x : SigmaInv y : rest) | x==y -> go True rest (SigmaInv x : Sigma y : rest) | x==y -> go True rest (this : rest) -> liftM (this:) $ go changed rest _ -> if changed then Just w else Nothing -------------------------------------------------------------------------------- -- * Some specific braids -- | The braid generator @sigma_i@ as a braid sigma :: KnownNat n => Int -> Braid (n :: Nat) sigma k = braid where braid = if k > 0 && k < numberOfStrands braid then Braid [Sigma k] else error "sigma: braid generator index out of range" -- | The braid generator @sigma_i^(-1)@ as a braid sigmaInv :: KnownNat n => Int -> Braid (n :: Nat) sigmaInv k = braid where braid = if k > 0 && k < numberOfStrands braid then Braid [SigmaInv k] else error "sigma: braid generator index out of range" -- | @doubleSigma s t@ (for s Int -> Int -> Braid (n :: Nat) doubleSigma s t = braid where n = numberOfStrands braid braid | s < 1 || s > n = error "doubleSigma: s index out of range" | t < 1 || t > n = error "doubleSigma: t index out of range" | s >= t = error "doubleSigma: s >= t" | otherwise = Braid $ [ Sigma i | i<-[t-1,t-2..s] ] ++ [ SigmaInv i | i<-[s+1..t-1] ] -- | @positiveWord [2,5,1]@ is shorthand for the word @sigma_2*sigma_5*sigma_1@. positiveWord :: KnownNat n => [Int] -> Braid (n :: Nat) positiveWord idxs = braid where braid = Braid (map gen idxs) n = numberOfStrands braid gen i = if i>0 && i Braid n halfTwist = braid where braid = Braid $ map Sigma $ _halfTwist n n = numberOfStrands braid -- | The untyped version of 'halfTwist' _halfTwist :: Int -> [Int] _halfTwist n = gens where gens = concat [ sub k | k<-[1..n-1] ] sub k = [ j | j<-[n-1,n-2..k] ] -- | Synonym for 'halfTwist' theGarsideBraid :: KnownNat n => Braid n theGarsideBraid = halfTwist -- | The inner automorphism defined by @tau(X) = Delta^-1 X Delta@, -- where @Delta@ is the positive half-twist. -- -- This sends each generator @sigma_j@ to @sigma_(n-j)@. -- tau :: KnownNat n => Braid n -> Braid n tau braid@(Braid gens) = Braid (map f gens) where n = numberOfStrands braid f (Sigma i) = Sigma (n-i) f (SigmaInv i) = SigmaInv (n-i) -- | The involution @tau@ on permutations (permutation braids) -- tauPerm :: Permutation -> Permutation tauPerm (Permutation arr) = Permutation $ listArray (1,n) [ (n+1) - arr!(n-i) | i<-[0..n-1] ] where (1,n) = bounds arr -------------------------------------------------------------------------------- -- * Group operations -- | The trivial braid identity :: Braid n identity = Braid [] -- | The inverse of a braid. Note: we do not perform reduction here, -- as a word is reduced if and only if its inverse is reduced. inverse :: Braid n -> Braid n inverse = Braid . reverse . map invBrGen . braidWord -- | Composes two braids, doing free reduction on the result -- (that is, removing @(sigma_k * sigma_k^-1)@ pairs@) compose :: Braid n -> Braid n -> Braid n compose (Braid gs) (Braid hs) = freeReduceBraidWord $ Braid (gs++hs) composeMany :: [Braid n] -> Braid n composeMany = freeReduceBraidWord . Braid . concat . map braidWord -- | Composes two braids without doing any reduction. composeDontReduce :: Braid n -> Braid n -> Braid n composeDontReduce (Braid gs) (Braid hs) = Braid (gs++hs) -------------------------------------------------------------------------------- -- * Braid permutations -- | A braid is pure if its permutation is trivial isPureBraid :: KnownNat n => Braid n -> Bool isPureBraid braid = (braidPermutation braid == P.identity n) where n = numberOfStrands braid -- | Returns the left-to-right permutation associated to the braid. -- We follow the strands /from the left to the right/ (or from the top to the -- bottom), and return the permutation taking the left side to the right side. -- -- This is compatible with /right/ (standard) action of the permutations: -- @permuteRight (braidPermutationRight b1)@ corresponds to the left-to-right -- permutation of the strands; also: -- -- > (braidPermutation b1) `multiply` (braidPermutation b2) == braidPermutation (b1 `compose` b2) -- -- Writing the right numbering of the strands below the left numbering, -- we got the two-line notation of the permutation. -- braidPermutation :: KnownNat n => Braid n -> Permutation braidPermutation braid@ (Braid gens) = perm where n = numberOfStrands braid perm = _braidPermutation n (map brGenIdx gens) -- | This is an untyped version of 'braidPermutation' _braidPermutation :: Int -> [Int] -> Permutation _braidPermutation n idxs = Permutation (runSTUArray action) where action :: forall s. ST s (STUArray s Int Int) action = do arr <- newArray_ (1,n) forM_ [1..n] $ \i -> writeArray arr i i worker arr idxs return arr worker arr = go where go [] = return arr go (i:is) = do a <- readArray arr i b <- readArray arr (i+1) writeArray arr i b writeArray arr (i+1) a go is -------------------------------------------------------------------------------- -- * Permutation braids -- | A positive braid word contains only positive (@Sigma@) generators. isPositiveBraidWord :: KnownNat n => Braid n -> Bool isPositiveBraidWord (Braid gs) = all (isPlus . brGenSign) gs -- | A /permutation braid/ is a positive braid where any two strands cross -- at most one, and /positively/. -- isPermutationBraid :: KnownNat n => Braid n -> Bool isPermutationBraid braid = isPositiveBraidWord braid && crosses where crosses = and [ check i j | i<-[1..n-1], j<-[i+1..n] ] check i j = zeroOrOne (lkMatrix ! (i,j)) zeroOrOne a = (a==1 || a==0) lkMatrix = linkingMatrix braid n = numberOfStrands braid -- | Untyped version of 'isPermutationBraid' for positive words. _isPermutationBraid :: Int -> [Int] -> Bool _isPermutationBraid n gens = crosses where crosses = and [ check i j | i<-[1..n-1], j<-[i+1..n] ] check i j = zeroOrOne (lkMatrix ! (i,j)) zeroOrOne a = (a==1 || a==0) lkMatrix = _linkingMatrix n $ map Sigma gens -- | For any permutation this functions returns a /permutation braid/ realizing -- that permutation. Note that this is not unique, so we make an arbitrary choice -- (except for the permutation @[n,n-1..1]@ reversing the order, in which case -- the result must be the half-twist braid). -- -- The resulting braid word will have a length at most @choose n 2@ (and will have -- that length only for the permutation @[n,n-1..1]@) -- -- > braidPermutationRight (permutationBraid perm) == perm -- > isPermutationBraid (permutationBraid perm) == True -- permutationBraid :: KnownNat n => Permutation -> Braid n permutationBraid perm = braid where n1 = numberOfStrands braid n2 = P.permutationSize perm braid = if n1 == n2 then Braid (map Sigma $ _permutationBraid perm) else error $ "permutationBraid: incompatible n: " ++ show n1 ++ " vs. " ++ show n2 -- | Untyped version of 'permutationBraid' _permutationBraid :: Permutation -> [Int] _permutationBraid = concat . _permutationBraid' -- | Returns the individual \"phases\" of the a permutation braid realizing the -- given permutation. _permutationBraid' :: Permutation -> [[Int]] _permutationBraid' perm@(Permutation arr) = runST action where (1,n) = bounds arr action :: forall s. ST s [[Int]] action = do -- cfwd = the current state of strands : cfwd!j = where is strand #j now? -- cinv = the inverse of that permutation : cinv!i = which strand is on the #i position now? cfwd <- newArray_ (1,n) :: ST s (STUArray s Int Int) cinv <- newArray_ (1,n) :: ST s (STUArray s Int Int) forM_ [1..n] $ \j -> do writeArray cfwd j j writeArray cinv j j let doSwap i = do a <- readArray cinv i b <- readArray cinv (i+1) writeArray cinv i b writeArray cinv (i+1) a u <- readArray cfwd a v <- readArray cfwd b writeArray cfwd a v writeArray cfwd b u -- at the k-th phase, we move the (inv!k)-th strand, which is the k-th strand /on the RHS/, to correct position. let worker phase | phase >= n = return [] | otherwise = do let tgt = (arr ! phase) src <- readArray cfwd tgt let this = [src-1,src-2..phase] mapM_ doSwap $ this rest <- worker (phase+1) return (this:rest) worker 1 -- | We compute the linking numbers between all pairs of strands: -- -- > linkingMatrix braid ! (i,j) == strandLinking braid i j -- linkingMatrix :: KnownNat n => Braid n -> UArray (Int,Int) Int linkingMatrix braid@(Braid gens) = _linkingMatrix (numberOfStrands braid) gens where -- | Untyped version of 'linkingMatrix' _linkingMatrix :: Int -> [BrGen] -> UArray (Int,Int) Int _linkingMatrix n gens = runSTUArray action where action :: forall s. ST s (STUArray s (Int,Int) Int) action = do perm <- newArray_ (1,n) :: ST s (STUArray s Int Int) forM_ [1..n] $ \i -> writeArray perm i i let doSwap :: Int -> ST s () doSwap i = do a <- readArray perm i b <- readArray perm (i+1) writeArray perm i b writeArray perm (i+1) a mat <- newArray ((1,1),(n,n)) 0 :: ST s (STUArray s (Int,Int) Int) let doAdd :: Int -> Int -> Int -> ST s () doAdd i j pm1 = do x <- readArray mat (i,j) writeArray mat (i,j) (x+pm1) writeArray mat (j,i) (x+pm1) forM_ gens $ \g -> do let (sgn,k) = brGenSignIdx g u <- readArray perm k v <- readArray perm (k+1) doAdd u v (signValue sgn) doSwap k return mat -- | The linking number between two strands numbered @i@ and @j@ -- (numbered such on the /left/ side). strandLinking :: KnownNat n => Braid n -> Int -> Int -> Int strandLinking braid@(Braid gens) i0 j0 | i0 < 1 || i0 > n = error $ "strandLinkingNumber: invalid strand index i: " ++ show i0 | j0 < 1 || j0 > n = error $ "strandLinkingNumber: invalid strand index j: " ++ show j0 | i0 == j0 = 0 | otherwise = go i0 j0 gens where n = numberOfStrands braid go !i !j [] = 0 go !i !j (g:gs) | i == k && j == k+1 = s + go (i+1) (j-1) gs | j == k && i == k+1 = s + go (i-1) (j+1) gs | i == k = go (i+1) j gs | i == k+1 = go (i-1) j gs | j == k = go i (j+1) gs | j == k+1 = go i (j-1) gs | otherwise = go i j gs where (sgn,k) = brGenSignIdx g s = signValue sgn -------------------------------------------------------------------------------- -- * Growth -- | Bronfman's recursive formula for the reciprocial of the growth function -- of /positive/ braids. It was already known (by Deligne) that these generating functions -- are reciprocials of polynomials; Bronfman [1] gave a recursive formula for them. -- -- > let count n l = length $ nub $ [ braidNormalForm w | w <- allPositiveBraidWords n l ] -- > let convertPoly (1:cs) = zip (map negate cs) [1..] -- > pseries' (convertPoly $ bronfmanH n) == expandBronfmanH n == [ count n l | l <- [0..] ] -- -- * [1] Aaron Bronfman: Growth functions of a class of monoids. Preprint, 2001 -- bronfmanH :: Int -> [Int] bronfmanH n = bronfmanHsList !! n -- | An infinite list containing the Bronfman polynomials: -- -- > bronfmanH n = bronfmanHsList !! n -- bronfmanHsList :: [[Int]] bronfmanHsList = list where list = map go [0..] go 0 = [1] go n = sumSeries [ sgn i $ replicate (choose2 i) 0 ++ list !! (n-i) | i<-[1..n] ] sgn i = if odd i then id else map negate choose2 k = div (k*(k-1)) 2 -- | Expands the reciprocial of @H(n)@ into an infinite power series, -- giving the growth function of the positive braids on @n@ strands. expandBronfmanH :: Int -> [Int] expandBronfmanH n = pseries' (convertPoly $ bronfmanH n) where convertPoly (1:cs) = zip (map negate cs) [1..] -------------------------------------------------------------------------------- -- * ASCII diagram instance KnownNat n => DrawASCII (Braid n) where ascii = horizBraidASCII -- | Horizontal braid diagram, drawn from left to right, -- with strands numbered from the bottom to the top horizBraidASCII :: KnownNat n => Braid n -> ASCII horizBraidASCII = horizBraidASCII' True -- | Horizontal braid diagram, drawn from left to right. -- The boolean flag indicates whether to flip the strands -- vertically ('True' means bottom-to-top, 'False' means top-to-bottom) horizBraidASCII' :: KnownNat n => Bool -> Braid n -> ASCII horizBraidASCII' flipped braid@(Braid gens) = final where n = numberOfStrands braid final = vExtendWith VTop 1 $ hCatTop allBlocks allBlocks = prelude ++ middleBlocks ++ epilogue prelude = [ numberBlock , spaceBlock , beginEndBlock ] epilogue = [ beginEndBlock , spaceBlock , numberBlock' ] middleBlocks = map block gens block g = case g of Sigma i -> block' i $ if flipped then over else under SigmaInv i -> block' i $ if flipped then under else over block' i middle = asciiFromLines $ drop 2 $ concat $ replicate a horiz ++ [space3, middle] ++ replicate b horiz where (a,b) = if flipped then (n-i-1,i-1) else (i-1,n-i-1) -- cycleN :: Int -> [a] -> [a] -- cycleN n = concat . replicate n spaceBlock = transparentBox (1,n*3-2) beginEndBlock = asciiFromLines $ drop 2 $ concat $ replicate n horiz numberBlock = mkNumbers [1..n] numberBlock' = mkNumbers $ P.fromPermutation $ braidPermutation braid mkNumbers :: [Int] -> ASCII mkNumbers list = vCatWith HRight (VSepSpaces 2) $ map asciiShow $ (if flipped then reverse else id) $ list under = [ "\\ /" , " / " , "/ \\" ] over = [ "\\ /" , " \\ " , "/ \\" ] horiz = [ " " , " " , "___" ] space3 = [ " " , " " , " " ] -------------------------------------------------------------------------------- {- this is unusably ugly and vertically loooong -- | Vertical braid diagram, drawn from the top to the bottom. -- Strands are numbered from the left to the right. -- -- Writing down the strand numbers from the top and and the bottom -- gives the two-line notation of the permutation realized by the braid. -- verticalBraidASCII :: KnownNat n => Braid n -> ASCII verticalBraidASCII braid@(Braid gens) = final where n = numberOfStrands braid final = hExtendWith HLeft 1 $ vCatLeft allBlocks allBlocks = prelude ++ middleBlocks ++ epilogue prelude = [ numberBlock , spaceBlock , beginEndBlock ] epilogue = [ beginEndBlock , spaceBlock , numberBlock' ] middleBlocks = map block gens block g = case g of Sigma i -> block' i under SigmaInv i -> block' i over block' i middle = asciiFromLines (map f middle) where f xs = drop 1 $ concat $ h (i-1) ++ [" ",xs] ++ h (n-i-1) h k = replicate k " |" spaceBlock = transparentBox (n*3-2,1) beginEndBlock = asciiFromLines $ replicate 3 $ drop 1 $ concat (replicate n " |") numberBlock = mkNumbers [1..n] numberBlock' = mkNumbers $ P.fromPermutation $ braidPermutation braid mkNumbers :: [Int] -> ASCII mkNumbers list = asciiFromString (drop 1 $ concatMap show3 list) show3 k = let s = show k in replicate (3-length s) ' ' ++ s under = [ "\\ /" , " / " , "/ \\" ] over = [ "\\ /" , " \\ " , "/ \\" ] -} -------------------------------------------------------------------------------- -- * List of all words -- | All positive braid words of the given length allPositiveBraidWords :: KnownNat n => Int -> [Braid n] allPositiveBraidWords l = braids where n = numberOfStrands (head braids) braids = map Braid $ _allPositiveBraidWords n l -- | All braid words of the given length allBraidWords :: KnownNat n => Int -> [Braid n] allBraidWords l = braids where n = numberOfStrands (head braids) braids = map Braid $ _allBraidWords n l -- | Untyped version of 'allPositiveBraidWords' _allPositiveBraidWords :: Int -> Int -> [[BrGen]] _allPositiveBraidWords n = go where go 0 = [[]] go k = [ Sigma i : rest | i<-[1..n-1] , rest <- go (k-1) ] -- | Untyped version of 'allBraidWords' _allBraidWords :: Int -> Int -> [[BrGen]] _allBraidWords n = go where go 0 = [[]] go k = [ gen : rest | gen <- gens , rest <- go (k-1) ] gens = concat [ [ Sigma i , SigmaInv i ] | i<-[1..n-1] ] -------------------------------------------------------------------------------- -- * Random braids -- | Random braid word of the given length randomBraidWord :: (RandomGen g, KnownNat n) => Int -> g -> (Braid n, g) randomBraidWord len g = (braid, g') where braid = Braid w n = numberOfStrands braid (w,g') = _randomBraidWord n len g -- | Random /positive/ braid word of the given length randomPositiveBraidWord :: (RandomGen g, KnownNat n) => Int -> g -> (Braid n, g) randomPositiveBraidWord len g = (braid, g') where braid = Braid w n = numberOfStrands braid (w,g') = _randomPositiveBraidWord n len g -------------------------------------------------------------------------------- -- | Given a braid word, we perturb it randomly @m@ times using the braid relations, -- so that the resulting new braid word is equivalent to the original. -- -- Useful for testing. -- randomPerturbBraidWord :: forall n g. (RandomGen g, KnownNat n) => Int -> Braid n -> g -> (Braid n, g) randomPerturbBraidWord m braid@(Braid xs) g = (Braid word' , g') where (word',g') = go m (length xs) xs g n = numberOfStrands braid -- | A random pair cancelling each other rndE :: g -> ([BrGen],g) rndE g = (e1,g'') where (i , g' ) = randomR (1,n-1) g (b , g'' ) = random g' e0 = [SigmaInv i, Sigma i] e1 = if b then reverse e0 else e0 brg s i = case s of { Plus -> Sigma i ; Minus -> SigmaInv i } brginv s i = case s of { Plus -> SigmaInv i ; Minus -> Sigma i } go :: Int -> Int -> [BrGen] -> g -> ([BrGen], g) go !cnt !len !word !g | cnt <= 0 = (word, g) | len < 2 = let w' = if b1 then (e++word) else (word++e) -- if it is short, we just add a trivial pair somewhere in continue g4 (len+2) w' | abs (i-j) >= 2 = continue g4 len (as ++ v:u:bs) -- they commute, so we just commute them | i == j && s/=t = continue g4 (len-2) (as ++ bs ) -- they are inverse of each other, so we kill them | abs (i-j) == 1 && s == t = let mid = if b1 then [ brg s j , brg s i , brg s j , brginv s i ] -- insert pair and else [ brginv s j , brg s i , brg s j , brg s i ] -- apply ternary relation in continue g4 (len+2) (as ++ mid ++ bs) | otherwise = let mid = if b1 then (u : e ++ [v]) else if b2 then [u,v] ++ e else e ++ [u,v] in continue g4 (len+2) (as++(u:e)++[v]++bs) -- otherwise we just insert an trivial pair where (pos , g1 ) = randomR (0,len-2) g (b1 :: Bool , g2 ) = random g1 (b2 :: Bool , g3 ) = random g2 (e , g4 ) = rndE g3 (as,u:v:bs) = splitAt pos word (s,i) = brGenSignIdx u (t,j) = brGenSignIdx v continue g' len' word' = go (cnt-1) len' word' g' -------------------------------------------------------------------------------- -- | This version of 'randomBraidWord' may be convenient to avoid the type level stuff withRandomBraidWord :: RandomGen g => (forall n. KnownNat n => Braid n -> a) -> Int -- ^ number of strands -> Int -- ^ length of the random word -> g -> (a, g) withRandomBraidWord f n len = runRand $ do withSelectedM f (rand $ randomBraidWord len) n -- | This version of 'randomPositiveBraidWord' may be convenient to avoid the type level stuff withRandomPositiveBraidWord :: RandomGen g => (forall n. KnownNat n => Braid n -> a) -> Int -- ^ number of strands -> Int -- ^ length of the random word -> g -> (a, g) withRandomPositiveBraidWord f n len = runRand $ do withSelectedM f (rand $ randomPositiveBraidWord len) n -- | Untyped version of 'randomBraidWord' _randomBraidWord :: (RandomGen g) => Int -- ^ number of strands -> Int -- ^ length of the random word -> g -> ([BrGen], g) _randomBraidWord n len = runRand $ replicateM len $ do k <- randChoose (1,n-1) s <- randRoll return $ case s of Plus -> Sigma k Minus -> SigmaInv k -- | Untyped version of 'randomPositiveBraidWord' _randomPositiveBraidWord :: (RandomGen g) => Int -- ^ number of strands -> Int -- ^ length of the random word -> g -> ([BrGen], g) _randomPositiveBraidWord n len = runRand $ replicateM len $ do liftM Sigma $ randChoose (1,n-1) --------------------------------------------------------------------------------