racket/collects/srfi/63/63.rkt
2010-12-31 15:59:39 -05:00

396 lines
14 KiB
Racket

;; Implementation of SRFI 63 "Homogeneous and Heterogeneous Arrays" for PLT
;; Scheme.
;; Copyright (C) 2007-2011 Chongkai Zhu
;; Released under the same terms as the SRFI reference implementation.
;; Parts of this file are based on SLIB "array.scm" Arrays for Scheme.
;; Copyright (C) 2001, 2003, 2005, 2006 Aubrey Jaffer
(module |63| mzscheme
(require srfi/4
mzlib/contract)
(define-syntax make-cvector
(syntax-rules ()
((_ make-fvector)
(case-lambda
((n)
(cons (make-fvector n)
(make-fvector n)))
((n fill)
(cons (make-fvector n (real-part fill))
(make-fvector n (imag-part fill))))))))
(define-syntax cvector-ref
(syntax-rules ()
((_ fvector-ref)
(lambda (v n)
(make-rectangular (fvector-ref (car v) n)
(fvector-ref (cdr v) n))))))
(define-syntax cvector-set!
(syntax-rules ()
((_ fvector-set!)
(lambda (v n z)
(fvector-set! (car v) n (real-part z))
(fvector-set! (cdr v) n (imag-part z))))))
(define make-c64vector
(make-cvector make-f64vector))
(define c64vector-ref
(cvector-ref f64vector-ref))
(define c64vector-set!
(cvector-set! f64vector-set!))
(define make-c32vector
(make-cvector make-f32vector))
(define c32vector-ref
(cvector-ref f32vector-ref))
(define c32vector-set!
(cvector-set! f32vector-set!))
(define (make-floc/c _) complex?)
(define (make-flor/c _) real?)
(define (make-fix/c n signed?)
(and/c exact?
(if signed?
(let ((x (arithmetic-shift 1 (sub1 n))))
(integer-in (- x) (sub1 x)))
(integer-in 0 (sub1 (arithmetic-shift 1 n))))))
(define implementations
(let ((table
(make-immutable-hash-table
(list (list 'char make-string string-ref string-set! char?)
(list 'byte make-bytes bytes-ref bytes-set! byte?)
(list 'vector make-vector vector-ref vector-set! any/c)
(list 'bool make-vector vector-ref vector-set! boolean?)
(list 'floC64b make-c64vector c64vector-ref c64vector-set! (make-floc/c 64))
(list 'floC32b make-c32vector c32vector-ref c32vector-set! (make-floc/c 32))
(list 'floR64b make-f64vector f64vector-ref f64vector-set! (make-flor/c 64))
(list 'floR32b make-f32vector f32vector-ref f32vector-set! (make-flor/c 32))
(list 'fixZ64b make-s64vector s64vector-ref s64vector-set! (make-fix/c 64 #t))
(list 'fixZ32b make-s32vector s32vector-ref s32vector-set! (make-fix/c 32 #t))
(list 'fixZ16b make-s16vector s16vector-ref s16vector-set! (make-fix/c 16 #t))
(list 'fixZ8b make-s8vector s8vector-ref s8vector-set! (make-fix/c 8 #t))
(list 'fixN64b make-u64vector u64vector-ref u64vector-set! (make-fix/c 64 #f))
(list 'fixN32b make-u32vector u32vector-ref u32vector-set! (make-fix/c 32 #f))
(list 'fixN16b make-u16vector u16vector-ref u16vector-set! (make-fix/c 16 #f))
(list 'fixN8b make-u8vector u8vector-ref u8vector-set! (make-fix/c 8 #f))))))
(lambda (type pos)
(list-ref (hash-table-get table type) pos))))
(define (array-print array port write?)
(display "#" port)
(display (length (my-array-ref array 0)) port)
(display "A" port)
(let ((type (my-array-ref array 4)))
(unless (eq? type 'vector)
(display ":" port)
(display type port)))
(display (array->list array) port))
(define-values (struct:array my-make-array my-array? my-array-ref my-array-set!)
(make-struct-type 'array #f 5 0 #f
(list (cons prop:custom-write array-print))
#f))
(define (array-dimensions array)
(cond ((vector? array) (list (vector-length array)))
((string? array) (list (string-length array)))
((bytes? array) (list (bytes-length array)))
(else (my-array-ref array 0))))
(define (array-scales obj)
(if (or (string? obj)
(bytes? obj)
(vector? obj))
'(1)
(my-array-ref obj 1)))
(define (array-offset obj)
(if (or (string? obj)
(bytes? obj)
(vector? obj))
0
(my-array-ref obj 2)))
(define (array-store obj)
(if (or (string? obj)
(bytes? obj)
(vector? obj))
obj
(my-array-ref obj 3)))
(define (array-store-type obj)
(cond ((string? obj) 'char)
((bytes? obj) 'byte)
((vector? obj) 'vector)
(else (my-array-ref obj 4))))
(define (array-store-maker array-type)
(implementations array-type 0))
(define (array-store-ref array)
(implementations (array-store-type array) 1))
(define (array-store-set array)
(implementations (array-store-type array) 2))
(define (array? obj)
(or (string? obj)
(bytes? obj)
(vector? obj)
(my-array? obj)))
(define (s:equal? obj1 obj2)
(or (equal? obj1 obj2)
(cond ((and (box? obj1)
(box? obj2))
(s:equal? (unbox obj1)
(unbox obj2)))
((and (pair? obj1)
(pair? obj2))
(and (s:equal? (car obj1) (car obj2))
(s:equal? (cdr obj1) (cdr obj2))))
((and (vector? obj1)
(vector? obj2))
(and (equal? (vector-length obj1) (vector-length obj2))
(let lp ((idx (sub1 (vector-length obj1))))
(or (negative? idx)
(and (s:equal? (vector-ref obj1 idx)
(vector-ref obj2 idx))
(lp (sub1 idx)))))))
((and (string? obj1)
(string? obj2))
(string=? obj1 obj2))
((and (array? obj1)
(array? obj2))
(and (equal? (array-dimensions obj1) (array-dimensions obj2))
(s:equal? (array->vector obj1) (array->vector obj2))))
((and (struct? obj1)
(struct? obj2))
(let-values (((obj1-type obj1-skipped?)
(struct-info obj1))
((obj2-type obj2-skipped?)
(struct-info obj2)))
(and (eq? obj1-type obj2-type)
(not obj1-skipped?)
(not obj2-skipped?)
(s:equal? (struct->vector obj1)
(struct->vector obj2)))))
(else #f))))
(define (array-rank obj)
(if (array? obj) (length (array-dimensions obj)) 0))
(define (make-array prototype . dimensions)
(let ((prot (array-store prototype))
(pdims (array-dimensions prototype))
(onedim? (eqv? 1 (length dimensions)))
(tcnt (apply * dimensions)))
(let ((initializer
(if (zero? (apply * pdims)) '()
(list ;; a list with single element at origin
(apply array-ref prototype
(map (lambda (x) 0) pdims))))))
(cond ((and onedim? (string? prot))
(apply make-string (car dimensions) initializer))
((and onedim? (vector? prot))
(apply make-vector (car dimensions) initializer))
(else
(let* ((store-type (array-store-type prototype))
(store (apply (array-store-maker store-type)
tcnt initializer)))
(let loop ((dims (reverse dimensions)) (scales '(1)))
(if (null? dims)
(my-make-array dimensions (cdr scales) 0
store
store-type)
(loop (cdr dims)
(cons (* (car dims) (car scales)) scales))))))))))
(define (make-shared-array array mapper . dimensions)
(define odl (array-scales array))
(define rank (length dimensions))
(define shape
(map (lambda (dim) (if (list? dim) dim (list 0 (sub1 dim)))) dimensions))
(do ((idx (sub1 rank) (sub1 idx))
(uvt (if (zero? rank)
'()
(append (cdr (vector->list (make-vector rank 0))) '(1)))
(append (cdr uvt) '(0)))
(uvts '() (cons uvt uvts)))
((negative? idx)
(let ((ker0 (apply + (map * odl (apply mapper uvt)))))
(my-make-array
(map (lambda (dim) (add1 (- (cadr dim) (car dim)))) shape)
(map (lambda (uvt) (- (apply + (map * odl (apply mapper uvt))) ker0))
uvts)
(apply +
(array-offset array)
(map * odl (apply mapper (map car shape))))
(array-store array)
(array-store-type array))))))
(define (list->array rank proto lst)
(define dimensions
(do ((shp '() (cons (length row) shp))
(row lst (car lst))
(rnk (sub1 rank) (sub1 rnk)))
((negative? rnk) (reverse shp))))
(let ((nra (apply make-array proto dimensions)))
(define (l2ra dims idxs row)
(cond ((null? dims)
(apply array-set! nra row (reverse idxs)))
((unless (eqv? (car dims) (length row))
(error 'list->array
"non-rectangular array ~a ~a"
dims dimensions))
(do ((idx 0 (add1 idx))
(row row (cdr row)))
((>= idx (car dims)))
(l2ra (cdr dims) (cons idx idxs) (car row))))))
(l2ra dimensions '() lst)
nra))
(define (array->list ra)
(define (ra2l dims idxs)
(if (null? dims)
(apply array-ref ra (reverse idxs))
(do ((lst '() (cons (ra2l (cdr dims) (cons idx idxs)) lst))
(idx (sub1 (car dims)) (sub1 idx)))
((negative? idx) lst))))
(ra2l (array-dimensions ra) '()))
(define (vector->array vect prototype . dimensions)
(define vdx (vector-length vect))
(let ((ra (apply make-array prototype dimensions)))
(define (v2ra dims idxs)
(cond ((null? dims)
(set! vdx (sub1 vdx))
(apply array-set! ra (vector-ref vect vdx) (reverse idxs)))
(else
(do ((idx (sub1 (car dims)) (sub1 idx)))
((negative? idx) vect)
(v2ra (cdr dims) (cons idx idxs))))))
(v2ra dimensions '())
ra))
(define (array->vector ra)
(define dims (array-dimensions ra))
(let* ((vdx (apply * dims))
(vect (make-vector vdx)))
(define (ra2v dims idxs)
(if (null? dims)
(let ((val (apply array-ref ra (reverse idxs))))
(set! vdx (sub1 vdx))
(vector-set! vect vdx val)
vect)
(do ((idx (sub1 (car dims)) (sub1 idx)))
((negative? idx) vect)
(ra2v (cdr dims) (cons idx idxs)))))
(ra2v dims '())))
(define (array-in-bounds? array . indices)
(do ((bnds (array-dimensions array) (cdr bnds))
(idxs indices (cdr idxs)))
((or (null? bnds)
(null? idxs)
(not (integer? (car idxs)))
(not (< -1 (car idxs) (car bnds))))
(and (null? bnds) (null? idxs)))))
(define (array-ref array . indices)
((array-store-ref array)
(array-store array)
(apply + (array-offset array) (map * (array-scales array) indices))))
(define (array-set! array obj . indices)
((array-store-set array)
(array-store array)
(apply + (array-offset array) (map * (array-scales array) indices))
obj))
(define (tag-maker array-type)
(case-lambda
(() (my-make-array
'(0) '(1) 0
((array-store-maker array-type) 0)
array-type))
((x) (my-make-array
'(1) '(1) 0
((array-store-maker array-type) 1 x)
array-type))))
(define (make-A:/c c)
(case-> (-> any)
(-> c any)))
(provide array?
array-rank
s:equal?)
(provide/contract
(array-dimensions (-> array? any))
(make-array
(->* (array?) (listof natural-number/c) any))
(make-shared-array
(->* (array?
(unconstrained-domain-> (listof natural-number/c)))
(listof natural-number/c)
any))
(list->array
(->r ((rank natural-number/c)
(proto array?)
(list (if (zero? rank)
any/c
list?)))
any))
(array->list
(-> array? any))
(array->vector
(-> array? any))
(array-in-bounds?
(->* (array?) (listof natural-number/c) any))
(array-set!
(->r ((array array?)
(val (implementations (array-store-type array) 3)))
indices (lambda _ (apply array-in-bounds? array indices))
any))
(array-ref
(->r ((array array?)) indices
(lambda _ (apply array-in-bounds? array indices))
any))
(vector->array
(->r ((vector vector?) (proto array?))
dimensions (lambda _ (eqv? (vector-length vector) (apply * dimensions)))
any)))
(define-syntax A:
(syntax-rules ()
((_ name label)
(begin (define name (tag-maker label))
(provide/contract
(name (make-A:/c (implementations label 3))))))))
(A: A:floC32b 'floC32b)
(A: A:floR64b 'floR64b)
(A: A:floR32b 'floR32b)
(A: A:fixZ64b 'fixZ64b)
(A: A:fixZ16b 'fixZ16b)
(A: A:fixZ32b 'fixZ32b)
(A: A:fixZ8b 'fixZ8b)
(A: A:fixN64b 'fixN64b)
(A: A:fixN32b 'fixN32b)
(A: A:fixN16b 'fixN16b)
(A: A:fixN8b 'fixN8b)
(A: A:bool 'bool)
)