#lang racket/base

(require
  rackunit
  racket/sandbox
  racket/flonum racket/fixnum racket/unsafe/ops
  racket/math
  syntax/srcloc
  (for-syntax
    racket/base
    syntax/parse))

(provide tests)


(define (mk-eval lang)
  (call-with-trusted-sandbox-configuration
   (λ ()
     (parameterize ([sandbox-memory-limit 300])
       (make-evaluator lang)))))
(define racket-eval (mk-eval 'racket))
(define tr-eval     (mk-eval 'typed/racket))

(define-syntax bad-opt
  (syntax-parser
    [(_ exp:expr)
     #`(test-case #,(format "~a" (syntax->datum #'exp))
         (define r-value (racket-eval #'exp))
         (define tr-value (tr-eval #'exp))
         (with-check-info (['r-value r-value]
                           ['tr-value tr-value]
                           ['location (build-source-location-list (quote-syntax exp))])
           (when (equal? r-value tr-value)
             (fail-check "Known bug no longer exists."))))]))

(define-syntax good-opt
  (syntax-parser
    [(_ exp:expr)
     #`(test-case #,(format "~a" (syntax->datum #'exp))
         (define r-value (racket-eval #'exp))
         (define tr-value (tr-eval #'exp))
         (with-check-info (['r-value r-value]
                           ['tr-value tr-value]
                           ['location (build-source-location-list (quote-syntax exp))])
           (when (not (equal? r-value tr-value))
             (fail-check "Optimizer regression"))))]))


;; TODO add this as a test
;; type-before = Single-Flonum-Complex
;; type-after = Float-Complex
;; redex-check: <collects>/typed-racket-test/tr-random-testing.rkt:258
;; counterexample found after 499 attempts:
;(log (make-rectangular -inf.f -inf.f))

(define tests
  (test-suite "Known bugs"

    ;; Arguments are converted to inexact too early
    (good-opt (* (make-rectangular -inf.0 1) (* 1 1)))
    (good-opt (/ -inf.0-inf.0i 8))
    (good-opt (- (* -1 1 +nan.0) 1.0+1.0i))
    (good-opt (- (* (/ 6 11) (/ 1.2345678f0 123456.7f0)) (make-rectangular 0.0 0.3)))
    (good-opt (/ 1.0 0.0+0.0i))
    (good-opt (+ 0.0+0.0i (* 1 1 +inf.0)))
    (bad-opt (* 1.0f-30 1.0f-30 1.0e60+1.0e60i))

    ;; Unary division has bad underflow
    (good-opt (/ (make-rectangular 1e+100 1e-300)))
    (good-opt (/ 0.5+1.7e+308i))
    (good-opt (/ 1 (make-rectangular 1e+100 1e-300)))
    (good-opt (/ 1 0.5+1.7e+308i))

    ;; Division of complex 0 should only make part of the result nan
    (good-opt (/ 0.0+0.0i))
    (good-opt (/ 1 0.0+0.0i))
    (good-opt (/ 1.5 -3.0+9.8e-324i))

    ;; Division of complex infinity should only make part of the result nan
    (good-opt (/ (make-rectangular 1.0 +inf.0)))
    (good-opt (/ (make-rectangular +inf.0 1.0)))
    (good-opt (/ 1 (make-rectangular 1.0 +inf.0)))
    (good-opt (/ 1 (make-rectangular +inf.0 1.0)))

    ;; Exp of large real should have 0 imaginary component
    (good-opt (+ (exp 1.7976931348623151e+308) 0.0+0.0i))

    ;; Multiplication of multiple args should keep exact semantics for exact args
    (bad-opt (* (expt 10 500) (expt 10 -500) 1.0+1.0i))

    ;; Addition of multiple args should keep exact semantics for exact args
    (bad-opt (+ (expt 10 501) (expt -10 501) 1.0+1.0i))

    ;; Magnitude should not overflow unless necessary
    (bad-opt (magnitude 3.0e300+4.0e300i))

    ;; Negation should correctly compute sign of 0.0
    (good-opt (- 0.0+0.0i))
    (bad-opt (- 0+0i 0.0+0.0i))

    ;; Conjugate should correctly compute sign of 0.0
    (good-opt (conjugate 0.0+0.0i))))

(module+ main
  (require rackunit/text-ui)
  (void (run-tests tests)))
