import Data.Array.Parallel.Base ( Tag, tagToInt, fromBool ) import qualified GHC.Base import Prelude ((.), ($), Num(..), Eq(..), seq) import qualified Prelude instance Elt Int instance Elt Word8 instance Elt Bool instance Elt Float instance Elt Double instance (Elt a, Elt b) => Elt (a, b) infixl 9 !: infixr 5 +:+ -- Basics --------------------------------------------------------------------- -- | O(1). Take the number of elements in an array. length :: Elt a => Array a -> Int {-# INLINE_BACKEND length #-} -- Constructors --------------------------------------------------------------- -- | An array with no elements. empty :: Elt a => Array a {-# INLINE_BACKEND empty #-} -- | O(n). Append two arrays. (+:+) :: Elt a => Array a -> Array a -> Array a {-# INLINE_BACKEND (+:+) #-} -- | Generate a new array given its length and a function to compute each element. generate :: Elt a => Int -> (Int -> a) -> Array a {-# INLINE_BACKEND generate #-} generate n f = map f (enumFromTo 0 (n-1)) generate_cheap :: Elt a => Int -> (Int -> a) -> Array a {-# INLINE_BACKEND generate_cheap #-} generate_cheap n f = map f (enumFromTo 0 (n-1)) -- | O(n). Produce a new array by replicating a single element the given number of times. replicate :: Elt a => Int -> a -> Array a {-# INLINE CONLIKE PHASE_BACKEND replicate #-} {-# RULES "seq/replicate" forall n x y. seq (replicate n x) y = n `seq` x `seq` y #-} -- | Produce an array by copying a portion of another array. repeat :: Elt a => Int -- ^ number of times to repeat the source -> Int -- ^ length of source (can be less than the provided array) -> Array a -- ^ array elements to repeat -> Array a {-# INLINE_BACKEND repeat #-} -- | Tag each element of an array with its index. -- -- Example: @indexed [:42, 93, 13:] = [:(0, 42), (1, 93), (2, 13):]@ indexed :: Elt a => Array a -> Array (Int, a) {-# INLINE_BACKEND indexed #-} -- | Generate a range of @Int@s. enumFromTo :: Int -> Int -> Array Int {-# INLINE_BACKEND enumFromTo #-} enumFromThenTo :: Int -> Int -> Int -> Array Int {-# INLINE_BACKEND enumFromThenTo #-} enumFromStepLen :: Int -> Int -> Int -> Array Int {-# INLINE_BACKEND enumFromStepLen #-} enumFromStepLenEach :: Int -> Array Int -> Array Int -> Array Int -> Array Int {-# INLINE_BACKEND enumFromStepLenEach #-} {-# RULES "repeat/enumFromStepLen[Int]" forall i j k n len. repeat n len (enumFromStepLen i j k) = generate_cheap len (\m -> i + ((m `Prelude.rem` k) * j)) #-} -- Projections ---------------------------------------------------------------- -- | O(1). Retrieve a numbered element from an array. (!:) :: Elt a => Array a -> Int -> a {-# INLINE_BACKEND (!:) #-} -- | O(n). Extract a subrange of elements from an array. -- Example: @extract [:23, 42, 93, 50, 27:] 1 3 = [:42, 93, 50:]@ extract :: Elt a => Array a -- ^ source array -> Int -- ^ starting index in source array -> Int -- ^ length of result array -> Array a {-# INLINE_BACKEND extract #-} -- | O(n). Drop some elements from the front of an array, -- returning the latter portion. drop :: Elt a => Int -> Array a -> Array a {-# INLINE_BACKEND drop #-} -- Permutation ---------------------------------------------------------------- -- | O(n). Forwards permutation of array elements. permute :: Elt a => Array a -- ^ source array -> Array Int -- ^ indices in the destination to copy elements to -> Array a {-# INLINE_BACKEND permute #-} -- | O(n). Backwards permutation of array elements. -- -- Example @bpermute [:50, 60, 20, 30:] 3 [:0, 3, 2:] = [:50, 30, 20:]@ bpermute :: Elt a => Array a -- ^ source array -> Array Int -- ^ indices in the source to copy elements from. -> Array a {-# INLINE_BACKEND bpermute #-} -- | Combination of map and bpermute. -- -- The advantage of using this combined version is that we dont need -- to apply the parameter function to source elements that dont appear -- in the result. mbpermute :: (Elt a, Elt b) => (a->b) -> Array a -> Array Int -> Array b {-# INLINE_BACKEND mbpermute #-} -- | Default backwards permutation. -- -- * The values of the index-value pairs are written into the position in the -- result array that is indicated by the corresponding index. -- -- * All positions not covered by the index-value pairs will have the value -- determined by the initialiser function for that index position. -- bpermuteDft:: Elt e => Int -> (Int -> e) -> Array (Int, e) -> Array e {-# INLINE_BACKEND bpermuteDft #-} {-# RULES "bpermute/repeat" forall n len xs is. bpermute (repeat n len xs) is = len `Prelude.seq` bpermute xs (map (dph_mod_index len) is) "bpermute/bpermute" forall xs is js. bpermute (bpermute xs is) js = bpermute xs (bpermute is js) #-} -- Update --------------------------------------------------------------------- -- | O(n). Copy the source array in the destination, using new values for the given indices. update :: Elt a => Array a -> Array (Int, a) -> Array a {-# INLINE_BACKEND update #-} -- Packing and Combining ----------------------------------------------------- -- | Extract the elements from an array that match the given predicate. filter :: Elt a => (a -> Bool) -> Array a -> Array a {-# INLINE_BACKEND filter #-} -- | Extract elements of an array where the associated flag is true. pack :: Elt a => Array a -> Array Bool -> Array a {-# INLINE_BACKEND pack #-} -- | Combine two arrays, using a tag array to tell us where to get each element from. -- -- Example: @combine [T,F,F,T,T,F] [1,2,3] [4,5,6] = [1,4,5,2,3,6]@ combine :: Elt a => Array Bool -> Array a -> Array a -> Array a {-# INLINE_BACKEND combine #-} -- | Like `combine`, but use a precomputed selector to speed up the process. -- -- See dph-prim-seq:"Data.Array.Parallel.Unlifted.Sequential.Segmented.USel" -- for a description of how this works. -- combine2 :: Elt a => Array Tag -> SelRep2 -> Array a -> Array a -> Array a {-# INLINE_BACKEND combine2 #-} -- | Interleave the elements of two arrays. -- -- Example: @interleave [1,2,3] [4,5,6] = [1,4,2,5,3,6]@ interleave :: Elt a => Array a -> Array a -> Array a {-# INLINE_BACKEND interleave #-} -- Zipping and Unzipping ------------------------------------------------------ -- | O(1). Takes two arrays and returns an array of corresponding pairs. -- If one array is short, excess elements of the longer array are discarded. zip :: (Elt a, Elt b) => Array a -> Array b -> Array (a, b) {-# INLINE CONLIKE PHASE_BACKEND zip #-} -- | O(1). Transform an array into an array of the first components, -- and an array of the second components. unzip :: (Elt a, Elt b) => Array (a, b) -> (Array a, Array b) {-# INLINE_BACKEND unzip #-} -- | O(1). Take the first elements of an array of pairs. fsts :: (Elt a, Elt b) => Array (a, b) -> Array a {-# INLINE_BACKEND fsts #-} -- | O(1). Take the second elements of an array of pairs. snds :: (Elt a, Elt b) => Array (a, b) -> Array b {-# INLINE_BACKEND snds #-} -- Maps and zipWith ----------------------------------------------------------- -- | Apply a worker function to each element of an array, yielding a new array. map :: (Elt a, Elt b) => (a -> b) -> Array a -> Array b {-# INLINE_BACKEND map #-} -- | zipWith generalises zip by zipping with the function given as the first -- argument, instead of a tupling function. zipWith :: (Elt a, Elt b, Elt c) => (a -> b -> c) -> Array a -> Array b -> Array c {-# INLINE_BACKEND zipWith #-} zipWith3 :: (Elt a, Elt b, Elt c, Elt d) => (a -> b -> c -> d) -> Array a -> Array b -> Array c -> Array d {-# INLINE zipWith3 #-} zipWith3 f xs ys zs = zipWith (\(x, y) z -> f x y z) (zip xs ys) zs zipWith4 :: (Elt a, Elt b, Elt c, Elt d, Elt e) => (a -> b -> c -> d -> e) -> Array a -> Array b -> Array c -> Array d -> Array e {-# INLINE zipWith4 #-} zipWith4 f as bs cs ds = zipWith (\(a, b) (c, d) -> f a b c d) (zip as bs) (zip cs ds) -- Generally useful rules ------------- {-# RULES "zipWith/replicate" forall f m n x y. zipWith f (replicate m x) (replicate n y) = replicate m (f x y) "zipWith/plusInt0_1" forall n xs. zipWith GHC.Base.plusInt (replicate n (GHC.Base.I# 0#)) xs = xs "zipWith/plusInt0_2" forall n xs. zipWith GHC.Base.plusInt xs (replicate n (GHC.Base.I# 0#)) = xs "zipWith(plusInt)/enumFromStepLen" forall i1 k1 n1 i2 k2 n2. zipWith GHC.Base.plusInt (enumFromStepLen i1 k1 n1) (enumFromStepLen i2 k2 n2) = enumFromStepLen (i1+i2) (k1+k2) n1 #-} -- FIXME: These are the SMVM rules. They are intentionally quite specific and -- we want to get rid of the ASAP. {-# RULES "map/zipWith (+)/enumFromStepLen" forall m n is. map (dph_mod_index m) (zipWith GHC.Base.plusInt (enumFromStepLen 0 m n) is) = map (dph_mod_index m) is "map dph_mod_index/enumFromStepLenEach" forall k l is n1 n2. map (dph_mod_index k) (enumFromStepLenEach l is (replicate n1 (GHC.Base.I# 1#)) (replicate n2 k)) = enumFromStepLenEach l (map (dph_mod_index k) is) (replicate n1 (GHC.Base.I# 1#)) (replicate n2 k) "map dph_mod_index/replicate_s" forall k segd xs. map (dph_mod_index k) (replicate_s segd xs) = replicate_s segd (map (dph_mod_index k) xs) "map dph_mod_index/enumFromStepLen" forall k# i n. map (dph_mod_index (GHC.Base.I# k#)) (enumFromStepLen i (GHC.Base.I# k#) n) = replicate n i "enumFromStepLenEach/replicate x 3" forall k m n1 n2 n3. enumFromStepLenEach m (replicate n1 (GHC.Base.I# 0#)) (replicate n2 (GHC.Base.I# 1#)) (replicate n3 k) = generate_cheap m (dph_mod_index k) "bpermute/generate_cheap" forall n f xs. bpermute (generate_cheap n f) xs = map f xs #-} -- The following rules fuse arithmetic operations that shouldnt have been -- vectorised in the first place. For example, with z = x * y + a, the vectoriser -- will lift * and + to vector operations. The result of the the multiply will be -- written to a vector, and then read back to do the addition. -- -- Adding the zipWith rules ensures that the multiply and addition are performed -- in one go. On the other hand, they can break fusion in the backend library. -- -- NOTE: These rules are only temporary, they should go away when we have -- vectorisation avoidance for scalar operations. {- RULES **************** DISABLED "zipWith/zipWith/zipWith" forall f g h as bs cs ds. zipWith f (zipWith g as bs) (zipWith h cs ds) = zipWith4 (\a b c d -> f (g a b) (h c d)) as bs cs ds "zipWith/zipWith_left" forall f g as bs cs. zipWith f (zipWith g as bs) cs = zipWith3 (\a b c -> f (g a b) c) as bs cs "zipWith/zipWith_right" forall f g as bs cs. zipWith f as (zipWith g bs cs) = zipWith3 (\a b c -> f a (g b c)) as bs cs -} -- More rules to recover from the lack of vectorisation avoidance. -- The regular form of the rules shows why we really dont want to do it this way. {- RULES ****************** DISABLED "map/zipWith" forall f g xs ys. map f (zipWith g xs ys) = zipWith (\x y -> f (g x y)) xs ys "zipWith3/map_1" forall f g xs ys zs. zipWith3 f (map g xs) ys zs = zipWith3 (\x y z -> f (g x) y z) xs ys zs "zipWith3/map_2" forall f g xs ys zs. zipWith3 f xs (map g ys) zs = zipWith3 (\x y z -> f x (g y) z) xs ys zs "zipWith3/map_3" forall f g xs ys zs. zipWith3 f xs ys (map g zs) = zipWith3 (\x y z -> f x y (g z)) xs ys zs -} -- Folds ---------------------------------------------------------------------- -- | Left fold over an array. fold :: Elt a => (a -> a -> a) -> a -> Array a -> a {-# INLINE_BACKEND fold #-} -- | Left fold over an array, using the first element to initialise the state. fold1 :: Elt a => (a -> a -> a) -> Array a -> a {-# INLINE_BACKEND fold1 #-} -- | Compute the conjunction of all elements in a boolean array. and :: Array Bool -> Bool {-# INLINE_BACKEND and #-} -- | Compute the sum of an array of numbers. sum :: (Num a, Elt a) => Array a -> a {-# INLINE_BACKEND sum #-} -- | Similar to `foldl` but return an array of the intermediate states, including -- the final state that is computed by `foldl`. scan :: Elt a => (a -> a -> a) -> a -> Array a -> Array a {-# INLINE_BACKEND scan #-} {-# RULES "seq/sum" forall xs e. seq (sum xs) e = seq xs e "seq/scan (+)" forall i xs e. seq (scan GHC.Base.plusInt i xs) e = i `seq` xs `seq` e "scan/replicate" forall z n x. scan GHC.Base.plusInt z (replicate n x) = enumFromStepLen z x n #-} -- Segmented Constructors ----------------------------------------------------- append_s :: Elt a => Segd -- ^ segment descriptor of result aarray -> Segd -- ^ segment descriptor of first array -> Array a -- ^ data of first array -> Segd -- ^ segment descriptor of second array -> Array a -- ^ data of first array -> Array a {-# INLINE_BACKEND append_s #-} replicate_s :: Elt a => Segd -> Array a -> Array a {-# INLINE CONLIKE PHASE_BACKEND replicate_s #-} replicate_rs :: Elt a => Int -> Array a -> Array a {-# INLINE CONLIKE PHASE_BACKEND replicate_rs #-} {-# RULES "append_s->interleave" forall n k idxs1 idxs2 idxs3 m1 m2 m3 xs ys. append_s (mkSegd (replicate n k) idxs1 m1) (mkSegd (replicate n (GHC.Base.I# 1#)) idxs2 m2) xs (mkSegd (replicate n (GHC.Base.I# 1#)) idxs3 m3) ys = interleave xs ys #-} {-# RULES "replicate_s/replicate" forall segd k x. replicate_s segd (replicate k x) = replicate (elementsSegd segd) x "replicate_s->replicate_rs" forall n m idxs nm xs. replicate_s (mkSegd (replicate n m) idxs nm) xs = replicate_rs m xs "replicate_rs/replicate" forall m n x. replicate_rs m (replicate n x) = replicate (m*n) x "sum/replicate_rs" forall n xs. sum (replicate_rs n xs) = sum xs * n "count/replicate_s" forall segd xs tag. count (replicate_s segd xs) tag = sum (packByTag (lengthsSegd segd) xs tag) #-} -- Segmented Folds ------------------------------------------------------------ fold_s :: Elt a => (a -> a -> a) -> a -> Segd -> Array a -> Array a {-# INLINE_BACKEND fold_s #-} fold1_s :: Elt a => (a -> a -> a) -> Segd -> Array a -> Array a {-# INLINE_BACKEND fold1_s #-} fold_r :: Elt a => (a -> a -> a) -> a -> Int -> Array a -> Array a {-# INLINE_BACKEND fold_r #-} sum_s :: (Num a, Elt a) => Segd -> Array a -> Array a {-# INLINE sum_s #-} sum_s = fold_s (Prelude.+) 0 sum_r :: (Num a, Elt a) => Int ->Array a -> Array a {-# INLINE_BACKEND sum_r #-} {-# RULES "fold_s/replicate1" forall f z n idxs n' xs. fold_s f z (mkSegd (replicate n (GHC.Base.I# 1#)) idxs n') xs = xs "fold_s/replicate" forall f z m n idxs mn xs. fold_s f z (mkSegd (replicate m n) idxs mn) xs = fold_r f z n xs #-} -- Operations on Segment Descriptors ------------------------------------------ indices_s :: Segd -> Array Int {-# INLINE_BACKEND indices_s #-} lengthSegd :: Segd -> Int {-# INLINE_BACKEND lengthSegd #-} lengthsSegd :: Segd -> Array Int {-# INLINE_BACKEND lengthsSegd #-} indicesSegd :: Segd -> Array Int {-# INLINE_BACKEND indicesSegd #-} elementsSegd :: Segd -> Int {-# INLINE_BACKEND elementsSegd #-} lengthsToSegd :: Array Int -> Segd {-# INLINE lengthsToSegd #-} lengthsToSegd ns = mkSegd ns (scan (+) 0 ns) (sum ns) mkSegd :: Array Int -> Array Int -> Int -> Segd {-# INLINE CONLIKE PHASE_BACKEND mkSegd #-} plusSegd :: Segd -> Segd -> Segd {-# INLINE plusSegd #-} plusSegd segd1 segd2 = mkSegd (zipWith (+) (lengthsSegd segd1) (lengthsSegd segd2)) (zipWith (+) (indicesSegd segd1) (indicesSegd segd2)) (elementsSegd segd1 `dph_plus` elementsSegd segd2) {-# RULES "lengthsSegd/mkSegd" forall lens idxs n. lengthsSegd (mkSegd lens idxs n) = lens "indicesSegd/mkSegd" forall lens idxs n. indicesSegd (mkSegd lens idxs n) = idxs "elementsSegd/mkSegd" forall lens idxs n. elementsSegd (mkSegd lens idxs n) = n "seq/elementsSegd" forall segd e. seq (elementsSegd segd) e = seq segd e "seq/mkSegd" forall lens idxs n e. seq (mkSegd lens idxs n) e = lens `seq` idxs `seq` n `seq` e #-} -- Operations on Selectors ---------------------------------------------------- -- | O(1). Construct a selector. Selectors are used to speed up the `combine2` operation. -- -- See dph-prim-seq:"Data.Array.Parallel.Unlifted.Sequential.Segmented.USel" -- for a description of how this works. mkSel2 :: Array Tag -- ^ tags array -> Array Int -- ^ indices array -> Int -- ^ number of elements taken from first source array -> Int -- ^ number of elements taken from second source array -> SelRep2 -> Sel2 {-# INLINE CONLIKE PHASE_BACKEND mkSel2 #-} -- | O(1). Get the tags array of a selector. tagsSel2 :: Sel2 -> Array Tag {-# INLINE_BACKEND tagsSel2 #-} -- | O(1). Get the indices array of a selector. indicesSel2 :: Sel2 -> Array Int {-# INLINE_BACKEND indicesSel2 #-} -- | O(1). Get the number of elements that will be taken from the first array. elementsSel2_0 :: Sel2 -> Int {-# INLINE_BACKEND elementsSel2_0 #-} -- | O(1). Get the number of elements that will be taken from the second array. elementsSel2_1 :: Sel2 -> Int {-# INLINE_BACKEND elementsSel2_1 #-} repSel2 :: Sel2 -> SelRep2 {-# INLINE_BACKEND repSel2 #-} mkSelRep2 :: Array Tag -> SelRep2 {-# INLINE CONLIKE PHASE_BACKEND mkSelRep2 #-} indicesSelRep2 :: Array Tag -> SelRep2 -> Array Int {-# INLINE_BACKEND indicesSelRep2 #-} elementsSelRep2_0 :: Array Tag -> SelRep2 -> Int {-# INLINE_BACKEND elementsSelRep2_0 #-} elementsSelRep2_1 :: Array Tag -> SelRep2 -> Int {-# INLINE_BACKEND elementsSelRep2_1 #-} -- | O(n), Compute a selector from a tags array. tagsToSel2 :: Array Tag -> Sel2 {-# INLINE tagsToSel2 #-} tagsToSel2 tags = let rep = mkSelRep2 tags in mkSel2 tags (indicesSelRep2 tags rep) (elementsSelRep2_0 tags rep) (elementsSelRep2_1 tags rep) rep {-# RULES "tagsSel2/mkSel2" forall ts is n0 n1 r. tagsSel2 (mkSel2 ts is n0 n1 r) = ts "indicesSel2/mkSel2" forall ts is n0 n1 r. indicesSel2 (mkSel2 ts is n0 n1 r) = is "elementsSel2_0/mkSel2" forall ts is n0 n1 r. elementsSel2_0 (mkSel2 ts is n0 n1 r) = n0 "elementsSel2_1/mkSel2" forall ts is n0 n1 r. elementsSel2_1 (mkSel2 ts is n0 n1 r) = n1 "repSel2/mkSel2" forall ts is n0 n1 r. repSel2 (mkSel2 ts is n0 n1 r) = r #-} -- Packing and Picking -------------------------------------------------------- -- | Select the elements of an array that have a corresponding tag. -- -- @ -- packByTag [12, 24, 42, 93] [1, 0, 0, 1] 0 -- = [24, 42] -- @ -- packByTag :: Elt a => Array a -- ^ data values -> Array Tag -- ^ tag values -> Tag -- ^ the tag of values to select -> Array a -- ^ data values that had that tag {-# INLINE_BACKEND packByTag #-} packByTag xs tags !tag = fsts (filter (\p -> Prelude.snd p == tag) (zip xs tags)) pick :: (Elt a, Eq a) => Array a -> a -> Array Bool {-# INLINE pick #-} pick xs !x = map (x==) xs {-# RULES "tagZeroes" UNTIL_PHASE_BACKEND forall xs n. map fromBool (zipWith GHC.Base.eqInt xs (replicate n (GHC.Base.I# 0#))) = tagZeroes xs "replicate_s/tagZeroes" forall lens idxs n. replicate_s (mkSegd lens idxs n) (tagZeroes lens) = replicate n 0 "packByTag/replicate" forall xs n t u. packByTag xs (replicate n t) u = if t == u then xs else empty #-} {-# RULES "packByTag/bpermute" forall xs is tags n. packByTag (bpermute xs is) tags n = bpermute xs (packByTag is tags n) #-} -- Counting ------------------------------------------------------------------- -- | Count the number of elements in array that are equal to the given value. count :: (Elt a, Eq a) => Array a -> a -> Int {-# INLINE_BACKEND count #-} count xs !x = sum (map (tagToInt . fromBool . (==) x) xs) -- | Count the number of elements in segments that are equal to the given value. count_s :: (Elt a, Eq a) => Segd -> Array a -> a -> Array Int {-# INLINE_BACKEND count_s #-} count_s segd xs !x = sum_s segd (map (tagToInt . fromBool . (==) x) xs) {-# RULES "count/seq" forall xs x y. seq (count xs x) y = seq xs (seq x y) #-} -- Random Arrays -------------------------------------------------------------- randoms :: (Elt a, System.Random.Random a, System.Random.RandomGen g) => Int -> g -> Array a {-# INLINE_BACKEND randoms #-} randomRs :: (Elt a, System.Random.Random a, System.Random.RandomGen g) => Int -> (a,a) -> g -> Array a {-# INLINE_BACKEND randomRs #-} -- Array IO ------------------------------------------------------------------- instance IOElt Int instance IOElt Double instance (IOElt a, IOElt b) => IOElt (a, b) -- | Write an array to a file. hPut :: IOElt a => Handle -> Array a -> IO () {-# INLINE_BACKEND hPut #-} -- | Read an array from a file. hGet :: IOElt a => Handle -> IO (Array a) {-# INLINE_BACKEND hGet #-} -- | Convert an array to a list of elements. toList :: Elt a => Array a -> [a] {-# INLINE_BACKEND toList #-} -- | Convert a list of elements to an array. fromList :: Elt a => [a] -> Array a {-# INLINE_BACKEND fromList #-} -- Aliases for primitive operations ------------------------------------------- dph_mod_index :: Int -> Int -> Int {-# INLINE_BACKEND dph_mod_index #-} dph_mod_index by idx = idx `GHC.Base.remInt` by dph_plus :: Int -> Int -> Int {-# INLINE_BACKEND dph_plus #-} dph_plus x y = x Prelude.+ y {-# RULES "dph_plus" forall m n. dph_plus (GHC.Base.I# m) (GHC.Base.I# n) = GHC.Base.I# m Prelude.+ GHC.Base.I# n #-} dph_mult :: Int -> Int -> Int {-# INLINE_BACKEND dph_mult #-} dph_mult x y = x Prelude.* y tagZeroes :: Array Int -> Array Tag {-# INLINE CONLIKE PHASE_BACKEND tagZeroes #-} tagZeroes xs = map (\x -> fromBool (x==0)) xs ------------------------------------------------------------------------------- -- Currently disabled rules ------------------------------------------------------------------------------- {- RULES "packByTag/combine2ByTag" forall tags1 xs ys tags2 n. packByTag (combine2ByTag tags1 xs ys) tags2 n = combine2ByTag (packByTag tags1 tags2 n) (packByTag xs (packByTag tags2 tags1 0) n) (packByTag ys (packByTag tags2 tags1 1) n) -}