
(with_import "./match.kp"
(let (
    ; This is based on https://www.cs.cornell.edu/courses/cs3110/2020sp/a4/deletion.pdf
    ; and the figure references refer to it
    ; Insert is taken from the same paper, but is origional to Okasaki, I belive

    ; The tree has been modified slightly to take in a comparison function
    ; and override if insert replaces or not to allow use as a set or as a map

    ; I think this is actually pretty cool - instead of having a bunch of seperate ['B]
    ; be our leaf node, we use ['B] with all nils. This allows us to not use -B, as
    ; both leaf and non-leaf 'BB has the same structure with children! Also, we make
    ; sure to use empty itself so we don't make a ton of empties...
    empty ['B nil nil nil]
    E  empty
    EE ['BB nil nil nil]

    size (rec-lambda recurse (t) (match t
                                        ~E  0
                                        [c a x b] (+ 1 (recurse a) (recurse b))))

    generic-foldl (rec-lambda recurse (f z t) (match t
                                                     ~E z
                                                     [c a x b]  (recurse f (f (recurse f z a) x) b)))

    generic-contains? (rec-lambda recurse (t cmp v found not-found) (match t
                                            ~E                            (not-found)
                                            [c a x b] (match (cmp v x) '< (recurse a cmp v found not-found)
                                                                       '= (found x)
                                                                       '> (recurse b cmp v found not-found))))

    blacken (lambda (t) (match t
                            ['R a x b] ['B a x b]
                            t          t))
    balance (lambda (t) (match t
                            ; figures 1 and 2
                            ['B ['R ['R a x b] y c] z d] ['R ['B a x b] y ['B c z d]]
                            ['B ['R a x ['R b y c]] z d] ['R ['B a x b] y ['B c z d]]
                            ['B a x ['R ['R b y c] z d]] ['R ['B a x b] y ['B c z d]]
                            ['B a x ['R b y ['R c z d]]] ['R ['B a x b] y ['B c z d]]
                            ; figure 8, double black cases
                            ['BB ['R a x ['R b y c]] z d] ['B ['B a x b] y ['B c z d]]
                            ['BB a x ['R ['R b y c] z d]] ['B ['B a x b] y ['B c z d]]
                            ; already balenced
                            t                            t))
    generic-insert (lambda (t cmp v replace) (let (
        ins (rec-lambda ins (t) (match t
                                    ~E                            ['R t v t]
                                    [c a x b] (match (cmp v x) '< (balance [c (ins a) x b])
                                                               '= (if replace [c a v b]
                                                                              t)
                                                               '> (balance [c a x (ins b)]))))
    ) (blacken (ins t))))

    rotate (lambda (t) (match t
      ; case 1, fig 6
      ['R ['BB a x b] y ['B c z d]]          (balance ['B ['R ['B a x b] y c] z d])
      ['R ['B a x b] y ['BB c z d]]          (balance ['B a x ['R b y ['B c z d]]])
      ; case 2, figure 7
      ['B ['BB a x b] y ['B c z d]]          (balance ['BB ['R ['B a x b] y c] z d])
      ['B ['B a x b] y ['BB c z d]]          (balance ['BB a x ['R b y ['B c z d]]])
      ; case 3, figure 9
      ['B ['BB a w b] x ['R ['B c y d] z e]] ['B (balance ['B ['R ['B a w b] x c] y d]) z e]
      ['B ['R a w ['B b x c]] y ['BB d z e]] ['B a w (balance ['B b x ['R c y ['B d z e]]])]
      ; fall through
      t                                     t))

    redden (lambda (t) (match t
                        ['B a x b] (if (and (= 'B (idx a 0)) (= 'B (idx b 0))) ['R a x b]
                                                                               t)
                        t                                                      t))

    min_delete (rec-lambda recurse (t) (match t
                    ~E                                      (error "min_delete empty tree")
                    ['R ~E x ~E]                            [x E]
                    ['B ~E x ~E]                            [x EE]
                    ['B ~E x ['R a y b]]                    [x ['B a y b]]
                    [c a x b]   (let ((v ap) (recurse a))   [v (rotate [c ap x b])])))
    generic-delete (lambda (t cmp v) (let (
        del (rec-lambda del (t v) (match t
                                    ; figure 3
                                    ~E                                          t
                                    ; figure 4
                                    ['R ~E x ~E]            (match (cmp v x) '= E
                                                                             _  t)
                                    ['B ['R a x b] y ~E]    (match (cmp v y) '< (rotate ['B (del ['R a x b] v) y ~E])
                                                                             '= ['B a x b]
                                                                             '> t)
                                    ; figure 5
                                    ['B ~E x ~E]            (match (cmp v x) '= EE
                                                                             _  t)
                                    [c a x b]               (match (cmp v x) '< (rotate [c (del a v) x b])
                                                                             '= (let ([vp bp] (min_delete b))
                                                                                (rotate [c a vp bp]))
                                                                             '> (rotate [c a x (del b v)]))))
    ) (del (redden t) v)))


    set-cmp (lambda (a b) (cond (< a b) '<
                                (= a b) '=
                                true    '>))
    set-empty empty
    set-foldl generic-foldl
    set-insert           (lambda (t x)   (generic-insert     t set-cmp x false))
    set-contains?        (lambda (t x)   (generic-contains?  t set-cmp x       (lambda (f) true)      (lambda () false)))
    set-remove           (lambda (t x)   (generic-delete     t set-cmp x))

    map-cmp (lambda (a b) (let (ak (idx a 0)
                                bk (idx b 0))
                                (cond (< ak bk) '<
                                      (= ak bk) '=
                                      true      '>)))
    map-empty empty
    map-insert           (lambda (t k v) (generic-insert     t map-cmp [k v] true))
    map-contains-key?    (lambda (t k)   (generic-contains?  t map-cmp [k nil] (lambda (f) true)      (lambda () false)))
    map-get              (lambda (t k)   (generic-contains?  t map-cmp [k nil] (lambda (f) (idx f 1)) (lambda () (error (str "didn't find key " k " in map " t)))))
    map-get-or-default   (lambda (t k d) (generic-contains?  t map-cmp [k nil] (lambda (f) (idx f 1)) (lambda () d)))
    map-get-with-default (lambda (t k d) (generic-contains?  t map-cmp [k nil] (lambda (f) (idx f 1)) (lambda () (d))))
    map-remove           (lambda (t k)   (generic-delete     t map-cmp [k nil]))

    ; This could be 2x as efficent by being implmented on generic instead of map,
    ; as we wouldn't have to traverse once to find and once to insert
    multimap-empty map-empty
    multimap-insert           (lambda (t k v) (map-insert t k (set-insert (map-get-or-default t k set-empty) v)))
    multimap-get              (lambda (t k)   (map-get-or-default t k set-empty))
)
    (provide set-empty set-foldl set-insert set-contains? set-remove
             map-empty map-insert map-contains-key? map-get map-get-or-default map-get-with-default map-remove
             multimap-empty multimap-insert multimap-get
             size)
))
