-- | Ribbons (also called border strips, skew hooks, skew rim hooks, etc...). -- -- Ribbons are skew partitions that are 1) connected, 2) do not contain -- 2x2 blocks. Intuitively, they are 1-box wide continuous strips on -- the boundary. -- -- An alternative definition that they are skew partitions whose projection -- to the diagonal line is a continuous segment of width 1. {-# LANGUAGE BangPatterns, ScopedTypeVariables #-} module Math.Combinat.Partitions.Skew.Ribbon where -------------------------------------------------------------------------------- import Data.Array import Data.List import Data.Maybe import qualified Data.Map as Map import Math.Combinat.Sets import Math.Combinat.Partitions.Integer import Math.Combinat.Partitions.Integer.IntList ( _diffSequence ) import Math.Combinat.Partitions.Skew import Math.Combinat.Tableaux import Math.Combinat.Tableaux.LittlewoodRichardson import Math.Combinat.Tableaux.GelfandTsetlin import Math.Combinat.Helper -------------------------------------------------------------------------------- -- * Corners (TODO: move to Partitions - but we also want to refactor that) -- | The coordinates of the outer corners outerCorners :: Partition -> [(Int,Int)] outerCorners = outerCornerBoxes -- | The coordinates of the inner corners, including the two on the two coordinate -- axes. For the partition @[5,4,1]@ the result should be @[(0,5),(1,4),(2,1),(3,0)]@ extendedInnerCorners:: Partition -> [(Int,Int)] extendedInnerCorners (Partition_ ps) = (0, head ps') : catMaybes mbCorners where ps' = ps ++ [0] mbCorners = zipWith3 f [1..] (tail ps') (_diffSequence ps') f !y !x !k = if k > 0 then Just (y,x) else Nothing -- | Sequence of all the (extended) corners extendedCornerSequence :: Partition -> [(Int,Int)] extendedCornerSequence (Partition_ ps) = {- if null ps then [(0,0)] else -} interleave inner outer where inner = (0, head ps') : [ (y,x) | (y,x,k) <- zip3 [1..] (tail ps') diff , k>0 ] outer = [ (y,x) | (y,x,k) <- zip3 [1..] ps' diff , k>0 ] diff = _diffSequence ps' ps' = ps ++ [0] -- | The inner corner /boxes/ of the partition. Coordinates are counted from 1 -- (cf.the 'elements' function), and the first coordinate is the row, the second -- the column (in English notation). -- -- For the partition @[5,4,1]@ the result should be @[(1,4),(2,1)]@ -- -- > innerCornerBoxes lambda == (tail $ init $ extendedInnerCorners lambda) -- innerCornerBoxes :: Partition -> [(Int,Int)] innerCornerBoxes (Partition_ ps) = case ps of [] -> [] _ -> catMaybes mbCorners where mbCorners = zipWith3 f [1..] (tail ps) (_diffSequence ps) f !y !x !k = if k > 0 then Just (y,x) else Nothing -- | The outer corner /boxes/ of the partition. Coordinates are counted from 1 -- (cf.the 'elements' function), and the first coordinate is the row, the second -- the column (in English notation). -- -- For the partition @[5,4,1]@ the result should be @[(1,5),(2,4),(3,1)]@ outerCornerBoxes :: Partition -> [(Int,Int)] outerCornerBoxes (Partition_ ps) = catMaybes mbCorners where mbCorners = zipWith3 f [1..] ps (_diffSequence ps) f !y !x !k = if k > 0 then Just (y,x) else Nothing -- | The outer and inner corner boxes interleaved, so together they form -- the turning points of the full border strip cornerBoxSequence :: Partition -> [(Int,Int)] cornerBoxSequence (Partition_ ps) = if null ps then [] else interleave outer inner where inner = [ (y,x) | (y,x,k) <- zip3 [1..] tailps diff , k>0 ] outer = [ (y,x) | (y,x,k) <- zip3 [1..] ps diff , k>0 ] diff = _diffSequence ps tailps = case ps of { [] -> [] ; _-> tail ps } -------------------------------------------------------------------------------- -- | Naive (and very slow) implementation of @innerCornerBoxes@, for testing purposes innerCornerBoxesNaive :: Partition -> [(Int,Int)] innerCornerBoxesNaive part = filter f boxes where boxes = elements part f (y,x) = elem (y+1,x ) boxes && elem (y ,x+1) boxes && not (elem (y+1,x+1) boxes) -- | Naive (and very slow) implementation of @outerCornerBoxes@, for testing purposes outerCornerBoxesNaive :: Partition -> [(Int,Int)] outerCornerBoxesNaive part = filter f boxes where boxes = elements part f (y,x) = not (elem (y+1,x ) boxes) && not (elem (y ,x+1) boxes) && not (elem (y+1,x+1) boxes) -------------------------------------------------------------------------------- -- * Ribbon -- | A skew partition is a a ribbon (or border strip) if and only if projected -- to the diagonals the result is an interval. isRibbon :: SkewPartition -> Bool isRibbon skewp = go Nothing proj where proj = Map.toList $ Map.fromListWith (+) [ (x-y , 1) | (y,x) <- skewPartitionElements skewp ] go Nothing [] = False go (Just _) [] = True go Nothing ((a,h):rest) = (h == 1) && go (Just a) rest go (Just b) ((a,h):rest) = (h == 1) && (a == b+1) && go (Just a) rest {- -- | Naive (and slow) reference implementation of "isRibbon" isRibbonNaive :: SkewPartition -> Bool isRibbonNaive skewp = isConnectedSkewPartition skewp && no2x2 where boxes = skewPartitionElements skewp no2x2 = and [ not ( elem (y+1,x ) boxes && elem (y ,x+1) boxes && elem (y+1,x+1) boxes ) -- no 2x2 blocks | (y,x) <- boxes ] -} toRibbon :: SkewPartition -> Maybe Ribbon toRibbon skew = if not (isRibbon skew) then Nothing else Just ribbon where ribbon = Ribbon { rbShape = skew , rbLength = skewPartitionWeight skew , rbHeight = height , rbWidth = width } elems = skewPartitionElements skew height = (length $ group $ sort $ map fst elems) - 1 -- TODO: optimize these width = (length $ group $ sort $ map snd elems) - 1 -- | Border strips (or ribbons) are defined to be skew partitions which are -- connected and do not contain 2x2 blocks. -- -- The /length/ of a border strip is the number of boxes it contains, -- and its /height/ is defined to be one less than the number of rows -- (in English notation) it occupies. The /width/ is defined symmetrically to -- be one less than the number of columns it occupies. -- data Ribbon = Ribbon { rbShape :: SkewPartition , rbLength :: Int , rbHeight :: Int , rbWidth :: Int } deriving (Eq,Ord,Show) -------------------------------------------------------------------------------- -- * Inner border strips -- | Ribbons (or border strips) are defined to be skew partitions which are -- connected and do not contain 2x2 blocks. This function returns the -- border strips whose outer partition is the given one. innerRibbons :: Partition -> [Ribbon] innerRibbons part@(Partition ps) = if null ps then [] else strips where strips = [ mkStrip i j | i<-[1..n] , _canStartStrip (annArr!i) , j<-[i..n] , _canEndStrip (annArr!j) ] n = length annList annList = annotatedInnerBorderStrip part annArr = listArray (1,n) annList mkStrip !i1 !i2 = Ribbon shape len height width where ps' = ps ++ [0] shape = SkewPartition [ (p-k,k) | (i,p,q) <- zip3 [1..] ps (tail ps') , let k = indent i p q ] indent !i !p !q | i < y1 = 0 | i > y2 = 0 | i == y2 = p - x2 + 1 -- the order is important here !!! | otherwise = p - q + 1 -- because of the case y1 == y2 == i len = i2 - i1 + 1 height = y2 - y1 width = x1 - x2 BorderBox _ _ y1 x1 = annArr ! i1 BorderBox _ _ y2 x2 = annArr ! i2 -- | Inner border strips (or ribbons) of the given length innerRibbonsOfLength :: Partition -> Int -> [Ribbon] innerRibbonsOfLength part@(Partition ps) givenLength = if null ps then [] else strips where strips = [ mkStrip i j | i<-[1..n] , _canStartStrip (annArr!i) , j<-[i..n] , _canEndStrip (annArr!j) , j-i+1 == givenLength ] n = length annList annList = annotatedInnerBorderStrip part annArr = listArray (1,n) annList mkStrip !i1 !i2 = Ribbon shape givenLength height width where ps' = ps ++ [0] shape = SkewPartition [ (p-k,k) | (i,p,q) <- zip3 [1..] ps (tail ps') , let k = indent i p q ] indent !i !p !q | i < y1 = 0 | i > y2 = 0 | i == y2 = p - x2 + 1 -- the order is important here !!! | otherwise = p - q + 1 -- because of the case y1 == y2 == i height = y2 - y1 width = x1 - x2 BorderBox _ _ y1 x1 = annArr ! i1 BorderBox _ _ y2 x2 = annArr ! i2 -------------------------------------------------------------------------------- -- * Outer border strips -- | Hooks of length @n@ (TODO: move to the partition module) listHooks :: Int -> [Partition] listHooks 0 = [] listHooks 1 = [ Partition [1] ] listHooks n = [ Partition (k : replicate (n-k) 1) | k<-[1..n] ] -- | Outer border strips (or ribbons) of the given length outerRibbonsOfLength :: Partition -> Int -> [Ribbon] outerRibbonsOfLength part@(Partition ps) givenLength = result where result = if null ps then [ Ribbon shape givenLength ht wd | p <- listHooks givenLength , let shape = mkSkewPartition (p,part) , let ht = partitionWidth p - 1 -- pretty inconsistent names here :((( , let wd = partitionHeight p - 1 ] else strips strips = [ mkStrip i j | i<-[1..n] , _canStartStrip (annArr!i) , j<-[i..n] , _canEndStrip (annArr!j) , j-i+1 == givenLength ] ysize = partitionWidth part xsize = partitionHeight part annList = [ BorderBox True False 1 x | x <- reverse [xsize+2 .. xsize+givenLength ] ] ++ annList0 ++ [ BorderBox False True y 1 | y <- [ysize+2 .. ysize+givenLength ] ] n = length annList annList0 = annotatedOuterBorderStrip part annArr = listArray (1,n) annList mkStrip !i1 !i2 = Ribbon shape len height width where ps' = (-666) : ps ++ replicate (givenLength) 0 shape = SkewPartition [ (p,k) | (i,p,q) <- zip3 [1..max ysize y2] (tail ps') ps' , let k = indent i p q ] indent !i !p !q | i < y1 = 0 | i > y2 = 0 | i == y1 = x1 - p -- the order is important here !!! -- | i == y2 = x2 - p | otherwise = q - p + 1 len = i2 - i1 + 1 height = y2 - y1 width = x1 - x2 BorderBox _ _ y1 x1 = annArr ! i1 BorderBox _ _ y2 x2 = annArr ! i2 -------------------------------------------------------------------------------- -- * Naive implementations (for testing) -- | Naive (and slow) implementation listing all inner border strips innerRibbonsNaive :: Partition -> [Ribbon] innerRibbonsNaive outer = list where list = [ Ribbon skew (len skew) (ht skew) (wt skew) | skew <- allSkewPartitionsWithOuterShape outer , isRibbon skew ] len skew = length (skewPartitionElements skew) ht skew = (length $ group $ sort $ map fst $ skewPartitionElements skew) - 1 wt skew = (length $ group $ sort $ map snd $ skewPartitionElements skew) - 1 -- | Naive (and slow) implementation listing all inner border strips of the given length innerRibbonsOfLengthNaive :: Partition -> Int -> [Ribbon] innerRibbonsOfLengthNaive outer givenLength = list where pweight = partitionWeight outer list = [ Ribbon skew (len skew) (ht skew) (wt skew) | skew <- skewPartitionsWithOuterShape outer givenLength , isRibbon skew ] len skew = length (skewPartitionElements skew) ht skew = (length $ group $ sort $ map fst $ skewPartitionElements skew) - 1 wt skew = (length $ group $ sort $ map snd $ skewPartitionElements skew) - 1 -- | Naive (and slow) implementation listing all outer border strips of the given length outerRibbonsOfLengthNaive :: Partition -> Int -> [Ribbon] outerRibbonsOfLengthNaive inner givenLength = list where pweight = partitionWeight inner list = [ Ribbon skew (len skew) (ht skew) (wt skew) | skew <- skewPartitionsWithInnerShape inner givenLength , isRibbon skew ] len skew = length (skewPartitionElements skew) ht skew = (length $ group $ sort $ map fst $ skewPartitionElements skew) - 1 wt skew = (length $ group $ sort $ map snd $ skewPartitionElements skew) - 1 -------------------------------------------------------------------------------- -- * Annotated borders -- | A box on the border of a partition data BorderBox = BorderBox { _canStartStrip :: !Bool , _canEndStrip :: !Bool , _yCoord :: !Int , _xCoord :: !Int } deriving Show -- | The boxes of the full inner border strip, annotated with whether a border strip -- can start or end there. annotatedInnerBorderStrip :: Partition -> [BorderBox] annotatedInnerBorderStrip partition = if isEmptyPartition partition then [] else list where list = goVert (head corners) (tail corners) corners = extendedCornerSequence partition goVert (y1,x ) ((y2,_ ):rest) = [ BorderBox True (y==y2) y x | y<-[y1+1..y2] ] ++ goHoriz (y2,x) rest goVert _ [] = [] goHoriz (y ,x1) ((_, x2):rest) = case rest of [] -> [ BorderBox False True y x | x<-[x1-1,x1-2..x2+1] ] _ -> [ BorderBox False (x/=x2) y x | x<-[x1-1,x1-2..x2 ] ] ++ goVert (y,x2) rest -- | The boxes of the full outer border strip, annotated with whether a border strip -- can start or end there. annotatedOuterBorderStrip :: Partition -> [BorderBox] annotatedOuterBorderStrip partition = if isEmptyPartition partition then [] else list where list = goVert (head corners) (tail corners) corners = extendedCornerSequence partition goVert (y1,x ) ((y2,_ ):rest) = [ BorderBox (y==y1) (y/=y2) (y+1) (x+1) | y<-[y1..y2] ] ++ goHoriz (y2,x) rest goVert _ [] = [] goHoriz (y ,x1) ((_, x2):rest) = case rest of [] -> [ BorderBox True (x==0) (y+1) (x+1) | x<-[x1-1,x1-2..x2 ] ] _ -> [ BorderBox True False (y+1) (x+1) | x<-[x1-1,x1-2..x2+1] ] ++ goVert (y,x2) rest --------------------------------------------------------------------------------