;;; foreign.ms
;;; Copyright 1984-2016 Cisco Systems, Inc.
;;; 
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;; 
;;; http://www.apache.org/licenses/LICENSE-2.0
;;; 
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

(define-syntax machine-case
  (lambda (x)
    (syntax-case x ()
      [(_ [(a ...) e ...] m ...)
       (if (memq (machine-type) (datum (a ...)))
           #'(begin (void) e ...)
           #'(machine-case m ...))]
      [(_ [else e ...]) #'(begin (void) e ...)]
      [(_) #'(void)])))

#;(define-syntax foreign-struct-mat
 (syntax-rules ()
   [(_ name n)
    (mat name
       (set! fs-size
          ((foreign-procedure (format "s~a_size" n) () unsigned-32)))
       (set! fs-align
          ((foreign-procedure (format "s~a_align" n) () unsigned-32)))
       (set! fs-get-s
          (eval `(foreign-procedure ,(format "get_s~a" n) (char)
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-get-sp
          (foreign-procedure (format "get_s~ap" n) (char)
             foreign-pointer))
       (set! fs-s_f1_s
          (eval `(foreign-procedure ,(format "s~a_f1_s~a" n n)
                    ((foreign-object ,fs-size ,fs-align)
                     (foreign-object ,fs-size ,fs-align))
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-sp_f1_s
          (eval `(foreign-procedure ,(format "s~ap_f1_s~a" n n)
                    (foreign-pointer
                     (foreign-object ,fs-size ,fs-align))
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-s_f1_sp
          (eval `(foreign-procedure ,(format "s~a_f1_s~ap" n n)
                    ((foreign-object ,fs-size ,fs-align)
                     foreign-pointer)
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-sp_f1_sp
          (eval `(foreign-procedure ,(format "s~ap_f1_s~ap" n n)
                    (foreign-pointer
                     foreign-pointer)
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-s_f2_s
          (eval `(foreign-procedure ,(format "s~a_f2_s~a" n n)
                    (integer-32
                     (foreign-object ,fs-size ,fs-align)
                     (foreign-object ,fs-size ,fs-align))
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-sp_f2_s
          (eval `(foreign-procedure ,(format "s~ap_f2_s~a" n n)
                    (integer-32
                     foreign-pointer
                     (foreign-object ,fs-size ,fs-align))
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-s_f2_sp
          (eval `(foreign-procedure ,(format "s~a_f2_s~ap" n n)
                    (integer-32
                     (foreign-object ,fs-size ,fs-align)
                     foreign-pointer)
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-sp_f2_sp
          (eval `(foreign-procedure ,(format "s~ap_f2_s~ap" n n)
                    (integer-32
                     foreign-pointer
                     foreign-pointer)
                    (foreign-object ,fs-size ,fs-align))))
       (set! fs-s_f3_s
          (eval `(foreign-procedure ,(format "s~a_f3_s~a" n n)
                    ((foreign-object ,fs-size ,fs-align)
                     (foreign-object ,fs-size ,fs-align))
                    boolean)))
       (set! fs-sp_f3_s
          (eval `(foreign-procedure ,(format "s~ap_f3_s~a" n n)
                    (foreign-pointer
                     (foreign-object ,fs-size ,fs-align))
                    boolean)))
       (set! fs-s_f3_sp
          (eval `(foreign-procedure ,(format "s~a_f3_s~ap" n n)
                    ((foreign-object ,fs-size ,fs-align)
                     foreign-pointer)
                    boolean)))
       (set! fs-sp_f3_sp
          (eval `(foreign-procedure ,(format "s~ap_f3_s~ap" n n)
                    (foreign-pointer
                     foreign-pointer)
                    boolean)))

       (set! fs-a (fs-get-s #\a))
       (string? fs-a)
       (set! fs-ap (fs-get-sp #\a))
       (integer? fs-ap)
       (set! fs-b (fs-get-s #\b))
       (string? fs-b)
       (set! fs-bp (fs-get-sp #\b))
       (integer? fs-bp)


       (fs-s_f3_s fs-a fs-a)
       (fs-s_f3_s fs-a fs-ap)
       (fs-s_f3_s fs-ap fs-a)
       (fs-s_f3_s fs-ap fs-ap)
       (fs-sp_f3_s fs-a fs-a)
       (fs-sp_f3_s fs-a fs-ap)
       (fs-sp_f3_s fs-ap fs-a)
       (fs-sp_f3_s fs-ap fs-ap)
       (fs-s_f3_sp fs-a fs-a)
       (fs-s_f3_sp fs-a fs-ap)
       (fs-s_f3_sp fs-ap fs-a)
       (fs-s_f3_sp fs-ap fs-ap)
       (fs-sp_f3_sp fs-a fs-a)
       (fs-sp_f3_sp fs-a fs-ap)
       (fs-sp_f3_sp fs-ap fs-a)
       (fs-sp_f3_sp fs-ap fs-ap)

       (not (fs-s_f3_s fs-a fs-b))
       (not (fs-s_f3_s fs-a fs-bp))
       (not (fs-s_f3_s fs-ap fs-b))
       (not (fs-s_f3_s fs-ap fs-bp))
       (not (fs-sp_f3_s fs-a fs-b))
       (not (fs-sp_f3_s fs-a fs-bp))
       (not (fs-sp_f3_s fs-ap fs-b))
       (not (fs-sp_f3_s fs-ap fs-bp))
       (not (fs-s_f3_sp fs-a fs-b))
       (not (fs-s_f3_sp fs-a fs-bp))
       (not (fs-s_f3_sp fs-ap fs-b))
       (not (fs-s_f3_sp fs-ap fs-bp))
       (not (fs-sp_f3_sp fs-a fs-b))
       (not (fs-sp_f3_sp fs-a fs-bp))
       (not (fs-sp_f3_sp fs-ap fs-b))
       (not (fs-sp_f3_sp fs-ap fs-bp))

       (fs-sp_f3_sp (fs-s_f1_s fs-ap fs-bp) (fs-sp_f1_s fs-a fs-bp))
       (fs-sp_f3_sp (fs-s_f1_sp fs-ap fs-b) (fs-sp_f1_sp fs-a fs-b))

       (fs-sp_f3_sp (fs-s_f2_s 1 fs-ap fs-bp) (fs-sp_f2_s 1 fs-a fs-bp))
       (fs-sp_f3_sp (fs-s_f2_sp 1 fs-ap fs-b) (fs-sp_f2_sp 1 fs-a fs-b))
      )]))

(define-syntax auto-mat-ick
  (lambda (x)
    (syntax-case x ()
      ((_ name)
       (let ((ls (let f ([ls (string->list (datum name))])
                   (if (null? ls)
                       '()
                       (cons (car ls) (f (cddr ls)))))))
         (with-syntax ([((p v) ...)
                        (map (lambda (c)
                               (case (syntax->datum c)
                                 [(#\n) `(,(syntax integer-32)
                                          ,(random 1000))]
                                 [(#\s) `(,(syntax single-float)
                                          ,(truncate (random 1000.0)))]
                                 [(#\d) `(,(syntax double-float)
                                          ,(truncate (random 1000.0)))]))
                             ls)])
           (syntax (= (let ([x (foreign-procedure name (p ...) double-float)])
                        (x v ...))
                      (+ v ...)))))))))

(machine-case
  [(i3ob ti3ob a6ob ta6ob i3fb ti3fb a6fb ta6fb a6s2 ta6s2 i3s2 ti3s2 i3qnx ti3qnx)
   (mat load-shared-object
      (file-exists? "foreign1.so")
      (begin (load-shared-object "./foreign1.so") #t)
      (begin (load-shared-object "libc.so") #t)
      (error? (load-shared-object 3))
    )
   ]
  [(i3le ti3le a6le ta6le arm32le tarm32le ppc32le tppc32le)
   (mat load-shared-object
      (file-exists? "foreign1.so")
      (begin (load-shared-object "./foreign1.so") #t)
      (begin (load-shared-object "libc.so.6") #t)
      (error? (load-shared-object 3))
    )
   ]
  [(i3nb ti3nb a6nb ta6nb)
   (mat load-shared-object
      (file-exists? "foreign1.so")
      (begin (load-shared-object "./foreign1.so") #t)
      (begin (load-shared-object "libc.so") #t)
      (error? (load-shared-object 3))
    )
   ]
  [(i3nt ti3nt a6nt ta6nt)
   (mat load-shared-object
      (file-exists? "foreign1.so")
      (begin (load-shared-object "foreign1.so") #t)
      (begin
        (load-shared-object
          (let ([bindir (format "../bin/~a" (machine-type))])
            (define prefix?
              (lambda (x y)
                (let ([n (string-length x)])
                  (and (fx<= n (string-length y))
                       (let prefix? ([i 0])
                         (or (fx= i n)
                             (and (char=? (string-ref x i) (string-ref y i))
                                  (prefix? (fx+ i 1)))))))))
            (format "~a/~a" bindir
              (or (find (lambda (s) (prefix? "vcruntime" s)) (directory-list bindir))
                  (errorf #f "did not find C runtime vcruntime*.dll in ~a" bindir)))))
        #t)
      (begin (load-shared-object "kernel32.dll") #t)
      (error? (load-shared-object 3))
    )
   ]
  [(i3osx ti3osx a6osx ta6osx)
   (mat load-shared-object
      (file-exists? "foreign1.so")
      (begin (load-shared-object "./foreign1.so") #t)
      (begin (load-shared-object "libc.dylib") #t)
      #t
      (error? (load-shared-object 3))
    )
   ]
  [else
   (mat foreign-procedure
      (error? (foreign-procedure "foo" () scheme-object))
      (begin (define (idint32 x)
            (errorf 'idint32 "invalid foreign-procedure argument ~s" x))
          (procedure? idint32))
      (error? (idint32 #x80000000))
      (error? (idint32 #x80000001))
      (error? (idint32 #xffffffff))
      (error? (idint32 #x8000000080000000))
      (error? (idint32 #x-80000001))
      (error? (idint32 #x-8000000080000000))
      (error? (idint32 #f))
      (error? (idint32 "hi"))
      (begin (define (iduns32 x)
            (errorf 'iduns32 "invalid foreign-procedure argument ~s" x))
             (procedure? iduns32))
         (error? (iduns32 #x100000000))
      (error? (iduns32 #x8000000080000000))
      (error? (iduns32 -1))
      (error? (iduns32 #x-7fffffff))
      (error? (iduns32 #x-80000000))
      (error? (iduns32 #x-80000001))
      (error? (iduns32 #x-8000000080000000))
      (error? (iduns32 #f))
      (error? (iduns32 "hi"))
      (begin (define (idfix x)
            (errorf 'idfix "invalid foreign-procedure argument ~s" x))
             (procedure? idfix))
      (error? (idfix (+ (most-positive-fixnum) 1)))
      (error? (idfix (- (most-negative-fixnum) 1)))
      (error? (errorf 'id "return value ~s is out of range" #x7fffffff))
      (error? (errorf 'id "return value ~s is out of range" #x-80000000))
      (error? (errorf 'id "invalid foreign-procedure argument ~s" 0))
      (error? (errorf 'id "return value ~s is out of range" #x7fffffff))
      (error? (errorf 'id "invalid foreign-procedure argument ~s" 'foo))
      (error? (foreign-procedure 'abcde (integer-32) integer-32))
      (error? (errorf 'float_id "invalid foreign-procedure argument ~s" 0))
    )
   ])

(mat foreign-entry?
   (foreign-entry? "id")
   (foreign-entry? "idid")
   (foreign-entry? "ididid")
   (not (foreign-entry? "foo")))

(mat foreign-procedure
   (procedure? (foreign-procedure "idiptr" (scheme-object) scheme-object))
   (error? (foreign-procedure "i do not exist" (scheme-object) scheme-object))
   (error? (begin (foreign-procedure "i do not exist" () scheme-object) 'q))
   (error? (if (foreign-procedure "i do not exist" () scheme-object) 'q 'q))
   (error? (foreign-procedure 'foo () scheme-object))
   (error? (begin (foreign-procedure 'foo () scheme-object) 'q))
   (error? (if (foreign-procedure 'foo () scheme-object) 'q 'q))

   (eq? 'foo ((foreign-procedure "idiptr" (scheme-object) scheme-object) 'foo))

   (parameterize ([current-eval interpret])
       (eq? 'foo ((foreign-procedure "idiptr" (scheme-object) scheme-object) 'foo)))

   (not (eq? 'foo ((foreign-procedure "idiptr" (scheme-object) void) 'foo)))

   (begin (define idint32 (foreign-procedure "id" (integer-32) integer-32))
          (procedure? idint32))
   (eqv? (idint32 0) 0)
   (eqv? (idint32 #x7fffffff) #x7fffffff)
   (eqv? (idint32 -1) -1)
   (eqv? (idint32 #x-7fffffff) #x-7fffffff)
   (eqv? (idint32 #x-80000000) #x-80000000)
   (eqv? (idint32 #x80000000) (+ #x-100000000 #x80000000))
   (eqv? (idint32 #x80000001) (+ #x-100000000 #x80000001))
   (eqv? (idint32 #xffffffff) (+ #x-100000000 #xffffffff))
   (error? (idint32 #x100000000))
   (error? (idint32 #x100000001))
   (error? (idint32 #xfffffffffffffffffffffffffffff))
   (error? (idint32 #x8000000080000000))
   (error? (idint32 #x-80000001))
   (error? (idint32 #x-8000000080000000))
   (error? (idint32 #f))
   (error? (idint32 "hi"))

   (begin (define iduns32 (foreign-procedure "id" (unsigned-32) unsigned-32))
          (procedure? iduns32))
   (eqv? (iduns32 0) 0)
   (eqv? (iduns32 #x7fffffff) #x7fffffff)
   (eqv? (iduns32 #x80000000) #x80000000)
   (eqv? (iduns32 #x80000001) #x80000001)
   (eqv? (iduns32 #x88000000) #x88000000)
   (eqv? (iduns32 #xffffffff) #xffffffff)
   (error? (iduns32 #x100000000))
   (error? (iduns32 #x8000000080000000))
   (eqv? (iduns32 -1) (+ #x100000000 -1))
   (eqv? (iduns32 #x-7fffffff) (+ #x100000000 #x-7fffffff))
   (eqv? (iduns32 #x-80000000) (+ #x100000000 #x-80000000))
   (error? (iduns32 #x-80000001))
   (error? (iduns32 #x-ffffffff))
   (error? (iduns32 #x-fffffffffffffffffffffffffffffffff))
   (error? (iduns32 #x-80000001))
   (error? (iduns32 #x-8000000080000000))
   (error? (iduns32 #f))
   (error? (iduns32 "hi"))

   (eqv? #xffffffff ((foreign-procedure "id" (integer-32) unsigned-32) -1))
   (eqv? -1 ((foreign-procedure "id" (unsigned-32) integer-32) #xffffffff))

   (begin (define idfix (foreign-procedure "idiptr" (fixnum) fixnum))
          (procedure? idfix))
   (eqv? 0 (idfix 0))
   (eqv? -1 (idfix -1))
   (eqv? (quotient (most-positive-fixnum) 2)
         (idfix (quotient (most-positive-fixnum) 2)))
   (eqv? (quotient (most-negative-fixnum) 2)
         (idfix (quotient (most-negative-fixnum) 2)))
   (eqv? (most-positive-fixnum) (idfix (most-positive-fixnum)))
   (eqv? (most-negative-fixnum) (idfix (most-negative-fixnum)))
   (error? (idfix (+ (most-positive-fixnum) 1)))
   (error? (idfix (- (most-negative-fixnum) 1)))

; we've eliminated the return range checks---caveat emptor
;   (error? ((foreign-procedure "id" (integer-32) fixnum) #x7fffffff))
;   (error? ((foreign-procedure "id" (integer-32) fixnum) #x-80000000))
;   (error? ((foreign-procedure "id" (integer-32) char) #x7fffffff))

   (error? (foreign-procedure "id" (booleen) char))
   (error? (foreign-procedure "id" (integer-32 integer-34) char))
   (error? (foreign-procedure "id" () chare))
   (error? (foreign-procedure "id" (void) char))

   ((foreign-procedure "id" (boolean) boolean) #t)
   (not ((foreign-procedure "id" (boolean) boolean) #f))
   ((foreign-procedure "id" (boolean) boolean) 0)
   (= 1 ((foreign-procedure "id" (boolean) integer-32) #t))
   (= 1 ((foreign-procedure "id" (boolean) integer-32) 0))
   (= 0 ((foreign-procedure "id" (boolean) integer-32) #f))
   (not ((foreign-procedure "id" (integer-32) boolean) 0))
   ((foreign-procedure "id" (integer-32) boolean) 1)

   (char=? #\a ((foreign-procedure "id" (char) char) #\a))
   (= 0 ((foreign-procedure "id" (char) integer-32) #\nul))
   (char=? #\nul ((foreign-procedure "id" (integer-32) char) 0))
   (eqv? ((foreign-procedure "id" (integer-32) char) -1) #\377)
   (error? ((foreign-procedure "id" (char) void) 0))

   (let ([s "now is the time for all good men"])
      (string=? s ((foreign-procedure "idiptr" (string) string) s)))
   (let ([s "now is the time for all good men"])
      (not (eq? s ((foreign-procedure "idiptr" (string) string) s))))
  ; assuming iptr is same size as char *:
   (let ([id1 (foreign-procedure "idiptr" (string) string)]
         [id2 (foreign-procedure "idiptr" (string) iptr)]
         [id3 (foreign-procedure "idiptr" (iptr) string)])
      (and (eq? (id1 #f) #f) (eq? (id2 #f) 0) (eq? (id3 0) #f)))
   (let ()
     (define $string->bytevector
       (lambda (s)
         (let ([n (string-length s)])
           (let ([bv (make-bytevector (+ n 1))])
             (do ([i 0 (fx+ i 1)])
                 ((fx= i n))
               (bytevector-u8-set! bv i (char->integer (string-ref s i))))
             (bytevector-u8-set! bv n 0)
             bv))))
     (let ([s "now is the time for all good men"]
           [r "                                "])
       (let ([bv ($string->bytevector r)])
         ((foreign-procedure (if (windows?) "windows_strcpy" "strcpy") (u8* string) void) bv s)
         (= 0 ((foreign-procedure (if (windows?) "windows_strcmp" "strcmp") (u8* string) integer-32) bv s)))))
   (error? ((foreign-procedure "id" (string) void) 'foo))

   (= ((foreign-procedure "idid" (integer-32) integer-32) #xc7c7c7) #xc7c7c7)
   (= ((foreign-procedure "ididid" (integer-32) integer-32) #x7c7c7c7c)
      #x7c7c7c7c)

   (= ((foreign-procedure "id" (unsigned-32) unsigned-32) #x80000000)
      #x80000000)
   (= ((foreign-procedure "id" (unsigned-32) integer-32) #x80000000)
      #x-80000000)

   (error? (foreign-procedure 'abcde (integer-32) integer-32))
   (let ([template
          (lambda (x)
             (foreign-procedure x (char) boolean))])
      (let ([id (template "id")]
            [idid (template "idid")]
            [ididid (template "ididid")])
         (and (eqv? (id #\nul) #f)
              (eqv? (idid #\001) #t)
              (eqv? (idid #\a) #t))))

   (= 0.0 ((foreign-procedure "float_id" (double-float) double-float) 0.0))
   (= 1.1 ((foreign-procedure "float_id" (double-float) double-float) 1.1))
   (error? ((foreign-procedure "float_id" (double-float) void) 0))

   (let ([fid (foreign-procedure "float_id" (double-float) double-float)])
     (let f ((n 10000))
       (or (= n 0)
           (let ([x (random 1.0)])
             (and (eqv? x (fid x))
                  (f (- n 1)))))))

   (= (+ (*  1 29) (*  2 31) (*  3 37) (*  5 41) (*  7 43)
         (* 11 47) (* 13 49) (* 17 53) (* 19 59) (* 23 61))
      ((foreign-procedure "testten"
          (integer-32 integer-32 integer-32 integer-32 integer-32
           integer-32 integer-32 integer-32 integer-32 integer-32)
          integer-32)
       29 31 37 41 43 47 49 53 59 61))

   (= (+ 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8)
      ((foreign-procedure "flsum8"
          (double-float double-float double-float double-float
           double-float double-float double-float double-float)
          double-float)
       1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8))

   (= (+ 1 2 3 4 5 6.75 7 8.5)
      ((foreign-procedure "sparcfltest"
          (integer-32 integer-32 integer-32 integer-32
           integer-32 double-float integer-32 double-float)
          double-float)
       1 2 3 4 5 6.75 7 8.5))

   (= (+ 1 2 3.3)
      ((foreign-procedure "mipsfltest1"
          (integer-32 integer-32 double-float)
          double-float)
       1 2 3.3))

   (= (+ 1 2.2 3.3)
      ((foreign-procedure "mipsfltest2"
          (integer-32 double-float double-float)
          double-float)
       1 2.2 3.3))

   (= (+ 1 2.25 3 4.5 5 6.75 7 8.25 9.5 10.75 11.25 12.5 13.75 14.25 15.5
       16.75 17.25 18.75 19.25)
      ((foreign-procedure "ppcfltest"
          (integer-32 double-float integer-32 double-float integer-32
           double-float integer-32 double-float double-float double-float
           double-float double-float double-float double-float double-float
           double-float double-float double-float double-float)
          double-float)
       1 2.25 3 4.5 5 6.75 7 8.25 9.5 10.75 11.25 12.5 13.75 14.25 15.5
       16.75 17.25 18.75 19.25))

   (= (+ 1 2.25 3 4.5 5
         (expt 2 36) 6.75 7 8.25
         (expt 2 39) 75
         9.5 10.75 11.25 12.5
         13.75 14.25 15.5
         20 16.75 21 (expt 2 37) 18.75 22
         19.25)
      ((foreign-procedure "ppcfltest2"
         (integer-32 double-float integer-32 double-float integer-32
          integer-64 double-float integer-32 double-float
          ; next integer should be stack-allocated with the PPC ABI
          integer-64 integer-32
          ; but next four floats should still get registers
          double-float double-float double-float double-float
          ; and remaining floags and ints should go on the stack
          double-float single-float double-float
          integer-32 double-float integer-32 integer-64 double-float integer-32
          double-float)
         double-float)
        1 2.25 3 4.5 5
        (expt 2 36) 6.75 7 8.25
        (expt 2 39) 75
        9.5 10.75 11.25 12.5
        13.75 14.25 15.5
        20 16.75 21 (expt 2 37) 18.75 22
        19.25))

   ((foreign-procedure "chk_data" () boolean))
   ((foreign-procedure "chk_bss" () boolean))
   ((foreign-procedure "chk_malloc" () boolean))

   (begin
     (define $fp-tlv (foreign-procedure "(cs)s_tlv" (ptr) ptr))
     (define $fp-stlv! (foreign-procedure "(cs)s_stlv" (ptr ptr) void))
     #t)

   (equal?
     (let ()
       (define-syntax list-in-order
         (syntax-rules ()
           [(_) '()]
           [(_ e . rest) (let ([t e]) (cons t (list-in-order . rest)))]))
       (list-in-order
         ($fp-tlv 'cons)
         ($fp-stlv! '$fp-spam 'yum)
         ($fp-tlv '$fp-spam)
         (top-level-value '$fp-spam)))
     `(,cons ,(void) yum yum))

   (equal?
     (let ()
       (define-syntax list-in-order
         (syntax-rules ()
           [(_) '()]
           [(_ e . rest) (let ([t e]) (cons t (list-in-order . rest)))]))
       (parameterize ([interaction-environment (copy-environment (scheme-environment))])
         (list-in-order
           (define-top-level-value 'foo 17)
           ($fp-tlv 'foo)
           ($fp-stlv! 'bar 55)
           ($fp-tlv 'bar)
           (top-level-value 'bar))))
     `(,(void) 17 ,(void) 55 55))

  (equal?
    (parameterize ([interaction-environment (copy-environment (scheme-environment))])
     ; should have no effect
      ($fp-stlv! cons 3)
      (list
        (#%$tc-field 'disable-count (#%$tc))
        cons
        ($fp-tlv 'cons)))
    `(0 ,cons ,cons))

  (equal?
    (parameterize ([interaction-environment (copy-environment (scheme-environment))])
     ; should have no effect
      ($fp-stlv! 'let 3)
      (list
        (#%$tc-field 'disable-count (#%$tc))
        (eval '(let ((x 23)) x))))
    '(0 23))

  (equal?
    (let ([x ($fp-tlv '$fp-i-am-not-bound)])
      (list (#%$tc-field 'disable-count (#%$tc)) x))
    `(0 ,(#%$unbound-object)))

  (equal?
    (let ([x ($fp-tlv 'let)])
      (list (#%$tc-field 'disable-count (#%$tc)) x))
    `(0 ,(#%$unbound-object)))

  (equal? ((foreign-procedure "(cs)s_test_schlib" () void)) (void))

  (begin
    (define $siv (foreign-procedure "(cs)Sinteger_value" (ptr) void))
    (define $si32v (foreign-procedure "(cs)Sinteger32_value" (ptr) void))
    (define $si64v (foreign-procedure "(cs)Sinteger64_value" (ptr) void))
    (define ($check p n)
      (or (= (optimize-level) 3)
          (guard (c [(and (assertion-violation? c)
                          (irritants-condition? c)
                          (equal? (condition-irritants c) (list n)))
                     #t])
            (p n)
            #f)))
    #t)

 ; make sure no errors for in-range inputs
  (begin
    ($si32v (- (expt 2 32) 1))
    ($si32v (- (expt 2 31)))
    ($si64v (- (expt 2 64) 1))
    ($si64v (- (expt 2 63)))
    (if (< (fixnum-width) 32)
        (begin ; assume 32-bit words
          ($siv (- (expt 2 32) 1))
          ($siv (- (expt 2 31))))
        (begin ; assume 64-bit words
          ($siv (- (expt 2 64) 1))
          ($siv (- (expt 2 63)))))
    #t)

 ; check barely out-of-range inputs
  ($check $si32v (expt 2 32))
  ($check $si32v (- -1 (expt 2 31)))
  ($check $si64v (expt 2 64))
  ($check $si64v (- -1 (expt 2 63)))
  ($check $siv (expt 2 (if (< (fixnum-width) 32) 32 64)))
  ($check $siv (- -1 (expt 2 (if (< (fixnum-width) 32) 31 63))))

 ; check further out-of-range inputs
  ($check $si32v (expt 2 36))
  ($check $si32v (- -1 (expt 2 35)))
  ($check $si64v (expt 2 68))
  ($check $si64v (- -1 (expt 2 67)))
  ($check $siv (expt 2 (if (< (fixnum-width) 32) 36 68)))
  ($check $siv (- -1 (expt 2 (if (< (fixnum-width) 32) 35 67))))
  ($check $si32v (expt 2 100))
  ($check $si32v (- -1 (expt 2 100)))
  ($check $si64v (expt 2 100))
  ($check $si64v (- -1 (expt 2 100)))
  ($check $siv (expt 2 100))
  ($check $siv (- -1 (expt 2 100)))
)

(mat foreign-sizeof
  (equal?
    (list
      (foreign-sizeof 'integer-8)
      (foreign-sizeof 'unsigned-8)
      (foreign-sizeof 'integer-16)
      (foreign-sizeof 'unsigned-16)
      (foreign-sizeof 'integer-24)
      (foreign-sizeof 'unsigned-24)
      (foreign-sizeof 'integer-32)
      (foreign-sizeof 'unsigned-32)
      (foreign-sizeof 'integer-40)
      (foreign-sizeof 'unsigned-40)
      (foreign-sizeof 'integer-48)
      (foreign-sizeof 'unsigned-48)
      (foreign-sizeof 'integer-56)
      (foreign-sizeof 'unsigned-56)
      (foreign-sizeof 'integer-64)
      (foreign-sizeof 'unsigned-64)
      (foreign-sizeof 'single-float)
      (foreign-sizeof 'double-float))
    '(1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 4 8))
  ((foreign-procedure "check_types" (int int int int int int int int int) boolean)
   (foreign-sizeof 'char)
   (foreign-sizeof 'wchar)
   (foreign-sizeof 'short)
   (foreign-sizeof 'int)
   (foreign-sizeof 'long)
   (foreign-sizeof 'long-long)
   (foreign-sizeof 'float)
   (foreign-sizeof 'double)
   (foreign-sizeof 'void*))
  (equal? (foreign-sizeof 'unsigned) (foreign-sizeof 'int))
  (equal? (foreign-sizeof 'unsigned-int) (foreign-sizeof 'int))
  (equal? (foreign-sizeof 'unsigned-short) (foreign-sizeof 'short))
  (equal? (foreign-sizeof 'unsigned-long) (foreign-sizeof 'long))
  (equal? (foreign-sizeof 'unsigned-long-long) (foreign-sizeof 'long-long))
  (equal? (foreign-sizeof 'boolean) (foreign-sizeof 'int))
  (equal? (foreign-sizeof 'fixnum) (foreign-sizeof 'iptr))
  (equal? (foreign-sizeof 'scheme-object) (foreign-sizeof 'void*))
  (equal? (foreign-sizeof 'ptr) (foreign-sizeof 'void*))
  (equal? (foreign-sizeof 'iptr) (foreign-sizeof 'void*))
  (equal? (foreign-sizeof 'uptr) (foreign-sizeof 'void*))
  (error? (foreign-sizeof))
  (error? (foreign-sizeof 'int 'int))
  (error? (foreign-sizeof 'i-am-not-a-type))
  (error? (foreign-sizeof '1))
)

(mat foreign-bytevectors
 ; test u8*, u16*, u32*
  (begin
    (define u8*->u8* (foreign-procedure "u8_star_to_u8_star" (u8*) u8*))
    (define u16*->u16* (foreign-procedure "u16_star_to_u16_star" (u16*) u16*))
    (define u32*->u32* (foreign-procedure "u32_star_to_u32_star" (u32*) u32*))
    #t)
  (equal? (u8*->u8* #vu8(1 2 3 4 0)) #vu8(2 3 4))
  (equal? (u16*->u16* #vu8(1 2 3 4 5 6 7 8 0 0)) #vu8(3 4 5 6 7 8))
  (equal? (u32*->u32* #vu8(1 2 3 4 5 6 7 8 9 10 11 12 0 0 0 0)) #vu8(5 6 7 8 9 10 11 12))

  (eq? (u8*->u8* #vu8(1 0)) #vu8())
  (eq? (u16*->u16* #vu8(1 2 0 0)) #vu8())
  (eq? (u32*->u32* #vu8(1 2 3 4 0 0 0 0)) #vu8())

  (eq? (u8*->u8* #f) #f)
  (eq? (u16*->u16* #f) #f)
  (eq? (u32*->u32* #f) #f)

  (error? (u8*->u8* "hello"))
  (error? (u16*->u16* "hello"))
  (error? (u32*->u32* "hello"))
  (error? (u8*->u8* 0))
  (error? (u16*->u16* 0))
  (error? (u32*->u32* 0))

  (begin
    (define call-u8* (foreign-procedure "call_u8_star" (ptr u8*) u8*))
    (define call-u16* (foreign-procedure "call_u16_star" (ptr u16*) u16*))
    (define call-u32* (foreign-procedure "call_u32_star" (ptr u32*) u32*))
    (define $bytevector-map
      (lambda (p bv)
        (u8-list->bytevector (map p (bytevector->u8-list bv)))))
    #t)
  (equal?
    (call-u8* (foreign-callable
                (lambda (x) ($bytevector-map (lambda (x) (if (= x 255) 0 (+ x 100))) x))
                (u8*) u8*)
      #vu8(1 2 3 4 5 255 0 ))
    '#vu8(103 104 105))
  (equal?
    (call-u16* (foreign-callable
                 (lambda (x) ($bytevector-map (lambda (x) (if (= x 255) 0 (+ x 100))) x))
                 (u16*) u16*)
      #vu8(1 2 3 4 5 6 255 255 0 0))
    '#vu8(105 106))
  (equal?
    (call-u32* (foreign-callable
                 (lambda (x) ($bytevector-map (lambda (x) (if (= x 255) 0 (+ x 100))) x))
                 (u32*) u32*)
      #vu8(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 255 255 255 255 0 0 0 0))
    '#vu8(109 110 111 112 113 114 115 116 117 118 119 120))
  (error?
    (let ([frotz (foreign-callable
                   (lambda (x) (list x (bytevector-length x)))
                   (u8*) u8*)])
      (call-u8* frotz #vu8(1 2 3 4 5 0))))
  (error?
    (call-u16* (foreign-callable
                 (lambda (x) (list x (bytevector-length x)))
                 (u16*) u16*)
      #vu8(1 2 3 4 5 6 0 0)))
  (error?
    (call-u32* (foreign-callable
                 (lambda (x) (list x (bytevector-length x)))
                 (u32*) u32*)
      #vu8(1 2 3 4 5 6 7 8 0 0 0 0)))
  (error?
    (call-u8* (foreign-callable
                (lambda (x) (list x (bytevector-length x)))
                (u8*) u8*)
      '#(1 2 3 4 5 0)))
  (error?
    (call-u16* (foreign-callable
                 (lambda (x) (list x (bytevector-length x)))
                 (u16*) u16*)
      '#(1 2 3 4 5 6 0 0)))
  (error?
    (call-u32* (foreign-callable
                 (lambda (x) (list x (bytevector-length x)))
                 (u32*) u32*)
      '#(1 2 3 4 5 6 7 8 0 0 0 0)))
)

(mat foreign-strings
 ; test utf-8, utf-16le, utf-16be, utf-32le, utf-32be, string, wstring
  (begin
    (define utf-8->utf-8 (foreign-procedure "u8_star_to_u8_star" (utf-8) utf-8))
    (define utf-16le->utf-16le (foreign-procedure "u16_star_to_u16_star" (utf-16le) utf-16le))
    (define utf-16be->utf-16be (foreign-procedure "u16_star_to_u16_star" (utf-16be) utf-16be))
    (define utf-32le->utf-32le (foreign-procedure "u32_star_to_u32_star" (utf-32le) utf-32le))
    (define utf-32be->utf-32be (foreign-procedure "u32_star_to_u32_star" (utf-32be) utf-32be))
    (define string->string (foreign-procedure "char_star_to_char_star" (string) string))
    (define wstring->wstring (foreign-procedure "wchar_star_to_wchar_star" (wstring) wstring))
    #t)
  (equal? (utf-8->utf-8 "hello") "ello")
  (equal? (utf-16le->utf-16le "hello") "ello")
  (equal? (utf-16be->utf-16be "hello") "ello")
  (equal? (utf-32le->utf-32le "hello") "ello")
  (equal? (utf-32be->utf-32be "hello") "ello")
  (equal? (string->string "hello") "ello")
  (equal? (wstring->wstring "hello") "ello")

  (eq? (utf-8->utf-8 "h") "")
  (eq? (utf-16le->utf-16le "h") "")
  (eq? (utf-16be->utf-16be "h") "")
  (eq? (utf-32le->utf-32le "h") "")
  (eq? (utf-32be->utf-32be "h") "")
  (eq? (string->string "h") "")
  (eq? (wstring->wstring "h") "")

  (eq? (utf-8->utf-8 #f) #f)
  (eq? (utf-16le->utf-16le #f) #f)
  (eq? (utf-16be->utf-16be #f) #f)
  (eq? (utf-32le->utf-32le #f) #f)
  (eq? (utf-32be->utf-32be #f) #f)
  (eq? (string->string #f) #f)
  (eq? (wstring->wstring #f) #f)

  (error? (utf-8->utf-8 #vu8(1 2 3 4 0 0 0 0)))
  (error? (utf-16le->utf-16le #vu8(1 2 3 4 0 0 0 0)))
  (error? (utf-16be->utf-16be #vu8(1 2 3 4 0 0 0 0)))
  (error? (utf-32le->utf-32le #vu8(1 2 3 4 0 0 0 0)))
  (error? (utf-32be->utf-32be #vu8(1 2 3 4 0 0 0 0)))
  (error? (string->string #vu8(1 2 3 4 0 0 0 0)))
  (error? (wstring->wstring #vu8(1 2 3 4 0 0 0 0)))

  (error? (utf-8->utf-8 0))
  (error? (utf-16le->utf-16le 0))
  (error? (utf-16be->utf-16be 0))
  (error? (utf-32le->utf-32le 0))
  (error? (utf-32be->utf-32be 0))
  (error? (string->string 0))
  (error? (wstring->wstring 0))

  (begin
    (define call-utf-8 (foreign-procedure "call_u8_star" (ptr utf-8) utf-8))
    (define call-utf-16le (foreign-procedure "call_u16_star" (ptr utf-16le) utf-16le))
    (define call-utf-16be (foreign-procedure "call_u16_star" (ptr utf-16be) utf-16be))
    (define call-utf-32le (foreign-procedure "call_u32_star" (ptr utf-32le) utf-32le))
    (define call-utf-32be (foreign-procedure "call_u32_star" (ptr utf-32be) utf-32be))
    (define call-string (foreign-procedure "call_string" (ptr string) string))
    (define call-wstring (foreign-procedure "call_wstring" (ptr wstring) wstring))
    #t)
  (equal?
    (call-utf-8 (foreign-callable
                  (lambda (x) (string-append x "$q"))
                  (utf-8) utf-8)
      "hello")
    "llo$q")
  (equal?
    (call-utf-16le (foreign-callable
                     (lambda (x) (string-append x "$q"))
                     (utf-16le) utf-16le)
      "hello")
    "llo$q")
  (equal?
    (call-utf-16be (foreign-callable
                     (lambda (x) (string-append x "$q"))
                     (utf-16be) utf-16be)
      "hello")
    "llo$q")
  (equal?
    (call-utf-32le (foreign-callable
                     (lambda (x) (string-append x "$q"))
                     (utf-32le) utf-32le)
      "hello")
    "llo$q")
  (equal?
    (call-utf-32be (foreign-callable
                     (lambda (x) (string-append x "$q"))
                     (utf-32be) utf-32be)
      "hello")
    "llo$q")
  (equal?
    (call-string (foreign-callable
                   (lambda (x) (string-append x "$q"))
                   (string) string)
      "hello")
    "llo$q")
  (equal?
    (call-wstring (foreign-callable
                    (lambda (x) (string-append x "$q"))
                    (wstring) wstring)
      "hello")
    "llo$q")
  (error?
    (call-utf-8 (foreign-callable
                  (lambda (x) (list x (string-length x)))
                  (utf-8) utf-8)
      "hello"))
  (error?
    (call-utf-16le (foreign-callable
                     (lambda (x) (list x (string-length x)))
                     (utf-16le) utf-16le)
      "hello"))
  (error?
    (call-utf-16be (foreign-callable
                     (lambda (x) (list x (string-length x)))
                     (utf-16be) utf-16be)
      "hello"))
  (error?
    (call-utf-32le (foreign-callable
                     (lambda (x) (list x (string-length x)))
                     (utf-32le) utf-32le)
      "hello"))
  (error?
    (call-utf-32be (foreign-callable
                     (lambda (x) (list x (string-length x)))
                     (utf-32be) utf-32be)
      "hello"))
  (error?
    (call-string (foreign-callable
                   (lambda (x) (list x (string-length x)))
                   (string) string)
      "hello"))
  (error?
    (call-wstring (foreign-callable
                    (lambda (x) (list x (string-length x)))
                    (wstring) wstring)
      "hello"))
)

(mat foreign-fixed-types
 ; test {integer,unsigned}-8, {single,double}-float
  (begin
    (define i8-to-i8 (foreign-procedure "i8_to_i8" (integer-8 int) integer-8))
    (define u8-to-u8 (foreign-procedure "u8_to_u8" (unsigned-8 int) unsigned-8))
    (define i16-to-i16 (foreign-procedure "i16_to_i16" (integer-16 int) integer-16))
    (define u16-to-u16 (foreign-procedure "u16_to_u16" (unsigned-16 int) unsigned-16))
    (define i24-to-i24 (foreign-procedure "i32_to_i32" (integer-24 int) integer-24))
    (define u24-to-u24 (foreign-procedure "u32_to_u32" (unsigned-24 int) unsigned-24))
    (define i32-to-i32 (foreign-procedure "i32_to_i32" (integer-32 int) integer-32))
    (define u32-to-u32 (foreign-procedure "u32_to_u32" (unsigned-32 int) unsigned-32))
    (define i40-to-i40 (foreign-procedure "i64_to_i64" (integer-40 int) integer-40))
    (define u40-to-u40 (foreign-procedure "u64_to_u64" (unsigned-40 int) unsigned-40))
    (define i48-to-i48 (foreign-procedure "i64_to_i64" (integer-48 int) integer-48))
    (define u48-to-u48 (foreign-procedure "u64_to_u64" (unsigned-48 int) unsigned-48))
    (define i56-to-i56 (foreign-procedure "i64_to_i64" (integer-56 int) integer-56))
    (define u56-to-u56 (foreign-procedure "u64_to_u64" (unsigned-56 int) unsigned-56))
    (define i64-to-i64 (foreign-procedure "i64_to_i64" (integer-64 int) integer-64))
    (define u64-to-u64 (foreign-procedure "u64_to_u64" (unsigned-64 int) unsigned-64))
    (define sf-to-sf (foreign-procedure "sf_to_sf" (single-float) single-float))
    (define df-to-df (foreign-procedure "df_to_df" (double-float) double-float))
    (define $test-int-to-int
      (lambda (fp size signed?)
        (define n10000 (expt 256 size))
        (define nffff (- n10000 1))
        (define nfffe (- nffff 1))
        (define n8000 (ash n10000 -1))
        (define n8001 (+ n8000 1))
        (define n7fff (- n8000 1))
        (define n7ffe (- n7fff 1))
        (define n100 (expt 16 size))
        (define n101 (+ n100 1))
        (define nff (- n100 1))
        (define nfe (- nff 1))
        (define n80 (ash n100 -1))
        (define n81 (+ n80 1))
        (define n7f (- n80 1))
        (define n7e (- n7f 1))
        (define (expect x k)
          (if signed?
              (if (<= (- n8000) x nffff)
                  (mod0 (+ x k) n10000)
                  'err)
              (if (<= (- n8000) x nffff)
                  (mod (+ x k) n10000)
                  'err)))
        (define (check x)
          (define (do-one x k)
            (let ([a (expect x k)])
              (if (eq? a 'err)
                  (or (= (optimize-level) 3)
                      (guard (c [#t (display-condition c) (newline) #t])
                        (fp x k)
                        (printf "no error for x = ~x, k = ~d\n" x k)
                        #f))
                  (or (eqv? (fp x k) a)
                      (begin
                        (printf "incorrect answer ~x should be ~x for x = ~x, k = ~d\n" (fp x k) a x k)
                        #f)))))
          (list
            (do-one x 1)
            (do-one x -1)
            (do-one (- x) 1)
            (do-one (- x) -1)))
        (andmap
          (lambda (x) (and (list? x) (= (length x) 4) (andmap (lambda (x) (eq? x #t)) x)))
          (list
            (check n10000)
            (check nffff)
            (check nfffe)
            (check n8001)
            (check n8000)
            (check n7fff)
            (check n7ffe)
            (check n101)
            (check n100)
            (check nff)
            (check nfe)
            (check n81)
            (check n80)
            (check n7f)
            (check n7e)
            (check 73)
            (check 5)
            (check 1)
            (check 0)))))
    #t)
  ($test-int-to-int i8-to-i8 1 #t)
  ($test-int-to-int u8-to-u8 1 #f)
  ($test-int-to-int i16-to-i16 2 #t)
  ($test-int-to-int u16-to-u16 2 #f)
  ($test-int-to-int i24-to-i24 3 #t)
  ($test-int-to-int u24-to-u24 3 #f)
  ($test-int-to-int i32-to-i32 4 #t)
  ($test-int-to-int u32-to-u32 4 #f)
  ($test-int-to-int i40-to-i40 5 #t)
  ($test-int-to-int u40-to-u40 5 #f)
  ($test-int-to-int i48-to-i48 6 #t)
  ($test-int-to-int u48-to-u48 6 #f)
  ($test-int-to-int i56-to-i56 7 #t)
  ($test-int-to-int u56-to-u56 7 #f)
  ($test-int-to-int i64-to-i64 8 #t)
  ($test-int-to-int u64-to-u64 8 #f)
  (eqv? (sf-to-sf 73.5) 74.5)
  (eqv? (df-to-df 73.5) 74.5)

  (error? (i8-to-i8 'qqq 0))
  (error? (u8-to-u8 'qqq 0))
  (error? (i16-to-i16 'qqq 0))
  (error? (u16-to-u16 'qqq 0))
  (error? (i24-to-i24 'qqq 0))
  (error? (u24-to-u24 'qqq 0))
  (error? (i32-to-i32 'qqq 0))
  (error? (u32-to-u32 'qqq 0))
  (error? (i64-to-i64 'qqq 0))
  (error? (u64-to-u64 'qqq 0))
  (error? (i8-to-i8 0 "oops"))
  (error? (u8-to-u8 0 "oops"))
  (error? (i16-to-i16 0 "oops"))
  (error? (u16-to-u16 0 "oops"))
  (error? (i32-to-i32 0 "oops"))
  (error? (u32-to-u32 0 "oops"))
  (error? (i64-to-i64 0 "oops"))
  (error? (u64-to-u64 0 "oops"))

  (error? (sf-to-sf 'qqq))
  (error? (df-to-df 'qqq))

  (begin
    (define call-i8 (foreign-procedure "call_i8" (ptr integer-8 int int) integer-8))
    (define call-u8 (foreign-procedure "call_u8" (ptr unsigned-8 int int) unsigned-8))
    (define call-i16 (foreign-procedure "call_i16" (ptr integer-16 int int) integer-16))
    (define call-u16 (foreign-procedure "call_u16" (ptr unsigned-16 int int) unsigned-16))
    (define call-i24 (foreign-procedure "call_i32" (ptr integer-24 int int) integer-24))
    (define call-u24 (foreign-procedure "call_u32" (ptr unsigned-24 int int) unsigned-24))
    (define call-i32 (foreign-procedure "call_i32" (ptr integer-32 int int) integer-32))
    (define call-u32 (foreign-procedure "call_u32" (ptr unsigned-32 int int) unsigned-32))
    (define call-i40 (foreign-procedure "call_i64" (ptr integer-40 int int) integer-40))
    (define call-u40 (foreign-procedure "call_u64" (ptr unsigned-40 int int) unsigned-40))
    (define call-i48 (foreign-procedure "call_i64" (ptr integer-48 int int) integer-48))
    (define call-u48 (foreign-procedure "call_u64" (ptr unsigned-48 int int) unsigned-48))
    (define call-i56 (foreign-procedure "call_i64" (ptr integer-56 int int) integer-56))
    (define call-u56 (foreign-procedure "call_u64" (ptr unsigned-56 int int) unsigned-56))
    (define call-i64 (foreign-procedure "call_i64" (ptr integer-64 int int) integer-64))
    (define call-u64 (foreign-procedure "call_u64" (ptr unsigned-64 int int) unsigned-64))
    (define call-sf (foreign-procedure "call_sf" (ptr single-float int int) single-float))
    (define call-df (foreign-procedure "call_df" (ptr double-float int int) double-float))
    (define ($test-call-int signed? size call-int make-fc)
      (define n10000 (expt 256 size))
      (define nffff (- n10000 1))
      (define nfffe (- nffff 1))
      (define n8000 (ash n10000 -1))
      (define n8001 (+ n8000 1))
      (define n7fff (- n8000 1))
      (define n7ffe (- n7fff 1))
      (define n100 (expt 16 size))
      (define n101 (+ n100 1))
      (define nff (- n100 1))
      (define nfe (- nff 1))
      (define n80 (ash n100 -1))
      (define n81 (+ n80 1))
      (define n7f (- n80 1))
      (define n7e (- n7f 1))
      (define (expect x m k)
        (if signed?
            (if (<= (- n8000) x nffff)
                (mod0 (+ x m k) n10000)
                'err)
            (if (<= (- n8000) x nffff)
                (mod (+ x m k) n10000)
                'err)))
      (define fc (make-fc values))
      (define fp (lambda (x m k) (call-int fc x m k)))
      (define (check x)
        (define (do-one x m k)
          (let ([a (expect x m k)])
            (if (eq? a 'err)
                (or (= (optimize-level) 3)
                    (guard (c [#t (display-condition c) (newline) #t]) (fp x m k)))
                (eqv? (fp x m k) a))))
        (list
          (do-one x 0 0)
          (do-one x 5 7)
          (do-one x -5 7)
          (do-one x 5 -7)
          (do-one x -5 -7)
          (do-one (- x) 0 0)
          (do-one (- x) 5 7)
          (do-one (- x) -5 7)
          (do-one (- x) 5 -7)
          (do-one (- x) -5 -7)))
      (andmap
        (lambda (x) (and (list? x) (= (length x) 10) (andmap (lambda (x) (eq? x #t)) x)))
        (list
          (check n10000)
          (check nffff)
          (check nfffe)
          (check n8001)
          (check n8000)
          (check n7fff)
          (check n7ffe)
          (check n101)
          (check n100)
          (check nff)
          (check nfe)
          (check n81)
          (check n80)
          (check n7f)
          (check n7e)
          (check 73)
          (check 5)
          (check 1)
          (check 0))))
    #t)
  ($test-call-int #t (foreign-sizeof 'integer-8) call-i8
    (lambda (p) (foreign-callable p (integer-8) integer-8)))
  ($test-call-int #t (foreign-sizeof 'integer-16) call-i16
    (lambda (p) (foreign-callable p (integer-16) integer-16)))
  ($test-call-int #t (foreign-sizeof 'integer-24) call-i24
    (lambda (p) (foreign-callable p (integer-24) integer-24)))
  ($test-call-int #t (foreign-sizeof 'integer-32) call-i32
    (lambda (p) (foreign-callable p (integer-32) integer-32)))
  ($test-call-int #t (foreign-sizeof 'integer-40) call-i40
    (lambda (p) (foreign-callable p (integer-40) integer-40)))
  ($test-call-int #t (foreign-sizeof 'integer-48) call-i48
    (lambda (p) (foreign-callable p (integer-48) integer-48)))
  ($test-call-int #t (foreign-sizeof 'integer-56) call-i56
    (lambda (p) (foreign-callable p (integer-56) integer-56)))
  ($test-call-int #t (foreign-sizeof 'integer-64) call-i64
     (lambda (p) (foreign-callable p (integer-64) integer-64)))
  ($test-call-int #f (foreign-sizeof 'unsigned-8) call-u8
    (lambda (p) (foreign-callable p (unsigned-8) unsigned-8)))
  ($test-call-int #f (foreign-sizeof 'unsigned-16) call-u16
    (lambda (p) (foreign-callable p (unsigned-16) unsigned-16)))
  ($test-call-int #f (foreign-sizeof 'unsigned-24) call-u24
    (lambda (p) (foreign-callable p (unsigned-24) unsigned-24)))
  ($test-call-int #f (foreign-sizeof 'unsigned-32) call-u32
    (lambda (p) (foreign-callable p (unsigned-32) unsigned-32)))
  ($test-call-int #f (foreign-sizeof 'unsigned-40) call-u40
    (lambda (p) (foreign-callable p (unsigned-40) unsigned-40)))
  ($test-call-int #f (foreign-sizeof 'unsigned-48) call-u48
    (lambda (p) (foreign-callable p (unsigned-48) unsigned-48)))
  ($test-call-int #f (foreign-sizeof 'unsigned-56) call-u56
    (lambda (p) (foreign-callable p (unsigned-56) unsigned-56)))
  ($test-call-int #f (foreign-sizeof 'unsigned-64) call-u64
    (lambda (p) (foreign-callable p (unsigned-64) unsigned-64)))
  (equal?
    (call-sf
      (foreign-callable
        (lambda (x) (+ x 5))
        (single-float) single-float)
      73.25 7 23)
    108.25)
  (equal?
    (call-df
      (foreign-callable
        (lambda (x) (+ x 5))
        (double-float) double-float)
      73.25 7 23)
    108.25)

  (error?
    (call-i8
      (foreign-callable
        (lambda (x) '(- x 7))
        (integer-8) integer-8)
      73 0 0))
  (error?
    (call-u8
      (foreign-callable
        (lambda (x) '(- x 7))
        (unsigned-8) unsigned-8)
      73 0 0))
  (error?
    (call-i16
      (foreign-callable
        (lambda (x) '(- x 7))
        (integer-16) integer-16)
      73 0 0))
  (error?
    (call-u16
      (foreign-callable
        (lambda (x) '(- x 7))
        (unsigned-16) unsigned-16)
      73 0 0))
  (error?
    (call-i32
      (foreign-callable
        (lambda (x) '(- x 7))
        (integer-32) integer-32)
      73 0 0))
  (error?
    (call-u32
      (foreign-callable
        (lambda (x) '(- x 7))
        (unsigned-32) unsigned-32)
      73 0 0))
  (error?
    (call-i64
      (foreign-callable
        (lambda (x) '(- x 7))
        (integer-64) integer-64)
      73 0 0))
  (error?
    (call-u64
      (foreign-callable
        (lambda (x) '(- x 7))
        (unsigned-64) unsigned-64)
      73 0 0))
  (error?
    (call-sf
      (foreign-callable
        (lambda (x) '(- x 7))
        (single-float) single-float)
      73.25 0 0))
  (error?
    (call-df
      (foreign-callable
        (lambda (x) '(- x 7))
        (double-float) double-float)
      73.25 0 0))

  (begin
    (define u32xu32->u64
      (foreign-procedure "u32xu32_to_u64" (unsigned-32 unsigned-32)
        unsigned-64))
    (define i32xu32->i64
      (foreign-procedure "i32xu32_to_i64" (integer-32 unsigned-32)
        integer-64))
    (define call-i32xu32->i64
      (foreign-procedure "call_i32xu32_to_i64"
        (ptr integer-32 unsigned-32 int)
        integer-64))
    (define fc-i32xu32->i64
      (foreign-callable i32xu32->i64
        (integer-32 unsigned-32)
        integer-64))
    #t)

  (eqv? (u32xu32->u64 #xFFFFFFFF #xFFFFFFFF) #xFFFFFFFFFFFFFFFF)
  (eqv? (u32xu32->u64 #xFF3FFFFF #xFFFFF0FF) #xFF3FFFFFFFFFF0FF)
  (eqv? (u32xu32->u64 #xFFFFFFFF #xF0000000) #xFFFFFFFFF0000000)

  (eqv? (i32xu32->i64 #x0 #x5) #x5)
  (eqv? (i32xu32->i64 #x7 #x5) #x700000005)
  (eqv? (i32xu32->i64 #xFFFFFFFF #xFFFFFFFF) #x-1)
  (eqv? (fixnum? (i32xu32->i64 #xFFFFFFFF #xFFFFFFFF)) #t)
  (eqv? (i32xu32->i64 #xFFFFFFFF #xFFFFFFFE) #x-2)
  (eqv? (i32xu32->i64 #xFFFFFFFF #x00000000) #x-100000000)
  (eqv? (i32xu32->i64 #xFFFFFFFE #x00000000) #x-200000000)
  (eqv? (i32xu32->i64 #xFFFFFFFF #x00000001) #x-FFFFFFFF)
  (eqv? (i32xu32->i64 #x0 #xFFFFFFFF) #xFFFFFFFF)
  (eqv? (i32xu32->i64 #x7FFFFFFF #xFFFFFFFF) #x7FFFFFFFFFFFFFFF)
  (eqv? (i32xu32->i64 #x80000000 #x00000000) #x-8000000000000000)

  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #x0 #x5 #x13) #x18)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #x7 #x5 7) #x70000000C)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #xFFFFFFFF #xFFFFFFFF -3) #x-4)
  (eqv? (fixnum? (call-i32xu32->i64 fc-i32xu32->i64 #xFFFFFFFF #xFFFFFFFF 0)) #t)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #xFFFFFFFF #xFFFFFFFE -1) #x-3)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #xFFFFFFFF #x00000000 0) #x-100000000)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #xFFFFFFFE #x00000000 0) #x-200000000)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #xFFFFFFFF #x00000001 0) #x-FFFFFFFF)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #x0 #xFFFFFFFF 0) #xFFFFFFFF)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #x7FFFFFFF #xFFFFFFFF 0) #x7FFFFFFFFFFFFFFF)
  (eqv? (call-i32xu32->i64 fc-i32xu32->i64 #x80000000 #x00000000 0) #x-8000000000000000)

 ; check for 64-bit alignment issues
  (begin
    (define ufoo64a
      (foreign-procedure "ufoo64a" (unsigned-64 unsigned-64 unsigned-64 unsigned-64 unsigned-64 unsigned-64 unsigned-64)
        unsigned-64))
    (define ufoo64b
      (foreign-procedure "ufoo64b" (integer-32 unsigned-64 unsigned-64 unsigned-64 unsigned-64 unsigned-64 unsigned-64 unsigned-64)
        unsigned-64))
    (define test-ufoo
      (lambda (foo x a b c d e f g)
        (eqv? (foo x a b c d e f g)
              (mod (+ x (- a b) (- c d) (- e f) g) (expt 2 64)))))
    #t)
  (test-ufoo (lambda (x a b c d e f g) (+ x (ufoo64a a b c d e f g)))
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #x0700000000000080)
  (test-ufoo ufoo64b
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #x0700000000000080)
  (test-ufoo (lambda (x a b c d e f g) (+ x (ufoo64a a b c d e f g)))
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #xC700000000000080)
  (test-ufoo ufoo64b
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #xC700000000000080)
  (do ([i 1000 (fx- i 1)])
      ((fx= i 0) #t)
    (let ([ls (cons (random (expt 2 32))
                    (map random (make-list 7 (expt 2 64))))])
      (unless (apply test-ufoo
                (lambda (x a b c d e f g)
                  (+ x (ufoo64a a b c d e f g)))
                ls)
        (pretty-print ls)
        (errorf #f "failed for ufoo64a on ~s" ls))
      (unless (apply test-ufoo ufoo64b ls)
        (pretty-print ls)
        (errorf #f "failed for ufoo64b on ~s" ls))))
  (begin
    (define ifoo64a
      (foreign-procedure "ifoo64a" (integer-64 integer-64 integer-64 integer-64 integer-64 integer-64 integer-64)
        integer-64))
    (define ifoo64b
      (foreign-procedure "ifoo64b" (integer-32 integer-64 integer-64 integer-64 integer-64 integer-64 integer-64 integer-64)
        integer-64))
    (define test-ifoo
      (lambda (foo x a b c d e f g)
        (eqv? (foo x a b c d e f g)
              (mod0 (+ x (- a b) (- c d) (- e f) g) (expt 2 64)))))
    #t)
  (test-ifoo (lambda (x a b c d e f g) (+ x (ifoo64a a b c d e f g)))
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #x0700000000000080)
  (test-ifoo ifoo64b
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #x0700000000000080)
  (test-ifoo (lambda (x a b c d e f g) (+ x (ifoo64a a b c d e f g)))
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #xC700000000000080)
  (test-ifoo ifoo64b
    #x0000000010000000
    #x0000000120000000
    #x0000002003000000
    #x0000030000400000
    #x0000400000050000
    #x0005000000006000
    #x0060000000000700
    #xC700000000000080)
  (do ([i 1000 (fx- i 1)])
      ((fx= i 0) #t)
    (let ([ls (cons (- (random (expt 2 32)) (expt 2 31))
                    (map (lambda (n) (- (random n) (expt 2 31))) (make-list 7 (expt 2 64))))])
      (unless (apply test-ifoo
                (lambda (x a b c d e f g)
                  (+ x (ifoo64a a b c d e f g)))
                ls)
        (pretty-print ls)
        (errorf #f "failed for ifoo64a on ~s" ls))
      (unless (apply test-ifoo ifoo64b ls)
        (pretty-print ls)
        (errorf #f "failed for ifoo64b on ~s" ls))))
)

(mat foreign-C-types
 ; test void*, int, unsigned, float, etc.
  (begin
    (define int-to-int (foreign-procedure "int_to_int" (int int) int))
    (define unsigned-to-unsigned (foreign-procedure "unsigned_to_unsigned" (unsigned int) unsigned))
    (define unsigned-int-to-unsigned-int (foreign-procedure "unsigned_to_unsigned" (unsigned-int int) unsigned-int))
    (define char-to-char (foreign-procedure "char_to_char" (char) char))
    (define wchar-to-wchar (foreign-procedure "wchar_to_wchar" (wchar) wchar))
    (define short-to-short (foreign-procedure "short_to_short" (short int) short))
    (define unsigned-short-to-unsigned-short (foreign-procedure "unsigned_short_to_unsigned_short" (unsigned-short int) unsigned-short))
    (define long-to-long (foreign-procedure "long_to_long" (long int) long))
    (define unsigned-long-to-unsigned-long (foreign-procedure "unsigned_long_to_unsigned_long" (unsigned-long int) unsigned-long))
    (define long-long-to-long-long (foreign-procedure "long_long_to_long_long" (long-long int) long-long))
    (define unsigned-long-long-to-unsigned-long-long (foreign-procedure "unsigned_long_long_to_unsigned_long_long" (unsigned-long-long int) unsigned-long-long))
    (define float-to-float (foreign-procedure "float_to_float" (float) float))
    (define double-to-double (foreign-procedure "double_to_double" (double) double))
    (define iptr-to-iptr (foreign-procedure "iptr_to_iptr" (iptr int) iptr))
    (define uptr-to-uptr (foreign-procedure "uptr_to_uptr" (uptr int) uptr))
    (define void*-to-void* (foreign-procedure "uptr_to_uptr" (void* int) void*))
    #t)
  ($test-int-to-int int-to-int (foreign-sizeof 'int) #t)
  ($test-int-to-int unsigned-to-unsigned (foreign-sizeof 'unsigned) #f)
  ($test-int-to-int unsigned-int-to-unsigned-int (foreign-sizeof 'unsigned-int) #f)
  ($test-int-to-int short-to-short (foreign-sizeof 'short) #t)
  ($test-int-to-int unsigned-short-to-unsigned-short (foreign-sizeof 'unsigned-short) #f)
  ($test-int-to-int long-to-long (foreign-sizeof 'long) #t)
  ($test-int-to-int unsigned-long-to-unsigned-long (foreign-sizeof 'unsigned-long) #f)
  ($test-int-to-int long-long-to-long-long (foreign-sizeof 'long-long) #t)
  ($test-int-to-int unsigned-long-long-to-unsigned-long-long (foreign-sizeof 'unsigned-long-long) #f)
  ($test-int-to-int iptr-to-iptr (foreign-sizeof 'iptr) #t)
  ($test-int-to-int uptr-to-uptr (foreign-sizeof 'uptr) #f)
  ($test-int-to-int void*-to-void* (foreign-sizeof 'void*) #f)

  (eqv? (char-to-char #\a) #\A)
  (eqv? (wchar-to-wchar #\x3bb) #\x39b)
  (eqv? (float-to-float 73.5) 74.5)
  (eqv? (double-to-double 73.5) 74.5)

  (error? (int-to-int 'qqq 0))
  (error? (unsigned-to-unsigned 'qqq 0))
  (error? (unsigned-int-to-unsigned-int 'qqq 0))
  (error? (unsigned-short-to-unsigned-short 'qqq 0))
  (error? (short-to-short 'qqq 0))
  (error? (long-to-long 'qqq 0))
  (error? (unsigned-long-to-unsigned-long 'qqq 0))
  (error? (long-long-to-long-long 'qqq 0))
  (error? (unsigned-long-long-to-unsigned-long-long 'qqq 0))
  (error? (iptr-to-iptr 'qqq 0))
  (error? (uptr-to-uptr 'qqq 0))
  (error? (void*-to-void* 'qqq 0))
  (error? (int-to-int 0 "oops"))
  (error? (unsigned-to-unsigned 0 "oops"))
  (error? (unsigned-int-to-unsigned-int 0 "oops"))
  (error? (unsigned-short-to-unsigned-short 0 "oops"))
  (error? (short-to-short 0 "oops"))
  (error? (long-to-long 0 "oops"))
  (error? (unsigned-long-to-unsigned-long 0 "oops"))
  (error? (long-long-to-long-long 0 "oops"))
  (error? (unsigned-long-long-to-unsigned-long-long 0 "oops"))
  (error? (iptr-to-iptr 0 "oops"))
  (error? (uptr-to-uptr 0 "oops"))
  (error? (void*-to-void* 0 "oops"))

  (error? (char-to-char 73))
  (error? (char-to-char #\x100))
  (error? (wchar-to-wchar 73))
  (or (= (optimize-level) 3)
      (if (eq? (foreign-sizeof 'wchar) 16)
          (guard? (c [#t]) (wchar-to-char #\x10000) #f)
          #t))
  (error? (float-to-float 'qqq.5))
  (error? (double-to-double 'qqq.5))

  (begin
    (define call-int (foreign-procedure "call_int" (ptr int int int) int))
    (define call-unsigned (foreign-procedure "call_unsigned" (ptr unsigned int int) unsigned))
    (define call-unsigned-int (foreign-procedure "call_unsigned" (ptr unsigned-int int int) unsigned-int))
    (define call-char (foreign-procedure "call_char" (ptr char int int) char))
    (define call-wchar (foreign-procedure "call_wchar" (ptr wchar int int) wchar))
    (define call-short (foreign-procedure "call_short" (ptr short int int) short))
    (define call-unsigned-short (foreign-procedure "call_unsigned_short" (ptr unsigned-short int int) unsigned-short))
    (define call-long (foreign-procedure "call_long" (ptr long int int) long))
    (define call-unsigned-long (foreign-procedure "call_unsigned_long" (ptr unsigned-long int int) unsigned-long))
    (define call-long-long (foreign-procedure "call_long_long" (ptr long-long int int) long-long))
    (define call-unsigned-long-long (foreign-procedure "call_unsigned_long_long" (ptr unsigned-long-long int int) unsigned-long-long))
    (define call-float (foreign-procedure "call_float" (ptr float int int) float))
    (define call-double (foreign-procedure "call_double" (ptr double int int) double))
    (define call-iptr (foreign-procedure "call_iptr" (ptr iptr int int) iptr))
    (define call-uptr (foreign-procedure "call_uptr" (ptr uptr int int) uptr))
    (define call-void* (foreign-procedure "call_uptr" (ptr void* int int) void*))
    #t)
  ($test-call-int #t (foreign-sizeof 'int) call-int
    (lambda (p) (foreign-callable p (int) int)))
  ($test-call-int #f (foreign-sizeof 'unsigned) call-unsigned
    (lambda (p) (foreign-callable p (unsigned) unsigned)))
  ($test-call-int #f (foreign-sizeof 'unsigned-int) call-unsigned-int
    (lambda (p) (foreign-callable p (unsigned-int) unsigned-int)))
  ($test-call-int #t (foreign-sizeof 'short) call-short
    (lambda (p) (foreign-callable p (short) short)))
  ($test-call-int #f (foreign-sizeof 'unsigned-short) call-unsigned-short
    (lambda (p) (foreign-callable p (unsigned-short) unsigned-short)))
  ($test-call-int #t (foreign-sizeof 'long) call-long
    (lambda (p) (foreign-callable p (long) long)))
  ($test-call-int #f (foreign-sizeof 'unsigned-long) call-unsigned-long
    (lambda (p) (foreign-callable p (unsigned-long) unsigned-long)))
  ($test-call-int #t (foreign-sizeof 'long-long) call-long-long
    (lambda (p) (foreign-callable p (long-long) long-long)))
  ($test-call-int #f (foreign-sizeof 'unsigned-long-long) call-unsigned-long-long
    (lambda (p) (foreign-callable p (unsigned-long-long) unsigned-long-long)))
  ($test-call-int #t (foreign-sizeof 'iptr) call-iptr
    (lambda (p) (foreign-callable p (iptr) iptr)))
  ($test-call-int #f (foreign-sizeof 'uptr) call-uptr
    (lambda (p) (foreign-callable p (uptr) uptr)))
  ($test-call-int #f (foreign-sizeof 'void*) call-void*
    (lambda (p) (foreign-callable p (void*) void*)))
  (equal?
    (call-char
      (foreign-callable
        (lambda (x) (integer->char (+ (char->integer x) 5)))
        (char) char)
      #\a 7 11)
    #\x)
  (equal?
    (call-wchar
      (foreign-callable
        (lambda (x) (integer->char (+ (char->integer x) 5)))
        (wchar) wchar)
      #\x3bb 7 11)
    #\x3d2)
  (equal?
    (call-float
      (foreign-callable
        (lambda (x) (+ x 5))
        (float) single-float)
      73.25 7 23)
    108.25)
  (equal?
    (call-double
      (foreign-callable
        (lambda (x) (+ x 5))
        (double) double-float)
      73.25 7 23)
    108.25)

  (error?
    (call-int
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (int) int)
      73 0 0))
  (error?
    (call-unsigned
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (unsigned) unsigned)
      73 0 0))
  (error?
    (call-unsigned-int
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (unsigned-int) unsigned-int)
      73 0 0))
  (error?
    (call-char
      (foreign-callable
        (lambda (x) (list x))
        (char) char)
      #\a 0 0))
  (error?
    (call-wchar
      (foreign-callable
        (lambda (x) (list x))
        (wchar) wchar)
      #\a 0 0))
  (error?
    (call-short
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (short) short)
      73 0 0))
  (error?
    (call-unsigned-short
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (unsigned-short) unsigned-short)
      73 0 0))
  (error?
    (call-long
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (long) long)
      73 0 0))
  (error?
    (call-unsigned-long
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (unsigned-long) unsigned-long)
      73 0 0))
  (error?
    (call-long-long
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (long-long) long-long)
      73 0 0))
  (error?
    (call-unsigned-long-long
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (unsigned-long-long) unsigned-long-long)
      73 0 0))
  (error?
    (call-float
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (float) float)
      73.25 0 0))
  (error?
    (call-double
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (double) double)
      73.25 0 0))
  (error?
    (call-iptr
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (iptr) iptr)
      73 0 0))
  (error?
    (call-uptr
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (uptr) uptr)
      73 0 0))
  (error?
    (call-void*
      (foreign-callable
        (lambda (x) (list x (+ x 1)))
        (void*) void*)
      73 0 0))
)

(mat foreign-ftype
  (begin
    (define-ftype A (struct [x double] [y wchar]))
    (define-ftype B (struct [x (array 10 A)] [y A]))
    (define B->*int (foreign-procedure "uptr_to_uptr" ((* B) int) (* int)))
    (define B->A (foreign-procedure "uptr_to_uptr" ((* B) int) (* A)))
    (define B->uptr (foreign-procedure "uptr_to_uptr" ((* B) int) uptr))
    (define uptr->A (foreign-procedure "uptr_to_uptr" (uptr int) (* A)))
    (define b ((foreign-procedure (if (windows?) "windows_malloc" "malloc") (ssize_t) (* B)) (ftype-sizeof B)))
    #t)
  (eqv?
    (ftype-pointer-address (uptr->A (ftype-pointer-address (ftype-&ref B (y) b)) 0))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (eqv?
    (ftype-pointer-address (uptr->A (ftype-pointer-address b) (* 10 (ftype-sizeof A))))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (eqv?
    (B->uptr b (* 10 (ftype-sizeof A)))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (eqv?
    (ftype-pointer-address (B->A b (* 10 (ftype-sizeof A))))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (begin
    (define uptr->uptr (foreign-callable values (uptr) uptr))
    (define uptr->A (foreign-callable (lambda (a) (make-ftype-pointer A a)) (uptr) (* A)))
    (define B->uptr (foreign-callable ftype-pointer-address ((* B)) uptr))
    (define B->A (foreign-callable (lambda (b) (ftype-&ref B (y) b)) ((* B)) (* A)))
    (define call-B->A (foreign-procedure "call_uptr" (ptr (* B) int int) (* A)))
    #t)
  (eqv?
    (ftype-pointer-address (call-B->A uptr->uptr b (* 5 (ftype-sizeof A)) (* 5 (ftype-sizeof A))))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (eqv?
    (ftype-pointer-address (call-B->A uptr->A b (* 5 (ftype-sizeof A)) (* 5 (ftype-sizeof A))))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (eqv?
    (ftype-pointer-address (call-B->A B->uptr b (* 5 (ftype-sizeof A)) (* 5 (ftype-sizeof A))))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (eqv?
    (ftype-pointer-address (call-B->A B->A b 0 0))
    (ftype-pointer-address (ftype-&ref B (y) b)))
  (begin
    ((foreign-procedure (if (windows?) "windows_free" "free") ((* B)) void) b)
    (set! b #f)
    #t)
  (error? ; unrecognized foreign-procedure argument ftype name
    (foreign-procedure "foo" ((* broken)) void))
  (error? ; invalid foreign-procedure argument type specifier
    (foreign-procedure "foo" ((+ * -)) void))
  (error? ; invalid foreign-procedure argument type specifier
    (foreign-procedure "foo" ((* * *)) void))
  (error? ; invalid foreign-procedure argument type specifier
    (foreign-procedure "foo" ((struct [a int])) void))
  (error? ; invalid foreign-procedure argument type specifier
    (foreign-procedure "foo" (hag) void))
  (error? ; unrecognized foreign-procedure return ftype name
    (foreign-procedure "foo" () (* broken)))
  (error? ; invalid foreign-procedure return type specifier
    (foreign-procedure "foo" () (+ * -)))
  (error? ; invalid foreign-procedure return type specifier
    (foreign-procedure "foo" () (* * *)))
  (error? ; invalid foreign-procedure argument type specifier
    (foreign-procedure "foo" () ((struct [a int]))))
  (error? ; invalid foreign-procedure return type specifier
    (foreign-procedure "foo" () hag))
  (error? ; invalid (non-base) ... ftype
    (foreign-procedure "foo" (A) void))
  (error? ; invalid (non-base) ... ftype
    (foreign-procedure "foo" () A))
  (begin
    (meta-cond
      [(eq? (native-endianness) 'little)
       (define-ftype swap-fixnum (endian big fixnum))]
      [(eq? (native-endianness) 'big)
       (define-ftype swap-fixnum (endian little fixnum))])
    #t)
  (error? ; invalid (swapped) ... ftype
    (foreign-procedure "foo" (swap-fixnum) void))
  (error? ; invalid (swapped) ... ftype
    (foreign-procedure "foo" () swap-fixnum))
  (error? ; invalid syntax
    (define-ftype foo (function "wtf" () void) +))
  (error? ; invalid convention
    (define-ftype foo (function "wtf" () void)))
  (error? ; invalid argument type void
    (define-ftype foo (function (void) int)))
  (equal?
    (let ()
      (define-ftype foo (function (int) void))
      (list (ftype-pointer? (make-ftype-pointer foo 0))
            (ftype-pointer? foo (make-ftype-pointer double 0))
            (ftype-pointer? foo (make-ftype-pointer foo 0))))
    '(#t #f #t))
  (error? ; non-function ftype with "memcpy" address
    (define $fp-bvcopy (make-ftype-pointer double "memcpy")))
  (error? ; unrecognized ftype
    (define $fp-bvcopy (make-ftype-pointer spam "memcpy")))
  (error? ; invalid syntax 
    (define $fp-bvcopy (make-ftype-pointer (struct [x int]) "memcpy")))
  (error? ; invalid function-ftype result type specifier u8
    (let ()
      (define-ftype foo (function (u8* u8* size_t) u8))
      (define $fp-bvcopy (make-ftype-pointer foo "memcpy"))))
  (error? ; invalid function-ftype argument type specifier u8
    (let ()
      (define-ftype foo (function (u8* u8 size_t) u8*))
      (define $fp-bvcopy (make-ftype-pointer foo "memcpy"))))
  (begin
    (define-ftype memcpy_t (function (u8* u8* size_t) u8*))
    (define $fp-bvcopy (ftype-ref memcpy_t () (make-ftype-pointer memcpy_t "memcpy")))
    #t)
  (let ([bv1 (string->utf8 "hello")] [bv2 (make-bytevector 5)])
    ($fp-bvcopy bv2 bv1 5)
    (and (bytevector=? bv1 bv2) (bytevector=? bv1 (string->utf8 "hello"))))
  (begin
    (define-ftype bvcopy-t (function (u8* u8* size_t) u8*))
    (define $fp-bvcopy (ftype-ref bvcopy-t () (make-ftype-pointer bvcopy-t "memcpy")))
    #t)
  (let ([bv1 (string->utf8 "hello")] [bv2 (make-bytevector 5)])
    ($fp-bvcopy bv2 bv1 5)
    (and (bytevector=? bv1 bv2) (bytevector=? bv1 (string->utf8 "hello"))))
  ;; No longer an error since make-ftype-pointer also serves to make foriegn-pointers
  #;(error? ; "memcpy" is not a procedure
      (make-ftype-pointer memcpy_t "memcpy"))
  (error? ; unrecognized ftype
    (make-ftype-pointer spam +))
  (error? ; non-function ftype
    (make-ftype-pointer double +))
  (error? ; invalid syntax
    (make-ftype-pointer (struct [x int]) +))
  (eqv?
    (let ()
      (define-ftype foo (function (int int) double))
      (define code
        (make-ftype-pointer foo
          (lambda (x y) (inexact (+ x y)))))
      (let ([code-object (foreign-callable-code-object (ftype-pointer-address code))])
        (dynamic-wind
          (lambda () (lock-object code-object))
          (lambda ()
            (define f (ftype-ref foo () code))
            (f 3 4))
          (lambda () (unlock-object code-object)))))
    7.0)
  (eqv?
    (let ()
      (define-ftype foo (function (int int) double))
      (define code
        (make-ftype-pointer foo
          (lambda (x y) (inexact (+ x y)))))
      (define f (ftype-ref foo () code))
      (let ([x (f 8 4)])
        (unlock-object (foreign-callable-code-object (ftype-pointer-address code)))
        x))
    12.0)
  (eqv?
    (let ()
      (define-ftype foo (function (void* void*) ptrdiff_t))
      (define code (make-ftype-pointer foo -))
      (let ([code-object (foreign-callable-code-object (ftype-pointer-address code))])
        (dynamic-wind
          (lambda () (lock-object code-object))
          (lambda () ((ftype-ref foo () code) 17 (* (most-positive-fixnum) 2)))
          (lambda () (unlock-object code-object)))))
    (- 17 (* (most-positive-fixnum) 2)))
  (eqv?
    (let ()
      (define-ftype foo (function (void* void*) ptrdiff_t))
      (define code (make-ftype-pointer foo -))
      (let ([x ((ftype-ref foo () code) 19 (* (most-positive-fixnum) 2))])
        (unlock-object (foreign-callable-code-object (ftype-pointer-address code)))
        x))
    (- 19 (* (most-positive-fixnum) 2)))
  (eqv?
    (let ()
      (define-ftype foo (function (int int) size_t))
      (define code (make-ftype-pointer foo -))
      (let ([code-object (foreign-callable-code-object (ftype-pointer-address code))])
        (dynamic-wind
          (lambda () (lock-object code))
          (lambda () ((ftype-ref foo () code) 17 32))
          (lambda () (unlock-object code)))))
    (- (expt 2 (* (ftype-sizeof size_t) 8)) 15))
  (eqv?
    (let ()
      (define-ftype foo (function (int int) size_t))
      (define code (make-ftype-pointer foo -))
      (let ([x ((ftype-ref foo () code) 17 32)])
        (unlock-object (foreign-callable-code-object (ftype-pointer-address code)))
        x))
    (- (expt 2 (* (ftype-sizeof size_t) 8)) 15))

  (error? ; not a string
    (foreign-entry #e1e6))

  (error? ; no entry for "i am not defined"
    (foreign-entry "i am not defined"))

  (begin
    (define-ftype F (function (size_t) int))
    (define malloc-fptr1 (make-ftype-pointer F (if (windows?) "windows_malloc" "malloc")))
    (define malloc-fptr2 (make-ftype-pointer F (foreign-entry (if (windows?) "windows_malloc" "malloc"))))
    #t)

  (equal?
    (foreign-address-name (ftype-pointer-address malloc-fptr1))
    (if (windows?) "windows_malloc" "malloc"))

  (equal?
    (foreign-address-name (ftype-pointer-address malloc-fptr2))
    (if (windows?) "windows_malloc" "malloc"))

  (eqv?
    (ftype-pointer-address malloc-fptr1)
    (ftype-pointer-address malloc-fptr2))

  (procedure?
    (ftype-ref F () malloc-fptr1))

  (begin
    (define-ftype SF (struct [i int] [f (* F)]))
    (define sf (make-ftype-pointer SF (foreign-alloc (ftype-sizeof SF))))
    (ftype-set! SF (i) sf 10)
    (ftype-set! SF (f) sf malloc-fptr2)
    #t)

  (ftype-pointer? F (ftype-ref SF (f) sf))

  (procedure? (ftype-ref SF (f *) sf))

  (error?
    (begin
      (define-ftype A (struct [x double] [y wchar]))
      (define-ftype B (struct [x (array 10 A)] [y A]))
      ; see if defns above mess up defn below
      (define-ftype
        [A (function ((* B)) (* B))]
        [B (struct [x A])])))

  (begin
    (define-ftype A (struct [x double] [y wchar]))
    (define-ftype B (struct [x (array 10 A)] [y A]))
    ; see if defns above mess up defn below
    (define-ftype
      [A (function ((* B)) (* B))]
      [B (struct [x (* A)])])
    (define b (make-ftype-pointer B (foreign-alloc (ftype-sizeof B))))
    (define a (ftype-ref A () (make-ftype-pointer A "idiptr")))
    #t)
  (eqv? (ftype-pointer-address (a b)) (ftype-pointer-address b))

  (begin
    (define-ftype
      [A (function ((* B)) (* B))]
      [B (struct [x (* A)])])
    (define b (make-ftype-pointer B (foreign-alloc (ftype-sizeof B))))
    (define a (ftype-ref A () (make-ftype-pointer A "idiptr")))
    #t)
  (eqv? (ftype-pointer-address (a b)) (ftype-pointer-address b))

  (begin
    (define-ftype
      [B (struct [x (* A)])]
      [A (function ((* B)) (* B))])
    (define b (make-ftype-pointer B (foreign-alloc (ftype-sizeof B))))
    (define a (ftype-ref A () (make-ftype-pointer A "idiptr")))
    #t)
  (eqv? (ftype-pointer-address (a b)) (ftype-pointer-address b))

  (begin
    (define-ftype A (function ((* A)) (* A)))
    (define a (make-ftype-pointer A "idiptr"))
    #t)
  (eqv? (ftype-pointer-address ((ftype-ref A () a) a)) (ftype-pointer-address a))

  (begin 
    (define-ftype A (struct [x uptr] [y uptr]))
    (define a (make-ftype-pointer A (foreign-alloc (ftype-sizeof A))))
    (define ff-init-lock (foreign-procedure "init_lock" ((* uptr)) void))
    (define ff-spinlock (foreign-procedure "spinlock" ((* uptr)) void))
    (define ff-unlock (foreign-procedure "unlock" ((* uptr)) void))
    (define ff-locked-incr (foreign-procedure "locked_incr" ((* uptr)) boolean))
    (define ff-locked-decr (foreign-procedure "locked_decr" ((* uptr)) boolean))
    #t)
  (eq? (ff-init-lock (ftype-&ref A (x) a)) (void))
  (ftype-lock! A (x) a)
  (not (ftype-lock! A (x) a))
  (eq? (ftype-unlock! A (x) a) (void))
  (eq? (ff-spinlock (ftype-&ref A (x) a)) (void))
  (not (ftype-lock! A (x) a))
  (eq? (ff-unlock (ftype-&ref A (x) a)) (void))
  (ftype-lock! A (x) a)
  (eq? (ff-unlock (ftype-&ref A (x) a)) (void))
  (eq? (ff-spinlock (ftype-&ref A (x) a)) (void))
  (not (ftype-lock! A (x) a))
  (eq? (ff-unlock (ftype-&ref A (x) a)) (void))
  (eq? (ftype-set! A (y) a 1) (void))
  (not (ff-locked-incr (ftype-&ref A (y) a)))
  (eqv? (ftype-ref A (y) a) 2)
  (not (ff-locked-decr (ftype-&ref A (y) a)))
  (ff-locked-decr (ftype-&ref A (y) a))
  (eqv? (ftype-ref A (y) a) 0)
  (not (ff-locked-decr (ftype-&ref A (y) a)))
  (ff-locked-incr (ftype-&ref A (y) a))
)

(mat foreign-anonymous
  (eqv?
    (let ([addr ((foreign-procedure "idiptr_addr" () iptr))])
      (define idiptr (foreign-procedure addr (scheme-object) scheme-object))
      (idiptr 'friggle))
    'friggle)
)

(machine-case
  [(i3nt ti3nt)
   (mat i3nt-stdcall
     (let ()
        (define (win32:number-32-ptr->number n32ptr)
          (+ (fx+ (char->integer (string-ref n32ptr 0))
               (fxsll (char->integer (string-ref n32ptr 1)) 8)
               (fxsll (char->integer (string-ref n32ptr 2)) 16))
            (* (char->integer (string-ref n32ptr 3)) #x1000000)))
        (define (win32:GetVolumeSerialNumber root)
          (define f-proc
            (foreign-procedure __stdcall "GetVolumeInformationA"
              (string string unsigned-32 string string string string unsigned-32)
              boolean))
          (let ([vol-sid (make-string 4)]
                [max-filename-len (make-string 4)]
                [sys-flags (make-string 4)])
            (and (f-proc root #f 0 vol-sid max-filename-len sys-flags #f 0)
                 (win32:number-32-ptr->number vol-sid))))
        (number? (win32:GetVolumeSerialNumber "C:\\"))))])

(mat single-float
  (= (let ((x (foreign-procedure "sxstos" (single-float single-float)
                single-float)))
       (x 3.0 5.0))
     15)
  (let ((args '(1.25 2.25 3.25 4.25 5.25 6.25 7.25 8.25 9.25 10.25 11.25 12.25)))
    (= (apply + args)
       (apply
         (foreign-procedure "singlesum12"
           (single-float single-float single-float single-float
            single-float single-float single-float single-float
            single-float single-float single-float single-float)
           single-float)
         args)))
 )

(mat auto-mat-icks
  (auto-mat-ick "d1d2")
  (auto-mat-ick "s1s2")
  (auto-mat-ick "s1d1")
  (auto-mat-ick "d1s1")
  (auto-mat-ick "n1n2n3n4")
  (auto-mat-ick "d1n1d2")
  (auto-mat-ick "d1n1n2")
  (auto-mat-ick "s1n1n2")
  (auto-mat-ick "n1n2n3d1")
  (auto-mat-ick "n1n2n3s1")
  (auto-mat-ick "n1n2d1")
  (auto-mat-ick "n1d1")
  (auto-mat-ick "s1s2s3s4")
  (auto-mat-ick "s1n1s2n2")
  (auto-mat-ick "d1s1s2")
  (auto-mat-ick "s1s2d1")
  (auto-mat-ick "n1s1n2s2")
  (auto-mat-ick "n1s1n2n3")
  (auto-mat-ick "n1n2s1n3")
  (auto-mat-ick "d1d2s1s2")
  (auto-mat-ick "d1d2n1n2")
  (auto-mat-ick "s1d1s2s3")
 )

(mat foreign-callable
  (error? ; spam is not a procedure
    (foreign-callable 'spam () void))
  (error? ; spam is not a procedure
    (begin (foreign-callable 'spam () void) 'q))
  (error? ; spam is not a procedure
    (if (foreign-callable 'spam () void) 'q 'p))
  (equal?
    (let ()
      (define Sinvoke2
        (foreign-procedure "Sinvoke2"
          (scheme-object scheme-object iptr)
          scheme-object))
      (define Fcons
        (foreign-callable
          (lambda (x y)
            (collect)
            (let ([ls (make-list 20000 #\z)])
              (collect)
              (collect)
              (collect)
              (collect)
              (collect)
              (cons (length ls) (cons x y))))
          (scheme-object iptr)
          scheme-object))
      (define (go) (Sinvoke2 Fcons 4 5))
      (go))
    '(20000 4 . 5))
  (eqv?
    (let ()
      (define Sinvoke2
        (foreign-procedure "Sinvoke2"
          (scheme-object scheme-object iptr)
          scheme-object))
      (define fxFsum
        (foreign-callable
          (lambda (x y)
            (if (fx= x 0)
                y
                (fx+ x (Sinvoke2 fxFsum (fx- x 1) y))))
          (scheme-object iptr)
          scheme-object))
      (define (fxgosum n) (Sinvoke2 fxFsum n 0))
      (fxgosum 20))
    210)
  (eqv?
    (let ()
      (define Sinvoke2
        (foreign-procedure "Sinvoke2"
          (scheme-object scheme-object iptr)
          scheme-object))
      (define Fsum
        (foreign-callable
          (lambda (x y)
            (if (= x 0)
                y
                (+ x (Sinvoke2 Fsum (- x 1) y))))
          (scheme-object iptr)
          scheme-object))
      (define (gosum n) (Sinvoke2 Fsum n (most-positive-fixnum)))
      (gosum 20))
    (+ (most-positive-fixnum) 210))
  (let ()
    (define Fargtest
      (foreign-callable
        (lambda (bool char fixnum double single string)
          (list string single double fixnum char bool))
        (boolean char fixnum double-float single-float string)
        scheme-object))
    (define Sargtest
      (foreign-procedure "Sargtest"
        (iptr boolean char fixnum double-float single-float string)
        scheme-object))
    (define args1 (list #t #\Q 12345 3.1415 2.0 "hit me"))
    (define args2 (list #f #\newline -51293 3.1415 2.5 ""))
    (define args3 (list #f #\newline -51293 3.1415 2.5 #f))
    (let ()
      (define addr
        (begin
          (lock-object Fargtest)
          (foreign-callable-entry-point Fargtest)))
      (dynamic-wind
        void
        (lambda ()
          (collect (collect-maximum-generation))
          (collect (collect-maximum-generation))
          (and
            (equal? (apply Sargtest addr args1) (reverse args1))
            (equal? (apply Sargtest addr args2) (reverse args2))
            (equal? (apply Sargtest addr args3) (reverse args3))))
        (lambda () (unlock-object Fargtest)))))
  (let ()
    (define Fargtest2
      (foreign-callable
        (lambda (x1 x2 x3 x4 x5 x6)
          (list x6 x5 x4 x3 x2 x1))
        (short int char double short char)
        scheme-object))
    (define Sargtest2
      (foreign-procedure "Sargtest2"
        (iptr short int char double short char)
        scheme-object))
    (define args1 (list 32123 #xc7c7c7 #\% 3.1415 -32768 #\!))
    (define args2 (list 17 #x-987654 #\P -521.125 -1955 #\Q))
    (define args3 (list -7500 #x987654 #\? +inf.0 3210 #\7))
    (let ()
      (define addr
        (begin
          (lock-object Fargtest2)
          (foreign-callable-entry-point Fargtest2)))
      (dynamic-wind
        void
        (lambda ()
          (collect (collect-maximum-generation))
          (collect (collect-maximum-generation))
          (and
            (equal? (apply Sargtest2 addr args1) (reverse args1))
            (equal? (apply Sargtest2 addr args2) (reverse args2))
            (equal? (apply Sargtest2 addr args3) (reverse args3))))
        (lambda () (unlock-object Fargtest2)))))
  (let ()
    (define Frvtest_int32
      (foreign-callable
        (lambda (x) (* x x))
        (scheme-object)
        integer-32))
    (define Srvtest_int32
      (foreign-procedure "Srvtest_int32"
        (scheme-object scheme-object)
        integer-32))
    (and
      (eqv? (Srvtest_int32 Frvtest_int32 16) 256)
      (eqv? (Srvtest_int32 Frvtest_int32 #x8000) #x40000000)))
  (let ()
    (define Frvtest_uns32
      (foreign-callable
        (lambda (x) (- (* x x) 1))
        (scheme-object)
        unsigned-32))
    (define Srvtest_uns32
      (foreign-procedure "Srvtest_uns32"
        (scheme-object scheme-object)
        unsigned-32))
    (and
      (eqv? (Srvtest_uns32 Frvtest_uns32 16) 255)
      (eqv? (Srvtest_uns32 Frvtest_uns32 #x10000) #xffffffff)))
  (let ()
    (define Frvtest_single
      (foreign-callable
        (lambda (x) (* x x))
        (scheme-object)
        single-float))
    (define Srvtest_single
      (foreign-procedure "Srvtest_single"
        (scheme-object scheme-object)
        single-float))
    (eqv? (Srvtest_single Frvtest_single 16.0) 256.0))
  (let ()
    (define Frvtest_double
      (foreign-callable
        (lambda (x) (* x x))
        (scheme-object)
        double-float))
    (define Srvtest_double
      (foreign-procedure "Srvtest_double"
        (scheme-object scheme-object)
        double-float))
    (eqv? (Srvtest_double Frvtest_double 16.0) 256.0))
  (let ()
    (define Frvtest_char
      (foreign-callable
        (lambda (x) (string-ref x 3))
        (scheme-object)
        char))
    (define Srvtest_char
      (foreign-procedure "Srvtest_char"
        (scheme-object scheme-object)
        char))
    (eqv? (Srvtest_char Frvtest_char "abcdefg") #\d))
  (let ()
    (define Frvtest_boolean
      (foreign-callable
        (lambda (x) (equal? x "abcdefg"))
        (scheme-object)
        boolean))
    (define Srvtest_boolean
      (foreign-procedure "Srvtest_int32"
        (scheme-object scheme-object)
        boolean))
    (and
      (eqv? (Srvtest_boolean Frvtest_boolean "abcdefg") #t)
      (eqv? (Srvtest_boolean Frvtest_boolean "gfedcba") #f)))
  (let ()
    (define Frvtest_fixnum
      (foreign-callable
        (lambda (x) (* x x))
        (scheme-object)
        fixnum))
    (define Srvtest_fixnum
      (foreign-procedure "Srvtest_int32"
        (scheme-object scheme-object)
        fixnum))
    (eqv? (Srvtest_fixnum Frvtest_fixnum 16) 256))
  (let ()
    (define Frvtest_fixnum
      (foreign-callable
        (lambda (x) (* x x))
        (scheme-object)
        void))
    (define Srvtest_fixnum
      (foreign-procedure "Srvtest_int32"
        (scheme-object scheme-object)
        void))
    (eqv? (Srvtest_fixnum Frvtest_fixnum 16) (void)))
  #;(error? (foreign-callable values (scheme-object) foreign-pointer))
  #;(error? (foreign-callable values (scheme-object) (foreign-object 16 4)))
  #;(error? (foreign-callable values (foreign-pointer) void))
  #;(error? (foreign-callable values ((foreign-object 16 4)) void))
  (equal?
    (let ([x 5])
      (define call-twice (foreign-procedure "call_twice" (void* int int) void))
      (let ([co (foreign-callable (lambda (y) (set! x (+ x y))) (int) void)])
        (lock-object co)
        (call-twice (foreign-callable-entry-point co) 7 31)
        (unlock-object co))
      x)
    43)
  (equal?
    (let ()
     ; foreign_callable example adapted from foreign.stex
      (define cb-init
        (foreign-procedure "cb_init" () void))
      (define register-callback
        (foreign-procedure "register_callback" (char iptr) void))
      (define event-loop
        (foreign-procedure "event_loop" (string) void))

      (define callback
        (lambda (p)
          (let ([code (foreign-callable p (char) void)])
            (lock-object code)
            (foreign-callable-entry-point code))))
      (let ()
        (define ouch
          (callback
            (lambda (c)
              (printf "Ouch! Hit by '~c'~%" c))))
        (define rats
          (callback
            (lambda (c)
              (printf "Rats! Received '~c'~%" c))))
  
        (cb-init)
        (register-callback #\a ouch)
        (register-callback #\c rats)
        (register-callback #\e ouch)
  
        (parameterize ([current-output-port (open-output-string)])
          (event-loop "abcde")
          (get-output-string (current-output-port)))))
    (format "Ouch! Hit by 'a'~%Rats! Received 'c'~%Ouch! Hit by 'e'~%"))
   ; make sure foreign-procedure's code-object is properly locked when
   ; calling back into Scheme
   (begin
     (define call-collect (lambda () (collect) (collect (collect-maximum-generation))))
     (define code (foreign-callable call-collect () void))
     (collect)
     #t)
   ; this form needs to be after the preceding form and not part of it, so that when
   ; we lock code we don't also lock the code object created by foreign-procedure
   (begin
     (lock-object code)
     ((foreign-procedure (foreign-callable-entry-point code) () scheme-object))
     (unlock-object code)
     #t)

  (not (locked-object?
         (let ()
           (define cb (foreign-callable (lambda (i) i) (int) int))
           (define unlock-callback (foreign-procedure "unlock_callback" (void*) void))
           (lock-object cb)
           (unlock-callback (foreign-callable-entry-point cb))
           cb)))
  (not (locked-object?
         (let ()
           (define cb (foreign-callable (lambda (i) i) (int) int))
           (define unlock-callback (foreign-procedure "unlock_callback" (void*) void))
           (lock-object cb)
           (collect)
           (unlock-callback (foreign-callable-entry-point cb))
           cb)))
  (equal?
    (let ()
      (define cb (foreign-callable (lambda (i) (+ i 10)) (int) int))
      (define call-and-unlock (foreign-procedure "call_and_unlock" (void* int) int))
      (lock-object cb)
      (let ([ans (call-and-unlock (foreign-callable-entry-point cb) 5)])
        (list (locked-object? cb) ans)))
    '(#f 15))
  (equal?
    (let ()
      (define cb (foreign-callable (lambda (i) (+ i 10)) (int) int))
      (define call-and-unlock (foreign-procedure "call_and_unlock" (void* int) int))
      (lock-object cb)
      (collect)
      (let ([ans (call-and-unlock (foreign-callable-entry-point cb) 3)])
        (list (locked-object? cb) ans)))
    '(#f 13))
  (begin
    (define $stack-depth 8000)
    (define $base-value 37)
    #t)
  (eqv? ; make sure foreign-callable does it's overflow checks
    (let ()
      (define-ftype foo (function (fixnum fixnum) fixnum))
      (define f (lambda (n m) (if (fx= n 0) m (g (fx- n 1) (fx+ m 1)))))
      (define fptr (make-ftype-pointer foo f))
      (define g (ftype-ref foo () fptr))
      (let ([v (f $stack-depth $base-value)])
        (unlock-object
          (foreign-callable-code-object
            (ftype-pointer-address fptr)))
        v))
    (+ $stack-depth $base-value))
  (begin
    (define $with-exit-proc
      ; if you change this, consider changing the definition of with-exit-proc
      ; in foreign.stex
      (lambda (p)
        (define th (lambda () (call/cc p)))
        (define-ftype ->ptr (function () ptr))
        (let ([fptr (make-ftype-pointer ->ptr th)])
          (let ([v ((ftype-ref ->ptr () fptr))])
            (unlock-object
              (foreign-callable-code-object
                (ftype-pointer-address fptr)))
            v))))
    #t)
  (eqv? ; make sure we can jump out of a deep nest of C/Scheme calls
    (let ()
      (define *k*)
      (define-ftype foo (function (fixnum fixnum) fixnum))
      (define f (lambda (n m) (if (fx= n 0) (*k* m) (g (fx- n 1) (fx+ m 1)))))
      (define fptr (make-ftype-pointer foo f))
      (define g (ftype-ref foo () fptr))
      (let ([v ($with-exit-proc
                 (lambda (k)
                   (set! *k* k)
                   (f $stack-depth $base-value)))])
        (unlock-object
          (foreign-callable-code-object
            (ftype-pointer-address fptr)))
        v))
    (+ $stack-depth $base-value))
  (eqv? ; make sure we can jump out a few frames at a time
    (let ()
      (define-ftype foo (function (fixnum fixnum ptr) fixnum))
      (define f
        (lambda (n m k)
          (if (fx= n 0)
              (k m)
              (if (fx= (fxmodulo n 10) 0)
                  (k (call/cc
                       (lambda (k)
                         (g (fx- n 1) (fx+ m 1) k))))
                  (g (fx- n 1) (fx+ m 1) k)))))
      (define fptr (make-ftype-pointer foo f))
      (define g (ftype-ref foo () fptr))
      (let ([v ($with-exit-proc
                 (lambda (k)
                   (f $stack-depth $base-value k)))])
        (unlock-object
          (foreign-callable-code-object
            (ftype-pointer-address fptr)))
        v))
    (+ $stack-depth $base-value))
  (or (= (optimize-level) 3)
      ; make sure we can jump out a few frames at a time, returning from
      ; each with an invalid number of values, just for fun
      (eqv?
        ($with-exit-proc
          (lambda (ignore)
            (define *m*)
            (define *k*)
            (define-ftype foo (function (fixnum fixnum) fixnum))
            (define f
              (lambda (n m)
                (if (fx= n 0)
                    (begin (set! *m* m) (values))
                    (if (fx= (fxmodulo n 10) 0)
                        (begin
                          (set! *m*
                            (call/cc
                              (lambda (k)
                                (fluid-let ([*k* k])
                                  (g (fx- n 1) (fx+ m 1))))))
                          (values))
                        (g (fx- n 1) (fx+ m 1))))))
            (define fptr (make-ftype-pointer foo f))
            (define g (ftype-ref foo () fptr))
            (with-exception-handler
              (lambda (c) (*k* *m*))
              (lambda ()
                (call/cc
                  (lambda (k)
                    (fluid-let ([*k* k]) (f $stack-depth $base-value))))))
            (unlock-object
              (foreign-callable-code-object
                (ftype-pointer-address fptr)))
            *m*))
        (+ $stack-depth $base-value)))
  (or (= (optimize-level) 3)
      ; similarly, but with a ptr return value so the values error is signaled
      ; by S_call_help wrather than the foreign-procedure wrapper
      (eqv?
        ($with-exit-proc
          (lambda (ignore)
            (define *m*)
            (define *k*)
            (define-ftype foo (function (fixnum fixnum) ptr))
            (define f
              (lambda (n m)
                (if (fx= n 0)
                    (begin (set! *m* m) (values))
                    (if (fx= (fxmodulo n 10) 0)
                        (begin
                          (set! *m*
                            (call/cc
                              (lambda (k)
                                (fluid-let ([*k* k])
                                  (g (fx- n 1) (fx+ m 1))))))
                          (values))
                        (g (fx- n 1) (fx+ m 1))))))
            (define fptr (make-ftype-pointer foo f))
            (define g (ftype-ref foo () fptr))
            (with-exception-handler
              (lambda (c) (*k* *m*))
              (lambda ()
                (call/cc
                  (lambda (k)
                    (fluid-let ([*k* k]) (f $stack-depth $base-value))))))
            (unlock-object
              (foreign-callable-code-object
                (ftype-pointer-address fptr)))
            *m*))
        (+ $stack-depth $base-value)))
  (or (= (optimize-level) 3)
      ; make sure we can jump out a few frames at a time, returning from
      ; each with an fasl-reading error, just for fun
      (eqv?
        (let ()
          (define *m*)
          (define *k*)
          (define ip (open-file-input-port "mat.ss"))
          (define-ftype foo (function (fixnum fixnum) fixnum))
          (define f
            (lambda (n m)
              (if (fx= n 0)
                  (begin (set! *m* m) (fasl-read ip))
                  (if (fx= (fxmodulo n 10) 0)
                      (begin
                        (set! *m*
                          (call/cc
                            (lambda (k)
                              (fluid-let ([*k* k])
                                (g (fx- n 1) (fx+ m 1))))))
                        (fasl-read ip))
                      (g (fx- n 1) (fx+ m 1))))))
          (define fptr (make-ftype-pointer foo f))
          (define g (ftype-ref foo () fptr))
          ; position "fasl" file at eof to make sure fasl-read isn't tripped up
          ; by something that appears almost valid
          (get-bytevector-all ip)
          (with-exception-handler
            (lambda (c) (*k* *m*))
            (lambda ()
              ($with-exit-proc
                (lambda (k)
                  (fluid-let ([*k* k]) (f $stack-depth $base-value))))))
          (unlock-object
            (foreign-callable-code-object
              (ftype-pointer-address fptr)))
          *m*)
        (+ $stack-depth $base-value)))
)

(machine-case
  [(i3nt ti3nt)
   (mat i3nt-stdcall-foreign-callable
     (equal?
       (let ()
         (define Sinvoke2
           (foreign-procedure "Sinvoke2_stdcall"
             (scheme-object scheme-object iptr)
             scheme-object))
         (define Fcons
           (foreign-callable __stdcall
             (lambda (x y)
               (collect)
               (let ([ls (make-list 20000 #\z)])
                 (collect)
                 (collect)
                 (collect)
                 (collect)
                 (collect)
                 (cons (length ls) (cons x y))))
             (scheme-object iptr)
             scheme-object))
         (define (go) (Sinvoke2 Fcons 4 5))
         (go))
       '(20000 4 . 5))
     (eqv?
       (let ()
         (define Sinvoke2
           (foreign-procedure "Sinvoke2_stdcall"
             (scheme-object scheme-object iptr)
             scheme-object))
         (define fxFsum
           (foreign-callable __stdcall
             (lambda (x y)
               (if (fx= x 0)
                   y
                   (fx+ x (Sinvoke2 fxFsum (fx- x 1) y))))
             (scheme-object iptr)
             scheme-object))
         (define (fxgosum n) (Sinvoke2 fxFsum n 0))
         (fxgosum 20))
       210)
     (eqv?
       (let ()
         (define Sinvoke2
           (foreign-procedure "Sinvoke2_stdcall"
             (scheme-object scheme-object iptr)
             scheme-object))
         (define Fsum
           (foreign-callable __stdcall
             (lambda (x y)
               (if (= x 0)
                   y
                   (+ x (Sinvoke2 Fsum (- x 1) y))))
             (scheme-object iptr)
             scheme-object))
         (define (gosum n) (Sinvoke2 Fsum n (most-positive-fixnum)))
         (gosum 20))
       536871121))
   (mat i3nt-com
     (eqv?
       (let ()
         (define com-instance ((foreign-procedure "get_com_instance" () iptr)))
         ((foreign-procedure __com 0 (iptr int) int) com-instance 3)
         ((foreign-procedure __com 4 (iptr int) int) com-instance 17))
       37))])

(mat die-gracefully-without-stderr
  (let-values ([(to-stdin from-stdout from-stderr pid)
                (open-process-ports (format "~a -q" (patch-exec-path *scheme*))
                  (buffer-mode block)
                  (native-transcoder))])
    (fprintf to-stdin "(error #f \"oops 1\")\n")
    (flush-output-port to-stdin)
    (let ([s1 (get-line from-stderr)])
      (close-port from-stderr)
      (fprintf to-stdin "(error #f \"oops 2\")\n") ; this message should disappear
      (flush-output-port to-stdin)
      (fprintf to-stdin "(+ 17 44)\n")
      (flush-output-port to-stdin)
      (let ([s2 (get-line from-stdout)])
        (fprintf to-stdin "(reset-handler abort)\n")
        (fprintf to-stdin "(reset-handler)\n")
        (flush-output-port to-stdin)
        (let ([s3 (get-line from-stdout)])
          (close-port from-stdout)
          (fprintf to-stdin "'hello\n") ; should cause exception, then abort (via reset)
          (flush-output-port to-stdin)
          (let ([pid^ (machine-case
                        [(i3nt ti3nt a6nt ta6nt) pid]
                        [else ((foreign-procedure "waitpid" (int (* int) int) int) pid (make-ftype-pointer int 0) 0)])])
            (and
              (equal? s1 "Exception: oops 1")
              (equal? s2 "61")
              (equal? s3 "#<procedure abort>")
              (eqv? pid^ pid)))))))
)
