
(load-relative "loadtest.ss")

(Section 'procs)

;; ----------------------------------------

(define (f0) null)
(define (f1 x) (list x))
(define (f1+ x . rest) (cons x rest))
(define (f0:a #:a a) (list a))
(define (f0:a? #:a [a 0]) (list a))
(define (f1:a x #:a a) (list x a))
(define (f1:a? x #:a [a 0]) (list x a))
(define (f1+:a x #:a a . args) (list* x a args))
(define (f1+:a? x #:a [a 0] . args) (list* x a args))
(define (f0:a:b #:a a #:b b) (list a b))
(define (f0:a?:b #:a [a 0] #:b b) (list a b))
(define (f1:a:b x #:a a #:b b) (list x a b))
(define (f1:a?:b x #:a [a 0] #:b b) (list x a b))
(define (f1+:a:b x #:a a #:b b . args) (list* x a b args))
(define (f1+:a?:b x #:a [a 0] #:b b . args) (list* x a b args))
(define (f0:a:b? #:a a #:b [b 1]) (list a b))
(define (f0:a?:b? #:a [a 0] #:b [b 1]) (list a b))
(define (f1:a:b? x #:a a #:b [b 1]) (list x a b))
(define (f1:a?:b? x #:a [a 0] #:b [b 1]) (list x a b))
(define (f1+:a:b? x #:a a #:b [b 1] . args) (list* x a b args))
(define (f1+:a?:b? x #:a [a 0] #:b [b 1] . args) (list* x a b args))
(define f_ (case-lambda))
(define f_1_2 (case-lambda
               [(x) (list x)]
               [(x y) (list x y)]))
(define f_0_2+ (case-lambda
               [() null]
               [(x y . args) (list* x y args)]))
(define f1:+ (make-keyword-procedure
              (lambda (kws kw-args x)
                (cons x kw-args))
              (lambda (x) (list x))))

(define procs
  `((,f0 0 () ())
    (,f1 1 () ())
    (,f1+ ,(make-arity-at-least 1) () ())
    (,f0:a 0 (#:a) (#:a))
    (,f0:a? 0 () (#:a))
    (,f1:a 1 (#:a) (#:a))
    (,f1:a? 1 () (#:a))
    (,f1+:a ,(make-arity-at-least 1) (#:a) (#:a))
    (,f1+:a? ,(make-arity-at-least 1) () (#:a))
    (,f0:a:b 0 (#:a #:b) (#:a #:b))
    (,f0:a?:b 0 (#:b) (#:a #:b))
    (,f1:a:b 1 (#:a #:b) (#:a #:b))
    (,f1:a?:b 1 (#:b) (#:a #:b))
    (,f1+:a:b ,(make-arity-at-least 1) (#:a #:b) (#:a #:b))
    (,f1+:a?:b ,(make-arity-at-least 1) (#:b) (#:a #:b))
    (,f0:a:b? 0 (#:a) (#:a #:b))
    (,f0:a?:b? 0 () (#:a #:b))
    (,f1:a:b? 1 (#:a) (#:a #:b))
    (,f1:a?:b? 1 () (#:a #:b))
    (,f1+:a:b? ,(make-arity-at-least 1) (#:a) (#:a #:b))
    (,f1+:a?:b? ,(make-arity-at-least 1) () (#:a #:b))
    (,f_ () () ())
    (,f_1_2 (1 2) () ())
    (,f_0_2+ ,(list 0 (make-arity-at-least 2)) () ())
    (,f1:+ 1 () #f)))

(for-each (lambda (p)
            (let ([a (cadr p)])
              (test a procedure-arity (car p))
              (test-values (list (caddr p)  (cadddr p))
                           (lambda ()
                             (procedure-keywords (car p))))
              (let ([1-ok? (let loop ([a a])
                             (or (equal? a 1)
                                 (and (arity-at-least? a)
                                      ((arity-at-least-value a) . <= . 1))
                                 (and (list? a)
                                      (ormap loop a))))])
                (test 1-ok? procedure-arity-includes? (car p) 1)
                (let ([allowed (cadddr p)])
                  (if 1-ok?
                      (cond
                       [(equal? allowed '())
                        (test (let ([auto (cddddr p)])
                                (cond
                                 [(equal? auto '((#:a #:b))) '(1 0 1)]
                                 [(equal? auto '((#:a))) '(1 0)]
                                 [(equal? auto '((#:a))) '(1 0)]
                                 [else '(1)]))
                              (car p) 1)
                        (err/rt-test ((car p) 1 #:a 0))
                        (err/rt-test ((car p) 1 #:b 0))
                        (err/rt-test ((car p) 1 #:a 0 #:b 0))]
                       [(equal? allowed '(#:a))
                        (test (if (pair? (cddddr p)) 
                                  '(10 20 1) ; dropped #:b
                                  '(10 20))
                              (car p) 10 #:a 20)
                        (err/rt-test ((car p) 1 #:b 0))
                        (err/rt-test ((car p) 1 #:a 0 #:b 0))]
                       [(equal? allowed '(#:b))
                        (test '(10.0 20.0) (car p) 10.0 #:b 20.0)
                        (err/rt-test ((car p) 1 #:a 0))
                        (err/rt-test ((car p) 1 #:a 0 #:b 0))]
                       [(equal? allowed '(#:a #:b))
                        (test '(100 200 300) (car p) 100 #:b 300 #:a 200)
                        (err/rt-test ((car p) 1 #:a 0 #:b 0 #:c 3))]
                       [(equal? allowed #f)
                        (test '(1 2 3) (car p) 1 #:b 3 #:a 2)])
                      (begin
                        ;; Try just 1:
                        (err/rt-test ((car p) 1))
                        ;; Try with right keyword args, to make sure the by-position
                        ;; arity is checked:
                        (cond
                         [(equal? allowed '())
                          (void)]
                         [(equal? allowed '(#:a))
                          (err/rt-test ((car p) 1 #:a 1))]
                         [(equal? allowed '(#:b))
                          (err/rt-test ((car p) 1 #:b 1))]
                         [(equal? allowed '(#:a #:b))
                          (err/rt-test ((car p) 1 #:a 1 #:b 1))]
                         [(equal? allowed #f)
                          (err/rt-test ((car p) 1 #:a 1 #:b 1))])))))))
          (append procs
                  ;; reduce to arity 1 or nothing:
                  (map (lambda (p)
                         (let ([p (car p)])
                           (let-values ([(req allowed) (procedure-keywords p)])
                             (if (null? allowed)
                                 (if (procedure-arity-includes? p 1)
                                     (list (procedure-reduce-arity p 1) 1 req allowed)
                                     (list (procedure-reduce-arity p '()) '() req allowed))
                                 (if (procedure-arity-includes? p 1)
                                     (list (procedure-reduce-keyword-arity p 1 req allowed) 1 req allowed)
                                     (list (procedure-reduce-keyword-arity p '() req allowed) '() req allowed))))))
                       procs)
                  ;; reduce to arity 0 or nothing:
                  (map (lambda (p)
                         (let ([p (car p)])
                           (let-values ([(req allowed) (procedure-keywords p)])
                             (if (null? allowed)
                                 (if (procedure-arity-includes? p 0)
                                     (list (procedure-reduce-arity p 0) 0 req allowed)
                                     (list (procedure-reduce-arity p '()) '() req allowed))
                                 (if (procedure-arity-includes? p 0)
                                     (list (procedure-reduce-keyword-arity p 0 req allowed) 0 req allowed)
                                     (list (procedure-reduce-keyword-arity p '() req allowed) '() req allowed))))))
                       procs)
                  ;; reduce to arity 1 or nothing --- no keywords:
                  (map (lambda (p)
                         (let ([p (car p)])
                           (let-values ([(req allowed) (procedure-keywords p)])
                             (if (and (procedure-arity-includes? p 1)
                                      (null? req))
                                 (list* (procedure-reduce-arity p 1) 1 '() '()
                                        (if (null? allowed)
                                            null
                                            (list allowed)))
                                 (list (procedure-reduce-arity p '()) '() '() '())))))
                       procs)
                  ;; reduce to arity 0 or nothing --- no keywords:
                  (map (lambda (p)
                         (let ([p (car p)])
                           (let-values ([(req allowed) (procedure-keywords p)])
                             (if (and (procedure-arity-includes? p 0)
                                      (null? req))
                                 (list (procedure-reduce-arity p 0) 0 '() '())
                                 (list (procedure-reduce-arity p '()) '() '() '())))))
                       procs)
                  ;; make #:a required, if possible:
                  (map (lambda (p)
                         (let-values ([(req allowed) (procedure-keywords (car p))])
                           (let ([new-req (if (member '#:a req)
                                              req
                                              (cons '#:a req))])
                             (list (procedure-reduce-keyword-arity 
                                    (car p)
                                    (cadr p)
                                    new-req
                                    allowed)
                                   (cadr p)
                                   new-req
                                   allowed))))
                       (filter (lambda (p)
                                 (let-values ([(req allowed) (procedure-keywords (car p))])
                                   (or (not allowed)
                                       (memq '#:a allowed))))
                               procs))
                  ;; remove #:b, if allowed and not required:
                  (map (lambda (p)
                         (let-values ([(req allowed) (procedure-keywords (car p))])
                           (let ([new-allowed (if allowed
                                                  (remove '#:b allowed)
                                                  '(#:a))])
                             (list* (procedure-reduce-keyword-arity 
                                     (car p)
                                     (cadr p)
                                     req
                                     new-allowed)
                                    (cadr p)
                                    req
                                    new-allowed
                                    (if allowed
                                        (list allowed)
                                        '())))))
                       (filter (lambda (p)
                                 (let-values ([(req allowed) (procedure-keywords (car p))])
                                   (and (or (not allowed)
                                            (memq '#:b allowed))
                                        (not (memq '#:b req)))))
                               procs))))

;; ----------------------------------------

(report-errs)
