packages feed

combinat-compat (empty) → 0.2.8.2

raw patch · 59 files changed

+11445/−0 lines, 59 filesdep +QuickCheckdep +arraydep +basesetup-changed

Dependencies added: QuickCheck, array, base, combinat-compat, containers, random, test-framework, test-framework-quickcheck2, transformers

Files

+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2008-2016, Balazs Komuves+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++- Redistributions of source code must retain the above copyright notice,+this list of conditions and the following disclaimer.+ +- Redistributions in binary form must reproduce the above copyright notice,+this list of conditions and the following disclaimer in the documentation+and/or other materials provided with the distribution.+ +- Neither names of the copyright holders nor the names of the contributors+may be used to endorse or promote products derived from this software without+specific prior written permission. ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Math/Combinat.hs view
@@ -0,0 +1,76 @@++-- | A collection of functions to generate, manipulate,+-- visualize and count combinatorial objects like partitions, +-- compositions, permutations, braids, Young tableaux, +-- lattice paths, various tree structures, etc etc.+--+-- +-- See also the @combinat-diagrams@ library for generating+-- graphical representations of (some of) these structure using +-- the @diagrams@ library (<http://projects.haskell.org/diagrams>).+--+--+-- The long-term goals are +--+--  (1) generate most of the standard structures;+-- +--  (2) manipulate these structures;+--+--  (3) visualize these structures;+--+--  (4) the generation should be efficient; +--+--  (5) to be able to enumerate the structures +--      with constant memory usage;+--+--  (6) to be able to randomly sample from them;+--   +--  (7) finally, to be a repository of algorithms.+--+--+-- The short-term goal is simply to generate +-- and manipulate many interesting structures.+--+--+-- Naming conventions (subject to change): +--+--  * prime suffix: additional constrains, typically more general;+--+--  * underscore prefix: use plain lists instead of other types with +--    enforced invariants;+--+--  * \"random\" prefix: generates random objects +--    (typically with uniform distribution); +--+--  * \"count\" prefix: counting functions.+--+--+-- This module re-exports the most commonly used modules.+--++module Math.Combinat +  ( module Math.Combinat.Numbers+  , module Math.Combinat.Sign+  , module Math.Combinat.Sets+  , module Math.Combinat.Tuples+  , module Math.Combinat.Compositions+  , module Math.Combinat.Partitions+  , module Math.Combinat.Permutations+  , module Math.Combinat.Tableaux+  , module Math.Combinat.Trees+  , module Math.Combinat.LatticePaths+  , module Math.Combinat.ASCII+  ) +  where++import Math.Combinat.Numbers+import Math.Combinat.Sign+import Math.Combinat.Sets+import Math.Combinat.Tuples+import Math.Combinat.Compositions+import Math.Combinat.Partitions+import Math.Combinat.Permutations+import Math.Combinat.Tableaux+import Math.Combinat.Trees+import Math.Combinat.LatticePaths+import Math.Combinat.ASCII
+ Math/Combinat/ASCII.hs view
@@ -0,0 +1,438 @@++-- | A mini-DSL for ASCII drawing of structures.+--+--+-- From some structures there is also Graphviz and\/or @diagrams@ +-- (<http://projects.haskell.org/diagrams>) visualization support +-- (the latter in the separate libray @combinat-diagrams@).+--++module Math.Combinat.ASCII where++--------------------------------------------------------------------------------++import Data.Char ( isSpace )+import Data.List ( transpose , intercalate )++import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * The basic ASCII type++-- | The type of a (rectangular) ASCII figure. +-- Internally it is a list of lines of the same length plus the size.+--+-- Note: The Show instance is pretty-printing, so that it\'s convenient in ghci.+--+data ASCII = ASCII +  { asciiSize  :: (Int,Int) +  , asciiLines :: [String]+  }++-- | A type class to have a simple way to draw things +class DrawASCII a where+  ascii :: a -> ASCII++instance Show ASCII where+  show = asciiString++-- | An empty (0x0) rectangle+emptyRect :: ASCII+emptyRect = ASCII (0,0) []++asciiXSize, asciiYSize :: ASCII -> Int+asciiXSize = fst . asciiSize+asciiYSize = snd . asciiSize++asciiString :: ASCII -> String+asciiString (ASCII sz ls) = unlines ls++printASCII :: ASCII -> IO ()+printASCII = putStrLn . asciiString++asciiFromLines :: [String] -> ASCII+asciiFromLines ls = ASCII (x,y) (map f ls) where+  y   = length ls+  x   = maximum (map length ls)+  f l = l ++ replicate (x - length l) ' '++asciiFromString :: String -> ASCII+asciiFromString = asciiFromLines . lines++--------------------------------------------------------------------------------+-- * Alignment++-- | Horizontal alignment+data HAlign +  = HLeft +  | HCenter +  | HRight +  deriving (Eq,Show)++-- | Vertical alignment+data VAlign +  = VTop +  | VCenter +  | VBottom +  deriving (Eq,Show)++data Alignment = Align HAlign VAlign++--------------------------------------------------------------------------------+-- * Separators++-- | Horizontal separator+data HSep +  = HSepEmpty           -- ^ empty separator+  | HSepSpaces Int      -- ^ @n@ spaces+  | HSepString String   -- ^ some custom string, eg. @\" | \"@+  deriving Show++hSepSize :: HSep -> Int+hSepSize hsep = case hsep of+  HSepEmpty    -> 0+  HSepSpaces k -> k+  HSepString s -> length s++hSepString :: HSep -> String+hSepString hsep = case hsep of+  HSepEmpty    -> ""+  HSepSpaces k -> replicate k ' '+  HSepString s -> s++-- | Vertical separator+data VSep +  = VSepEmpty           -- ^ empty separator+  | VSepSpaces Int      -- ^ @n@ spaces+  | VSepString [Char]   -- ^ some custom list of characters, eg. @\" - \"@ (the characters are interpreted as below each other)+  deriving Show++vSepSize :: VSep -> Int+vSepSize vsep = case vsep of+  VSepEmpty    -> 0+  VSepSpaces k -> k+  VSepString s -> length s++vSepString :: VSep -> [Char]+vSepString vsep = case vsep of+  VSepEmpty    -> []+  VSepSpaces k -> replicate k ' '+  VSepString s -> s+                                        +--------------------------------------------------------------------------------+-- * Concatenation++-- | Horizontal append, centrally aligned, no separation.+(|||) :: ASCII -> ASCII -> ASCII+(|||) p q = hCatWith VCenter HSepEmpty [p,q]++-- | Vertical append, centrally aligned, no separation.+(===) :: ASCII -> ASCII -> ASCII+(===) p q = vCatWith HCenter VSepEmpty [p,q]++-- | Horizontal concatenation, top-aligned, no separation+hCatTop :: [ASCII] -> ASCII+hCatTop = hCatWith VTop HSepEmpty++-- | Horizontal concatenation, bottom-aligned, no separation+hCatBot :: [ASCII] -> ASCII+hCatBot = hCatWith VBottom HSepEmpty++-- | Vertical concatenation, left-aligned, no separation+vCatLeft :: [ASCII] -> ASCII+vCatLeft = vCatWith HLeft VSepEmpty++-- | Vertical concatenation, right-aligned, no separation+vCatRight :: [ASCII] -> ASCII+vCatRight = vCatWith HRight VSepEmpty++-- | General horizontal concatenation+hCatWith :: VAlign -> HSep -> [ASCII] -> ASCII+hCatWith valign hsep rects = ASCII (x',maxy) final where+  n    = length rects+  maxy = maximum [ y | ASCII (_,y) _ <- rects ]+  xsz  =         [ x | ASCII (x,_) _ <- rects ]+  sep   = hSepString hsep+  sepx  = length sep+  rects1 = map (vExtendTo valign maxy) rects+  x' = sum' xsz + (n-1)*sepx+  final = map (intercalate sep) $ transpose (map asciiLines rects1)++-- | General vertical concatenation+vCatWith :: HAlign -> VSep -> [ASCII] -> ASCII+vCatWith halign vsep rects = ASCII (maxx,y') final where+  n    = length rects+  maxx = maximum [ x | ASCII (x,_) _ <- rects ]+  ysz  =         [ y | ASCII (_,y) _ <- rects ]+  sepy    = vSepSize vsep+  fullsep = transpose (replicate maxx $ vSepString vsep) :: [String]+  rects1  = map (hExtendTo halign maxx) rects+  y'    = sum' ysz + (n-1)*sepy+  final = intercalate fullsep $ map asciiLines rects1++--------------------------------------------------------------------------------+-- * Padding++-- | Horizontally pads with the given number of spaces, on both sides+hPad :: Int -> ASCII -> ASCII+hPad k (ASCII (x,y) ls) = ASCII (x+2*k,y) (map f ls) where+  f l = pad ++ l ++ pad +  pad = replicate k ' '++-- | Vertically pads with the given number of empty lines, on both sides+vPad :: Int -> ASCII -> ASCII+vPad k (ASCII (x,y) ls) = ASCII (x,y+2*k) (pad ++ ls ++ pad) where+  pad = replicate k (replicate x ' ')++-- | Pads by single empty lines vertically and two spaces horizontally+pad :: ASCII -> ASCII+pad = vPad 1 . hPad 2 ++--------------------------------------------------------------------------------+-- * Extension++-- | Extends an ASCII figure with spaces horizontally to the given width.+-- Note: the alignment is the alignment of the original picture in the new bigger picture!+hExtendTo :: HAlign -> Int -> ASCII -> ASCII+hExtendTo halign n0 rect@(ASCII (x,y) ls) = hExtendWith halign (max n0 x - x) rect+  +-- | Extends an ASCII figure with spaces vertically to the given height.+-- Note: the alignment is the alignment of the original picture in the new bigger picture!+vExtendTo :: VAlign -> Int -> ASCII -> ASCII+vExtendTo valign n0 rect@(ASCII (x,y) ls) = vExtendWith valign (max n0 y - y) rect++-- | Extend horizontally with the given number of spaces.+hExtendWith :: HAlign -> Int -> ASCII -> ASCII+hExtendWith alignment d (ASCII (x,y) ls) = ASCII (x+d,y) (map f ls) where+  f l = case alignment of+    HLeft   -> l ++ replicate d ' '   +    HRight  -> replicate d ' ' ++ l+    HCenter -> replicate a ' ' ++ l ++ replicate (d-a) ' ' +  a = div d 2++-- | Extend vertically with the given number of empty lines.+vExtendWith :: VAlign -> Int -> ASCII -> ASCII+vExtendWith valign d (ASCII (x,y) ls) = ASCII (x,y+d) (f ls) where+  f ls = case valign of+    VTop     -> ls ++ replicate d emptyline   +    VBottom  -> replicate d emptyline ++ ls+    VCenter  -> replicate a emptyline ++ ls ++ replicate (d-a) emptyline+  a = div d 2+  emptyline = replicate x ' '++-- | Horizontal indentation+hIndent :: Int -> ASCII -> ASCII+hIndent d = hExtendWith HRight d++-- | Vertical indentation+vIndent :: Int -> ASCII -> ASCII+vIndent d = vExtendWith VBottom d++--------------------------------------------------------------------------------+-- * Cutting++-- | Cuts the given number of columns from the picture. +-- The alignment is the alignment of the /picture/, not the cuts.+--+-- This should be the (left) inverse of 'hExtendWith'.+hCut :: HAlign -> Int -> ASCII -> ASCII+hCut halign k (ASCII (x,y) ls) = ASCII (x',y) (map f ls) where+  x' = max 0 (x-k)+  f  = case halign of+    HLeft   -> reverse . drop  k    . reverse+    HCenter -> reverse . drop (k-a) . reverse . drop a+    HRight  -> drop k +  a = div k 2++-- | Cuts the given number of rows from the picture. +-- The alignment is the alignment of the /picture/, not the cuts.+--+-- This should be the (left) inverse of 'vExtendWith'.+vCut :: VAlign -> Int -> ASCII -> ASCII+vCut valign k (ASCII (x,y) ls) = ASCII (x,y') (g ls) where+  y' = max 0 (y-k)+  g  = case valign of+    VTop    -> reverse . drop  k    . reverse+    VCenter -> reverse . drop (k-a) . reverse . drop a+    VBottom -> drop k +  a = div k 2++--------------------------------------------------------------------------------+-- * Pasting++-- | Pastes the first ASCII graphics onto the second, keeping the second one's dimension+-- (that is, overlapping parts of the first one are ignored). +-- The offset is relative to the top-left corner of the second picture.+-- Spaces at treated as transparent.+--+-- Example:+--+-- > tabulate (HCenter,VCenter) (HSepSpaces 2, VSepSpaces 1)+-- >  [ [ caption (show (x,y)) $+-- >      pasteOnto (x,y) (filledBox '@' (4,3)) (asciiBox (7,5))+-- >    | x <- [-4..7] ] +-- >  | y <- [-3..5] ]+--+pasteOnto :: (Int,Int) -> ASCII -> ASCII -> ASCII+pasteOnto = pasteOnto' isSpace ++-- | Pastes the first ASCII graphics onto the second, keeping the second one's dimension.+-- The first argument specifies the transparency condition (on the first picture).+-- The offset is relative to the top-left corner of the second picture.+-- +pasteOnto' +  :: (Char -> Bool)      -- ^ transparency condition+  -> (Int,Int)           -- ^ offset relative to the top-left corner of the second picture+  -> ASCII               -- ^ picture to paste+  -> ASCII               -- ^ picture to paste onto+  -> ASCII+pasteOnto' transparent (xpos,ypos) small big = new where+  new = ASCII (xbig,ybig) lines'+  (xbig,ybig) = asciiSize  big+  bigLines    = asciiLines big+  small'      = (if (ypos>=0) then vExtendWith VBottom ypos else vCut VBottom (-ypos))+              $ (if (xpos>=0) then hExtendWith HRight  xpos else hCut HRight  (-xpos))+              $ small+  smallLines  = asciiLines small'+  lines'  = zipWith f bigLines (smallLines ++ repeat "")+  f bl sl = zipWith g bl (sl ++ repeat ' ')+  g b  s  = if transparent s then b else s++-- | A version of 'pasteOnto' where we can specify the corner of the second picture+-- to which the offset is relative:+--+-- > pasteOntoRel (HLeft,VTop) == pasteOnto+--+pasteOntoRel :: (HAlign,VAlign) -> (Int,Int) -> ASCII -> ASCII -> ASCII+pasteOntoRel = pasteOntoRel' isSpace++pasteOntoRel' :: (Char -> Bool) -> (HAlign,VAlign) -> (Int,Int) -> ASCII -> ASCII -> ASCII+pasteOntoRel' transparent (halign,valign) (xpos,ypos) small big = new where+  new = pasteOnto' transparent (xpos',ypos') small big +  (xsize,ysize) = asciiSize big+  xpos' = case halign of+    HLeft   -> xpos+    HCenter -> xpos + div xsize 2+    HRight  -> xpos +     xsize+  ypos' = case valign of+    VTop    -> ypos+    VCenter -> ypos + div ysize 2+    VBottom -> ypos +     ysize++--------------------------------------------------------------------------------+-- * Tabulate++-- | Tabulates the given matrix of pictures. Example:+--+-- > tabulate (HCenter, VCenter) (HSepSpaces 2, VSepSpaces 1)+-- >   [ [ asciiFromLines [ "x=" ++ show x , "y=" ++ show y ] | x<-[7..13] ] +-- >   | y<-[98..102] ]+--+tabulate :: (HAlign,VAlign) -> (HSep,VSep) -> [[ASCII]] -> ASCII+tabulate (halign,valign) (hsep,vsep) rects0 = final where+  n = length rects0+  m = maximum (map length rects0)+  rects1 = map (\rs -> rs ++ replicate (m - length rs) emptyRect) rects0+  ys = map (\rs -> maximum (map asciiYSize rs)) rects1+  xs = map (\rs -> maximum (map asciiXSize rs)) (transpose rects1)+  rects2 = map (\rs -> [      hExtendTo halign x  r  | (x,r ) <- zip xs rs     ]) rects1+  rects3 =             [ map (vExtendTo valign y) rs | (y,rs) <- zip ys rects2 ]  +  final  = vCatWith HLeft vsep +         $ map (hCatWith VTop hsep) rects3++-- | Order of elements in a matrix+data MatrixOrder +  = RowMajor+  | ColMajor+  deriving (Eq,Ord,Show,Read)++-- | Automatically tabulates ASCII rectangles.+--+autoTabulate +  :: MatrixOrder      -- ^ whether to use row-major or column-major ordering of the elements+  -> Either Int Int   -- ^ @(Right x)@ creates x columns, while @(Left y)@ creates y rows+  -> [ASCII]          -- ^ list of ASCII rectangles+  -> ASCII+autoTabulate mtxorder ei list = final where+  +  final = tabulate (HLeft,VBottom) (HSepSpaces 2,VSepSpaces 1) rects ++  n = length list++  rects = case ei of++    Left y  -> case mtxorder of+                 ColMajor -> transpose (parts y list)+                 RowMajor -> invparts y list++    Right x -> case mtxorder of+                 ColMajor -> transpose (invparts x list)+                 RowMajor -> parts x list++  transposeIf b = if b then transpose else id++  -- chops into parts (the last one can be smaller)+  parts d = go where+    go [] = []+    go xs = take d xs : go (drop d xs)++  invparts d xs = parts' ds xs where+    (q,r) = divMod n d+    ds = replicate r (q+1) ++ replicate (d-r) q++  parts' ds xs = go ds xs where+    go _  [] = []                                      +    go [] _  = []+    go (d:ds) xs = take d xs : go ds (drop d xs)++--------------------------------------------------------------------------------+-- * Captions++-- | Adds a caption to the bottom, with default settings.+caption :: String -> ASCII -> ASCII+caption = caption' False HLeft++-- | Adds a caption to the bottom. The @Bool@ flag specifies whether to add an empty between +-- the caption and the figure+caption' :: Bool -> HAlign -> String -> ASCII -> ASCII+caption' emptyline halign str rect = vCatWith halign sep [rect,capt] where+  sep  = if emptyline then VSepSpaces 1 else VSepEmpty +  capt = asciiFromString str++--------------------------------------------------------------------------------+-- * Ready-made boxes++-- | An ASCII border box of the given size +asciiBox :: (Int,Int) -> ASCII+asciiBox (x,y) = ASCII (max x 2, max y 2) (h : replicate (y-2) m ++ [h]) where+  h = "+" ++ replicate (x-2) '-' ++ "+"+  m = "|" ++ replicate (x-2) ' ' ++ "|"++-- | An \"rounded\" ASCII border box of the given size+roundedAsciiBox :: (Int,Int) -> ASCII+roundedAsciiBox (x,y) = ASCII (max x 2, max y 2) (a : replicate (y-2) m ++ [b]) where+  a = "/"  ++ replicate (x-2) '-' ++ "\\"+  m = "|"  ++ replicate (x-2) ' ' ++ "|"+  b = "\\" ++ replicate (x-2) '-' ++ "/"++-- | A box simply filled with the given character+filledBox :: Char -> (Int,Int) -> ASCII+filledBox c (x0,y0) = asciiFromLines $ replicate y (replicate x c) where+  x = max 0 x0+  y = max 0 y0++-- | A box of spaces+transparentBox :: (Int,Int) -> ASCII+transparentBox = filledBox ' '++--------------------------------------------------------------------------------+-- * Testing \/ miscellanea++-- | An integer+asciiNumber :: Int -> ASCII+asciiNumber = asciiShow++asciiShow :: Show a => a -> ASCII+asciiShow = asciiFromLines . (:[]) . show++--------------------------------------------------------------------------------
+ Math/Combinat/Classes.hs view
@@ -0,0 +1,66 @@++-- | Type classes for some common properties shared by different objects++{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}+module Math.Combinat.Classes where++--------------------------------------------------------------------------------++-- | Emptyness+class CanBeEmpty a where+  isEmpty :: a -> Bool+  empty   :: a++--------------------------------------------------------------------------------+-- * Partitions++-- | Number of parts+class HasNumberOfParts a where+  numberOfParts :: a -> Int++--------------------------------------------------------------------------------++class HasWidth a where+  width :: a -> Int++class HasHeight a where+  height :: a -> Int++--------------------------------------------------------------------------------++-- | Weight (of partitions, tableaux, etc)+class HasWeight a where+  weight :: a -> Int++--------------------------------------------------------------------------------++-- | Duality (of partitions, tableaux, etc)+class HasDuality a where+  dual :: a -> a++--------------------------------------------------------------------------------+-- * Tableau++-- | Shape (of tableaux, skew tableaux)+class HasShape a s | a -> s where+  shape :: a -> s++--------------------------------------------------------------------------------+-- * Trees++-- | Number of nodes (of trees)+class HasNumberOfNodes t where+  numberOfNodes :: t -> Int++-- | Number of leaves (of trees)+class HasNumberOfLeaves t where+  numberOfLeaves :: t -> Int++--------------------------------------------------------------------------------+-- * Permutations++-- | Number of cycles (of partitions)+class HasNumberOfCycles p where+  numberOfCycles :: p -> Int++--------------------------------------------------------------------------------
+ Math/Combinat/Compositions.hs view
@@ -0,0 +1,109 @@++-- | Compositions. +--+-- See eg. <http://en.wikipedia.org/wiki/Composition_%28combinatorics%29>+--++module Math.Combinat.Compositions where++--------------------------------------------------------------------------------++import System.Random++import Math.Combinat.Sets    ( randomChoice )+import Math.Combinat.Numbers ( factorial , binomial )+import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * generating all compositions++-- | A /composition/ of an integer @n@ into @k@ parts is an ordered @k@-tuple of nonnegative (sometimes positive) integers+-- whose sum is @n@.+type Composition = [Int]++-- | Compositions fitting into a given shape and having a given degree.+--   The order is lexicographic, that is, +--+-- > sort cs == cs where cs = compositions' shape k+--+compositions'  +  :: [Int]         -- ^ shape+  -> Int           -- ^ sum+  -> [[Int]]+compositions' [] 0 = [[]]+compositions' [] _ = []+compositions' shape@(s:ss) n = +  [ x:xs | x <- [0..min s n] , xs <- compositions' ss (n-x) ] ++countCompositions' :: [Int] -> Int -> Integer+countCompositions' [] 0 = 1+countCompositions' [] _ = 0+countCompositions' shape@(s:ss) n = sum +  [ countCompositions' ss (n-x) | x <- [0..min s n] ] ++-- | All positive compositions of a given number (filtrated by the length). +-- Total number of these is @2^(n-1)@+allCompositions1 :: Int -> [[Composition]]+allCompositions1 n = map (\d -> compositions1 d n) [1..n] ++-- | All compositions fitting into a given shape.+allCompositions' :: [Int] -> [[Composition]]+allCompositions' shape = map (compositions' shape) [0..d] where d = sum shape++-- | Nonnegative compositions of a given length.+compositions +  :: Integral a +  => a       -- ^ length+  -> a       -- ^ sum+  -> [[Int]]+compositions len' d' = compositions' (replicate len d) d where+  len = fromIntegral len'+  d   = fromIntegral d'++-- | # = \\binom { len+d-1 } { len-1 }+countCompositions :: Integral a => a -> a -> Integer+countCompositions len d = binomial (len+d-1) (len-1)++-- | Positive compositions of a given length.+compositions1  +  :: Integral a +  => a       -- ^ length+  -> a       -- ^ sum+  -> [[Int]]+compositions1 len d +  | len > d   = []+  | otherwise = map plus1 $ compositions len (d-len)+  where+    plus1 = map (+1)+    -- len = fromIntegral len'+    -- d   = fromIntegral d'++countCompositions1 :: Integral a => a -> a -> Integer+countCompositions1 len d = countCompositions len (d-len)++--------------------------------------------------------------------------------+-- * random compositions++-- | @randomComposition k n@ returns a uniformly random composition +-- of the number @n@ as an (ordered) sum of @k@ /nonnegative/ numbers+randomComposition :: RandomGen g => Int -> Int -> g -> ([Int],g)+randomComposition k n g0 = +  if k<1 || n<0 +    then error "randomComposition: k should be positive, and n should be nonnegative" +    else (comp, g1) +  where+    (cs,g1) = randomChoice (k-1) (n+k-1) g0+    comp = pairsWith (\x y -> y-x-1) (0 : cs ++ [n+k])+  +-- | @randomComposition1 k n@ returns a uniformly random composition +-- of the number @n@ as an (ordered) sum of @k@ /positive/ numbers+randomComposition1 :: RandomGen g => Int -> Int -> g -> ([Int],g)+randomComposition1 k n g0 = +  if k<1 || n<k +    then error "randomComposition1: we require 0 < k <= n" +    else (comp, g1) +  where+    (cs,g1) = randomComposition k (n-k) g0 +    comp = map (+1) cs++--------------------------------------------------------------------------------
+ Math/Combinat/Groups/Braid.hs view
@@ -0,0 +1,744 @@++-- | Braids. See eg. <https://en.wikipedia.org/wiki/Braid_group>+--+--+-- Based on: +--+--  * Joan S. Birman, Tara E. Brendle: BRAIDS - A SURVEY+--    <https://www.math.columbia.edu/~jb/Handbook-21.pdf>+--+--+-- 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<t)is the generator @sigma_{s,t}@ in Birman-Ko-Lee's+-- \"new presentation\". It twistes the strands @s@ and @t@ while going over all+-- other strands. For @t==s+1@ we get back @sigma s@+-- +doubleSigma :: KnownNat n => 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<n then Sigma i else error "positiveWord: index out of range"+       +-- | The (positive) half-twist of all the braid strands, usually denoted by @Delta@.+halfTwist :: KnownNat n => 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)++--------------------------------------------------------------------------------+
+ Math/Combinat/Groups/Braid/NF.hs view
@@ -0,0 +1,534 @@++-- | Normal form of braids, take 1.+--+-- We implement the Adyan-Thurston-ElRifai-Morton solution to the word problem in braid groups.+--+--+-- Based on:+--+-- * [1] Joan S. Birman, Tara E. Brendle: BRAIDS - A SURVEY+--   <https://www.math.columbia.edu/~jb/Handbook-21.pdf> (chapter 5.1)+--+-- * [2] Elsayed A. Elrifai, Hugh R. Morton: Algorithms for positive braids+--++{-# LANGUAGE +      CPP, BangPatterns, +      ScopedTypeVariables, ExistentialQuantification,+      DataKinds, KindSignatures, Rank2Types #-}++module Math.Combinat.Groups.Braid.NF  +  ( -- * Normal form+    BraidNF (..)+  , nfReprWord+  , braidNormalForm+  , braidNormalForm'+  , braidNormalFormNaive'+    -- * Starting and finishing sets+  , permWordStartingSet+  , permWordFinishingSet    +  , permutationStartingSet+  , permutationFinishingSet    +  )+  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 Math.Combinat.Helper+import Math.Combinat.Sign++import Math.Combinat.Permutations ( Permutation(..) , isIdentityPermutation , isReversePermutation )+import qualified Math.Combinat.Permutations as P++import Math.Combinat.Groups.Braid++--------------------------------------------------------------------------------++-- | A unique normal form for braids, called the /left-greedy normal form/.+-- It looks like @Delta^i*P@, where @Delta@ is the positive half-twist, @i@ is an integer,+-- and @P@ is a positive word, which can be further decomposed into non-@Delta@ /permutation words/; +-- these words themselves are not unique, but the permutations they realize /are/ unique.+--+-- This will solve the word problem relatively fast, +-- though it is not the fastest known algorithm.+--+data BraidNF (n :: Nat) = BraidNF+  { _nfDeltaExp :: !Int              -- ^ the exponent of @Delta@+  , _nfPerms    :: [Permutation]     -- ^ the permutations+  }+  deriving (Eq,Ord,Show)++-- | A braid word representing the given normal form+nfReprWord :: KnownNat n => BraidNF n -> Braid n+nfReprWord (BraidNF k perms) = freeReduceBraidWord $ composeMany (deltas ++ rest) where++  deltas +    | k > 0     = replicate   k           halfTwist+    | k < 0     = replicate (-k) (inverse halfTwist)+    | otherwise = []++  rest = map permutationBraid perms++--------------------------------------------------------------------------------++-- | Computes the normal form of a braid. We apply free reduction first, it should be faster that way.+braidNormalForm :: KnownNat n => Braid n -> BraidNF n+braidNormalForm = braidNormalForm' . freeReduceBraidWord++-- | This function does not apply free reduction before computing the normal form+braidNormalForm' :: KnownNat n => Braid n -> BraidNF n+braidNormalForm' braid@(Braid gens) = BraidNF (dexp+pexp) perms where+  n = numberOfStrands braid+  invless = replaceInverses n gens+  (dexp,posxword) = moveDeltasLeft n invless+  factors = leftGreedyFactors n $ expandPosXWord n posxword+  (pexp,perms) = normalizePermFactors n $ map (_braidPermutation n) factors++-- | This one uses the naive inverse replacement method. Probably somewhat slower than 'braidNormalForm''.+braidNormalFormNaive' :: KnownNat n => Braid n -> BraidNF n+braidNormalFormNaive' braid@(Braid gens) = BraidNF (dexp+pexp) perms where+  n = numberOfStrands braid+  invless = replaceInversesNaive gens+  (dexp,posxword) = moveDeltasLeft n invless+  factors = leftGreedyFactors n $ expandPosXWord n posxword+  (pexp,perms) = normalizePermFactors n $ map (_braidPermutation n) factors++--------------------------------------------------------------------------------++-- | Replaces groups of @sigma_i^-1@ generators by @(Delta^-1 * P)@, +-- where @P@ is a positive word.+--+-- This should be more clever (resulting in shorter words) than the naive version below+--+replaceInverses :: Int -> [BrGen] -> [XGen]+replaceInverses n gens = worker gens where++  worker [] = []+  worker xs = replaceNegs neg ++ map (XSigma . brGenIdx) pos ++ worker rest where +    (neg,tmp ) = span (isMinus . brGenSign) xs+    (pos,rest) = span (isPlus  . brGenSign) tmp+  +  replaceNegs gs = concatMap replaceFac facs where+    facs = leftGreedyFactors n $ map brGenIdx gs+  +  replaceFac idxs = XDelta (-1) : map XSigma (_permutationBraid perm) where+    perm = (P.reversePermutation n) `P.multiply` (P.adjacentTranspositions n idxs)+++-- | Replaces @sigma_i^-1@ generators by @(Delta^-1 * L_i)@.+replaceInversesNaive :: [BrGen] -> [XGen]+replaceInversesNaive gens = concatMap f gens where +  f (Sigma    i) = [ XSigma i ]+  f (SigmaInv i) = [ XDelta (-1) , XL i ]++--------------------------------------------------------------------------------++-- | Temporary data structure to be used during the normal form computation+data XGen+  = XDelta !Int   -- ^ @Delta^k@+  | XSigma !Int   -- ^ @Sigma_j@+  | XL     !Int   -- ^ @L_j = Delta * sigma_j^-1@+  | XTauL  !Int   -- ^ @tau(L_j)@+  deriving (Eq,Show)++isXDelta :: XGen -> Bool+isXDelta x = case x of { XDelta {} -> True ; _ -> False }++-- | We move the all @Delta@'s to the left+moveDeltasLeft :: Int -> [XGen] -> (Int,[XGen])+moveDeltasLeft n input = (finalExp, finalPosWord) where+  +  (XDelta finalExp : finalPosWord) =  reverse $ worker 0 (reverse input) ++  -- we start from the right end, and work towards the left end+  worker  dexp [] = [ XDelta dexp ]+  worker !dexp xs = this' ++ worker dexp' rest where +    (delta,notdelta) = span isXDelta xs+    (this ,rest    ) = span (not . isXDelta) notdelta+    dexp' = dexp + sumDeltas delta+    this' = if even dexp' +      then this+      else map xtau this++  sumDeltas :: [XGen] -> Int+  sumDeltas xs = foldl' (+) 0 [ k | XDelta k <- xs ]++  -- | The @X -> Delta^-1 * X * Delta@ inner automorphism+  xtau :: XGen -> XGen+  xtau (XSigma j) = XSigma (n-j)+  xtau (XDelta k) = XDelta k  +  xtau (XL     k) = XTauL  k  +  xtau (XTauL  k) = XL     k  ++--------------------------------------------------------------------------------++-- | Expands a /positive/ \"X-word\" into a positive braid word+expandPosXWord :: Int -> [XGen] -> [Int]+expandPosXWord n = concatMap f where++  posHalfTwist = _halfTwist n++  jtau :: Int -> Int+  jtau j = n-j++  posLTable    = listArray (1,n-1) [ _permutationBraid (posLPerm n i) | i<-[1..n-1] ] :: Array Int [Int]+  posTauLTable = amap (map jtau) posLTable++  -- posRTable = listArray (1,n-1) [ _permutationBraid (posRPerm n i) | i<-[1..n-1] ] :: Array Int [Int]++  f x = case x of+    XSigma i -> [i]+    XL     i -> posLTable    ! i+    XTauL  i -> posTauLTable ! i+    XDelta i +      | i > 0     -> concat (replicate i posHalfTwist)+      | i < 0     -> error "expandPosXWord: negative delta power"+      | otherwise -> []++  -- word :: Braid n -> [Int]+  -- word (Braid gens) = map brGenIdx gens+++-- | Expands an \"X-word\" into a braid word. Useful for debugging.+expandAnyXWord :: forall n. KnownNat n => [XGen] -> Braid n+expandAnyXWord xgens = braid where+  n = numberOfStrands braid++  braid = composeMany (map f xgens)++  posHalfTwist = halfTwist            :: Braid n+  negHalfTwist = inverse posHalfTwist :: Braid n++  posLTable    = listArray (1,n-1) [ permutationBraid (posLPerm n i) | i<-[1..n-1] ] :: Array Int (Braid n)+  posTauLTable = amap tau posLTable++  -- posRTable = listArray (1,n-1) [ permutationBraid (posRPerm n i) | i<-[1..n-1] ] :: Array Int (Braid n)++  f :: XGen -> Braid n+  f x = case x of+    XSigma i -> sigma i+    XL     i -> posLTable    ! i+    XTauL  i -> posTauLTable ! i+    XDelta i +      | i > 0     -> composeMany (replicate   i  posHalfTwist)+      | i < 0     -> composeMany (replicate (-i) negHalfTwist)+      | otherwise -> identity++--------------------------------------------------------------------------------++-- | @posL k@ (denoted as @L_k@) is a /positive word/ which +-- satisfies @Delta = L_k * sigma_k@, or:+-- +-- > (inverse halfTwist) `compose` (posL k) ~=~ sigmaInv k@+-- +-- Thus we can replace any word with a positive word plus some @Delta^-1@\'s+--+posL :: KnownNat n => Int -> Braid n+posL k = braid where+  n = numberOfStrands braid+  braid = permutationBraid (posLPerm n k)++-- | @posR k n@ (denoted as @R_k@) is a /permutation braid/ which +-- satisfies @Delta = sigma_k * R_k@+-- +-- > (posR k) `compose` (inverse halfTwist) ~=~ sigmaInv k@+-- +-- Thus we can replace any word with a positive word plus some @Delta^-1@'s+--+posR :: KnownNat n => Int -> Braid n+posR k = braid where+  n = numberOfStrands braid+  braid = permutationBraid (posRPerm n k)++-- | The permutation @posL k :: Braid n@ is realizing+posLPerm :: Int -> Int -> Permutation+posLPerm n k +  | k>0 && k<n  = (P.reversePermutation n `P.multiply` P.adjacentTransposition n k)+  | otherwise   = error "posLPerm: index out of range"++-- | The permutation @posR k :: Braid n@ is realizing+posRPerm :: Int -> Int -> Permutation+posRPerm n k +  | k>0 && k<n  = (P.adjacentTransposition n k `P.multiply` P.reversePermutation n )+  | otherwise   = error "posRPerm: index out of range"++--------------------------------------------------------------------------------++-- | We recognize left-greedy factors which are @Delta@-s (easy, since they are the only ones+-- with length @(n choose 2)@), and move them to the left, returning their summed exponent+-- and the filtered new factors. We also filter trivial permutations (which should only happen +-- for the trivial braid, but it happens there?)+--+filterDeltaFactors :: Int -> [[Int]] -> (Int, [[Int]])+filterDeltaFactors n facs = (exp',facs'') where++  (exp',facs') = go 0 (reverse facs)++  jtau j = n-j+  facs'' = reverse facs'+  maxlen = div (n*(n-1)) 2++  go !e []       = (e,[])+  go !e (xs:xxs)  +    | null xs             = go e xxs+    | length xs == maxlen = go (e+1) xxs+    | otherwise           =  +        if even e+          then let (e',yys) = go e xxs in (e' ,          xs : yys) +          else let (e',yys) = go e xxs in (e' , map jtau xs : yys)  ++-------------------------------------------------------------------------------- ++-- | The /starting set/ of a positive braid P is the subset of @[1..n-1]@ defined by+-- +-- > S(P) = [ i | P = sigma_i * Q , Q is positive ] = [ i | (sigma_i^-1 * P) is positive ] +--+-- This function returns the starting set a positive word, assuming it +-- is a /permutation braid/ (see Lemma 2.4 in [2])+--+permWordStartingSet :: Int -> [Int] -> [Int]+permWordStartingSet n xs = permWordFinishingSet n (reverse xs)++-- | The /finishing set/ of a positive braid P is the subset of @[1..n-1]@ defined by+-- +-- > F(P) = [ i | P = Q * sigma_i , Q is positive ] = [ i | (P * sigma_i^-1) is positive ] +--+-- This function returns the finishing set, assuming the input is a /permutation braid/+--+permWordFinishingSet :: Int -> [Int] -> [Int]+permWordFinishingSet n input = runST action where++  action :: forall s. ST s [Int]+  action = do+    perm <- newArray_ (1,n) :: ST s (STUArray s Int Int)+    forM_ [1..n] $ \i -> writeArray perm i i+    forM_ input $ \i -> do+      a <- readArray perm  i+      b <- readArray perm (i+1)+      writeArray perm  i    b+      writeArray perm (i+1) a+    flip filterM [1..n-1] $ \i -> do+      a <- readArray perm  i+      b <- readArray perm (i+1) +      return (b<a)                    -- Lemma 2.4 in [2]++-- | This satisfies+-- +-- > permutationStartingSet p == permWordStartingSet n (_permutationBraid p)+--+permutationStartingSet :: Permutation -> [Int]+permutationStartingSet = permutationFinishingSet . P.inverse++-- | This satisfies+-- +-- > permutationFinishingSet p == permWordFinishingSet n (_permutationBraid p)+--+permutationFinishingSet :: Permutation -> [Int]+permutationFinishingSet (Permutation arr) +  = [ i | i<-[1..n-1] , arr ! i > arr ! (i+1) ] where (1,n) = bounds arr++-- | Returns the list of permutations failing Lemma 2.5 in [2] +-- (so an empty list means the implementaton is correct)+fails_lemmma_2_5 :: Int -> [Permutation]+fails_lemmma_2_5 n = [ p | p <- P.permutations n , not (test p) ] where+  test p = and [ check i | i<-[1..n-1] ] where+    w = _permutationBraid p+    s = permWordStartingSet n w+    check i = _isPermutationBraid n (i:w) == (not $ elem i s)++-------------------------------------------------------------------------------- +                    +-- | Given factors defined as permutation braids, we normalize them+-- to /left-canonical form/ by ensuring that+--+-- * for each consecutive pair @(P,Q)@ the finishing set F(P) contains the starting set S(Q)+--+-- * all @Delta@-s (corresponding to the reverse permutation) are moved to the left+--+-- * all trivial factors are filtered out+--+-- Unfortunately, it seems that we may need multiple sweeps to do that...+--+normalizePermFactors :: Int -> [Permutation] -> (Int,[Permutation])+normalizePermFactors n = go 0 where+  go !acc input = +    if (exp==0 && input == output) +      then (acc,input) +      else go (acc+exp) output +    where +      (exp,output) = normalizePermFactors1 n input++-- | Does 1 sweep of the above normalization process.+-- Unfortunately, it seems that we may need to do this multiple times...+--+normalizePermFactors1 :: Int -> [Permutation] -> (Int,[Permutation])+normalizePermFactors1 n input = (exp, reverse output) where+  (exp, output) = worker 0 (reverse input)++  -- Notes: We work in reverse order, from the right to the left.+  -- We maintain the number of Delta-s pushed through; the tau involutions+  -- are implicit in the parity of this number+  --+  worker :: Int -> [Permutation] -> (Int,[Permutation])+  worker = worker' 0 0+  +  -- We also maintain additional 0/1 flip flags for the first two permutations+  -- this is a little bit of hack but it should work nicely+  --+  worker' :: Int -> Int -> Int -> [Permutation] -> (Int,[Permutation])+  worker' !ep !eq !e (!p : rest@(!q : rest')) ++    -- check if the very first element is identity or Delta +    -- (note: these are tau-invariants)++    | isIdentityPermutation p  = worker'  eq  0  e    rest+    | isReversePermutation  p  = worker'  eq  0 (e+1) rest++    -- check if the second element is identity or Delta +    -- this is necessary since we "fatten" the second element and it can possibly+    -- become Delta after a while (?)++    | isIdentityPermutation q  = worker'  ep    0  e    (p : rest')+    | isReversePermutation  q  = worker' (ep-1) 0 (e+1) (p : rest')    ++    -- ok so we have something like "... : Q : P"+    -- if F(Q) contains S(P) then we can move on; +    -- otherwise there is an element j in S(P) \\ F(Q), so we can +    -- replace it by "... : Qj : jP"++    | otherwise = +        case permutationStartingSet preal \\ permutationFinishingSet qreal of  +          []    -> let (e',rs) = worker' eq 0 e rest in (e', preal : rs)+          (j:_) -> worker' (-e) (-e) e (p':q':rest') where +                     s  = P.adjacentTransposition n j+                     p' = P.multiply s preal+                     q' = P.multiply qreal s+        where+          preal = oddTau (e+ep) p       -- the "real" p+          qreal = oddTau (e+eq) q       -- the "real" q++  worker' _   _  !e [ ] = (e,[])+  worker' !ep _  !e [p] +    | isIdentityPermutation p  = (e   , [])+    | isReversePermutation  p  = (e+1 , [])+    | otherwise                = (e   , [oddTau (e+ep) p] )++  oddTau :: Int -> Permutation -> Permutation+  oddTau !e p = if even e then p else tauPerm p++{-+  checkDelta :: Int -> Permutation -> [Permutation] -> (Int,[Permutation])+  checkDelta !e !p !rest +    | P.isIdentityPermutation p  = worker  e    rest+    | isReversePermutation    p  = worker (e+1) rest+    | otherwise                  = let (e',rs) = worker e rest in (e', oddTau e p : rs)+-}        ++-------------------------------------------------------------------------------- ++-- | Given a /positive/ word, we apply left-greedy factorization of+-- that word into subwords representing /permutation braids/.+--+-- Example 5.1 from the above handbook:+--+-- > leftGreedyFactors 7 [1,3,2,2,1,3,3,2,3,2] == [[1,3,2],[2,1,3],[3,2,3],[2]]+--+leftGreedyFactors :: Int -> [Int] -> [[Int]]+leftGreedyFactors n input = filter (not . null) $ runST (action input) where++  action :: forall s. [Int] -> ST s [[Int]]+  action input = 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 clearMat = forM_ [1..n] $ \i -> +          forM_ [1..n] $ \j -> writeArray mat (i,j) 0+          +    let doAdd1 :: Int -> Int -> ST s Int+        doAdd1 i j = do+          x <- readArray mat (i,j)+          let y = x+1+          writeArray mat (i,j) y +          writeArray mat (j,i) y+          return y+           +    let worker :: [Int] -> ST s [[Int]]+        worker []     = return [[]]+        worker (p:ps) = do+          u <- readArray perm  p +          v <- readArray perm (p+1)+          c <- doAdd1 u v +          doSwap p+          if c<=1+            then do+              (f:fs) <- worker ps+              return ((p:f):fs)+            else do+              clearMat+              fs <- worker (p:ps)+              return ([]:fs)+              +    worker input++--------------------------------------------------------------------------------++{-++-- | Finds ternary braid relations, and returns them as a list of indices, decorated+-- with a flag specifying which side of the relation we found, a sign specifying+-- whether it is a relation between positive or negative generators.+--+findTernaryBraidRelations :: Braid n -> [(Int,Bool,Sign)]+findTernaryBraidRelations (Braid gens) = go 0 gens where+  go !k (Sigma a : rest@(Sigma b : Sigma c : _))  +    | a==c && b==a+1 = (k,True ,Plus) : go (k+1) rest+    | a==c && b==a-1 = (k,False,Plus) : go (k+1) rest+    | otherwise      =                  go (k+1) rest+  go !k (SigmaInv a : rest@(SigmaInv b : SigmaInv c : _))  +    | a==c && b==a+1 = (k,True ,Minus) : go (k+1) rest+    | a==c && b==a-1 = (k,False,Minus) : go (k+1) rest+    | otherwise      =                   go (k+1) rest+  go !k (x:xs) = go (k+1) xs+  go _  []     = []++-- | Finds subsequences like @(i,i+1,i)@ and @(i+1,i,i+1)@, and returns them+-- and a list of indices, plus a flag specifying which one we found (the first +-- one is 'True', second one is 'False')+--+_findTernaryBraidRelations :: [Int] -> [(Int,Bool)]+_findTernaryBraidRelations = go 0 where+  go !k (a:rest@(b:c:_))  +    | a==c && b==a+1 = (k,True ) : go (k+1) rest+    | a==c && b==a-1 = (k,False) : go (k+1) rest+    | otherwise      =             go (k+1) rest+  go !k (x:xs) = go (k+1) xs+  go _  []     = []++-}++--------------------------------------------------------------------------------+
+ Math/Combinat/Groups/Free.hs view
@@ -0,0 +1,523 @@++-- | Words in free groups (and free powers of cyclic groups).+--+-- This module is not re-exported by "Math.Combinat"+--+{-# LANGUAGE CPP, BangPatterns, PatternGuards #-}+module Math.Combinat.Groups.Free where++--------------------------------------------------------------------------------++-- new Base exports "Word" from Data.Word...+#ifdef MIN_VERSION_base+#if MIN_VERSION_base(4,7,1)+import Prelude hiding ( Word )+#endif+#elif __GLASGOW_HASKELL__ >= 709+import Prelude hiding ( Word )+#endif++import Data.Char     ( chr )+import Data.List     ( mapAccumL , groupBy )++import Control.Monad ( liftM )+import System.Random++import Math.Combinat.Numbers+import Math.Combinat.Sign+import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * Words++-- | A generator of a (free) group, indexed by which \"copy\" of the group we are dealing with.+data Generator idx+  = Gen !idx          -- @a@+  | Inv !idx          -- @a^(-1)@+  deriving (Eq,Ord,Show,Read)++-- | The index of a generator+genIdx :: Generator idx -> idx+genIdx g = case g of+  Gen x -> x+  Inv x -> x++-- | The sign of the (exponent of the) generator (that is, the generator is 'Plus', the inverse is 'Minus')+genSign :: Generator idx -> Sign+genSign g = case g of { Gen _ -> Plus ; Inv _ -> Minus }  ++genSignValue :: Generator idx -> Int+genSignValue g = case g of { Gen _ -> (1::Int) ; Inv _ -> (-1::Int) } ++-- | keep the index, but return always the 'Gen' one.+absGen :: Generator idx -> Generator idx +absGen g = case g of+  Gen x -> Gen x+  Inv x -> Gen x++-- | A /word/, describing (non-uniquely) an element of a group.+-- The identity element is represented (among others) by the empty word.+type Word idx = [Generator idx] ++--------------------------------------------------------------------------------++-- | Generators are shown as small letters: @a@, @b@, @c@, ...+-- and their inverses are shown as capital letters, so @A=a^-1@, @B=b^-1@, etc.+showGen :: Generator Int -> Char+showGen (Gen i) = chr (96+i)+showGen (Inv i) = chr (64+i)++showWord :: Word Int -> String+showWord = map showGen++--------------------------------------------------------------------------------+  +instance Functor Generator where+  fmap f g = case g of +    Gen x -> Gen (f x) +    Inv y -> Inv (f y)+    +--------------------------------------------------------------------------------++-- | The inverse of a generator+inverseGen :: Generator a -> Generator a+inverseGen g = case g of+  Gen x -> Inv x+  Inv x -> Gen x++-- | The inverse of a word+inverseWord :: Word a -> Word a+inverseWord = map inverseGen . reverse++-- | Lists all words of the given length (total number will be @(2g)^n@).+-- The numbering of the generators is @[1..g]@.+allWords +  :: Int         -- ^ @g@ = number of generators +  -> Int         -- ^ @n@ = length of the word+  -> [Word Int]+allWords g = go where+  go !0 = [[]]+  go !n = [ x:xs | xs <- go (n-1) , x <- elems ]+  elems =  [ Gen a | a<-[1..g] ]+        ++ [ Inv a | a<-[1..g] ]++-- | Lists all words of the given length which do not contain inverse generators+-- (total number will be @g^n@).+-- The numbering of the generators is @[1..g]@.+allWordsNoInv +  :: Int         -- ^ @g@ = number of generators +  -> Int         -- ^ @n@ = length of the word+  -> [Word Int]+allWordsNoInv g = go where+  go !0 = [[]]+  go !n = [ x:xs | xs <- go (n-1) , x <- elems ]+  elems = [ Gen a | a<-[1..g] ]++--------------------------------------------------------------------------------+-- * Random words++-- | A random group generator (or its inverse) between @1@ and @g@+randomGenerator+  :: RandomGen g+  => Int         -- ^ @g@ = number of generators +  -> g -> (Generator Int, g)+randomGenerator !d !g0 = (gen, g2) where+  (b, !g1) = random        g0+  (k, !g2) = randomR (1,d) g1+  gen = if b then Gen k else Inv k++-- | A random group generator (but never its inverse) between @1@ and @g@+randomGeneratorNoInv+  :: RandomGen g+  => Int         -- ^ @g@ = number of generators +  -> g -> (Generator Int, g)+randomGeneratorNoInv !d !g0 = (Gen k, g1) where+  (!k, !g1) = randomR (1,d) g0++-- | A random word of length @n@ using @g@ generators (or their inverses)+randomWord +  :: RandomGen g+  => Int         -- ^ @g@ = number of generators +  -> Int         -- ^ @n@ = length of the word+  -> g -> (Word Int, g)+randomWord !d !n !g0 = (word,g1) where+  (g1,word) = mapAccumL (\g _ -> swap (randomGenerator d g)) g0 [1..n]   ++-- | A random word of length @n@ using @g@ generators (but not their inverses)+randomWordNoInv+  :: RandomGen g+  => Int         -- ^ @g@ = number of generators +  -> Int         -- ^ @n@ = length of the word+  -> g -> (Word Int, g)+randomWordNoInv !d !n !g0 = (word,g1) where+  (g1,word) = mapAccumL (\g _ -> swap (randomGeneratorNoInv d g)) g0 [1..n]   +  +--------------------------------------------------------------------------------+-- * The free group on @g@ generators++{-# SPECIALIZE multiplyFree        :: Word Int -> Word Int -> Word Int #-}+{-# SPECIALIZE equivalentFree      :: Word Int -> Word Int -> Bool     #-}+{-# SPECIALIZE reduceWordFree      :: Word Int -> Word Int #-}+{-# SPECIALIZE reduceWordFreeNaive :: Word Int -> Word Int #-}++-- | Multiplication of the free group (returns the reduced result). It is true+-- for any two words w1 and w2 that+--+-- > multiplyFree (reduceWordFree w1) (reduceWord w2) = multiplyFree w1 w2+--+multiplyFree :: Eq idx => Word idx -> Word idx -> Word idx+multiplyFree w1 w2 = reduceWordFree (w1 ++ w2)++-- | Decides whether two words represent the same group element in the free group+equivalentFree :: Eq idx => Word idx -> Word idx -> Bool+equivalentFree w1 w2 = null $ reduceWordFree $ w1 ++ inverseWord w2++-- | Reduces a word in a free group by repeatedly removing @x*x^(-1)@ and+-- @x^(-1)*x@ pairs. The set of /reduced words/ forms the free group; the+-- multiplication is obtained by concatenation followed by reduction.+--+reduceWordFree :: Eq idx => Word idx -> Word idx+reduceWordFree = loop where++  loop w = case reduceStep w of+    Nothing -> w+    Just w' -> loop w'+  +  reduceStep :: Eq a => Word a -> Maybe (Word a)+  reduceStep = go False where    +    go !changed w = case w of+      (Gen x : Inv y : rest) | x==y   -> go True rest+      (Inv x : Gen y : rest) | x==y   -> go True rest+      (this : rest)                   -> liftM (this:) $ go changed rest+      _                               -> if changed then Just w else Nothing+++-- | Naive (but canonical) reduction algorithm for the free groups+reduceWordFreeNaive :: Eq idx => Word idx -> Word idx+reduceWordFreeNaive = loop where+  loop w = let w' = step w in if w/=w' then loop w' else w+  step   = concatMap worker . groupBy (equating genIdx) where+  worker gs +    | s>0       = replicate      s  (Gen i)+    | s<0       = replicate (abs s) (Inv i)+    | otherwise = []+    where +      i = genIdx (head gs)+      s = sum' (map genSignValue gs)++--------------------------------------------------------------------------------++-- | Counts the number of words of length @n@ which reduce to the identity element.+--+-- Generating function is @Gf_g(u) = \\frac {2g-1} { g-1 + g \\sqrt{ 1 - (8g-4)u^2 } }@+--+countIdentityWordsFree+  :: Int   -- ^ g = number of generators in the free group+  -> Int   -- ^ n = length of the unreduced word+  -> Integer+countIdentityWordsFree g n = countWordReductionsFree g n 0+  +-- | Counts the number of words of length @n@ whose reduced form has length @k@+-- (clearly @n@ and @k@ must have the same parity for this to be nonzero):+--+-- > countWordReductionsFree g n k == sum [ 1 | w <- allWords g n, k == length (reduceWordFree w) ]+--+countWordReductionsFree +  :: Int   -- ^ g = number of generators in the free group+  -> Int   -- ^ n = length of the unreduced word+  -> Int   -- ^ k = length of the reduced word+  -> Integer+countWordReductionsFree gens_ nn_ kk_+  | nn==0              = if k==0 then 1 else 0+  | even nn && kk == 0 = sum [ ( binomial (nn-i) (n  -i) * gg^(i  ) * (gg-1)^(n  -i  ) * (   i) ) `div` (nn-i) | i<-[0..n  ] ]+  | even nn && even kk = sum [ ( binomial (nn-i) (n-k-i) * gg^(i+1) * (gg-1)^(n+k-i-1) * (kk+i) ) `div` (nn-i) | i<-[0..n-k] ] +  | odd  nn && odd  kk = sum [ ( binomial (nn-i) (n-k-i) * gg^(i+1) * (gg-1)^(n+k-i  ) * (kk+i) ) `div` (nn-i) | i<-[0..n-k] ]+  | otherwise          = 0  +  where+    g  = fromIntegral gens_ :: Integer+    nn = fromIntegral nn_   :: Integer+    kk = fromIntegral kk_   :: Integer+    +    gg = 2*g+    n = div nn 2+    k = div kk 2+    +--------------------------------------------------------------------------------+-- * Free powers of cyclic groups++{-# SPECIALIZE multiplyZ2 ::        Word Int -> Word Int -> Word Int #-}+{-# SPECIALIZE multiplyZ3 ::        Word Int -> Word Int -> Word Int #-}+{-# SPECIALIZE multiplyZm :: Int -> Word Int -> Word Int -> Word Int #-}++-- | Multiplication in free products of Z2's+multiplyZ2 :: Eq idx => Word idx -> Word idx -> Word idx+multiplyZ2 w1 w2 = reduceWordZ2 (w1 ++ w2)++-- | Multiplication in free products of Z3's+multiplyZ3 :: Eq idx => Word idx -> Word idx -> Word idx+multiplyZ3 w1 w2 = reduceWordZ3 (w1 ++ w2)++-- | Multiplication in free products of Zm's+multiplyZm :: Eq idx => Int -> Word idx -> Word idx -> Word idx+multiplyZm k w1 w2 = reduceWordZm k (w1 ++ w2)++--------------------------------------------------------------------------------++{-# SPECIALIZE equivalentZ2 ::        Word Int -> Word Int -> Bool #-}+{-# SPECIALIZE equivalentZ3 ::        Word Int -> Word Int -> Bool #-}+{-# SPECIALIZE equivalentZm :: Int -> Word Int -> Word Int -> Bool #-}++-- | Decides whether two words represent the same group element in free products of Z2+equivalentZ2 :: Eq idx => Word idx -> Word idx -> Bool+equivalentZ2 w1 w2 = null $ reduceWordZ2 $ w1 ++ inverseWord w2++-- | Decides whether two words represent the same group element in free products of Z3+equivalentZ3 :: Eq idx => Word idx -> Word idx -> Bool+equivalentZ3 w1 w2 = null $ reduceWordZ3 $ w1 ++ inverseWord w2++-- | Decides whether two words represent the same group element in free products of Zm+equivalentZm :: Eq idx => Int -> Word idx -> Word idx -> Bool+equivalentZm m w1 w2 = null $ reduceWordZm m $ w1 ++ inverseWord w2++--------------------------------------------------------------------------------++{-# SPECIALIZE reduceWordZ2 ::        Word Int -> Word Int #-}+{-# SPECIALIZE reduceWordZ3 ::        Word Int -> Word Int #-}+{-# SPECIALIZE reduceWordZm :: Int -> Word Int -> Word Int #-}++--------------------------------------------------------------------------------++-- | Reduces a word, where each generator @x@ satisfies the additional relation @x^2=1@+-- (that is, free products of Z2's)+reduceWordZ2 :: Eq idx => Word idx -> Word idx+reduceWordZ2 = loop where+  loop w = case reduceStep w of+    Nothing -> w+    Just w' -> loop w'+ +  reduceStep :: Eq a => Word a -> Maybe (Word a)+  reduceStep = go False where   +    go !changed w = case w of+      (Gen x : Gen y : rest) | x==y   -> go True rest+      (Gen x : Inv y : rest) | x==y   -> go True rest+      (Inv x : Gen y : rest) | x==y   -> go True rest+      (Inv x : Inv y : rest) | x==y   -> go True rest+      (this : rest)                   -> liftM (absGen this:) $ go changed rest+      _                               -> if changed then Just w else Nothing++-- | Reduces a word, where each generator @x@ satisfies the additional relation @x^3=1@+-- (that is, free products of Z3's)+reduceWordZ3 :: Eq idx => Word idx -> Word idx+reduceWordZ3 = loop where+  loop w = case reduceStep w of+    Nothing -> w+    Just w' -> loop w'+ +  reduceStep :: Eq a => Word a -> Maybe (Word a)+  reduceStep = go False where   +    go !changed w = case w of+      (Gen x : Inv y : rest)         | x==y           -> go True rest+      (Inv x : Gen y : rest)         | x==y           -> go True rest+      (Gen x : Gen y : Gen z : rest) | x==y && y==z   -> go True rest+      (Inv x : Inv y : Inv z : rest) | x==y && y==z   -> go True rest+      (Gen x : Gen y : rest)         | x==y           -> go True (Inv x : rest)       -- !!!+      (Inv x : Inv y : rest)         | x==y           -> go True (Gen x : rest)+      (this : rest)                                   -> liftM (this:) $ go changed rest+      _                                               -> if changed then Just w else Nothing+      +-- | Reduces a word, where each generator @x@ satisfies the additional relation @x^m=1@+-- (that is, free products of Zm's)+reduceWordZm :: Eq idx => Int -> Word idx -> Word idx+reduceWordZm m = loop where++  loop w = case reduceStep w of+    Nothing -> w+    Just w' -> loop w'++  halfm = div m 2  -- if we encounter strictly more than m/2 equal elements in a row, we replace them by the inverses+ +  -- reduceStep :: Eq a => Word a -> Maybe (Word a)+  reduceStep = go False where   +    go !changed w = case w of+      (Gen x : Inv y : rest) | x==y                        -> go True rest+      (Inv x : Gen y : rest) | x==y                        -> go True rest+      something | Just (k,rest) <- dropIfMoreThanHalf w    -> go True (replicate (m-k) (inverseGen (head w)) ++ rest)+      (this : rest)                                        -> liftM (this:) $ go changed rest+      _                                                    -> if changed then Just w else Nothing+  +  -- dropIfMoreThanHalf :: Eq a => Word a -> Maybe (Int, Word a)+  dropIfMoreThanHalf w = +    let (!k,rest) = dropWhileEqual w +    in  if k > halfm then Just (k,rest)+                     else Nothing+                     +  -- dropWhileEqual :: Eq a => Word a -> (Int, Word a) +  dropWhileEqual []     = (0,[])+  dropWhileEqual (x0:rest) = go 1 rest where+    go !k []         = (k,[])+    go !k xxs@(x:xs) = if k==m then (m,xxs) +                               else if x==x0 then go (k+1) xs +                                             else (k,xxs)++{-  +  dropm :: Eq a => Word a -> Maybe (Word a)    +  dropm []     = Nothing+  dropm (x:xs) = go (m-1) xs where+    go 0 rest    = Just rest+    go j (y:ys)  = if y==x +      then go (j-1) ys+      else Nothing +    go j []      = Nothing+-}++--------------------------------------------------------------------------------++{-# SPECIALIZE reduceWordZ2Naive ::        Word Int -> Word Int #-}+{-# SPECIALIZE reduceWordZ3Naive ::        Word Int -> Word Int #-}+{-# SPECIALIZE reduceWordZmNaive :: Int -> Word Int -> Word Int #-}++-- | Reduces a word, where each generator @x@ satisfies the additional relation @x^2=1@+-- (that is, free products of Z2's). Naive (but canonical) algorithm.+reduceWordZ2Naive :: Eq idx => Word idx -> Word idx+reduceWordZ2Naive = loop where+  loop w = let w' = step w in if w/=w' then loop w' else w+  step   = concatMap worker . groupBy (equating genIdx) where+  worker gs = +    case mod s 2 of+      1 -> [Gen i]+      0 -> []+      _ -> error "reduceWordZ2: fatal error, shouldn't happen"+    where +      i = genIdx (head gs)+      s = sum' (map genSignValue gs)++-- | Reduces a word, where each generator @x@ satisfies the additional relation @x^3=1@+-- (that is, free products of Z3's). Naive (but canonical) algorithm.+reduceWordZ3Naive :: Eq idx => Word idx -> Word idx+reduceWordZ3Naive = loop where+  loop w = let w' = step w in if w/=w' then loop w' else w+  step   = concatMap worker . groupBy (equating genIdx) where+  worker gs = +    case mod s 3 of+      0 -> []+      1 -> [Gen i]+      2 -> [Inv i]+      _ -> error "reduceWordZ3: fatal error, shouldn't happen"+    where +      i = genIdx (head gs)+      s = sum' (map genSignValue gs)++-- | Reduces a word, where each generator @x@ satisfies the additional relation @x^m=1@+-- (that is, free products of Zm's). Naive (but canonical) algorithm.+reduceWordZmNaive :: Eq idx => Int -> Word idx -> Word idx+reduceWordZmNaive m = loop where+  loop w = let w' = step w in if w/=w' then loop w' else w+  step   = concatMap worker . groupBy (equating genIdx) where+  halfm1 = div (m+1) 2+  worker gs +    | mods <= halfm1  = replicate    mods  (Gen i)+    | otherwise       = replicate (m-mods) (Inv i)+    where +      i = genIdx (head gs)+      s = sum' (map genSignValue gs)+      mods = mod s m++--------------------------------------------------------------------------------++-- | Counts the number of words (without inverse generators) of length @n@ +-- which reduce to the identity element, using the relations @x^2=1@.+--+-- Generating function is @Gf_g(u) = \\frac {2g-2} { g-2 + g \\sqrt{ 1 - (4g-4)u^2 } }@+--+-- The first few @g@ cases:+--+-- > A000984 = [ countIdentityWordsZ2 2 (2*n) | n<-[0..] ] = [1,2,6,20,70,252,924,3432,12870,48620,184756...]+-- > A089022 = [ countIdentityWordsZ2 3 (2*n) | n<-[0..] ] = [1,3,15,87,543,3543,23823,163719,1143999,8099511,57959535...]+-- > A035610 = [ countIdentityWordsZ2 4 (2*n) | n<-[0..] ] = [1,4,28,232,2092,19864,195352,1970896,20275660,211823800,2240795848...]+-- > A130976 = [ countIdentityWordsZ2 5 (2*n) | n<-[0..] ] = [1,5,45,485,5725,71445,925965,12335685,167817405,2321105525,32536755565...]+--+countIdentityWordsZ2+  :: Int   -- ^ g = number of generators in the free group+  -> Int   -- ^ n = length of the unreduced word+  -> Integer+countIdentityWordsZ2 g n = countWordReductionsZ2 g n 0++-- | Counts the number of words (without inverse generators) of length @n@ whose +-- reduced form in the product of Z2-s (that is, for each generator @x@ we have @x^2=1@) +-- has length @k@+-- (clearly @n@ and @k@ must have the same parity for this to be nonzero):+--+-- > countWordReductionsZ2 g n k == sum [ 1 | w <- allWordsNoInv g n, k == length (reduceWordZ2 w) ]+--+countWordReductionsZ2 +  :: Int   -- ^ g = number of generators in the free group+  -> Int   -- ^ n = length of the unreduced word+  -> Int   -- ^ k = length of the reduced word+  -> Integer+countWordReductionsZ2 gens_ nn_ kk_+  | nn==0              = if k==0 then 1 else 0+  | even nn && kk == 0 = sum [ ( binomial (nn-i) (n  -i) * g^(i  ) * (g-1)^(n  -i  ) * (   i) ) `div` (nn-i) | i<-[0..n  ] ]+  | even nn && even kk = sum [ ( binomial (nn-i) (n-k-i) * g^(i+1) * (g-1)^(n+k-i-1) * (kk+i) ) `div` (nn-i) | i<-[0..n-k] ] +  | odd  nn && odd  kk = sum [ ( binomial (nn-i) (n-k-i) * g^(i+1) * (g-1)^(n+k-i  ) * (kk+i) ) `div` (nn-i) | i<-[0..n-k] ]+  | otherwise          = 0  +  where+    g  = fromIntegral gens_ :: Integer+    nn = fromIntegral nn_   :: Integer+    kk = fromIntegral kk_   :: Integer+    +    n = div nn 2+    k = div kk 2++-- | Counts the number of words (without inverse generators) of length @n@ +-- which reduce to the identity element, using the relations @x^3=1@.+--+-- > countIdentityWordsZ3NoInv g n == sum [ 1 | w <- allWordsNoInv g n, 0 == length (reduceWordZ2 w) ]+--+-- In mathematica, the formula is: @Sum[ g^k * (g-1)^(n-k) * k/n * Binomial[3*n-k-1, n-k] , {k, 1,n} ]@+--+countIdentityWordsZ3NoInv+  :: Int   -- ^ g = number of generators in the free group+  -> Int   -- ^ n = length of the unreduced word+  -> Integer+countIdentityWordsZ3NoInv gens_ nn_ +  | nn==0           = 1+  | mod nn 3 == 0   = sum [ ( binomial (3*n-i-1) (n-i) * g^i * (g-1)^(n-i) * i ) `div` n | i<-[1..n] ]+  | otherwise       = 0+  where+    g  = fromIntegral gens_ :: Integer+    nn = fromIntegral nn_   :: Integer+    +    n = div nn 3+  +--------------------------------------------------------------------------------+      +{-++-- some basic testing. TODO: real tests++import Math.Combinat.Helper+import Math.Combinat.Groups.Free++g    = 3 :: Int+maxn = 8 :: Int++bad_free = [ w | n<-[0..maxn] , w <- allWords g n , not (reduceWordFree w `equivalentFree` reduceWordFreeNaive w) ]+bad_z2   = [ w | n<-[0..maxn] , w <- allWords g n , not (reduceWordZ2   w `equivalentZ2`   reduceWordZ2Naive   w) ]+bad_z3   = [ w | n<-[0..maxn] , w <- allWords g n , not (reduceWordZ3   w `equivalentZ3`   reduceWordZ3Naive   w) ]+bad_zm m = [ w | n<-[0..maxn] , w <- allWords g n , not (equivalentZm m (reduceWordZm m w) (reduceWordZmNaive m w)) ]++speed_free = sum' [ length (reduceWordFree w) | n<-[0..maxn] , w <- allWords g n ]+speed_z2   = sum' [ length (reduceWordZ2   w) | n<-[0..maxn] , w <- allWords g n ]+speed_z3   = sum' [ length (reduceWordZ3   w) | n<-[0..maxn] , w <- allWords g n ]+speed_zm m = sum' [ length (reduceWordZm m w) | n<-[0..maxn] , w <- allWords g n ]++naive_speed_free = sum' [ length (reduceWordFreeNaive w) | n<-[0..maxn] , w <- allWords g n ]+naive_speed_z2   = sum' [ length (reduceWordZ2Naive   w) | n<-[0..maxn] , w <- allWords g n ]+naive_speed_z3   = sum' [ length (reduceWordZ3Naive   w) | n<-[0..maxn] , w <- allWords g n ]+naive_speed_zm m = sum' [ length (reduceWordZmNaive m w) | n<-[0..maxn] , w <- allWords g n ]++-}++--------------------------------------------------------------------------------++
+ Math/Combinat/Groups/Thompson/F.hs view
@@ -0,0 +1,404 @@++-- | Thompson's group F.+--+-- See eg. <https://en.wikipedia.org/wiki/Thompson_groups>+--+-- Based mainly on James Michael Belk's PhD thesis \"THOMPSON'S GROUP F\";+-- see <http://www.math.u-psud.fr/~breuilla/Belk.pdf>+--++{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, BangPatterns, PatternSynonyms, DeriveFunctor #-}+module Math.Combinat.Groups.Thompson.F where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Classes+import Math.Combinat.ASCII++import Math.Combinat.Trees.Binary ( BinTree )+import qualified Math.Combinat.Trees.Binary as B++--------------------------------------------------------------------------------+-- * Tree diagrams++-- | A tree diagram, consisting of two binary trees with the same number of leaves, +-- representing an element of the group F.+data TDiag = TDiag +  { _width  :: !Int      -- ^ the width is the number of leaves, minus 1, of both diagrams+  , _domain :: !T        -- ^ the top diagram correspond to the /domain/+  , _range  :: !T        -- ^ the bottom diagram corresponds to the /range/+  }+  deriving (Eq,Ord,Show)++instance DrawASCII TDiag where+  ascii = asciiTDiag++instance HasWidth TDiag where+  width = _width++-- | Creates a tree diagram from two trees+mkTDiag :: T -> T -> TDiag +mkTDiag d1 d2 = reduce $ mkTDiagDontReduce d1 d2++-- | Creates a tree diagram, but does not reduce it.+mkTDiagDontReduce :: T -> T -> TDiag +mkTDiagDontReduce top bot = +  if w1 == w2 +    then TDiag w1 top bot +    else error "mkTDiag: widths do not match"+  where+    w1 = treeWidth top +    w2 = treeWidth bot+++isValidTDiag :: TDiag -> Bool+isValidTDiag (TDiag w top bot) = (treeWidth top == w && treeWidth bot == w)++isPositive :: TDiag -> Bool+isPositive (TDiag w top bot) = (bot == rightVine w)++isReduced :: TDiag -> Bool+isReduced diag = (reduce diag == diag)++-- | The generator x0+x0 :: TDiag+x0 = TDiag 2 top bot where+  top = branch caret leaf+  bot = branch leaf  caret++-- | The generator x1+x1 :: TDiag+x1 = xk 1++-- | The generators x0, x1, x2 ...+xk :: Int -> TDiag+xk = go where+  go k | k< 0      = error "xk: negative indexed generator"+       | k==0      = x0+       | otherwise = let TDiag _ t b = go (k-1) +                     in  TDiag (k+2) (branch leaf t) (branch leaf b)++-- | The identity element in the group F                     +identity :: TDiag+identity = TDiag 0 Lf Lf++-- | A /positive diagram/ is a diagram whose bottom tree (the range) is a right vine.+positive :: T -> TDiag+positive t = TDiag w t (rightVine w) where w = treeWidth t++-- | Swaps the top and bottom of a tree diagram. This is the inverse in the group F.+-- (Note: we don't do reduction here, as this operation keeps the reducedness)+inverse :: TDiag -> TDiag+inverse (TDiag w top bot) = TDiag w bot top++-- | Decides whether two (possibly unreduced) tree diagrams represents the same group element in F.+equivalent :: TDiag -> TDiag -> Bool+equivalent diag1 diag2 = (identity == reduce (compose diag1 (inverse diag2)))++--------------------------------------------------------------------------------+-- * Reduction of tree diagrams++-- | Reduces a diagram. The result is a normal form of an element in the group F.+reduce :: TDiag -> TDiag+reduce = worker where++  worker :: TDiag -> TDiag+  worker diag = case step diag of+    Nothing    -> diag+    Just diag' -> worker diag'++  step :: TDiag -> Maybe TDiag+  step (TDiag w top bot) = +    if null idxs +      then Nothing+      else Just $ TDiag w' top' bot'+    where+      cs1  = treeCaretList top+      cs2  = treeCaretList bot+      idxs = sortedIntersect cs1 cs2+      w'   = w - length idxs+      top' = removeCarets idxs top+      bot' = removeCarets idxs bot++  -- | Intersects sorted lists      +  sortedIntersect :: [Int] -> [Int] -> [Int]+  sortedIntersect = go where+    go [] _  = []+    go _  [] = []+    go xxs@(x:xs) yys@(y:ys) = case compare x y of+      LT ->     go  xs yys+      EQ -> x : go  xs  ys+      GT ->     go xxs  ys++-- | List of carets at the bottom of the tree, indexed by their left edge position+treeCaretList :: T -> [Int]+treeCaretList = snd . go 0 where+  go !x t = case t of +    Lf        ->  (x+1 , []  )+    Ct        ->  (x+2 , [x] )+    Br t1 t2  ->  (x2  , cs1++cs2) where+      (x1 , cs1) = go x  t1+      (x2 , cs2) = go x1 t2++-- | Remove the carets with the given indices +-- (throws an error if there is no caret at the given index)+removeCarets :: [Int] -> T -> T+removeCarets idxs tree = if null rem then final else error ("removeCarets: some stuff remained: " ++ show rem) where++  (_,rem,final) =  go 0 idxs tree where++  go :: Int -> [Int] -> T -> (Int,[Int],T)+  go !x []         t  = (x + treeWidth t , [] , t)+  go !x iis@(i:is) t  = case t of+    Lf        ->  (x+1 , iis , t)+    Ct        ->  if x==i then (x+2 , is , Lf) else (x+2 , iis , Ct)+    Br t1 t2  ->  (x2  , iis2 , Br t1' t2') where+      (x1 , iis1 , t1') = go x  iis  t1+      (x2 , iis2 , t2') = go x1 iis1 t2+      +--------------------------------------------------------------------------------+-- * Composition of tree diagrams++-- | If @diag1@ corresponds to the PL function @f@, and @diag2@ to @g@, then @compose diag1 diag2@ +-- will correspond to @(g.f)@ (note that the order is opposite than normal function composition!)+--+-- This is the multiplication in the group F.+--+compose :: TDiag -> TDiag -> TDiag+compose d1 d2 = reduce (composeDontReduce d1 d2)++-- | Compose two tree diagrams without reducing the result+composeDontReduce :: TDiag -> TDiag -> TDiag+composeDontReduce (TDiag w1 top1 bot1) (TDiag w2 top2 bot2) = new where+  new = mkTDiagDontReduce top' bot' +  (list1,list2) = extensionToCommonTree bot1 top2+  top' = listGraft list1 top1+  bot' = listGraft list2 bot2++-- | Given two binary trees, we return a pair of list of subtrees which, grafted the to leaves of+-- the first (resp. the second) tree, results in the same extended tree.+extensionToCommonTree :: T -> T -> ([T],[T])+extensionToCommonTree t1 t2 = snd $ go (0,0) (t1,t2) where+  go (!x1,!x2) (!t1,!t2) = +    case (t1,t2) of+      ( Lf       , Lf       ) -> ( (x1+n1 , x2+n2 ) , (             [Lf] ,             [Lf] ) )+      ( Lf       , Br _  _  ) -> ( (x1+n1 , x2+n2 ) , (             [t2] , replicate n2 Lf  ) )+      ( Br _  _  , Lf       ) -> ( (x1+n1 , x2+n2 ) , ( replicate n1 Lf  ,             [t1] ) )+      ( Br l1 r1 , Br l2 r2 ) +        -> let ( (x1' ,x2' ) , (ps1,ps2) ) = go (x1 ,x2 ) (l1,l2)+               ( (x1'',x2'') , (qs1,qs2) ) = go (x1',x2') (r1,r2)+           in  ( (x1'',x2'') , (ps1++qs1, ps2++qs2) )+    where+      n1 = numberOfLeaves t1+      n2 = numberOfLeaves t2++--------------------------------------------------------------------------------+-- * Subdivions++-- | Returns the list of dyadic subdivision points+subdivision1 :: T -> [Rational]+subdivision1 = go 0 1 where+  go !a !b t = case t of+    Leaf   _   -> [a,b]+    Branch l r -> go a c l ++ tail (go c b r) where c = (a+b)/2++-- | Returns the list of dyadic intervals+subdivision2 :: T -> [(Rational,Rational)]+subdivision2 = go 0 1 where+  go !a !b t = case t of+    Leaf   _   -> [(a,b)]+    Branch l r -> go a c l ++ go c b r where c = (a+b)/2+++--------------------------------------------------------------------------------+-- * Binary trees++-- | A (strict) binary tree with labelled leaves (but unlabelled nodes)+data Tree a+  = Branch !(Tree a) !(Tree a)+  | Leaf   !a+  deriving (Eq,Ord,Show,Functor)++-- | The monadic join operation of binary trees+graft :: Tree (Tree a) -> Tree a+graft = go where+  go (Branch l r) = Branch (go l) (go r)+  go (Leaf   t  ) = t ++-- | A list version of 'graft'+listGraft :: [Tree a] -> Tree b -> Tree a+listGraft subs big = snd $ go subs big where  +  go ggs@(g:gs) t = case t of+    Leaf   _   -> (gs,g)+    Branch l r -> (gs2, Branch l' r') where+                    (gs1,l') = go ggs l+                    (gs2,r') = go gs1 r++-- | A completely unlabelled binary tree+type T = Tree ()++instance DrawASCII T where+  ascii = asciiT ++instance HasNumberOfLeaves (Tree a) where+  numberOfLeaves = treeNumberOfLeaves++instance HasWidth (Tree a) where+  width = treeWidth++leaf :: T+leaf = Leaf ()++branch :: T -> T -> T+branch = Branch++caret :: T+caret = branch leaf leaf++treeNumberOfLeaves :: Tree a -> Int+treeNumberOfLeaves = go where+  go (Branch l r) = go l + go r+  go (Leaf   _  ) = 1  ++-- | The width of the tree is the number of leaves minus 1.+treeWidth :: Tree a -> Int+treeWidth t = numberOfLeaves t - 1++-- | Enumerates the leaves a tree, starting from 0+enumerate_ :: Tree a -> Tree Int+enumerate_ = snd . enumerate++-- | Enumerates the leaves a tree, and also returns the number of leaves+enumerate :: Tree a -> (Int, Tree Int)+enumerate = go 0 where+  go !k t = case t of+    Leaf   _   -> (k+1 , Leaf k)+    Branch l r -> let (k' ,l') = go k  l+                      (k'',r') = go k' r+                  in (k'', Branch l' r') ++-- | \"Right vine\" of the given width +rightVine :: Int -> T+rightVine k +  | k< 0      = error "rightVine: negative width"+  | k==0      = leaf+  | otherwise = branch leaf (rightVine (k-1))++-- | \"Left vine\" of the given width +leftVine :: Int -> T+leftVine k +  | k< 0      = error "leftVine: negative width"+  | k==0      = leaf+  | otherwise = branch (leftVine (k-1)) leaf ++-- | Flips each node of a binary tree+flipTree :: Tree a -> Tree a+flipTree = go where+  go t = case t of+    Leaf   _   -> t+    Branch l r -> Branch (go r) (go l)++--------------------------------------------------------------------------------+-- * Conversion to\/from BinTree++-- | 'Tree' and 'BinTree' are the same type, except that 'Tree' is strict.+--+-- TODO: maybe unify these two types? Until that, you can convert between the two+-- with these functions if necessary.+--+toBinTree :: Tree a -> B.BinTree a+toBinTree = go where+  go (Branch l r) = B.Branch (go l) (go r)+  go (Leaf   y  ) = B.Leaf   y++fromBinTree :: B.BinTree a -> Tree a +fromBinTree = go where+  go (B.Branch l r) = Branch (go l) (go r)+  go (B.Leaf   y  ) = Leaf   y+    +--------------------------------------------------------------------------------+-- * Pattern synonyms++pattern Lf     = Leaf ()+pattern Br l r = Branch l r+pattern Ct     = Br Lf Lf+pattern X0     = TDiag 2        (Br Ct Lf)         (Br Lf Ct)+pattern X1     = TDiag 3 (Br Lf (Br Ct Lf)) (Br Lf (Br Lf Ct))++--------------------------------------------------------------------------------+-- * ASCII++-- | Draws a binary tree, with all leaves at the same (bottom) row+asciiT :: T -> ASCII+asciiT = asciiT' False++-- | Draws a binary tree; when the boolean flag is @True@, we draw upside down+asciiT' :: Bool -> T -> ASCII+asciiT' inv = go where++  go t = case t of+    Leaf _                   -> emptyRect +    Branch l r -> +      if yl >= yr+        then pasteOnto (yl+yr+1,if inv then yr else 0) (rs $ yl+1) $ +               vcat HCenter +                 (bc $ yr+1) +                 (hcat bot al ar)+        else pasteOnto (yl, if inv then yl else 0) (ls $ yr+1) $+               vcat HCenter +                 (bc $ yl+1) +                 (hcat bot al ar)+      where+        al = go l+        ar = go r+        yl = asciiYSize al +        yr = asciiYSize ar ++  bot = if inv then VTop else VBottom+  hcat align p q = hCatWith align (HSepString "  ") [p,q]+  vcat align p q = vCatWith align VSepEmpty $ if inv then [q,p] else [p,q]+  bc = if inv then asciiBigInvCaret   else asciiBigCaret+  ls = if inv then asciiBigRightSlope else asciiBigLeftSlope+  rs = if inv then asciiBigLeftSlope  else asciiBigRightSlope++  asciiBigCaret :: Int -> ASCII+  asciiBigCaret k = hCatWith VTop HSepEmpty [ asciiBigLeftSlope k , asciiBigRightSlope k ]++  asciiBigInvCaret :: Int -> ASCII+  asciiBigInvCaret k = hCatWith VTop HSepEmpty [ asciiBigRightSlope k , asciiBigLeftSlope k ]++  asciiBigLeftSlope :: Int -> ASCII  +  asciiBigLeftSlope k = if k>0 +    then asciiFromLines [ replicate l ' ' ++ "/" | l<-[k-1,k-2..0] ]+    else emptyRect++  asciiBigRightSlope :: Int -> ASCII  +  asciiBigRightSlope k = if k>0 +    then asciiFromLines [ replicate l ' ' ++ "\\" | l<-[0..k-1] ]+    else emptyRect+  +-- | Draws a binary tree, with all leaves at the same (bottom) row, and labelling+-- the leaves starting with 0 (continuing with letters after 9)+asciiTLabels :: T -> ASCII+asciiTLabels = asciiTLabels' False++-- | When the flag is true, we draw upside down+asciiTLabels' :: Bool -> T -> ASCII+asciiTLabels' inv t = +  if inv +    then vCatWith HLeft VSepEmpty [ labels , asciiT' inv t ]+    else vCatWith HLeft VSepEmpty [ asciiT' inv t , labels ]+  where+    w = treeWidth t+    labels = asciiFromString $ intersperse ' ' $ take (w+1) allLabels+    allLabels = ['0'..'9'] ++ ['a'..'z']+    +-- | Draws a tree diagram+asciiTDiag :: TDiag -> ASCII+asciiTDiag (TDiag _ top bot) = vCatWith HLeft (VSepString " ") [asciiT' False top , asciiT' True bot]++--------------------------------------------------------------------------------++
+ Math/Combinat/Helper.hs view
@@ -0,0 +1,280 @@++-- | Miscellaneous helper functions++{-# LANGUAGE BangPatterns, PolyKinds, GeneralizedNewtypeDeriving #-}+module Math.Combinat.Helper where++--------------------------------------------------------------------------------++import Control.Monad+import Control.Applicative ( Applicative(..) )    -- required before AMP (before GHC 7.10)+import Data.Functor.Identity++import Data.List+import Data.Ord+import Data.Proxy++import Data.Set (Set) ; import qualified Data.Set as Set+import Data.Map (Map) ; import qualified Data.Map as Map++import Debug.Trace++import System.Random+import Control.Monad.Trans.State++--------------------------------------------------------------------------------+-- * debugging++debug :: Show a => a -> b -> b+debug x y = trace ("-- " ++ show x ++ "\n") y++--------------------------------------------------------------------------------+-- * pairs++swap :: (a,b) -> (b,a)+swap (x,y) = (y,x)++pairs :: [a] -> [(a,a)]+pairs = go where+  go (x:xs@(y:_)) = (x,y) : go xs+  go _            = []++pairsWith :: (a -> a -> b) -> [a] -> [b]+pairsWith f = go where+  go (x:xs@(y:_)) = f x y : go xs+  go _            = []++--------------------------------------------------------------------------------+-- * lists++{-# SPECIALIZE sum' :: [Int]     -> Int     #-}+{-# SPECIALIZE sum' :: [Integer] -> Integer #-}+sum' :: Num a => [a] -> a+sum' = foldl' (+) 0++--------------------------------------------------------------------------------+-- * equality and ordering ++equating :: Eq b => (a -> b) -> a -> a -> Bool+equating f x y = (f x == f y)++reverseOrdering :: Ordering -> Ordering+reverseOrdering LT = GT+reverseOrdering GT = LT+reverseOrdering EQ = EQ++reverseCompare :: Ord a => a -> a -> Ordering+reverseCompare x y = reverseOrdering $ compare x y++reverseSort :: Ord a => [a] -> [a]+reverseSort = sortBy reverseCompare++groupSortBy :: (Eq b, Ord b) => (a -> b) -> [a] -> [[a]]+groupSortBy f = groupBy (equating f) . sortBy (comparing f) ++nubOrd :: Ord a => [a] -> [a]+nubOrd = worker Set.empty where+  worker _ [] = []+  worker s (x:xs) +    | Set.member x s = worker s xs+    | otherwise      = x : worker (Set.insert x s) xs++--------------------------------------------------------------------------------+-- * increasing \/ decreasing sequences++{-# SPECIALIZE isWeaklyIncreasing :: [Int] -> Bool #-}+isWeaklyIncreasing :: Ord a => [a] -> Bool+isWeaklyIncreasing = go where+  go xs = case xs of +    (a:rest@(b:_)) -> a <= b && go rest+    [_]            -> True+    []             -> True++{-# SPECIALIZE isStrictlyIncreasing :: [Int] -> Bool #-}+isStrictlyIncreasing :: Ord a => [a] -> Bool+isStrictlyIncreasing = go where+  go xs = case xs of +    (a:rest@(b:_)) -> a < b && go rest+    [_]            -> True+    []             -> True++{-# SPECIALIZE isWeaklyDecreasing :: [Int] -> Bool #-}+isWeaklyDecreasing :: Ord a => [a] -> Bool+isWeaklyDecreasing = go where+  go xs = case xs of +    (a:rest@(b:_)) -> a >= b && go rest+    [_]            -> True+    []             -> True++{-# SPECIALIZE isStrictlyDecreasing :: [Int] -> Bool #-}+isStrictlyDecreasing :: Ord a => [a] -> Bool+isStrictlyDecreasing = go where+  go xs = case xs of +    (a:rest@(b:_)) -> a > b && go rest+    [_]            -> True+    []             -> True++--------------------------------------------------------------------------------+-- * first \/ last ++-- | The boolean argument will @True@ only for the last element+mapWithLast :: (Bool -> a -> b) -> [a] -> [b]+mapWithLast f = go where+  go (x : []) = f True  x : []+  go (x : xs) = f False x : go xs++mapWithFirst :: (Bool -> a -> b) -> [a] -> [b]+mapWithFirst f = go True where+  go b (x:xs) = f b x : go False xs +  +mapWithFirstLast :: (Bool -> Bool -> a -> b) -> [a] -> [b]+mapWithFirstLast f = go True where+  go b (x : []) = f b True  x : []+  go b (x : xs) = f b False x : go False xs++--------------------------------------------------------------------------------+-- * older helpers for ASCII drawing++-- | extend lines with spaces so that they have the same line+mkLinesUniformWidth :: [String] -> [String]+mkLinesUniformWidth old = zipWith worker ls old where+  ls = map length old+  m  = maximum ls+  worker l s = s ++ replicate (m-l) ' '++mkBlocksUniformHeight :: [[String]] -> [[String]]+mkBlocksUniformHeight old = zipWith worker ls old where+  ls = map length old+  m  = maximum ls+  worker l s = s ++ replicate (m-l) ""+    +mkUniformBlocks :: [[String]] -> [[String]] +mkUniformBlocks = map mkLinesUniformWidth . mkBlocksUniformHeight+    +hConcatLines :: [[String]] -> [String]+hConcatLines = map concat . transpose . mkUniformBlocks++vConcatLines :: [[String]] -> [String]  +vConcatLines = concat++--------------------------------------------------------------------------------+-- * counting++-- helps testing the random rutines +count :: Eq a => a -> [a] -> Int+count x xs = length $ filter (==x) xs++histogram :: (Eq a, Ord a) => [a] -> [(a,Int)]+histogram xs = Map.toList table where+  table = Map.fromListWith (+) [ (x,1) | x<-xs ] ++--------------------------------------------------------------------------------+-- * maybe++fromJust :: Maybe a -> a+fromJust (Just x) = x+fromJust Nothing = error "fromJust: Nothing"++--------------------------------------------------------------------------------+-- * bool++intToBool :: Int -> Bool+intToBool 0 = False+intToBool 1 = True+intToBool _ = error "intToBool"++boolToInt :: Bool -> Int +boolToInt False = 0+boolToInt True  = 1++--------------------------------------------------------------------------------+-- * iteration+    +-- iterated function application+nest :: Int -> (a -> a) -> a -> a+nest !0 _ x = x+nest !n f x = nest (n-1) f (f x)++unfold1 :: (a -> Maybe a) -> a -> [a]+unfold1 f x = case f x of +  Nothing -> [x] +  Just y  -> x : unfold1 f y +  +unfold :: (b -> (a,Maybe b)) -> b -> [a]+unfold f y = let (x,m) = f y in case m of +  Nothing -> [x]+  Just y' -> x : unfold f y'++unfoldEither :: (b -> Either c (b,a)) -> b -> (c,[a])+unfoldEither f y = case f y of+  Left z -> (z,[])+  Right (y,x) -> let (z,xs) = unfoldEither f y in (z,x:xs)+  +unfoldM :: Monad m => (b -> m (a,Maybe b)) -> b -> m [a]+unfoldM f y = do+  (x,m) <- f y+  case m of+    Nothing -> return [x]+    Just y' -> do+      xs <- unfoldM f y'+      return (x:xs)++mapAccumM :: Monad m => (acc -> x -> m (acc, y)) -> acc -> [x] -> m (acc, [y])+mapAccumM _ s [] = return (s, [])+mapAccumM f s (x:xs) = do+  (s1,y) <- f s x+  (s2,ys) <- mapAccumM f s1 xs+  return (s2, y:ys)++--------------------------------------------------------------------------------+-- * long zipwith    ++longZipWith :: a -> b -> (a -> b -> c) -> [a] -> [b] -> [c]+longZipWith a0 b0 f = go where+  go (x:xs) (y:ys)  =   f x  y : go xs ys+  go []     ys      = [ f a0 y | y<-ys ]+  go xs     []      = [ f x b0 | x<-xs ]++{-+longZipWithZero :: (Num a, Num b) => (a -> b -> c) -> [a] -> [b] -> [c]+longZipWithZero = longZipWith 0 0 +-}++--------------------------------------------------------------------------------+-- * random++-- | A simple random monad to make life suck less+type Rand g = RandT g Identity++runRand :: Rand g a -> g -> (a,g)+runRand action g = runIdentity (runRandT action g)++flipRunRand :: Rand s a -> s -> (s,a)+flipRunRand action g = runIdentity (flipRunRandT action g)+++-- | The Rand monad transformer+newtype RandT g m a = RandT (StateT g m a) deriving (Functor,Applicative,Monad)++runRandT :: RandT g m a -> g -> m (a,g)+runRandT (RandT stuff) = runStateT stuff++-- | This may be occasionally useful+flipRunRandT :: Monad m => RandT s m a -> s -> m (s,a)+flipRunRandT action ini = liftM swap $ runRandT action ini+++-- | Puts a standard-conforming random function into the monad+rand :: (g -> (a,g)) -> Rand g a+rand user = RandT (state user)++randRoll :: (RandomGen g, Random a) => Rand g a+randRoll = rand random++randChoose :: (RandomGen g, Random a) => (a,a) -> Rand g a+randChoose uv = rand (randomR uv)++randProxy1 :: Rand g (f n) -> Proxy n -> Rand g (f n)+randProxy1 action _ = action++--------------------------------------------------------------------------------
+ Math/Combinat/LatticePaths.hs view
@@ -0,0 +1,386 @@++-- | Dyck paths, lattice paths, etc+--+-- For example, the following figure represents a Dyck path of height 5 with 3 zero-touches (not counting the starting point,+-- but counting the endpoint) and 7 peaks:+--+-- <<svg/dyck_path.svg>>+--++{-# LANGUAGE BangPatterns, FlexibleInstances, TypeSynonymInstances #-}+module Math.Combinat.LatticePaths where++--------------------------------------------------------------------------------++import Data.List+import System.Random++import Math.Combinat.Classes+import Math.Combinat.Numbers+import Math.Combinat.Trees.Binary+import Math.Combinat.ASCII as ASCII++--------------------------------------------------------------------------------+-- * Types++-- | A step in a lattice path+data Step +  = UpStep         -- ^ the step @(1,1)@+  | DownStep       -- ^ the step @(1,-1)@+  deriving (Eq,Ord,Show)++-- | A lattice path is a path using only the allowed steps, never going below the zero level line @y=0@. +--+-- Note that if you rotate such a path by 45 degrees counterclockwise,+-- you get a path which uses only the steps @(1,0)@ and @(0,1)@, and stays+-- above the main diagonal (hence the name, we just use a different convention).+--+type LatticePath = [Step]++--------------------------------------------------------------------------------+-- * ascii drawing of paths++-- | Draws the path into a list of lines. For example try:+--+-- > autotabulate RowMajor (Right 5) (map asciiPath $ dyckPaths 4)+--+asciiPath :: LatticePath -> ASCII+asciiPath p = asciiFromLines $ transpose (go 0 p) where++  go !h [] = []+  go !h (x:xs) = case x of+    UpStep   -> ee  h    x : go (h+1) xs+    DownStep -> ee (h-1) x : go (h-1) xs++  maxh   = pathHeight p++  ee h x = replicate (maxh-h-1) ' ' ++ [ch x] ++ replicate h ' '+  ch x   = case x of +    UpStep   -> '/' +    DownStep -> '\\' ++instance DrawASCII LatticePath where +  ascii = asciiPath++--------------------------------------------------------------------------------+-- * elementary queries++-- | A lattice path is called \"valid\", if it never goes below the @y=0@ line.+isValidPath :: LatticePath -> Bool+isValidPath = go 0 where+  go :: Int -> LatticePath -> Bool+  go !y []     = y>=0+  go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }+                 in  if y'<0 then False +                             else go y' ts++-- | A Dyck path is a lattice path whose last point lies on the @y=0@ line+isDyckPath :: LatticePath -> Bool+isDyckPath = go 0 where+  go :: Int -> LatticePath -> Bool+  go !y []     = y==0+  go !y (t:ts) = let y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }+                 in  if y'<0 then False +                             else go y' ts++-- | Maximal height of a lattice path+pathHeight :: LatticePath -> Int+pathHeight = go 0 0 where+  go :: Int -> Int -> LatticePath -> Int+  go !h !y []     = h+  go !h !y (t:ts) = case t of+    UpStep   -> go (max h (y+1)) (y+1) ts+    DownStep -> go      h        (y-1) ts++instance HasHeight LatticePath where+  height = pathHeight++instance HasWidth LatticePath where+  width = length++-- | Endpoint of a lattice path, which starts from @(0,0)@.+pathEndpoint :: LatticePath -> (Int,Int)+pathEndpoint = go 0 0 where+  go :: Int -> Int -> LatticePath -> (Int,Int)+  go !x !y []     = (x,y)+  go !x !y (t:ts) = case t of                         +    UpStep   -> go (x+1) (y+1) ts+    DownStep -> go (x+1) (y-1) ts++-- | Returns the coordinates of the path (excluding the starting point @(0,0)@, but including+-- the endpoint)+pathCoordinates :: LatticePath -> [(Int,Int)]+pathCoordinates = go 0 0 where+  go :: Int -> Int -> LatticePath -> [(Int,Int)]+  go _  _  []     = []+  go !x !y (t:ts) = let x' = x + 1+                        y' = case t of { UpStep -> y+1 ; DownStep -> y-1 }+                    in  (x',y') : go x' y' ts++-- | Counts the up-steps+pathNumberOfUpSteps :: LatticePath -> Int+pathNumberOfUpSteps   = fst . pathNumberOfUpDownSteps++-- | Counts the down-steps+pathNumberOfDownSteps :: LatticePath -> Int+pathNumberOfDownSteps = snd . pathNumberOfUpDownSteps++-- | Counts both the up-steps and down-steps+pathNumberOfUpDownSteps :: LatticePath -> (Int,Int)+pathNumberOfUpDownSteps = go 0 0 where +  go :: Int -> Int -> LatticePath -> (Int,Int)+  go !u !d (p:ps) = case p of +    UpStep   -> go (u+1)  d    ps  +    DownStep -> go  u    (d+1) ps    +  go !u !d []     = (u,d)++--------------------------------------------------------------------------------+-- * path-specific queries++-- | Number of peaks of a path (excluding the endpoint)+pathNumberOfPeaks :: LatticePath -> Int+pathNumberOfPeaks = go 0 where+  go :: Int -> LatticePath -> Int+  go !k (x:xs@(y:_)) = go (if x==UpStep && y==DownStep then k+1 else k) xs+  go !k [x] = k+  go !k [ ] = k++-- | Number of points on the path which touch the @y=0@ zero level line+-- (excluding the starting point @(0,0)@, but including the endpoint; that is, for Dyck paths it this is always positive!).+pathNumberOfZeroTouches :: LatticePath -> Int+pathNumberOfZeroTouches = pathNumberOfTouches' 0++-- | Number of points on the path which touch the level line at height @h@+-- (excluding the starting point @(0,0)@, but including the endpoint).+pathNumberOfTouches' +  :: Int       -- ^ @h@ = the touch level+  -> LatticePath -> Int+pathNumberOfTouches' h = go 0 0 0 where+  go :: Int -> Int -> Int -> LatticePath -> Int+  go !cnt _  _  []     = cnt+  go !cnt !x !y (t:ts) = let y'   = case t of { UpStep -> y+1 ; DownStep -> y-1 }+                             cnt' = if y'==h then cnt+1 else cnt+                         in  go cnt' (x+1) y' ts++--------------------------------------------------------------------------------+-- * Dyck paths++-- | @dyckPaths m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@. +-- +-- Remark: Dyck paths are obviously in bijection with nested parentheses, and thus+-- also with binary trees.+--+-- Order is reverse lexicographical:+--+-- > sort (dyckPaths m) == reverse (dyckPaths m)+-- +dyckPaths :: Int -> [LatticePath]+dyckPaths = map nestedParensToDyckPath . nestedParentheses ++-- | @dyckPaths m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@. +--+-- > sort (dyckPathsNaive m) == sort (dyckPaths m) +--  +-- Naive recursive algorithm, order is ad-hoc+--+dyckPathsNaive :: Int -> [LatticePath]+dyckPathsNaive = worker where+  worker  0 = [[]]+  worker  m = as ++ bs where+    as = [ bracket p      | p <- worker (m-1) ] +    bs = [ bracket p ++ q | k <- [1..m-1] , p <- worker (k-1) , q <- worker (m-k) ]+  bracket p = UpStep : p ++ [DownStep]++-- | The number of Dyck paths from @(0,0)@ to @(2m,0)@ is simply the m\'th Catalan number.+countDyckPaths :: Int -> Integer+countDyckPaths m = catalan m++-- | The trivial bijection+nestedParensToDyckPath :: [Paren] -> LatticePath+nestedParensToDyckPath = map f where+  f p = case p of { LeftParen -> UpStep ; RightParen -> DownStep }++-- | The trivial bijection in the other direction+dyckPathToNestedParens :: LatticePath -> [Paren]+dyckPathToNestedParens = map g where+  g s = case s of { UpStep -> LeftParen ; DownStep -> RightParen }++--------------------------------------------------------------------------------+-- * Bounded Dyck paths++-- | @boundedDyckPaths h m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ whose height is at most @h@.+-- Synonym for 'boundedDyckPathsNaive'.+--+boundedDyckPaths+  :: Int   -- ^ @h@ = maximum height+  -> Int   -- ^ @m@ = half-length+  -> [LatticePath]+boundedDyckPaths = boundedDyckPathsNaive ++-- | @boundedDyckPathsNaive h m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ whose height is at most @h@.+--+-- > sort (boundedDyckPaths h m) == sort [ p | p <- dyckPaths m , pathHeight p <= h ]+-- > sort (boundedDyckPaths m m) == sort (dyckPaths m) +--+-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+boundedDyckPathsNaive+  :: Int   -- ^ @h@ = maximum height+  -> Int   -- ^ @m@ = half-length+  -> [LatticePath]+boundedDyckPathsNaive = worker where+  worker !h !m +    | h<0        = []+    | m<0        = []+    | m==0       = [[]]+    | h<=0       = []+    | otherwise  = as ++ bs +    where+      bracket p = UpStep : p ++ [DownStep]+      as = [ bracket p      |                 p <- boundedDyckPaths (h-1) (m-1)                                 ]+      bs = [ bracket p ++ q | k <- [1..m-1] , p <- boundedDyckPaths (h-1) (k-1) , q <- boundedDyckPaths h (m-k) ]++--------------------------------------------------------------------------------+-- * More general lattice paths++-- | All lattice paths from @(0,0)@ to @(x,y)@. Clearly empty unless @x-y@ is even.+-- Synonym for 'latticePathsNaive'+--+latticePaths :: (Int,Int) -> [LatticePath]+latticePaths = latticePathsNaive++-- | All lattice paths from @(0,0)@ to @(x,y)@. Clearly empty unless @x-y@ is even.+--+-- Note that+--+-- > sort (dyckPaths n) == sort (latticePaths (0,2*n))+--+-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+latticePathsNaive :: (Int,Int) -> [LatticePath]+latticePathsNaive (x,y) = worker x y where+  worker !x !y +    | odd (x-y)     = []+    | x<0           = []+    | y<0           = []+    | y==0          = dyckPaths (div x 2)+    | x==1 && y==1  = [[UpStep]]+    | otherwise     = as ++ bs+    where+      bracket p = UpStep : p ++ [DownStep] +      as = [ UpStep : p     | p <- worker (x-1) (y-1) ]+      bs = [ bracket p ++ q | k <- [1..(div x 2)] , p <- dyckPaths (k-1) , q <- worker (x-2*k) y ]++-- | Lattice paths are counted by the numbers in the Catalan triangle.+countLatticePaths :: (Int,Int) -> Integer+countLatticePaths (x,y) +  | even (x+y)  = catalanTriangle (div (x+y) 2) (div (x-y) 2)+  | otherwise   = 0++--------------------------------------------------------------------------------+-- * Zero-level touches++-- | @touchingDyckPaths k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ which touch the +-- zero level line @y=0@ exactly @k@ times (excluding the starting point, but including the endpoint;+-- thus, @k@ should be positive). Synonym for 'touchingDyckPathsNaive'.+touchingDyckPaths+  :: Int   -- ^ @k@ = number of zero-touches+  -> Int   -- ^ @m@ = half-length+  -> [LatticePath]+touchingDyckPaths = touchingDyckPathsNaive+++-- | @touchingDyckPathsNaive k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ which touch the +-- zero level line @y=0@ exactly @k@ times (excluding the starting point, but including the endpoint;+-- thus, @k@ should be positive).+--+-- > sort (touchingDyckPathsNaive k m) == sort [ p | p <- dyckPaths m , pathNumberOfZeroTouches p == k ]+-- +-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+touchingDyckPathsNaive+  :: Int   -- ^ @k@ = number of zero-touches+  -> Int   -- ^ @m@ = half-length+  -> [LatticePath]+touchingDyckPathsNaive = worker where+  worker !k !m +    | m == 0    = if k==0 then [[]] else []+    | k <= 0    = []+    | m <  0    = []+    | k == 1    = [ bracket p      |                 p <- dyckPaths (m-1)                           ]+    | otherwise = [ bracket p ++ q | l <- [1..m-1] , p <- dyckPaths (l-1) , q <- worker (k-1) (m-l) ]+    where+      bracket p = UpStep : p ++ [DownStep] +++-- | There is a bijection from the set of non-empty Dyck paths of length @2n@ which touch the zero lines @t@ times,+-- to lattice paths from @(0,0)@ to @(2n-t-1,t-1)@ (just remove all the down-steps just before touching+-- the zero line, and also the very first up-step). This gives us a counting formula.+countTouchingDyckPaths +  :: Int   -- ^ @k@ = number of zero-touches+  -> Int   -- ^ @m@ = half-length+  -> Integer+countTouchingDyckPaths t n+  | t==0 && n==0   = 1+  | otherwise      = countLatticePaths (2*n-t-1,t-1)++--------------------------------------------------------------------------------+-- * Dyck paths with given number of peaks++-- | @peakingDyckPaths k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ with exactly @k@ peaks.+--+-- Synonym for 'peakingDyckPathsNaive'+--+peakingDyckPaths+  :: Int      -- ^ @k@ = number of peaks+  -> Int      -- ^ @m@ = half-length+  -> [LatticePath]+peakingDyckPaths = peakingDyckPathsNaive ++-- | @peakingDyckPathsNaive k m@ lists all Dyck paths from @(0,0)@ to @(2m,0)@ with exactly @k@ peaks.+--+-- > sort (peakingDyckPathsNaive k m) = sort [ p | p <- dyckPaths m , pathNumberOfPeaks p == k ]+--  +-- Naive recursive algorithm, resulting order is pretty ad-hoc.+--+peakingDyckPathsNaive +  :: Int      -- ^ @k@ = number of peaks+  -> Int      -- ^ @m@ = half-length+  -> [LatticePath]+peakingDyckPathsNaive = worker where+  worker !k !m+    | m == 0    = if k==0 then [[]] else []       +    | k <= 0    = []+    | m <  0    = []+    | k == 1    = [ singlePeak m ] +    | otherwise = as ++ bs ++ cs+    where+      as = [ bracket p      |                                 p <- worker k (m-1)                           ]+      bs = [ smallHill ++ q |                                                       q <- worker (k-1) (m-1) ]+      cs = [ bracket p ++ q | l <- [2..m-1] , a <- [1..k-1] , p <- worker a (l-1) , q <- worker (k-a) (m-l) ]+      smallHill     = [ UpStep , DownStep ]+      singlePeak !m = replicate m UpStep ++ replicate m DownStep +      bracket p = UpStep : p ++ [DownStep] ++-- | Dyck paths of length @2m@ with @k@ peaks are counted by the Narayana numbers @N(m,k) = \binom{m}{k} \binom{m}{k-1} / m@+countPeakingDyckPaths+  :: Int      -- ^ @k@ = number of peaks+  -> Int      -- ^ @m@ = half-length+  -> Integer+countPeakingDyckPaths k m +  | m == 0    = if k==0 then 1 else 0+  | k <= 0    = 0+  | m <  0    = 0+  | k == 1    = 1+  | otherwise = div (binomial m k * binomial m (k-1)) (fromIntegral m)++--------------------------------------------------------------------------------+-- * Random lattice paths++-- | A uniformly random Dyck path of length @2m@+randomDyckPath :: RandomGen g => Int -> g -> (LatticePath,g)+randomDyckPath m g0 = (nestedParensToDyckPath parens, g1) where+  (parens,g1) = randomNestedParentheses m g0++--------------------------------------------------------------------------------+
+ Math/Combinat/Numbers.hs view
@@ -0,0 +1,194 @@++-- | A few important number sequences. +--  +-- See the \"On-Line Encyclopedia of Integer Sequences\",+-- <https://oeis.org> .++module Math.Combinat.Numbers where++--------------------------------------------------------------------------------++import Data.Array++import Math.Combinat.Helper ( sum' )+import Math.Combinat.Sign++--------------------------------------------------------------------------------++-- | A000142.+factorial :: Integral a => a -> Integer+factorial n+  | n <  0    = error "factorial: input should be nonnegative"+  | n == 0    = 1+  | otherwise = product [1..fromIntegral n]++-- | A006882.+doubleFactorial :: Integral a => a -> Integer+doubleFactorial n+  | n <  0    = error "doubleFactorial: input should be nonnegative"+  | n == 0    = 1+  | odd n     = product [1,3..fromIntegral n]+  | otherwise = product [2,4..fromIntegral n]++-- | A007318. Note: This is zero for @n<0@ or @k<0@; see also 'signedBinomial' below.+binomial :: Integral a => a -> a -> Integer+binomial n k +  | k > n = 0+  | k < 0 = 0+  | k > (n `div` 2) = binomial n (n-k)+  | otherwise = (product [n'-k'+1 .. n']) `div` (product [1..k'])+  where +    k' = fromIntegral k+    n' = fromIntegral n++-- | The extension of the binomial function to negative inputs. This should satisfy the following properties:+--+-- > for n,k >=0 : signedBinomial n k == binomial n k+-- > for any n,k : signedBinomial n k == signedBinomial n (n-k) +-- > for k >= 0  : signedBinomial (-n) k == (-1)^k * signedBinomial (n+k-1) k+--+-- Note: This is compatible with Mathematica's @Binomial@ function.+--+signedBinomial :: Int -> Int -> Integer+signedBinomial n k+  | n >= 0     = binomial n k+  | k >= 0     = negateIfOdd    k  $ binomial (k-n-1)   k  +  | otherwise  = negateIfOdd (n+k) $ binomial (-k-1) (-n-1)++{-+test_signed_0 = [ signedBinomial ( n) k == signedBinomial ( n) ( n-k)                | n<-[-30..40] , k<-[-30..40] ]+test_signed_1 = [ signedBinomial (-n) k == signedBinomial (-n) (-n-k)                | n<-[-30..40] , k<-[-30..40] ]+test_signed_2 = [ signedBinomial (-n) k == negateIfOdd k $ signedBinomial (n+k-1) k  | n<-[-30..40] , k<-[0..30] ]+-}++-- | A given row of the Pascal triangle; equivalent to a sequence of binomial +-- numbers, but much more efficient. You can also left-fold over it.+--+-- > pascalRow n == [ binomial n k | k<-[0..n] ]+pascalRow :: Integral a => a -> [Integer]+pascalRow n' = worker 0 1 where+  n = fromIntegral n'+  worker j x+    | j>n   = [] +    | True  = let j'=j+1 in x : worker j' (div (x*(n-j)) j') ++multinomial :: Integral a => [a] -> Integer+multinomial xs = div+  (factorial (sum xs))+  (product [ factorial x | x<-xs ])  +  +--------------------------------------------------------------------------------+-- * Catalan numbers++-- | Catalan numbers. OEIS:A000108.+catalan :: Integral a => a -> Integer+catalan n +  | n < 0     = 0+  | otherwise = binomial (n+n) n `div` fromIntegral (n+1)++-- | Catalan's triangle. OEIS:A009766.+-- Note:+--+-- > catalanTriangle n n == catalan n+-- > catalanTriangle n k == countStandardYoungTableaux (toPartition [n,k])+--+catalanTriangle :: Integral a => a -> a -> Integer+catalanTriangle n k+  | k > n     = 0+  | k < 0     = 0+  | otherwise = (binomial (n+k) n * fromIntegral (n-k+1)) `div` fromIntegral (n+1)++--------------------------------------------------------------------------------+-- * Stirling numbers++-- | Rows of (signed) Stirling numbers of the first kind. OEIS:A008275.+-- Coefficients of the polinomial @(x-1)*(x-2)*...*(x-n+1)@.+-- This function uses the recursion formula.+signedStirling1stArray :: Integral a => a -> Array Int Integer+signedStirling1stArray n+  | n <  1    = error "stirling1stArray: n should be at least 1"+  | n == 1    = listArray (1,1 ) [1]+  | otherwise = listArray (1,n') [ lkp (k-1) - fromIntegral (n-1) * lkp k | k<-[1..n'] ] +  where+    prev = signedStirling1stArray (n-1)+    n' = fromIntegral n :: Int+    lkp j | j <  1    = 0+          | j >= n'   = 0+          | otherwise = prev ! j +        +-- | (Signed) Stirling numbers of the first kind. OEIS:A008275.+-- This function uses 'signedStirling1stArray', so it shouldn't be used+-- to compute /many/ Stirling numbers.+--+-- Argument order: @signedStirling1st n k@+--+signedStirling1st :: Integral a => a -> a -> Integer+signedStirling1st n k +  | k==0 && n==0 = 1+  | k < 1        = 0+  | k > n        = 0+  | otherwise    = signedStirling1stArray n ! (fromIntegral k)++-- | (Unsigned) Stirling numbers of the first kind. See 'signedStirling1st'.+unsignedStirling1st :: Integral a => a -> a -> Integer+unsignedStirling1st n k = abs (signedStirling1st n k)++-- | Stirling numbers of the second kind. OEIS:A008277.+-- This function uses an explicit formula.+-- +-- Argument order: @stirling2nd n k@+--+stirling2nd :: Integral a => a -> a -> Integer+stirling2nd n k +  | k==0 && n==0 = 1+  | k < 1        = 0+  | k > n        = 0+  | otherwise = sum xs `div` factorial k where+      xs = [ negateIfOdd (k-i) $ binomial k i * (fromIntegral i)^n | i<-[0..k] ]++--------------------------------------------------------------------------------+-- * Bernoulli numbers++-- | Bernoulli numbers. @bernoulli 1 == -1%2@ and @bernoulli k == 0@ for+-- k>2 and /odd/. This function uses the formula involving Stirling numbers+-- of the second kind. Numerators: A027641, denominators: A027642.+bernoulli :: Integral a => a -> Rational+bernoulli n +  | n <  0    = error "bernoulli: n should be nonnegative"+  | n == 0    = 1+  | n == 1    = -1/2+  | otherwise = sum [ f k | k<-[1..n] ] +  where+    f k = toRational (negateIfOdd (n+k) $ factorial k * stirling2nd n k) +        / toRational (k+1)++--------------------------------------------------------------------------------+-- * Bell numbers++-- | Bell numbers (Sloane's A000110) from B(0) up to B(n). B(0)=B(1)=1, B(2)=2, etc. +--+-- The Bell numbers count the number of /set partitions/ of a set of size @n@+-- +-- See <http://en.wikipedia.org/wiki/Bell_number>+--+bellNumbersArray :: Integral a => a -> Array Int Integer+bellNumbersArray nn = arr where+  arr = array (0::Int,n) kvs +  n = fromIntegral nn :: Int+  kvs = (0,1) : [ (k, f k) | k<-[1..n] ] +  f n = sum' [ binomial (n-1) k * arr ! k | k<-[0..n-1] ]++-- | The n-th Bell number B(n), using the Stirling numbers of the second kind.+-- This may be slower than using 'bellNumbersArray'.+bellNumber :: Integral a => a -> Integer+bellNumber nn+  | n <  0     = error "bellNumber: expecting a nonnegative index"+  | n == 0     = 1+  | otherwise  = sum' [ stirling2nd n k | k<-[1..n] ] +  where+    n = fromIntegral nn :: Int++--------------------------------------------------------------------------------+++ 
+ Math/Combinat/Numbers/Primes.hs view
@@ -0,0 +1,354 @@++-- | Prime numbers and related number theoretical stuff.++module Math.Combinat.Numbers.Primes +  ( -- * List of prime numbers+    primes+  , primesSimple+  , primesTMWE+    -- * Prime factorization+  , groupIntegerFactors+  , integerFactorsTrialDivision+    -- * Integer logarithm+  , integerLog2+  , ceilingLog2+    -- * Integer square root+  , isSquare+  , integerSquareRoot+  , ceilingSquareRoot+  , integerSquareRoot' +  , integerSquareRootNewton'+    -- * Modulo @m@ arithmetic+  , powerMod+    -- * Prime testing+  , millerRabinPrimalityTest+  , isProbablyPrime+  , isVeryProbablyPrime+  )+  where++--------------------------------------------------------------------------------++-- import Math.Combinat.Numbers++import Data.List ( group , sort )+import Data.Bits++import System.Random++--------------------------------------------------------------------------------+-- List of prime numbers ++-- | Infinite list of primes, using the TMWE algorithm.+primes :: [Integer]+primes = primesTMWE++-- | A relatively simple but still quite fast implementation of list of primes.+-- By Will Ness <http://www.haskell.org/pipermail/haskell-cafe/2009-November/068441.html>+primesSimple :: [Integer]+primesSimple = 2 : 3 : sieve 0 primes' 5 where+  primes' = tail primesSimple+  sieve k (p:ps) x = noDivs k h ++ sieve (k+1) ps (t+2) where+    t = p*p +    h = [x,x+2..t-2]+  noDivs k = filter (\x -> all (\y -> rem x y /= 0) (take k primes'))+  +-- | List of primes, using tree merge with wheel. Code by Will Ness.+primesTMWE :: [Integer]+primesTMWE = 2:3:5:7: gaps 11 wheel (fold3t $ roll 11 wheel primes') where                                                             ++  primes' = 11: gaps 13 (tail wheel) (fold3t $ roll 11 wheel primes')+  fold3t ((x:xs): ~(ys:zs:t)) +    = x : union xs (union ys zs) `union` fold3t (pairs t)            +  pairs ((x:xs):ys:t) = (x : union xs ys) : pairs t +  wheel = 2:4:2:4:6:2:6:4:2:4:6:6:2:6:4:2:6:4:6:8:4:2:4:2:  +          4:8:6:4:6:2:4:6:2:6:6:4:2:4:6:2:6:4:2:4:2:10:2:10:wheel +  gaps k ws@(w:t) cs@ ~(c:u) +    | k==c  = gaps (k+w) t u              +    | True  = k : gaps (k+w) t cs  +  roll k ws@(w:t) ps@ ~(p:u) +    | k==p  = scanl (\c d->c+p*d) (p*p) ws : roll (k+w) t u              +    | True  = roll (k+w) t ps   ++  minus xxs@(x:xs) yys@(y:ys) = case compare x y of +    LT -> x : minus xs  yys+    EQ ->     minus xs  ys +    GT ->     minus xxs ys+  minus xs [] = xs+  minus [] _  = []+  +  union xxs@(x:xs) yys@(y:ys) = case compare x y of +    LT -> x : union xs  yys+    EQ -> x : union xs  ys +    GT -> y : union xxs ys+  union xs [] = xs+  union [] ys =ys++--------------------------------------------------------------------------------+-- Prime factorization++-- | Groups integer factors. Example: from [2,2,2,3,3,5] we produce [(2,3),(3,2),(5,1)]  +groupIntegerFactors :: [Integer] -> [(Integer,Int)]+groupIntegerFactors = map f . group . sort where+  f xs = (head xs, length xs)++-- | The naive trial division algorithm.+integerFactorsTrialDivision :: Integer -> [Integer]+integerFactorsTrialDivision n +  | n<1 = error "integerFactorsTrialDivision: n should be at least 1"+  | otherwise = go primes n +  where+    go _  1 = []+    go rs k = sub ps k where+      sub [] k = [k]+      sub qqs@(q:qs) k = case mod k q of+        0 -> q : go qqs (div k q)+        _ -> sub qs k+      ps = takeWhile (\p -> p*p <= k) rs  +{-+    go 1 = []+    go k = sub ps k where+      sub [] k = [k]+      sub (q:qs) k = case mod k q of+        0 -> q : go (div k q)+        _ -> sub qs k+      ps = takeWhile (\p -> p*p <= k) primes+-}++{-    +-- brute force testing of factors+ifactorsTest :: (Integer -> [Integer]) -> Integer -> Bool+ifactorsTest alg n = and [ product (alg k) == k | k<-[1..n] ]   +-}++--------------------------------------------------------------------------------+-- Integer logarithm++-- | Largest integer @k@ such that @2^k@ is smaller or equal to @n@+integerLog2 :: Integer -> Integer+integerLog2 n = go n where+  go 0 = -1+  go k = 1 + go (shiftR k 1)++-- | Smallest integer @k@ such that @2^k@ is larger or equal to @n@+ceilingLog2 :: Integer -> Integer+ceilingLog2 0 = 0+ceilingLog2 n = 1 + go (n-1) where+  go 0 = -1+  go k = 1 + go (shiftR k 1)+  +--------------------------------------------------------------------------------+-- Integer square root++isSquare :: Integer -> Bool+isSquare n = +  if (fromIntegral $ mod n 32) `elem` rs +    then snd (integerSquareRoot' n) == 0+    else False+  where+    rs = [0,1,4,9,16,17,25] :: [Int]+    +-- | Integer square root (largest integer whose square is smaller or equal to the input)+-- using Newton's method, with a faster (for large numbers) inital guess based on bit shifts.+integerSquareRoot :: Integer -> Integer+integerSquareRoot = fst . integerSquareRoot'++-- | Smallest integer whose square is larger or equal to the input+ceilingSquareRoot :: Integer -> Integer+ceilingSquareRoot n = (if r>0 then u+1 else u) where (u,r) = integerSquareRoot' n ++-- | We also return the excess residue; that is+--+-- > (a,r) = integerSquareRoot' n+-- +-- means that+--+-- > a*a + r = n+-- > a*a <= n < (a+1)*(a+1)+integerSquareRoot' :: Integer -> (Integer,Integer)+integerSquareRoot' n+  | n<0 = error "integerSquareRoot: negative input"+  | n<2 = (n,0)+  | otherwise = go firstGuess +  where+    k = integerLog2 n+    firstGuess = 2^(div (k+2) 2) -- !! note that (div (k+1) 2) is NOT enough !!+    go a = +      if m < a+        then go a' +        else (a, r + a*(m-a))+      where+        (m,r) = divMod n a+        a' = div (m + a) 2++-- | Newton's method without an initial guess. For very small numbers (<10^10) it+-- is somewhat faster than the above version.+integerSquareRootNewton' :: Integer -> (Integer,Integer)+integerSquareRootNewton' n+  | n<0 = error "integerSquareRootNewton: negative input"+  | n<2 = (n,0)+  | otherwise = go (div n 2) +  where+    go a = +      if m < a+        then go a' +        else (a, r + a*(m-a))+      where+        (m,r) = divMod n a+        a' = div (m + a) 2++{-+-- brute force test of integer square root+isqrt_test n1 n2 = +  [ k +  | k<-[n1..n2] +  , let (a,r) = integerSquareRoot' k+  , (a*a+r/=k) || (a*a>k) || (a+1)*(a+1)<=k +  ]+-}++--------------------------------------------------------------------------------+-- Modulo @m@ arithmetic++-- | Efficient powers modulo m.+-- +-- > powerMod a k m == (a^k) `mod` m+powerMod :: Integer -> Integer -> Integer -> Integer+powerMod a' k m = {- debug bs $ -} go a bs where++  bs = bin k++  bin 0 = []+  bin x = (x .&. 1 /= 0) : bin (shiftR x 1)++  a = mod a' m++  go _ [] = 1+  go x (b:bs) = -- debug (x,b) $ +    if b +      then mod (x*rest) m+      else rest+    where +      rest = go (mod (x*x) m) bs +      +--------------------------------------------------------------------------------+-- Prime testing+ +-- | Miller-Rabin Primality Test (taken from Haskell wiki). +-- We test the primality of the first argument @n@ by using the second argument @a@ as a candidate witness.+-- If it returs @False@, then @n@ is composite. If it returns @True@, then @n@ is either prime or composite.+--+-- A random choice between @2@ and @(n-2)@ is a good choice for @a@.+millerRabinPrimalityTest :: Integer -> Integer -> Bool+millerRabinPrimalityTest n a+  | a <= 1 || a >= n-1 = +      error $ "millerRabinPrimalityTest: a out of range (" ++ show a ++ " for "++ show n ++ ")" +  | n < 2 = False+  | even n = False+  | b0 == 1 || b0 == n' = True+  | otherwise = iter (tail b)+  where+    n' = n-1+    (k,m) = find2km n'+    b0 = powMod n a m+    b = take (fromIntegral k) $ iterate (squareMod n) b0+    iter [] = False+    iter (x:xs)+      | x == 1 = False+      | x == n' = True+      | otherwise = iter xs+++{-# SPECIALIZE find2km :: Integer -> (Integer,Integer) #-}+find2km :: Integral a => a -> (a,a)+find2km n = f 0 n where +  f k m+    | r == 1 = (k,m)+    | otherwise = f (k+1) q+    where (q,r) = quotRem m 2        + +{-# SPECIALIZE pow' :: (Integer -> Integer -> Integer) -> (Integer -> Integer) -> Integer -> Integer -> Integer #-}+pow' :: (Num a, Integral b) => (a -> a -> a) -> (a -> a) -> a -> b -> a+pow' _ _ _ 0 = 1+pow' mul sq x' n' = f x' n' 1 where +  f x n y+    | n == 1 = x `mul` y+    | r == 0 = f x2 q y+    | otherwise = f x2 q (x `mul` y)+    where+      (q,r) = quotRem n 2+      x2 = sq x+ +{-# SPECIALIZE mulMod :: Integer -> Integer -> Integer -> Integer #-}+mulMod :: Integral a => a -> a -> a -> a+mulMod a b c = (b * c) `mod` a++{-# SPECIALIZE squareMod :: Integer -> Integer -> Integer #-}+squareMod :: Integral a => a -> a -> a+squareMod a b = (b * b) `rem` a++{-# SPECIALIZE powMod :: Integer -> Integer -> Integer -> Integer #-}+powMod :: Integral a => a -> a -> a -> a+powMod m = pow' (mulMod m) (squareMod m)++--------------------------------------------------------------------------------++-- | For very small numbers, we use trial division, for larger numbers, we apply the +-- Miller-Rabin primality test @log4(n)@ times, with candidate witnesses derived +-- deterministically from @n@ using a pseudo-random sequence +-- (which /should be/ based on a cryptographic hash function, but isn\'t, yet). +--+-- Thus the candidate witnesses should behave essentially like random, but the +-- resulting function is still a deterministic, pure function.+--+-- TODO: implement the hash sequence, at the moment we use 'System.Random' instead...+--+isProbablyPrime :: Integer -> Bool+isProbablyPrime n +  | n < 2      = False+  | even n     = (n==2)+  | n < 1000   = length (integerFactorsTrialDivision n) == 1+  | otherwise  = and [ millerRabinPrimalityTest n a | a <- witnessList ]+  where+    log2n       = integerLog2 n +    nchecks     = 1 + fromInteger (div log2n 2) :: Int+    witnessList = take nchecks pseudoRnds+    pseudoRnds  = 2 : [ a | a <- integerRndSequence n , a > 1 && a < (n-1) ]++-- | A more exhaustive version of 'isProbablyPrime', this one tests candidate+-- witnesses both the first log4(n) prime numbers and then log4(n) pseudo-random+-- numbers+isVeryProbablyPrime :: Integer -> Bool+isVeryProbablyPrime n+  | n < 2      = False+  | even n     = (n==2)+  | n < 1000   = length (integerFactorsTrialDivision n) == 1+  | otherwise  = and [ millerRabinPrimalityTest n a | a <- witnessList ]+  where+    log2n       = integerLog2 n +    nchecks     = 1 + fromInteger (div log2n 2) :: Int+    witnessList = take nchecks primes ++ take nchecks pseudoRnds+    pseudoRnds  = [ a | a <- integerRndSequence (n+3) , a > 1 && a < (n-1) ]++--------------------------------------------------------------------------------++{-+-- | Given an integer @n@, we return an infinite sequence of pseudo-random integers +-- between @0..n-1@, generated using a crypographic hash function.+--+integerHashSequence :: Integer -> [Integer]+integerHashSequence = error "integerHashSequence: not implemented yet"+-}++-- | Given an integer @n@, we initialize a system random generator with using a +-- seed derived from @n@ (note that this uses at most 32 or 64 bits), and generate +-- an infinite sequence of pseudo-random integers between @0..n-1@, generated by +-- that random generator. +--+-- Note that this is not really a preferred way of generating such sequences!+-- +integerRndSequence :: Integer -> [Integer]+integerRndSequence n = randomRs (0,n-1) gen where+  gen = mkStdGen $ fromInteger (n + 17 * integerLog2 n)++--------------------------------------------------------------------------------
+ Math/Combinat/Numbers/Series.hs view
@@ -0,0 +1,376 @@++-- | Some basic univariate power series expansions.+-- This module is not re-exported by "Math.Combinat".+--+-- Note: the \"@convolveWithXXX@\" functions are much faster than the equivalent+-- @(XXX \`convolve\`)@!+-- +-- TODO: better names for these functions.+--++{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-}+module Math.Combinat.Numbers.Series where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Sign+import Math.Combinat.Numbers+import Math.Combinat.Partitions.Integer+import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * Trivial series++-- | The series [1,0,0,0,0,...], which is the neutral element for the convolution.+{-# SPECIALIZE unitSeries :: [Integer] #-}+unitSeries :: Num a => [a]+unitSeries = 1 : repeat 0++-- | Constant zero series+zeroSeries :: Num a => [a]+zeroSeries = repeat 0++-- | Power series representing a constant function+constSeries :: Num a => a -> [a]+constSeries x = x : repeat 0++-- | The power series representation of the identity function @x@+idSeries :: Num a => [a]+idSeries = 0 : 1 : repeat 0++-- | The power series representation of @x^n@+powerTerm :: Num a => Int -> [a]+powerTerm n = replicate n 0 ++ (1 : repeat 0)++--------------------------------------------------------------------------------+-- * Basic operations on power series++addSeries :: Num a => [a] -> [a] -> [a]+addSeries xs ys = longZipWith 0 0 (+) xs ys++sumSeries :: Num a => [[a]] -> [a]+sumSeries [] = [0]+sumSeries xs = foldl1' addSeries xs++subSeries :: Num a => [a] -> [a] -> [a]+subSeries xs ys = longZipWith 0 0 (-) xs ys++negateSeries :: Num a => [a] -> [a]+negateSeries = map negate++scaleSeries :: Num a => a -> [a] -> [a]+scaleSeries s = map (*s)++mulSeries :: Num a => [a] -> [a] -> [a]+mulSeries = convolve++productOfSeries :: Num a => [[a]] -> [a]+productOfSeries = convolveMany++--------------------------------------------------------------------------------+-- * Convolution (product)++-- | Convolution of series (that is, multiplication of power series). +-- The result is always an infinite list. Warning: This is slow!+convolve :: Num a => [a] -> [a] -> [a]+convolve xs1 ys1 = res where+  res = [ foldl' (+) 0 (zipWith (*) xs (reverse (take n ys)))+        | n<-[1..] +        ]+  xs = xs1 ++ repeat 0+  ys = ys1 ++ repeat 0++-- | Convolution (= product) of many series. Still slow!+convolveMany :: Num a => [[a]] -> [a]+convolveMany []  = 1 : repeat 0+convolveMany xss = foldl1 convolve xss++--------------------------------------------------------------------------------+-- * Reciprocals of general power series++-- | Given a power series, we iteratively compute its multiplicative inverse+reciprocalSeries :: (Eq a, Fractional a) => [a] -> [a]+reciprocalSeries series = case series of+  [] -> error "reciprocalSeries: empty input series (const 0 function does not have an inverse)"+  (a:as) -> case a of+    0 -> error "reciprocalSeries: input series has constant term 0"+    _ -> map (/a) $ integralReciprocalSeries $ map (/a) series++-- | Given a power series starting with @1@, we can compute its multiplicative inverse+-- without divisions.+--+{-# SPECIALIZE integralReciprocalSeries :: [Int]     -> [Int]     #-}+{-# SPECIALIZE integralReciprocalSeries :: [Integer] -> [Integer] #-}+integralReciprocalSeries :: (Eq a, Num a) => [a] -> [a]+integralReciprocalSeries series = case series of +  [] -> error "integralReciprocalSeries: empty input series (const 0 function does not have an inverse)"+  (a:as) -> case a of+    1 -> 1 : worker [1]+    _ -> error "integralReciprocalSeries: input series must start with 1"+  where+    worker bs = let b' = - sum (zipWith (*) (tail series) bs) +                in  b' : worker (b':bs)++--------------------------------------------------------------------------------+-- * Composition of formal power series++-- | @g \`composeSeries\` f@ is the power series expansion of @g(f(x))@.+-- This is a synonym for @flip substitute@.+--+-- We require that the constant term of @f@ is zero.+composeSeries :: (Eq a, Num a) => [a] -> [a] -> [a]+composeSeries g f = substitute f g++-- | @substitute f g@ is the power series corresponding to @g(f(x))@. +-- Equivalently, this is the composition of univariate functions (in the \"wrong\" order).+--+-- Note: for this to be meaningful in general (not depending on convergence properties),+-- we need that the constant term of @f@ is zero.+substitute :: (Eq a, Num a) => [a] -> [a] -> [a]+substitute as_ bs_ = +  case head as of+    0 -> [ f n | n<-[0..] ]+    _ -> error "PowerSeries/substitute: we expect the the constant term of the inner series to be zero"+  where+    as = as_ ++ repeat 0+    bs = bs_ ++ repeat 0+    a i = as !! i+    b j = bs !! j+    f n = sum+            [ b m * product [ (a i)^j | (i,j)<-es ] * fromInteger (multinomial (map snd es))+            | p <- partitions n +            , let es = toExponentialForm p+            , let m  = partitionWidth    p+            ]++--------------------------------------------------------------------------------+-- * Lagrange inversions++-- | Coefficients of the Lagrange inversion+lagrangeCoeff :: Partition -> Integer+lagrangeCoeff p = div numer denom where+  numer = (-1)^m * product (map fromIntegral [n+1..n+m])+  denom = fromIntegral (n+1) * product (map (factorial . snd) es)+  m  = partitionWidth    p+  n  = partitionWeight   p+  es = toExponentialForm p++-- | We expect the input series to match @(0:1:_)@. The following is true for the result (at least with exact arithmetic):+--+-- > substitute f (integralLagrangeInversion f) == (0 : 1 : repeat 0)+-- > substitute (integralLagrangeInversion f) f == (0 : 1 : repeat 0)+--+integralLagrangeInversion :: (Eq a, Num a) => [a] -> [a]+integralLagrangeInversion series_ = +  case series of+    (0:1:rest) -> 0 : 1 : [ f n | n<-[1..] ]+    _ -> error "integralLagrangeInversion: the series should start with (0 + x + a2*x^2 + ...)"+  where+    series = series_ ++ repeat 0+    as  = tail series +    a i = as !! i+    f n = sum [ fromInteger (lagrangeCoeff p) * product [ (a i)^j | (i,j) <- toExponentialForm p ]+              | p <- partitions n+              ] ++-- | We expect the input series to match @(0:a1:_)@. with a1 nonzero The following is true for the result (at least with exact arithmetic):+--+-- > substitute f (lagrangeInversion f) == (0 : 1 : repeat 0)+-- > substitute (lagrangeInversion f) f == (0 : 1 : repeat 0)+--+lagrangeInversion :: (Eq a, Fractional a) => [a] -> [a]+lagrangeInversion series_ = +  case series of+    (0:a1:rest) -> if a1 ==0 +      then err +      else 0 : (1/a1) : [ f n / a1^(n+1) | n<-[1..] ]+    _ -> err+  where+    err    = error "lagrangeInversion: the series should start with (0 + a1*x + a2*x^2 + ...) where a1 is non-zero"+    series = series_ ++ repeat 0+    a1  = series !! 1+    as  = map (/a1) (tail series)+    a i = as !! i+    f n = sum [ fromInteger (lagrangeCoeff p) * product [ (a i)^j | (i,j) <- toExponentialForm p ]+              | p <- partitions n+              ] +  +--------------------------------------------------------------------------------+-- * Power series expansions of elementary functions++-- | Power series expansion of @exp(x)@+expSeries :: Fractional a => [a]+expSeries = go 0 1 where+  go i e = e : go (i+1) (e / (i+1))++-- | Power series expansion of @cos(x)@+cosSeries :: Fractional a => [a]+cosSeries = go 0 1 where+  go i e = e : 0 : go (i+2) (-e / ((i+1)*(i+2)))++-- | Power series expansion of @sin(x)@+sinSeries :: Fractional a => [a]+sinSeries = go 1 1 where+  go i e = 0 : e : go (i+2) (-e / ((i+1)*(i+2)))++-- | Power series expansion of @cosh(x)@+coshSeries :: Fractional a => [a]+coshSeries = go 0 1 where+  go i e = e : 0 : go (i+2) (e / ((i+1)*(i+2)))++-- | Power series expansion of @sinh(x)@+sinhSeries :: Fractional a => [a]+sinhSeries = go 1 1 where+  go i e = 0 : e : go (i+2) (e / ((i+1)*(i+2)))++-- | Power series expansion of @log(1+x)@+log1Series :: Fractional a => [a]+log1Series = 0 : go 1 1 where+  go i e = (e/i) : go (i+1) (-e)++-- | Power series expansion of @(1-Sqrt[1-4x])/(2x)@ (the coefficients are the Catalan numbers)+dyckSeries :: Num a => [a]+dyckSeries = [ fromInteger (catalan i) | i<-[(0::Int)..] ]++--------------------------------------------------------------------------------+-- * \"Coin\" series++-- | Power series expansion of +-- +-- > 1 / ( (1-x^k_1) * (1-x^k_2) * ... * (1-x^k_n) )+--+-- Example:+--+-- @(coinSeries [2,3,5])!!k@ is the number of ways +-- to pay @k@ dollars with coins of two, three and five dollars.+--+-- TODO: better name?+coinSeries :: [Int] -> [Integer]+coinSeries [] = 1 : repeat 0+coinSeries (k:ks) = xs where+  xs = zipWith (+) (coinSeries ks) (replicate k 0 ++ xs) ++-- | Generalization of the above to include coefficients: expansion of +--  +-- > 1 / ( (1-a_1*x^k_1) * (1-a_2*x^k_2) * ... * (1-a_n*x^k_n) ) +-- +coinSeries' :: Num a => [(a,Int)] -> [a]+coinSeries' [] = 1 : repeat 0+coinSeries' ((a,k):aks) = xs where+  xs = zipWith (+) (coinSeries' aks) (replicate k 0 ++ map (*a) xs) ++convolveWithCoinSeries :: [Int] -> [Integer] -> [Integer]+convolveWithCoinSeries ks series1 = worker ks where+  series = series1 ++ repeat 0+  worker [] = series+  worker (k:ks) = xs where+    xs = zipWith (+) (worker ks) (replicate k 0 ++ xs)++convolveWithCoinSeries' :: Num a => [(a,Int)] -> [a] -> [a]+convolveWithCoinSeries' ks series1 = worker ks where+  series = series1 ++ repeat 0+  worker [] = series+  worker ((a,k):aks) = xs where+    xs = zipWith (+) (worker aks) (replicate k 0 ++ map (*a) xs)++--------------------------------------------------------------------------------+-- * Reciprocals of products of polynomials++-- | Convolution of many 'pseries', that is, the expansion of the reciprocal+-- of a product of polynomials+productPSeries :: [[Int]] -> [Integer]+productPSeries = foldl (flip convolveWithPSeries) unitSeries++-- | The same, with coefficients.+productPSeries' :: Num a => [[(a,Int)]] -> [a]+productPSeries' = foldl (flip convolveWithPSeries') unitSeries++convolveWithProductPSeries :: [[Int]] -> [Integer] -> [Integer]+convolveWithProductPSeries kss ser = foldl (flip convolveWithPSeries) ser kss++-- | This is the most general function in this module; all the others+-- are special cases of this one.  +convolveWithProductPSeries' :: Num a => [[(a,Int)]] -> [a] -> [a] +convolveWithProductPSeries' akss ser = foldl (flip convolveWithPSeries') ser akss+  +--------------------------------------------------------------------------------+-- * Reciprocals of polynomials++-- Reciprocals of polynomials, without coefficients++-- | The power series expansion of +--+-- > 1 / (1 - x^k_1 - x^k_2 - x^k_3 - ... - x^k_n)+--+pseries :: [Int] -> [Integer]+pseries ks = convolveWithPSeries ks unitSeries++-- | Convolve with (the expansion of) +--+-- > 1 / (1 - x^k_1 - x^k_2 - x^k_3 - ... - x^k_n)+--+convolveWithPSeries :: [Int] -> [Integer] -> [Integer]+convolveWithPSeries ks series1 = ys where +  series = series1 ++ repeat 0 +  ys = worker ks ys +  worker [] _ = series +  worker (k:ks) ys = xs where+    xs = zipWith (+) (replicate k 0 ++ ys) (worker ks ys)++--------------------------------------------------------------------------------+--  Reciprocals of polynomials, with coefficients++-- | The expansion of +--+-- > 1 / (1 - a_1*x^k_1 - a_2*x^k_2 - a_3*x^k_3 - ... - a_n*x^k_n)+--+pseries' :: Num a => [(a,Int)] -> [a]+pseries' aks = convolveWithPSeries' aks unitSeries++-- | Convolve with (the expansion of) +--+-- > 1 / (1 - a_1*x^k_1 - a_2*x^k_2 - a_3*x^k_3 - ... - a_n*x^k_n)+--+convolveWithPSeries' :: Num a => [(a,Int)] -> [a] -> [a]+convolveWithPSeries' aks series1 = ys where +  series = series1 ++ repeat 0 +  ys = worker aks ys +  worker [] _ = series+  worker ((a,k):aks) ys = xs where+    xs = zipWith (+) (replicate k 0 ++ map (*a) ys) (worker aks ys)++{-+data Sign = Plus | Minus deriving (Eq,Show)++signValue :: Num a => Sign -> a+signValue Plus  =  1+signValue Minus = -1+-}++signedPSeries :: [(Sign,Int)] -> [Integer] +signedPSeries aks = convolveWithSignedPSeries aks unitSeries++-- | Convolve with (the expansion of) +--+-- > 1 / (1 +- x^k_1 +- x^k_2 +- x^k_3 +- ... +- x^k_n)+--+-- Should be faster than using `convolveWithPSeries'`.+-- Note: 'Plus' corresponds to the coefficient @-1@ in `pseries'` (since+-- there is a minus sign in the definition there)!+convolveWithSignedPSeries :: [(Sign,Int)] -> [Integer] -> [Integer]+convolveWithSignedPSeries aks series1 = ys where +  series = series1 ++ repeat 0 +  ys = worker aks ys +  worker [] _ = series+  worker ((a,k):aks) ys = xs where+    xs = case a of+      Minus -> zipWith (+) one two +      Plus  -> zipWith (-) one two+    one = worker aks ys+    two = replicate k 0 ++ ys+     +--------------------------------------------------------------------------------++
+ Math/Combinat/Partitions.hs view
@@ -0,0 +1,22 @@++-- | Partitions of integers and multisets. +-- Integer partitions are nonincreasing sequences of positive integers.+--+-- See:+--+--  * Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 3B.+--+--  * <http://en.wikipedia.org/wiki/Partition_(number_theory)>+--++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions+  ( module Math.Combinat.Partitions.Integer+  )+  where++--------------------------------------------------------------------------------++import Math.Combinat.Partitions.Integer++--------------------------------------------------------------------------------
+ Math/Combinat/Partitions/Integer.hs view
@@ -0,0 +1,708 @@++-- | Partitions of integers.+-- Integer partitions are nonincreasing sequences of positive integers.+--+-- See:+--+--  * Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 3B.+--+--  * <http://en.wikipedia.org/wiki/Partition_(number_theory)>+--+-- For example the partition+--+-- > Partition [8,6,3,3,1]+--+-- can be represented by the (English notation) Ferrers diagram:+--+-- <<svg/ferrers.svg>>+-- ++{-# LANGUAGE CPP, BangPatterns, ScopedTypeVariables #-}+module Math.Combinat.Partitions.Integer where++--------------------------------------------------------------------------------++import Data.List+import Control.Monad ( liftM , replicateM )++-- import Data.Map (Map)+-- import qualified Data.Map as Map++import Math.Combinat.Classes+import Math.Combinat.ASCII as ASCII+import Math.Combinat.Numbers (factorial,binomial,multinomial)+import Math.Combinat.Helper++import Data.Array+import System.Random++--------------------------------------------------------------------------------+-- * Type and basic stuff++-- | A partition of an integer. The additional invariant enforced here is that partitions +-- are monotone decreasing sequences of /positive/ integers. The @Ord@ instance is lexicographical.+newtype Partition = Partition [Int] deriving (Eq,Ord,Show,Read)++instance HasNumberOfParts Partition where+  numberOfParts (Partition p) = length p++---------------------------------------------------------------------------------+  +-- | Sorts the input, and cuts the nonpositive elements.+mkPartition :: [Int] -> Partition+mkPartition xs = Partition $ sortBy (reverseCompare) $ filter (>0) xs++-- | Assumes that the input is decreasing.+toPartitionUnsafe :: [Int] -> Partition+toPartitionUnsafe = Partition++-- | Checks whether the input is an integer partition. See the note at 'isPartition'!+toPartition :: [Int] -> Partition+toPartition xs = if isPartition xs+  then toPartitionUnsafe xs+  else error "toPartition: not a partition"+  +-- | This returns @True@ if the input is non-increasing sequence of +-- /positive/ integers (possibly empty); @False@ otherwise.+--+isPartition :: [Int] -> Bool+isPartition []  = True+isPartition [x] = x > 0+isPartition (x:xs@(y:_)) = (x >= y) && isPartition xs++isEmptyPartition :: Partition -> Bool+isEmptyPartition (Partition p) = null p++emptyPartition :: Partition+emptyPartition = Partition []++instance CanBeEmpty Partition where+  empty   = emptyPartition+  isEmpty = isEmptyPartition++fromPartition :: Partition -> [Int]+fromPartition (Partition part) = part++-- | The first element of the sequence.+partitionHeight :: Partition -> Int+partitionHeight (Partition part) = case part of+  (p:_) -> p+  []    -> 0+  +-- | The length of the sequence (that is, the number of parts).+partitionWidth :: Partition -> Int+partitionWidth (Partition part) = length part++instance HasHeight Partition where+  height = partitionHeight+ +instance HasWidth Partition where+  width = partitionWidth++heightWidth :: Partition -> (Int,Int)+heightWidth part = (height part, width part)++-- | The weight of the partition +--   (that is, the sum of the corresponding sequence).+partitionWeight :: Partition -> Int+partitionWeight (Partition part) = sum' part++instance HasWeight Partition where +  weight = partitionWeight++-- | The dual (or conjugate) partition.+dualPartition :: Partition -> Partition+dualPartition (Partition part) = Partition (_dualPartition part)++instance HasDuality Partition where +  dual = dualPartition++data Pair = Pair !Int !Int++_dualPartition :: [Int] -> [Int]+_dualPartition [] = []+_dualPartition xs = go 0 (diffSequence xs) [] where+  go !i (d:ds) acc = go (i+1) ds (d:acc)+  go n  []     acc = finish n acc +  finish !j (k:ks) = replicate k j ++ finish (j-1) ks+  finish _  []     = []++{-+-- more variations:++_dualPartition_b :: [Int] -> [Int]+_dualPartition_b [] = []+_dualPartition_b xs = go 1 (diffSequence xs) [] where+  go !i (d:ds) acc = go (i+1) ds ((d,i):acc)+  go _  []     acc = concatMap (\(d,i) -> replicate d i) acc++_dualPartition_c :: [Int] -> [Int]+_dualPartition_c [] = []+_dualPartition_c xs = reverse $ concat $ zipWith f [1..] (diffSequence xs) where+  f _ 0 = []+  f k d = replicate d k+-}++-- | A simpler, but bit slower (about twice?) implementation of dual partition+_dualPartitionNaive :: [Int] -> [Int]+_dualPartitionNaive [] = []+_dualPartitionNaive xs@(k:_) = [ length $ filter (>=i) xs | i <- [1..k] ]++-- | From a sequence @[a1,a2,..,an]@ computes the sequence of differences+-- @[a1-a2,a2-a3,...,an-0]@+diffSequence :: [Int] -> [Int]+diffSequence = go where+  go (x:ys@(y:_)) = (x-y) : go ys +  go [x] = [x]+  go []  = []++-- | Example:+--+-- > elements (toPartition [5,4,1]) ==+-- >   [ (1,1), (1,2), (1,3), (1,4), (1,5)+-- >   , (2,1), (2,2), (2,3), (2,4)+-- >   , (3,1)+-- >   ]+--+elements :: Partition -> [(Int,Int)]+elements (Partition part) = _elements part++_elements :: [Int] -> [(Int,Int)]+_elements shape = [ (i,j) | (i,l) <- zip [1..] shape, j<-[1..l] ] ++---------------------------------------------------------------------------------+-- * Exponential form++-- | We convert a partition to exponential form.+-- @(i,e)@ mean @(i^e)@; for example @[(1,4),(2,3)]@ corresponds to @(1^4)(2^3) = [2,2,2,1,1,1,1]@. Another example:+--+-- > toExponentialForm (Partition [5,5,3,2,2,2,2,1,1]) == [(1,2),(2,4),(3,1),(5,2)]+--+toExponentialForm :: Partition -> [(Int,Int)]+toExponentialForm = _toExponentialForm . fromPartition++_toExponentialForm :: [Int] -> [(Int,Int)]+_toExponentialForm = reverse . map (\xs -> (head xs,length xs)) . group++fromExponentialFrom :: [(Int,Int)] -> Partition+fromExponentialFrom = Partition . sortBy reverseCompare . go where+  go ((j,e):rest) = replicate e j ++ go rest+  go []           = []   ++---------------------------------------------------------------------------------+-- * Automorphisms ++-- | Computes the number of \"automorphisms\" of a given integer partition.+countAutomorphisms :: Partition -> Integer  +countAutomorphisms = _countAutomorphisms . fromPartition++_countAutomorphisms :: [Int] -> Integer+_countAutomorphisms = multinomial . map length . group++---------------------------------------------------------------------------------+-- * Generating partitions++-- | Partitions of @d@.+partitions :: Int -> [Partition]+partitions = map Partition . _partitions++-- | Partitions of @d@, as lists+_partitions :: Int -> [[Int]]+_partitions d = go d d where+  go _  0  = [[]]+  go !h !n = [ a:as | a<-[1..min n h], as <- go a (n-a) ]++-- | Number of partitions of @n@+countPartitions :: Int -> Integer+countPartitions n = partitionCountList !! n++-- | This uses 'countPartitions'', and thus is slow+countPartitionsNaive :: Int -> Integer+countPartitionsNaive d = countPartitions' (d,d) d++--------------------------------------------------------------------------------++-- | Infinite list of number of partitions of @0,1,2,...@+--+-- This uses the infinite product formula the generating function of partitions, recursively+-- expanding it; it is quite fast.+--+-- > partitionCountList == map countPartitions [0..]+--+partitionCountList :: [Integer]+partitionCountList = final where++  final = go 1 (1:repeat 0) ++  go !k (x:xs) = x : go (k+1) ys where+    ys = zipWith (+) xs (take k final ++ ys)+    -- explanation:+    --   xs == drop k $ f (k-1)+    --   ys == drop k $ f (k  )  ++{-++Full explanation of 'partitionCountList':+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++let f k = productPSeries $ map (:[]) [1..k]++f 0 = [1,0,0,0,0,0,0,0...]+f 1 = [1,1,1,1,1,1,1,1...]+f 2 = [1,1,2,2,3,3,4,4...]+f 3 = [1,1,2,3,4,5,7,8...]++observe: ++* take (k+1) (f k) == take (k+1) partitionCountList+* f (k+1) == zipWith (+) (f k) (replicate (k+1) 0 ++ f (k+1))++now apply (drop (k+1)) to the second one : ++* drop (k+1) (f (k+1)) == zipWith (+) (drop (k+1) $ f k) (f (k+1))+* f (k+1) = take (k+1) final ++ drop (k+1) (f (k+1))++-}++--------------------------------------------------------------------------------++-- | Naive infinite list of number of partitions of @0,1,2,...@+--+-- > partitionCountListNaive == map countPartitionsNaive [0..]+--+-- This is much slower than the power series expansion above.+--+partitionCountListNaive :: [Integer]+partitionCountListNaive = map countPartitionsNaive [0..]++-- | All integer partitions up to a given degree (that is, all integer partitions whose sum is less or equal to @d@)+allPartitions :: Int -> [Partition]+allPartitions d = concat [ partitions i | i <- [0..d] ]++-- | All integer partitions up to a given degree (that is, all integer partitions whose sum is less or equal to @d@),+-- grouped by weight+allPartitionsGrouped :: Int -> [[Partition]]+allPartitionsGrouped d = [ partitions i | i <- [0..d] ]++-- | All integer partitions fitting into a given rectangle.+allPartitions'  +  :: (Int,Int)        -- ^ (height,width)+  -> [Partition]+allPartitions' (h,w) = concat [ partitions' (h,w) i | i <- [0..d] ] where d = h*w++-- | All integer partitions fitting into a given rectangle, grouped by weight.+allPartitionsGrouped'  +  :: (Int,Int)        -- ^ (height,width)+  -> [[Partition]]+allPartitionsGrouped' (h,w) = [ partitions' (h,w) i | i <- [0..d] ] where d = h*w++-- | # = \\binom { h+w } { h }+countAllPartitions' :: (Int,Int) -> Integer+countAllPartitions' (h,w) = +  binomial (h+w) (min h w)+  --sum [ countPartitions' (h,w) i | i <- [0..d] ] where d = h*w++countAllPartitions :: Int -> Integer+countAllPartitions d = sum' [ countPartitions i | i <- [0..d] ]++-- | Integer partitions of @d@, fitting into a given rectangle, as lists.+_partitions' +  :: (Int,Int)     -- ^ (height,width)+  -> Int           -- ^ d+  -> [[Int]]        +_partitions' _ 0 = [[]] +_partitions' ( 0 , _) d = if d==0 then [[]] else []+_partitions' ( _ , 0) d = if d==0 then [[]] else []+_partitions' (!h ,!w) d = +  [ i:xs | i <- [1..min d h] , xs <- _partitions' (i,w-1) (d-i) ]++-- | Partitions of d, fitting into a given rectangle. The order is again lexicographic.+partitions'  +  :: (Int,Int)     -- ^ (height,width)+  -> Int           -- ^ d+  -> [Partition]+partitions' hw d = map toPartitionUnsafe $ _partitions' hw d        ++countPartitions' :: (Int,Int) -> Int -> Integer+countPartitions' _ 0 = 1+countPartitions' (0,_) d = if d==0 then 1 else 0+countPartitions' (_,0) d = if d==0 then 1 else 0+countPartitions' (h,w) d = sum+  [ countPartitions' (i,w-1) (d-i) | i <- [1..min d h] ] +++---------------------------------------------------------------------------------+-- * Random partitions++-- | Uniformly random partition of the given weight. +--+-- NOTE: This algorithm is effective for small @n@-s (say @n@ up to a few hundred \/ one thousand it should work nicely),+-- and the first time it is executed may be slower (as it needs to build the table 'partitionCountList' first)+--+-- Algorithm of Nijenhuis and Wilf (1975); see+--+-- * Knuth Vol 4A, pre-fascicle 3B, exercise 47;+--+-- * Nijenhuis and Wilf: Combinatorial Algorithms for Computers and Calculators, chapter 10+--+randomPartition :: RandomGen g => Int -> g -> (Partition, g)+randomPartition n g = (p, g') where+  ([p], g') = randomPartitions 1 n g++-- | Generates several uniformly random partitions of @n@ at the same time.+-- Should be a little bit faster then generating them individually.+--+randomPartitions +  :: forall g. RandomGen g +  => Int   -- ^ number of partitions to generate+  -> Int   -- ^ the weight of the partitions+  -> g -> ([Partition], g)+randomPartitions howmany n = runRand $ replicateM howmany (worker n []) where++  table = listArray (0,n) $ take (n+1) partitionCountList :: Array Int Integer+  cnt k = table ! k+ +  finish :: [(Int,Int)] -> Partition+  finish = mkPartition . concatMap f where f (j,d) = replicate j d++  fi :: Int -> Integer +  fi = fromIntegral++  find_jd :: Int -> Integer -> (Int,Int)+  find_jd m capm = go 0 [ (j,d) | j<-[1..n], d<-[1..div m j] ] where+    go :: Integer -> [(Int,Int)] -> (Int,Int)+    go !s []   = (1,1)       -- ??+    go !s [jd] = jd          -- ??+    go !s (jd@(j,d):rest) = +      if s' > capm +        then jd +        else go s' rest+      where+        s' = s + fi d * cnt (m - j*d)++  worker :: Int -> [(Int,Int)] -> Rand g Partition+  worker  0 acc = return $ finish acc+  worker !m acc = do+    capm <- randChoose (0, (fi m) * cnt m - 1)+    let jd@(!j,!d) = find_jd m capm+    worker (m - j*d) (jd:acc)+++---------------------------------------------------------------------------------+-- * Dominance order ++-- | @q \`dominates\` p@ returns @True@ if @q >= p@ in the dominance order of partitions+-- (this is partial ordering on the set of partitions of @n@).+--+-- See <http://en.wikipedia.org/wiki/Dominance_order>+--+dominates :: Partition -> Partition -> Bool+dominates (Partition qs) (Partition ps) +  = and $ zipWith (>=) (sums (qs ++ repeat 0)) (sums ps)+  where+    sums = scanl (+) 0+++-- | Lists all partitions of the same weight as @lambda@ and also dominated by @lambda@+-- (that is, all partial sums are less or equal):+--+-- > dominatedPartitions lam == [ mu | mu <- partitions (weight lam), lam `dominates` mu ]+-- +dominatedPartitions :: Partition -> [Partition]    +dominatedPartitions (Partition lambda) = map Partition (_dominatedPartitions lambda)++_dominatedPartitions :: [Int] -> [[Int]]+_dominatedPartitions []     = [[]]+_dominatedPartitions lambda = go (head lambda) w dsums 0 where++  n = length lambda+  w = sum    lambda+  dsums = scanl1 (+) (lambda ++ repeat 0)++  go _   0 _       _  = [[]]+  go !h !w (!d:ds) !e  +    | w >  0  = [ (a:as) | a <- [1..min h (d-e)] , as <- go a (w-a) ds (e+a) ] +    | w == 0  = [[]]+    | w <  0  = error "_dominatedPartitions: fatal error; shouldn't happen"++-- | Lists all partitions of the sime weight as @mu@ and also dominating @mu@+-- (that is, all partial sums are greater or equal):+--+-- > dominatingPartitions mu == [ lam | lam <- partitions (weight mu), lam `dominates` mu ]+-- +dominatingPartitions :: Partition -> [Partition]    +dominatingPartitions (Partition mu) = map Partition (_dominatingPartitions mu)++_dominatingPartitions :: [Int] -> [[Int]]+_dominatingPartitions []     = [[]]+_dominatingPartitions mu     = go w w dsums 0 where++  n = length mu+  w = sum    mu+  dsums = scanl1 (+) (mu ++ repeat 0)++  go _   0 _       _  = [[]]+  go !h !w (!d:ds) !e  +    | w >  0  = [ (a:as) | a <- [max 0 (d-e)..min h w] , as <- go a (w-a) ds (e+a) ] +    | w == 0  = [[]]+    | w <  0  = error "_dominatingPartitions: fatal error; shouldn't happen"++--------------------------------------------------------------------------------+-- * Partitions with given number of parts++-- | Lists partitions of @n@ into @k@ parts.+--+-- > sort (partitionsWithKParts k n) == sort [ p | p <- partitions n , numberOfParts p == k ]+--+-- Naive recursive algorithm.+--+partitionsWithKParts +  :: Int    -- ^ @k@ = number of parts+  -> Int    -- ^ @n@ = the integer we partition+  -> [Partition]+partitionsWithKParts k n = map Partition $ go n k n where+{-+  h = max height+  k = number of parts+  n = integer+-}+  go !h !k !n +    | k <  0     = []+    | k == 0     = if h>=0 && n==0 then [[] ] else []+    | k == 1     = if h>=n && n>=1 then [[n]] else []+    | otherwise  = [ a:p | a <- [1..(min h (n-k+1))] , p <- go a (k-1) (n-a) ]++countPartitionsWithKParts +  :: Int    -- ^ @k@ = number of parts+  -> Int    -- ^ @n@ = the integer we partition+  -> Integer+countPartitionsWithKParts k n = go n k n where+  go !h !k !n +    | k <  0     = 0+    | k == 0     = if h>=0 && n==0 then 1 else 0+    | k == 1     = if h>=n && n>=1 then 1 else 0+    | otherwise  = sum' [ go a (k-1) (n-a) | a<-[1..(min h (n-k+1))] ]++--------------------------------------------------------------------------------+-- * Partitions with only odd\/distinct parts++-- | Partitions of @n@ with only odd parts+partitionsWithOddParts :: Int -> [Partition]+partitionsWithOddParts d = map Partition (go d d) where+  go _  0  = [[]]+  go !h !n = [ a:as | a<-[1,3..min n h], as <- go a (n-a) ]++{-+-- | Partitions of @n@ with only even parts+--+-- Note: this is not very interesting, it's just @(map.map) (2*) $ _partitions (div n 2)@+--+partitionsWithEvenParts :: Int -> [Partition]+partitionsWithEvenParts d = map Partition (go d d) where+  go _  0  = [[]]+  go !h !n = [ a:as | a<-[2,4..min n h], as <- go a (n-a) ]+-}++-- | Partitions of @n@ with distinct parts.+-- +-- Note:+--+-- > length (partitionsWithDistinctParts d) == length (partitionsWithOddParts d)+--+partitionsWithDistinctParts :: Int -> [Partition]+partitionsWithDistinctParts d = map Partition (go d d) where+  go _  0  = [[]]+  go !h !n = [ a:as | a<-[1..min n h], as <- go (a-1) (n-a) ]++--------------------------------------------------------------------------------+-- * Sub- and super-partitions of a given partition++-- | Returns @True@ of the first partition is a subpartition (that is, fit inside) of the second.+-- This includes equality+isSubPartitionOf :: Partition -> Partition -> Bool+isSubPartitionOf (Partition ps) (Partition qs) = and $ zipWith (<=) ps (qs ++ repeat 0)++-- | This is provided for convenience\/completeness only, as:+--+-- > isSuperPartitionOf q p == isSubPartitionOf p q+--+isSuperPartitionOf :: Partition -> Partition -> Bool+isSuperPartitionOf (Partition qs) (Partition ps) = and $ zipWith (<=) ps (qs ++ repeat 0)+++-- | Sub-partitions of a given partition with the given weight:+--+-- > sort (subPartitions d q) == sort [ p | p <- partitions d, isSubPartitionOf p q ]+--+subPartitions :: Int -> Partition -> [Partition]+subPartitions d (Partition ps) = map Partition (_subPartitions d ps)++_subPartitions :: Int -> [Int] -> [[Int]]+_subPartitions d big+  | null big       = if d==0 then [[]] else []+  | d > sum' big   = []+  | d < 0          = []+  | otherwise      = go d (head big) big+  where+    go :: Int -> Int -> [Int] -> [[Int]]+    go !k !h []      = if k==0 then [[]] else []+    go !k !h (b:bs) +      | k<0 || h<0   = []+      | k==0         = [[]]+      | h==0         = []+      | otherwise    = [ this:rest | this <- [1..min h b] , rest <- go (k-this) this bs ]++----------------------------------------++-- | All sub-partitions of a given partition+allSubPartitions :: Partition -> [Partition]+allSubPartitions (Partition ps) = map Partition (_allSubPartitions ps)++_allSubPartitions :: [Int] -> [[Int]]+_allSubPartitions big +  | null big   = [[]]+  | otherwise  = go (head big) big+  where+    go _  [] = [[]]+    go !h (b:bs) +      | h==0         = []+      | otherwise    = [] : [ this:rest | this <- [1..min h b] , rest <- go this bs ]++----------------------------------------++-- | Super-partitions of a given partition with the given weight:+--+-- > sort (superPartitions d p) == sort [ q | q <- partitions d, isSubPartitionOf p q ]+--+superPartitions :: Int -> Partition -> [Partition]+superPartitions d (Partition ps) = map Partition (_superPartitions d ps)++_superPartitions :: Int -> [Int] -> [[Int]]+_superPartitions dd small+  | dd < w0     = []+  | null small  = _partitions dd+  | otherwise   = go dd w1 dd (small ++ repeat 0)+  where+    w0 = sum' small+    w1 = w0 - head small+    -- d = remaining weight of the outer partition we are constructing+    -- w = remaining weight of the inner partition (we need to reserve at least this amount)+    -- h = max height (decreasing)+    go !d !w !h (!a:as@(b:_)) +      | d < 0     = []+      | d == 0    = if a == 0 then [[]] else []+      | otherwise = [ this:rest | this <- [max 1 a .. min h (d-w)] , rest <- go (d-this) (w-b) this as ]+    +--------------------------------------------------------------------------------+-- * The Pieri rule++-- | The Pieri rule computes @s[lambda]*h[n]@ as a sum of @s[mu]@-s (each with coefficient 1).+--+-- See for example <http://en.wikipedia.org/wiki/Pieri's_formula>+--+pieriRule :: Partition -> Int -> [Partition] +pieriRule (Partition lambda) n = map Partition (_pieriRule lambda n) where++  -- | We assume here that @lambda@ is a partition (non-increasing sequence of /positive/ integers)! +  _pieriRule :: [Int] -> Int -> [[Int]] +  _pieriRule lambda n+    | n == 0     = [lambda]+    | n <  0     = [] +    | otherwise  = go n diffs dsums (lambda++[0]) +    where+      diffs = n : diffSequence lambda                 -- maximum we can add to a given row+      dsums = reverse $ scanl1 (+) (reverse diffs)    -- partial sums of remaining total we can add+      go !k (d:ds) (p:ps@(q:_)) (l:ls) +        | k > p     = []+        | otherwise = [ h:tl | a <- [ max 0 (k-q) .. min d k ] , let h = l+a , tl <- go (k-a) ds ps ls ]+      go !k [d]    _      [l]    = if k <= d +                                     then if l+k>0 then [[l+k]] else [[]]+                                     else []+      go !k []     _      _      = if k==0 then [[]] else []++-- | The dual Pieri rule computes @s[lambda]*e[n]@ as a sum of @s[mu]@-s (each with coefficient 1)+dualPieriRule :: Partition -> Int -> [Partition] +dualPieriRule lam n = map dualPartition $ pieriRule (dualPartition lam) n+++{- +-- moved to "Math.Combinat.Tableaux.GelfandTsetlin"++-- | Computes the Schur expansion of @h[n1]*h[n2]*h[n3]*...*h[nk]@ via iterating the Pieri rule+iteratedPieriRule :: Num coeff => [Int] -> Map Partition coeff+iteratedPieriRule = iteratedPieriRule' (Partition [])++-- | Iterating the Pieri rule, we can compute the Schur expansion of+-- @h[lambda]*h[n1]*h[n2]*h[n3]*...*h[nk]@+iteratedPieriRule' :: Num coeff => Partition -> [Int] -> Map Partition coeff+iteratedPieriRule' plambda ns = iteratedPieriRule'' (plambda,1) ns++{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Int    ) -> [Int] -> Map Partition Int     #-}+{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Integer) -> [Int] -> Map Partition Integer #-}+iteratedPieriRule'' :: Num coeff => (Partition,coeff) -> [Int] -> Map Partition coeff+iteratedPieriRule'' (plambda,coeff0) ns = worker (Map.singleton plambda coeff0) ns where+  worker old []     = old+  worker old (n:ns) = worker new ns where+    stuff = [ (coeff, pieriRule lam n) | (lam,coeff) <- Map.toList old ] +    new   = foldl' f Map.empty stuff +    f t0 (c,ps) = foldl' (\t p -> Map.insertWith (+) p c t) t0 ps  +-}++--------------------------------------------------------------------------------+-- * ASCII Ferrers diagrams++-- | Which orientation to draw the Ferrers diagrams.+-- For example, the partition [5,4,1] corrsponds to:+--+-- In standard English notation:+-- +-- >  @@@@@+-- >  @@@@+-- >  @+--+--+-- In English notation rotated by 90 degrees counter-clockwise:+--+-- > @  +-- > @@+-- > @@+-- > @@+-- > @@@+--+--+-- And in French notation:+--+-- +-- >  @+-- >  @@@@+-- >  @@@@@+--+--+data PartitionConvention+  = EnglishNotation          -- ^ English notation+  | EnglishNotationCCW       -- ^ English notation rotated by 90 degrees counterclockwise+  | FrenchNotation           -- ^ French notation (mirror of English notation to the x axis)+  deriving (Eq,Show)++-- | Synonym for @asciiFerrersDiagram\' EnglishNotation \'\@\'@+--+-- Try for example:+--+-- > autoTabulate RowMajor (Right 8) (map asciiFerrersDiagram $ partitions 9)+--+asciiFerrersDiagram :: Partition -> ASCII+asciiFerrersDiagram = asciiFerrersDiagram' EnglishNotation '@'++asciiFerrersDiagram' :: PartitionConvention -> Char -> Partition -> ASCII+asciiFerrersDiagram' conv ch part = ASCII.asciiFromLines (map f ys) where+  f n = replicate n ch +  ys  = case conv of+          EnglishNotation    -> fromPartition part+          EnglishNotationCCW -> reverse $ fromPartition $ dualPartition part+          FrenchNotation     -> reverse $ fromPartition $ part++instance DrawASCII Partition where+  ascii = asciiFerrersDiagram++--------------------------------------------------------------------------------+
+ Math/Combinat/Partitions/Multiset.hs view
@@ -0,0 +1,24 @@++-- | Partitions of a multiset+module Math.Combinat.Partitions.Multiset where++--------------------------------------------------------------------------------++import Data.Array.Unboxed+import Data.List++import Math.Combinat.Partitions.Vector++--------------------------------------------------------------------------------+                              +-- | Partitions of a multiset. Internally, this uses the vector partition algorithm+partitionMultiset :: (Eq a, Ord a) => [a] -> [[[a]]]+partitionMultiset xs = parts where+  parts = (map . map) (f . elems) temp+  f ns = concat (zipWith replicate ns zs)+  temp = fasc3B_algorithm_M counts+  counts = map length ys+  ys = group (sort xs) +  zs = map head ys++--------------------------------------------------------------------------------
+ Math/Combinat/Partitions/NonCrossing.hs view
@@ -0,0 +1,205 @@++-- | Non-crossing partitions.+--+-- See eg. <http://en.wikipedia.org/wiki/Noncrossing_partition>+--+-- Non-crossing partitions of the set @[1..n]@ are encoded as lists of lists+-- in standard form: Entries decreasing in each block  and blocks listed in increasing order of their first entries.+-- For example the partition in the diagram+--+-- <<svg/noncrossing.svg>>+--+-- is represented as+--+-- > NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]+--++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.NonCrossing where++--------------------------------------------------------------------------------++import Control.Applicative++import Data.List+import Data.Ord++import System.Random++import Math.Combinat.Numbers+import Math.Combinat.LatticePaths+import Math.Combinat.Helper+import Math.Combinat.Partitions.Set+import Math.Combinat.Classes++--------------------------------------------------------------------------------+-- * The type of non-crossing partitions++-- | A non-crossing partition of the set @[1..n]@ in standard form: +-- entries decreasing in each block  and blocks listed in increasing order of their first entries.+newtype NonCrossing = NonCrossing [[Int]] deriving (Eq,Ord,Show,Read)++-- | Checks whether a set partition is noncrossing.+--+-- Implementation method: we convert to a Dyck path and then back again, and finally compare. +-- Probably not very efficient, but should be better than a naive check for crosses...)+--+_isNonCrossing :: [[Int]] -> Bool+_isNonCrossing zzs0 = _isNonCrossingUnsafe (_standardizeNonCrossing zzs0)++-- | Warning: This function assumes the standard ordering!+_isNonCrossingUnsafe :: [[Int]] -> Bool+_isNonCrossingUnsafe zzs = +  case _nonCrossingPartitionToDyckPathMaybe zzs of+    Nothing   -> False+    Just dyck -> case dyckPathToNonCrossingPartitionMaybe dyck of+      Nothing                -> False+      Just (NonCrossing yys) -> yys == zzs++-- | Convert to standard form: entries decreasing in each block +-- and blocks listed in increasing order of their first entries.+_standardizeNonCrossing :: [[Int]] -> [[Int]]+_standardizeNonCrossing = sortBy (comparing myhead) . map reverseSort where+  myhead xs = case xs of+    (x:xs) -> x+    []     -> error "_standardizeNonCrossing: empty subset"++fromNonCrossing :: NonCrossing -> [[Int]]+fromNonCrossing (NonCrossing xs) = xs++toNonCrossingUnsafe :: [[Int]] -> NonCrossing+toNonCrossingUnsafe = NonCrossing++-- | Throws an error if the input is not a non-crossing partition+toNonCrossing :: [[Int]] -> NonCrossing+toNonCrossing xxs = case toNonCrossingMaybe xxs of+  Just nc -> nc+  Nothing -> error "toNonCrossing: not a non-crossing partition"++toNonCrossingMaybe :: [[Int]] -> Maybe NonCrossing+toNonCrossingMaybe xxs0 = +  if _isNonCrossingUnsafe xxs+    then Just $ NonCrossing xxs+    else Nothing+  where +    xxs = _standardizeNonCrossing xxs0++-- | If a set partition is actually non-crossing, then we can convert it+setPartitionToNonCrossing :: SetPartition -> Maybe NonCrossing+setPartitionToNonCrossing (SetPartition zzs0) =+  if _isNonCrossingUnsafe zzs+    then Just $ NonCrossing zzs+    else Nothing+  where+    zzs = _standardizeNonCrossing zzs0++instance HasNumberOfParts NonCrossing where+  numberOfParts (NonCrossing p) = length p++--------------------------------------------------------------------------------+-- * Bijection to Dyck paths++-- | Bijection between Dyck paths and noncrossing partitions+--+-- Based on: David Callan: /Sets, Lists and Noncrossing Partitions/+--+-- Fails if the input is not a Dyck path.+dyckPathToNonCrossingPartition :: LatticePath -> NonCrossing+dyckPathToNonCrossingPartition = NonCrossing . go 0 [] [] [] where+  go :: Int -> [Int] -> [Int] -> [[Int]] -> LatticePath -> [[Int]] +  go !cnt stack small big path =+    case path of+      (x:xs) -> case x of +        UpStep   -> let cnt' = cnt + 1 in case xs of+          (y:ys)   -> case y of+            UpStep   -> go cnt' (cnt':stack) small                  big  xs  +            DownStep -> go cnt' (cnt':stack) []    (reverse small : big) xs+          []       -> error "dyckPathToNonCrossingPartition: last step is an UpStep (thus input was not a Dyck path)"+        DownStep -> case stack of+          (k:ks)   -> go cnt ks (k:small) big xs+          []       -> error "dyckPathToNonCrossingPartition: empty stack, shouldn't happen (thus input was not a Dyck path)"+      [] -> tail $ reverse (reverse small : big)++-- | Safe version of 'dyckPathToNonCrossingPartition'+dyckPathToNonCrossingPartitionMaybe :: LatticePath -> Maybe NonCrossing+dyckPathToNonCrossingPartitionMaybe = fmap NonCrossing . go 0 [] [] [] where+  go :: Int -> [Int] -> [Int] -> [[Int]] -> LatticePath -> Maybe [[Int]] +  go !cnt stack small big path =+    case path of+      (x:xs) -> case x of +        UpStep   -> let cnt' = cnt + 1 in case xs of+          (y:ys)   -> case y of+            UpStep   -> go cnt' (cnt':stack) small                  big  xs  +            DownStep -> go cnt' (cnt':stack) []    (reverse small : big) xs+          []       -> Nothing+        DownStep -> case stack of+          (k:ks)   -> go cnt ks (k:small) big xs+          []       -> Nothing+      [] -> Just $ tail $ reverse (reverse small : big)++-- | The inverse bijection (should never fail proper 'NonCrossing'-s)+nonCrossingPartitionToDyckPath :: NonCrossing -> LatticePath+nonCrossingPartitionToDyckPath (NonCrossing zzs) = go 0 zzs where+  go !k (ys@(y:_):yys) = replicate (y-k) UpStep ++ replicate (length ys) DownStep ++ go y yys+  go !k []             = []+  go _  _              = error "nonCrossingPartitionToDyckPath: shouldnt't happen"++-- | Safe version 'nonCrossingPartitionToDyckPath'+_nonCrossingPartitionToDyckPathMaybe :: [[Int]] -> Maybe LatticePath+_nonCrossingPartitionToDyckPathMaybe = go 0 where+  go !k (ys@(y:_):yys) = fmap (\zs -> replicate (y-k) UpStep ++ replicate (length ys) DownStep ++ zs) (go y yys)+  go !k []             = Just []+  go _  _              = Nothing++--------------------------------------------------------------------------------++{- +-- this should be mapped to NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]+testpath = [u,u,u,d,u,u,d,d,d,u,u,d,d,d,u,u,d,d] where+  u = UpStep+  d = DownStep++testnc = NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]+-}++--------------------------------------------------------------------------------+-- * Generating non-crossing partitions++-- | Lists all non-crossing partitions of @[1..n]@+--+-- Equivalent to (but orders of magnitude faster than) filtering out the non-crossing ones:+--+-- > (sort $ catMaybes $ map setPartitionToNonCrossing $ setPartitions n) == sort (nonCrossingPartitions n)+--+nonCrossingPartitions :: Int -> [NonCrossing]+nonCrossingPartitions = map dyckPathToNonCrossingPartition . dyckPaths++-- | Lists all non-crossing partitions of @[1..n]@ into @k@ parts.+--+-- > sort (nonCrossingPartitionsWithKParts k n) == sort [ p | p <- nonCrossingPartitions n , numberOfParts p == k ]+--+nonCrossingPartitionsWithKParts +  :: Int   -- ^ @k@ = number of parts +  -> Int   -- ^ @n@ = size of the set+  -> [NonCrossing]+nonCrossingPartitionsWithKParts k n = map dyckPathToNonCrossingPartition $ peakingDyckPaths k n++-- | Non-crossing partitions are counted by the Catalan numbers+countNonCrossingPartitions :: Int -> Integer+countNonCrossingPartitions = countDyckPaths++-- | Non-crossing partitions with @k@ parts are counted by the Naranaya numbers+countNonCrossingPartitionsWithKParts +  :: Int   -- ^ @k@ = number of parts +  -> Int   -- ^ @n@ = size of the set+  -> Integer+countNonCrossingPartitionsWithKParts = countPeakingDyckPaths++--------------------------------------------------------------------------------++-- | Uniformly random non-crossing partition+randomNonCrossingPartition :: RandomGen g => Int -> g -> (NonCrossing,g)+randomNonCrossingPartition n g0 = (dyckPathToNonCrossingPartition dyck, g1) where+  (dyck,g1) = randomDyckPath n g0++--------------------------------------------------------------------------------
+ Math/Combinat/Partitions/Plane.hs view
@@ -0,0 +1,124 @@++-- | Plane partitions. See eg. <http://en.wikipedia.org/wiki/Plane_partition>+--+-- Plane partitions are encoded as lists of lists of Z heights. For example the plane +-- partition in the picture+-- +-- <<svg/plane_partition.svg>>+--+-- is encoded as+--+-- > PlanePart [ [5,4,3,3,1]+-- >           , [4,4,2,1]+-- >           , [3,2]+-- >           , [2,1]+-- >           , [1]+-- >           , [1]+-- >           ]+-- +{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.Plane where++--------------------------------------------------------------------------------++import Data.List+import Data.Array++import Math.Combinat.Classes+import Math.Combinat.Partitions+import Math.Combinat.Tableaux as Tableaux+import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * the type of plane partitions++-- | A plane partition encoded as a tablaeu (the \"Z\" heights are the numbers)+newtype PlanePart = PlanePart [[Int]] deriving (Eq,Ord,Show)++fromPlanePart :: PlanePart -> [[Int]]+fromPlanePart (PlanePart xs) = xs++isValidPlanePart :: [[Int]] -> Bool+isValidPlanePart pps = +  and [ table!(i,j) >= table!(i  ,j+1) &&+        table!(i,j) >= table!(i+1,j  )+      | i<-[0..y-1] , j<-[0..x-1] +      ]+  where+    table :: Array (Int,Int) Int+    table = accumArray const 0 ((0,0),(y,x)) [ ((i,j),k) | (i,ps) <- zip [0..] pps , (j,k) <- zip [0..] ps ]+    y = length pps+    x = maximum (map length pps)++instance CanBeEmpty PlanePart where+  isEmpty = null . fromPlanePart+  empty   = PlanePart []++-- | Throws an exception if the input is not a plane partition+toPlanePart :: [[Int]] -> PlanePart+toPlanePart pps = if isValidPlanePart pps+  then PlanePart $ filter (not . null) $ map (filter (>0)) $ pps+  else error "toPlanePart: not a plane partition"++-- | The XY projected shape of a plane partition, as an integer partition+planePartShape :: PlanePart -> Partition+planePartShape = Tableaux.tableauShape . fromPlanePart++-- | The Z height of a plane partition+planePartZHeight :: PlanePart -> Int+planePartZHeight (PlanePart xs) = +  case xs of+    ((h:_):_) -> h+    _         -> 0++planePartWeight :: PlanePart -> Int+planePartWeight (PlanePart xs) = sum' (map sum' xs)++instance HasWeight PlanePart where+  weight = planePartWeight++--------------------------------------------------------------------------------+-- * constructing plane partitions++singleLayer :: Partition -> PlanePart+singleLayer = PlanePart . map (\k -> replicate k 1) . fromPartition ++-- |  Stacks layers of partitions into a plane partition.+-- Throws an exception if they do not form a plane partition.+stackLayers :: [Partition] -> PlanePart+stackLayers layers = if and [ isSubPartitionOf p q | (q,p) <- pairs layers ]+  then unsafeStackLayers layers+  else error "stackLayers: the layers do not form a plane partition"++-- | Stacks layers of partitions into a plane partition.+-- This is unsafe in the sense that we don't check that the partitions fit on the top of each other.+unsafeStackLayers :: [Partition] -> PlanePart+unsafeStackLayers []            = PlanePart []+unsafeStackLayers (bottom:rest) = PlanePart $ foldl addLayer (fromPlanePart $ singleLayer bottom) rest where+  addLayer :: [[Int]] -> Partition -> [[Int]]+  addLayer xxs (Partition ps) = [ zipWith (+) xs (replicate p 1 ++ repeat 0) | (xs,p) <- zip xxs (ps ++ repeat 0) ] ++-- | The \"layers\" of a plane partition (in direction @Z@). We should have+--+-- > unsafeStackLayers (planePartLayers pp) == pp+-- +planePartLayers :: PlanePart -> [Partition]+planePartLayers pp@(PlanePart xs) = [ layer h | h<-[1..planePartZHeight pp] ] where+  layer h = Partition $ filter (>0) $ map sum' $ (map . map) (f h) xs+  f h = \k -> if k>=h then 1 else 0++--------------------------------------------------------------------------------+-- * generating plane partitions++-- | Plane partitions of a given weight+planePartitions :: Int -> [PlanePart]+planePartitions d +  | d <  0     = []+  | d == 0     = [PlanePart []]+  | otherwise  = concat [ go (d-n) [p] | n<-[1..d] , p<-partitions n ]+  where+    go :: Int -> [Partition] -> [PlanePart]+    go  0   acc       = [unsafeStackLayers (reverse acc)]+    go !rem acc@(h:_) = concat [ go (rem-k) (this:acc) | k<-[1..rem] , this <- subPartitions k h ]++--------------------------------------------------------------------------------
+ Math/Combinat/Partitions/Set.hs view
@@ -0,0 +1,109 @@++-- | Set partitions.+--+-- See eg. <http://en.wikipedia.org/wiki/Partition_of_a_set>+-- ++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.Set where++--------------------------------------------------------------------------------++import Data.List+import Data.Ord++import System.Random++import Math.Combinat.Sets+import Math.Combinat.Numbers+import Math.Combinat.Helper+import Math.Combinat.Classes+import Math.Combinat.Partitions.Integer++--------------------------------------------------------------------------------+-- * The type of set partitions++-- | A partition of the set @[1..n]@ (in standard order)+newtype SetPartition = SetPartition [[Int]] deriving (Eq,Ord,Show,Read)++_standardizeSetPartition :: [[Int]] -> [[Int]]+_standardizeSetPartition = sortBy (comparing myhead) . map sort where+  myhead xs = case xs of+    (x:xs) -> x+    []     -> error "_standardizeSetPartition: empty subset"++fromSetPartition :: SetPartition -> [[Int]]+fromSetPartition (SetPartition zzs) = zzs++toSetPartitionUnsafe :: [[Int]] -> SetPartition+toSetPartitionUnsafe = SetPartition++toSetPartition :: [[Int]] -> SetPartition+toSetPartition zzs = if _isSetPartition zzs+  then SetPartition (_standardizeSetPartition zzs)+  else error "toSetPartition: not a set partition"++_isSetPartition :: [[Int]] -> Bool+_isSetPartition zzs = sort (concat zzs) == [1..n] where +  n = sum' (map length zzs)++instance HasNumberOfParts SetPartition where+  numberOfParts (SetPartition p) = length p++--------------------------------------------------------------------------------+-- * Forgetting the set structure++-- | The \"shape\" of a set partition is the partition we get when we forget the+-- set structure, keeping only the cardinalities.+--+setPartitionShape :: SetPartition -> Partition+setPartitionShape (SetPartition pps) = mkPartition (map length pps)++--------------------------------------------------------------------------------+-- * Generating set partitions++-- | Synonym for 'setPartitionsNaive'+setPartitions :: Int -> [SetPartition]+setPartitions = setPartitionsNaive++-- | Synonym for 'setPartitionsWithKPartsNaive'+--+-- > sort (setPartitionsWithKParts k n) == sort [ p | p <- setPartitions n , numberOfParts p == k ]+-- +setPartitionsWithKParts   +  :: Int    -- ^ @k@ = number of parts+  -> Int    -- ^ @n@ = size of the set+  -> [SetPartition]+setPartitionsWithKParts = setPartitionsWithKPartsNaive++-- | List all set partitions of @[1..n]@, naive algorithm+setPartitionsNaive :: Int -> [SetPartition]+setPartitionsNaive n = map (SetPartition . _standardizeSetPartition) $ go [1..n] where+  go :: [Int] -> [[[Int]]]+  go []     = [[]]+  go (z:zs) = [ s : rest | k <- [1..n] , s0 <- choose (k-1) zs , let s = z:s0 , rest <- go (zs \\ s) ]++-- | Set partitions of the set @[1..n]@ into @k@ parts+setPartitionsWithKPartsNaive +  :: Int    -- ^ @k@ = number of parts+  -> Int    -- ^ @n@ = size of the set+  -> [SetPartition]+setPartitionsWithKPartsNaive k n = map (SetPartition . _standardizeSetPartition) $ go k [1..n] where+  go :: Int -> [Int] -> [[[Int]]]+  go !k []     = if k==0 then [[]] else []+  go  1 zs     = [[zs]]+  go !k (z:zs) = [ s : rest | l <- [1..n] , s0 <- choose (l-1) zs , let s = z:s0 , rest <- go (k-1) (zs \\ s) ]+++-- | Set partitions are counted by the Bell numbers+countSetPartitions :: Int -> Integer+countSetPartitions = bellNumber ++-- | Set partitions of size @k@ are counted by the Stirling numbers of second kind+countSetPartitionsWithKParts +  :: Int    -- ^ @k@ = number of parts+  -> Int    -- ^ @n@ = size of the set+  -> Integer+countSetPartitionsWithKParts k n = stirling2nd n k++--------------------------------------------------------------------------------
+ Math/Combinat/Partitions/Skew.hs view
@@ -0,0 +1,135 @@++-- | Skew partitions.+--+-- Skew partitions are the difference of two integer partitions, denoted by @lambda/mu@.+--+-- For example+--+-- > mkSkewPartition (Partition [9,7,3,2,2,1] , Partition [5,3,2,1])+--+-- creates the skew partition @(9,7,3,2,2,1) / (5,3,2,1)@, which looks like+--+-- <<svg/skew3.svg>>+--++{-# LANGUAGE CPP, BangPatterns #-}+module Math.Combinat.Partitions.Skew where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Classes+import Math.Combinat.Partitions.Integer+import Math.Combinat.ASCII++--------------------------------------------------------------------------------+-- * Basics++-- | A skew partition @lambda/mu@ is internally represented by the list @[ (mu_i , lambda_i-mu_i) | i<-[1..n] ]@+newtype SkewPartition = SkewPartition [(Int,Int)] deriving (Eq,Ord,Show)++-- | @mkSkewPartition (lambda,mu)@ creates the skew partition @lambda/mu@.+-- Throws an error if @mu@ is not a sub-partition of @lambda@.+mkSkewPartition :: (Partition,Partition) -> SkewPartition+mkSkewPartition ( lam@(Partition bs) , mu@(Partition as)) = if mu `isSubPartitionOf` lam +  then SkewPartition $ zipWith (\b a -> (a,b-a)) bs (as ++ repeat 0)+  else error "mkSkewPartition: mu should be a subpartition of lambda!" ++-- | Returns 'Nothing' if @mu@ is not a sub-partition of @lambda@.+safeSkewPartition :: (Partition,Partition) -> Maybe SkewPartition+safeSkewPartition ( lam@(Partition bs) , mu@(Partition as)) = if mu `isSubPartitionOf` lam +  then Just $ SkewPartition $ zipWith (\b a -> (a,b-a)) bs (as ++ repeat 0)+  else Nothing++-- | The weight of a skew partition is the weight of the outer partition minus the+-- the weight of the inner partition (that is, the number of boxes present).+skewPartitionWeight :: SkewPartition -> Int+skewPartitionWeight (SkewPartition abs) = foldl' (+) 0 (map snd abs)++instance HasWeight SkewPartition where+  weight = skewPartitionWeight++-- | This function \"cuts off\" the \"uninteresting parts\" of a skew partition+normalizeSkewPartition :: SkewPartition -> SkewPartition+normalizeSkewPartition (SkewPartition abs) = SkewPartition abs' where+  (as,bs) = unzip abs+  a0 = minimum as+  k  = length (takeWhile (==0) bs)+  abs' = zip [ a-a0 | a <- drop k as ] (drop k bs)+   +-- | Returns the outer and inner partition of a skew partition, respectively:+--+-- > mkSkewPartition . fromSkewPartition == id+--+fromSkewPartition :: SkewPartition -> (Partition,Partition)+fromSkewPartition (SkewPartition list) = (toPartition (zipWith (+) as bs) , toPartition (filter (>0) as)) where+  (as,bs) = unzip list++-- | The @lambda@ part of @lambda/mu@+outerPartition :: SkewPartition -> Partition  +outerPartition = fst . fromSkewPartition ++-- | The @mu@ part of @lambda/mu@+innerPartition :: SkewPartition -> Partition  +innerPartition = snd . fromSkewPartition ++-- | The dual skew partition (that is, the mirror image to the main diagonal)+dualSkewPartition :: SkewPartition -> SkewPartition+dualSkewPartition = mkSkewPartition . f . fromSkewPartition where+  f (lam,mu) = (dualPartition lam, dualPartition mu)++instance HasDuality SkewPartition where+  dual = dualSkewPartition++--------------------------------------------------------------------------------+-- * Listing skew partitions++-- | Lists all skew partitions with the given outer shape and given (skew) weight+skewPartitionsWithOuterShape :: Partition -> Int -> [SkewPartition]+skewPartitionsWithOuterShape outer skewWeight +  | innerWeight < 0 || innerWeight > outerWeight = []+  | otherwise = [ mkSkewPartition (outer,inner) | inner <- subPartitions innerWeight outer ]+  where+    outerWeight = weight outer+    innerWeight = outerWeight - skewWeight ++-- | Lists all skew partitions with the given outer shape and any (skew) weight+allSkewPartitionsWithOuterShape :: Partition -> [SkewPartition]+allSkewPartitionsWithOuterShape outer +  = concat [ skewPartitionsWithOuterShape outer w | w<-[0..outerWeight] ]+  where+    outerWeight = weight outer++-- | Lists all skew partitions with the given inner shape and given (skew) weight+skewPartitionsWithInnerShape :: Partition -> Int -> [SkewPartition]+skewPartitionsWithInnerShape inner skewWeight +  | innerWeight > outerWeight = []+  | otherwise = [ mkSkewPartition (outer,inner) | outer <- superPartitions outerWeight inner ]+  where+    outerWeight = innerWeight + skewWeight +    innerWeight = weight inner ++--------------------------------------------------------------------------------+-- * ASCII++asciiSkewFerrersDiagram :: SkewPartition -> ASCII+asciiSkewFerrersDiagram = asciiSkewFerrersDiagram' ('@','.') EnglishNotation++asciiSkewFerrersDiagram' +  :: (Char,Char)       +  -> PartitionConvention -- Orientation+  -> SkewPartition +  -> ASCII+asciiSkewFerrersDiagram' (outer,inner) orient (SkewPartition abs) = asciiFromLines stuff where+  stuff = case orient of+    EnglishNotation    -> ls+    EnglishNotationCCW -> reverse (transpose ls)+    FrenchNotation     -> reverse ls+  ls = [ replicate a inner ++ replicate b outer | (a,b) <- abs ]++instance DrawASCII SkewPartition where+  ascii = asciiSkewFerrersDiagram     ++--------------------------------------------------------------------------------+
+ Math/Combinat/Partitions/Vector.hs view
@@ -0,0 +1,82 @@++-- | Vector partitions. See:+--+--  * Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 3B.+--++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Partitions.Vector where++--------------------------------------------------------------------------------++import Data.Array.Unboxed+import Data.List++--------------------------------------------------------------------------------++-- | Integer vectors. The indexing starts from 1.+type IntVector = UArray Int Int++-- | Vector partitions. Basically a synonym for 'fasc3B_algorithm_M'.+vectorPartitions :: IntVector -> [[IntVector]]+vectorPartitions = fasc3B_algorithm_M . elems++_vectorPartitions :: [Int] -> [[[Int]]]+_vectorPartitions = map (map elems) . fasc3B_algorithm_M++-- | Generates all vector partitions +--   (\"algorithm M\" in Knuth). +--   The order is decreasing lexicographic.  +fasc3B_algorithm_M :: [Int] -> [[IntVector]] +{- note to self: Knuth's descriptions of algorithms are still totally unreadable -}+fasc3B_algorithm_M xs = worker [start] where++  -- n = sum xs+  m = length xs++  start = [ (j,x,x) | (j,x) <- zip [1..] xs ]  +  +  worker stack@(last:_) = +    case decrease stack' of+      Nothing -> [visited]+      Just stack'' -> visited : worker stack''+    where+      stack'  = subtract_rec stack+      visited = map to_vector stack'+      +  decrease (last:rest) = +    case span (\(_,_,v) -> v==0) (reverse last) of+      ( _ , [(_,_,1)] ) -> case rest of+        [] -> Nothing+        _  -> decrease rest+      ( second , (c,u,v):first ) -> Just (modified:rest) where +        modified =   +          reverse first ++ +          (c,u,v-1) :  +          [ (c,u,u) | (c,u,_) <- reverse second ] +      _ -> error "fasc3B_algorithm_M: should not happen"+        +  to_vector cuvs = +    accumArray (flip const) 0 (1,m)+      [ (c,v) | (c,_,v) <- cuvs ] ++  subtract_rec all@(last:_) = +    case subtract last of +      []  -> all+      new -> subtract_rec (new:all) ++  subtract [] = []+  subtract full@((c,u,v):rest) = +    if w >= v +      then (c,w,v) : subtract   rest+      else           subtract_b full+    where w = u - v+    +  subtract_b [] = []+  subtract_b ((c,u,v):rest) = +    if w /= 0 +      then (c,w,w) : subtract_b rest+      else           subtract_b rest+    where w = u - v++--------------------------------------------------------------------------------
+ Math/Combinat/Permutations.hs view
@@ -0,0 +1,862 @@++-- | Permutations. +--+-- See eg.:+-- Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 2B.+--+-- WARNING: As of version 0.2.8.0, I changed the convention of how permutations+-- are represented internally. Also now they act on the /right/ by default!+--++{-# LANGUAGE CPP, BangPatterns, ScopedTypeVariables, GeneralizedNewtypeDeriving, FlexibleContexts #-}+module Math.Combinat.Permutations +  ( -- * The Permutation type+    Permutation (..)+  , fromPermutation+  , permutationArray+  , permutationUArray+  , uarrayToPermutationUnsafe+  , isPermutation+  , maybePermutation+  , toPermutation+  , toPermutationUnsafe+  , permutationSize+    -- * Disjoint cycles+  , DisjointCycles (..)+  , fromDisjointCycles+  , disjointCyclesUnsafe+  , permutationToDisjointCycles+  , disjointCyclesToPermutation+  , numberOfCycles+    -- * Queries+  , isIdentityPermutation+  , isReversePermutation+  , isEvenPermutation+  , isOddPermutation+  , signOfPermutation  +  , signValueOfPermutation  +  , module Math.Combinat.Sign   --  , Sign(..)+  , isCyclicPermutation+    -- * Some concrete permutations+  , transposition+  , transpositions+  , adjacentTransposition+  , adjacentTranspositions+  , cycleLeft+  , cycleRight+  , reversePermutation+    -- * Inversions+  , inversions+  , numberOfInversions+  , numberOfInversionsNaive+  , numberOfInversionsMerge+  , bubbleSort2+  , bubbleSort+    -- * Permutation groups+  , identity+  , inverse+  , multiply+  , multiplyMany +  , multiplyMany'+    -- * Action of the permutation group+  , permute +  , permuteList+  , permuteLeft , permuteRight+  , permuteLeftList , permuteRightList+    -- * ASCII drawing+  , asciiPermutation+  , asciiDisjointCycles+  , twoLineNotation +  , inverseTwoLineNotation+  , genericTwoLineNotation+    -- * List of permutations+  , permutations+  , _permutations+  , permutationsNaive+  , _permutationsNaive+  , countPermutations+    -- * Random permutations+  , randomPermutation+  , _randomPermutation+  , randomCyclicPermutation+  , _randomCyclicPermutation+  , randomPermutationDurstenfeld+  , randomCyclicPermutationSattolo+    -- * Multisets+  , permuteMultiset+  , countPermuteMultiset+  , fasc2B_algorithm_L+  ) +  where++--------------------------------------------------------------------------------++import Control.Monad+import Control.Monad.ST++import Data.List hiding ( permutations )+import Data.Ord ( comparing )++import Data.Array (Array)+import Data.Array.ST+import Data.Array.Unboxed+import Data.Array.IArray+import Data.Array.MArray+import Data.Array.Unsafe++import Math.Combinat.ASCII+import Math.Combinat.Classes+import Math.Combinat.Helper+import Math.Combinat.Sign+import Math.Combinat.Numbers ( factorial , binomial )++import System.Random++--------------------------------------------------------------------------------+-- * Types++-- | A permutation. Internally it is an (unboxed) array of the integers @[1..n]@, with +-- indexing range also being @(1,n)@. +--+-- If this array of integers is @[p1,p2,...,pn]@, then in two-line +-- notations, that represents the permutation+--+-- > ( 1  2  3  ... n  )+-- > ( p1 p2 p3 ... pn )+--+-- That is, it is the permutation @sigma@ whose (right) action on the set @[1..n]@ is+--+-- > sigma(1) = p1+-- > sigma(2) = p2 +-- > ...+--+-- (NOTE: this changed at version 0.2.8.0!)+--+newtype Permutation = Permutation (UArray Int Int) deriving (Eq,Ord) -- ,Show,Read)++instance Show Permutation where+  showsPrec d (Permutation arr) +    = showParen (d > 10)  +    $ showString "toPermutation " . showsPrec 11 (elems arr)       -- app_prec = 10++instance Read Permutation where+  readsPrec d r = readParen (d > 10) fun r where+    fun r = [ (toPermutation p,t) +            | ("toPermutation",s) <- lex r+            , (p,t) <- readsPrec 11 s                              -- app_prec = 10+            ] ++instance DrawASCII Permutation where+  ascii = asciiPermutation++-- | Disjoint cycle notation for permutations. Internally it is @[[Int]]@.+--+-- The cycles are to be understood as follows: a cycle @[c1,c2,...,ck]@ means+-- the permutation+--+-- > ( c1 c2 c3 ... ck )+-- > ( c2 c3 c4 ... c1 )+--+newtype DisjointCycles = DisjointCycles [[Int]] deriving (Eq,Ord,Show,Read)++fromPermutation :: Permutation -> [Int]+fromPermutation (Permutation ar) = elems ar++permutationUArray :: Permutation -> UArray Int Int+permutationUArray (Permutation ar) = ar++-- | Note: this is slower than 'permutationUArray'+permutationArray :: Permutation -> Array Int Int+permutationArray (Permutation ar) = listArray (1,n) (elems ar) where+  (1,n) = bounds ar++-- | Assumes that the input is a permutation of the numbers @[1..n]@.+toPermutationUnsafe :: [Int] -> Permutation+toPermutationUnsafe xs = Permutation perm where+  n    = length xs+  perm = listArray (1,n) xs++-- | Note: Indexing starts from 1.+uarrayToPermutationUnsafe :: UArray Int Int -> Permutation+uarrayToPermutationUnsafe = Permutation++-- | Checks whether the input is a permutation of the numbers @[1..n]@.+isPermutation :: [Int] -> Bool+isPermutation xs = (ar!0 == 0) && and [ ar!j == 1 | j<-[1..n] ] where+  n = length xs+  -- the zero index is an unidiomatic hack+  ar = (accumArray (+) 0 (0,n) $ map f xs) :: UArray Int Int+  f :: Int -> (Int,Int)+  f !j = if j<1 || j>n then (0,1) else (j,1)++-- | Checks whether the input is a permutation of the numbers @[1..n]@.+maybePermutation :: [Int] -> Maybe Permutation+maybePermutation input = runST action where+  n = length input+  action :: forall s. ST s (Maybe Permutation)+  action = do+    ar <- newArray (1,n) 0 :: ST s (STUArray s Int Int)+    let go []     = return $ Just (Permutation $ listArray (1,n) input)+        go (j:js) = if j<1 || j>n +          then return Nothing+          else do+            z <- readArray ar j+            writeArray ar j (z+1)+            if z==0 then go js+                    else return Nothing               +    go input+    +-- | Checks the input.+toPermutation :: [Int] -> Permutation+toPermutation xs = case maybePermutation xs of+  Just p  -> p+  Nothing -> error "toPermutation: not a permutation"++-- | Returns @n@, where the input is a permutation of the numbers @[1..n]@+permutationSize :: Permutation -> Int+permutationSize (Permutation ar) = snd $ bounds ar++instance HasWidth Permutation where+  width = permutationSize++-- | Checks whether the permutation is the identity permutation+isIdentityPermutation :: Permutation -> Bool+isIdentityPermutation (Permutation ar) = (elems ar == [1..n]) where+  (1,n) = bounds ar++--------------------------------------------------------------------------------+-- * ASCII++-- | Synonym for 'twoLineNotation'+asciiPermutation :: Permutation -> ASCII+asciiPermutation = twoLineNotation ++asciiDisjointCycles :: DisjointCycles -> ASCII+asciiDisjointCycles (DisjointCycles cycles) = final where+  final = hCatWith VTop (HSepSpaces 1) boxes +  boxes = [ genericTwoLineNotation (f cyc) | cyc <- cycles ]+  f cyc = pairs (cyc ++ [head cyc])++-- | The standard two-line notation, moving the element indexed by the top row into+-- the place indexed by the corresponding element in the bottom row.+twoLineNotation :: Permutation -> ASCII+twoLineNotation (Permutation arr) = genericTwoLineNotation $ zip [1..] (elems arr)++-- | The inverse two-line notation, where the it\'s the bottom line +-- which is in standard order. The columns of this are a permutation+-- of the columns 'twoLineNotation'.+--+-- Remark: the top row of @inverseTwoLineNotation perm@ is the same +-- as the bottom row of @twoLineNotation (inverse perm)@.+--+inverseTwoLineNotation :: Permutation -> ASCII+inverseTwoLineNotation (Permutation arr) =+  genericTwoLineNotation $ sortBy (comparing snd) $ zip [1..] (elems arr) ++-- | Two-line notation for any set of numbers+genericTwoLineNotation :: [(Int,Int)] -> ASCII+genericTwoLineNotation xys = asciiFromLines [ topLine, botLine ] where+  topLine = "( " ++ intercalate " " us ++ " )"+  botLine = "( " ++ intercalate " " vs ++ " )"+  pairs   = [ (show x, show y) | (x,y) <- xys ]+  (us,vs) = unzip (map f pairs) +  f (s,t) = (s',t') where+    a = length s +    b = length t+    c = max a b+    s' = replicate (c-a) ' ' ++ s+    t' = replicate (c-b) ' ' ++ t++--------------------------------------------------------------------------------+-- * Disjoint cycles++fromDisjointCycles :: DisjointCycles -> [[Int]]+fromDisjointCycles (DisjointCycles cycles) = cycles++disjointCyclesUnsafe :: [[Int]] -> DisjointCycles +disjointCyclesUnsafe = DisjointCycles++instance DrawASCII DisjointCycles where+  ascii = asciiDisjointCycles++instance HasNumberOfCycles DisjointCycles where+  numberOfCycles (DisjointCycles cycles) = length cycles++instance HasNumberOfCycles Permutation where+  numberOfCycles = numberOfCycles . permutationToDisjointCycles+  +disjointCyclesToPermutation :: Int -> DisjointCycles -> Permutation+disjointCyclesToPermutation n (DisjointCycles cycles) = Permutation perm where++  pairs :: [Int] -> [(Int,Int)]+  pairs xs@(x:_) = worker (xs++[x]) where+    worker (x:xs@(y:_)) = (x,y):worker xs+    worker _ = [] +  pairs [] = error "disjointCyclesToPermutation: empty cycle"++  perm = runSTUArray $ do+    ar <- newArray_ (1,n) :: ST s (STUArray s Int Int)+    forM_ [1..n] $ \i -> writeArray ar i i +    forM_ cycles $ \cyc -> forM_ (pairs cyc) $ \(i,j) -> writeArray ar i j+    return ar -- freeze ar+  +-- | Convert to disjoint cycle notation.+--+-- This is compatible with Maple's @convert(perm,\'disjcyc\')@ +-- and also with Mathematica's @PermutationCycles[perm]@+--+-- Note however, that for example Mathematica uses the +-- /top row/ to represent a permutation, while we use the+-- /bottom row/ - thus even though this function looks+-- identical, the /meaning/ of both the input and output+-- is different!+-- +permutationToDisjointCycles :: Permutation -> DisjointCycles+permutationToDisjointCycles (Permutation perm) = res where++  (1,n) = bounds perm++  -- we don't want trivial cycles+  f :: [Int] -> Bool+  f [_] = False+  f _ = True+  +  res = runST $ do+    tag <- newArray (1,n) False +    cycles <- unfoldM (step tag) 1 +    return (DisjointCycles $ filter f cycles)+    +  step :: STUArray s Int Bool -> Int -> ST s ([Int],Maybe Int)+  step tag k = do+    cyc <- worker tag k k [k] +    m <- next tag (k+1)+    return (reverse cyc, m) +    +  next :: STUArray s Int Bool -> Int -> ST s (Maybe Int)+  next tag k = if k > n+    then return Nothing+    else readArray tag k >>= \b -> if b +      then next tag (k+1)  +      else return (Just k)+       +  worker :: STUArray s Int Bool -> Int -> Int -> [Int] -> ST s [Int]+  worker tag k l cyc = do+    writeArray tag l True+    let m = perm ! l+    if m == k +      then return cyc+      else worker tag k m (m:cyc)      ++isEvenPermutation :: Permutation -> Bool+isEvenPermutation (Permutation perm) = res where++  (1,n) = bounds perm+  res = runST $ do+    tag <- newArray (1,n) False +    cycles <- unfoldM (step tag) 1 +    return $ even (sum cycles)+    +  step :: STUArray s Int Bool -> Int -> ST s (Int,Maybe Int)+  step tag k = do+    cyclen <- worker tag k k 0+    m <- next tag (k+1)+    return (cyclen,m)+    +  next :: STUArray s Int Bool -> Int -> ST s (Maybe Int)+  next tag k = if k > n+    then return Nothing+    else readArray tag k >>= \b -> if b +      then next tag (k+1)  +      else return (Just k)+      +  worker :: STUArray s Int Bool -> Int -> Int -> Int -> ST s Int+  worker tag k l cyclen = do+    writeArray tag l True+    let m = perm ! l+    if m == k +      then return cyclen+      else worker tag k m (1+cyclen)      ++isOddPermutation :: Permutation -> Bool+isOddPermutation = not . isEvenPermutation++signOfPermutation :: Permutation -> Sign+signOfPermutation perm = case isEvenPermutation perm of+  True  -> Plus+  False -> Minus++-- | Plus 1 or minus 1.+{-# SPECIALIZE signValueOfPermutation :: Permutation -> Int     #-}+{-# SPECIALIZE signValueOfPermutation :: Permutation -> Integer #-}+signValueOfPermutation :: Num a => Permutation -> a+signValueOfPermutation = signValue . signOfPermutation+  +isCyclicPermutation :: Permutation -> Bool+isCyclicPermutation perm = +  case cycles of+    []    -> True+    [cyc] -> (length cyc == n)+    _     -> False+  where +    n = permutationSize perm+    DisjointCycles cycles = permutationToDisjointCycles perm++--------------------------------------------------------------------------------+-- * Inversions++-- | An /inversion/ of a permutation @sigma@ is a pair @(i,j)@ such that+-- @i<j@ and @sigma(i) > sigma(j)@.+--+-- This functions returns the inversion of a permutation.+--+inversions :: Permutation -> [(Int,Int)]+inversions (Permutation arr) = list where+  (_,n) = bounds arr+  list = [ (i,j) | i<-[1..n-1], j<-[i+1..n], arr!i > arr!j ]++-- | Returns the number of inversions:+--+-- > numberOfInversions perm = length (inversions perm)+--+-- Synonym for 'numberOfInversionsMerge'+--+numberOfInversions :: Permutation -> Int+numberOfInversions = numberOfInversionsMerge++-- | Returns the number of inversions, using the merge-sort algorithm.+-- This should be @O(n*log(n))@+--+numberOfInversionsMerge :: Permutation -> Int+numberOfInversionsMerge (Permutation arr) = fst (sortCnt n $ elems arr) where+  (_,n) = bounds arr+                                        +  -- | First argument is length of the list.+  -- Returns also the inversion count.+  sortCnt :: Int -> [Int] -> (Int,[Int])+  sortCnt 0 _     = (0,[] )+  sortCnt 1 [x]   = (0,[x])+  sortCnt 2 [x,y] = if x>y then (1,[y,x]) else (0,[x,y])+  sortCnt n xs    = mergeCnt (sortCnt k us) (sortCnt l vs) where+    k = div n 2+    l = n - k +    (us,vs) = splitAt k xs++  mergeCnt :: (Int,[Int]) -> (Int,[Int]) -> (Int,[Int])+  mergeCnt (!c,us) (!d,vs) = (c+d+e,ws) where++    (e,ws) = go 0 us vs ++    go !k xs [] = ( k*length xs , xs )+    go _  [] ys = ( 0 , ys)+    go !k xxs@(x:xs) yys@(y:ys) = if x < y+      then let (a,zs) = go  k     xs yys in (a+k, x:zs)+      else let (a,zs) = go (k+1) xxs  ys in (a  , y:zs)++-- | Returns the number of inversions, using the definition, thus it's @O(n^2)@.+--+numberOfInversionsNaive :: Permutation -> Int+numberOfInversionsNaive (Permutation arr) = length list where+  (_,n) = bounds arr+  list = [ (0::Int) | i<-[1..n-1], j<-[i+1..n], arr!i > arr!j ]++-- | Bubble sorts breaks a permutation into the product of adjacent transpositions:+--+-- > multiplyMany' n (map (transposition n) $ bubbleSort2 perm) == perm+--+-- Note that while this is not unique, the number of transpositions +-- equals the number of inversions.+--+bubbleSort2 :: Permutation -> [(Int,Int)]+bubbleSort2 = map f . bubbleSort where f i = (i,i+1)++-- | Another version of bubble sort. An entry @i@ in the return sequence means+-- the transposition @(i,i+1)@:+--+-- > multiplyMany' n (map (adjacentTransposition n) $ bubbleSort perm) == perm+--+bubbleSort :: Permutation -> [Int]+bubbleSort perm@(Permutation tgt) = runST action where+  (_,n)           = bounds tgt++  action :: forall s. ST s [Int]+  action = do+    fwd <- newArray_ (1,n) :: ST s (STUArray s Int Int)+    inv <- newArray_ (1,n) :: ST s (STUArray s Int Int)+    forM_ [1..n] $ \i -> writeArray fwd i i+    forM_ [1..n] $ \i -> writeArray inv i i++    list <- forM [1..n] $ \x -> do++      let k = tgt ! x        -- we take the number which will be at the @x@-th position at the end+      i <- readArray inv k   -- number @k@ is at the moment at position @i@+      let j = x              -- but the final place is at @x@      ++      let swaps = move i j+      forM_ swaps $ \y -> do++        a <- readArray fwd  y+        b <- readArray fwd (y+1)+        writeArray fwd (y+1) a+        writeArray fwd  y    b++        u <- readArray inv a+        v <- readArray inv b+        writeArray inv b u+        writeArray inv a v++      return swaps+  +    return (concat list)++  move :: Int -> Int -> [Int]+  move !i !j+    | j == i  = []+    | j >  i  = [i..j-1]+    | j <  i  = [i-1,i-2..j]++--------------------------------------------------------------------------------+-- * Some concrete permutations++-- | The permutation @[n,n-1,n-2,...,2,1]@. Note that it is the inverse of itself.+reversePermutation :: Int -> Permutation+reversePermutation n = Permutation $ listArray (1,n) [n,n-1..1]++-- | Checks whether the permutation is the reverse permutation @[n,n-1,n-2,...,2,1].+isReversePermutation :: Permutation -> Bool+isReversePermutation (Permutation arr) = elems arr == [n,n-1..1] where (1,n) = bounds arr++-- | A transposition (swapping two elements). +--+-- @transposition n (i,j)@ is the permutation of size @n@ which swaps @i@\'th and @j@'th elements.+--+transposition :: Int -> (Int,Int) -> Permutation+transposition n (i,j) = +  if i>=1 && j>=1 && i<=n && j<=n +    then Permutation $ listArray (1,n) [ f k | k<-[1..n] ]+    else error "transposition: index out of range"+  where+    f k | k == i    = j+        | k == j    = i+        | otherwise = k++-- | Product of transpositions.+--+-- > transpositions n list == multiplyMany [ transposition n pair | pair <- list ]+--+transpositions :: Int -> [(Int,Int)] -> Permutation+transpositions n list = Permutation (runSTUArray action) where++  action :: ST s (STUArray s Int Int)+  action = do+    arr <- newArray_ (1,n) +    forM_ [1..n] $ \i -> writeArray arr i i    +    let doSwap (i,j) = do+          u <- readArray arr i+          v <- readArray arr j+          writeArray arr i v+          writeArray arr j u          +    mapM_ doSwap list+    return arr++-- | @adjacentTransposition n k@ swaps the elements @k@ and @(k+1)@.+adjacentTransposition :: Int -> Int -> Permutation+adjacentTransposition n k +  | k>0 && k<n  = transposition n (k,k+1)+  | otherwise   = error "adjacentTransposition: index out of range"++-- | Product of adjacent transpositions.+--+-- > adjacentTranspositions n list == multiplyMany [ adjacentTransposition n idx | idx <- list ]+--+adjacentTranspositions :: Int -> [Int] -> Permutation+adjacentTranspositions n list = Permutation (runSTUArray action) where++  action :: ST s (STUArray s Int Int)+  action = do+    arr <- newArray_ (1,n) +    forM_ [1..n] $ \i -> writeArray arr i i    +    let doSwap i+          | i<0 || i>=n  = error "adjacentTranspositions: index out of range"+          | otherwise    = do+              u <- readArray arr  i+              v <- readArray arr (i+1)+              writeArray arr  i    v+              writeArray arr (i+1) u          +    mapM_ doSwap list+    return arr++-- | The permutation which cycles a list left by one step:+-- +-- > permuteList (cycleLeft 5) "abcde" == "bcdea"+--+-- Or in two-line notation:+--+-- > ( 1 2 3 4 5 )+-- > ( 2 3 4 5 1 )+-- +cycleLeft :: Int -> Permutation+cycleLeft n = Permutation $ listArray (1,n) $ [2..n] ++ [1]++-- | The permutation which cycles a list right by one step:+-- +-- > permuteList (cycleRight 5) "abcde" == "eabcd"+--+-- Or in two-line notation:+--+-- > ( 1 2 3 4 5 )+-- > ( 5 1 2 3 4 )+-- +cycleRight :: Int -> Permutation+cycleRight n = Permutation $ listArray (1,n) $ n : [1..n-1]+   +--------------------------------------------------------------------------------+-- * Permutation groups++-- | Multiplies two permutations together: @p `multiply` q@+-- means the permutation when we first apply @p@, and then @q@+-- (that is, the natural action is the /right/ action)+--+-- See also 'permute' for our conventions.  +--+multiply :: Permutation -> Permutation -> Permutation+multiply pi1@(Permutation perm1) pi2@(Permutation perm2) = +  if (n==m) +    then Permutation result+    else error "multiply: permutations of different sets"  +  where+    (_,n) = bounds perm1+    (_,m) = bounds perm2    +    result = permute pi2 perm1+  +infixr 7 `multiply`  ++-- | The inverse permutation.+inverse :: Permutation -> Permutation    +inverse (Permutation perm1) = Permutation result+  where+    result = array (1,n) $ map swap $ assocs perm1+    (_,n) = bounds perm1+    +-- | The identity (or trivial) permutation.+identity :: Int -> Permutation +identity n = Permutation $ listArray (1,n) [1..n]++-- | Multiply together a /non-empty/ list of permutations (the reason for requiring the list to+-- be non-empty is that we don\'t know the size of the result). See also 'multiplyMany''.+multiplyMany :: [Permutation] -> Permutation +multiplyMany [] = error "multiplyMany: empty list, we don't know size of the result"+multiplyMany ps = foldl1' multiply ps    ++-- | Multiply together a (possibly empty) list of permutations, all of which has size @n@+multiplyMany' :: Int -> [Permutation] -> Permutation +multiplyMany' n []       = identity n+multiplyMany' n ps@(p:_) = if n == permutationSize p +  then foldl1' multiply ps    +  else error "multiplyMany': incompatible permutation size(s)"++--------------------------------------------------------------------------------+-- * Action of the permutation group++-- | /Right/ action of a permutation on a set. If our permutation is +-- encoded with the sequence @[p1,p2,...,pn]@, then in the+-- two-line notation we have+--+-- > ( 1  2  3  ... n  )+-- > ( p1 p2 p3 ... pn )+--+-- We adopt the convention that permutations act /on the right/ +-- (as in Knuth):+--+-- > permute pi2 (permute pi1 set) == permute (pi1 `multiply` pi2) set+--+-- Synonym to 'permuteRight'+--+{-# SPECIALIZE permute :: Permutation -> Array  Int b   -> Array  Int b   #-}+{-# SPECIALIZE permute :: Permutation -> UArray Int Int -> UArray Int Int #-}+permute :: IArray arr b => Permutation -> arr Int b -> arr Int b    +permute = permuteRight++-- | Right action on lists. Synonym to 'permuteListRight'+--+permuteList :: Permutation -> [a] -> [a]+permuteList = permuteRightList+    +-- | The right (standard) action of permutations on sets. +-- +-- > permuteRight pi2 (permuteRight pi1 set) == permuteRight (pi1 `multiply` pi2) set+--   +-- The second argument should be an array with bounds @(1,n)@.+-- The function checks the array bounds.+--+{-# SPECIALIZE permuteRight :: Permutation -> Array  Int b   -> Array  Int b   #-}+{-# SPECIALIZE permuteRight :: Permutation -> UArray Int Int -> UArray Int Int #-}+permuteRight :: IArray arr b => Permutation -> arr Int b -> arr Int b    +permuteRight pi@(Permutation perm) ar = +  if (a==1) && (b==n) +    then listArray (1,n) [ ar!(perm!i) | i <- [1..n] ] +    else error "permuteRight: array bounds do not match"+  where+    (_,n) = bounds perm  +    (a,b) = bounds ar   ++-- | The right (standard) action on a list. The list should be of length @n@.+--+-- > fromPermutation perm == permuteRightList perm [1..n]+-- +permuteRightList :: forall a . Permutation -> [a] -> [a]    +permuteRightList perm xs = elems $ permuteRight perm $ arr where+  arr = listArray (1,n) xs :: Array Int a+  n   = permutationSize perm++-- | The left (opposite) action of the permutation group.+--+-- > permuteLeft pi2 (permuteLeft pi1 set) == permuteLeft (pi2 `multiply` pi1) set+--+-- It is related to 'permuteLeft' via:+--+-- > permuteLeft  pi arr == permuteRight (inverse pi) arr+-- > permuteRight pi arr == permuteLeft  (inverse pi) arr+--+{-# SPECIALIZE permuteLeft :: Permutation -> Array  Int b   -> Array  Int b   #-}+{-# SPECIALIZE permuteLeft :: Permutation -> UArray Int Int -> UArray Int Int #-}+permuteLeft :: IArray arr b => Permutation -> arr Int b -> arr Int b    +permuteLeft pi@(Permutation perm) ar =    +  -- permuteRight (inverse pi) ar+  if (a==1) && (b==n) +    then array (1,n) [ ( perm!i , ar!i ) | i <- [1..n] ] +    else error "permuteLeft: array bounds do not match"+  where+    (_,n) = bounds perm  +    (a,b) = bounds ar   ++-- | The left (opposite) action on a list. The list should be of length @n@.+--+-- > permuteLeftList perm set == permuteList (inverse perm) set+-- > fromPermutation (inverse perm) == permuteLeftList perm [1..n]+--+permuteLeftList :: forall a. Permutation -> [a] -> [a]    +permuteLeftList perm xs = elems $ permuteLeft perm $ arr where+  arr = listArray (1,n) xs :: Array Int a+  n   = permutationSize perm++--------------------------------------------------------------------------------+-- * Permutations of distinct elements++-- | A synonym for 'permutationsNaive'+permutations :: Int -> [Permutation]+permutations = permutationsNaive++_permutations :: Int -> [[Int]]+_permutations = _permutationsNaive++-- | All permutations of @[1..n]@ in lexicographic order, naive algorithm.+permutationsNaive :: Int -> [Permutation]+permutationsNaive n = map toPermutationUnsafe $ _permutations n ++_permutationsNaive :: Int -> [[Int]]  +_permutationsNaive 0 = [[]]+_permutationsNaive 1 = [[1]]+_permutationsNaive n = helper [1..n] where+  helper [] = [[]]+  helper xs = [ i : ys | i <- xs , ys <- helper (xs `minus` i) ]+  minus [] _ = []+  minus (x:xs) i = if x < i then x : minus xs i else xs+          +-- | # = n!+countPermutations :: Int -> Integer+countPermutations = factorial++--------------------------------------------------------------------------------+-- * Random permutations++-- | A synonym for 'randomPermutationDurstenfeld'.+randomPermutation :: RandomGen g => Int -> g -> (Permutation,g)+randomPermutation = randomPermutationDurstenfeld++_randomPermutation :: RandomGen g => Int -> g -> ([Int],g)+_randomPermutation n rndgen = (fromPermutation perm, rndgen') where+  (perm, rndgen') = randomPermutationDurstenfeld n rndgen ++-- | A synonym for 'randomCyclicPermutationSattolo'.+randomCyclicPermutation :: RandomGen g => Int -> g -> (Permutation,g)+randomCyclicPermutation = randomCyclicPermutationSattolo++_randomCyclicPermutation :: RandomGen g => Int -> g -> ([Int],g)+_randomCyclicPermutation n rndgen = (fromPermutation perm, rndgen') where+  (perm, rndgen') = randomCyclicPermutationSattolo n rndgen ++-- | Generates a uniformly random permutation of @[1..n]@.+-- Durstenfeld's algorithm (see <http://en.wikipedia.org/wiki/Knuth_shuffle>).+randomPermutationDurstenfeld :: RandomGen g => Int -> g -> (Permutation,g)+randomPermutationDurstenfeld = randomPermutationDurstenfeldSattolo False++-- | Generates a uniformly random /cyclic/ permutation of @[1..n]@.+-- Sattolo's algorithm (see <http://en.wikipedia.org/wiki/Knuth_shuffle>).+randomCyclicPermutationSattolo :: RandomGen g => Int -> g -> (Permutation,g)+randomCyclicPermutationSattolo = randomPermutationDurstenfeldSattolo True++randomPermutationDurstenfeldSattolo :: RandomGen g => Bool -> Int -> g -> (Permutation,g)+randomPermutationDurstenfeldSattolo isSattolo n rnd = res where+  res = runST $ do+    ar <- newArray_ (1,n) +    forM_ [1..n] $ \i -> writeArray ar i i+    rnd' <- worker n (if isSattolo then n-1 else n) rnd ar +    perm <- Data.Array.Unsafe.unsafeFreeze ar+    return (Permutation perm, rnd')+  worker :: RandomGen g => Int -> Int -> g -> STUArray s Int Int -> ST s g +  worker n m rnd ar = +    if n==1 +      then return rnd +      else do+        let (k,rnd') = randomR (1,m) rnd+        when (k /= n) $ do+          y <- readArray ar k +          z <- readArray ar n+          writeArray ar n y+          writeArray ar k z+        worker (n-1) (m-1) rnd' ar ++--------------------------------------------------------------------------------+-- * Permutations of a multiset++-- | Generates all permutations of a multiset.  +--   The order is lexicographic. A synonym for 'fasc2B_algorithm_L'+permuteMultiset :: (Eq a, Ord a) => [a] -> [[a]] +permuteMultiset = fasc2B_algorithm_L++-- | # = \\frac { (\sum_i n_i) ! } { \\prod_i (n_i !) }    +countPermuteMultiset :: (Eq a, Ord a) => [a] -> Integer+countPermuteMultiset xs = factorial n `div` product [ factorial (length z) | z <- group ys ] +  where+    ys = sort xs+    n = length xs+  +-- | Generates all permutations of a multiset +--   (based on \"algorithm L\" in Knuth; somewhat less efficient). +--   The order is lexicographic.  +fasc2B_algorithm_L :: (Eq a, Ord a) => [a] -> [[a]] +fasc2B_algorithm_L xs = unfold1 next (sort xs) where++  -- next :: [a] -> Maybe [a]+  next xs = case findj (reverse xs,[]) of +    Nothing -> Nothing+    Just ( (l:ls) , rs) -> Just $ inc l ls (reverse rs,[]) +    Just ( [] , _ ) -> error "permute: should not happen"++  -- we use simple list zippers: (left,right)+  -- findj :: ([a],[a]) -> Maybe ([a],[a])   +  findj ( xxs@(x:xs) , yys@(y:_) ) = if x >= y +    then findj ( xs , x : yys )+    else Just ( xxs , yys )+  findj ( x:xs , [] ) = findj ( xs , [x] )  +  findj ( [] , _ ) = Nothing+  +  -- inc :: a -> [a] -> ([a],[a]) -> [a]+  inc !u us ( (x:xs) , yys ) = if u >= x+    then inc u us ( xs , x : yys ) +    else reverse (x:us)  ++ reverse (u:yys) ++ xs+  inc _ _ ( [] , _ ) = error "permute: should not happen"+      +--------------------------------------------------------------------------------++
+ Math/Combinat/Sets.hs view
@@ -0,0 +1,212 @@++-- | Subsets. ++{-# LANGUAGE BangPatterns, Rank2Types #-}+module Math.Combinat.Sets +  ( +    -- * Choices+    choose_ , choose , choose' , choose'' , chooseTagged+    -- * Compositions+  , combine , compose+    -- * Tensor products+  , tuplesFromList+  , listTensor+    -- * Sublists+  , kSublists+  , sublists+  , countKSublists+  , countSublists+    -- * Random choice+  , randomChoice+  ) +  where++--------------------------------------------------------------------------------++import Data.List ( sort , mapAccumL )+import System.Random++import Control.Monad+import Control.Monad.ST+import Data.Array.ST+import Data.Array.MArray++-- import Data.Map (Map)+-- import qualified Data.Map as Map++import Math.Combinat.Numbers ( binomial )+import Math.Combinat.Helper  ( swap )++--------------------------------------------------------------------------------+-- * choices+++-- | @choose_ k n@ returns all possible ways of choosing @k@ disjoint elements from @[1..n]@+--+-- > choose_ k n == choose k [1..n]+--+choose_ :: Int -> Int -> [[Int]]+choose_ k n  = if n<0 || k<0+  then error "choose_: n and k should nonnegative"+  else if k>n || k<0 +    then []+    else choose k [1..n]++-- | All possible ways to choose @k@ elements from a list, without+-- repetitions. \"Antisymmetric power\" for lists. Synonym for 'kSublists'.+choose :: Int -> [a] -> [[a]]+choose 0 _  = [[]]+choose k [] = []+choose k (x:xs) = map (x:) (choose (k-1) xs) ++ choose k xs  ++-- | A version of 'choose' which also returns the complementer sets.+--+-- > choose k = map fst . choose' k+--+choose' :: Int -> [a] -> [([a],[a])]+choose' 0 xs = [([],xs)]+choose' k [] = []+choose' k (x:xs) = map f (choose' (k-1) xs) ++ map g (choose' k xs) where+  f (as,bs) = (x:as ,   bs)+  g (as,bs) = (  as , x:bs)++-- | Another variation of 'choose''. This satisfies+--+-- > choose'' k == map (\(xs,ys) -> (map fst xs, map snd ys)) . choose' k+--+choose'' :: Int -> [(a,b)] -> [([a],[b])]+choose'' 0 xys = [([] , map snd xys)]+choose'' k []  = []+choose'' k ((x,y):xs) = map f (choose'' (k-1) xs) ++ map g (choose'' k xs) where+  f (as,bs) = (x:as ,   bs)+  g (as,bs) = (  as , y:bs)++-- | Another variation on 'choose' which tags the elements based on whether they are part of+-- the selected subset ('Right') or not ('Left'):+--+-- > choose k = map rights . chooseTagged k+--+chooseTagged :: Int -> [a] -> [[Either a a]]+chooseTagged 0 xs = [map Left xs]+chooseTagged k [] = []+chooseTagged k (x:xs) = map f (chooseTagged (k-1) xs) ++ map g (chooseTagged k xs) where+  f eis = Right x : eis+  g eis = Left  x : eis++-- | All possible ways to choose @k@ elements from a list, /with repetitions/. +-- \"Symmetric power\" for lists. See also "Math.Combinat.Compositions".+-- TODO: better name?+combine :: Int -> [a] -> [[a]]+combine 0 _  = [[]]+combine k [] = []+combine k xxs@(x:xs) = map (x:) (combine (k-1) xxs) ++ combine k xs  ++-- | A synonym for 'combine'.+compose :: Int -> [a] -> [[a]]+compose = combine++--------------------------------------------------------------------------------+-- * tensor products++-- | \"Tensor power\" for lists. Special case of 'listTensor':+--+-- > tuplesFromList k xs == listTensor (replicate k xs)+-- +-- See also "Math.Combinat.Tuples".+-- TODO: better name?+tuplesFromList :: Int -> [a] -> [[a]]+tuplesFromList 0 _  = [[]]+tuplesFromList k xs = [ (y:ys) | ys <- tuplesFromList (k-1) xs , y <- xs ]+--the order seems to be very important, the wrong order causes a memory leak!+--tuplesFromList k xs = [ (y:ys) | y <- xs, ys <- tuplesFromList (k-1) xs ]+ +-- | \"Tensor product\" for lists.+listTensor :: [[a]] -> [[a]]+listTensor [] = [[]]+listTensor (xs:xss) = [ y:ys | ys <- listTensor xss , y <- xs ]+--the order seems to be very important, the wrong order causes a memory leak!+--listTensor (xs:xss) = [ y:ys | y <- xs, ys <- listTensor xss ]++--------------------------------------------------------------------------------+-- * sublists++-- | Sublists of a list having given number of elements. Synonym for 'choose'.+kSublists :: Int -> [a] -> [[a]]+kSublists = choose++-- | @# = \binom { n } { k }@.+countKSublists :: Int -> Int -> Integer+countKSublists k n = binomial n k ++-- | All sublists of a list.+sublists :: [a] -> [[a]]+sublists [] = [[]]+sublists (x:xs) = sublists xs ++ map (x:) (sublists xs)  +--the order seems to be very important, the wrong order causes a memory leak!+--sublists (x:xs) = map (x:) (sublists xs) ++ sublists xs ++-- | @# = 2^n@.+countSublists :: Int -> Integer+countSublists n = 2 ^ n++--------------------------------------------------------------------------------+-- * random choice++-- | @randomChoice k n@ returns a uniformly random choice of @k@ elements from the set @[1..n]@+--+-- Example:+--+-- > do+-- >   cs <- replicateM 10000 (getStdRandom (randomChoice 3 7))+-- >   mapM_ print $ histogram cs+-- +randomChoice :: RandomGen g => Int -> Int -> g -> ([Int],g)+randomChoice k n g0 = +  if k>n || k<0 +    then error "randomChoice: k out of range" +    else (makeChoiceFromIndicesKnuth n as, g1) +  where+    -- choose numbers from [1..n], [1..n-1], [1..n-2] etc+    (g1,as) = mapAccumL (\g m -> swap (randomR (1,m) g)) g0 [n,n-1..n-k+1]   ++--------------------------------------------------------------------------------+   +-- | From a list of $k$ numbers, where the first is in the interval @[1..n]@, +-- the second in @[1..n-1]@, the third in @[1..n-2]@, we create a size @k@ subset of @n@.+--+-- Knuth's method. The first argument is the number @n@.+--+makeChoiceFromIndicesKnuth :: Int -> [Int] -> [Int]+makeChoiceFromIndicesKnuth n xs = +  runST $ do+    arr <- newArray_ (1,n) :: forall s. ST s (STUArray s Int Int)+    forM_ [1..n] $ \(!j) -> writeArray arr j j+    forM_ (zip [n,n-1..] xs) $ \(!j,!i) -> do+      a <- readArray arr j+      b <- readArray arr i+      writeArray arr j b+      writeArray arr i a+    sel <- forM (zip [n,n-1..] xs) $ \(!j,_) -> readArray arr j+    return (sort sel)++-- | From a list of $k$ numbers, where the first is in the interval @[1..n]@, +-- the second in @[1..n-1]@, the third in @[1..n-2]@, we create a size @k@ subset of @n@.+makeChoiceFromIndicesNaive :: [Int] -> [Int]+makeChoiceFromIndicesNaive = sort . go [] where++  go :: [Int] -> [Int] -> [Int]+  go acc (b:bs) = b' : go (insert b' acc) bs where b' = skip b acc+  go _   [] = []++  -- skip over the already occupied positions. Second argument should be a sorted list+  skip :: Int -> [Int] -> Int+  skip x (y:ys) = if x>=y then skip (x+1) ys else x+  skip x [] = x++  -- insert into a sorted list+  insert :: Int -> [Int] -> [Int]+  insert x (y:ys) = if x<=y then x:y:ys else y : insert x ys+  insert x [] = [x]++--------------------------------------------------------------------------------+
+ Math/Combinat/Sign.hs view
@@ -0,0 +1,90 @@++-- | Signs++{-# LANGUAGE BangPatterns #-}+module Math.Combinat.Sign where++--------------------------------------------------------------------------------++import           Data.Monoid+import           System.Random++--------------------------------------------------------------------------------++data Sign+  = Plus                            -- hmm, this way @Plus < Minus@, not sure about that+  | Minus+  deriving (Eq,Ord,Show,Read)++instance Semigroup Sign where+    (<>) = mulSign++instance Monoid Sign where+  mempty  = Plus+  mconcat = productOfSigns++instance Random Sign where+  random        g = let (b,g') = random g in (if b    then Plus else Minus, g')+  randomR (u,v) g = let (y,g') = random g in (if u==v then u    else y    , g')++isPlus, isMinus :: Sign -> Bool+isPlus  s = case s of { Plus  -> True ; _ -> False }+isMinus s = case s of { Minus -> True ; _ -> False }++{-# SPECIALIZE signValue :: Sign -> Int     #-}+{-# SPECIALIZE signValue :: Sign -> Integer #-}++-- | @+1@ or @-1@+signValue :: Num a => Sign -> a+signValue s = case s of+  Plus  ->  1+  Minus -> -1++{-# SPECIALIZE signed :: Sign -> Int     -> Int     #-}+{-# SPECIALIZE signed :: Sign -> Integer -> Integer #-}++-- | Negate the second argument if the first is 'Minus'+signed :: Num a => Sign -> a -> a+signed s y = case s of+  Plus  -> y+  Minus -> negate y++{-# SPECIALIZE paritySign :: Int     -> Sign #-}+{-# SPECIALIZE paritySign :: Integer -> Sign #-}++-- | 'Plus' if even, 'Minus' if odd+paritySign :: Integral a => a -> Sign+paritySign x = if even x then Plus else Minus++{-# SPECIALIZE paritySignValue :: Int     -> Integer #-}+{-# SPECIALIZE paritySignValue :: Integer -> Integer #-}++-- | @(-1)^k@+paritySignValue :: Integral a => a -> Integer+paritySignValue k = if odd k then (-1) else 1++{-# SPECIALIZE negateIfOdd :: Int     -> Int     -> Int     #-}+{-# SPECIALIZE negateIfOdd :: Int     -> Integer -> Integer #-}++-- | Negate the second argument if the first is odd+negateIfOdd :: (Integral a, Num b) => a -> b -> b+negateIfOdd k y = if even k then y else negate y++oppositeSign :: Sign -> Sign+oppositeSign s = case s of+  Plus  -> Minus+  Minus -> Plus++mulSign :: Sign -> Sign -> Sign+mulSign s1 s2 = case s1 of+  Plus  -> s2+  Minus -> oppositeSign s2++productOfSigns :: [Sign] -> Sign+productOfSigns = go Plus where+  go !acc []     = acc+  go !acc (x:xs) = case x of+    Plus  -> go acc xs+    Minus -> go (oppositeSign acc) xs++--------------------------------------------------------------------------------
+ Math/Combinat/Tableaux.hs view
@@ -0,0 +1,241 @@++-- | Young tableaux and similar gadgets. +--+--   See e.g. William Fulton: Young Tableaux, with Applications to +--   Representation theory and Geometry (CUP 1997).+-- +--   The convention is that we use +--   the English notation, and we store the tableaux as lists of the rows.+-- +--   That is, the following standard Young tableau of shape [5,4,1]+-- +-- >  1  3  4  6  7+-- >  2  5  8 10+-- >  9+--+-- <<svg/young_tableau.svg>>+--+--   is encoded conveniently as+-- +-- > [ [ 1 , 3 , 4 , 6 , 7 ]+-- > , [ 2 , 5 , 8 ,10 ]+-- > , [ 9 ]+-- > ]+--++{-# LANGUAGE CPP, BangPatterns, FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses #-}+module Math.Combinat.Tableaux where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Classes+import Math.Combinat.Numbers (factorial,binomial)+import Math.Combinat.Partitions+import Math.Combinat.ASCII+import Math.Combinat.Helper++import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map++--------------------------------------------------------------------------------+-- * Basic stuff++-- | A tableau is simply represented as a list of lists.+type Tableau a = [[a]]++-- | ASCII diagram of a tableau+asciiTableau :: Show a => Tableau a -> ASCII+asciiTableau t = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) +           $ (map . map) asciiShow+           $ t++instance CanBeEmpty (Tableau a) where+  empty   = []+  isEmpty = null++instance Show a => DrawASCII (Tableau a) where +  ascii = asciiTableau++_tableauShape :: Tableau a -> [Int]+_tableauShape t = map length t ++-- | The shape of a tableau+tableauShape :: Tableau a -> Partition+tableauShape t = toPartition (_tableauShape t)++instance HasShape (Tableau a) Partition where+  shape = tableauShape++-- | Number of entries+tableauWeight :: Tableau a -> Int+tableauWeight = sum' . map length++instance HasWeight (Tableau a) where+  weight = tableauWeight++-- | The dual of the tableau is the mirror image to the main diagonal.+dualTableau :: Tableau a -> Tableau a+dualTableau = transpose++instance HasDuality (Tableau a) where+  dual = dualTableau++-- | The content of a tableau is the list of its entries. The ordering is from the left to the right and+-- then from the top to the bottom+tableauContent :: Tableau a -> [a]+tableauContent = concat++-- | An element @(i,j)@ of the resulting tableau (which has shape of the+-- given partition) means that the vertical part of the hook has length @i@,+-- and the horizontal part @j@. The /hook length/ is thus @i+j-1@. +--+-- Example:+--+-- > > mapM_ print $ hooks $ toPartition [5,4,1]+-- > [(3,5),(2,4),(2,3),(2,2),(1,1)]+-- > [(2,4),(1,3),(1,2),(1,1)]+-- > [(1,1)]+--+hooks :: Partition -> Tableau (Int,Int)+hooks part = zipWith f p [1..] where +  p = fromPartition part+  q = _dualPartition p+  f l i = zipWith (\x y -> (x-i+1,y)) q [l,l-1..1] ++hookLengths :: Partition -> Tableau Int+hookLengths part = (map . map) (\(i,j) -> i+j-1) (hooks part) ++--------------------------------------------------------------------------------+-- * Row and column words++-- | The /row word/ of a tableau is the list of its entry read from the right to the left and then+-- from the top to the bottom.+rowWord :: Tableau a -> [a]+rowWord = concat . reverse++-- | /Semistandard/ tableaux can be reconstructed from their row words+rowWordToTableau :: Ord a => [a] -> Tableau a+rowWordToTableau xs = reverse rows where+  rows = break xs+  break [] = [[]]+  break [x] = [[x]]+  break (x:xs@(y:_)) = if x>y+    then [x] : break xs+    else let (h:t) = break xs in (x:h):t++-- | The /column word/ of a tableau is the list of its entry read from the bottom to the top and then from the left to the right+columnWord :: Tableau a -> [a]+columnWord = rowWord . transpose++-- | /Standard/ tableaux can be reconstructed from either their column or row words+columnWordToTableau :: Ord a => [a] -> Tableau a+columnWordToTableau = transpose . rowWordToTableau++-- | Checks whether a sequence of positive integers is a /lattice word/, +-- which means that in every initial part of the sequence any number @i@+-- occurs at least as often as the number @i+1@+--+isLatticeWord :: [Int] -> Bool+isLatticeWord = go Map.empty where+  go :: Map Int Int -> [Int] -> Bool+  go _      []     = True+  go !table (i:is) =+    if check i+      then go table' is+      else False+    where+      table'  = Map.insertWith (+) i 1 table+      check j = j==1 || cnt (j-1) >= cnt j+      cnt j   = case Map.lookup j table' of+        Just k  -> k+        Nothing -> 0++--------------------------------------------------------------------------------+-- * Semistandard Young tableaux++-- | A tableau is /semistandard/ if its entries are weekly increasing horizontally+-- and strictly increasing vertically+isSemiStandardTableau :: Tableau Int -> Bool+isSemiStandardTableau t = weak && strict where+  weak   = and [ isWeaklyIncreasing   xs | xs <- t  ]+  strict = and [ isStrictlyIncreasing ys | ys <- dt ]+  dt     = dualTableau t+   +-- | Semistandard Young tableaux of given shape, \"naive\" algorithm    +semiStandardYoungTableaux :: Int -> Partition -> [Tableau Int]+semiStandardYoungTableaux n part = worker (repeat 0) shape where+  shape = fromPartition part+  worker _ [] = [[]] +  worker prevRow (s:ss) +    = [ (r:rs) | r <- row n s 1 prevRow, rs <- worker (map (+1) r) ss ]++  -- weekly increasing lists of length @len@, pointwise at least @xs@, +  -- maximum value @n@, minimum value @prev@.+  row :: Int -> Int -> Int -> [Int] -> [[Int]]+  row _ 0   _    _      = [[]]+  row n len prev (x:xs) = [ (a:as) | a <- [max x prev..n] , as <- row n (len-1) a xs ]++-- | Stanley's hook formula (cf. Fulton page 55)+countSemiStandardYoungTableaux :: Int -> Partition -> Integer+countSemiStandardYoungTableaux n shape = k `div` h where+  h = product $ map fromIntegral $ concat $ hookLengths shape +  k = product [ fromIntegral (n+j-i) | (i,j) <- elements shape ]++   +--------------------------------------------------------------------------------+-- * Standard Young tableaux++-- | A tableau is /standard/ if it is semistandard and its content is exactly @[1..n]@,+-- where @n@ is the weight.+isStandardTableau :: Tableau Int -> Bool+isStandardTableau t = isSemiStandardTableau t && sort (concat t) == [1..n] where+  n = sum [ length xs | xs <- t ]++-- | Standard Young tableaux of a given shape.+--   Adapted from John Stembridge, +--   <http://www.math.lsa.umich.edu/~jrs/software/SFexamples/tableaux>.+standardYoungTableaux :: Partition -> [Tableau Int]+standardYoungTableaux shape' = map rev $ tableaux shape where+  shape = fromPartition shape'+  rev = reverse . map reverse+  tableaux :: [Int] -> [Tableau Int]+  tableaux p = +    case p of+      []  -> [[]]+      [n] -> [[[n,n-1..1]]]+      _   -> worker (n,k) 0 [] p+    where+      n = sum p+      k = length p+  worker :: (Int,Int) -> Int -> [Int] -> [Int] -> [Tableau Int]+  worker _ _ _ [] = []+  worker nk i ls (x:rs) = case rs of+    (y:_) -> if x==y +      then worker nk (i+1) (x:ls) rs+      else worker2 nk i ls x rs+    [] ->  worker2 nk i ls x rs+  worker2 :: (Int,Int) -> Int -> [Int] -> Int -> [Int] -> [Tableau Int]+  worker2 nk@(n,k) i ls x rs = new ++ worker nk (i+1) (x:ls) rs where+    old = if x>1 +      then             tableaux $ reverse ls ++ (x-1) : rs+      else map ([]:) $ tableaux $ reverse ls ++ rs   +    a = k-1-i+    new = {- debug ( i , a , head old , f a (head old) ) $ -}+      map (f a) old+    f :: Int -> Tableau Int -> Tableau Int+    f _ [] = []+    f 0 (t:ts) = (n:t) : f (-1) ts+    f j (t:ts) = t : f (j-1) ts+  +-- | hook-length formula+countStandardYoungTableaux :: Partition -> Integer+countStandardYoungTableaux part = {- debug (hookLengths part) $ -}+  factorial n `div` h where+    h = product $ map fromIntegral $ concat $ hookLengths part +    n = weight part++--------------------------------------------------------------------------------+        +    
+ Math/Combinat/Tableaux/GelfandTsetlin.hs view
@@ -0,0 +1,341 @@++-- | Gelfand-Tsetlin patterns and Kostka numbers.+--+-- Gelfand-Tsetlin patterns (or tableaux) are triangular arrays like+--+-- > [ 3 ]+-- > [ 3 , 2 ]+-- > [ 3 , 1 , 0 ]+-- > [ 2 , 0 , 0 , 0 ]+--+-- with both rows and columns non-increasing non-negative integers.+-- Note: these are in bijection with the semi-standard Young tableaux.+--+-- If we add the further restriction that+-- the top diagonal reads @lambda@, +-- and the diagonal sums are partial sums of @mu@, where @lambda@ and @mu@ are two+-- partitions (in this case @lambda=[3,2]@ and @mu=[2,1,1,1]@), +-- then the number of the resulting patterns +-- or tableaux is the Kostka number @K(lambda,mu)@.+-- Actually @mu@ doesn't even need to the be non-increasing.+--++{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}+module Math.Combinat.Tableaux.GelfandTsetlin where++--------------------------------------------------------------------------------++import Data.List+import Data.Maybe+import Data.Monoid+import Data.Ord++import Control.Monad+import Control.Monad.Trans.State++import Data.Map (Map)+import qualified Data.Map as Map++import Math.Combinat.Partitions.Integer+import Math.Combinat.Tableaux+import Math.Combinat.Helper+import Math.Combinat.ASCII++--------------------------------------------------------------------------------+-- * Kostka numbers++-- | Kostka numbers (via counting Gelfand-Tsetlin patterns). See for example <http://en.wikipedia.org/wiki/Kostka_number>+--+-- @K(lambda,mu)==0@ unless @lambda@ dominates @mu@:+--+-- > [ mu | mu <- partitions (weight lam) , kostkaNumber lam mu > 0 ] == dominatedPartitions lam+--+kostkaNumber :: Partition -> Partition -> Int+kostkaNumber = countKostkaGelfandTsetlinPatterns++-- | Very naive (and slow) implementation of Kostka numbers, for reference.+kostkaNumberReferenceNaive :: Partition -> Partition -> Int+kostkaNumberReferenceNaive plambda pmu@(Partition mu) = length stuff where+  stuff  = [ (1::Int) | t <- semiStandardYoungTableaux k plambda , cond t ]+  k      = length mu+  cond t = [ (head xs, length xs) | xs <- group (sort $ concat t) ] == zip [1..] mu ++--------------------------------------------------------------------------------++-- | Lists all (positive) Kostka numbers @K(lambda,mu)@ with the given @lambda@:+--+-- > kostkaNumbersWithGivenLambda lambda == Map.fromList [ (mu , kostkaNumber lambda mu) | mu <- dominatedPartitions lambda ]+--+-- It's much faster than computing the individual Kostka numbers, but not as fast+-- as it could be.+--+{-# SPECIALIZE kostkaNumbersWithGivenLambda :: Partition -> Map Partition Int     #-}+{-# SPECIALIZE kostkaNumbersWithGivenLambda :: Partition -> Map Partition Integer #-}+kostkaNumbersWithGivenLambda :: forall coeff. Num coeff => Partition -> Map Partition coeff+kostkaNumbersWithGivenLambda plambda@(Partition lam) = evalState (worker lam) Map.empty where++  worker :: [Int] -> State (Map Partition (Map Partition coeff)) (Map Partition coeff)+  worker unlam = case unlam of+    [] -> return $ Map.singleton (Partition []) 1+    _  -> do+      cache <- get+      case Map.lookup (Partition unlam) cache of+        Just sol -> return sol+        Nothing  -> do+          let s = foldl' (+) 0 unlam+          subsols <- forM (prevLambdas0 unlam) $ \p -> do+            sub <- worker p +            let t = s - foldl' (+) 0 p              +                f (Partition xs , c) = case xs of+                  (y:_) -> if t >= y then Just (Partition (t:xs) , c) else Nothing+                  []    -> if t >  0 then Just (Partition [t]    , c) else Nothing+            if t > 0+              then return $ Map.fromList $ mapMaybe f $ Map.toList sub+              else return $ Map.empty++          let sol = Map.unionsWith (+) subsols+          put $! (Map.insert (Partition unlam) sol cache)+          return sol++  -- needs decreasing sequence+  prevLambdas0 :: [Int] -> [[Int]]+  prevLambdas0 (l:ls) = go l ls where+    go b [a]    = [ [x]   | x <- [a..b] ] ++ [ [x,y] | x <- [a..b] , y<-[1..a] ]+    go b (a:as) = [ x:xs  | x <- [a..b] , xs <- go a as ]+    go b []     = [] : [ [j] | j <- [1..b] ]+  prevLambdas0 []  = []++-- | Lists all (positive) Kostka numbers @K(lambda,mu)@ with the given @mu@:+--+-- > kostkaNumbersWithGivenMu mu == Map.fromList [ (lambda , kostkaNumber lambda mu) | lambda <- dominatingPartitions mu ]+--+-- This function uses the iterated Pieri rule, and is relatively fast.+--+kostkaNumbersWithGivenMu :: Partition -> Map Partition Int+kostkaNumbersWithGivenMu (Partition mu) = iteratedPieriRule (reverse mu)++--------------------------------------------------------------------------------+-- * Gelfand-Tsetlin patterns++-- | A Gelfand-Tstetlin tableau+type GT = [[Int]]++asciiGT :: GT -> ASCII+asciiGT gt = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) +           $ (map . map) asciiShow+           $ gt++kostkaGelfandTsetlinPatterns :: Partition -> Partition -> [GT]+kostkaGelfandTsetlinPatterns lambda (Partition mu) = kostkaGelfandTsetlinPatterns' lambda mu++-- | Generates all Kostka-Gelfand-Tsetlin tableau, that is, triangular arrays like+--+-- > [ 3 ]+-- > [ 3 , 2 ]+-- > [ 3 , 1 , 0 ]+-- > [ 2 , 0 , 0 , 0 ]+--+-- with both rows and column non-increasing such that+-- the top diagonal read lambda (in this case @lambda=[3,2]@) and the diagonal sums+-- are partial sums of mu (in this case @mu=[2,1,1,1]@)+--+-- The number of such GT tableaux is the Kostka+-- number K(lambda,mu).+--+kostkaGelfandTsetlinPatterns' :: Partition -> [Int] -> [GT]+kostkaGelfandTsetlinPatterns' plam@(Partition lambda0) mu0+  | minimum mu0 < 0                       = []+  | wlam == 0                             = if wmu == 0 then [ [] ] else []+  | wmu  == wlam && plam `dominates` pmu  = list+  | otherwise                             = []+  where++    pmu = mkPartition mu0++    nlam = length lambda0+    nmu  = length mu0++    n = max nlam nmu++    lambda = lambda0 ++ replicate (n - nlam) 0+    mu     = mu0     ++ replicate (n - nmu ) 0++    revlam = reverse lambda++    wmu  = sum' mu+    wlam = sum' lambda++    list = worker +             revlam +             (scanl1 (+) mu) +             (replicate (n-1) 0) +             (replicate (n  ) 0) +             []++    worker+      :: [Int]       -- lambda_i in reverse order+      -> [Int]       -- partial sums of mu+      -> [Int]       -- sums of the tails of previous rows+      -> [Int]       -- last row+      -> [[Int]]     -- the lower part of GT tableau we accumulated so far (this is not needed if we only want to count)+      -> [GT]   ++    worker (rl:rls) (smu:smus) (a:acc) (lastx0:lastrowt) table = stuff +      where+        x0 = smu - a+        stuff = concat +          [ worker rls smus (zipWith (+) acc (tail row)) (init row) (row:table)+          | row <- boundedNonIncrSeqs' x0 (map (max rl) (max lastx0 x0 : lastrowt)) lambda+          ]+    worker [rl] _ _ _ table = [ [rl]:table ] +    worker []   _ _ _ _     = [ []         ]++    boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+    boundedNonIncrSeqs' = go where+      go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ]+      go _  []     _      = [[]]+      go _  _      []     = [[]]++--------------------------------------------------------------------------------++-- | This returns the corresponding Kostka number:+--+-- > countKostkaGelfandTsetlinPatterns lambda mu == length (kostkaGelfandTsetlinPatterns lambda mu)+-- +countKostkaGelfandTsetlinPatterns :: Partition -> Partition -> Int+countKostkaGelfandTsetlinPatterns plam@(Partition lambda0) pmu@(Partition mu0) +  | wlam == 0                             = if wmu == 0 then 1 else 0+  | wmu  == wlam && plam `dominates` pmu  = cnt+  | otherwise                             = 0+  where++    nlam = length lambda0+    nmu  = length mu0++    n = max nlam nmu++    lambda = lambda0 ++ replicate (n - nlam) 0+    mu     = mu0     ++ replicate (n - nmu ) 0++    revlam = reverse lambda++    wmu  = sum' mu+    wlam = sum' lambda++    cnt = worker +            revlam +            (scanl1 (+) mu) +            (replicate (n-1) 0) +            (replicate (n  ) 0) ++    worker+      :: [Int]       -- lambda_i in reverse order+      -> [Int]       -- partial sums of mu+      -> [Int]       -- sums of the tails of previous rows+      -> [Int]       -- last row+      -> Int++    worker (rl:rls) (smu:smus) (a:acc) (lastx0:lastrowt) = stuff +      where+        x0 = smu - a+        stuff = sum'+          [ worker rls smus (zipWith (+) acc (tail row)) (init row) +          | row <- boundedNonIncrSeqs' x0 (map (max rl) (max lastx0 x0 : lastrowt)) lambda+          ]+    worker [rl] _ _ _ = 1 +    worker []   _ _ _ = 1++    boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+    boundedNonIncrSeqs' = go where+      go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ]+      go _  []     _      = [[]]+      go _  _      []     = [[]]++--------------------------------------------------------------------------------++{-++-- | All non-increasing sentences between a lower and an upper bound+boundedNonIncrSeqs :: [Int] -> [Int] -> [[Int]]+boundedNonIncrSeqs as bs = case bs of  +  (h0:_) -> boundedNonIncrSeqs' h0 as bs+  []     -> [[]]++-- | All non-increasing sentences between a lower and an upper bound, and also less-or-equal than the given number+boundedNonIncrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+boundedNonIncrSeqs' = go where+  go h0 (a:as) (b:bs) = [ h:hs | h <- [(max 0 a)..(min h0 b)] , hs <- go h as bs ]+  go _  []     _      = [[]]+  go _  _      []     = [[]]++-- | All non-decreasing sentences between a lower and an upper bound+boundedNonDecrSeqs :: [Int] -> [Int] -> [[Int]]+boundedNonDecrSeqs = boundedNonDecrSeqs' 0++-- | All non-decreasing sentences between a lower and an upper bound, and also greator-or-equal then the given number+boundedNonDecrSeqs' :: Int -> [Int] -> [Int] -> [[Int]]+boundedNonDecrSeqs' h0 = go (max 0 h0) where+  go h0 (a:as) (b:bs) = [ h:hs | h <- [(max h0 a)..b] , hs <- go h as bs ]+  go _  []     _      = [[]]+  go _  _      []     = [[]]++-}++--------------------------------------------------------------------------------+-- * The iterated Pieri rule ++-- | Computes the Schur expansion of @h[n1]*h[n2]*h[n3]*...*h[nk]@ via iterating the Pieri rule.+-- Note: the coefficients are actually the Kostka numbers; the following is true:+--+-- > Map.toList (iteratedPieriRule (fromPartition mu))  ==  [ (lam, kostkaNumber lam mu) | lam <- dominatingPartitions mu ]+-- +-- This should be faster than individually computing all these Kostka numbers.+--+iteratedPieriRule :: Num coeff => [Int] -> Map Partition coeff+iteratedPieriRule = iteratedPieriRule' (Partition [])++-- | Iterating the Pieri rule, we can compute the Schur expansion of+-- @h[lambda]*h[n1]*h[n2]*h[n3]*...*h[nk]@+iteratedPieriRule' :: Num coeff => Partition -> [Int] -> Map Partition coeff+iteratedPieriRule' plambda ns = iteratedPieriRule'' (plambda,1) ns++{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Int    ) -> [Int] -> Map Partition Int     #-}+{-# SPECIALIZE iteratedPieriRule'' :: (Partition,Integer) -> [Int] -> Map Partition Integer #-}+iteratedPieriRule'' :: Num coeff => (Partition,coeff) -> [Int] -> Map Partition coeff+iteratedPieriRule'' (plambda,coeff0) ns = worker (Map.singleton plambda coeff0) ns where+  worker old []     = old+  worker old (n:ns) = worker new ns where+    stuff = [ (coeff, pieriRule lam n) | (lam,coeff) <- Map.toList old ] +    new   = foldl' f Map.empty stuff +    f t0 (c,ps) = foldl' (\t p -> Map.insertWith (+) p c t) t0 ps  ++--------------------------------------------------------------------------------++-- | Computes the Schur expansion of @e[n1]*e[n2]*e[n3]*...*e[nk]@ via iterating the Pieri rule.+-- Note: the coefficients are actually the Kostka numbers; the following is true:+--+-- > Map.toList (iteratedDualPieriRule (fromPartition mu))  ==  +-- >   [ (dualPartition lam, kostkaNumber lam mu) | lam <- dominatingPartitions mu ]+-- +-- This should be faster than individually computing all these Kostka numbers.+-- It is a tiny bit slower than 'iteratedPieriRule'.+--+iteratedDualPieriRule :: Num coeff => [Int] -> Map Partition coeff+iteratedDualPieriRule = iteratedDualPieriRule' (Partition [])++-- | Iterating the Pieri rule, we can compute the Schur expansion of+-- @e[lambda]*e[n1]*e[n2]*e[n3]*...*e[nk]@+iteratedDualPieriRule' :: Num coeff => Partition -> [Int] -> Map Partition coeff+iteratedDualPieriRule' plambda ns = iteratedDualPieriRule'' (plambda,1) ns++{-# SPECIALIZE iteratedDualPieriRule'' :: (Partition,Int    ) -> [Int] -> Map Partition Int     #-}+{-# SPECIALIZE iteratedDualPieriRule'' :: (Partition,Integer) -> [Int] -> Map Partition Integer #-}+iteratedDualPieriRule'' :: Num coeff => (Partition,coeff) -> [Int] -> Map Partition coeff+iteratedDualPieriRule'' (plambda,coeff0) ns = worker (Map.singleton plambda coeff0) ns where+  worker old []     = old+  worker old (n:ns) = worker new ns where+    stuff = [ (coeff, dualPieriRule lam n) | (lam,coeff) <- Map.toList old ] +    new   = foldl' f Map.empty stuff +    f t0 (c,ps) = foldl' (\t p -> Map.insertWith (+) p c t) t0 ps  ++--------------------------------------------------------------------------------
+ Math/Combinat/Tableaux/GelfandTsetlin/Cone.hs view
@@ -0,0 +1,261 @@++-- TODO: better name?++-- | This module contains a function to generate (equivalence classes of) +-- triangular tableaux of size /k/, strictly increasing to the right and +-- to the bottom. For example+-- +-- >  1  +-- >  2  4  +-- >  3  5  8  +-- >  6  7  9  10 +--+-- is such a tableau of size 4.+-- The numbers filling a tableau always consist of an interval @[1..c]@;+-- @c@ is called the /content/ of the tableaux. There is a unique tableau+-- of minimal content @2k-1@:+--+-- >  1  +-- >  2  3  +-- >  3  4  5 +-- >  4  5  6  7 +-- +-- Let us call the tableaux with maximal content (that is, @m = binomial (k+1) 2@)+-- /standard/. The number of such standard tableaux are+--+-- > 1, 1, 2, 12, 286, 33592, 23178480, ...+--+-- OEIS:A003121, \"Strict sense ballot numbers\", +-- <https://oeis.org/A003121>.+--+-- See +-- R. M. Thrall, A combinatorial problem, Michigan Math. J. 1, (1952), 81-88.+-- +-- The number of tableaux with content @c=m-d@ are+-- +-- >  d=  |     0      1      2      3    ...+-- > -----+----------------------------------------------+-- >  k=2 |     1+-- >  k=3 |     2      1+-- >  k=4 |    12     18      8      1+-- >  k=5 |   286    858   1001    572    165     22     1+-- >  k=6 | 33592 167960 361114 436696 326196 155584 47320 8892 962 52 1 +--+-- We call these \"GT simplex tableaux\" (in the lack of a better name), since+-- they are in bijection with the simplicial cones in a canonical simplicial +-- decompositions of the Gelfand-Tsetlin cones (the content corresponds+-- to the dimension), which encode the combinatorics of Kostka numbers.+--++{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}+module Math.Combinat.Tableaux.GelfandTsetlin.Cone+  ( +    -- * Types+    Tableau+  , Tri(..)+  , TriangularArray+  , fromTriangularArray+  , triangularArrayUnsafe+    -- * ASCII+  , asciiTriangularArray+  , asciiTableau+    -- * Content+  , gtSimplexContent+  , _gtSimplexContent+  , invertGTSimplexTableau+  , _invertGTSimplexTableau+    -- * Enumeration+  , gtSimplexTableaux+  , _gtSimplexTableaux+  , countGTSimplexTableaux+  ) +  where++--------------------------------------------------------------------------------++import Data.Ix+import Data.Ord+import Data.List++import Control.Monad+import Control.Monad.ST+import Data.Array.IArray+import Data.Array.Unboxed+import Data.Array.ST++import Math.Combinat.Tableaux (Tableau)+import Math.Combinat.Helper+import Math.Combinat.ASCII++--------------------------------------------------------------------------------++-- | Triangular arrays+type TriangularArray a = Array Tri a++-- | Set of @(i,j)@ pairs with @i>=j>=1@.+newtype Tri = Tri { unTri :: (Int,Int) } deriving (Eq,Ord,Show)++binom2 :: Int -> Int+binom2 n = (n*(n-1)) `div` 2++index' :: Tri -> Int+index' (Tri (i,j)) = binom2 i + j - 1++-- it should be (1+8*m), +-- the 2 is a hack to be safe with the floating point stuff+deIndex' :: Int -> Tri +deIndex' m = Tri ( i+1 , m - binom2 (i+1) + 1 ) where+  i = ( (floor.sqrt.(fromIntegral::Int->Double)) (2+8*m) - 1 ) `div` 2  ++instance Ix Tri where+  index   (a,b) x = index' x - index' a +  inRange (a,b) x = (u<=j && j<=v) where+    u = index' a +    v = index' b+    j = index' x+  range     (a,b) = map deIndex' [ index' a .. index' b ] +  rangeSize (a,b) = index' b - index' a + 1 ++triangularArrayUnsafe :: Tableau a -> TriangularArray a+triangularArrayUnsafe tableau = listArray (Tri (1,1),Tri (k,k)) (concat tableau) +  where k = length tableau++fromTriangularArray :: TriangularArray a -> Tableau a+fromTriangularArray arr = (map.map) snd $ groupBy (equating f) $ assocs arr+  where f = fst . unTri . fst++--------------------------------------------------------------------------------++asciiTriangularArray :: Show a => TriangularArray a -> ASCII+asciiTriangularArray = asciiTableau . fromTriangularArray++asciiTableau :: Show a => Tableau a -> ASCII+asciiTableau xxs = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) +                 $ (map . map) asciiShow+                 $ xxs++instance Show a => DrawASCII (TriangularArray a) where+  ascii = asciiTriangularArray++-- instance Show a => DrawASCII (Tableau a) where+--   ascii = asciiTableau++--------------------------------------------------------------------------------++-- "fractional fillings"+data Hole = Hole Int Int deriving (Eq,Ord,Show)++type ReverseTableau      = [[Int ]] +type ReverseHoleTableau  = [[Hole]]      ++toHole :: Int -> Hole+toHole k = Hole k 0++nextHole :: Hole -> Hole+nextHole (Hole k l) = Hole k (l+1)++reverseTableau :: [[a]] -> [[a]]+reverseTableau = reverse . map reverse++--------------------------------------------------------------------------------++gtSimplexContent :: TriangularArray Int -> Int+gtSimplexContent arr = max (arr ! (fst (bounds arr))) (arr ! (snd (bounds arr)))   -- we also handle inverted tableau++_gtSimplexContent :: Tableau Int -> Int+_gtSimplexContent t = max (head $ head t) (last $ last t)   -- we also handle inverted tableau+ +normalize :: ReverseHoleTableau -> TriangularArray Int +normalize = snd . normalize'++-- returns ( content , tableau )+normalize' :: ReverseHoleTableau -> ( Int , TriangularArray Int )   +normalize' holes = ( c , array (Tri (1,1), Tri (k,k)) xys ) where+  k = length holes+  c = length sorted+  xys = concat $ zipWith hs [1..] sorted+  hs a xs     = map (h a) xs+  h  a (ij,_) = (Tri ij , a)  +  sorted = groupSortBy snd (concat withPos)+  withPos = zipWith f [1..] (reverseTableau holes) +  f i xs = zipWith (g i) [1..] xs +  g i j hole = ((i,j),hole) ++--------------------------------------------------------------------------------++startHole :: [Hole] -> [Int] -> Hole +startHole (t:ts) (p:ps) = max t (toHole p)+startHole (t:ts) []     = t+startHole []     (p:ps) = toHole p+startHole []     []     = error "startHole"++-- c is the "content" of the small tableau+enumHoles :: Int -> Hole -> [Hole]+enumHoles c start@(Hole k l) +  = nextHole start +  : [ Hole i 0 | i <- [k+1..c] ] ++ [ Hole i 1 | i <- [k+1..c] ]++helper :: Int -> [Int] -> [Hole] -> [[Hole]]+helper c [] this = [[]] +helper c prev@(p:ps) this = +  [ t:rest | t <- enumHoles c (startHole this prev), rest <- helper c ps (t:this) ]++newLines' :: Int -> [Int] -> [[Hole]]+newLines' c lastReversed = helper c last []  +  where+    top  = head lastReversed+    last = reverse (top : lastReversed)++newLines :: [Int] -> [[Hole]]+newLines lastReversed = newLines' (head lastReversed) lastReversed++-- | Generates all tableaux of size @k@. Effective for @k<=6@.+gtSimplexTableaux :: Int -> [TriangularArray Int]+gtSimplexTableaux 0 = [ triangularArrayUnsafe [] ]+gtSimplexTableaux 1 = [ triangularArrayUnsafe [[1]] ]+gtSimplexTableaux k = map normalize $ concatMap f smalls where+  smalls :: [ [[Int]] ]+  smalls = map (reverseTableau . fromTriangularArray) $ gtSimplexTableaux (k-1)+  f :: [[Int]] -> [ [[Hole]] ]+  f small = map (:smallhole) $ map reverse $ newLines (head small) where+    smallhole = map (map toHole) small++_gtSimplexTableaux :: Int -> [Tableau Int]+_gtSimplexTableaux k = map fromTriangularArray $ gtSimplexTableaux k++--------------------------------------------------------------------------------++-- | Note: This is slow (it actually generates all the tableaux)+countGTSimplexTableaux :: Int -> [Int]+countGTSimplexTableaux = elems . sizes'++sizes' :: Int -> UArray Int Int+sizes' k = +  runSTUArray $ do+    let (a,b) = ( 2*k-1 , binom2 (k+1) )+    ar <- newArray (a,b) 0 :: ST s (STUArray s Int Int)   +    mapM_ (worker ar) $ gtSimplexTableaux k +    return ar+  where+    worker :: STUArray s Int Int -> TriangularArray Int -> ST s ()+    worker ar t = do+      let c = gtSimplexContent t +      n <- readArray ar c  +      writeArray ar c (n+1)+     +--------------------------------------------------------------------------------++-- | We can flip the numbers in the tableau so that the interval @[1..c]@ becomes+-- @[c..1]@. This way we a get a maybe more familiar form, when each row and each+-- column is strictly /decreasing/ (to the right and to the bottom).+invertGTSimplexTableau :: TriangularArray Int -> TriangularArray Int +invertGTSimplexTableau t = amap f t where+  c = gtSimplexContent t +  f x = c+1-x  ++_invertGTSimplexTableau :: [[Int]] -> [[Int]]+_invertGTSimplexTableau t = (map . map) f t where+  c = _gtSimplexContent t  +  f x = c+1-x++--------------------------------------------------------------------------------
+ Math/Combinat/Tableaux/LittlewoodRichardson.hs view
@@ -0,0 +1,399 @@++-- | The Littlewood-Richardson rule++module Math.Combinat.Tableaux.LittlewoodRichardson +  ( lrCoeff , lrCoeff'+  , lrMult+  , lrRule  , _lrRule , lrRuleNaive+  , lrScalar , _lrScalar+  ) +  where++--------------------------------------------------------------------------------++import Data.List+import Data.Maybe++import Math.Combinat.Partitions.Integer+import Math.Combinat.Partitions.Skew+import Math.Combinat.Tableaux+import Math.Combinat.Tableaux.Skew+import Math.Combinat.Helper++import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map++--------------------------------------------------------------------------------++-- | Naive (very slow) reference implementation of the Littlewood-Richardson rule, based +-- on the definition "count the semistandard skew tableaux whose row content is a lattice word"+--+lrRuleNaive :: SkewPartition -> Map Partition Int+lrRuleNaive skew = final where+  n     = skewPartitionWeight skew+  ssst  = semiStandardSkewTableaux n skew +  final = foldl' f Map.empty $ catMaybes [ skewTableauRowContent skew | skew <- ssst  ]+  f old nu = Map.insertWith (+) nu 1 old++--------------------------------------------------------------------------------+-- SKEW EXPANSION++-- | @lrRule@ computes the expansion of a skew Schur function +-- @s[lambda\/mu]@ via the Littlewood-Richardson rule.+--+-- Adapted from John Stembridge's Maple code: +-- <http://www.math.lsa.umich.edu/~jrs/software/SFexamples/LR_rule>+--+-- > lrRule (mkSkewPartition (lambda,nu)) == Map.fromList list where+-- >   muw  = weight lambda - weight nu+-- >   list = [ (mu, coeff) +-- >          | mu <- partitions muw +-- >          , let coeff = lrCoeff lambda (mu,nu)+-- >          , coeff /= 0+-- >          ] +--+lrRule :: SkewPartition -> Map Partition Int+lrRule skew = _lrRule lam mu where+  (lam,mu) = fromSkewPartition skew++-- | @_lrRule lambda mu@ computes the expansion of the skew+-- Schur function @s[lambda\/mu]@ via the Littlewood-Richardson rule.+--+_lrRule :: Partition -> Partition -> Map Partition Int+_lrRule plam@(Partition lam) pmu@(Partition mu0) = +  if not (pmu `isSubPartitionOf` plam) +    then Map.empty+    else foldl' f Map.empty [ nu | (nu,_) <- fillings n diagram ]+  where+    f old nu = Map.insertWith (+) (Partition nu) 1 old+    diagram  = [ (i,j) | (i,a,b) <- reverse (zip3 [1..] lam mu) , j <- [b+1..a] ]    +    mu       = mu0 ++ repeat 0+    n        = sum' $ zipWith (-) lam mu    -- n == length diagram++{-+LR_rule:=proc(lambda) local l,mu,alpha,beta,i,j,dgrm;+  if not `LR_rule/fit`(lambda,args[2]) then RETURN(0) fi;+  l:=nops(lambda); mu:=[op(args[2]),0$l];+  dgrm:=[seq(seq([i,-j],j=-lambda[i]..-1-mu[i]),i=1..l)];+  if nargs>2 then alpha:=args[3];+    if nargs>3 then beta:=args[4] else beta:=[] fi;+    if not `LR_rule/fit`(alpha,beta) then RETURN(0) fi;+    l:=convert([op(lambda),op(beta)],`+`);+    if l<>convert([op(alpha),op(mu)],`+`) then RETURN(0) fi;+    nops(LR_fillings(dgrm,[alpha,beta]))+  else+    convert([seq(s[op(i[1])],i=LR_fillings(dgrm))],`+`)+  fi+end;+-}++--------------------------------------------------------------------------------++-- | A filling is a pair consisting a shape @nu@ and a lattice permutation @lp@+type Filling = ( [Int] , [Int] )++-- | A diagram is a set of boxes in a skew shape (in the right order)+type Diagram = [ (Int,Int) ]++-- | Note: we use reverse ordering in Diagrams compared the Stembridge's code.+-- Also, for performance reasons, we need the length of the diagram+fillings :: Int -> Diagram -> [Filling]+fillings _ []                   = [ ([],[]) ]+fillings n diagram@((x,y):rest) = concatMap (nextLetter lower upper) (fillings (n-1) rest) where+  upper = case findIndex (==(x  ,y+1)) diagram of { Just j -> n-j ; Nothing -> 0 }+  lower = case findIndex (==(x-1,y  )) diagram of { Just j -> n-j ; Nothing -> 0 }++{-+LR_fillings:=proc(dgrm) local n,x,upper,lower;+  if dgrm=[] then+    if nargs=1 then x:=[] else x:=args[2][2] fi;+    RETURN([[x,[]]])+  fi;+  n:=nops(dgrm); x:=dgrm[n];+  if not member([x[1],x[2]+1],dgrm,'upper') then upper:=0 fi;+  if not member([x[1]-1,x[2]],dgrm,'lower') then lower:=0 fi;+  if nargs=1 then+    map(`LR/nextletter`,LR_fillings([op(1..n-1,dgrm)]),lower,upper)+  else+    map(`LR/nextletter`,LR_fillings([op(1..n-1,dgrm)],args[2]),+      lower,upper,args[2][1])+  fi;+end:+-}++--------------------------------------------------------------------------------++nextLetter :: Int -> Int -> Filling -> [Filling]+nextLetter lower upper (nu,lpart) = stuff where+  stuff = [ ( incr i shape , lpart++[i] ) | i<-nlist ] +  shape = nu ++ [0] +  lb = if lower>0+    then lpart !! (lower-1)+    else 0+  ub = if upper>0 +    then min (length shape) (lpart !! (upper-1))  +    else      length shape++  nlist = filter (>0) $ map f [lb+1..ub] +  f j   = if j==1 || shape!!(j-2) > shape!!(j-1) then j else 0++{-+  -- another nlist implementation, but doesn't seem to be faster+  (h0:hs0) = drop lb (-666:shape)+  nlist = go h0 hs0 [lb+1..ub] where+    go !lasth (h:hs) (j:js) = if j==1 || lasth > h +      then j : go h hs js +      else     go h hs js+    go _      _      []     = []+-}++  -- increments the i-th element (starting from 1)+  incr :: Int -> [Int] -> [Int]+  incr i (x:xs) = case i of+    0 ->         finish (x:xs)+    1 -> (x+1) : finish xs+    _ -> x     : incr (i-1) xs+  incr _ []     = []++  -- removes tailing zeros+  finish :: [Int] -> [Int]+  finish (x:xs) = if x>0 then x : finish xs else []    +  finish []     = [] ++{-+`LR/nextletter`:=proc(T) local shape,lp,lb,ub,i,nl;+  shape:=[op(T[1]),0]; lp:=T[2]; ub:=nops(shape);+  if nargs>3 then ub:=min(ub,nops(args[4])) fi;+  if args[2]=0 then lb:=0 else lb:=lp[args[2]] fi;+  if args[3]>0 then ub:=min(lp[args[3]],ub) fi;+  if nargs<4 then+    nl:=map(proc(x,y) if x=1 or y[x-1]>y[x] then x fi end,[$lb+1..ub],shape)+  else+    nl:=map(proc(x,y,z) if y[x]<z[x] and (x=1 or y[x-1]>y[x]) then x fi end,+      [$lb+1..ub],shape,args[4])+  fi;+  nl:=[seq([subsop(i=shape[i]+1,shape),[op(lp),i]],i=nl)];+  op(subs(0=NULL,nl))+end:+-}++--------------------------------------------------------------------------------+-- COEFF++-- | @lrCoeff lam (mu,nu)@ computes the coressponding Littlewood-Richardson coefficients.+-- This is also the coefficient of @s[lambda]@ in the product @s[mu]*s[nu]@+--+-- Note: This is much slower than using 'lrRule' or 'lrMult' to compute several coefficients+-- at the same time!+lrCoeff :: Partition -> (Partition,Partition) -> Int+lrCoeff lam (mu,nu) = +  if nu `isSubPartitionOf` lam+    then lrScalar (mkSkewPartition (lam,nu)) (mkSkewPartition (mu,emptyPartition))+    else 0++-- | @lrCoeff (lam\/nu) mu@ computes the coressponding Littlewood-Richardson coefficients.+-- This is also the coefficient of @s[mu]@ in the product @s[lam\/nu]@+--+-- Note: This is much slower than using 'lrRule' or 'lrMult' to compute several coefficients+-- at the same time!+lrCoeff' :: SkewPartition -> Partition -> Int+lrCoeff' skew p = lrScalar skew (mkSkewPartition (p,emptyPartition))+  +--------------------------------------------------------------------------------+-- SCALAR PRODUCT++-- | @lrScalar (lambda\/mu) (alpha\/beta)@ computes the scalar product of the two skew+-- Schur functions @s[lambda\/mu]@ and @s[alpha\/beta]@ via the Littlewood-Richardson rule.+--+-- Adapted from John Stembridge Maple code: +-- <http://www.math.lsa.umich.edu/~jrs/software/SFexamples/LR_rule>+--+lrScalar :: SkewPartition -> SkewPartition -> Int+lrScalar lambdaMu alphaBeta = _lrScalar (fromSkewPartition lambdaMu) (fromSkewPartition alphaBeta)++_lrScalar :: (Partition,Partition) -> (Partition,Partition) -> Int+_lrScalar (plam  @(Partition lam  ) , pmu  @(Partition mu0)  ) +         (palpha@(Partition alpha) , pbeta@(Partition beta)) = +  if    not (pmu   `isSubPartitionOf` plam  ) +     || not (pbeta `isSubPartitionOf` palpha) +     || (sum' lam + sum' beta) /= (sum' alpha + sum' mu0)     -- equivalent to (lambda-mu) /= (alpha-beta)+    then 0+    else length $ fillings' n diagram (alpha,beta) +  where+    f old nu = Map.insertWith (+) (Partition nu) 1 old+    diagram  = [ (i,j) | (i,a,b) <- reverse (zip3 [1..] lam mu) , j <- [b+1..a] ]    +    mu       = mu0 ++ repeat 0+    n        = sum' $ zipWith (-) lam mu    -- n == length diagram++{-+LR_rule:=proc(lambda) local l,mu,alpha,beta,i,j,dgrm;+  if not `LR_rule/fit`(lambda,args[2]) then RETURN(0) fi;+  l:=nops(lambda); mu:=[op(args[2]),0$l];+  dgrm:=[seq(seq([i,-j],j=-lambda[i]..-1-mu[i]),i=1..l)];+  if nargs>2 then alpha:=args[3];+    if nargs>3 then beta:=args[4] else beta:=[] fi;+    if not `LR_rule/fit`(alpha,beta) then RETURN(0) fi;+    l:=convert([op(lambda),op(beta)],`+`);+    if l<>convert([op(alpha),op(mu)],`+`) then RETURN(0) fi;+    nops(LR_fillings(dgrm,[alpha,beta]))+  else+    convert([seq(s[op(i[1])],i=LR_fillings(dgrm))],`+`)+  fi+end;+-}++--------------------------------------------------------------------------------++-- | Note: we use reverse ordering in Diagrams compared the Stembridge's code.+-- Also, for performance reasons, we need the length of the diagram+fillings' :: Int -> Diagram -> ([Int],[Int]) -> [Filling]+fillings' _         []                     (alpha,beta) = [ (beta,[]) ]+fillings' n diagram@((x,y):rest) alphaBeta@(alpha,beta) = stuff where+  stuff = concatMap (nextLetter' lower upper alpha) (fillings' (n-1) rest alphaBeta) +  upper = case findIndex (==(x  ,y+1)) diagram of { Just j -> n-j ; Nothing -> 0 }+  lower = case findIndex (==(x-1,y  )) diagram of { Just j -> n-j ; Nothing -> 0 }++{-+LR_fillings:=proc(dgrm) local n,x,upper,lower;+  if dgrm=[] then+    if nargs=1 then x:=[] else x:=args[2][2] fi;+    RETURN([[x,[]]])+  fi;+  n:=nops(dgrm); x:=dgrm[n];+  if not member([x[1],x[2]+1],dgrm,'upper') then upper:=0 fi;+  if not member([x[1]-1,x[2]],dgrm,'lower') then lower:=0 fi;+  if nargs=1 then+    map(`LR/nextletter`,LR_fillings([op(1..n-1,dgrm)]),lower,upper)+  else+    map(`LR/nextletter`,LR_fillings([op(1..n-1,dgrm)],args[2]),+      lower,upper,args[2][1])+  fi;+end:+-}++--------------------------------------------------------------------------------++nextLetter' :: Int -> Int -> [Int] -> Filling -> [Filling]+nextLetter' lower upper alpha (nu,lpart) = stuff where+  stuff = [ ( incr i shape , lpart++[i] ) | i<-nlist ] +  shape = nu ++ [0] +  lb = if lower>0+    then lpart !! (lower-1)+    else 0+  ub1 = if upper>0 +    then min (length shape) (lpart !! (upper-1))  +    else      length shape+  ub = min (length alpha) ub1+  nlist = filter (>0) $ map f [lb+1..ub] +  f j   = if (        shape!!(j-1) < alpha!!(j-1)) &&+             (j==1 || shape!!(j-2) > shape!!(j-1)) +          then j +          else 0++  -- increments the i-th element (starting from 1)+  incr :: Int -> [Int] -> [Int]+  incr i (x:xs) = case i of+    0 ->         finish (x:xs)+    1 -> (x+1) : finish xs+    _ -> x     : incr (i-1) xs+  incr _ []     = []++  -- removes tailing zeros+  finish :: [Int] -> [Int]+  finish (x:xs) = if x>0 then x : finish xs else []    +  finish []     = [] ++{-+`LR/nextletter`:=proc(T) local shape,lp,lb,ub,i,nl;+  shape:=[op(T[1]),0]; lp:=T[2]; ub:=nops(shape);+  if nargs>3 then ub:=min(ub,nops(args[4])) fi;+  if args[2]=0 then lb:=0 else lb:=lp[args[2]] fi;+  if args[3]>0 then ub:=min(lp[args[3]],ub) fi;+  if nargs<4 then+    nl:=map(proc(x,y) if x=1 or y[x-1]>y[x] then x fi end,[$lb+1..ub],shape)+  else+    nl:=map(proc(x,y,z) if y[x]<z[x] and (x=1 or y[x-1]>y[x]) then x fi end,+      [$lb+1..ub],shape,args[4])+  fi;+  nl:=[seq([subsop(i=shape[i]+1,shape),[op(lp),i]],i=nl)];+  op(subs(0=NULL,nl))+end:+-}++--------------------------------------------------------------------------------+-- MULTIPLICATION++type Part = [Int]++-- | Computes the expansion of the product of Schur polynomials @s[mu]*s[nu]@ using the+-- Littlewood-Richardson rule. Note: this is symmetric in the two arguments.+--+-- Based on the wikipedia article <https://en.wikipedia.org/wiki/Littlewood-Richardson_rule>+--+-- > lrMult mu nu == Map.fromList list where+-- >   lamw = weight nu + weight mu+-- >   list = [ (lambda, coeff) +-- >          | lambda <- partitions lamw +-- >          , let coeff = lrCoeff lambda (mu,nu)+-- >          , coeff /= 0+-- >          ] +--+lrMult :: Partition -> Partition -> Map Partition Int+lrMult pmu@(Partition mu) pnu@(Partition nu) = result where+  result = foldl' add Map.empty (addMu mu nu) where+  add !old lambda = Map.insertWith (+) (Partition lambda) 1 old++-- | This basically lists all the outer shapes (with multiplicities) which can be result from the LR rule+addMu :: Part -> Part -> [Part]+addMu mu part = go ubs0 mu dmu part where++  go _   []     _      part = [part]+  go ubs (m:ms) (d:ds) part = concat [ go (drop d ubs') ms ds part' | (ubs',part') <- addRowOf ubs part ]++  ubs0 = take (headOrZero mu) [headOrZero part + 1..]+  dmu  = diffSeq mu+++-- | Adds a full row of @(length pcols)@ boxes containing to a partition, with+-- pcols being the upper bounds of the columns, respectively. We also return the+-- newly added columns+addRowOf :: [Int] -> Part -> [([Int],Part)]+addRowOf pcols part = go 0 pcols part [] where+  go !lb []        p ncols = [(reverse ncols , p)]+  go !lb (!ub:ubs) p ncols = concat [ go col ubs (addBox ij p) (col:ncols) | ij@(row,col) <- newBoxes (lb+1) ub p ]++-- | Returns the (row,column) pairs of the new boxes which +-- can be added to the given partition with the given column bounds+-- and the 1-Rieri rule +newBoxes :: Int -> Int -> Part -> [(Int,Int)]+newBoxes lb ub part = reverse $ go [1..] part (headOrZero part + 1) where+  go (!i:_ ) []      !lp+    | lb <= 1 && 1 <= ub && lp > 0  =  [(i,1)]+    | otherwise                     =  []+  go (!i:is) (!j:js) !lp +    | j1 <  lb            =  []+    | j1 <= ub && lp > j  =  (i,j1) : go is js j       +    | otherwise           =           go is js j+    where +      j1 = j+1++-- | Adds a box to a partition+addBox :: (Int,Int) -> Part -> Part+addBox (k,_) part = go 1 part where+  go !i (p:ps) = if i==k then (p+1):ps else p : go (i+1) ps+  go !i []     = if i==k then [1] else error "addBox: shouldn't happen"++-- | Safe head defaulting to zero           +headOrZero :: [Int] -> Int+headOrZero xs = case xs of +  (!x:_) -> x+  []     -> 0++-- | Computes the sequence of differences from a partition (including the last difference to zero)+diffSeq :: Part -> [Int]+diffSeq = go where+  go (p:ps@(q:_)) = (p-q) : go ps+  go [p]          = [p]+  go []           = []++--------------------------------------------------------------------------------  
+ Math/Combinat/Tableaux/Skew.hs view
@@ -0,0 +1,223 @@++-- | Skew tableaux are skew partitions filled with numbers.+--+-- For example:+--+-- <<svg/skew_tableau.svg>>+--++{-# LANGUAGE CPP, BangPatterns, ScopedTypeVariables, MultiParamTypeClasses #-}++module Math.Combinat.Tableaux.Skew where++--------------------------------------------------------------------------------++import Data.List++import Math.Combinat.Classes+import Math.Combinat.Partitions.Integer+import Math.Combinat.Partitions.Skew+import Math.Combinat.Tableaux+import Math.Combinat.ASCII+import Math.Combinat.Helper++import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map++--------------------------------------------------------------------------------+-- * Basics+-- | A skew tableau is represented by a list of offsets and entries+newtype SkewTableau a = SkewTableau [(Int,[a])] deriving (Eq,Ord,Show)++-- unSkewTableau :: SkewTableau a -> [(Int,[a])]+-- unSkewTableau (SkewTableau a) = a++instance Functor SkewTableau where+  fmap f (SkewTableau t) = SkewTableau [ (a, map f xs) | (a,xs) <- t ]++-- | The shape of a skew tableau +skewTableauShape :: SkewTableau a -> SkewPartition+skewTableauShape (SkewTableau list) = SkewPartition [ (o,length xs) | (o,xs) <- list ]++instance HasShape (SkewTableau a) SkewPartition where+  shape = skewTableauShape++-- | The weight of a tableau is the weight of its shape, or the number of entries+skewTableauWeight :: SkewTableau a -> Int+skewTableauWeight = skewPartitionWeight . skewTableauShape++instance HasWeight (SkewTableau a) where+  weight = skewTableauWeight++--------------------------------------------------------------------------------++-- | The dual of a skew tableau, that is, its mirror image to the main diagonal+dualSkewTableau :: forall a. SkewTableau a -> SkewTableau a+dualSkewTableau (SkewTableau axs) = SkewTableau (go axs) where++  go []  = []  +  go axs = case sub 0 axs of+    (0,[]) -> []+    this   -> this : go (strip axs)++  strip :: [(Int,[a])] -> [(Int,[a])]+  strip []            = []+  strip ((a,xs):rest) = if a>0 +    then (a-1,xs) : strip rest+    else case xs of+      []     -> []+      (z:zs) -> case zs of+        []      -> []+        _       -> (0,zs) : strip rest++  sub :: Int -> [(Int,[a])] -> (Int,[a])+  sub !b [] = (b,[])+  sub !b ((a,this):rest) = if a>0 +    then sub (b+1) rest  +    else (b,ys) where      +      ys = map head $ takeWhile (not . null) (this : map snd rest)++{-+test_dualSkewTableau :: [SkewTableau Int]+test_dualSkewTableau = bad where +  ps = allPartitions 11+  bad = [ st +        | p<-ps , q<-ps +        , (q `isSubPartitionOf` p) +        , let sp = mkSkewPartition (p,q) +        , let st = fillSkewPartitionWithRowWord sp [1..] +        , dualSkewTableau (dualSkewTableau st) /= st+        ]+-}++instance HasDuality (SkewTableau a) where+  dual = dualSkewTableau++--------------------------------------------------------------------------------+-- * Semistandard tableau++-- | A tableau is /semistandard/ if its entries are weekly increasing horizontally+-- and strictly increasing vertically+isSemiStandardSkewTableau :: SkewTableau Int -> Bool+isSemiStandardSkewTableau st@(SkewTableau axs) = weak && strict where+  weak   = and [ isWeaklyIncreasing   xs | (a,xs) <- axs ]+  strict = and [ isStrictlyIncreasing ys | (b,ys) <- bys ]+  SkewTableau bys = dualSkewTableau st++-- | A tableau is /standard/ if it is semistandard and its content is exactly @[1..n]@,+-- where @n@ is the weight.+isStandardSkewTableau :: SkewTableau Int -> Bool+isStandardSkewTableau st = isSemiStandardSkewTableau st && sort (skewTableauRowWord st) == [1..n] where+  n = skewTableauWeight st+  +--------------------------------------------------------------------------------++-- | All semi-standard skew tableaux filled with the numbers @[1..n]@+semiStandardSkewTableaux :: Int -> SkewPartition -> [SkewTableau Int]+semiStandardSkewTableaux n (SkewPartition abs) = map SkewTableau stuff where++  stuff = worker as bs ds (repeat 1) +  (as,bs) = unzip abs+  ds = diffSequence as+  +  -- | @worker inner outerMinusInner innerdiffs lowerbound+  worker :: [Int] -> [Int] -> [Int] -> [Int] -> [[(Int,[Int])]]+  worker (a:as) (b:bs) (d:ds) lb = [ (a,this):rest +                                   | this <- row b 1 lb +                                   , let lb' = (replicate d 1 ++ map (+1) this) +                                   , rest <- worker as bs ds lb' ] +  worker []     _      _      _  = [ [] ]++  -- @row length minimum lowerbound@+  row 0  _  _       = [[]]+  row _  _  []      = []+  row !k !m (!a:as) = [ x:xs | x <- [(max a m)..n] , xs <- row (k-1) x as ] ++{-+-- | from a sequence @[a1,a2,..,an]@ computes the sequence of differences+-- @[a1-a2,a2-a3,...,an-0]@+diffSequence :: [Int] -> [Int]+diffSequence = go where+  go (x:ys@(y:_)) = (x-y) : go ys +  go [x] = [x]+  go []  = []+-}++--------------------------------------------------------------------------------+-- * ASCII++-- | ASCII drawing of a skew tableau (using the English notation)+asciiSkewTableau :: Show a => SkewTableau a -> ASCII+asciiSkewTableau = asciiSkewTableau' "." EnglishNotation++asciiSkewTableau' +  :: Show a+  => String               -- ^ string representing the elements of the inner (unfilled) partition+  -> PartitionConvention  -- ^ orientation+  -> SkewTableau a +  -> ASCII+asciiSkewTableau' innerstr orient (SkewTableau axs) = tabulate (HRight,VTop) (HSepSpaces 1, VSepEmpty) stuff where+  stuff = case orient of+    EnglishNotation    -> es+    EnglishNotationCCW -> reverse (transpose es)+    FrenchNotation     -> reverse es+  inner = asciiFromString innerstr+  es = [ replicate a inner ++ map asciiShow xs | (a,xs) <- axs ]++instance Show a => DrawASCII (SkewTableau a) where+  ascii = asciiSkewTableau++--------------------------------------------------------------------------------+-- * Row \/ column words++-- | The reversed (right-to-left) rows, concatenated+skewTableauRowWord :: SkewTableau a -> [a]+skewTableauRowWord (SkewTableau axs) = concatMap (reverse . snd) axs++-- | The reversed (bottom-to-top) columns, concatenated+skewTableauColumnWord :: SkewTableau a -> [a]+skewTableauColumnWord = skewTableauRowWord . dualSkewTableau++-- | Fills a skew partition with content, in row word order +fillSkewPartitionWithRowWord :: SkewPartition -> [a] -> SkewTableau a+fillSkewPartitionWithRowWord (SkewPartition abs) xs = SkewTableau $ go abs xs where+  go ((b,a):rest) xs = let (ys,zs) = splitAt a xs in (b,reverse ys) : go rest zs+  go []           xs = []++-- | Fills a skew partition with content, in column word order +fillSkewPartitionWithColumnWord :: SkewPartition -> [a] -> SkewTableau a+fillSkewPartitionWithColumnWord shape content +  = dualSkewTableau +  $ fillSkewPartitionWithRowWord (dualSkewPartition shape) content++--------------------------------------------------------------------------------++-- | If the skew tableau's row word is a lattice word, we can make a partition from its content+skewTableauRowContent :: SkewTableau Int -> Maybe Partition+skewTableauRowContent (SkewTableau axs) = go Map.empty rowword where++  rowword = concatMap (reverse . snd) axs++  finish table = Partition (f 1) where+    f !i = case lkp i of+      0 -> []+      y -> y : f (i+1) +    lkp j = case Map.lookup j table of+      Just k  -> k+      Nothing -> 0++  go :: Map Int Int -> [Int] -> Maybe Partition+  go !table []     = Just (finish table)+  go !table (i:is) =+    if check i+      then go table' is+      else Nothing+    where+      table'  = Map.insertWith (+) i 1 table+      check j = j==1 || cnt (j-1) >= cnt j+      cnt j   = case Map.lookup j table' of+        Just k  -> k+        Nothing -> 0++--------------------------------------------------------------------------------+
+ Math/Combinat/Trees.hs view
@@ -0,0 +1,9 @@++module Math.Combinat.Trees+  ( module Math.Combinat.Trees.Binary+  , module Math.Combinat.Trees.Nary+  ) where++import Math.Combinat.Trees.Binary+import Math.Combinat.Trees.Nary+
+ Math/Combinat/Trees/Binary.hs view
@@ -0,0 +1,491 @@++-- | Binary trees, forests, etc. See:+--   Donald E. Knuth: The Art of Computer Programming, vol 4, pre-fascicle 4A.+--+-- For example, here are all the binary trees on 4 nodes:+--+-- <<svg/bintrees.svg>>+--++{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}+module Math.Combinat.Trees.Binary +  ( -- * Types+    BinTree(..)+  , leaf +  , graft+  , BinTree'(..)+  , forgetNodeDecorations+  , Paren(..)+  , parenthesesToString+  , stringToParentheses  +  , numberOfNodes+  , numberOfLeaves+    -- * Conversion to rose trees (@Data.Tree@)+  , toRoseTree , toRoseTree'+  , module Data.Tree +    -- * Enumerate leaves+  , enumerateLeaves_ +  , enumerateLeaves +  , enumerateLeaves'+    -- * Nested parentheses+  , nestedParentheses +  , randomNestedParentheses+  , nthNestedParentheses+  , countNestedParentheses+  , fasc4A_algorithm_P+  , fasc4A_algorithm_W+  , fasc4A_algorithm_U+    -- * Generating binary trees+  , binaryTrees+  , countBinaryTrees+  , binaryTreesNaive+  , randomBinaryTree+  , fasc4A_algorithm_R+    -- * ASCII drawing+  , asciiBinaryTree_+    -- * Graphviz drawing+  , Dot+  , graphvizDotBinTree+  , graphvizDotBinTree'+  , graphvizDotForest+  , graphvizDotTree  +    -- * Bijections+  , forestToNestedParentheses+  , forestToBinaryTree+  , nestedParenthesesToForest+  , nestedParenthesesToForestUnsafe+  , nestedParenthesesToBinaryTree+  , nestedParenthesesToBinaryTreeUnsafe+  , binaryTreeToForest+  , binaryTreeToNestedParentheses+  ) +  where++--------------------------------------------------------------------------------++import Control.Applicative+import Control.Monad+import Control.Monad.ST++import Data.Array+import Data.Array.ST+import Data.Array.Unsafe++import Data.List+import Data.Tree (Tree(..),Forest(..))++import Data.Monoid+import Data.Foldable (Foldable(foldMap))+import Data.Traversable (Traversable(traverse))++import System.Random++import Math.Combinat.Numbers (factorial,binomial)++import Math.Combinat.Trees.Graphviz +  ( Dot +  , graphvizDotBinTree , graphvizDotBinTree' +  , graphvizDotForest  , graphvizDotTree +  )+import Math.Combinat.Classes+import Math.Combinat.Helper+import Math.Combinat.ASCII as ASCII++--------------------------------------------------------------------------------+-- * Types++-- | A binary tree with leaves decorated with type @a@.+data BinTree a+  = Branch (BinTree a) (BinTree a)+  | Leaf a+  deriving (Eq,Ord,Show,Read)++leaf :: BinTree ()+leaf = Leaf ()++-- | The monadic join operation of binary trees+graft :: BinTree (BinTree a) -> BinTree a+graft = go where+  go (Branch l r) = Branch (go l) (go r)+  go (Leaf   t  ) = t ++--------------------------------------------------------------------------------++-- | A binary tree with leaves and internal nodes decorated +-- with types @a@ and @b@, respectively.+data BinTree' a b+  = Branch' (BinTree' a b) b (BinTree' a b)+  | Leaf' a+  deriving (Eq,Ord,Show,Read)++forgetNodeDecorations :: BinTree' a b -> BinTree a+forgetNodeDecorations = go where+  go (Branch' left _ right) = Branch (go left) (go right)+  go (Leaf'   decor       ) = Leaf decor ++--------------------------------------------------------------------------------++instance HasNumberOfNodes (BinTree a) where+  numberOfNodes = go where+    go (Leaf   _  ) = 0+    go (Branch l r) = go l + go r + 1++instance HasNumberOfLeaves (BinTree a) where+  numberOfLeaves = go where+    go (Leaf   _  ) = 1+    go (Branch l r) = go l + go r +++instance HasNumberOfNodes (BinTree' a b) where+  numberOfNodes = go where+    go (Leaf'   _    ) = 0+    go (Branch' l _ r) = go l + go r + 1++instance HasNumberOfLeaves (BinTree' a b) where+  numberOfLeaves = go where+    go (Leaf'   _    ) = 1+    go (Branch' l _ r) = go l + go r ++--------------------------------------------------------------------------------+-- * Enumerate leaves++-- | Enumerates the leaves a tree, starting from 0, ignoring old labels+enumerateLeaves_ :: BinTree a -> BinTree Int+enumerateLeaves_ = snd . go 0 where+  go !k t = case t of+    Leaf   _   -> (k+1 , Leaf k)+    Branch l r -> (k'', Branch l' r')  where+                    (k' ,l') = go k  l+                    (k'',r') = go k' r++-- | Enumerates the leaves a tree, starting from zero, and also returns the number of leaves+enumerateLeaves' :: BinTree a -> (Int, BinTree (a,Int))+enumerateLeaves' = go 0 where+  go !k t = case t of+    Leaf   y   -> (k+1 , Leaf (y,k))+    Branch l r -> (k'', Branch l' r')  where+                    (k' ,l') = go k  l+                    (k'',r') = go k' r++-- | Enumerates the leaves a tree, starting from zero+enumerateLeaves :: BinTree a -> BinTree (a,Int)+enumerateLeaves = snd . enumerateLeaves'++--------------------------------------------------------------------------------+-- * conversion to 'Data.Tree'++-- | Convert a binary tree to a rose tree (from "Data.Tree")+toRoseTree :: BinTree a -> Tree (Maybe a)+toRoseTree = go where+  go (Branch t1 t2) = Node Nothing  [go t1, go t2]+  go (Leaf x)       = Node (Just x) [] ++toRoseTree' :: BinTree' a b -> Tree (Either b a)+toRoseTree' = go where+  go (Branch' t1 y t2) = Node (Left  y) [go t1, go t2]+  go (Leaf' x)         = Node (Right x) [] +  +--------------------------------------------------------------------------------+-- instances+  +instance Functor BinTree where+  fmap f = go where+    go (Branch left right) = Branch (go left) (go right)+    go (Leaf x) = Leaf (f x)+  +instance Foldable BinTree where+  foldMap f = go where+    go (Leaf x) = f x+    go (Branch left right) = (go left) `mappend` (go right)  ++instance Traversable BinTree where+  traverse f = go where +    go (Leaf x) = Leaf <$> f x+    go (Branch left right) = Branch <$> go left <*> go right++instance Applicative BinTree where+  pure    = Leaf+  u <*> t = go u where+    go (Branch l r) = Branch (go l) (go r)+    go (Leaf   f  ) = fmap f t++instance Monad BinTree where+  return    = Leaf+  (>>=) t f = go t where+    go (Branch l r) = Branch (go l) (go r)+    go (Leaf   y  ) = f y ++--------------------------------------------------------------------------------+-- * Nested parentheses++data Paren +  = LeftParen +  | RightParen +  deriving (Eq,Ord,Show,Read)++parenToChar :: Paren -> Char+parenToChar LeftParen = '('+parenToChar RightParen = ')'++parenthesesToString :: [Paren] -> String+parenthesesToString = map parenToChar++stringToParentheses :: String -> [Paren]+stringToParentheses [] = []+stringToParentheses (x:xs) = p : stringToParentheses xs where+  p = case x of+    '(' -> LeftParen+    ')' -> RightParen+    _ -> error "stringToParentheses: invalid character"++--------------------------------------------------------------------------------+-- * Bijections++forestToNestedParentheses :: Forest a -> [Paren]+forestToNestedParentheses = forest where+  -- forest :: Forest a -> [Paren]+  forest = concatMap tree +  -- tree :: Tree a -> [Paren]+  tree (Node _ sf) = LeftParen : forest sf ++ [RightParen]++forestToBinaryTree :: Forest a -> BinTree ()+forestToBinaryTree = forest where+  -- forest :: Forest a -> BinTree ()+  forest = foldr Branch leaf . map tree +  -- tree :: Tree a -> BinTree ()+  tree (Node _ sf) = case sf of+    [] -> leaf+    _  -> forest sf +   +nestedParenthesesToForest :: [Paren] -> Maybe (Forest ())+nestedParenthesesToForest ps = +  case parseForest ps of +    (rest,forest) -> case rest of+      [] -> Just forest+      _  -> Nothing+  where  +    parseForest :: [Paren] -> ( [Paren] , Forest () )+    parseForest ps = unfoldEither parseTree ps+    parseTree :: [Paren] -> Either [Paren] ( [Paren] , Tree () )  +    parseTree orig@(LeftParen:ps) = let (rest,ts) = parseForest ps in case rest of+      (RightParen:qs) -> Right (qs, Node () ts)+      _ -> Left orig+    parseTree qs = Left qs++nestedParenthesesToForestUnsafe :: [Paren] -> Forest ()+nestedParenthesesToForestUnsafe = fromJust . nestedParenthesesToForest++nestedParenthesesToBinaryTree :: [Paren] -> Maybe (BinTree ())+nestedParenthesesToBinaryTree ps = +  case parseForest ps of +    (rest,forest) -> case rest of+      [] -> Just forest+      _  -> Nothing+  where  +    parseForest :: [Paren] -> ( [Paren] , BinTree () )+    parseForest ps = let (rest,ts) = unfoldEither parseTree ps in (rest , foldr Branch leaf ts)+    parseTree :: [Paren] -> Either [Paren] ( [Paren] , BinTree () )  +    parseTree orig@(LeftParen:ps) = let (rest,ts) = parseForest ps in case rest of+      (RightParen:qs) -> Right (qs, ts)+      _ -> Left orig+    parseTree qs = Left qs+    +nestedParenthesesToBinaryTreeUnsafe :: [Paren] -> BinTree ()+nestedParenthesesToBinaryTreeUnsafe = fromJust . nestedParenthesesToBinaryTree++binaryTreeToNestedParentheses :: BinTree a -> [Paren]+binaryTreeToNestedParentheses = worker where+  worker (Branch l r) = LeftParen : worker l ++ RightParen : worker r+  worker (Leaf _) = []++binaryTreeToForest :: BinTree a -> Forest ()+binaryTreeToForest = worker where+  worker (Branch l r) = Node () (worker l) : worker r+  worker (Leaf _) = []++--------------------------------------------------------------------------------+-- * Nested parentheses++-- | Generates all sequences of nested parentheses of length @2n@ in+-- lexigraphic order.+-- +-- Synonym for 'fasc4A_algorithm_P'.+--+nestedParentheses :: Int -> [[Paren]]+nestedParentheses = fasc4A_algorithm_P++-- | Synonym for 'fasc4A_algorithm_W'.+randomNestedParentheses :: RandomGen g => Int -> g -> ([Paren],g)+randomNestedParentheses = fasc4A_algorithm_W++-- | Synonym for 'fasc4A_algorithm_U'.+nthNestedParentheses :: Int -> Integer -> [Paren]+nthNestedParentheses = fasc4A_algorithm_U++countNestedParentheses :: Int -> Integer+countNestedParentheses = countBinaryTrees++-- | Generates all sequences of nested parentheses of length 2n.+-- Order is lexicographical (when right parentheses are considered +-- smaller then left ones).+-- Based on \"Algorithm P\" in Knuth, but less efficient because of+-- the \"idiomatic\" code.+fasc4A_algorithm_P :: Int -> [[Paren]]+fasc4A_algorithm_P 0 = [[]]+fasc4A_algorithm_P 1 = [[LeftParen,RightParen]]+fasc4A_algorithm_P n = unfold next ( start , [] ) where +  start = concat $ replicate n [RightParen,LeftParen]  -- already reversed!+   +  next :: ([Paren],[Paren]) -> ( [Paren] , Maybe ([Paren],[Paren]) )+  next ( (a:b:ls) , [] ) = next ( ls , b:a:[] )+  next ( lls@(l:ls) , rrs@(r:rs) ) = ( visit , new ) where+    visit = reverse lls ++ rrs+    new = +      {- debug (reverse ls,l,r,rs) $ -} +      case l of +        RightParen -> Just ( ls , LeftParen:RightParen:rs )+        LeftParen  -> +          {- debug ("---",reverse ls,l,r,rs) $ -}+          findj ( lls , [] ) ( reverse (RightParen:rs) , [] ) +  next _ = error "fasc4A_algorithm_P: fatal error shouldn't happen"++  findj :: ([Paren],[Paren]) -> ([Paren],[Paren]) -> Maybe ([Paren],[Paren])+  findj ( [] , _ ) _ = Nothing+  findj ( lls@(l:ls) , rs) ( xs , ys ) = +    {- debug ((reverse ls,l,rs),(reverse xs,ys)) $ -}+    case l of+      LeftParen  -> case xs of+        (a:_:as) -> findj ( ls, RightParen:rs ) ( as , LeftParen:a:ys )+        _ -> findj ( lls, [] ) ( reverse rs ++ xs , ys) +      RightParen -> Just ( reverse ys ++ xs ++ reverse (LeftParen:rs) ++ ls , [] )+    +-- | Generates a uniformly random sequence of nested parentheses of length 2n.    +-- Based on \"Algorithm W\" in Knuth.+fasc4A_algorithm_W :: RandomGen g => Int -> g -> ([Paren],g)+fasc4A_algorithm_W n' rnd = worker (rnd,n,n,[]) where+  n = fromIntegral n' :: Integer  +  -- the numbers we use are of order n^2, so for n >> 2^16 +  -- on a 32 bit machine, we need big integers.+  worker :: RandomGen g => (g,Integer,Integer,[Paren]) -> ([Paren],g)+  worker (rnd,_,0,parens) = (parens,rnd)+  worker (rnd,p,q,parens) = +    if x<(q+1)*(q-p) +      then worker (rnd' , p   , q-1 , LeftParen :parens)+      else worker (rnd' , p-1 , q   , RightParen:parens)+    where +      (x,rnd') = randomR ( 0 , (q+p)*(q-p+1)-1 ) rnd++-- | Nth sequence of nested parentheses of length 2n. +-- The order is the same as in 'fasc4A_algorithm_P'.+-- Based on \"Algorithm U\" in Knuth.+fasc4A_algorithm_U +  :: Int               -- ^ n+  -> Integer           -- ^ N; should satisfy 1 <= N <= C(n) +  -> [Paren]+fasc4A_algorithm_U n' bign0 = reverse $ worker (bign0,c0,n,n,[]) where+  n = fromIntegral n' :: Integer+  c0 = foldl f 1 [2..n]  +  f c p = ((4*p-2)*c) `div` (p+1) +  worker :: (Integer,Integer,Integer,Integer,[Paren]) -> [Paren]+  worker (_   ,_,_,0,parens) = parens+  worker (bign,c,p,q,parens) = +    if bign <= c' +      then worker (bign    , c'   , p   , q-1 , RightParen:parens)+      else worker (bign-c' , c-c' , p-1 , q   , LeftParen :parens)+    where+      c' = ((q+1)*(q-p)*c) `div` ((q+p)*(q-p+1))+  +--------------------------------------------------------------------------------+-- * Generating binary trees++-- | Generates all binary trees with @n@ nodes. +--   At the moment just a synonym for 'binaryTreesNaive'.+binaryTrees :: Int -> [BinTree ()]+binaryTrees = binaryTreesNaive++-- | # = Catalan(n) = \\frac { 1 } { n+1 } \\binom { 2n } { n }.+--+-- This is also the counting function for forests and nested parentheses.+countBinaryTrees :: Int -> Integer+countBinaryTrees n = binomial (2*n) n `div` (1 + fromIntegral n)+    +-- | Generates all binary trees with n nodes. The naive algorithm.+binaryTreesNaive :: Int -> [BinTree ()]+binaryTreesNaive 0 = [ leaf ]+binaryTreesNaive n = +  [ Branch l r +  | i <- [0..n-1] +  , l <- binaryTreesNaive i +  , r <- binaryTreesNaive (n-1-i) +  ]++-- | Generates an uniformly random binary tree, using 'fasc4A_algorithm_R'.+randomBinaryTree :: RandomGen g => Int -> g -> (BinTree (), g)+randomBinaryTree n rnd = (tree,rnd') where+  (decorated,rnd') = fasc4A_algorithm_R n rnd      +  tree = fmap (const ()) $ forgetNodeDecorations decorated++-- | Grows a uniformly random binary tree. +-- \"Algorithm R\" (Remy's procudere) in Knuth.+-- Nodes are decorated with odd numbers, leaves with even numbers (from the+-- set @[0..2n]@). Uses mutable arrays internally.+fasc4A_algorithm_R :: RandomGen g => Int -> g -> (BinTree' Int Int, g)+fasc4A_algorithm_R n0 rnd = res where+  res = runST $ do+    ar <- newArray (0,2*n0) 0+    rnd' <- worker rnd 1 ar+    links <- Data.Array.Unsafe.unsafeFreeze ar+    return (toTree links, rnd')+  toTree links = f (links!0) where+    f i = if odd i +      then Branch' (f $ links!i) i (f $ links!(i+1)) +      else Leaf' i  +  worker :: RandomGen g => g -> Int -> STUArray s Int Int -> ST s g+  worker rnd n ar = do +    if n > n0+      then return rnd+      else do+        writeArray ar (n2-b)   n2+        lk <- readArray ar k+        writeArray ar (n2-1+b) lk+        writeArray ar k        (n2-1)+        worker rnd' (n+1) ar      +    where  +      n2 = n+n+      (x,rnd') = randomR (0,4*n-3) rnd+      (k,b) = x `divMod` 2+      +--------------------------------------------------------------------------------      +-- * ASCII drawing  ++-- | Draws a binary tree in ASCII, ignoring node labels.+--+-- Example:+--+-- > autoTabulate RowMajor (Right 5) $ map asciiBinaryTree_ $ binaryTrees 4+--+asciiBinaryTree_ :: BinTree a -> ASCII+asciiBinaryTree_ = ASCII.asciiFromLines . fst . go where++  go :: BinTree a -> ([String],Int)+  go (Leaf x) = ([],0)+  go (Branch t1 t2) = ( new , j1+m ) where+    (ls1,j1) = go t1+    (ls2,j2) = go t2+    w1 = blockWidth ls1+    w2 = blockWidth ls2+    m = max 1 $ (w1-j1+j2+2) `div` 2+    s = 2*m - (w1-j1+j2)+    spaces = [replicate s ' ']+    ls = hConcatLines [ ls1 , spaces , ls2 ]+    top = [ replicate (j1+m-i) ' ' ++ "/" ++ replicate (2*(i-1)) ' ' ++ "\\" | i<-[1..m] ]+    new = mkLinesUniformWidth $ vConcatLines [ top , ls ] +        +  blockWidth ls = case ls of+    (l:_) -> length l+    []    -> 0++instance DrawASCII (BinTree ()) where+  ascii = asciiBinaryTree_ ++--------------------------------------------------------------------------------      
+ Math/Combinat/Trees/Binary.hs-boot view
@@ -0,0 +1,22 @@+++module Math.Combinat.Trees.Binary where++--------------------------------------------------------------------------------++import Data.Tree ( Tree(..) , Forest(..) )++--------------------------------------------------------------------------------++-- | A binary tree with leaves decorated with type @a@.+data BinTree a+  = Branch (BinTree a) (BinTree a)+  | Leaf a++-- | A binary tree with leaves and internal nodes decorated +-- with types @a@ and @b@, respectively.+data BinTree' a b+  = Branch' (BinTree' a b) b (BinTree' a b)+  | Leaf' a++--------------------------------------------------------------------------------
+ Math/Combinat/Trees/Graphviz.hs view
@@ -0,0 +1,115 @@++-- | Creates graphviz @.dot@ files from trees.++module Math.Combinat.Trees.Graphviz +  ( Dot+  , graphvizDotBinTree+  , graphvizDotBinTree'+  , graphvizDotForest+  , graphvizDotTree+  )+  where++--------------------------------------------------------------------------------++import Data.Tree++import Control.Applicative++import {-# SOURCE #-} Math.Combinat.Trees.Binary ( BinTree(..)         , BinTree'(..)          )+import {-# SOURCE #-} Math.Combinat.Trees.Nary   ( addUniqueLabelsTree , addUniqueLabelsForest )++--------------------------------------------------------------------------------++type Dot = String++digraphBracket :: String -> [String] -> String   +digraphBracket name lines = +  "digraph " ++ name ++ " {\n" ++ +  concatMap (\xs -> "  "++xs++"\n") lines    +  ++ "}\n"+  +--------------------------------------------------------------------------------++graphvizDotBinTree :: Show a => String -> BinTree a -> Dot+graphvizDotBinTree graphname tree = +  digraphBracket graphname $ binTreeDot' tree++graphvizDotBinTree' :: (Show a, Show b) => String -> BinTree' a b -> Dot+graphvizDotBinTree' graphname tree = +  digraphBracket graphname $ binTree'Dot' tree+  +binTreeDot' :: Show a => BinTree a -> [String]+binTreeDot' tree = lines where+  lines = worker (0::Int) "r" tree +  name path = "node_"++path+  worker depth path (Leaf x) = +    [ name path ++ "[shape=box,label=\"" ++ show x ++ "\"" ++ "];" ]+  worker depth path (Branch left right) +    = [vertex,leftedge,rightedge] ++ +      worker (depth+1) ('l':path) left ++ +      worker (depth+1) ('r':path) right+    where +      vertex = name path ++ "[shape=circle,style=filled,height=0.25,label=\"\"];"+      leftedge  = name path ++ " -> " ++ name ('l':path) ++ "[tailport=sw];"+      rightedge = name path ++ " -> " ++ name ('r':path) ++ "[tailport=se];"++binTree'Dot' :: (Show a, Show b) => BinTree' a b -> [String]+binTree'Dot' tree = lines where+  lines = worker (0::Int) "r" tree +  name path = "node_"++path+  worker depth path (Leaf' x) = +    [ name path ++ "[shape=box,label=\"" ++ show x ++ "\"" ++ "];" ]+  worker depth path (Branch' left y right) +    = [vertex,leftedge,rightedge] ++ +      worker (depth+1) ('l':path) left ++ +      worker (depth+1) ('r':path) right+    where +      vertex = name path ++ "[shape=ellipse,label=\"" ++ show y ++ "\"];"+      leftedge  = name path ++ " -> " ++ name ('l':path) ++ "[tailport=sw];"+      rightedge = name path ++ " -> " ++ name ('r':path) ++ "[tailport=se];"++--------------------------------------------------------------------------------+    +-- | Generates graphviz @.dot@ file from a forest. The first argument tells whether+-- to make the individual trees clustered subgraphs; the second is the name of the+-- graph.+graphvizDotForest+  :: Show a +  => Bool        -- ^ make the individual trees clustered subgraphs+  -> Bool        -- ^ reverse the direction of the arrows+  -> String      -- ^ name of the graph+  -> Forest a +  -> Dot+graphvizDotForest clustered revarrows graphname forest = digraphBracket graphname lines where+  lines = concat $ zipWith cluster [(1::Int)..] (addUniqueLabelsForest forest) +  name unique = "node_"++show unique+  cluster j tree = let treelines = worker (0::Int) tree in case clustered of+    False -> treelines+    True  -> ("subgraph cluster_"++show j++" {") : map ("  "++) treelines ++ ["}"] +  worker depth (Node (label,unique) subtrees) = vertex : edges ++ concatMap (worker (depth+1)) subtrees where+    vertex = name unique ++ "[label=\"" ++ show label ++ "\"" ++ "];"+    edges = map edge subtrees+    edge (Node (_,unique') _) = if not revarrows +      then name unique  ++ " -> " ++ name unique'   +      else name unique' ++ " -> " ++ name unique+      +-- | Generates graphviz @.dot@ file from a tree. The first argument is+-- the name of the graph.+graphvizDotTree+  :: Show a +  => Bool     -- ^ reverse the direction of the arrow+  -> String   -- ^ name of the graph+  -> Tree a +  -> Dot+graphvizDotTree revarrows graphname tree = digraphBracket graphname lines where+  lines = worker (0::Int) (addUniqueLabelsTree tree) +  name unique = "node_"++show unique+  worker depth (Node (label,unique) subtrees) = vertex : edges ++ concatMap (worker (depth+1)) subtrees where+    vertex = name unique ++ "[label=\"" ++ show label ++ "\"" ++ "];"+    edges = map edge subtrees+    edge (Node (_,unique') _) = if not revarrows +      then name unique  ++ " -> " ++ name unique'   +      else name unique' ++ " -> " ++ name unique++--------------------------------------------------------------------------------
+ Math/Combinat/Trees/Nary.hs view
@@ -0,0 +1,432 @@++-- | N-ary trees.++{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE TypeSynonymInstances #-}+module Math.Combinat.Trees.Nary+  (+    -- * Types+    module Data.Tree+  , Tree(..)+    -- * Regular trees+  , ternaryTrees+  , regularNaryTrees+  , semiRegularTrees+  , countTernaryTrees+  , countRegularNaryTrees+    -- * \"derivation trees\"+  , derivTrees+    -- * ASCII drawings+  , asciiTreeVertical_+  , asciiTreeVertical+  , asciiTreeVerticalLeavesOnly+    -- * Graphviz drawing+  , Dot+  , graphvizDotTree+  , graphvizDotForest+    -- * Classifying nodes+  , classifyTreeNode+  , isTreeLeaf  , isTreeNode+  , isTreeLeaf_ , isTreeNode_+  , treeNodeNumberOfChildren+    -- * Counting nodes+  , countTreeNodes+  , countTreeLeaves+  , countTreeLabelsWith+  , countTreeNodesWith+    -- * Left and right spines+  , leftSpine  , leftSpine_+  , rightSpine , rightSpine_+  , leftSpineLength , rightSpineLength+    -- * Unique labels+  , addUniqueLabelsTree+  , addUniqueLabelsForest+  , addUniqueLabelsTree_+  , addUniqueLabelsForest_+    -- * Labelling by depth+  , labelDepthTree+  , labelDepthForest+  , labelDepthTree_+  , labelDepthForest_+    -- * Labelling by number of children+  , labelNChildrenTree+  , labelNChildrenForest+  , labelNChildrenTree_+  , labelNChildrenForest_++  ) where+++--------------------------------------------------------------------------------++import           Data.List+import           Data.Tree++import           Control.Applicative++--import Control.Monad.State+import           Control.Monad.Trans.State+import           Data.Traversable                  (traverse)++import           Math.Combinat.Compositions        (compositions)+import           Math.Combinat.Numbers             (binomial, factorial)+import           Math.Combinat.Partitions.Multiset (partitionMultiset)+import           Math.Combinat.Sets                (listTensor)++import           Math.Combinat.Trees.Graphviz      (Dot, graphvizDotForest,+                                                    graphvizDotTree)++import           Math.Combinat.ASCII               as ASCII+import           Math.Combinat.Classes+import           Math.Combinat.Helper++--------------------------------------------------------------------------------++instance HasNumberOfNodes (Tree a) where+  numberOfNodes = go where+    go (Node label subforest) = if null subforest+      then 0+      else 1 + sum' (map go subforest)++instance HasNumberOfLeaves (Tree a) where+  numberOfLeaves = go where+    go (Node label subforest) = if null subforest+      then 1+      else sum' (map go subforest)++--------------------------------------------------------------------------------++-- | @regularNaryTrees d n@ returns the list of (rooted) trees on @n@ nodes where each+-- node has exactly @d@ children. Note that the leaves do not count in @n@.+-- Naive algorithm.+regularNaryTrees+  :: Int         -- ^ degree = number of children of each node+  -> Int         -- ^ number of nodes+  -> [Tree ()]+regularNaryTrees d = go where+  go 0 = [ Node () [] ]+  go n = [ Node () cs+         | is <- compositions d (n-1)+         , cs <- listTensor [ go i | i<-is ]+         ]++-- | Ternary trees on @n@ nodes (synonym for @regularNaryTrees 3@)+ternaryTrees :: Int -> [Tree ()]+ternaryTrees = regularNaryTrees 3++-- | We have+--+-- > length (regularNaryTrees d n) == countRegularNaryTrees d n == \frac {1} {(d-1)n+1} \binom {dn} {n}+--+countRegularNaryTrees :: (Integral a, Integral b) => a -> b -> Integer+countRegularNaryTrees d n = binomial (dd*nn) nn `div` ((dd-1)*nn+1) where+  dd = fromIntegral d :: Integer+  nn = fromIntegral n :: Integer++-- | @\# = \\frac {1} {(2n+1} \\binom {3n} {n}@+countTernaryTrees :: Integral a => a -> Integer+countTernaryTrees = countRegularNaryTrees (3::Int)++--------------------------------------------------------------------------------++-- | All trees on @n@ nodes where the number of children of all nodes is+-- in element of the given set. Example:+--+-- > autoTabulate RowMajor (Right 5) $ map asciiTreeVertical+-- >                                 $ map labelNChildrenTree_+-- >                                 $ semiRegularTrees [2,3] 2+-- >+-- > [ length $ semiRegularTrees [2,3] n | n<-[0..] ] == [1,2,10,66,498,4066,34970,312066,2862562,26824386,...]+--+-- The latter sequence is A027307 in OEIS: <https://oeis.org/A027307>+--+-- Remark: clearly, we have+--+-- > semiRegularTrees [d] n == regularNaryTrees d n+--+--+semiRegularTrees+  :: [Int]         -- ^ set of allowed number of children+  -> Int           -- ^ number of nodes+  -> [Tree ()]+semiRegularTrees []    n = if n==0 then [Node () []] else []+semiRegularTrees dset_ n =+  if head dset >=1+    then go n+    else error "semiRegularTrees: expecting a list of positive integers"+  where+    dset = map head $ group $ sort $ dset_++    go 0 = [ Node () [] ]+    go n = [ Node () cs+           | d <- dset+           , is <- compositions d (n-1)+           , cs <- listTensor [ go i | i<-is ]+           ]++{-++NOTES:++A006318 = [ length $ semiRegularTrees [1,2] n | n<-[0..] ] == [1,2,6,22,90,394,1806,8558,41586,206098,1037718.. ]+??      = [ length $ semiRegularTrees [1,3] n | n<-[0..] ] == [1,2,8,44,280,1936,14128,107088,834912,6652608 .. ]+??      = [ length $ semiRegularTrees [1,4] n | n<-[0..] ] == [1,2,10,74,642,6082,60970,635818,6826690++A027307 = [ length $ semiRegularTrees [2,3] n | n<-[0..] ] == [1,2,10,66,498,4066,34970,312066,2862562,26824386,...]+A219534 = [ length $ semiRegularTrees [2,4] n | n<-[0..] ] == [1,2,12,100,968,10208,113792,1318832 ..]+??      = [ length $ semiRegularTrees [2,5] n | n<-[0..] ] == [1,2,14,142,1690,21994,303126,4348102 ..]++A144097 = [ length $ semiRegularTrees [3,4] n | n<-[0..] ] == [1,2,14,134,1482,17818,226214,2984206,40503890..]++A107708 = [ length $ semiRegularTrees [1,2,3]   n | n<-[0..] ] == [1,3,18,144,1323,13176,138348,1507977 .. ]+??      = [ length $ semiRegularTrees [1,2,3,4] n | n<-[0..] ] == [1,4,40,560,9120,161856,3036800,59242240 .. ]++-}++--------------------------------------------------------------------------------++-- | Vertical ASCII drawing of a tree, without labels. Example:+--+-- > autoTabulate RowMajor (Right 5) $ map asciiTreeVertical_ $ regularNaryTrees 2 4+--+-- Nodes are denoted by @\@@, leaves by @*@.+--+asciiTreeVertical_ :: Tree a -> ASCII+asciiTreeVertical_ tree = ASCII.asciiFromLines (go tree) where+  go :: Tree b -> [String]+  go (Node _ cs) = case cs of+    [] -> ["-*"]+    _  -> concat $ mapWithFirstLast f $ map go cs++  f :: Bool -> Bool -> [String] -> [String]+  f bf bl (l:ls) = let indent = if bl           then "  "  else  "| "+                       gap    = if bl           then []    else ["| "]+                       branch = if bl && not bf+                                  then "\\-"+                                  else if bf then "@-"+                                             else "+-"+                   in  (branch++l) : map (indent++) ls ++ gap++instance DrawASCII (Tree ()) where+  ascii = asciiTreeVertical_++-- | Prints all labels. Example:+--+-- > asciiTreeVertical $ addUniqueLabelsTree_ $ (regularNaryTrees 3 9) !! 666+--+-- Nodes are denoted by @(label)@, leaves by @label@.+--+asciiTreeVertical :: Show a => Tree a -> ASCII+asciiTreeVertical tree = ASCII.asciiFromLines (go tree) where+  go :: Show b => Tree b -> [String]+  go (Node x cs) = case cs of+    [] -> ["-- " ++ show x]+    _  -> concat $ mapWithFirstLast (f (show x)) $ map go cs++  f :: String -> Bool -> Bool -> [String] -> [String]+  f label bf bl (l:ls) =+        let spaces = (map (const ' ') label  )+            dashes = (map (const '-') spaces )+            indent = if bl then "  " ++spaces++"  " else  " |" ++ spaces ++ "  "+            gap    = if bl then []                  else [" |" ++ spaces ++ "  "]+            branch = if bl && not bf+                           then " \\"++dashes++"--"+                           else if bf+                             then "-(" ++ label  ++ ")-"+                             else " +" ++ dashes ++ "--"+        in  (branch++l) : map (indent++) ls ++ gap++-- | Prints the labels for the leaves, but not for the  nodes.+asciiTreeVerticalLeavesOnly :: Show a => Tree a -> ASCII+asciiTreeVerticalLeavesOnly tree = ASCII.asciiFromLines (go tree) where+  go :: Show b => Tree b -> [String]+  go (Node x cs) = case cs of+    [] -> ["- " ++ show x]+    _  -> concat $ mapWithFirstLast f $ map go cs++  f :: Bool -> Bool -> [String] -> [String]+  f bf bl (l:ls) = let indent = if bl           then "  "  else  "| "+                       gap    = if bl           then []    else ["| "]+                       branch = if bl && not bf+                                  then "\\-"+                                  else if bf then "@-"+                                             else "+-"+                   in  (branch++l) : map (indent++) ls ++ gap++--------------------------------------------------------------------------------++-- | The leftmost spine (the second element of the pair is the leaf node)+leftSpine  :: Tree a -> ([a],a)+leftSpine = go where+  go (Node x cs) = case cs of+    [] -> ([],x)+    _  -> let (xs,y) = go (head cs) in (x:xs,y)++rightSpine  :: Tree a -> ([a],a)+rightSpine = go where+  go (Node x cs) = case cs of+    [] -> ([],x)+    _  -> let (xs,y) = go (last cs) in (x:xs,y)++-- | The leftmost spine without the leaf node+leftSpine_  :: Tree a -> [a]+leftSpine_ = go where+  go (Node x cs) = case cs of+    [] -> []+    _  -> x : go (head cs)++rightSpine_ :: Tree a -> [a]+rightSpine_ = go where+  go (Node x cs) = case cs of+    [] -> []+    _  -> x : go (last cs)++-- | The length (number of edges) on the left spine+--+-- > leftSpineLength tree == length (leftSpine_ tree)+--+leftSpineLength  :: Tree a -> Int+leftSpineLength = go 0 where+  go n (Node x cs) = case cs of+    [] -> n+    _  -> go (n+1) (head cs)++rightSpineLength :: Tree a -> Int+rightSpineLength = go 0 where+  go n (Node x cs) = case cs of+    [] -> n+    _  -> go (n+1) (last cs)++--------------------------------------------------------------------------------++-- | 'Left' is leaf, 'Right' is node+classifyTreeNode :: Tree a -> Either a a+classifyTreeNode (Node x cs) = case cs of { [] -> Left x ; _ -> Right x }++isTreeLeaf :: Tree a -> Maybe a+isTreeLeaf (Node x cs) = case cs of { [] -> Just x ; _ -> Nothing }++isTreeNode :: Tree a -> Maybe a+isTreeNode (Node x cs) = case cs of { [] -> Nothing ; _ -> Just x }++isTreeLeaf_ :: Tree a -> Bool+isTreeLeaf_ (Node x cs) = case cs of { [] -> True ; _ -> False }++isTreeNode_ :: Tree a -> Bool+isTreeNode_ (Node x cs) = case cs of { [] -> False ; _ -> True }++treeNodeNumberOfChildren :: Tree a -> Int+treeNodeNumberOfChildren (Node _ cs) = length cs++--------------------------------------------------------------------------------+-- counting++countTreeNodes :: Tree a -> Int+countTreeNodes = go where+  go (Node x cs) = case cs of+    [] -> 0+    _  -> 1 + sum (map go cs)++countTreeLeaves :: Tree a -> Int+countTreeLeaves = go where+  go (Node x cs) = case cs of+    [] -> 1+    _  -> sum (map go cs)++countTreeLabelsWith :: (a -> Bool) -> Tree a -> Int+countTreeLabelsWith f = go where+  go (Node label cs) = (if f label then 1 else 0) + sum (map go cs)++countTreeNodesWith :: (Tree a -> Bool) -> Tree a -> Int+countTreeNodesWith f = go where+  go node@(Node _ cs) = (if f node then 1 else 0) + sum (map go cs)++--------------------------------------------------------------------------------++-- | Adds unique labels to the nodes (including leaves) of a 'Tree'.+addUniqueLabelsTree :: Tree a -> Tree (a,Int)+addUniqueLabelsTree tree = head (addUniqueLabelsForest [tree])++-- | Adds unique labels to the nodes (including leaves) of a 'Forest'+addUniqueLabelsForest :: Forest a -> Forest (a,Int)+addUniqueLabelsForest forest = evalState (mapM globalAction forest) 1 where+  globalAction tree =+    unwrapMonad $ traverse localAction tree+  localAction x = WrapMonad $ do+    i <- get+    put (i+1)+    return (x,i)++addUniqueLabelsTree_ :: Tree a -> Tree Int+addUniqueLabelsTree_ = fmap snd . addUniqueLabelsTree++addUniqueLabelsForest_ :: Forest a -> Forest Int+addUniqueLabelsForest_ = map (fmap snd) . addUniqueLabelsForest++--------------------------------------------------------------------------------++-- | Attaches the depth to each node. The depth of the root is 0.+labelDepthTree :: Tree a -> Tree (a,Int)+labelDepthTree tree = worker 0 tree where+  worker depth (Node label subtrees) = Node (label,depth) (map (worker (depth+1)) subtrees)++labelDepthForest :: Forest a -> Forest (a,Int)+labelDepthForest forest = map labelDepthTree forest++labelDepthTree_ :: Tree a -> Tree Int+labelDepthTree_ = fmap snd . labelDepthTree++labelDepthForest_ :: Forest a -> Forest Int+labelDepthForest_ = map (fmap snd) . labelDepthForest++--------------------------------------------------------------------------------++-- | Attaches the number of children to each node.+labelNChildrenTree :: Tree a -> Tree (a,Int)+labelNChildrenTree (Node x subforest) =+  Node (x, length subforest) (map labelNChildrenTree subforest)++labelNChildrenForest :: Forest a -> Forest (a,Int)+labelNChildrenForest forest = map labelNChildrenTree forest++labelNChildrenTree_ :: Tree a -> Tree Int+labelNChildrenTree_ = fmap snd . labelNChildrenTree++labelNChildrenForest_ :: Forest a -> Forest Int+labelNChildrenForest_ = map (fmap snd) . labelNChildrenForest++--------------------------------------------------------------------------------++-- | Computes the set of equivalence classes of rooted trees (in the+-- sense that the leaves of a node are /unordered/)+-- with @n = length ks@ leaves where the set of heights of+-- the leaves matches the given set of numbers.+-- The height is defined as the number of /edges/ from the leaf to the root.+--+-- TODO: better name?+derivTrees :: [Int] -> [Tree ()]+derivTrees xs = derivTrees' (map (+1) xs)++derivTrees' :: [Int] -> [Tree ()]+derivTrees' [] = []+derivTrees' [n] =+  if n>=1+    then [unfoldTree f 1]+    else []+  where+    f k = if k<n then ((),[k+1]) else ((),[])+derivTrees' ks =+  if and (map (>0) ks)+    then+      [ Node () sub+      | part <- parts+      , let subtrees = map g part+      , sub <- listTensor subtrees+      ]+    else []+  where+    parts = partitionMultiset ks+    g xs = derivTrees' (map (\x->x-1) xs)++--------------------------------------------------------------------------------+
+ Math/Combinat/Trees/Nary.hs-boot view
@@ -0,0 +1,16 @@++module Math.Combinat.Trees.Nary where++--------------------------------------------------------------------------------++import Data.Tree++--------------------------------------------------------------------------------++addUniqueLabelsTree   :: Tree   a -> Tree   (a,Int) +addUniqueLabelsForest :: Forest a -> Forest (a,Int) ++addUniqueLabelsTree_   :: Tree   a -> Tree   Int+addUniqueLabelsForest_ :: Forest a -> Forest Int++--------------------------------------------------------------------------------
+ Math/Combinat/Tuples.hs view
@@ -0,0 +1,61 @@++-- | Tuples.++module Math.Combinat.Tuples where++import Math.Combinat.Helper++-------------------------------------------------------+-- Tuples++-- | \"Tuples\" fitting into a give shape. The order is lexicographic, that is,+--+-- > sort ts == ts where ts = tuples' shape+--+--   Example: +--+-- > tuples' [2,3] = +-- >   [[0,0],[0,1],[0,2],[0,3],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3]]+--+tuples' :: [Int] -> [[Int]]+tuples' [] = [[]]+tuples' (s:ss) = [ x:xs | x <- [0..s] , xs <- tuples' ss ] ++-- | positive \"tuples\" fitting into a give shape.+tuples1' :: [Int] -> [[Int]]+tuples1' [] = [[]]+tuples1' (s:ss) = [ x:xs | x <- [1..s] , xs <- tuples1' ss ] ++-- | # = \\prod_i (m_i + 1)+countTuples' :: [Int] -> Integer+countTuples' shape = product $ map f shape where+  f k = 1 + fromIntegral k++-- | # = \\prod_i m_i+countTuples1' :: [Int] -> Integer+countTuples1' shape = product $ map fromIntegral shape++tuples +  :: Int    -- ^ length (width)+  -> Int    -- ^ maximum (height)+  -> [[Int]]+tuples len k = tuples' (replicate len k)++tuples1 +  :: Int    -- ^ length (width)+  -> Int    -- ^ maximum (height)+  -> [[Int]]+tuples1 len k = tuples1' (replicate len k)++-- | # = (m+1) ^ len+countTuples :: Int -> Int -> Integer+countTuples len k = (1 + fromIntegral k) ^ len++-- | # = m ^ len+countTuples1 :: Int -> Int -> Integer+countTuples1 len k = fromIntegral k ^ len++binaryTuples :: Int -> [[Bool]]+binaryTuples len = map (map intToBool) (tuples len 1)++-------------------------------------------------------
+ Math/Combinat/TypeLevel.hs view
@@ -0,0 +1,117 @@++-- | Type-level hackery.+--+-- This module is used for groups whose parameters are encoded as type-level natural numbers,+-- for example finite cyclic groups, free groups, symmetric groups and braid groups.+--++{-# LANGUAGE PolyKinds, DataKinds, KindSignatures, ScopedTypeVariables, +             ExistentialQuantification, Rank2Types #-}++module Math.Combinat.TypeLevel +  ( -- * Proxy+    Proxy(..)+  , proxyUndef+  , proxyOf+  , proxyOf1+  , proxyOf2+  , asProxyTypeOf   -- defined in Data.Proxy+  , asProxyTypeOf1+    -- * Type-level naturals as type arguments+  , typeArg +  , iTypeArg+    -- * Hiding the type parameter+  , Some (..)+  , withSome , withSomeM+  , selectSome , selectSomeM+  , withSelected , withSelectedM+  )+  where++--------------------------------------------------------------------------------++import Data.Proxy ( Proxy(..) , asProxyTypeOf )+import GHC.TypeLits++import Math.Combinat.Numbers.Primes ( isProbablyPrime )++--------------------------------------------------------------------------------+-- * Proxy++proxyUndef :: Proxy a -> a+proxyUndef _ = error "proxyUndef"++proxyOf :: a -> Proxy a+proxyOf _ = Proxy++proxyOf1 :: f a -> Proxy a+proxyOf1 _ = Proxy++proxyOf2 :: g (f a) -> Proxy a+proxyOf2 _ = Proxy++asProxyTypeOf1 :: f a -> Proxy a -> f a +asProxyTypeOf1 y _ = y++--------------------------------------------------------------------------------+-- * Type-level naturals as type arguments++typeArg :: KnownNat n => f (n :: Nat) -> Integer+typeArg = natVal . proxyOf1++iTypeArg :: KnownNat n => f (n :: Nat) -> Int+iTypeArg = fromIntegral . typeArg++--------------------------------------------------------------------------------+-- * Hiding the type parameter++-- | Hide the type parameter of a functor. Example: @Some Braid@+data Some f = forall n. KnownNat n => Some (f n)++-- | Uses the value inside a 'Some'+withSome :: Some f -> (forall n. KnownNat n => f n -> a) -> a+withSome some polyFun = case some of { Some stuff -> polyFun stuff }++-- | Monadic version of 'withSome'+withSomeM :: Monad m => Some f -> (forall n. KnownNat n => f n -> m a) -> m a+withSomeM some polyAct = case some of { Some stuff -> polyAct stuff }++-- | Given a polymorphic value, we select at run time the+-- one specified by the second argument+selectSome :: Integral int => (forall n. KnownNat n => f n) -> int -> Some f+selectSome poly n = case someNatVal (fromIntegral n :: Integer) of+  Nothing   -> error "selectSome: not a natural number"+  Just snat -> case snat of+    SomeNat pxy -> Some (asProxyTypeOf1 poly pxy)++-- | Monadic version of 'selectSome'+selectSomeM :: forall m f int. (Integral int, Monad m) => (forall n. KnownNat n => m (f n)) -> int -> m (Some f)+selectSomeM runPoly n = case someNatVal (fromIntegral n :: Integer) of+  Nothing   -> error "selectSomeM: not a natural number"+  Just snat -> case snat of+    SomeNat pxy -> do+      poly <- runPoly +      return $ Some (asProxyTypeOf1 poly pxy)++-- | Combination of 'selectSome' and 'withSome': we make a temporary structure+-- of the given size, but we immediately consume it.+withSelected +  :: Integral int +  => (forall n. KnownNat n => f n -> a) +  -> (forall n. KnownNat n => f n) +  -> int +  -> a+withSelected f x n = withSome (selectSome x n) f++-- | (Half-)monadic version of 'withSelected'+withSelectedM +  :: forall m f int a. (Integral int, Monad m) +  => (forall n. KnownNat n => f n -> a) +  -> (forall n. KnownNat n => m (f n)) +  -> int +  -> m a+withSelectedM f mx n = do +  s <- selectSomeM mx n+  return (withSome s f)++--------------------------------------------------------------------------------
+ Setup.lhs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ combinat-compat.cabal view
@@ -0,0 +1,102 @@+cabal-version: 1.18+name: combinat-compat+version: 0.2.8.2+license: BSD3+license-file: LICENSE+copyright: (c) 2008-2016 Balazs Komuves+maintainer: bkomuves (plus) hackage (at) gmail (dot) com+author: Balazs Komuves+stability: Experimental+tested-with: ghc ==8.4.1+homepage: http://code.haskell.org/~bkomuves/+synopsis: Generate and manipulate various combinatorial objects.+description:+    A collection of functions to generate, count, manipulate+    and visualize all kinds of combinatorial objects like+    partitions, compositions, trees, permutations, braids,+    Young tableaux, and so on.+    Forked from the [combinat](http://hackage.haskell.org/package/combinat) package.+category: Math+build-type: Simple+extra-source-files:+    svg/*.svg+    svg/src/gen_figures.hs+extra-doc-files: svg/*.svg++library+    exposed-modules:+        Math.Combinat+        Math.Combinat.Classes+        Math.Combinat.Numbers+        Math.Combinat.Numbers.Series+        Math.Combinat.Numbers.Primes+        Math.Combinat.Sign+        Math.Combinat.Sets+        Math.Combinat.Tuples+        Math.Combinat.Compositions+        Math.Combinat.Groups.Thompson.F+        Math.Combinat.Groups.Free+        Math.Combinat.Groups.Braid+        Math.Combinat.Groups.Braid.NF+        Math.Combinat.Partitions+        Math.Combinat.Partitions.Integer+        Math.Combinat.Partitions.Skew+        Math.Combinat.Partitions.Set+        Math.Combinat.Partitions.NonCrossing+        Math.Combinat.Partitions.Plane+        Math.Combinat.Partitions.Multiset+        Math.Combinat.Partitions.Vector+        Math.Combinat.Permutations+        Math.Combinat.Tableaux+        Math.Combinat.Tableaux.Skew+        Math.Combinat.Tableaux.GelfandTsetlin+        Math.Combinat.Tableaux.GelfandTsetlin.Cone+        Math.Combinat.Tableaux.LittlewoodRichardson+        Math.Combinat.Trees+        Math.Combinat.Trees.Binary+        Math.Combinat.Trees.Nary+        Math.Combinat.Trees.Graphviz+        Math.Combinat.LatticePaths+        Math.Combinat.ASCII+        Math.Combinat.Helper+        Math.Combinat.TypeLevel+    hs-source-dirs: .+    default-language: Haskell2010+    default-extensions: CPP BangPatterns+    other-extensions: MultiParamTypeClasses ScopedTypeVariables+                      GeneralizedNewtypeDeriving DataKinds KindSignatures+    ghc-options: -fwarn-tabs -fno-warn-unused-matches+                 -fno-warn-name-shadowing -fno-warn-unused-imports+    build-depends:+        base >=4.11 && <5,+        array >=0.5,+        containers -any,+        random -any,+        transformers -any++test-suite combinat-tests+    type: exitcode-stdio-1.0+    main-is: TestSuite.hs+    hs-source-dirs: test+    other-modules:+        Tests.Braid+        Tests.Common+        Tests.LatticePaths+        Tests.Permutations+        Tests.Series+        Tests.SkewTableaux+        Tests.Thompson+        Tests.Partitions.Integer+        Tests.Partitions.Skew+    default-language: Haskell2010+    default-extensions: CPP BangPatterns+    build-depends:+        base >=4 && <5,+        array >=0.5,+        containers -any,+        random -any,+        transformers -any,+        combinat-compat -any,+        QuickCheck >=2,+        test-framework -any,+        test-framework-quickcheck2 -any
+ svg/bintrees.svg view
@@ -0,0 +1,4 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="750.0" height="206.08695652173907" font-size="1" viewBox="0 0 750 206" stroke="rgb(0,0,0)" stroke-opacity="1"><g><g fill="rgb(0,0,0)" fill-opacity="0.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" stroke-width="1.5725913258888558" stroke-linecap="butt" stroke-linejoin="miter" font-size="1.0em" stroke-miterlimit="10.0"><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 618.3370976849237,109.9350649350649 l 33.879164313946916,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 618.3370976849237,109.9350649350649 l -8.469791078486729,18.068887634105018 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 655.0395256916995,130.26256352343304 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 609.867306606437,128.0039525691699 l 25.409373235460187,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 609.867306606437,128.0039525691699 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 638.0999435347261,148.33145115753805 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 601.3975155279502,146.07284020327495 l 16.939582156973458,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 601.3975155279502,146.07284020327495 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 621.1603613777526,166.4003387916431 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 592.9277244494634,164.14172783737996 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 592.9277244494634,164.14172783737996 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 604.220779220779,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 587.2811970638056,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 596.3156408808582,164.14172783737996 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 604.7854319593449,146.07284020327495 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 613.2552230378317,128.0039525691699 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 621.7250141163184,109.9350649350649 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 522.3461321287408,109.9350649350649 l 33.879164313946916,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 522.3461321287408,109.9350649350649 l -8.469791078486729,18.068887634105018 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 559.0485601355166,130.26256352343304 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 513.876341050254,128.0039525691699 l 25.409373235460187,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 513.876341050254,128.0039525691699 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 542.1089779785432,148.33145115753805 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 505.40654997176733,146.07284020327495 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 505.40654997176733,146.07284020327495 l -16.939582156973458,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 513.876341050254,164.14172783737996 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 513.876341050254,164.14172783737996 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 525.1693958215698,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 508.22981366459624,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 517.2642574816488,164.14172783737996 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 491.29023150762276,166.4003387916431 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 508.794466403162,146.07284020327495 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 517.2642574816488,128.0039525691699 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 525.7340485601355,109.9350649350649 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 426.35516657255783,118.9695087521174 l 33.879164313946916,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 426.35516657255783,118.9695087521174 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 463.0575945793337,139.29700734048555 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 417.8853754940711,137.03839638622242 l 16.939582156973458,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 417.8853754940711,137.03839638622242 l -16.939582156973458,18.06888763410502 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 434.82495765104454,155.10728402032743 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 434.82495765104454,155.10728402032743 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 446.1180124223602,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 429.17843026538674,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 438.2128740824392,155.10728402032743 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 400.94579333709765,155.10728402032743 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 400.94579333709765,155.10728402032743 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 412.2388481084133,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 395.29926595143985,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 404.3337097684923,155.10728402032743 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 421.2732919254658,137.03839638622242 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 429.7430830039525,118.9695087521174 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 330.3642010163749,109.9350649350649 l 33.879164313946916,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 330.3642010163749,109.9350649350649 l -8.469791078486729,18.068887634105018 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 367.06662902315077,130.26256352343304 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 321.8944099378882,128.0039525691699 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 321.8944099378882,128.0039525691699 l -25.409373235460187,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 330.364201016375,146.07284020327495 l 16.939582156973458,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 330.364201016375,146.07284020327495 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 350.12704686617735,166.4003387916431 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 321.89440993788827,164.14172783737996 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 321.89440993788827,164.14172783737996 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 333.18746470920394,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 316.24788255223046,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 325.28232636928294,164.14172783737996 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 333.75211744776965,146.07284020327495 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 299.30830039525694,148.33145115753805 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 325.2823263692829,128.0039525691699 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 333.7521174477696,109.9350649350649 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 234.37323546019198,109.9350649350649 l 33.879164313946916,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 234.37323546019198,109.9350649350649 l -8.469791078486729,18.068887634105018 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 271.0756634669678,130.26256352343304 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 225.90344438170524,128.0039525691699 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 225.90344438170524,128.0039525691699 l -25.409373235460187,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 234.37323546019198,146.07284020327495 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 234.37323546019198,146.07284020327495 l -16.939582156973458,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 242.8430265386787,164.14172783737996 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 242.8430265386787,164.14172783737996 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 254.13608130999432,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 237.19649915302088,184.4692264257481 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 246.23094297007341,164.14172783737996 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 220.25691699604744,166.4003387916431 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 237.76115189158668,146.07284020327495 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 203.31733483907396,148.33145115753805 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 229.29136081309994,128.0039525691699 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 237.76115189158668,109.9350649350649 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 138.38226990400904,118.9695087521174 l 25.409373235460187,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 138.38226990400904,118.9695087521174 l -16.939582156973458,18.06888763410502 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 163.79164313946922,137.03839638622242 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 163.79164313946922,137.03839638622242 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 175.08469791078483,157.36589497459056 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 158.14511575381138,157.36589497459056 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 167.17955957086392,137.03839638622242 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 121.44268774703558,137.03839638622242 l 16.939582156973458,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 121.44268774703558,137.03839638622242 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 141.20553359683794,157.36589497459056 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 112.97289666854886,155.10728402032743 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 112.97289666854886,155.10728402032743 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 124.2659514398645,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 107.32636928289104,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 116.36081309994354,155.10728402032743 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 124.83060417843026,137.03839638622242 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 141.77018633540374,118.9695087521174 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 42.39130434782612,118.9695087521174 l 25.409373235460187,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 42.39130434782612,118.9695087521174 l -16.939582156973458,18.06888763410502 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 67.8006775832863,137.03839638622242 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 67.8006775832863,137.03839638622242 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 79.09373235460195,157.36589497459056 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 62.15415019762848,157.36589497459056 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 71.18859401468099,137.03839638622242 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 25.451722190852664,137.03839638622242 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 25.451722190852664,137.03839638622242 l -16.939582156973458,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 33.92151326933939,155.10728402032743 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 33.92151326933939,155.10728402032743 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 45.21456804065503,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 28.274985883681573,175.43478260869557 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 37.309429700734086,155.10728402032743 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 11.335403726708115,157.36589497459056 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 28.839638622247357,137.03839638622242 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 45.779220779220815,118.9695087521174 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 618.3370976849237,18.461321287408232 l 16.939582156973458,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 618.3370976849237,18.461321287408232 l -25.409373235460187,18.06888763410502 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 635.2766798418971,36.53020892151325 l 16.939582156973458,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 635.2766798418971,36.53020892151325 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 655.0395256916995,56.8577075098814 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 626.8068887634104,54.59909655561827 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 626.8068887634104,54.59909655561827 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 638.099943534726,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 621.1603613777526,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 630.1948051948051,54.59909655561827 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 638.6645962732919,36.53020892151325 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 592.9277244494635,36.53020892151326 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 592.9277244494635,36.53020892151326 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 604.2207792207791,56.85770750988141 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 587.2811970638057,56.85770750988141 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 596.3156408808583,36.53020892151326 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 621.7250141163184,18.461321287408232 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 522.3461321287408,18.461321287408232 l 16.939582156973458,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 522.3461321287408,18.461321287408232 l -25.409373235460187,18.06888763410502 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 539.2857142857142,36.53020892151325 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 539.2857142857142,36.53020892151325 l -16.939582156973458,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 547.755505364201,54.59909655561827 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 547.755505364201,54.59909655561827 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 559.0485601355166,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 542.1089779785432,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 551.1434217955957,54.59909655561827 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 525.1693958215698,56.8577075098814 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 542.673630717109,36.53020892151325 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 496.9367588932806,36.53020892151326 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 496.9367588932806,36.53020892151326 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 508.2298136645963,56.85770750988141 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 491.2902315076228,56.85770750988141 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 500.3246753246753,36.53020892151326 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 525.7340485601355,18.461321287408232 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 426.35516657255783,9.42687747035572 l 8.469791078486729,18.068887634105018 " /></g><g stroke-width="0.9034443817052511"><path d="M 426.35516657255783,9.42687747035572 l -33.879164313946916,17.504234895539238 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 434.8249576510446,27.495765104460737 l 25.409373235460187,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 434.8249576510446,27.495765104460737 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 463.05759457933374,47.82326369282889 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 426.3551665725579,45.564652738565755 l 16.939582156973458,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 426.3551665725579,45.564652738565755 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 446.11801242236027,65.89215132693391 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 417.8853754940712,63.63354037267077 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 417.8853754940712,63.63354037267077 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 429.17843026538685,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 412.2388481084134,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 421.27329192546586,63.63354037267077 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 429.74308300395256,45.564652738565755 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 438.21287408243927,27.495765104460737 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 395.29926595143985,29.754376058723867 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 429.7430830039525,9.42687747035572 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 330.3642010163749,9.42687747035572 l 8.469791078486729,18.068887634105018 " /></g><g stroke-width="0.9034443817052511"><path d="M 330.3642010163749,9.42687747035572 l -33.879164313946916,17.504234895539238 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 338.8339920948617,27.495765104460737 l 25.409373235460187,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 338.8339920948617,27.495765104460737 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 367.0666290231508,47.82326369282889 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 330.364201016375,45.564652738565755 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 330.364201016375,45.564652738565755 l -16.939582156973458,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 338.8339920948617,63.63354037267077 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 338.8339920948617,63.63354037267077 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 350.12704686617735,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 333.1874647092039,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 342.22190852625636,63.63354037267077 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 316.2478825522304,65.89215132693391 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 333.75211744776965,45.564652738565755 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 342.22190852625636,27.495765104460737 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 299.30830039525694,29.754376058723867 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 333.7521174477696,9.42687747035572 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 234.37323546019198,18.461321287408232 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 234.37323546019198,18.461321287408232 l -33.879164313946916,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 242.8430265386787,36.53020892151325 l 16.939582156973458,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 242.8430265386787,36.53020892151325 l -16.939582156973458,18.06888763410502 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 259.78260869565213,54.59909655561827 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 259.78260869565213,54.59909655561827 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 271.07566346696774,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 254.1360813099943,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 263.1705251270468,54.59909655561827 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 225.90344438170524,54.59909655561827 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 225.90344438170524,54.59909655561827 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 237.19649915302085,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 220.2569169960474,74.92659514398642 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 229.29136081309994,54.59909655561827 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 246.23094297007341,36.53020892151325 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 203.31733483907396,38.78881987577638 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 237.76115189158668,18.461321287408232 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 138.38226990400904,9.42687747035572 l 8.469791078486729,18.068887634105018 " /></g><g stroke-width="0.9034443817052511"><path d="M 138.38226990400904,9.42687747035572 l -33.879164313946916,17.504234895539238 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 146.85206098249577,27.495765104460737 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 146.85206098249577,27.495765104460737 l -25.409373235460187,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 155.3218520609825,45.564652738565755 l 16.939582156973458,17.50423489553924 " /></g><g stroke-width="0.9034443817052511"><path d="M 155.3218520609825,45.564652738565755 l -8.469791078486729,18.06888763410502 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 175.08469791078485,65.89215132693391 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 146.85206098249577,63.63354037267077 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 146.85206098249577,63.63354037267077 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 158.1451157538114,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 141.20553359683794,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 150.23997741389047,63.63354037267077 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 158.7097684923772,45.564652738565755 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 124.26595143986451,47.82326369282889 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 150.23997741389047,27.495765104460737 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 107.32636928289104,29.754376058723867 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 141.77018633540374,9.42687747035572 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 42.39130434782612,9.42687747035572 l 8.469791078486729,18.068887634105018 " /></g><g stroke-width="0.9034443817052511"><path d="M 42.39130434782612,9.42687747035572 l -33.879164313946916,17.504234895539238 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 50.86109542631285,27.495765104460737 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 50.86109542631285,27.495765104460737 l -25.409373235460187,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 59.33088650479958,45.564652738565755 l 8.469791078486729,18.06888763410502 " /></g><g stroke-width="0.9034443817052511"><path d="M 59.33088650479958,45.564652738565755 l -16.939582156973458,17.50423489553924 " /></g><g><g><g stroke-width="0.9034443817052511"><g stroke-width="0.9034443817052511"><path d="M 67.8006775832863,63.63354037267077 l 8.469791078486729,17.504234895539238 " /></g><g stroke-width="0.9034443817052511"><path d="M 67.8006775832863,63.63354037267077 l -8.469791078486729,17.504234895539238 " /></g><g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 79.09373235460195,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 62.15415019762848,83.96103896103892 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 71.18859401468099,63.63354037267077 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 45.21456804065503,65.89215132693391 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 62.71880293619427,45.564652738565755 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 28.274985883681573,47.82326369282889 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 54.24901185770754,27.495765104460737 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g><g><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.9034443817052511"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"></g><g fill="rgb(0,0,255)" fill-opacity="1.0"><path d="M 11.335403726708115,29.754376058723867 l -6.268904712733802e-16,-5.646527385657819 h -5.646527385657819 l -6.268904712733802e-16,5.646527385657819 Z" /></g></g></g></g></g></g><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"></g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 45.779220779220815,9.42687747035572 c 0.0,-1.8710945787604518 -1.5168218526342394,-3.3879164313946917 -3.387916431394691 -3.3879164313946917c -1.8710945787604518,-1.145677154876327e-16 -3.3879164313946917,1.5168218526342394 -3.3879164313946917 3.3879164313946903c -2.291354309752654e-16,1.8710945787604518 1.5168218526342392,3.3879164313946917 3.3879164313946903 3.3879164313946917c 1.8710945787604518,3.4370314646289805e-16 3.3879164313946917,-1.516821852634239 3.387916431394692 -3.3879164313946903Z" /></g></g></g><g fill="rgb(255,0,0)" fill-opacity="1.0"></g></g></g></g></g></g></g></g></g></g></g></svg>
+ svg/dyck_path.svg view
@@ -0,0 +1,3 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg stroke="rgb(0,0,0)" version="1.1" xmlns="http://www.w3.org/2000/svg" height="97.7011" viewBox="0 0 500 98" stroke-opacity="1" font-size="1" xmlns:xlink="http://www.w3.org/1999/xlink" width="500.0000"><defs></defs><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 453.7182,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 436.3027,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 418.8871,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 401.4716,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 384.0561,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 366.6405,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 349.2250,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 331.8095,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 314.3939,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 296.9784,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 279.5629,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 262.1473,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 244.7318,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 227.3163,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 209.9007,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 192.4852,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 175.0697,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 157.6541,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 140.2386,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 122.8231,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 105.4075,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 87.9920,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 70.5765,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 53.1609,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 35.7454,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 18.3299,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,87.9920 v -87.0777 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,0.9143 h 452.8039 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,18.3299 h 452.8039 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,35.7454 h 452.8039 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,53.1609 h 452.8039 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,70.5765 h 452.8039 "/></g><defs></defs><g stroke="rgb(128,128,128)" fill="rgb(0,0,0)" stroke-width="0.4353883664228493" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,87.9920 h 452.8039 "/></g><defs></defs><g stroke="rgb(255,0,0)" fill="rgb(0,0,0)" stroke-width="0.8707767328456986" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 0.9143,87.9920 l 17.4155,-17.4155 l 17.4155,-17.4155 l 17.4155,17.4155 l 17.4155,-17.4155 l 17.4155,-17.4155 l 17.4155,-17.4155 l 17.4155,17.4155 l 17.4155,-17.4155 l 17.4155,17.4155 l 17.4155,17.4155 l 17.4155,-17.4155 l 17.4155,17.4155 l 17.4155,-17.4155 l 17.4155,-17.4155 l 17.4155,-17.4155 l 17.4155,17.4155 l 17.4155,17.4155 l 17.4155,17.4155 l 17.4155,17.4155 l 17.4155,17.4155 l 17.4155,-17.4155 l 17.4155,17.4155 l 17.4155,-17.4155 l 17.4155,-17.4155 l 17.4155,17.4155 l 17.4155,17.4155 "/></g></svg>
+ svg/ferrers.svg view
@@ -0,0 +1,4 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256.0" height="160.0" font-size="1" viewBox="0 0 256 160" stroke="rgb(0,0,0)" stroke-opacity="1"><g><g fill="rgb(0,0,0)" fill-opacity="0.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" stroke-width="0.8095430810031051" stroke-linecap="butt" stroke-linejoin="miter" font-size="1.0em" stroke-miterlimit="10.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" stroke-width="1.4545454545454546"><g><path d="M 232.72727272727272,0.0 v 29.09090909090909 " /></g><g><path d="M 203.63636363636363,0.0 v 29.09090909090909 " /></g><g><path d="M 174.54545454545453,0.0 v 58.18181818181818 " /></g><g><path d="M 145.45454545454544,0.0 v 58.18181818181818 " /></g><g><path d="M 116.36363636363636,0.0 v 58.18181818181818 " /></g><g><path d="M 87.27272727272727,0.0 v 116.36363636363636 " /></g><g><path d="M 58.18181818181818,0.0 v 116.36363636363636 " /></g><g><path d="M 29.09090909090909,0.0 v 145.45454545454544 " /></g><g><path d="M 0.0,0.0 v 145.45454545454544 " /></g><g><path d="M 0.0,145.45454545454544 h 29.09090909090909 " /></g><g><path d="M 0.0,116.36363636363636 h 87.27272727272727 " /></g><g><path d="M 0.0,87.27272727272727 h 87.27272727272727 " /></g><g><path d="M 0.0,58.18181818181818 h 174.54545454545453 " /></g><g><path d="M 0.0,29.09090909090909 h 232.72727272727272 " /></g><g><path d="M 0.0,0.0 h 232.72727272727272 " /></g></g><g stroke="rgb(255,0,0)" stroke-opacity="1.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 23.272727272727273,130.9090909090909 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 81.45454545454544,101.81818181818181 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 52.36363636363636,101.81818181818181 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 23.272727272727273,101.81818181818181 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 81.45454545454544,72.72727272727272 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 52.36363636363636,72.72727272727272 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 23.272727272727273,72.72727272727272 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 168.72727272727272,43.63636363636363 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 139.63636363636363,43.63636363636363 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 110.54545454545453,43.63636363636363 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 81.45454545454544,43.63636363636363 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 52.36363636363636,43.63636363636363 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 23.272727272727273,43.63636363636363 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 226.9090909090909,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 197.8181818181818,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 168.72727272727272,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 139.63636363636363,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 110.54545454545453,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 81.45454545454544,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 52.36363636363636,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,0,0)" fill-opacity="1.0" stroke-width="0.7272727272727273"><g fill="rgb(255,0,0)" fill-opacity="1.0"><g fill="rgb(255,0,0)" fill-opacity="1.0"><path d="M 23.272727272727273,14.545454545454545 c 0.0,-4.819939634886924 -3.907333092385801,-8.727272727272727 -8.727272727272725 -8.727272727272727c -4.819939634886924,-2.951264350961418e-16 -8.727272727272727,3.9073330923858007 -8.727272727272727 8.727272727272723c -5.902528701922836e-16,4.819939634886924 3.9073330923858003,8.727272727272727 8.727272727272723 8.727272727272727c 4.819939634886924,8.853793052884255e-16 8.727272727272727,-3.9073330923858 8.72727272727273 -8.727272727272723Z" /></g></g></g></g></g></g></g></svg>
+ svg/noncrossing.svg view
@@ -0,0 +1,4 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256.0" height="252.11078477112528" font-size="1" viewBox="0 0 256 252" stroke="rgb(0,0,0)" stroke-opacity="1"><g><g fill="rgb(0,0,0)" fill-opacity="0.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" stroke-width="1.0161918000173635" stroke-linecap="butt" stroke-linejoin="miter" font-size="1.0em" stroke-miterlimit="10.0"><g fill="rgb(0,0,255)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,58.9536640353175,45.86642929392933)" dominant-baseline="middle" text-anchor="middle" stroke="none">9</text></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,22.214876033057877,109.49987672234495)" dominant-baseline="middle" text-anchor="middle" stroke="none">8</text></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,34.97412320562606,181.86116324413933)" dominant-baseline="middle" text-anchor="middle" stroke="none">7</text></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,91.26121199774039,229.09163868964527)" dominant-baseline="middle" text-anchor="middle" stroke="none">6</text></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,164.7387880022597,229.09163868964527)" dominant-baseline="middle" text-anchor="middle" stroke="none">5</text></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,221.02587679437403,181.86116324413928)" dominant-baseline="middle" text-anchor="middle" stroke="none">4</text></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,233.78512396694217,109.49987672234491)" dominant-baseline="middle" text-anchor="middle" stroke="none">3</text></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,197.04633596468253,45.8664292939293)" dominant-baseline="middle" text-anchor="middle" stroke="none">2</text></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,255)" fill-opacity="1.0"><text transform="matrix(24.788545388692057,0.0,0.0,24.788545388692057,128.00000000000003,20.735618217640905)" dominant-baseline="middle" text-anchor="middle" stroke="none">1</text></g></g></g></g></g><g stroke="rgb(255,0,0)" stroke-opacity="1.0" stroke-width="6.6102787703178825"><path d="M 210.62848462897355,119.47665734926427 c 0.0,-45.63445196221021 -36.99403266676331,-82.62848462897352 -82.62848462897351 -82.62848462897352c -45.63445196221021,-2.7942119913062335e-15 -82.62848462897352,36.9940326667633 -82.62848462897352 82.6284846289735c -5.588423982612467e-15,45.63445196221021 36.9940326667633,82.62848462897352 82.62848462897348 82.62848462897352c 45.63445196221021,8.3826359739187e-15 82.62848462897352,-36.994032666763296 82.62848462897357 -82.6284846289735Z" /></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(255,165,0)" fill-opacity="1.0" stroke-width="1.6525696925794706"><g fill="rgb(255,165,0)" fill-opacity="1.0"><g fill="rgb(255,165,0)" fill-opacity="1.0"><g fill="rgb(255,165,0)" fill-opacity="1.0"><g fill="rgb(255,165,0)" fill-opacity="1.0"><g><g fill="rgb(255,165,0)" fill-opacity="1.0"><g fill="rgb(255,165,0)" fill-opacity="1.0"><path d="M 67.73159719683085,52.04814162444856 l -28.260606155584327,48.94880571416585 c -2.281722598110509,3.952059468705467 -0.9276470236638324,9.005538309828474 3.0244124450416336 11.287260907938983c 3.952059468705467,2.2817225981105094 9.005538309828474,0.9276470236638328 11.287260907938983 -3.0244124450416328l 28.260606155584327,-48.94880571416585 c 2.281722598110509,-3.952059468705467 0.927647023663832,-9.005538309828474 -3.0244124450416345 -11.287260907938983c -3.952059468705467,-2.2817225981105094 -9.005538309828474,-0.9276470236638324 -11.287260907938986 3.0244124450416336Z" /></g></g></g><g><g fill="rgb(255,165,0)" fill-opacity="1.0"><g fill="rgb(255,165,0)" fill-opacity="1.0"><path d="M 51.13037662242908,167.12060881308778 l 43.29776060931872,36.331134958081485 c 3.495801834043109,2.933326029614429 8.707639732390161,2.4773492973742828 11.64096576200459 -1.0184525366688244c 0.9323202645137305,-1.1110960246826247 1.5557430576990043,-2.448030519071843 1.8076080788896125 -3.8764280347477618l 28.260606155584334,-160.2738619015418 c 0.7924339422066867,-4.494116209684779 -2.20837244062142,-8.779711863939754 -6.702488650306198 -9.57214580614644c -3.4209150223784612,-0.6031996171562244 -6.853821179049025,0.9975908134267045 -8.59066525441043 4.005892996777644l -71.55836676490306,123.94272694346033 c -2.004953927164585,3.472682068683816 -1.2271875654055806,7.883614296037856 1.84458006382242 10.46113338078537Z" /></g></g></g></g><g><g fill="rgb(255,165,0)" fill-opacity="1.0"><g fill="rgb(255,165,0)" fill-opacity="1.0"><path d="M 161.57186276825226,203.45174377116925 l 43.29776060931873,-36.33113495808153 c 2.2613872731848765,-1.8975292268784263 3.3386754938661554,-4.8573542874446884 2.8260606155584305 -7.764537727256825l -18.44580063822424,-104.61133380785381 c -0.7924339422066877,-4.494116209684778 -5.078029596461661,-7.494922592512885 -9.57214580614644 -6.702488650306197c -3.4209150223784626,0.603199617156225 -6.099289033149974,3.281573627927738 -6.702488650306198 6.7024886503062l -24.851959971094487,140.94246876593533 c -0.7924339422066862,4.494116209684779 2.208372440621421,8.779711863939752 6.702488650306198 9.57214580614644c 2.4053330636402883,0.4241251167099334 4.875068211267832,-0.23763842161562668 6.746085190588008 -1.807608078889615Z" /></g></g></g></g><g><g fill="rgb(255,165,0)" fill-opacity="1.0"><g fill="rgb(255,165,0)" fill-opacity="1.0"><path d="M 217.63602074516058,105.12837157006305 c 0.0,-4.5634451962210205 -3.699403266676331,-8.262848462897352 -8.26284846289735 -8.262848462897352c -4.5634451962210205,-2.794211991306233e-16 -8.262848462897352,3.6994032666763306 -8.262848462897352 8.262848462897349c -5.588423982612466e-16,4.5634451962210205 3.69940326667633,8.262848462897352 8.262848462897349 8.262848462897352c 4.5634451962210205,8.3826359739187e-16 8.262848462897352,-3.6994032666763297 8.262848462897356 -8.262848462897349Z" /></g></g></g></g></g></g><g fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 79.01885810476983,56.17956585589723 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 50.75825194918551,105.12837157006308 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 60.57305746654565,160.79089966375108 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 103.87081807586438,197.12203462183254 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 160.39203038703306,197.12203462183254 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 203.68979099635177,160.79089966375102 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 213.5045965137119,105.12837157006305 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 185.24399035812755,56.179565855897216 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><path d="M 132.1314242314487,36.84817272029075 c 0.0,-2.2817225981105103 -1.8497016333381655,-4.131424231448676 -4.131424231448675 -4.131424231448676c -2.2817225981105103,-1.3971059956531165e-16 -4.131424231448676,1.8497016333381653 -4.131424231448676 4.131424231448674c -2.794211991306233e-16,2.2817225981105103 1.849701633338165,4.131424231448676 4.131424231448674 4.131424231448676c 2.2817225981105103,4.19131798695935e-16 4.131424231448676,-1.8497016333381648 4.131424231448678 -4.131424231448674Z" /></g></g></g></g></g></g></g></g></g></svg>
+ svg/plane_partition.svg view
@@ -0,0 +1,4 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="320.0" height="285.52595130832407" font-size="1" viewBox="0 0 320 286" stroke="rgb(0,0,0)" stroke-opacity="1"><g><g fill="rgb(0,0,0)" fill-opacity="0.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" stroke-width="1.209087619115596" stroke-linecap="butt" stroke-linejoin="miter" font-size="1.0em" stroke-miterlimit="10.0"><g fill="rgb(124,252,0)" fill-opacity="1.0" stroke-width="1.5268767449642997"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 174.54545454545453,15.268767449643008 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 174.54545454545453,76.343837248215 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 148.099173553719,61.075069798572 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 200.99173553719007,61.075069798572 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g></g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 121.65289256198346,106.881372147501 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 253.88429752066116,122.15013959714398 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 227.4380165289256,106.881372147501 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g></g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 95.20661157024792,152.68767449643 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 148.099173553719,152.68767449643 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 200.99173553719007,152.68767449642996 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g></g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 42.31404958677686,213.76274429500197 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 68.7603305785124,198.493976845359 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 121.65289256198346,198.493976845359 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 227.4380165289256,198.49397684535896 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g><g><g fill="rgb(124,252,0)" fill-opacity="1.0"><g fill="rgb(124,252,0)" fill-opacity="1.0"><path d="M 280.3305785123967,198.49397684535896 l -26.446280991735534,15.268767449642999 l 26.446280991735538,15.268767449642993 l 26.446280991735534,-15.268767449642999 Z" /></g></g></g></g></g></g></g><g fill="rgb(205,92,92)" fill-opacity="1.0" stroke-width="1.5268767449642997"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 200.99173553719007,61.075069798572 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 200.99173553719007,122.15013959714398 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 227.4380165289256,106.881372147501 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g></g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 148.099173553719,152.68767449643 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 200.99173553719007,152.68767449642996 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 280.3305785123967,167.95644194607297 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g></g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 121.65289256198346,198.493976845359 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 174.54545454545453,198.49397684535896 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 227.4380165289256,198.49397684535896 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 280.3305785123967,198.49397684535896 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g></g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 68.76033057851241,259.56904664393096 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 95.20661157024793,244.30027919428795 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 148.099173553719,244.30027919428795 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 174.54545454545453,229.03151174464494 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 253.88429752066116,244.30027919428795 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(205,92,92)" fill-opacity="1.0"><g fill="rgb(205,92,92)" fill-opacity="1.0"><path d="M 306.77685950413223,244.30027919428795 v -30.53753489928599 l -26.446280991735534,15.268767449642999 v 30.53753489928599 Z" /></g></g></g></g></g></g></g><g fill="rgb(95,158,160)" fill-opacity="1.0" stroke-width="1.5268767449642997"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 148.099173553719,61.075069798572 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 148.099173553719,122.15013959714399 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 121.65289256198346,106.881372147501 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g></g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 227.4380165289256,167.95644194607297 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 200.99173553719007,152.68767449642996 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 148.099173553719,152.68767449643 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 95.20661157024792,152.68767449643 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g></g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 227.4380165289256,198.49397684535896 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 174.54545454545453,198.49397684535896 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 121.65289256198346,198.493976845359 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 68.7603305785124,198.493976845359 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g></g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 253.88429752066116,244.30027919428795 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 200.99173553719007,244.30027919428795 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 174.54545454545453,229.03151174464494 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 95.20661157024793,244.30027919428795 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g><g><g fill="rgb(95,158,160)" fill-opacity="1.0"><g fill="rgb(95,158,160)" fill-opacity="1.0"><path d="M 15.867768595041298,259.56904664393096 v -30.53753489928599 l 26.446280991735538,15.268767449642993 v 30.53753489928599 Z" /></g></g></g></g></g></g></g></g></g></g></svg>
+ svg/skew3.svg view
@@ -0,0 +1,3 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg stroke="rgb(0,0,0)" version="1.1" xmlns="http://www.w3.org/2000/svg" height="171.6044" viewBox="0 0 256 172" stroke-opacity="1" font-size="1" xmlns:xlink="http://www.w3.org/1999/xlink" width="256.0000"><defs></defs><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 231.5125,1.3427 v 25.5744 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 205.9381,1.3427 v 25.5744 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 180.3636,1.3427 v 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 154.7892,1.3427 v 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 129.2148,1.3427 v 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 103.6404,26.9171 v 25.5744 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 78.0659,26.9171 v 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 52.4915,52.4915 v 76.7233 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 26.9171,78.0659 v 76.7233 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.3427,103.6404 v 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.3427,154.7892 h 25.5744 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.3427,129.2148 h 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.3427,103.6404 h 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 26.9171,78.0659 h 51.1489 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 52.4915,52.4915 h 127.8721 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 78.0659,26.9171 h 153.4466 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.2787212787212787" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 129.2148,1.3427 h 102.2977 "/></g></svg>
+ svg/skew_tableau.svg view
@@ -0,0 +1,3 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg stroke="rgb(0,0,0)" version="1.1" xmlns="http://www.w3.org/2000/svg" height="214.5055" viewBox="0 0 320 215" stroke-opacity="1" font-size="1" xmlns:xlink="http://www.w3.org/1999/xlink" width="320.0000"><defs></defs><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 161.5185,1.6783 v 31.9680 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 129.5504,1.6783 v 31.9680 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 97.5824,1.6783 v 63.9361 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 65.6144,1.6783 v 95.9041 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 33.6464,1.6783 v 127.8721 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,1.6783 v 127.8721 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,129.5504 h 31.9680 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,97.5824 h 63.9361 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,65.6144 h 95.9041 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,33.6464 h 159.8402 "/></g><defs></defs><g stroke="rgb(211,211,211)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,1.6783 h 159.8402 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 289.3906,1.6783 v 31.9680 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 257.4226,1.6783 v 31.9680 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 225.4545,1.6783 v 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 193.4865,1.6783 v 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 161.5185,1.6783 v 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 129.5504,33.6464 v 31.9680 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 97.5824,33.6464 v 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 65.6144,65.6144 v 95.9041 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 33.6464,97.5824 v 95.9041 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,129.5504 v 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,193.4865 h 31.9680 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,161.5185 h 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 1.6783,129.5504 h 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 33.6464,97.5824 h 63.9361 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 65.6144,65.6144 h 159.8402 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 97.5824,33.6464 h 191.8082 "/></g><defs></defs><g stroke="rgb(0,0,0)" fill="rgb(0,0,0)" stroke-width="1.5984015984015982" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="0.0"><path d="M 161.5185,1.6783 h 127.8721 "/></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,17.6623,186.4535)" dominant-baseline="middle">7</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,49.6304,154.4855)" dominant-baseline="middle">5</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,17.6623,154.4855)" dominant-baseline="middle">1</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,49.6304,122.5175)" dominant-baseline="middle">2</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,81.5984,90.5495)" dominant-baseline="middle">1</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,209.4705,58.5814)" dominant-baseline="middle">2</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,177.5025,58.5814)" dominant-baseline="middle">2</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,145.5345,58.5814)" dominant-baseline="middle">1</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,113.5664,58.5814)" dominant-baseline="middle">1</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,273.4066,26.6134)" dominant-baseline="middle">1</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,241.4386,26.6134)" dominant-baseline="middle">1</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,209.4705,26.6134)" dominant-baseline="middle">1</text></g><defs></defs><g stroke="rgb(0,0,255)" fill="rgb(0,0,255)" stroke-width="0.0" stroke-linecap="butt" stroke-miterlimit="10.0" stroke-linejoin="miter" stroke-opacity="1.0" fill-opacity="1.0" font-size="27.172827172827166px"><text stroke="none" text-anchor="middle" transform="matrix(1.0000,0.0000,0.0000,1.0000,177.5025,26.6134)" dominant-baseline="middle">1</text></g></svg>
+ svg/src/gen_figures.hs view
@@ -0,0 +1,81 @@++-- | A script to generate the SVG figures in the documentation.+-- We use the @combinat-diagrams@ library for that.++module Main where++--------------------------------------------------------------------------------++import Math.Combinat.Partitions.Integer+import Math.Combinat.Partitions.Plane+import Math.Combinat.Partitions.NonCrossing+import Math.Combinat.Partitions.Skew+import Math.Combinat.Tableaux+import Math.Combinat.Tableaux.Skew+import Math.Combinat.LatticePaths+import Math.Combinat.Trees.Binary++import Math.Combinat.Diagrams.Partitions.Integer+import Math.Combinat.Diagrams.Partitions.Plane+import Math.Combinat.Diagrams.Partitions.NonCrossing+import Math.Combinat.Diagrams.Partitions.Skew+import Math.Combinat.Diagrams.Tableaux+import Math.Combinat.Diagrams.Tableaux.Skew+import Math.Combinat.Diagrams.LatticePaths+import Math.Combinat.Diagrams.Trees.Binary++import Diagrams.Core+import Diagrams.Prelude+import Diagrams.Backend.SVG++--------------------------------------------------------------------------------++export fpath size what = renderSVG fpath size $ pad 1.10 what++vcatSep = vcat' (with & sep .~ 1) +hcatSep = hcat' (with & sep .~ 1) ++boxSep m xs = pad 1.05 $ vcatSep $ map hcatSep $ yys where+  yys = go xs where+    go [] = []+    go zs = take m zs : go (drop m zs) ++padding fac diag = pad fac $ centerXY diag+margin  siz diag = hcat [ strutX siz , vcat [ strutY siz , centerXY diag , strutY siz ] , strutX siz ]++--------------------------------------------------------------------------------++main = do ++  export "plane_partition.svg" (mkWidth 320) $ margin 0.05 $ drawPlanePartition3D $+    PlanePart [[5,4,3,3,1],[4,4,2,1],[3,2],[2,1],[1],[1]] ++  export "noncrossing.svg" (mkWidth 256) $ padding 1.10 $ drawNonCrossingCircleDiagram' orange True $+    NonCrossing [[3],[5,4,2],[7,6,1],[9,8]]++  export "young_tableau.svg" (mkWidth 256) $ margin 0.05 $ drawTableau $ +    [ [ 1 , 3 , 4 , 6 , 7 ]+    , [ 2 , 5 , 8 ,10 ]+    , [ 9 ]+    ]++  let u = UpStep+      d = DownStep+      path = [ u,u,d,u,u,u,d,u,d,d,u,d,u,u,u,d,d,d,d,d,u,d,u,u,d,d ]     +  export "dyck_path.svg" (mkWidth 500) $ margin 0.05 $ drawLatticePath $ path+  -- print (pathHeight path, pathNumberOfZeroTouches path, pathNumberOfPeaks path)++  export "ferrers.svg" (mkWidth 256) $ margin 0.05 $ drawFerrersDiagram' EnglishNotation red True $+    Partition [8,6,3,3,1]++  export "bintrees.svg" (mkWidth 750) $ boxSep 7 $ map drawBinTree_ (binaryTrees 4)++  let skew = mkSkewPartition (Partition [9,7,3,2,2,1] , Partition [5,3,2,1])+  -- export "skew.svg"  (mkWidth 256) $ margin 0.05 $ drawSkewFerrersDiagram  skew+  -- export "skew2.svg" (mkWidth 256) $ margin 0.05 $ drawSkewFerrersDiagram' EnglishNotation green True (True,True) skew+  export "skew3.svg" (mkWidth 256) $ margin 0.05 $ drawSkewPartitionBoxes  EnglishNotation skew++  let skewtableau  = (semiStandardSkewTableaux 7 skew) !! 123+  export "skew_tableau.svg" (mkWidth 320) $ margin 0.05 $ drawSkewTableau' EnglishNotation blue True skewtableau++--------------------------------------------------------------------------------
+ svg/young_tableau.svg view
@@ -0,0 +1,4 @@+<?xml version="1.0" encoding="UTF-8"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256.0" height="153.60000000000002" font-size="1" viewBox="0 0 256 154" stroke="rgb(0,0,0)" stroke-opacity="1"><g><g fill="rgb(0,0,0)" fill-opacity="0.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" stroke-width="0.793186989303279" stroke-linecap="butt" stroke-linejoin="miter" font-size="1.0em" stroke-miterlimit="10.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" stroke-width="2.3272727272727276"><g><path d="M 232.72727272727275,0.0 v 46.54545454545455 " /></g><g><path d="M 186.1818181818182,0.0 v 93.0909090909091 " /></g><g><path d="M 139.63636363636363,0.0 v 93.0909090909091 " /></g><g><path d="M 93.0909090909091,0.0 v 93.0909090909091 " /></g><g><path d="M 46.54545454545455,0.0 v 139.63636363636363 " /></g><g><path d="M 0.0,0.0 v 139.63636363636363 " /></g><g><path d="M 0.0,139.63636363636363 h 46.54545454545455 " /></g><g><path d="M 0.0,93.0909090909091 h 186.1818181818182 " /></g><g><path d="M 0.0,46.54545454545455 h 232.72727272727275 " /></g><g><path d="M 0.0,0.0 h 232.72727272727275 " /></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0"><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,23.272727272727273,129.39636363636365)" dominant-baseline="middle" text-anchor="middle" stroke="none">9</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,162.9090909090909,82.8509090909091)" dominant-baseline="middle" text-anchor="middle" stroke="none">10</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,116.36363636363637,82.8509090909091)" dominant-baseline="middle" text-anchor="middle" stroke="none">8</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,69.81818181818181,82.8509090909091)" dominant-baseline="middle" text-anchor="middle" stroke="none">5</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,23.272727272727273,82.8509090909091)" dominant-baseline="middle" text-anchor="middle" stroke="none">2</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,209.45454545454547,36.305454545454545)" dominant-baseline="middle" text-anchor="middle" stroke="none">7</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,162.9090909090909,36.305454545454545)" dominant-baseline="middle" text-anchor="middle" stroke="none">6</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,116.36363636363637,36.305454545454545)" dominant-baseline="middle" text-anchor="middle" stroke="none">4</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,69.81818181818181,36.305454545454545)" dominant-baseline="middle" text-anchor="middle" stroke="none">3</text></g></g><g stroke="rgb(0,0,0)" stroke-opacity="1.0" fill="rgb(0,0,0)" fill-opacity="1.0" stroke-width="0.0"><g fill="rgb(0,0,0)" fill-opacity="1.0"><text transform="matrix(39.56363636363636,0.0,0.0,39.56363636363636,23.272727272727273,36.305454545454545)" dominant-baseline="middle" text-anchor="middle" stroke="none">1</text></g></g></g></g></g></g></svg>
+ test/TestSuite.hs view
@@ -0,0 +1,41 @@++module Main where++--------------------------------------------------------------------------------++import Test.Framework+import Test.Framework.Providers.QuickCheck2++import Tests.Permutations       ( testgroup_Permutations      )+import Tests.Partitions.Integer ( testgroup_IntegerPartitions )+import Tests.Partitions.Skew    ( testgroup_SkewPartitions    )+import Tests.Braid              ( testgroup_Braid +                                , testgroup_Braid_NF          )+import Tests.Series             ( testgroup_PowerSeries       )+import Tests.SkewTableaux       ( testgroup_SkewTableaux      )+import Tests.Thompson           ( testgroup_ThompsonF         )+import Tests.LatticePaths       ( testgroup_LatticePaths      )++--------------------------------------------------------------------------------++main :: IO ()+main = defaultMain tests++tests :: [Test]+tests = +  [ testgroup_Permutations+  , testGroup "Partitions" +      [ testgroup_IntegerPartitions+      , testgroup_SkewPartitions+      ]+  , testgroup_SkewTableaux+  , testgroup_ThompsonF+  , testgroup_LatticePaths+  , testGroup "Braids" +      [ testgroup_Braid +      , testgroup_Braid_NF +      ]+  , testgroup_PowerSeries  +  ]++--------------------------------------------------------------------------------
+ test/Tests/Braid.hs view
@@ -0,0 +1,278 @@++-- | Tests for braids. ++{-# LANGUAGE +      CPP, BangPatterns, +      ScopedTypeVariables, ExistentialQuantification,+      DataKinds, KindSignatures, Rank2Types,+      TypeOperators, TypeFamilies,+      StandaloneDeriving #-}++module Tests.Braid where++--------------------------------------------------------------------------------++import Math.Combinat.Groups.Braid+import Math.Combinat.Groups.Braid.NF++import Tests.Permutations ()     -- arbitrary instance+import Tests.Common++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck+import Test.QuickCheck.Gen++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.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++--------------------------------------------------------------------------------+-- * Types and instances++maxBraidWordLen :: Int+maxBraidWordLen = 600++maxStrands :: Int+maxStrands = 18         -- normal forms are very slow for large ones++shrinkBraid :: KnownNat n => Braid n -> [Braid n]+shrinkBraid (Braid gens) = map Braid list where+  len  = length gens+  list = [ take i gens ++ drop (i+1) gens | i<-[0..len-1] ]++-- someRndBraid :: Int -> (forall (n :: Nat). KnownNat n => g -> (Braid n, g)) -> g -> (SomeBraid, g)+-- someRndBraid n f = \g -> let (x,g') = f g in (someBraid n x, g')++-- | equality as /braid words/+(=:=) :: Braid n -> Braid n -> Bool+(=:=) (Braid gens1) (Braid gens2) = (gens1 == gens2)++data UnreducedBraid   = forall n. KnownNat n => Unreduced (Braid n)              +data ReducedBraid     = forall n. KnownNat n => Reduced   (Braid n)              +data PositiveBraid    = forall n. KnownNat n => PositiveB (Braid n)              +data PerturbedBraid   = forall n. KnownNat n => Perturbed (Braid n)   (Braid n)  +data PermutationBraid = forall n. KnownNat n => PermBraid Permutation (Braid n)  +data TwoBraids        = forall n. KnownNat n => TwoBraids (Braid n)   (Braid n)  ++deriving instance Show UnreducedBraid+deriving instance Show ReducedBraid+deriving instance Show PositiveBraid+deriving instance Show PerturbedBraid+deriving instance Show PermutationBraid+deriving instance Show TwoBraids++instance KnownNat n => Random (Braid n) where+  randomR _ = random+  random g0 = (b, g2) where+    n = numberOfStrands b+    (l,g1) = randomR (0,maxBraidWordLen) g0+    (b,g2) = randomBraidWord l g1++instance Random UnreducedBraid where+  randomR _ = random+  random = runRand $ do+    n <- randChoose (2,maxStrands)+    l <- randChoose (0,maxBraidWordLen)+    withSelectedM Unreduced (rand $ randomBraidWord l) n++instance Random PositiveBraid where+  randomR _ = random+  random  = runRand $ do+    n <- randChoose (2,maxStrands)+    l <- randChoose (0,maxBraidWordLen)+    withSelectedM PositiveB (rand $ randomPositiveBraidWord l) n++instance Random PerturbedBraid where+  randomR _ = random+  random  = runRand $ do+    Unreduced b <- rand random+    k <- randChoose (20,1000)+    c <- rand $ randomPerturbBraidWord k b +    return (Perturbed b c)++instance KnownNat n => Arbitrary (Braid n) where+  arbitrary = choose_+  shrink    = shrinkBraid++instance Arbitrary UnreducedBraid where+  arbitrary = choose_+  shrink (Unreduced b) = map Unreduced (shrinkBraid b)++instance Arbitrary PositiveBraid where+  arbitrary = choose_+  shrink (PositiveB b) = map PositiveB (shrinkBraid b)++instance Arbitrary ReducedBraid where+  arbitrary = do+    Unreduced braid <- arbitrary+    return $ Reduced $ freeReduceBraidWord braid++instance Arbitrary PerturbedBraid where+  arbitrary = choose_+  shrink _  = []++instance Arbitrary TwoBraids where+  shrink _  = []+  arbitrary = do+    n <- choose (2::Int, maxStrands)+    let snat = case someNatVal (fromIntegral n :: Integer) of+          Just sn -> sn+          Nothing -> error "TwoBraids/arbitrary: shouldn't happen"+    case snat of +      SomeNat pxy -> do+        (braid1,braid2) <- choosePair_+        return $ TwoBraids (asProxyTypeOf1 braid1 pxy) (asProxyTypeOf1 braid2 pxy)++mkPermBraid :: Permutation -> PermutationBraid+mkPermBraid perm = +  case snat of    +    SomeNat pxy -> PermBraid perm (asProxyTypeOf1 (permutationBraid perm) pxy)+  where+    n = P.permutationSize perm+    Just snat = someNatVal (fromIntegral n :: Integer)++instance Arbitrary PermutationBraid where+  arbitrary = do+    perm <- arbitrary+    return $ mkPermBraid perm+  shrink (PermBraid x b) = [ PermBraid (braidPermutation s) s | s <- shrinkBraid b ]++--------------------------------------------------------------------------------+-- * test groups++testgroup_Braid :: Test+testgroup_Braid = testGroup "Braid"+  +  [ testProperty "linking matrix is invariant of reduction"    prop_link_reduce +  , testProperty "linking matrix is invariant of perturbation" prop_link_perturb+  +  , testProperty "tau^2 = identity"                    prop_tau_square+  , testProperty "tau commutes with braidPermutation"  prop_permTau_1++  , testProperty "braidPermutation . permutationBraid = identity"  prop_permBraid_perm+  , testProperty "permutation braid is indeed a permutation braid" prop_permBraid_valid+  , testProperty "multiplication commutes with braidPermutation" prop_braidPerm_comp++  , testProperty "positive braids have positive links" prop_link_positive+  , testProperty "definition of linking"               prop_linking++  ] ++--------------------------------------------------------------------------------++testgroup_Braid_NF :: Test+testgroup_Braid_NF = testGroup "Braid/NF"+  +  [ testProperty "NF with naive inverse elimination == less naive inverse elimination"  prop_braidnf_naive+  , testProperty "NF with reduction == NF without reduction"                            prop_braidnf_reduce++  , testProperty "NF = NF of representative word of NF"   prop_braidnf_reprs+  , testProperty "NF = NF of perturbed word"              prop_braidnf_perturb++  , testProperty "linking of word == linking of representative of NF"   prop_braidnf_link++  , testProperty "NF of positive word is positive"   prop_braidnf_pos++  , testProperty "Lemma 2.5"   prop_lemma_2_5++  , testProperty "permutationBraid and tau commutes, up to NF"   prop_permTau_2+  ]++--------------------------------------------------------------------------------+-- * braid properties++prop_link_reduce :: UnreducedBraid -> Bool+prop_link_reduce (Unreduced braid) = linkingMatrix braid == linkingMatrix braid' where+  braid' = freeReduceBraidWord braid++prop_link_perturb :: PerturbedBraid -> Bool+prop_link_perturb (Perturbed braid1 braid2) = linkingMatrix braid1 == linkingMatrix braid2 ++prop_tau_square :: ReducedBraid -> Bool+prop_tau_square (Reduced braid) = braidWord (tau (tau braid)) == braidWord braid++prop_permTau_1 :: PermutationBraid -> Bool+prop_permTau_1 (PermBraid perm braid) = tauPerm perm == braidPermutation (tau braid)++prop_permBraid_perm :: PermutationBraid -> Bool+prop_permBraid_perm (PermBraid perm braid) = (braidPermutation braid == perm)++prop_permBraid_valid :: PermutationBraid -> Bool+prop_permBraid_valid (PermBraid perm braid) = isPermutationBraid braid++prop_braidPerm_comp :: TwoBraids -> Bool+prop_braidPerm_comp (TwoBraids b1 b2) = (p == q) where+  p = braidPermutation (compose b1 b2) +  q = braidPermutation b1 `P.multiply` braidPermutation b2++prop_link_positive :: PositiveBraid -> Bool+prop_link_positive (PositiveB braid) = all (>=0) $ elems $ linkingMatrix braid++prop_linking :: UnreducedBraid -> Bool+prop_linking (Unreduced braid) = (linkingMatrix braid == matrix) where+  n = numberOfStrands braid+  matrix = array ((1,1),(n,n)) [ ((i,j),strandLinking braid i j) | i<-[1..n], j<-[1..n] ]++--------------------------------------------------------------------------------++prop_braidnf_naive :: UnreducedBraid -> Bool+prop_braidnf_naive (Unreduced braid) = (braidNormalFormNaive' braid == braidNormalForm' braid)++prop_braidnf_reduce :: UnreducedBraid -> Bool+prop_braidnf_reduce (Unreduced braid) = (braidNormalForm' braid == braidNormalForm braid)++prop_braidnf_reprs :: ReducedBraid -> Bool+prop_braidnf_reprs (Reduced braid) = (nf == nf') where+  nf  = braidNormalForm braid +  nf' = braidNormalForm braid'+  braid' = nfReprWord nf++prop_braidnf_perturb :: PerturbedBraid -> Bool+prop_braidnf_perturb (Perturbed braid1 braid2) = (braidNormalForm braid1 == braidNormalForm braid2)++prop_braidnf_link :: UnreducedBraid -> Bool+prop_braidnf_link (Unreduced braid) = (linkingMatrix braid == linkingMatrix braid') where+  nf  = braidNormalForm braid +  braid' = nfReprWord nf++prop_braidnf_pos :: PositiveBraid -> Bool+prop_braidnf_pos (PositiveB braid) = (_nfDeltaExp (braidNormalForm braid) >= 0)+ +prop_lemma_2_5 :: Permutation -> Bool+prop_lemma_2_5 p = and [ check i | i<-[1..n-1] ] where+  n = P.permutationSize p+  w = _permutationBraid p+  s = permWordStartingSet n w+  check i = _isPermutationBraid n (i:w) == (not $ elem i s)++prop_permTau_2 :: PermutationBraid -> Bool+prop_permTau_2 (PermBraid perm braid) = (nf1 == nf2) where+  nf1 = braidNormalForm $ permutationBraid (tauPerm perm)+  nf2 = braidNormalForm $ tau braid++--------------------------------------------------------------------------------++
+ test/Tests/Common.hs view
@@ -0,0 +1,35 @@+
+-- | Helper routines for tests
+
+{-# LANGUAGE Rank2Types #-}
+module Tests.Common where
+
+--------------------------------------------------------------------------------
+
+import Test.QuickCheck
+import Test.QuickCheck.Gen
+
+import System.Random
+
+--------------------------------------------------------------------------------
+
+-- | Generates a random element.
+choose_ :: Random a => Gen a
+choose_ = MkGen (\r _ -> let (x,_) = random r in x)
+
+-- | Generates two random elements 
+choosePair_ :: Random a => Gen (a,a)
+choosePair_ = do
+  x <- choose_
+  y <- choose_
+  return (x,y)
+
+-- | Generates a random element.
+myMkGen :: (forall g. RandomGen g => g -> (a,g)) -> Gen a
+myMkGen fun = MkGen (\r _ -> let (x,_) = fun r in x)
+
+-- | Generates a random element.
+myMkSizedGen :: (forall g. RandomGen g => Int -> g -> (a,g)) -> Gen a
+myMkSizedGen fun = MkGen (\r siz -> let (x,_) = fun siz r in x)
+
+--------------------------------------------------------------------------------
+ test/Tests/LatticePaths.hs view
@@ -0,0 +1,111 @@++-- | Tests for lattice paths +--++{-# LANGUAGE CPP, ScopedTypeVariables, GeneralizedNewtypeDeriving, FlexibleContexts #-}+module Tests.LatticePaths where++--------------------------------------------------------------------------------++import Math.Combinat.LatticePaths++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck+import System.Random++import Control.Monad++import Data.List  ++import Math.Combinat.Classes+import Math.Combinat.Helper+import Math.Combinat.Sign+import Math.Combinat.Numbers ( factorial , binomial )++--------------------------------------------------------------------------------+-- * instances++-- | Half-length of a Dyck path+newtype Half = Half Int deriving (Eq,Show)++-- | First number is (usually) less or equal than the second+data HalfPair = HalfPair Int Int deriving (Eq,Show)++maxHalfSize :: Int+maxHalfSize = 11     -- number of paths grow exponentially++instance Arbitrary Half where+  arbitrary = liftM Half $ choose (0,maxHalfSize)    ++instance Arbitrary HalfPair where+  arbitrary = do+    n <- choose (0,maxHalfSize)     +    k <- choose (0,n+1)+    return (HalfPair k n)++fi :: Int -> Integer+fi = fromIntegral++--------------------------------------------------------------------------------+-- * test group++testgroup_LatticePaths :: Test+testgroup_LatticePaths = testGroup "Lattice paths"+  +  [ testProperty "dyck paths are in reverse lexicographic order"      prop_revlex+  , testProperty "naive Dyck path algorithm = less naive algorithm"   prop_dyck_naive+  , testProperty "counting Dyck paths"                                prop_count+  , testProperty "counting Lattice paths"                             prop_count_lattice++  , testProperty "bounded Dyck paths, def, v1"                        prop_bounded_1+  , testProperty "bounded Dyck paths, def, v2"                        prop_bounded_2+  , testProperty "bounded Dyck paths w/ high bound = all dyck paths"  prop_not_bounded++  , testProperty "zero-touching Dyck paths"              prop_touching+  , testProperty "Dyck paths w/ k peaks"                 prop_peaking++  ]++--------------------------------------------------------------------------------+-- * test properties         ++prop_revlex :: Bool+prop_revlex = and [ sort (dyckPaths m) == reverse (dyckPaths m) | m <- [0..maxHalfSize] ]++prop_dyck_naive :: Bool+prop_dyck_naive = and [ sort (dyckPathsNaive m) == sort (dyckPaths m) | m <- [0..maxHalfSize] ]++prop_count :: Bool+prop_count = and [ fi (length (dyckPaths m)) == countDyckPaths m | m <- [0..maxHalfSize] ]++prop_count_lattice :: HalfPair -> Bool+prop_count_lattice (HalfPair y x) = fi (length (latticePaths (x,y))) == countLatticePaths (x,y)++prop_bounded_1 :: HalfPair -> Bool+prop_bounded_1 (HalfPair h m) = (one == two) where+  one = sort (boundedDyckPaths h m ) +  two = sort [ p | p <- dyckPaths m  , pathHeight p <= h ]+  +prop_bounded_2 :: Half -> Half -> Bool+prop_bounded_2 (Half h) (Half m) = (one == two) where+  one = sort (boundedDyckPaths h  m ) +  two = sort [ p | p <- dyckPaths m  , pathHeight p <= h  ]++prop_not_bounded :: Bool+prop_not_bounded = and [ sort (boundedDyckPaths m m) == sort (dyckPaths m) | m <- [0..maxHalfSize] ]++prop_touching :: HalfPair -> Bool+prop_touching (HalfPair k m) = (one == two && fi (length one) == cnt) where+  one = sort (touchingDyckPaths k m) +  two = sort [ p | p <- dyckPaths m , pathNumberOfZeroTouches p == k ]+  cnt = countTouchingDyckPaths k m++prop_peaking :: HalfPair -> Bool+prop_peaking (HalfPair k m) = (one == two && fi (length one) == cnt) where+  one = sort (peakingDyckPaths k m) +  two = sort [ p | p <- dyckPaths m , pathNumberOfPeaks p == k ]+  cnt = countPeakingDyckPaths k m++--------------------------------------------------------------------------------+
+ test/Tests/Partitions/Integer.hs view
@@ -0,0 +1,107 @@++-- | Tests for integer partitions.++{-# LANGUAGE CPP, BangPatterns #-}+module Tests.Partitions.Integer where++--------------------------------------------------------------------------------++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck++import Tests.Common++import Math.Combinat.Partitions.Integer++import Data.List+import Control.Monad++-- import Data.Map (Map)+-- import qualified Data.Map as Map++import Math.Combinat.Classes+import Math.Combinat.Numbers ( factorial , binomial , multinomial )+import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * Types and instances++newtype PartitionWeight     = PartitionWeight     Int              deriving (Eq,Show)+data    PartitionWeightPair = PartitionWeightPair Int Int          deriving (Eq,Show)+data    PartitionIntPair    = PartitionIntPair    Partition Int    deriving (Eq,Show)++maxPartitionSize :: Int+maxPartitionSize = 44++instance Arbitrary Partition where+  arbitrary = do+    n <- choose (0,maxPartitionSize)+    myMkGen (randomPartition n)++instance Arbitrary PartitionWeight where+  arbitrary = liftM PartitionWeight $ choose (0,maxPartitionSize)++instance Arbitrary PartitionWeightPair where+  arbitrary = do+    n <- choose (0,maxPartitionSize)+    k <- choose (0,n+2)+    return (PartitionWeightPair n k)++instance Arbitrary PartitionIntPair where+  arbitrary = do+    part <- arbitrary+    k <- choose (0,partitionWeight part + 2)+    return (PartitionIntPair part k)++--------------------------------------------------------------------------------+-- * test group++testgroup_IntegerPartitions :: Test+testgroup_IntegerPartitions = testGroup "Integer Partitions"  ++  [ testProperty "partitions in a box"             prop_partitions_in_bigbox+  , testProperty "partitions with k parts"         prop_kparts+  , testProperty "odd partitions"                  prop_odd_partitions +  , testProperty "partitions with distinct parts"  prop_distinct_partitions  +  , testProperty "subpartitions"                   prop_subparts+  , testProperty "dual^2 is identity"              prop_dual_dual+  , testProperty "dominated partitions"            prop_dominated_list+  , testProperty "dominating partitions"           prop_dominating_list+  , testProperty "counting partitions"             prop_countParts+  ]++--------------------------------------------------------------------------------+-- * properties++prop_partitions_in_bigbox :: PartitionWeight -> Bool+prop_partitions_in_bigbox (PartitionWeight n) = (partitions n == partitions' (n,n) n)++prop_kparts :: PartitionWeightPair -> Bool+prop_kparts (PartitionWeightPair n k) = (partitionsWithKParts k n == [ mu | mu <- partitions n, numberOfParts mu == k ])++prop_odd_partitions :: PartitionWeight -> Bool+prop_odd_partitions (PartitionWeight n) = +  (partitionsWithOddParts n == [ mu | mu <- partitions n, and (map odd (fromPartition mu)) ])++prop_distinct_partitions :: PartitionWeight -> Bool+prop_distinct_partitions (PartitionWeight n) = +  (partitionsWithDistinctParts n == [ mu | mu <- partitions n, let xs = fromPartition mu, xs == nub xs ])++prop_subparts :: PartitionIntPair -> Bool+prop_subparts (PartitionIntPair lam d) = (subPartitions d lam) == sort [ p | p <- partitions d, isSubPartitionOf p lam ]++prop_dual_dual :: Partition -> Bool+prop_dual_dual lam = (lam == dualPartition (dualPartition lam))++prop_dominated_list :: Partition -> Bool+prop_dominated_list lam = (dominatedPartitions  lam == [ mu  | mu  <- partitions (weight lam), lam `dominates` mu ])++prop_dominating_list :: Partition -> Bool+prop_dominating_list mu  = (dominatingPartitions mu  == [ lam | lam <- partitions (weight mu ), lam `dominates` mu ])++prop_countParts :: Bool+prop_countParts = (take 50 partitionCountList == take 50 partitionCountListNaive)++--------------------------------------------------------------------------------+
+ test/Tests/Partitions/Skew.hs view
@@ -0,0 +1,85 @@++-- | Tests for skew partitions.+--++{-# LANGUAGE CPP, BangPatterns #-}+module Tests.Partitions.Skew where++--------------------------------------------------------------------------------++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck++import Tests.Common+import Tests.Partitions.Integer ()     -- Arbitrary instances++import Math.Combinat.Partitions.Integer+import Math.Combinat.Partitions.Skew++import Data.List++import Math.Combinat.Classes++--------------------------------------------------------------------------------+-- * instances++instance Arbitrary SkewPartition where+  arbitrary = do+    p <- arbitrary+    let n = partitionWeight p+    d <- choose (0,n)+    let qs = subPartitions d p+        ln = length qs+    k <- choose (0,ln-1)+    let q = qs !! k+    return $ mkSkewPartition (p,q) ++--------------------------------------------------------------------------------+-- * test group++testgroup_SkewPartitions :: Test+testgroup_SkewPartitions = testGroup "Skew Partitions"  ++  [ testProperty "dual^2 = identity"              prop_dual_dual+  , testProperty "dual vs. inner/outer dual"      prop_dual_from+  , testProperty "to . from = identity"           prop_from_to+  , testProperty "from . to = identity"           prop_to_from+  , testProperty "from . to . from = from"        prop_from_to_from+  , testProperty "weight vs. inner/outer weight"  prop_weight+  ]++--------------------------------------------------------------------------------+-- * properties++prop_dual_dual :: SkewPartition -> Bool+prop_dual_dual sp = (dualSkewPartition (dualSkewPartition sp) == sp)++prop_dual_from :: SkewPartition -> Bool+prop_dual_from sp = (p == dual p' && q == dual q') where+  (p,q)   = fromSkewPartition sp+  sp'     = dualSkewPartition sp+  (p',q') = fromSkewPartition sp'++prop_from_to :: SkewPartition -> Bool+prop_from_to sp = (mkSkewPartition (fromSkewPartition sp) == sp)++prop_to_from :: (Partition,Partition) -> Bool+prop_to_from (p,q) = +  case mb of+    Nothing -> True+    Just sp -> fromSkewPartition sp == (p,q)+  where+    mb = safeSkewPartition (p,q)++prop_from_to_from :: SkewPartition -> Bool+prop_from_to_from sp = (pq == pq') where+  pq  = fromSkewPartition sp+  sp' = mkSkewPartition pq+  pq' = fromSkewPartition sp'++prop_weight :: SkewPartition -> Bool+prop_weight sp = (skewPartitionWeight sp == weight p - weight q) where+  (p,q) = fromSkewPartition sp++--------------------------------------------------------------------------------
+ test/Tests/Permutations.hs view
@@ -0,0 +1,219 @@++-- | Tests for permutations. +--++{-# LANGUAGE CPP, ScopedTypeVariables, GeneralizedNewtypeDeriving, FlexibleContexts #-}+module Tests.Permutations where++--------------------------------------------------------------------------------++import Math.Combinat.Permutations++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck+import System.Random++import Control.Monad+import Control.Monad.ST++import Data.List hiding (permutations)++import Data.Array (Array)+import Data.Array.ST+import Data.Array.Unboxed+import Data.Array.IArray+import Data.Array.MArray+import Data.Array.Unsafe++import Math.Combinat.ASCII+import Math.Combinat.Classes+import Math.Combinat.Helper+import Math.Combinat.Sign+import Math.Combinat.Numbers (factorial,binomial)++--------------------------------------------------------------------------------+-- * generating permutations (random & arbitrary instances, spec types etc)++minPermSize = 1+maxPermSize = 123++newtype Elem = Elem Int deriving Eq+newtype Nat  = Nat { fromNat :: Int } deriving (Eq,Ord,Show,Num,Random)++naturalSet :: Permutation -> Array Int Elem+naturalSet perm = listArray (1,n) [ Elem i | i<-[1..n] ] where+  n = permutationSize perm++permInternalSet :: Permutation -> Array Int Elem+permInternalSet perm@(Permutation arr) = listArray (1,n) [ Elem (arr!i) | i<-[1..n] ] where+  n = permutationSize perm++sameSize :: Permutation ->  Permutation -> Bool+sameSize perm1 perm2 = ( permutationSize perm1 == permutationSize perm2)++newtype CyclicPermutation = Cyclic { fromCyclic :: Permutation } deriving Show++data SameSize = SameSize Permutation Permutation deriving Show++instance Random Permutation where+  random g = randomPermutation size g1 where+    (size,g1) = randomR (minPermSize,maxPermSize) g+  randomR _ = random++instance Random CyclicPermutation where+  random g = (Cyclic cycl,g2) where+    (size,g1) = randomR (minPermSize,maxPermSize) g+    (cycl,g2) = randomCyclicPermutation size g1+  randomR _ = random++instance Random DisjointCycles where+  random g = (disjcyc,g2) where+    (size,g1) = randomR (minPermSize,maxPermSize) g+    (perm,g2) = randomPermutation size g1+    disjcyc   = permutationToDisjointCycles perm+  randomR _ = random++instance Random SameSize where+  random g = (SameSize prm1 prm2, g3) where+    (size,g1) = randomR (minPermSize,maxPermSize) g+    (prm1,g2) = randomPermutation size g1 +    (prm2,g3) = randomPermutation size g2+  randomR _ = random++instance Arbitrary Nat where+  arbitrary = choose (Nat 0 , Nat 50)+     +instance Arbitrary Permutation       where arbitrary = choose undefined+instance Arbitrary CyclicPermutation where arbitrary = choose undefined+instance Arbitrary DisjointCycles    where arbitrary = choose undefined+instance Arbitrary SameSize          where arbitrary = choose undefined++--------------------------------------------------------------------------------+-- * test group++testgroup_Permutations :: Test+testgroup_Permutations = testGroup "Permutations"+  +  [ testProperty "disjoint cycles /1" prop_disjcyc_1+  , testProperty "disjoint cycles /2" prop_disjcyc_2 ++  , testProperty "disjoint cycles compatibility" prop_disjcyc_Mathematica++  , testProperty "random cyclic permutation is indeed cyclic" prop_randCyclic+  , testProperty "inverse^2 is identity"                      prop_inverse++  , testProperty "permutation action is group action"              prop_mulPerm+  , testProperty "left permutation action is left group action"    prop_mulPermLeft+  , testProperty "right permutation action is right group action"  prop_mulPermRight++  , testProperty "permutation action convetion"        prop_perm+  , testProperty "left permutation action convention"  prop_permLeft+  , testProperty "right permutation action convention" prop_permRight+  , testProperty "left/right permutation action convention" prop_permLeftRight++  , testProperty "cycle left"  prop_cycleLeft+  , testProperty "cycle right" prop_cycleRight++  , testProperty "sign of permutation is multiplicative"     prop_mulSign      +  , testProperty "inverse is compatible with multiplication" prop_invMul++  , testProperty "parity of cyclic permutaiton" prop_cyclSign+  , testProperty "random permutation is valid"  prop_permIsPerm+  , testProperty "definition of parity"         prop_isEven++  , testProperty "bubbleSort works"    prop_bubbleSort+  , testProperty "bubbleSort2 works"   prop_bubbleSort2+  , testProperty "number of inversions = steps in bubble sort"         prop_bubble_inversions+  , testProperty "number of inversions = actual number of inversions"  prop_number_inversions +  , testProperty "number of inversions is the same for the inverse permutation"  prop_ninversions_inverse+  , testProperty "merge sort algorithm = naive inversion count"                  prop_merge_inversions++  ]++--------------------------------------------------------------------------------+-- * test properties+          +prop_disjcyc_1 perm = ( perm == disjointCyclesToPermutation n (permutationToDisjointCycles perm) )+  where n = permutationSize perm++prop_disjcyc_2 k dcyc = ( dcyc == permutationToDisjointCycles (disjointCyclesToPermutation n dcyc) )+  where +    n = fromNat k + m +    m = case fromDisjointCycles dcyc of+      []  -> 1+      xxs -> maximum (concat xxs)++-- PermutationCycles[ { 12, 15, 5, 6, 2, 7, 17, 9, 20, 3, 11, 18, 22, 21, 8, 10, 4, 19, 14, 16, 23, 1, 13 } ]+-- Cycles           [ {{1, 12, 18, 19, 14, 21, 23, 13, 22}, {2, 15, 8, 9, 20, 16, 10, 3, 5}, {4, 6, 7, 17}} ]+prop_disjcyc_Mathematica = (permutationToDisjointCycles   perm == disjcyc) +                        && (disjointCyclesToPermutation n disjcyc == perm)+  where+    n       = permutationSize perm+    perm    = toPermutation  [ 12, 15, 5, 6, 2, 7, 17, 9, 20, 3, 11, 18, 22, 21, 8, 10, 4, 19, 14, 16, 23, 1, 13 ]+    disjcyc = DisjointCycles [ [1, 12, 18, 19, 14, 21, 23, 13, 22], [2, 15, 8, 9, 20, 16, 10, 3, 5], [4, 6, 7, 17] ]++xperm    = toPermutation  [ 12, 15, 5, 6, 2, 7, 17, 9, 20, 3, 11, 18, 22, 21, 8, 10, 4, 19, 14, 16, 23, 1, 13 ]+xdisjcyc = DisjointCycles [ [1, 12, 18, 19, 14, 21, 23, 13, 22], [2, 15, 8, 9, 20, 16, 10, 3, 5], [4, 6, 7, 17] ]++prop_randCyclic cycl = ( isCyclicPermutation (fromCyclic cycl) )++prop_inverse perm = ( perm == inverse (inverse perm) ) ++prop_mulPerm (SameSize perm1 perm2) = +    ( permute perm2 (permute perm1 set) == permute (perm1 `multiply` perm2) set ) +  where +    set = naturalSet perm1++prop_mulPermRight (SameSize perm1 perm2) = +    ( permuteRight perm2 (permuteRight perm1 set) == permuteRight (perm1 `multiply` perm2) set ) +  where +    set = naturalSet perm1++prop_mulPermLeft (SameSize perm1 perm2) = +    ( permuteLeft perm2 (permuteLeft perm1 set) == permuteLeft (perm2 `multiply` perm1) set ) +  where +    set = naturalSet perm1++prop_perm          perm = permute perm (naturalSet perm) == permInternalSet perm+prop_permLeft      perm = permuteLeft  perm (permInternalSet perm) == naturalSet perm+prop_permRight     perm = permuteRight perm (naturalSet perm) == permInternalSet perm+prop_permLeftRight perm = permuteLeft (inverse perm) (naturalSet perm) == permuteRight (perm) (naturalSet perm) ++prop_cycleLeft  = permuteList (cycleLeft  5) "abcde" == "bcdea"+prop_cycleRight = permuteList (cycleRight 5) "abcde" == "eabcd"++prop_mulSign (SameSize perm1 perm2) = +    ( sgn perm1 * sgn perm2 == sgn (perm1 `multiply` perm2) ) +  where +    sgn = signValue . signOfPermutation :: Permutation -> Int++prop_invMul (SameSize perm1 perm2) =   +  ( inverse perm2 `multiply` inverse perm1 == inverse (perm1 `multiply` perm2) ) ++prop_cyclSign cycl = ( isEvenPermutation perm == odd n ) where+  perm = fromCyclic cycl+  n = permutationSize perm+  +prop_permIsPerm perm = ( isPermutation (fromPermutation perm) ) ++prop_isEven perm = ( isEvenPermutation perm == isEvenAlternative perm ) where+  isEvenAlternative p = +    even $ sum $ map (\x->x-1) $ map length $ fromDisjointCycles $ permutationToDisjointCycles p++prop_bubbleSort perm = multiplyMany' n (map (adjacentTransposition n) $ bubbleSort perm) == perm where+  n = permutationSize perm++prop_bubbleSort2 perm = multiplyMany' n (map (transposition n) $ bubbleSort2 perm) == perm where+  n = permutationSize perm++prop_bubble_inversions perm = length (bubbleSort perm) == numberOfInversions perm++prop_number_inversions perm = length (inversions perm) == numberOfInversions perm++prop_ninversions_inverse perm = numberOfInversions perm == numberOfInversions (inverse perm)++prop_merge_inversions perm = (numberOfInversionsMerge perm == numberOfInversionsNaive perm)++--------------------------------------------------------------------------------+
+ test/Tests/Series.hs view
@@ -0,0 +1,303 @@++-- | Tests for power series+--++{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-}+module Tests.Series where++--------------------------------------------------------------------------------++import Math.Combinat.Numbers.Series++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck+import System.Random++import Data.List++import Math.Combinat.Sign+import Math.Combinat.Numbers+import Math.Combinat.Partitions.Integer+import Math.Combinat.Helper++--------------------------------------------------------------------------------+-- * code used only for tests++-- | Expansion of @1 / (1-x^k)@. Included for completeness only; +-- it equals to @coinSeries [k]@, and for example+-- for @k=4@ it is simply+-- +-- > [1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0...]+--+pseries1 :: Int -> [Integer]+pseries1 k1 = convolveWithPSeries1 k1 unitSeries ++-- | The expansion of @1 / (1-x^k_1-x^k_2)@+pseries2 :: Int -> Int -> [Integer]+pseries2 k1 k2 = convolveWithPSeries2 k1 k2 unitSeries ++-- | The expansion of @1 / (1-x^k_1-x^k_2-x^k_3)@+pseries3 :: Int -> Int -> Int -> [Integer]+pseries3 k1 k2 k3 = convolveWithPSeries3 k1 k2 k3 unitSeries++--------------------------------------------------------------------------------++-- | Convolve with (the expansion of) @1 / (1-x^k1)@+convolveWithPSeries1 :: Int -> [Integer] -> [Integer]+convolveWithPSeries1 k1 series1 = xs where+  series = series1 ++ repeat 0 +  xs = zipWith (+) series ( replicate k1 0 ++ xs )++-- | Convolve with (the expansion of) @1 / (1-x^k1-x^k2)@+convolveWithPSeries2 :: Int -> Int -> [Integer] -> [Integer]+convolveWithPSeries2 k1 k2 series1 = xs where+  series = series1 ++ repeat 0 +  xs = zipWith3 (\x y z -> x + y + z)+    series+    ( replicate k1 0 ++ xs )+    ( replicate k2 0 ++ xs )+    +-- | Convolve with (the expansion of) @1 / (1-x^k_1-x^k_2-x^k_3)@+convolveWithPSeries3 :: Int -> Int -> Int -> [Integer] -> [Integer]+convolveWithPSeries3 k1 k2 k3 series1 = xs where+  series = series1 ++ repeat 0 +  xs = zipWith4 (\x y z w -> x + y + z + w)+    series+    ( replicate k1 0 ++ xs )+    ( replicate k2 0 ++ xs )+    ( replicate k3 0 ++ xs )++--------------------------------------------------------------------------------++-- | @1 / (1 - a*x^k)@. +-- For example, for @a=3@ and @k=2@ it is just+-- +-- > [1,0,3,0,9,0,27,0,81,0,243,0,729,0,2187,0,6561,0,19683,0...]+--+pseries1' :: Num a => (a,Int) -> [a]+pseries1' ak1 = convolveWithPSeries1' ak1 unitSeries++-- | @1 / (1 - a_1*x^k_1 - a_2*x^k_2)@+pseries2' :: Num a => (a,Int) -> (a,Int) -> [a]+pseries2' ak1 ak2 = convolveWithPSeries2' ak1 ak2 unitSeries++-- | @1 / (1 - a_1*x^k_1 - a_2*x^k_2 - a_3*x^k_3)@+pseries3' :: Num a => (a,Int) -> (a,Int) -> (a,Int) -> [a]+pseries3' ak1 ak2 ak3 = convolveWithPSeries3' ak1 ak2 ak3 unitSeries++--------------------------------------------------------------------------------++-- | Convolve with @1 / (1 - a*x^k)@. +convolveWithPSeries1' :: Num a => (a,Int) -> [a] -> [a]+convolveWithPSeries1' (a1,k1) series1 = xs where+  series = series1 ++ repeat 0 +  xs = zipWith (+)+    series+    ( replicate k1 0 ++ map (*a1) xs )++-- | Convolve with @1 / (1 - a_1*x^k_1 - a_2*x^k_2)@+convolveWithPSeries2' :: Num a => (a,Int) -> (a,Int) -> [a] -> [a]+convolveWithPSeries2' (a1,k1) (a2,k2) series1 = xs where+  series = series1 ++ repeat 0 +  xs = zipWith3 (\x y z -> x + y + z)+    series+    ( replicate k1 0 ++ map (*a1) xs )+    ( replicate k2 0 ++ map (*a2) xs )+    +-- | Convolve with @1 / (1 - a_1*x^k_1 - a_2*x^k_2 - a_3*x^k_3)@+convolveWithPSeries3' :: Num a => (a,Int) -> (a,Int) -> (a,Int) -> [a] -> [a]+convolveWithPSeries3' (a1,k1) (a2,k2) (a3,k3) series1 = xs where+  series = series1 ++ repeat 0 +  xs = zipWith4 (\x y z w -> x + y + z + w)+    series+    ( replicate k1 0 ++ map (*a1) xs )+    ( replicate k2 0 ++ map (*a2) xs )+    ( replicate k3 0 ++ map (*a3) xs )++--------------------------------------------------------------------------------+-- * Types and instances++{-+swap :: (a,b) -> (b,a)+swap (x,y) = (y,x)+-}++-- compare the first 500 elements of the infinite lists+(=!=) :: (Eq a, Num a) => [a] -> [a] -> Bool+(=!=) xs1 ys1 = (take m xs == take m ys) where +  m = 500+  xs = xs1 ++ repeat 0+  ys = ys1 ++ repeat 0++infix 4 =!=++newtype Nat = Nat { fromNat :: Int } deriving (Eq,Ord,Show,Num,Random)+newtype Ser = Ser { fromSer :: [Integer] } deriving (Eq,Ord,Show)+newtype Exp  = Exp  { fromExp  ::  Int  } deriving (Eq,Ord,Show,Num,Random)+newtype Exps = Exps { fromExps :: [Int] } deriving (Eq,Ord,Show)+newtype CoeffExp  = CoeffExp  { fromCoeffExp  ::  (Integer,Int)  } deriving (Eq,Ord,Show)+newtype CoeffExps = CoeffExps { fromCoeffExps :: [(Integer,Int)] } deriving (Eq,Ord,Show)++minSerSize = 0    :: Int+maxSerSize = 1000 :: Int++minSerValue = -10000 :: Int+maxSerValue =  10000 :: Int++rndList :: (RandomGen g, Random a) => Int -> (a, a) -> g -> ([a], g)+rndList n minmax g = swap $ mapAccumL f g [1..n] where+  f g _ = swap $ randomR minmax g ++instance Arbitrary Nat where+  arbitrary = choose (Nat 0 , Nat 750)++instance Arbitrary Exp where+  arbitrary = choose (Exp 1 , Exp 32)++instance Arbitrary CoeffExp where+  arbitrary = do+    coeff <- choose (minSerValue, maxSerValue) :: Gen Int+    exp   <- arbitrary :: Gen Exp+    return $ CoeffExp (fromIntegral coeff, fromExp exp)+   +instance Random Ser where+  random g = (Ser $ map fi list, g2) where+    (size,g1) = randomR (minSerSize,maxSerSize) g+    (list,g2) = rndList size (minSerValue,maxSerValue) g1+    fi :: Int -> Integer+    fi = fromIntegral +  randomR _ = random++instance Random Exps where+  random g = (Exps list, g2) where+    (size,g1) = randomR (0,10) g+    (list,g2) = rndList size (1,32) g1+  randomR _ = random++instance Random CoeffExps where+  random g = (CoeffExps (zip (map fromIntegral list2) list1), g3) where+    (size,g1) = randomR (0,10) g+    (list1,g2) = rndList size (1,32) g1+    (list2,g3) = rndList size (minSerValue,maxSerValue) g2+  randomR _ = random+  +instance Arbitrary Ser where+  arbitrary = choose undefined++instance Arbitrary Exps where+  arbitrary = choose undefined++instance Arbitrary CoeffExps where+  arbitrary = choose undefined++--------------------------------------------------------------------------------+-- * test group++testgroup_PowerSeries :: Test+testgroup_PowerSeries = testGroup "Power series"+  [ +    testProperty "convPSeries1 vs generic"     prop_conv1_vs_gen+  , testProperty "convPSeries2 vs generic"     prop_conv2_vs_gen+  , testProperty "convPSeries3 vs generic"     prop_conv3_vs_gen+  , testProperty "convPSeries1' vs generic"    prop_conv1_vs_gen'+  , testProperty "convPSeries2' vs generic"    prop_conv2_vs_gen'+  , testProperty "convPSeries3' vs generic"    prop_conv3_vs_gen'+  , testProperty "convolve_pseries"            prop_convolve_pseries +  , testProperty "convolve_pseries'"           prop_convolve_pseries' +  , testProperty "coinSeries vs pseries"       prop_coin_vs_pseries+  , testProperty "coinSeries vs pseries'"      prop_coin_vs_pseries'++    -- these are very slow, because random is slow+  , testProperty "leftIdentity"    prop_leftIdentity+  , testProperty "rightIdentity"   prop_rightIdentity+  , testProperty "commutativity"   prop_commutativity+  , testProperty "associativity"   prop_associativity+  ]++--------------------------------------------------------------------------------+-- * properties+     +prop_leftIdentity ser = ( xs =!= unitSeries `convolve` xs ) where +  xs = fromSer ser ++prop_rightIdentity ser = ( unitSeries `convolve` xs =!= xs ) where +  xs = fromSer ser ++prop_commutativity ser1 ser2 = ( xs `convolve` ys =!= ys `convolve` xs ) where +  xs = fromSer ser1+  ys = fromSer ser2++prop_associativity ser1 ser2 ser3 = ( one =!= two ) where+  one = (xs `convolve` ys) `convolve` zs+  two = xs `convolve` (ys `convolve` zs)+  xs = fromSer ser1+  ys = fromSer ser2+  zs = fromSer ser3+  +prop_conv1_vs_gen exp1 ser = ( one =!= two ) where+  one = convolveWithPSeries1 k1 xs +  two = convolveWithPSeries [k1] xs+  k1 = fromExp exp1+  xs = fromSer ser  ++prop_conv2_vs_gen exp1 exp2 ser = (one =!= two) where+  one = convolveWithPSeries2 k1 k2 xs +  two = convolveWithPSeries [k2,k1] xs+  k1 = fromExp exp1+  k2 = fromExp exp2+  xs = fromSer ser  ++prop_conv3_vs_gen exp1 exp2 exp3 ser = (one =!= two) where+  one = convolveWithPSeries3 k1 k2 k3 xs +  two = convolveWithPSeries [k2,k3,k1] xs+  k1 = fromExp exp1+  k2 = fromExp exp2+  k3 = fromExp exp3+  xs = fromSer ser  ++prop_conv1_vs_gen' exp1 ser = ( one =!= two ) where+  one = convolveWithPSeries1' ak1 xs +  two = convolveWithPSeries' [ak1] xs+  ak1 = fromCoeffExp exp1+  xs = fromSer ser  ++prop_conv2_vs_gen' exp1 exp2 ser = (one =!= two) where+  one = convolveWithPSeries2' ak1 ak2 xs +  two = convolveWithPSeries' [ak2,ak1] xs+  ak1 = fromCoeffExp exp1+  ak2 = fromCoeffExp exp2+  xs = fromSer ser  ++prop_conv3_vs_gen' exp1 exp2 exp3 ser = (one =!= two) where+  one = convolveWithPSeries3' ak1 ak2 ak3 xs +  two = convolveWithPSeries' [ak2,ak3,ak1] xs+  ak1 = fromCoeffExp exp1+  ak2 = fromCoeffExp exp2+  ak3 = fromCoeffExp exp3+  xs = fromSer ser  ++prop_convolve_pseries exps1 ser = (one =!= two) where+  one = convolveWithPSeries ks1 xs +  two = xs `convolve` pseries ks1 +  ks1 = fromExps exps1+  xs = fromSer ser  ++prop_convolve_pseries' cexps1 ser = (one =!= two) where+  one = convolveWithPSeries' aks1 xs +  two = xs `convolve` pseries' aks1 +  aks1 = fromCoeffExps cexps1+  xs = fromSer ser  ++prop_coin_vs_pseries exps1 = (one =!= two) where+  one = coinSeries ks1 +  two = convolveMany (map pseries1 ks1)+  ks1 = fromExps exps1++prop_coin_vs_pseries' cexps1 = (one =!= two) where+  one = coinSeries' aks1 +  two = convolveMany (map pseries1' aks1)+  aks1 = fromCoeffExps cexps1+    +--------------------------------------------------------------------------------+
+ test/Tests/SkewTableaux.hs view
@@ -0,0 +1,103 @@+
+-- | Tests for skew tableaux
+
+{-# LANGUAGE FlexibleInstances #-}
+module Tests.SkewTableaux where
+
+--------------------------------------------------------------------------------
+
+import Control.Monad
+
+import Test.Framework
+import Test.Framework.Providers.QuickCheck2
+import Test.QuickCheck
+import Test.QuickCheck.Gen
+
+import Tests.Partitions.Integer ()
+import Tests.Partitions.Skew    ()      -- arbitrary instances
+
+import Math.Combinat.Tableaux
+import Math.Combinat.Tableaux.Skew
+import Math.Combinat.Partitions.Integer
+import Math.Combinat.Partitions.Skew
+
+--------------------------------------------------------------------------------
+-- * code
+
+numberOfNonEmptyRows :: SkewPartition -> Int
+numberOfNonEmptyRows (SkewPartition xys) = length [ True | (x,y) <- xys, y/=0 ]
+
+-- | Breaks a skew partition into disjoint parts
+disjointParts :: SkewPartition -> [SkewPartition]
+disjointParts (SkewPartition xys) = map normalizeSkewPartition list where
+
+  list = map SkewPartition $ filter (not . isEmpty) $ break xys
+
+  isEmpty :: [(Int,Int)] -> Bool
+  isEmpty xys = and [ y==0 | (x,y) <- xys ]
+
+  break :: [(Int,Int)] -> [[(Int,Int)]]
+  break []   = [[]]
+  break [xy] = [[xy]]
+  break ( xy@(x,y) : rest@((x',y'):_) ) = if x >= x'+y' 
+    then [xy] : break rest
+    else let (     xys  : rest' ) = break rest
+         in  ( (xy:xys) : rest' )
+  
+  
+
+
+--------------------------------------------------------------------------------
+-- * instances 
+
+instance Arbitrary (SkewTableau Int) where
+  arbitrary = do
+    shape <- arbitrary
+    let w = skewPartitionWeight shape
+    content <- replicateM w $ choose (1,1000)
+    return $ fillSkewPartitionWithRowWord shape content
+
+--------------------------------------------------------------------------------
+-- * test group
+
+testgroup_SkewTableaux :: Test
+testgroup_SkewTableaux = testGroup "Skew tableaux"
+  [ testProperty "dual^2 = identity"            prop_skew_dual_dual
+  , testProperty "fill . rowWord = identity"    prop_rowWord
+  , testProperty "fill . columnWord = identity" prop_columnWord
+  , testProperty "fill respectes the shape"     prop_fill_shape 
+  , testProperty "semistandard skew tableaux are indeed semistandard"   prop_semistandard 
+  ]
+
+--------------------------------------------------------------------------------
+-- * properties
+
+prop_skew_dual_dual :: SkewTableau Int -> Bool
+prop_skew_dual_dual st = (dualSkewTableau (dualSkewTableau st) == st)
+
+prop_rowWord :: SkewTableau Int -> Bool
+prop_rowWord st = (fillSkewPartitionWithRowWord shape content == st) where
+  shape   = skewTableauShape st
+  content = skewTableauRowWord st
+
+prop_columnWord :: SkewTableau Int -> Bool
+prop_columnWord st = (fillSkewPartitionWithColumnWord shape content == st) where
+  shape   = skewTableauShape st
+  content = skewTableauColumnWord st
+
+prop_fill_shape :: SkewPartition -> Bool
+prop_fill_shape shape = (shape == shape') where
+  tableau = fillSkewPartitionWithColumnWord shape [1..]
+  shape'  = skewTableauShape tableau
+
+prop_semistandard :: SkewPartition -> Bool
+prop_semistandard shape = and 
+  [ isSemiStandardSkewTableau st 
+  | n  <- [kk..nn] 
+  , st <- take 500 (semiStandardSkewTableaux n shape)         -- we only take the first 500 because impossibly slow otherwise
+  ]
+  where
+    nn = min (kk + 10) (skewPartitionWeight shape)
+    kk = maximum $ 0 : (map numberOfNonEmptyRows $ disjointParts shape)
+
+--------------------------------------------------------------------------------
+ test/Tests/Thompson.hs view
@@ -0,0 +1,134 @@++-- | Tests for Thompson's group F+--++{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances, TypeSynonymInstances #-}+module Tests.Thompson where++--------------------------------------------------------------------------------++import Prelude hiding ( (**) )+import Control.Monad+import Data.List++import Math.Combinat.Groups.Thompson.F+import qualified Math.Combinat.Trees.Binary as B++import Tests.Common++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck+import System.Random++import Math.Combinat.Helper+++--------------------------------------------------------------------------------+-- * code++(**) :: TDiag -> TDiag -> TDiag+(**) x y = x `compose` y++(//) :: TDiag -> TDiag -> TDiag+(//) x y = x `compose` (inverse y)++growth_n_sphere     = [1,4,12,36,108,314,906,2576,7280,20352] :: [Int]+growth_pos_n_sphere = [1,2, 4, 9, 20, 45,101, 227, 510, 1146] :: [Int]++--------------------------------------------------------------------------------+-- * instances++-- | A pair of trees with the same size+data TPair = TPair !T !T deriving (Eq,Show)++newtype Unreduced = Unreduced TDiag deriving (Eq,Show)++instance Arbitrary T where+  arbitrary = liftM fromBinTree $ myMkSizedGen B.randomBinaryTree++instance Arbitrary TPair where+  arbitrary = myMkSizedGen $ \siz -> runRand $ do+    t1 <- rand (B.randomBinaryTree siz)+    t2 <- rand (B.randomBinaryTree siz)+    return $ TPair (fromBinTree t1) (fromBinTree t2)++instance Arbitrary TDiag where+  arbitrary = do +    TPair t1 t2 <- arbitrary+    return $ mkTDiag t1 t2++instance Arbitrary Unreduced where+  arbitrary = do +    TPair t1 t2 <- arbitrary+    return $ Unreduced $ mkTDiagDontReduce t1 t2++--------------------------------------------------------------------------------+-- * test group++testgroup_ThompsonF :: Test+testgroup_ThompsonF = testGroup "Thompson's group F"+  [ testProperty "identity element"                    prop_identity+  , testProperty "associativity"                       prop_assoc+  , testProperty "standard relations"                  prop_relations+  , testProperty "quotient of positives"               prop_quot_positive+  , testProperty "telescopic product"                  prop_telescope+  , testProperty "cyclic telescopic product (3)"       prop_cyclic_product_3+  , testProperty "cyclic telescopic product (4)"       prop_cyclic_product_4+  , testProperty "positive diagrams form a monoid"     prop_positive_product+  , testProperty "composition commutes with reduction" prop_reduce_composition+  , testProperty "inverse commutes with reduction"     prop_reduce_inverse+  ]++--------------------------------------------------------------------------------+-- * properties+    +prop_relations :: Bool+prop_relations = and [ rel k n | n<-[1..30] , k<-[0..n-1] ] where+  rel k n = (inverse $ xk k) `compose` (xk n) `compose` (xk k) == xk (n+1)++prop_quot_positive :: TPair -> Bool+prop_quot_positive (TPair t1 t2) = (mkTDiag t1 t2) == (positive t1 // positive t2)++prop_identity :: TDiag -> Bool+prop_identity x = (x ** identity) == x && (identity ** x) == x++prop_assoc :: TDiag -> TDiag -> TDiag -> Bool+prop_assoc a b c = (p == q) where+  p = compose (compose a b) c+  q = compose a (compose b c)++prop_telescope :: TDiag -> TDiag -> TDiag -> TDiag -> Bool+prop_telescope u v w z = (a `compose` b `compose` c) == (u // z) where+  a = u // v+  b = v // w+  c = w // z++prop_cyclic_product_3 :: TDiag -> TDiag -> TDiag -> Bool+prop_cyclic_product_3 u v w = (a `compose` b `compose` c) == identity where+  a = u // v+  b = v // w+  c = w // u++prop_cyclic_product_4 :: TDiag -> TDiag -> TDiag -> TDiag -> Bool+prop_cyclic_product_4 u v w z = (a `compose` b `compose` c `compose` d) == identity where+  a = u // v+  b = v // w+  c = w // z+  d = z // u++prop_positive_product :: T -> T -> Bool+prop_positive_product x y = isPositive (positive x `compose` positive y)++prop_reduce_composition :: Unreduced -> Unreduced -> Bool+prop_reduce_composition (Unreduced x) (Unreduced y) = lhs == rhs where+  lhs = reduce (x `composeDontReduce` y)+  rhs = compose (reduce x) (reduce y)++prop_reduce_inverse :: Unreduced -> Bool+prop_reduce_inverse (Unreduced x) = lhs == rhs where+  lhs = reduce (inverse x)+  rhs = inverse (reduce x)++--------------------------------------------------------------------------------+