(module finite-map mzscheme
  (require (prefix set: "red-black-tree-set.scm")
           (lib "67.ss" "srfi")
           (lib "42.ss" "srfi")
           (only (lib "list.ss") foldl))
  
        
  (define-struct finite-map (compare bindings))
  
  (define key car)
  (define value cdr)
  (define bindings  finite-map-bindings)
  
  (define empty
    (case-lambda 
      [()    (empty (current-compare))]
      [(cmp) (make-finite-map cmp
                              (set:empty (lambda (p1 p2)
                                           (cmp (key p1) (key p2)))))]))
  
  (define (empty? m)
    (set:empty? (bindings m)))
  
  (define (insert x v m)
    (make-finite-map (finite-map-compare m)
                     (set:insert (cons x v)
                                 (set:delete (cons x 'dummy) (bindings m)))))
  
  (define (insert* xvs m)
    (foldl (lambda (key-value-pair m)
             (insert (key key-value-pair) (value key-value-pair) m))
           (empty)
           xvs))
  
  (define (fold f init m)
                (set:fold (lambda (b a) (f (value b) a))
              init (bindings m)))
  
  (define (fold/key f init m)
                (set:fold (lambda (b a) (f (key b) (value b))  a))
              init (bindings m))
  
  
  (define (elements m)
    (fold cons '() m))
  
  (define (get x m)
    (let ([y (set:get (cons x 'ignore) (bindings m))])
      (if y
          (cons (key y) (value y))
          #f)))
  
  (define (lookup x m)
    (let ([kv (get x m)])
      (if kv
          (cdr kv)
          #f)))
  
  (define (lookup/default x m d)
    (let ([kv (get x m)])
      (if kv
          (cdr kv)
          d)))
  
  (define (member? x b)
    (if (get x b)
        #t
        #f))
  
  (define (delete x m)
    (make-finite-map (finite-map-compare m)
                     (set:delete (cons x 'dummy) (bindings m))))
  
    (define (delete* xs m)
    (foldl delete m xs))
  
    (define (delete-all x m)
    (delete x m))
  
  (define (union m1 m2)
    (make-finite-map (finite-map-compare m1)
                     (set:union (bindings m1) (bindings m2))))
  
  (define (union* ms)
    (foldl union (empty) ms))
  
  (define (from kvs)
    (foldl (lambda (p m)
             (insert (key p) (value p)))
           (empty) kvs))
  
  (define (difference m1 m2)
    (make-finite-map (finite-map-compare m1)
                     (set:difference (bindings m1) (bindings m2))))
  
  (define (intersection m1 m2)
    (make-finite-map (finite-map-compare m1)
                     (set:intersection (bindings m1) (bindings m2))))
  
  (define singleton
    (case-lambda
      [(x v)      (insert (cons x v) (empty))]
      [(cmp x v)  (insert x v (empty cmp))]))
  
  (define (size m)
    (set:size (bindings m)))
  
  (define (count x m)
    (if (member? (cons x 'dummy) (bindings m))
        1
        0))
  
  (define (select m)
    (when (empty? m)
      (error 'select "can't select element from an empty finite map"))
    (key (set:select (bindings m))))
  
  (define (equal=? m1 m2)
    (set:equal=? (bindings m1) (bindings m2)))
  
  
  (define (subbag? b1 b2)
    (let/ec return 
      (fold/val (lambda (x v p?)
                  (if p?
                      (<= no
                          (occurrences x b2))
                      (return #f)))
                #t b1)))
  
    
  
  (require "signatures/finite-map-signature.scm")
  (provide-finite-map)
)