#lang racket

; The code
(define (raw f)
  (f (λ () (void))))

(define (client seed use-affine)
  (random-seed seed)
  (use-affine
   (λ (f)
     (define used? #f)
     (for ([i (in-range 10000)])
       (sync (alarm-evt (random 100)))
       (unless (or used? (zero? (random 3)))
         (set! used? #t)
         (f))))))

(define (bad-client use-affine)
  (use-affine
   (λ (f)
     (f)
     (f))))

; The benchmarks
(define ctc
  (contract
   (-> (-> (-> any/c) any/c) any/c)
   raw 'pos 'neg))

(define aff->
  (make-contract
   #:name 'affine
   #:first-order procedure?
   #:projection
   (λ (b)
     (λ (f)
       (define called? #f)
       (λ ()
         (when called?
           (raise-blame-error b f "called twice!"))
         (set! called? #t)
         (define x (f))
         x)))))
(define ad-hoc
  (contract
   (-> (-> aff-> any/c) any/c)
   raw 'pos 'neg))

(require unstable/temp-c/dsl)
(define (rgx)
  (contract
   (with-monitor 
    (-> (-> (label 'affine (-> any/c)) any/c) any/c)
    (complement
     (seq (star _)
          (call 'affine)
          (star _)
          (call 'affine))))
   raw 'pos 'neg))

; The runner
(require tests/stress)
(define seed (+ 1 (random (expt 2 30))))
(define-syntax-rule (STRESS version ...)
  (begin
    (with-handlers ([exn? (λ (x) (void))])
      (bad-client version)
      (printf "~a does not fail when it should\n" 'version))
    ...
    (with-handlers ([exn? (λ (x) 
                            (printf "~a fails when it should not\n" 'version))])
      (client seed version))
    ...
    (newline)
    (stress 4 
            [(format "~a" 'version)
             (client seed version)]
            ...)))

(STRESS raw ctc ad-hoc (rgx))


