uAgda-1.2.0.4: Cubes.hs
{-# LANGUAGE TypeSynonymInstances #-}
module Cubes where
import Data.List
import Data.Array.IArray
import Data.Char
import Display
import Data.Monoid
import Permutation
data Bit = Zero | One deriving (Ord,Eq,Ix)
newtype BitVector = BV { unBV :: Array Int Bit} deriving (Eq)
instance Ord BitVector where
(BV i) <= (BV j) = all (\d -> i!d <= j!d) (range $ bounds i)
i < j = i <= j && i /= j
type Cube a = Array BitVector a
bitsFromString :: String -> [Bit]
bitsFromString ('0':xs) = Zero : bitsFromString xs
bitsFromString ('1':xs) = One : bitsFromString xs
bitsFromString [] = []
bvFromString :: String -> BitVector
bvFromString s = BV $ listArray (dims $ length s) (bitsFromString s)
subscriptPrettyBV :: BitVector -> Doc
subscriptPrettyBV bv = mconcat $ map (subscriptPretty . b2i) $ elems $ unBV $ bv
instance Monoid BitVector where
mempty = nil
mappend i j = BV $ listArray (dims $ bvDim i + bvDim j) $ elems (unBV i) ++ elems (unBV j)
cubeAccess :: String -> Cube a -> BitVector -> a
cubeAccess loc c i | dim c /= bvDim i = error $ loc ++ ": cube access: mismatched dimensions: " ++ show (dim c) ++ " /= " ++ show (bvDim i)
| otherwise = c ! i
(!?) :: Cube a -> BitVector -> a
c !? i = cubeAccess "??" c i
instance Show Bit where
show Zero = "0"
show One = "1"
instance Show BitVector where
show = concatMap show . elems . unBV
b2i Zero = 0
b2i One = 1
cubeElems :: Cube a -> [a]
cubeElems = elems
cubeAssocs :: Cube a -> [(BitVector,a)]
cubeAssocs = assocs
bits :: BitVector -> [Bit]
bits = elems . unBV
bitsToInt :: [Bit] -> Int
bitsToInt [] = 0
bitsToInt (x:xs) = b2i x + 2 * (bitsToInt xs)
toInt :: BitVector -> Int
toInt = bitsToInt . reverse . elems . unBV
-- | Number of set bits in the vector
setBits :: BitVector -> Int
setBits (BV i) = sum $ map b2i $ elems $ i
-- | Number of set clear in the vector
clearBits i = bvDim i - setBits i
bvTail (BV i) = BV $ listArray (0,h-1) $ tail $ elems i
where (0,h) = bounds i
bvIndex :: BitVector -> Int
bvIndex i | i == nil = 1
bvIndex i | otherwise = case unBV i!0 of
Zero -> bvIndex (bvTail i)
One -> bvIndex (bvTail i) + choose (bvDim i-1) (setBits i)
choose n 0 = 1
choose 0 k = 0
choose n k = choose (n-1) (k-1) * n `div` k
prettyBV0 :: BitVector -> String
prettyBV0 i = chr (ord 'a' + setBits i) : show (bvIndex i)
instance Pretty BitVector where
pretty = text . show
specialPretty i = superscriptPretty (setBits i) <> subscriptPretty (bvIndex i)
instance Ix BitVector where
index (l,h) i = toInt i - toInt l
range (BV l,BV h) = [BV $ listArray (bounds l) i | i <- rngs (elems l) (elems h)]
inRange (BV l,BV h) (BV i) = all (\(d,j) -> inRange (l!d,h!d) j) (assocs i)
-- "Product" of ranges
rngs [] [] = [[]]
rngs (a:as) (b:bs) = [x:xs | x <- range (a,b), xs <- rngs as bs]
bvDim i = 1 + (snd $ bounds $ unBV i)
-- Dimension of a cube
dim :: Cube a -> Int
dim c = bvDim (snd $ bounds $ c)
-- "Range" for a bitvector of dim. d
dims d = (0,d-1)
zeros d = BV $ listArray (dims d) (replicate d Zero)
ones d = BV $ listArray (dims d) (replicate d One)
nil :: BitVector
nil = BV $ listArray (dims 0) []
-- "Range" for a cube
spn d = (zeros d, ones d)
instance Permutable BitVector where
apply p (BV i) = BV $ ixmap (bounds i) (apply p) i
-- instance Permutable (Cube a) where
-- apply p c = ixmap (bounds c) (apply p) c
instance (Ix ix,Permutable ix) => Permutable (Array ix a) where
apply p a = ixmap (bounds a) (apply p) a
b2b Zero = False
b2b One = True
bv2bools (BV bv) = map b2b $ elems bv
-- Apply a function on elements of the cube that lie at the intersection of 2 dimensions
subAppl :: Permutation -> (Permutation -> a -> a) -> Cube a -> Cube a
subAppl p f c = listArray (bounds c) [f (project p (bv2bools i)) e | (i,e) <- assocs c]
full :: (BitVector -> a) -> Int -> Cube a
full f d = array (spn d) [(i,f i) | i <- range $ spn d]
unit :: a -> Cube a
unit a = listArray (spn 0) [a]
pair :: a -> a -> Cube a
pair a b = listArray (spn 1) [a,b]
cmap :: (a -> b) -> Cube a -> Cube b
cmap = fmap
{-
prettyCube :: Cube Doc -> [Doc]
prettyCube terms = [a ++ " " ++ prettyArgs i terms | (i,a) <- assocs terms]
-}
projectDim d valueKept c
| d0 == 0 = error "projecting trivial cube"
| d >= d0 = error "projecting away non-existing dimension"
| otherwise = listArray (spn $ d0-1) [a | (i,a) <- assocs c, unBV i!d == valueKept]
where d0 = dim c
-- prettyArgs :: BitVector -> Cube Doc -> Doc
-- prettyArgs i c = foldr mempty (<+>) [a | (j,a) <- assocs c, j < i]
interleave [] [] = []
interleave (x:xs) (y:ys) = x:y:interleave xs ys
cubeCons :: Cube a -> Cube a -> Cube a
cubeCons c1 c2 = listArray (spn (d+1)) (interleave (elems c1) (elems c2))
where d = dim c1
subCubeAt :: BitVector -> Cube a -> Cube a
subCubeAt i c = listArray (spn d) [a | (j,a) <- assocs c, keep j]
where d0 = dim c
d = d0 - clearBits i
keep j = and [(x == One) || (y == Zero) | (x,y) <- zip (elems $ unBV i) (elems $ unBV j)]
updateCube :: BitVector -> a -> Cube a -> Cube a
updateCube i x c = c // [(i,x)]
log2 :: Int -> Maybe Int
log2 0 = Nothing
log2 1 = return 0
log2 x = case quotRem x 2 of
(x',0) -> (+1) `fmap` log2 x'
(_,1) -> Nothing
cubeFromList :: [a] -> Maybe (Cube a)
cubeFromList xs = do
dim <- log2 (length xs)
return $ listArray (spn dim) xs