vhash-list.ss
#lang typed-scheme
(provide bind empty? get vhash-list VHashList)

(require (prefix-in ra: "skewbinaryrandomaccesslist.ss"))
(define-struct: (A) Base ([prevbase : (Block A)]
                          [prevoffset : Integer]
                          [key-value-pairs : (ra:RAList (Pair A A))]
                          [size : Integer]))
(define-struct: Mt ())

(define-type-alias (Block A) (U Mt (Base A)))

(define-struct: (A) List ([offset : Integer]
                          [base : (Base A)]
                          [size : Integer]))

(define-type-alias VHashList (All (A) (List A)))

(define empty-vlist (make-List 0 (make-Base (make-Mt) 0 ra:empty 1) 0))

(: empty? : (All (A) ((VHashList A) -> Boolean)))
(define (empty? vlist)
  (zero? (List-size vlist)))

(: bind : (All (A) (A A (VHashList A) -> (VHashList A))))
(define (bind key value vlst)
  (if (with-handlers ([exn:fail? (lambda (error?) #t)])
        (get key vlst)
        #f)
      (add-elem key value vlst)
      (error 
       (format "Duplicate key: ~a already exists in the hash-list :" key) 
       'add-elem)))

(: add-elem : (All (A) (A A (VHashList A) -> (VHashList A))))
(define (add-elem key value vlst)
  (let* ([offset (List-offset vlst)]
         [size (List-size vlst)]
         [base (List-base vlst)]
         [prevbase (Base-prevbase base)]
         [prevoffset (Base-prevoffset base)]
         [keys (Base-key-value-pairs base)]
         [basesize (Base-size base)]
         [newoffset (add1 offset)]
         [pair (cons key value)])
    (if (< offset basesize) 
        (make-List newoffset 
                   (make-Base prevbase 
                              prevoffset
                              (ra:kons pair keys)
                              basesize)
                   (add1 size))
        (make-List 1 
                   (make-Base base 
                              offset
                              (ra:kons pair ra:empty)
                              (* basesize 2))
                   (add1 size)))))

(: first : (All (A) ((VHashList A) -> (Pair A A))))
(define (first vlst)
  (let ([offset (List-offset vlst)]
        [size (List-size vlst)]
        [base (List-base vlst)])
    (if (< (sub1 offset) size)
        (ra:head (Base-key-value-pairs base))
        (error "List is empty :" 'first))))

(: rest : (All (A) ((VHashList A) -> (VHashList A))))
(define (rest vlst)
  (let* ([offset (List-offset vlst)]
         [size (List-size vlst)]
         [base (List-base vlst)]
         [len (Base-size base)]
         [prev (Base-prevbase base)])
    (cond 
      [(and (zero? len) (zero? offset)) (error "List is empty :" 'rest)]
      [(> offset 1) (make-List (sub1 offset) 
                               (make-Base (Base-prevbase base)
                                          (Base-prevoffset base)
                                          (ra:tail (Base-key-value-pairs base))
                                          (Base-size base))
                               (sub1 size))]
      [(Base? prev) (make-List (Base-prevoffset base) prev (sub1 size))]
      [else empty-vlist])))
        

(: size : (All (A) ((VHashList A) -> Integer)))
(define (size vlst)
  (List-size vlst))

(: get : (All (A) (A (VHashList A) -> A)))
(define (get key vlist)
  (if (zero? (List-size vlist)) 
      (error (format "Key ~a not found in hash list :" key) 'get)
      (get-helper key vlist)))

(: get-helper : (All (A) (A (VHashList A) -> A)))
(define (get-helper key vlist)
  (let ([fst (first vlist)])
    (if (eq? key (car fst)) 
        (cdr fst)
        (get key (rest vlist)))))

(: vhash-list : (All (A) ((Listof A) (Listof A) -> (VHashList A))))
(define (vhash-list keys values)
  (foldr (inst bind A) empty-vlist keys values))