import Control.Monad import Data.Maybe import Data.Bits import Data.Array import Data.List import qualified Data.Set as S import qualified Data.IntMap as IM import Text.Printf import System.IO type Board = Array (Int,Int) Bool unhex :: Char -> [Bool] unhex c = fromJust (lookup c table) where table = zip (['0'..'9'] ++ ['A'..'F']) [[testBit i j | j <- [3,2..0]] | i <- [(0::Int) .. 15]] mkBoard :: Int -> Int -> [[Bool]] -> Board mkBoard m n rows = array ((1,1),(m,n)) $ do (i,row) <- zip [1..] rows (j,v) <- zip [1..] row return ((i,j),v) showBoard :: Board -> String showBoard board = concat [[if board ! (i,j) then '1' else '0' | j <- [1..n]] ++ "\n" | i <- [1..m]] where (_, (m,n)) = bounds board f :: Int -> Int -> Board -> [(Int,Int)] f m n board = reverse $ IM.toAscList $ IM.fromListWith (+) [(size,1) | size <- g m n board (min m n) S.empty] g :: Int -> Int -> Board -> Int -> S.Set (Int,Int) -> [Int] g m n board 0 used = [] g m n board size used = case cs of [] -> g m n board (size - 1) used (i,j):_ -> size : g m n board size (S.union used (S.fromList [(i',j') | i' <- [i..i+size-1], j'<-[j..j+size-1]])) where cs = do i <- [1..m-size+1] j <- [1..n-size+1] guard $ isChecker board i j size guard $ and $ do i' <- [i..i+size-1] j' <- [j..j+size-1] return $ S.notMember (i',j') used return (i,j) isChecker :: Board -> Int -> Int -> Int -> Bool isChecker board i j size = and $ do (i', b) <- zip [i..i+size-1] (iterate not (board ! (i,j))) (j',b') <- zip [j..j+size-1] (iterate not b) return $ (board ! (i',j')) == b' main :: IO () main = do t <- readLn forM_ [(1::Int)..t] $ \x -> do [m,n] <- liftM (map read . words) getLine rows <- forM [1..m] $ \_ -> do row <- getLine return $ concatMap unhex row let board = mkBoard m n rows xs = f m n board printf "Case #%d: %d\n" x (length xs) forM_ xs $ \(size, cnt) -> printf "%d %d\n" size cnt hFlush stdout