Commit 5ff8ce7f authored by milan's avatar milan
Browse files

Replace FiniteMap and UniqFM with counterparts from containers.

The original interfaces are kept. There is small performance improvement:
- when compiling for five nofib, we get following speedups:
    Average                -----           -2.5%
    Average                -----           -0.6%
    Average                -----           -0.5%
    Average                -----           -5.5%
    Average                -----          -10.3%
- when compiling HPC ten times, we get:
    switches                          oldmaps   newmaps
    -O -fasm                          117.402s  116.081s (98.87%)
    -O -fasm -fregs-graph             119.993s  118.735s (98.95%)
    -O -fasm -fregs-iterative         120.191s  118.607s (98.68%)
parent 71c7067b
......@@ -27,10 +27,6 @@ import FastTypes
-- NOTE: This only works for arcitectures with just RcInteger and RcDouble
-- (which are disjoint) ie. x86, x86_64 and ppc
--
-- BL 2007/09
-- Doing a nice fold over the UniqSet makes trivColorable use
-- 32% of total compile time and 42% of total alloc when compiling SHA1.lhs from darcs.
--
-- The number of allocatable regs is hard coded here so we can do a fast
-- comparision in trivColorable.
--
......@@ -92,17 +88,42 @@ accSqueeze
-> UniqFM reg
-> FastInt
accSqueeze count maxCount squeeze ufm
= case ufm of
NodeUFM _ _ left right
-> case accSqueeze count maxCount squeeze right of
count' -> case count' >=# maxCount of
False -> accSqueeze count' maxCount squeeze left
True -> count'
LeafUFM _ reg -> count +# squeeze reg
EmptyUFM -> count
accSqueeze count maxCount squeeze ufm = acc count (eltsUFM ufm)
where acc count [] = count
acc count _ | count >=# maxCount = count
acc count (r:rs) = acc (count +# squeeze r) rs
{- Note [accSqueeze]
~~~~~~~~~~~~~~~~~~~~
BL 2007/09
Doing a nice fold over the UniqSet makes trivColorable use
32% of total compile time and 42% of total alloc when compiling SHA1.lhs from darcs.
Therefore the UniqFM is made non-abstract and we use custom fold.
MS 2010/04
When converting UniqFM to use Data.IntMap, the fold cannot use UniqFM internal
representation any more. But it is imperative that the assSqueeze stops
the folding if the count gets greater or equal to maxCount. We thus convert
UniqFM to a (lazy) list, do the fold and stops if necessary, which was
the most efficient variant tried. Benchmark compiling 10-times SHA1.lhs follows.
(original = previous implementation, folding = fold of the whole UFM,
lazyFold = the current implementation,
hackFold = using internal representation of Data.IntMap)
original folding hackFold lazyFold
-O -fasm (used everywhere) 31.509s 30.387s 30.791s 30.603s
100.00% 96.44% 97.72% 97.12%
-fregs-graph 67.938s 74.875s 62.673s 64.679s
100.00% 110.21% 92.25% 95.20%
-fregs-iterative 89.761s 143.913s 81.075s 86.912s
100.00% 160.33% 90.32% 96.83%
-fnew-codegen 38.225s 37.142s 37.551s 37.119s
100.00% 97.17% 98.24% 97.11%
-fnew-codegen -fregs-graph 91.786s 91.51s 87.368s 86.88s
100.00% 99.70% 95.19% 94.65%
-fnew-codegen -fregs-iterative 206.72s 343.632s 194.694s 208.677s
100.00% 166.23% 94.18% 100.95%
-}
trivColorable
:: (RegClass -> VirtualReg -> FastInt)
......
......@@ -3,24 +3,21 @@
% (c) The AQUA Project, Glasgow University, 1994-1998
%
``Finite maps'' are the heart of the compiler's
lookup-tables/environments and its implementation of sets. Important
stuff!
``Finite maps'' are the heart of the compiler's lookup-tables/environments
and its implementation of sets. Important stuff!
This code is derived from that in the paper:
\begin{display}
S Adams
"Efficient sets: a balancing act"
Journal of functional programming 3(4) Oct 1993, pp553-562
\end{display}
The implementation uses @Data.Map@ from the containers package, which
is both maintained and faster than the past implementation (see commit log).
The code is SPECIALIZEd to various highly-desirable types (e.g., Id)
near the end.
The orinigal interface is being kept around. It maps directly to Data.Map,
only ``Data.Map.union'' is left-biased and ``plusFM'' right-biased and
``addToFM\_C'' and ``Data.Map.insertWith'' differ in the order of
arguments of combining function.
\begin{code}
module FiniteMap (
-- * Mappings keyed from arbitrary types
FiniteMap, -- abstract type
FiniteMap, -- abstract data type
-- ** Manipulating those mappings
emptyFM, unitFM, listToFM,
......@@ -48,31 +45,11 @@ module FiniteMap (
bagToFM
) where
#if defined(DEBUG_FINITEMAPS)/* NB NB NB */
#define OUTPUTABLE_key , Outputable key
#else
#define OUTPUTABLE_key {--}
#endif
import Maybes
import Bag ( Bag, foldrBag )
import Outputable
#if 0
import GHC.Exts
-- was this import only needed for I#, or does it have something
-- to do with the (not-presently-used) IF_NCG also?
#endif
import Data.List
#if 0
#if ! OMIT_NATIVE_CODEGEN
# define IF_NCG(a) a
#else
# define IF_NCG(a) {--}
#endif
#endif
import qualified Data.Map as M
\end{code}
......@@ -87,55 +64,57 @@ import Data.List
emptyFM :: FiniteMap key elt
unitFM :: key -> elt -> FiniteMap key elt
-- | In the case of duplicates keys, the last item is taken
listToFM :: (Ord key OUTPUTABLE_key) => [(key,elt)] -> FiniteMap key elt
listToFM :: (Ord key) => [(key,elt)] -> FiniteMap key elt
-- | In the case of duplicate keys, who knows which item is taken
bagToFM :: (Ord key OUTPUTABLE_key) => Bag (key,elt) -> FiniteMap key elt
bagToFM :: (Ord key) => Bag (key,elt) -> FiniteMap key elt
-- ADDING AND DELETING
-- | Throws away any previous binding
addToFM :: (Ord key OUTPUTABLE_key)
addToFM :: (Ord key)
=> FiniteMap key elt -> key -> elt -> FiniteMap key elt
-- | Throws away any previous binding, items are added left-to-right
addListToFM :: (Ord key OUTPUTABLE_key)
addListToFM :: (Ord key)
=> FiniteMap key elt -> [(key,elt)] -> FiniteMap key elt
-- | Combines added item with previous item, if any
addToFM_C :: (Ord key OUTPUTABLE_key) => (elt -> elt -> elt)
-- | Combines added item with previous item, if any --
-- if the key is present, ``addToFM_C f`` inserts
-- ``(key, f old_value new_value)''
addToFM_C :: (Ord key) => (elt -> elt -> elt)
-> FiniteMap key elt -> key -> elt
-> FiniteMap key elt
-- | Combines added item with previous item, if any, items are added left-to-right
addListToFM_C :: (Ord key OUTPUTABLE_key) => (elt -> elt -> elt)
addListToFM_C :: (Ord key) => (elt -> elt -> elt)
-> FiniteMap key elt -> [(key,elt)]
-> FiniteMap key elt
-- | Deletion doesn't complain if you try to delete something which isn't there
delFromFM :: (Ord key OUTPUTABLE_key)
delFromFM :: (Ord key)
=> FiniteMap key elt -> key -> FiniteMap key elt
-- | Deletion doesn't complain if you try to delete something which isn't there
delListFromFM :: (Ord key OUTPUTABLE_key)
delListFromFM :: (Ord key)
=> FiniteMap key elt -> [key] -> FiniteMap key elt
-- COMBINING
-- | Bindings in right argument shadow those in the left
plusFM :: (Ord key OUTPUTABLE_key)
plusFM :: (Ord key)
=> FiniteMap key elt -> FiniteMap key elt -> FiniteMap key elt
-- | Combines bindings for the same thing with the given function,
-- bindings in right argument shadow those in the left
plusFM_C :: (Ord key OUTPUTABLE_key)
plusFM_C :: (Ord key)
=> (elt -> elt -> elt)
-> FiniteMap key elt -> FiniteMap key elt -> FiniteMap key elt
-- | Deletes from the left argument any bindings in the right argument
minusFM :: (Ord key OUTPUTABLE_key)
minusFM :: (Ord key)
=> FiniteMap key elt -> FiniteMap key elt -> FiniteMap key elt
intersectFM :: (Ord key OUTPUTABLE_key)
intersectFM :: (Ord key)
=> FiniteMap key elt -> FiniteMap key elt -> FiniteMap key elt
-- | Combines bindings for the same thing in the two maps with the given function
intersectFM_C :: (Ord key OUTPUTABLE_key)
intersectFM_C :: (Ord key)
=> (elt1 -> elt2 -> elt3)
-> FiniteMap key elt1 -> FiniteMap key elt2
-> FiniteMap key elt3
......@@ -144,7 +123,7 @@ intersectFM_C :: (Ord key OUTPUTABLE_key)
foldFM :: (key -> elt -> a -> a) -> a -> FiniteMap key elt -> a
mapFM :: (key -> elt1 -> elt2)
-> FiniteMap key elt1 -> FiniteMap key elt2
filterFM :: (Ord key OUTPUTABLE_key)
filterFM :: (Ord key)
=> (key -> elt -> Bool)
-> FiniteMap key elt -> FiniteMap key elt
......@@ -152,12 +131,12 @@ filterFM :: (Ord key OUTPUTABLE_key)
sizeFM :: FiniteMap key elt -> Int
isEmptyFM :: FiniteMap key elt -> Bool
elemFM :: (Ord key OUTPUTABLE_key)
elemFM :: (Ord key)
=> key -> FiniteMap key elt -> Bool
lookupFM :: (Ord key OUTPUTABLE_key)
lookupFM :: (Ord key)
=> FiniteMap key elt -> key -> Maybe elt
-- | Supplies a "default" element in return for an unmapped key
lookupWithDefaultFM :: (Ord key OUTPUTABLE_key)
lookupWithDefaultFM :: (Ord key)
=> FiniteMap key elt -> elt -> key -> elt
-- LISTIFYING
......@@ -168,489 +147,48 @@ eltsFM :: FiniteMap key elt -> [elt]
%************************************************************************
%* *
\subsection{The @FiniteMap@ data type, and building of same}
%* *
%************************************************************************
Invariants about @FiniteMap@:
\begin{enumerate}
\item
all keys in a FiniteMap are distinct
\item
all keys in left subtree are $<$ key in Branch and
all keys in right subtree are $>$ key in Branch
\item
size field of a Branch gives number of Branch nodes in the tree
\item
size of left subtree is differs from size of right subtree by a
factor of at most \tr{sIZE_RATIO}
\end{enumerate}
\begin{code}
-- | A finite mapping from (orderable) key types to elements
data FiniteMap key elt
= EmptyFM
| Branch key elt -- Key and elt stored here
{-# UNPACK #-} !Int -- Size >= 1
(FiniteMap key elt) -- Children
(FiniteMap key elt)
\end{code}
\begin{code}
emptyFM = EmptyFM
{-
emptyFM
= Branch bottom bottom 0 bottom bottom
where
bottom = panic "emptyFM"
-}
-- #define EmptyFM (Branch _ _ 0 _ _)
unitFM key elt = Branch key elt 1 emptyFM emptyFM
listToFM = addListToFM emptyFM
bagToFM = foldrBag (\(k,v) fm -> addToFM fm k v) emptyFM
\end{code}
%************************************************************************
%* *
\subsection{Adding to and deleting from @FiniteMaps@}
%* *
%************************************************************************
\begin{code}
addToFM fm key elt = addToFM_C (\ _old new -> new) fm key elt
addToFM_C _ EmptyFM key elt = unitFM key elt
addToFM_C combiner (Branch key elt size fm_l fm_r) new_key new_elt
= case compare new_key key of
LT -> mkBalBranch key elt (addToFM_C combiner fm_l new_key new_elt) fm_r
GT -> mkBalBranch key elt fm_l (addToFM_C combiner fm_r new_key new_elt)
EQ -> Branch new_key (combiner elt new_elt) size fm_l fm_r
addListToFM fm key_elt_pairs
= addListToFM_C (\ _old new -> new) fm key_elt_pairs
addListToFM_C combiner fm key_elt_pairs
= foldl' add fm key_elt_pairs -- foldl adds from the left
where
add fmap (key,elt) = addToFM_C combiner fmap key elt
\end{code}
\begin{code}
delFromFM EmptyFM _ = emptyFM
delFromFM (Branch key elt _ fm_l fm_r) del_key
= case compare del_key key of
GT -> mkBalBranch key elt fm_l (delFromFM fm_r del_key)
LT -> mkBalBranch key elt (delFromFM fm_l del_key) fm_r
EQ -> glueBal fm_l fm_r
delListFromFM fm keys = foldl' delFromFM fm keys
\end{code}
%************************************************************************
%* *
\subsection{Combining @FiniteMaps@}
%* *
%************************************************************************
\begin{code}
plusFM_C _ EmptyFM fm2 = fm2
plusFM_C _ fm1 EmptyFM = fm1
plusFM_C combiner fm1 (Branch split_key elt2 _ left right)
= mkVBalBranch split_key new_elt
(plusFM_C combiner lts left)
(plusFM_C combiner gts right)
where
lts = splitLT fm1 split_key
gts = splitGT fm1 split_key
new_elt = case lookupFM fm1 split_key of
Nothing -> elt2
Just elt1 -> combiner elt1 elt2
-- It's worth doing plusFM specially, because we don't need
-- to do the lookup in fm1.
-- FM2 over-rides FM1.
plusFM EmptyFM fm2 = fm2
plusFM fm1 EmptyFM = fm1
plusFM fm1 (Branch split_key elt1 _ left right)
= mkVBalBranch split_key elt1 (plusFM lts left) (plusFM gts right)
where
lts = splitLT fm1 split_key
gts = splitGT fm1 split_key
minusFM EmptyFM _ = emptyFM
minusFM fm1 EmptyFM = fm1
minusFM fm1 (Branch split_key _ _ left right)
= glueVBal (minusFM lts left) (minusFM gts right)
-- The two can be way different, so we need glueVBal
where
lts = splitLT fm1 split_key -- NB gt and lt, so the equal ones
gts = splitGT fm1 split_key -- are not in either.
intersectFM fm1 fm2 = intersectFM_C (\ _ right -> right) fm1 fm2
intersectFM_C _ _ EmptyFM = emptyFM
intersectFM_C _ EmptyFM _ = emptyFM
intersectFM_C combiner fm1 (Branch split_key elt2 _ left right)
| maybeToBool maybe_elt1 -- split_elt *is* in intersection
= mkVBalBranch split_key (combiner elt1 elt2)
(intersectFM_C combiner lts left)
(intersectFM_C combiner gts right)
| otherwise -- split_elt is *not* in intersection
= glueVBal (intersectFM_C combiner lts left)
(intersectFM_C combiner gts right)
where
lts = splitLT fm1 split_key -- NB gt and lt, so the equal ones
gts = splitGT fm1 split_key -- are not in either.
maybe_elt1 = lookupFM fm1 split_key
Just elt1 = maybe_elt1
\end{code}
%************************************************************************
%* *
\subsection{Mapping, folding, and filtering with @FiniteMaps@}
%* *
%************************************************************************
\begin{code}
foldFM _ z EmptyFM = z
foldFM k z (Branch key elt _ fm_l fm_r)
= foldFM k (k key elt (foldFM k z fm_r)) fm_l
mapFM _ EmptyFM = emptyFM
mapFM f (Branch key elt size fm_l fm_r)
= Branch key (f key elt) size (mapFM f fm_l) (mapFM f fm_r)
filterFM _ EmptyFM = emptyFM
filterFM p (Branch key elt _ fm_l fm_r)
| p key elt -- Keep the item
= mkVBalBranch key elt (filterFM p fm_l) (filterFM p fm_r)
| otherwise -- Drop the item
= glueVBal (filterFM p fm_l) (filterFM p fm_r)
\end{code}
%************************************************************************
%* *
\subsection{Interrogating @FiniteMaps@}
%* *
%************************************************************************
\begin{code}
--{-# INLINE sizeFM #-}
sizeFM EmptyFM = 0
sizeFM (Branch _ _ size _ _) = size
isEmptyFM fm = sizeFM fm == 0
lookupFM EmptyFM _ = Nothing
lookupFM (Branch key elt _ fm_l fm_r) key_to_find
= case compare key_to_find key of
LT -> lookupFM fm_l key_to_find
GT -> lookupFM fm_r key_to_find
EQ -> Just elt
key `elemFM` fm = isJust (lookupFM fm key)
lookupWithDefaultFM fm deflt key
= case (lookupFM fm key) of { Nothing -> deflt; Just elt -> elt }
\end{code}
%************************************************************************
%* *
\subsection{Listifying @FiniteMaps@}
\subsection{Implementation using ``Data.Map''}
%* *
%************************************************************************
\begin{code}
fmToList fm = foldFM (\ key elt rest -> (key, elt) : rest) [] fm
keysFM fm = foldFM (\ key _elt rest -> key : rest) [] fm
eltsFM fm = foldFM (\ _key elt rest -> elt : rest) [] fm
\end{code}
%************************************************************************
%* *
\subsection{The implementation of balancing}
%* *
%************************************************************************
%************************************************************************
%* *
\subsubsection{Basic construction of a @FiniteMap@}
%* *
%************************************************************************
@mkBranch@ simply gets the size component right. This is the ONLY
(non-trivial) place the Branch object is built, so the ASSERTion
recursively checks consistency. (The trivial use of Branch is in
@unitFM@.)
\begin{code}
sIZE_RATIO :: Int
sIZE_RATIO = 5
mkBranch :: (Ord key OUTPUTABLE_key) -- Used for the assertion checking only
=> Int
-> key -> elt
-> FiniteMap key elt -> FiniteMap key elt
-> FiniteMap key elt
mkBranch _which key elt fm_l fm_r
= --ASSERT( left_ok && right_ok && balance_ok )
#if defined(DEBUG_FINITEMAPS)
if not ( left_ok && right_ok && balance_ok ) then
pprPanic ("mkBranch:"++show _which)
(vcat [ppr [left_ok, right_ok, balance_ok],
ppr key,
ppr fm_l,
ppr fm_r])
else
#endif
let
result = Branch key elt (1 + left_size + right_size) fm_l fm_r
in
-- if sizeFM result <= 8 then
result
-- else
-- pprTrace ("mkBranch:"++(show which)) (ppr result) (
-- result
-- )
where
#if defined(DEBUG_FINITEMAPS)
left_ok = case fm_l of
EmptyFM -> True
Branch _ _ _ _ _ -> let
biggest_left_key = fst (findMax fm_l)
in
biggest_left_key < key
right_ok = case fm_r of
EmptyFM -> True
Branch _ _ _ _ _ -> let
smallest_right_key = fst (findMin fm_r)
in
key < smallest_right_key
balance_ok = True -- sigh
#endif
{- LATER:
balance_ok
= -- Both subtrees have one or no elements...
(left_size + right_size <= 1)
-- NO || left_size == 0 -- ???
-- NO || right_size == 0 -- ???
-- ... or the number of elements in a subtree does not exceed
-- sIZE_RATIO times the number of elements in the other subtree
|| (left_size * sIZE_RATIO >= right_size &&
right_size * sIZE_RATIO >= left_size)
-}
left_size = sizeFM fm_l
right_size = sizeFM fm_r
\end{code}
%************************************************************************
%* *
\subsubsection{{\em Balanced} construction of a @FiniteMap@}
%* *
%************************************************************************
@mkBalBranch@ rebalances, assuming that the subtrees aren't too far
out of whack.
\begin{code}
mkBalBranch :: (Ord key OUTPUTABLE_key)
=> key -> elt
-> FiniteMap key elt -> FiniteMap key elt
-> FiniteMap key elt
mkBalBranch key elt fm_L fm_R
| size_l + size_r < 2
= mkBranch 1{-which-} key elt fm_L fm_R
| size_r > sIZE_RATIO * size_l -- Right tree too big
= case fm_R of
Branch _ _ _ fm_rl fm_rr
| sizeFM fm_rl < 2 * sizeFM fm_rr -> single_L fm_L fm_R
| otherwise -> double_L fm_L fm_R
_ -> panic "mkBalBranch: impossible case 1"
| size_l > sIZE_RATIO * size_r -- Left tree too big
= case fm_L of
Branch _ _ _ fm_ll fm_lr
| sizeFM fm_lr < 2 * sizeFM fm_ll -> single_R fm_L fm_R
| otherwise -> double_R fm_L fm_R
_ -> panic "mkBalBranch: impossible case 2"
| otherwise -- No imbalance
= mkBranch 2{-which-} key elt fm_L fm_R
where
size_l = sizeFM fm_L
size_r = sizeFM fm_R
single_L fm_l (Branch key_r elt_r _ fm_rl fm_rr)
= mkBranch 3{-which-} key_r elt_r (mkBranch 4{-which-} key elt fm_l fm_rl) fm_rr
single_L _ _ = panic "mkBalBranch: impossible case 3"
double_L fm_l (Branch key_r elt_r _ (Branch key_rl elt_rl _ fm_rll fm_rlr) fm_rr)
= mkBranch 5{-which-} key_rl elt_rl
(mkBranch 6{-which-} key elt fm_l fm_rll)
(mkBranch 7{-which-} key_r elt_r fm_rlr fm_rr)
double_L _ _ = panic "mkBalBranch: impossible case 4"
single_R (Branch key_l elt_l _ fm_ll fm_lr) fm_r
= mkBranch 8{-which-} key_l elt_l fm_ll
(mkBranch 9{-which-} key elt fm_lr fm_r)
single_R _ _ = panic "mkBalBranch: impossible case 5"
double_R (Branch key_l elt_l _ fm_ll (Branch key_lr elt_lr _ fm_lrl fm_lrr)) fm_r
= mkBranch 10{-which-} key_lr elt_lr
(mkBranch 11{-which-} key_l elt_l fm_ll fm_lrl)
(mkBranch 12{-which-} key elt fm_lrr fm_r)
double_R _ _ = panic "mkBalBranch: impossible case 6"
\end{code}
\begin{code}
mkVBalBranch :: (Ord key OUTPUTABLE_key)
=> key -> elt
-> FiniteMap key elt -> FiniteMap key elt
-> FiniteMap key elt
-- Assert: in any call to (mkVBalBranch_C comb key elt l r),
-- (a) all keys in l are < all keys in r
-- (b) all keys in l are < key
-- (c) all keys in r are > key
newtype FiniteMap key elt = FM (M.Map key elt)
mkVBalBranch key elt EmptyFM fm_r = addToFM fm_r key elt
mkVBalBranch key elt fm_l EmptyFM = addToFM fm_l key elt
emptyFM = FM M.empty
unitFM k v = FM (M.singleton k v)
listToFM l = FM (M.fromList l)
mkVBalBranch key elt fm_l@(Branch key_l elt_l _ fm_ll fm_lr)
fm_r@(Branch key_r elt_r _ fm_rl fm_rr)
| sIZE_RATIO * size_l < size_r
= mkBalBranch key_r elt_r (mkVBalBranch key elt fm_l fm_rl) fm_rr
addToFM (FM m) k v = FM (M.insert k v m)
-- Arguments of combining function of M.insertWith and addToFM_C are flipped.
addToFM_C f (FM m) k v = FM (M.insertWith (flip f) k v m)
addListToFM = foldl (\m (k, v) -> addToFM m k v)
addListToFM_C f = foldl (\m (k, v) -> addToFM_C f m k v)
delFromFM (FM m) k = FM (M.delete k m)
delListFromFM = foldl delFromFM
| sIZE_RATIO * size_r < size_l
= mkBalBranch key_l elt_l fm_ll (mkVBalBranch key elt fm_lr fm_r)
-- M.union is left-biased, plusFM should be right-biased.
plusFM (FM x) (FM y) = FM (M.union y x)
plusFM_C f (FM x) (FM y) = FM (M.unionWith f x y)
minusFM (FM x) (FM y) = FM (M.difference x y)
foldFM k z (FM m) = M.foldWithKey k z m
| otherwise
= mkBranch 13{-which-} key elt fm_l fm_r
intersectFM (FM x) (FM y) = FM (M.intersection x y)
intersectFM_C f (FM x) (FM y) = FM (M.intersectionWith f x y)
mapFM f (FM m) = FM (M.mapWithKey f m)
filterFM p (FM m) = FM (M.filterWithKey p m)
where
size_l = sizeFM fm_l
size_r = sizeFM fm_r
\end{code}
sizeFM (FM m) = M.size m
isEmptyFM (FM m) = M.null m
elemFM k (FM m) = M.member k m
lookupFM (FM m) k = M.lookup k m
lookupWithDefaultFM (FM m) v k = M.findWithDefault v k m
%************************************************************************
%* *
\subsubsection{Gluing two trees together}
%* *