{-# OPTIONS_GHC -fglasgow-exts #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Tree.AVL.Internals.HJoin
-- Copyright   :  (c) Adrian Hey 2004,2005
-- License     :  BSD3
--
-- Maintainer  :  http://homepages.nildram.co.uk/~ahey/em.png
-- Stability   :  stable
-- Portability :  portable
--
-- Functions for joining AVL trees of known height.
-----------------------------------------------------------------------------
module Data.Tree.AVL.Internals.HJoin
        ( spliceH,joinH,joinH',
        ) where

import Data.Tree.AVL.Types(AVL(..))
import Data.Tree.AVL.Push(pushL,pushR)
import Data.Tree.AVL.Internals.HPush(pushHL_,pushHR_)
import Data.Tree.AVL.Internals.DelUtils(popRN,popRZ,popRP,popLN,popLZ,popLP)

#if __GLASGOW_HASKELL__
import GHC.Base
#include "ghcdefs.h"
#else
#include "h98defs.h"
#endif

-- | Join two trees of known height, returning an AVL tree.
-- It's OK if heights are relative (I.E. if they share same fixed offset).
--
-- Complexity: O(d), where d is the absolute difference in tree heights.
joinH'
       :: AVL e -> UINT -> AVL e -> UINT -> AVL e
joinH' l hl r hr
                 = if hl LEQ hr then let d = SUBINT(hr,hl) in joinHL d l r
                                else let d = SUBINT(hl,hr) in joinHR d l r

-- hr >= hl, join l to left subtree of r.
-- Int argument is absolute difference in tree height, hr-hl (>=0)
{-# INLINE joinHL #-}
joinHL :: UINT -> AVL e -> AVL e -> AVL e
joinHL _  E           r = r                                                  -- l was empty
joinHL d (N ll le lr) r = case popRN ll le lr of
                          UBT2(l_,e) -> case l_ of
                                        E       -> error "joinHL: Bug0"       -- impossible if BF=-1
                                        Z _ _ _ -> spliceL l_ e INCINT1(d) r  -- hl2=hl-1
                                        _       -> spliceL l_ e         d  r  -- hl2=hl
joinHL d (Z ll le lr) r = case popRZ ll le lr of
                          UBT2(l_,e) -> case l_ of
                                        E       -> e `pushL` r               -- l had only one element
                                        _       -> spliceL l_ e d  r         -- hl2=hl
joinHL d (P ll le lr) r = case popRP ll le lr of
                          UBT2(l_,e) -> case l_ of
                                        E       -> error "joinHL: Bug1"      -- impossible if BF=+1
                                        Z _ _ _ -> spliceL l_ e INCINT1(d) r -- hl2=hl-1
                                        _       -> spliceL l_ e         d  r -- hl2=hl


-- hl >= hr, join r to right subtree of l.
-- Int argument is absolute difference in tree height, hl-hr (>=0)
{-# INLINE joinHR #-}
joinHR :: UINT -> AVL e -> AVL e -> AVL e
joinHR _ l  E           = l                                    -- r was empty
joinHR d l (N rl re rr) = case popLN rl re rr of
                          UBT2(e,r_) -> case r_ of
                                        E       -> error "joinHR: Bug0"      -- impossible if BF=-1
                                        Z _ _ _ -> spliceR r_ e INCINT1(d) l -- hr2=hr-1
                                        _       -> spliceR r_ e         d  l -- hr2=hr
joinHR d l (Z rl re rr) = case popLZ rl re rr of
                          UBT2(e,r_) -> case r_ of
                                        E       -> l `pushR` e            -- r had only one element
                                        _       -> spliceR r_ e d l       -- hr2=hr
joinHR d l (P rl re rr) = case popLP rl re rr of
                          UBT2(e,r_) -> case r_ of
                                        E       -> error "joinHL: Bug1"      -- impossible if BF=+1
                                        Z _ _ _ -> spliceR r_ e INCINT1(d) l -- hr2=hr-1
                                        _       -> spliceR r_ e         d  l -- hr2=hr
-----------------------------------------------------------------------
--------------------------- joinH' Ends Here --------------------------
-----------------------------------------------------------------------

-- | Join two AVL trees of known height, returning an AVL tree of known height.
-- It's OK if heights are relative (I.E. if they share same fixed offset).
--
-- Complexity: O(d), where d is the absolute difference in tree heights.
joinH :: AVL e -> UINT -> AVL e -> UINT -> UBT2(AVL e,UINT)
joinH l hl r hr =
 case COMPAREUINT hl hr of
 -- hr > hl
 LT -> case l of
       E          -> UBT2(r,hr)
       N ll le lr -> case popRN ll le lr of
                     UBT2(l_,e) -> case l_ of
                                   Z _ _ _ -> spliceHL l_ DECINT1(hl) e r hr -- dH=-1
                                   _       -> spliceHL l_         hl  e r hr -- dH= 0
       Z ll le lr -> case popRZ ll le lr of
                     UBT2(l_,e) -> case l_ of
                                   E       -> pushHL_ l r hr                  -- l had only 1 element
                                   _       -> spliceHL l_         hl  e r hr -- dH=0
       P ll le lr -> case popRP ll le lr of
                     UBT2(l_,e) -> case l_ of
                                   Z _ _ _ -> spliceHL l_ DECINT1(hl) e r hr -- dH=-1
                                   _       -> spliceHL l_         hl  e r hr -- dH= 0
 -- hr = hl
 EQ -> case l of
       E          -> UBT2(l,hl)              -- r must be empty too, don't use emptyAVL!
       N ll le lr -> case popRN ll le lr of
                     UBT2(l_,e) -> case l_ of
                                   Z _ _ _ -> spliceHL l_ DECINT1(hl) e r hr -- dH=-1
                                   _       -> UBT2(Z l_ e r, INCINT1(hr))    -- dH= 0
       Z ll le lr -> case popRZ ll le lr of
                     UBT2(l_,e) -> case l_ of
                                   E       -> pushHL_ l r hr                 -- l had only 1 element
                                   _       -> UBT2(Z l_ e r, INCINT1(hr))    -- dH= 0
       P ll le lr -> case popRP ll le lr of
                     UBT2(l_,e) -> case l_ of
                                   Z _ _ _ -> spliceHL l_ DECINT1(hl) e r hr -- dH=-1
                                   _       -> UBT2(Z l_ e r, INCINT1(hr))    -- dH= 0
 -- hl > hr
 GT -> case r of
       E          -> UBT2(l,hl)
       N rl re rr -> case popLN rl re rr of
                     UBT2(e,r_) -> case r_ of
                                   Z _ _ _ -> spliceHR l hl e r_ DECINT1(hr) -- dH=-1
                                   _       -> spliceHR l hl e r_         hr  -- dH= 0
       Z rl re rr -> case popLZ rl re rr of
                     UBT2(e,r_) -> case r_ of
                                   E       -> pushHR_ l hl r                 -- r had only 1 element
                                   _       -> spliceHR l hl e r_ hr          -- dH=0
       P rl re rr -> case popLP rl re rr of
                     UBT2(e,r_) -> case r_ of
                                   Z _ _ _ -> spliceHR l hl e r_ DECINT1(hr) -- dH=-1
                                   _       -> spliceHR l hl e r_         hr  -- dH= 0


-- | Splice two AVL trees of known height using the supplied bridging element.
-- That is, the bridging element appears \"in the middle\" of the resulting AVL tree.
-- The elements of the first tree argument are to the left of the bridging element and
-- the elements of the second tree are to the right of the bridging element.
--
-- This function does not require that the AVL heights are absolutely correct, only that
-- the difference in supplied heights is equal to the difference in actual heights. So it's
-- OK if the input heights both have the same unknown constant offset. (The output height
-- will also have the same constant offset in this case.)
--
-- Complexity: O(d), where d is the absolute difference in tree heights.
spliceH :: AVL e -> UINT -> e -> AVL e -> UINT -> UBT2(AVL e,UINT)
-- You'd think inlining this function would make a significant difference to many functions
-- (such as set operations), but it doesn't. It makes them marginally slower!!
spliceH l hl b r hr =
 case COMPAREUINT hl hr of
 LT -> spliceHL l hl b r hr
 EQ -> UBT2(Z l b r, INCINT1(hl))
 GT -> spliceHR l hl b r hr

-- Splice two trees of known relative height where hr>hl, using the supplied bridging element,
-- returning another tree of known relative height.
spliceHL :: AVL e -> UINT -> e -> AVL e -> UINT -> UBT2(AVL e,UINT)
spliceHL l hl b r hr = let d = SUBINT(hr,hl)
                       in if d EQL L(1) then UBT2(N l b r, INCINT1(hr))
                                        else spliceHL_ hr d l b r

-- Splice two trees of known relative height where hl>hr, using the supplied bridging element,
-- returning another tree of known relative height.
spliceHR :: AVL e -> UINT -> e -> AVL e -> UINT -> UBT2(AVL e,UINT)
spliceHR l hl b r hr = let d = SUBINT(hl,hr)
                       in if d EQL L(1) then UBT2(P l b r, INCINT1(hl))
                                        else spliceHR_ hl d l b r

-- Splice two trees of known relative height where hr>hl+1, using the supplied bridging element,
-- returning another tree of known relative height. d >= 2
{-# INLINE spliceHL_ #-}
spliceHL_ :: UINT -> UINT -> AVL e -> e -> AVL e -> UBT2(AVL e,UINT)
spliceHL_ _  _ _ _  E           = error "spliceHL_: Bug0"          -- impossible if hr>hl
spliceHL_ hr d l b (N rl re rr) = let r_ = spliceLN l b DECINT2(d) rl re rr
                                  in  r_ `seq` UBT2(r_,hr)
spliceHL_ hr d l b (Z rl re rr) = let r_ = spliceLZ l b DECINT1(d) rl re rr
                                  in case r_ of
                                     E       -> error "spliceHL_: Bug1"
                                     Z _ _ _ -> UBT2(r_,        hr )
                                     _       -> UBT2(r_,INCINT1(hr))
spliceHL_ hr d l b (P rl re rr) = let r_ = spliceLP l b DECINT1(d) rl re rr
                                  in  r_ `seq` UBT2(r_,hr)

-- Splice two trees of known relative height where hl>hr+1, using the supplied bridging element,
-- returning another tree of known relative height. d >= 2 !!
{-# INLINE spliceHR_ #-}
spliceHR_ :: UINT -> UINT -> AVL e -> e -> AVL e -> UBT2(AVL e,UINT)
spliceHR_ _  _  E           _ _ = error "spliceHR_: Bug0"          -- impossible if hl>hr
spliceHR_ hl d (N ll le lr) b r = let l_ = spliceRN r b DECINT1(d) ll le lr
                                  in  l_ `seq` UBT2(l_,hl)
spliceHR_ hl d (Z ll le lr) b r = let l_ = spliceRZ r b DECINT1(d) ll le lr
                                  in case l_ of
                                     E       -> error "spliceHR_: Bug1"
                                     Z _ _ _ -> UBT2(l_,        hl )
                                     _       -> UBT2(l_,INCINT1(hl))
spliceHR_ hl d (P ll le lr) b r = let l_ = spliceRP r b DECINT2(d) ll le lr
                                  in  l_ `seq` UBT2(l_,hl)
-----------------------------------------------------------------------
-------------------------- spliceH Ends Here --------------------------
-----------------------------------------------------------------------

-- hr >= hl, splice s to left subtree of r, using b as the bridge
-- The Int argument is the absolute difference in tree height, hr-hl (>=0)
spliceL :: AVL e -> e -> UINT -> AVL e -> AVL e
spliceL s b L(0) r           = Z s b r
spliceL s b L(1) r           = N s b r
spliceL s b d   (N rl re rr) = spliceLN s b DECINT2(d) rl re rr   -- height diff of rl is two less
spliceL s b d   (Z rl re rr) = spliceLZ s b DECINT1(d) rl re rr   -- height diff of rl is one less
spliceL s b d   (P rl re rr) = spliceLP s b DECINT1(d) rl re rr   -- height diff of rl is one less
spliceL _ _ _    E           = error "spliceL: Bug0"              -- r can't be empty

-- Splice into left subtree of (N l e r), height cannot change as a result of this
spliceLN :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> AVL e
spliceLN s b L(0) l           e r = Z (Z s b l) e r                                             -- dH=0
spliceLN s b L(1) l           e r = Z (N s b l) e r                                             -- dH=0
spliceLN s b d   (N ll le lr) e r = let l_ = spliceLN s b DECINT2(d) ll le lr in l_ `seq` N l_ e r
spliceLN s b d   (Z ll le lr) e r = let l_ = spliceLZ s b DECINT1(d) ll le lr
                                    in case l_ of
                                       Z _ _ _ -> N l_ e r                                      -- dH=0
                                       P _ _ _ -> Z l_ e r                                      -- dH=0
                                       _       -> error "spliceLN: Bug0"                        -- impossible
spliceLN s b d   (P ll le lr) e r = let l_ = spliceLP s b DECINT1(d) ll le lr in l_ `seq` N l_ e r
spliceLN _ _ _    E           _ _ = error "spliceLN: Bug1"                                      -- impossible

-- Splice into left subtree of (Z l e r), Z->P if dH=1, Z->Z if dH=0
spliceLZ :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> AVL e
spliceLZ s b L(1) l           e r = P (N s b l) e r                                                -- Z->P, dH=1
spliceLZ s b d   (N ll le lr) e r = let l_ = spliceLN s b DECINT2(d) ll le lr in l_ `seq` Z l_ e r -- Z->Z, dH=0
spliceLZ s b d   (Z ll le lr) e r = let l_ = spliceLZ s b DECINT1(d) ll le lr
                                    in case l_ of
                                       Z _ _ _ -> Z l_ e r                                      -- Z->Z, dH=0
                                       P _ _ _ -> P l_ e r                                      -- Z->P, dH=1
                                       _       -> error "spliceLZ: Bug0"                        -- impossible
spliceLZ s b d   (P ll le lr) e r = let l_ = spliceLP s b DECINT1(d) ll le lr in l_ `seq` Z l_ e r -- Z->Z, dH=0
spliceLZ _ _ _    E           _ _ = error "spliceLZ: Bug1"                                      -- impossible

-- Splice into left subtree of (P l e r), height cannot change as a result of this
spliceLP :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> AVL e
spliceLP s b L(1) (N ll le lr) e r = Z (P s b ll) le (Z lr e r)                                     -- dH=0
spliceLP s b L(1) (Z ll le lr) e r = Z (Z s b ll) le (Z lr e r)                                     -- dH=0
spliceLP s b L(1) (P ll le lr) e r = Z (Z s b ll) le (N lr e r)                                     -- dH=0
spliceLP s b d    (N ll le lr) e r = let l_ = spliceLN s b DECINT2(d) ll le lr in l_ `seq` P l_ e r -- dH=0
spliceLP s b d    (Z ll le lr) e r = spliceLPZ s b DECINT1(d) ll le lr e r                          -- dH=0
spliceLP s b d    (P ll le lr) e r = let l_ = spliceLP s b DECINT1(d) ll le lr in l_ `seq` P l_ e r -- dH=0
spliceLP _ _ _     E           _ _ = error "spliceLP: Bug0"

-- Splice into left subtree of (P (Z ll le lr) e r)
{-# INLINE spliceLPZ #-}
spliceLPZ :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> e -> AVL e -> AVL e
spliceLPZ s b L(1) ll             le lr e r = Z (N s b ll) le (Z lr e r)                        -- dH=0
spliceLPZ s b d   (N lll lle llr) le lr e r = let ll_ = spliceLN s b DECINT2(d) lll lle llr     -- dH=0
                                              in  ll_ `seq` P (Z ll_ le lr) e r
spliceLPZ s b d   (Z lll lle llr) le lr e r = let ll_ = spliceLZ s b DECINT1(d) lll lle llr     -- dH=0
                                              in case ll_ of
                                                 Z _ _ _ -> P (Z ll_ le lr) e r                 -- dH=0
                                                 P _ _ _ -> Z ll_ le (Z lr e r)                 -- dH=0
                                                 _       -> error "spliceLPZ: Bug0"             -- impossible
spliceLPZ s b d   (P lll lle llr) le lr e r = let ll_ = spliceLP s b DECINT1(d) lll lle llr     -- dH=0
                                              in  ll_ `seq` P (Z ll_ le lr) e r
spliceLPZ _ _ _    E              _  _  _ _ = error "spliceLPZ: Bug1"
-----------------------------------------------------------------------
-------------------------- spliceL Ends Here --------------------------
-----------------------------------------------------------------------

-- hl >= hr, splice s to right subtree of l, using b as the bridge
-- The Int argument is the absolute difference in tree height, hl-hr (>=0)
spliceR :: AVL e -> e -> UINT -> AVL e -> AVL e
spliceR s b L(0) l           = Z l b s
spliceR s b L(1) l           = P l b s
spliceR s b d   (N ll le lr) = spliceRN s b DECINT1(d) ll le lr   -- height diff of lr is one less
spliceR s b d   (Z ll le lr) = spliceRZ s b DECINT1(d) ll le lr   -- height diff of lr is one less
spliceR s b d   (P ll le lr) = spliceRP s b DECINT2(d) ll le lr   -- height diff of lr is two less
spliceR _ _ _    E           = error "spliceR: Bug0"              -- l can't be empty

-- Splice into right subtree of (P l e r), height cannot change as a result of this
spliceRP :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> AVL e
spliceRP s b L(0) l e  r           = Z l e (Z r b s)                                             -- dH=0
spliceRP s b L(1) l e  r           = Z l e (P r b s)                                             -- dH=0
spliceRP s b d    l e (N rl re rr) = let r_ = spliceRN s b DECINT1(d) rl re rr in r_ `seq` P l e r_
spliceRP s b d    l e (Z rl re rr) = let r_ = spliceRZ s b DECINT1(d) rl re rr
                                     in case r_ of
                                        Z _ _ _ -> P l e r_                                      -- dH=0
                                        N _ _ _ -> Z l e r_                                      -- dH=0
                                        _       -> error "spliceRP: Bug0"                        -- impossible
spliceRP s b d    l e (P rl re rr) = let r_ = spliceRP s b DECINT2(d) rl re rr in r_ `seq` P l e r_
spliceRP _ _ _    _ _  E           = error "spliceRP: Bug1"                                      -- impossible

-- Splice into right subtree of (Z l e r), Z->N if dH=1, Z->Z if dH=0
spliceRZ :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> AVL e
spliceRZ s b L(1) l e  r           = N l e (P r b s)                                                -- Z->N, dH=1
spliceRZ s b d    l e (N rl re rr) = let r_ = spliceRN s b DECINT1(d) rl re rr in r_ `seq` Z l e r_ -- Z->Z, dH=0
spliceRZ s b d    l e (Z rl re rr) = let r_ = spliceRZ s b DECINT1(d) rl re rr
                                     in case r_ of
                                        Z _ _ _ -> Z l e r_                                         -- Z->Z, dH=0
                                        N _ _ _ -> N l e r_                                         -- Z->N, dH=1
                                        _       -> error "spliceRZ: Bug0"                           -- impossible
spliceRZ s b d    l e (P rl re rr) = let r_ = spliceRP s b DECINT2(d) rl re rr in r_ `seq` Z l e r_ -- Z->Z, dH=0
spliceRZ _ _ _    _ _  E           = error "spliceRZ: Bug1"                                         -- impossible

-- Splice into right subtree of (N l e r), height cannot change as a result of this
spliceRN :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> AVL e
spliceRN s b L(1) l e (N rl re rr) = Z (P l e rl) re (Z rr b s)                                     -- dH=0
spliceRN s b L(1) l e (Z rl re rr) = Z (Z l e rl) re (Z rr b s)                                     -- dH=0
spliceRN s b L(1) l e (P rl re rr) = Z (Z l e rl) re (N rr b s)                                     -- dH=0
spliceRN s b d    l e (N rl re rr) = let r_ = spliceRN s b DECINT1(d) rl re rr in r_ `seq` N l e r_ -- dH=0
spliceRN s b d    l e (Z rl re rr) = spliceRNZ s b DECINT1(d) l e rl re rr                          -- dH=0
spliceRN s b d    l e (P rl re rr) = let r_ = spliceRP s b DECINT2(d) rl re rr in r_ `seq` N l e r_ -- dH=0
spliceRN _ _ _    _ _  E           = error "spliceRN: Bug0"

-- Splice into right subtree of (N l e (Z rl re rr))
{-# INLINE spliceRNZ #-}
spliceRNZ :: AVL e -> e -> UINT -> AVL e -> e -> AVL e -> e -> AVL e -> AVL e
spliceRNZ s b L(1) l e rl re rr              = Z (Z l e rl) re (P rr b s)                        -- dH=0
spliceRNZ s b d    l e rl re (N rrl rre rrr) = let rr_ = spliceRN s b DECINT1(d) rrl rre rrr
                                               in  rr_ `seq` N l e (Z rl re rr_)                 -- dH=0
spliceRNZ s b d    l e rl re (Z rrl rre rrr) = let rr_ = spliceRZ s b DECINT1(d) rrl rre rrr     -- dH=0
                                               in case rr_ of
                                                  Z _ _ _ -> N l e (Z rl re rr_)                 -- dH=0
                                                  N _ _ _ -> Z (Z l e rl) re rr_                 -- dH=0
                                                  _       -> error "spliceRNZ: Bug0"             -- impossible
spliceRNZ s b d    l e rl re (P rrl rre rrr) = let rr_ = spliceRP s b DECINT2(d) rrl rre rrr     -- dH=0
                                               in rr_ `seq` N l e (Z rl re rr_)
spliceRNZ _ _ _    _ _ _  _   E              = error "spliceRNZ: Bug1"
-----------------------------------------------------------------------
-------------------------- spliceR Ends Here --------------------------
-----------------------------------------------------------------------