set.ss
#lang scheme
(require "private/common.ss")
(provide set? list->set set->list empty empty?
         intersection difference partition union xor
         intersections differences partitions unions xors
         adjoin add contains?
         set<=? set=?
         for/set for*/set)

(define-struct set (elts)
  #:property prop:custom-write (lambda (set port write?)
                                 (write-hash "set" (set-elts set) port write?)))

(define (list->set ls)
  (make-set (for/hash ([x ls])
              (values x #t))))

(define (set->list set)
  (for/list ([(key value) (set-elts set)])
    key))

(define (intersection set . sets)
  (make-set (hash-intersection (set-elts set) (map set-elts sets) for/hash)))

(define (intersections sets)
  (make-set (hash-intersection (set-elts (car sets)) (map set-elts (cdr sets)) for/hash)))

(define (difference set . sets)
  (make-set (hash-difference (set-elts set) (map set-elts sets) for/hash)))

(define (differences sets)
  (make-set (hash-difference (set-elts (car sets)) (map set-elts (cdr sets)) for/hash)))

(define (partition set . sets)
  (let-values ([(diff intersection) ((hash-partition #hash()) (set-elts set) (map set-elts sets))])
    (values (make-set diff) (make-set intersection))))

(define (partitions sets)
  (let-values ([(diff intersection) ((hash-partition #hash()) (set-elts (car sets)) (map set-elts (cdr sets)))])
    (values (make-set diff) (make-set intersection))))

(define empty (make-set #hash()))

(define (empty? set)
  (zero? (hash-count (set-elts set))))

(define (unions sets)
  (make-set (foldr union1 #hash() (map set-elts sets))))

(define (union . sets)
  (unions sets))

(define (xor . sets)
  (xors sets))

(define (xors sets)
  (make-set (foldr (xor1 #hash()) #hash() (map set-elts sets))))

(define (adjoin set . elts)
  (union set (list->set elts)))

(define (add elt set)
  (adjoin set elt))

(define (contains? set elt)
  (hash-ref (set-elts set) elt (lambda () #f)))

(define-syntax-rule (for/set (for-clause ...) body0 body ...)
  (make-set (for/hash (for-clause ...)
              (values (let () body0 body ...) #t))))
  
(define-syntax-rule (for*/set (for-clause ...) body0 body ...)
  (make-set (for*/hash (for-clause ...)
              (values (let () body0 body ...) #t))))

(define (set<=? . sets)
  (let loop ([hashes (map set-elts sets)])
    (match hashes
      [(cons hash1 (and hashes (cons hash2 _)))
       (and (<=?1 hash1 hash2) (loop hashes))]
      [_ #t])))

(define (set=? . sets)
  (let loop ([hashes (map set-elts sets)])
    (match hashes
      [(cons hash1 (and hashes (cons hash2 _)))
       (and (=?1 hash1 hash2) (loop hashes))]
      [_ #t])))