(module types-aux mzscheme
  (require ""

  (provide (all-defined) type-equal?)
  (define (make-union* set)
    (if (= 1 (set:size set))
        (set:select set)
        (make-union set)))
  (require (planet "" ("dherman" "memoize.plt" 2)))

  (define (Un . args) 
    (define (make-union/one t) (if (union? t) (union-elems t) (set:make-ordered type-compare t)))    
    (define (union2 a b)     
      (cond [(subtype a b) b]
            [(subtype b a) a]
            [(and (values-ty? a) (values-ty? b) (= (length (values-ty-types a)) (length (values-ty-types b))))
             (make-values-ty (map Un (values-ty-types a) (values-ty-types b)))]
            [(or (values-ty? a) (values-ty? b))
             (tc-error "Internal error: wrong values in Un: ~a ~a" a b)]
             (make-union* (set:union (make-union/one a) (make-union/one b)))]))
    (foldl union2 (make-union (set:make-ordered type-compare)) args))
  (define-syntax -v 
    (syntax-rules ()
      [(_ x) (make-tvar 'x)]))
  (define-syntax -poly
    (syntax-rules ()
      [(_ (vars ...) ty)
       (let ([vars (-v vars)] ...)
         (make-poly (list 'vars ...) ty))]))
  (define-syntax -mu
    (syntax-rules ()
      [(_ var ty)
       (let ([var (-v var)])
         (make-mu 'var ty))]))
  (define -values make-values-ty)
  ;; produce the appropriate type of a list of types
  ;; that is - if there is exactly one type, just produce it, otherwise produce a values-ty
  ;; list[type] -> type
  (define (list->values-ty l)
    (if (= 1 (length l)) (car l) (make-values-ty l)))

  (define -pair make-pair-ty)
  (define -base make-base-type)
  (define -struct make-struct-ty)
  (define A (Un Sym N B -String))
  (define (make-Listof elem) (-mu list-rec (Un (make-value null) (-pair elem list-rec))))
  (define -Listof (-poly (list-elem) (make-Listof list-elem)))
  (define -lst make-Listof)
  (define -val make-value)
  (define -Sexp (-mu x (Un Sym N B -String (make-value '()) (-pair x x))))
  (define -Port (Un -Input-Port -Output-Port))
  (define (-lst* . args) (if (null? args)
                             (-val null)
                             (-pair (car args) (apply -lst* (cdr args)))))

  #;(define NE (-mu x (Un N (make-Listof x))))
  (define -NE (-mu x (Un N (-pair x (-pair Sym (-pair x (-val null)))))))
  (define (Un/eff . args)
    (apply Un (map tc-result-t args)))

  (define -Param make-param-ty)
  (define make-pred-ty
      [(in out t)
       (make-funty (list (make-arr in out #f 
                                   (list (make-latent-restrict-effect t))
                                   (list (make-latent-remove-effect t)))))]
      [(t) (make-pred-ty (list Univ) B t)]))