diff --git a/pkgs/racket-doc/scribblings/reference/contracts.scrbl b/pkgs/racket-doc/scribblings/reference/contracts.scrbl index 7d08ff4201..7c2086d192 100644 --- a/pkgs/racket-doc/scribblings/reference/contracts.scrbl +++ b/pkgs/racket-doc/scribblings/reference/contracts.scrbl @@ -360,10 +360,32 @@ one of them.} @defproc[(real-in [n real?] [m real?]) flat-contract?]{ An alias for @racket[between/c].} -@defproc[(integer-in [j exact-integer?] [k exact-integer?]) flat-contract?]{ +@defproc[(integer-in [j (or/c exact-integer? #f)] [k (or/c exact-integer? #f)]) flat-contract?]{ Returns a @tech{flat contract} that requires the input to be an exact integer -between @racket[j] and @racket[k], inclusive.} +between @racket[j] and @racket[k], inclusive. If either @racket[j] or @racket[k] +is @racket[#f], then the range is unbounded on that end. + +@examples[#:eval (contract-eval) #:once + (define/contract two-digit-number + (integer-in 10 99) + 23) + + (define/contract not-a-two-digit-number + (integer-in 10 99) + 124) + + (define/contract negative-number + (integer-in #f -1) + -4) + + (define/contract not-a-negative-number + (integer-in #f -1) + 4)] + +@history[#:changed "6.8.0.2" @elem{Allow #@racket[j] and @racket[k] to be @racket[#f]}] + +} @defproc[(char-in [a char?] [b char?]) flat-contract?]{ diff --git a/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt b/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt index 2211802533..080e6430ba 100644 --- a/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt +++ b/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt @@ -1,24 +1,30 @@ #lang racket/base -(require "test-util.rkt") +(require "test-util.rkt" + (for-syntax racket/base)) (parameterize ([current-contract-namespace (make-basic-contract-namespace 'racket/class 'racket/contract/combinator)]) - (define (test-flat-contract contract pass fail) + (define-syntax (test-flat-contract stx) + (syntax-case stx () + [(_ contract pass fail) + #`(test-flat-contract/proc contract pass fail #,(syntax-line stx))])) + + (define (test-flat-contract/proc contract pass fail line) (contract-eval `(,test #t flat-contract? ,contract)) (define (run-two-tests maybe-rewrite) (let ([name (if (pair? contract) (car contract) contract)]) (let/ec k - (test/spec-failed (format "~a fail" name) + (test/spec-failed (format "~a fail, line ~a" name line) (maybe-rewrite `(contract ,contract ',fail 'pos 'neg) k) 'pos)) (let/ec k (test/spec-passed/result - (format "~a pass" name) + (format "~a pass, line ~a" name line) (maybe-rewrite `(contract ,contract ',pass 'pos 'neg) k) pass)))) (run-two-tests (λ (x k) x)) @@ -42,6 +48,9 @@ (test-flat-contract '(integer-in 0 10) 10 3/2) (test-flat-contract '(integer-in 0 10) 1 1.0) (test-flat-contract '(integer-in 1 1) 1 1.0) + (test-flat-contract '(integer-in 1 #f) 1 -1) + (test-flat-contract '(integer-in #f 1) -1 2) + (test-flat-contract '(integer-in #f #f) -1 "x") (test-flat-contract '(char-in #\a #\z) #\a #\Z) (test-flat-contract '(char-in #\a #\z) #\z #\A) (test-flat-contract '(char-in #\a #\z) #\b "b") diff --git a/pkgs/racket-test/tests/racket/contract/name.rkt b/pkgs/racket-test/tests/racket/contract/name.rkt index d2322bb92b..09d7505027 100644 --- a/pkgs/racket-test/tests/racket/contract/name.rkt +++ b/pkgs/racket-test/tests/racket/contract/name.rkt @@ -249,6 +249,9 @@ (test-name 'real? (between/c -inf.0 +inf.0)) (test-name '5 (between/c 5 5)) (test-name '(integer-in 0 10) (integer-in 0 10)) + (test-name '(integer-in 10 #f) (integer-in 10 #f)) + (test-name '(integer-in #f 10) (integer-in #f 10)) + (test-name 'exact-integer? (integer-in #f #f)) (test-name '(char-in #\a #\z) (char-in #\a #\z)) (test-name '(and/c 0 exact?) (integer-in 0 0)) (test-name '(real-in 1 10) (real-in 1 10)) diff --git a/pkgs/racket-test/tests/racket/contract/random-generate.rkt b/pkgs/racket-test/tests/racket/contract/random-generate.rkt index 2b1d98b077..f98472e4e9 100644 --- a/pkgs/racket-test/tests/racket/contract/random-generate.rkt +++ b/pkgs/racket-test/tests/racket/contract/random-generate.rkt @@ -41,10 +41,13 @@ (check-not-exn (λ () (test-contract-generation (listof boolean?)))) (check-not-exn (λ () (test-contract-generation (listof number?)))) -(check-not-exn (λ () (test-contract-generation (integer-in 0 100)))) (check-not-exn (λ () (test-contract-generation exact-nonnegative-integer?))) (check-not-exn (λ () (test-contract-generation natural?))) +(check-not-exn (λ () (test-contract-generation (integer-in 0 100)))) (check-not-exn (λ () (test-contract-generation (integer-in 0 (expt 2 1000))))) +(check-not-exn (λ () (test-contract-generation (integer-in 0 #f)))) +(check-not-exn (λ () (test-contract-generation (integer-in #f 0)))) +(check-not-exn (λ () (test-contract-generation (integer-in #f #f)))) (check-not-exn (λ () (test-contract-generation (char-in #\a #\z)))) (check-not-exn (λ () (test-contract-generation #\a))) (check-not-exn (λ () (test-contract-generation (between/c 1 100)))) diff --git a/pkgs/racket-test/tests/racket/contract/stronger.rkt b/pkgs/racket-test/tests/racket/contract/stronger.rkt index 0a5905d8fc..98499cef58 100644 --- a/pkgs/racket-test/tests/racket/contract/stronger.rkt +++ b/pkgs/racket-test/tests/racket/contract/stronger.rkt @@ -14,6 +14,12 @@ (ctest #t contract-stronger? (integer-in 0 4) (integer-in 0 4)) (ctest #t contract-stronger? (integer-in 1 3) (integer-in 0 4)) (ctest #f contract-stronger? (integer-in 0 4) (integer-in 1 3)) + (ctest #f contract-stronger? (integer-in 0 4) (integer-in 1 #f)) + (ctest #t contract-stronger? (integer-in 0 4) (integer-in #f 3)) + (ctest #t contract-stronger? (integer-in 0 #f) (integer-in #f #f)) + (ctest #t contract-stronger? (integer-in #f 0) (integer-in #f #f)) + (ctest #t contract-stronger? (integer-in 0 0) (and/c 0 exact?)) + (ctest #t contract-stronger? (and/c 0 exact?) (integer-in 0 0)) (ctest #t contract-stronger? #\a (char-in #\a #\c)) (ctest #f contract-stronger? #\a (char-in #\b #\c)) (ctest #t contract-stronger? (char-in #\f #\q) (char-in #\a #\z)) diff --git a/racket/collects/racket/contract/private/and.rkt b/racket/collects/racket/contract/private/and.rkt index c7a5eea6a4..d65fd0c7bc 100644 --- a/racket/collects/racket/contract/private/and.rkt +++ b/racket/collects/racket/contract/private/and.rkt @@ -214,32 +214,63 @@ (struct integer-in-ctc (start end) #:property prop:flat-contract (build-flat-contract-property - #:name (λ (ctc) - `(integer-in ,(integer-in-ctc-start ctc) - ,(integer-in-ctc-end ctc))) + #:name (λ (ctc) + (define start (integer-in-ctc-start ctc)) + (define end (integer-in-ctc-end ctc)) + (cond + [(or start end) + `(integer-in ,(integer-in-ctc-start ctc) + ,(integer-in-ctc-end ctc))] + [else 'exact-integer?])) #:first-order (λ (ctc) (define start (integer-in-ctc-start ctc)) (define end (integer-in-ctc-end ctc)) - (λ (x) (and (exact-integer? x) - (<= start x end)))) + (cond + [(and start end) (λ (x) (and (exact-integer? x) (<= start x end)))] + [start (λ (x) (and (exact-integer? x) (<= start x)))] + [end (λ (x) (and (exact-integer? x) (<= x end)))] + [else exact-integer?])) #:stronger (λ (this that) - (define this-start (integer-in-ctc-start this)) - (define this-end (integer-in-ctc-end that)) + (define this-start (or (integer-in-ctc-start this) -inf.0)) + (define this-end (or (integer-in-ctc-end this) +inf.0)) (cond [(integer-in-ctc? that) - (define that-start (integer-in-ctc-start that)) - (define that-end (integer-in-ctc-end that)) + (define that-start (or (integer-in-ctc-start that) -inf.0)) + (define that-end (or (integer-in-ctc-end that) +inf.0)) (<= that-start this-start this-end that-end)] [else #f])) #:generate (λ (ctc) (define start (integer-in-ctc-start ctc)) (define end (integer-in-ctc-end ctc)) - (λ (fuel) - (λ () - (+ start (random (min 4294967087 (+ (- end start) 1))))))))) + (define max-random-range 4294967087) + (cond + [(or start end) + (define _start (or start (- end max-random-range))) + (define _end (or end (+ start max-random-range))) + (λ (fuel) + (λ () + (+ _start (random (min 4294967087 (+ (- _end _start) 1))))))] + [else + (λ (fuel) + (λ () + (cond + [(zero? (random 20)) 0] + [else + (* (if (zero? (random 2)) -1 1) + (+ (expt 2 (geo-dist 1/2)) + (geo-dist 1/2)))])))])))) + +(define (geo-dist p) + (let loop ([n 0]) + (cond + [(< (random) p) (loop (+ n 1))] + [else n]))) (define/final-prop (integer-in start end) - (check-two-args 'integer-in start end exact-integer? exact-integer?) - (if (= start end) - (and/c start exact?) - (integer-in-ctc start end))) \ No newline at end of file + (define (|(or/c #f exact-integer?)| x) (or (not x) (exact-integer? x))) + (check-two-args 'integer-in start end |(or/c #f exact-integer?)| |(or/c #f exact-integer?)|) + (cond + [(and start end (= start end)) + (and/c start exact?)] + [else + (integer-in-ctc start end)]))