{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} -- | Braids represented as Haskell types with support for generation and transformations. -- -- = Braid Typeclass -- -- `Braid` is a typeclass over the braid rep itself and its value type. Since a goal of this library is to use braids for non-mathematical purposes (ie music composition), a Braid can be indexed over any `Integral` type, to support braids representing pitch values in a register for instance. -- -- = Generators -- -- All braids are represented using Artin generators as `Gen`, with `Polarity` defining the "power" of a generator as `O`ver or `U`nder. -- -- Generator indexes are usually 0-indexed, which differs from the 1-indexed generators in the literature. However, again these braids can represent other ranges of numbers as branch indexes. -- -- = Braid instances -- -- `Artin` creates canonical, "one-at-a-time", generator braids. -- -- @ -- Artin [Gen 0 O,Gen 1 U] -- @ -- -- `MultiGen` creates "compressed", "many-at-a-time" braids of `Step`s, which -- prevent invalid adjacent generators. -- -- @ -- MultiGen [Step (Gen 1 U) [Gen 0 U],Step (Gen 1 O) []] -- @ -- -- `DimBraid` is for creating "padded" braids, since generators cannot express the absence of a cross. -- -- = Birman\/Ko\/Lee generators. -- -- `bandGen` creates Birman\/Ko\/Lee-style band generators. -- -- = Transformations\/Moves -- -- In addition to operations like `merge` etc, the type `Move` represents Reidemeister-type isotopy moves. `makeTree` unfolds a potentially-infinite tree representing all possible applications of a move. -- -- = Graphics -- -- `renderBraid`, `renderBraids` and `renderStrand` allow drawings of braids, admitting extra functions for colorizing etc. -- -- @ -- renderBraid 60 [colorStrands] "braid.png" $ bandGen 0 5 -- @ -- -- <> -- -- module Fadno.Braids ( -- * Braid Types module Fadno.Braids.Internal -- * Braid find\/merge\/clear ,find,find',merge,mergeAt,mergeAt',clear,clearMerge -- * Isotopy\/Reidemeister moves ,reidemeister2,reidemeister3,findMoves,applyMove,moves,makeTree -- * Band generators ,bandGen -- * Braid Graphics ,module Fadno.Braids.Graphics ) where import Control.Lens hiding (op,(#),Empty) import Fadno.Braids.Internal import Fadno.Braids.Graphics import Data.Tree import Data.Default -- | Birman, Ko, Lee "band generators" (sigma-s-t) bandGen :: Integral a => a -> a -> Artin a bandGen s t | s >= t = error "invalid, s >= t" | otherwise = Artin $ map (`Gen` O) (reverse [s + 1 .. t - 1]) ++ [Gen s O] ++ map (`Gen` U) [s + 1 .. t - 1] -- | Merge one braid into another at offsets. mergeAt :: (Integral a, Braid b a) => Int -> a -> b a -> b a -> MultiGen a mergeAt x y ba bb = mergeAt' x y (toGens ba) (toGens bb) -- | Matrix version. mergeAt' :: forall a . (Integral a) => Int -> a -> [[Gen a]] -> [[Gen a]] -> MultiGen a mergeAt' x y ba bb = MultiGen $ loop (offset ba) bb where loop :: [[Gen a]] -> [[Gen a]] -> [Step a] loop [] [] = [] loop (a:as) [] = gensToStep a:loop as [] loop [] (b:bs) = gensToStep b:loop [] bs loop (a:as) (b:bs) = foldl (flip insertS) (gensToStep a) b:loop as bs offset gens = replicate x [] ++ offsetGenVals y gens offsetGenVals :: Integral a => a -> [[Gen a]] -> [[Gen a]] offsetGenVals y = map (map (fmap (+y))) normalGenVals :: Integral a => [[Gen a]] -> [[Gen a]] normalGenVals gs = offsetGenVals (- (minimum . map _gPos $ concat gs)) gs -- | Rectangular gen eraser. clear :: Integral a => Int -> a -> Int -> a -> [[Gen a]] -> [[Gen a]] clear x y w h = clrx 0 where clrx _ [] = [] clrx xi (s:ss) = (if xi >= x && xi < x + w then clry s else s):clrx (succ xi) ss clry [] = [] clry (g@(Gen i _):gs) | i >= y && i < y + h = clry gs | otherwise = g:clry gs -- | Merge one braid into another. merge :: forall b a . (Integral a, Braid b a) => b a -> b a -> MultiGen a merge = mergeAt 0 0 _bands :: MultiGen Int _bands = mergeAt 5 5 (bandGen 0 4) (bandGen 0 9) _testpath :: FilePath _testpath = "output/test.png" -- renderBraid :: Braid b a => Int -> [BraidDrawF a] -> FilePath -> b a -> IO () _drawLoops,_drawStrands :: (Show a, Integral a, Braid b a) => b a -> IO () _drawLoops = renderBraid def { stepWidth = 400 } [colorLoops] _testpath _drawStrands = renderBraid def { stepWidth = 400 } [colorStrands] _testpath -- | Reidemeister move 2, [s1,s1^-1] === flat reidemeister2 :: Integral a => Move Artin a reidemeister2 = Move (Artin [Gen 0 O,Gen 0 U]) (Artin []) -- | Reidemeister move 3, [s1,s2,s1^-1] === [s2^-1,s1,s2], and inverse polarity. -- Rule: a pattern of [(i,p),(i',p),(i,^p)] moves to [(i',^p),(i,p),(i',p)], -- where i' = i `op` i where op is plus or minus; with the reversed lists too. reidemeister3 :: Integral a => [Move Artin a] reidemeister3 = [ mk (zipWith Gen is ps) | ps <- [[U,U,O],[O,O,U]], is <- [[0,1,0],[1,0,1]]] where mk gs = Move (Artin gs) $ Artin $ over (traverse.gPos) (\i -> if i == 0 then 1 else 0) (reverse gs) _drawReid3s :: IO () _drawReid3s = renderBraids def { stepWidth = 80 } [colorStrands] "output/reid3.png" $ map (\(Move a b) -> [a,b]) (reidemeister3 :: [Move Artin Int]) -- drawMove (last reidemeister3) bands _drawMove :: (Integral i, Braid a i, Braid b i) => Move a i -> b i -> IO () _drawMove m b = renderBraids def { stepWidth = 80 } [colorStrands] "output/move.png" [toMultiGen b:map toMultiGen [view _1 m, view _2 m], --[toMultiGen b], map (\l -> applyMove m l b) (findMoves m b)] -- | Find all locations of a sub-braid within a braid. find :: (Integral i, Braid a i, Braid b i) => a i -> b i -> [Loc i] find ba = find' (normalGenVals $ toGens ba) (stepCount ba) (strandCount ba) -- | Find all locations of a sub-braid within a braid, matrix version. find' :: (Integral i, Braid b i) => [[Gen i]] -> Int -> i -> b i -> [Loc i] find' ba w h bb = [ Loc x y | x <- [0 .. stepCount bb] , y <- [minIndex bb .. maxIndex bb] , test x y ] where gba = normalGenVals ba gbb = toGens bb test x y = gbb == toGens (clearMerge gba x y w h gbb) -- | clear generators and merge. TODO: doesn't clear adjacent gens. clearMerge :: Integral a => [[Gen a]] -> Int -> a -> Int -> a -> [[Gen a]] -> MultiGen a clearMerge ba x y w h = mergeAt' x y ba . clear x y w h -- | Locates all move location in a braid. findMoves :: (Integral i, Braid a i, Braid b i) => Move a i -> b i -> [Loc i] findMoves m@(Move m1 _) = find' (toGens m1) (moveW m) (moveH m) -- | Apply a move at a location. applyMove :: (Integral i, Braid a i, Braid b i) => Move a i -> Loc i -> b i -> MultiGen i applyMove m@(Move _ m2) (Loc x y) = clearMerge (toGens m2) x y (moveW m) (moveH m) . toGens -- | Test a collection of moves against a braid and pair results with location. moves :: (Integral i, Braid a i, Braid b i) => [Move a i] -> b i -> [(Move a i,[Loc i])] moves mvs target = filter (not . null . snd) $ map (\m -> (m, findMoves m target)) mvs -- | Unfold a tree of all possible move applications on a braid. -- A permutation is the permuted braid + the [(move,loc)]s that got us there. -- Thus the root is (original braid, []); children are [(b1,(move,loc))]. makeTree :: (Integral i, Braid a i, Braid b i) => [Move a i] -> b i -> Tree (MultiGen i,[(Move a i,Loc i)]) makeTree mvs org = unfoldTree go (toMultiGen org,[]) where go n@(seed,path) = (n,concatMap (gen seed path) $ moves mvs seed) gen target path (mv,locs) = map (\l -> (applyMove mv l target,(mv,l):path)) locs -- let t = makeTree reidemeister3 _bands -- let t = makeTree (reidemeister3 ++ map inverse reidemeister3) _bands -- renderBraids 100 [colorStrands] "allmoves.png" $ nub $ map (return.fst) $ flatten t zipTail :: (a -> a -> c) -> [a] -> [c] zipTail f = zipWith f <*> tail