diff --git a/collects/math/array.rkt b/collects/math/array.rkt index 39bfc288e0..5d90c239a1 100644 --- a/collects/math/array.rkt +++ b/collects/math/array.rkt @@ -50,4 +50,4 @@ Listof* Vectorof* Indexes - User-Indexes) + In-Indexes) diff --git a/collects/math/private/array/array-comprehension.rkt b/collects/math/private/array/array-comprehension.rkt index 965929a37b..fc5268b56a 100644 --- a/collects/math/private/array/array-comprehension.rkt +++ b/collects/math/private/array/array-comprehension.rkt @@ -18,7 +18,7 @@ (with-syntax ([(maybe-fill ...) (if (attribute fill-expr) #'(#:fill fill-expr) #'())] [(maybe-type ...) (if (attribute A) #'(: A) #'())]) (syntax/loc stx - (let*: ([ds : User-Indexes ds-expr] + (let*: ([ds : In-Indexes ds-expr] [ds : Indexes (check-array-shape ds (λ () (raise-argument-error 'name "Indexes" ds)))]) (define vs (for/vector #:length (array-shape-size ds) maybe-fill ... diff --git a/collects/math/private/array/array-pointwise.rkt b/collects/math/private/array/array-pointwise.rkt index 02223df23f..01d55a3091 100644 --- a/collects/math/private/array/array-pointwise.rkt +++ b/collects/math/private/array/array-pointwise.rkt @@ -1,63 +1,79 @@ #lang racket/base (require typed/untyped-utils - (rename-in - (only-in "typed-array-pointwise.rkt" - array-map - array-sqrt - array-log - array< - array<= - array> - array>= - array= - array-not - array-and - array-or - array-if) - [array-map typed:array-map]) + racket/math + (rename-in "typed-array-pointwise.rkt" + [array-map typed:array-map]) (rename-in "untyped-array-pointwise.rkt" [array-map untyped:array-map])) (define-typed/untyped-identifier array-map typed:array-map untyped:array-map) -(require/untyped-contract - (begin (require "array-struct.rkt")) - "typed-array-pointwise.rkt" - [array-abs ((Array Real) -> (Array Real))] - [array-round ((Array Real) -> (Array Real))] - [array-floor ((Array Real) -> (Array Real))] - [array-ceiling ((Array Real) -> (Array Real))] - [array-truncate ((Array Real) -> (Array Real))] - [array-conjugate ((Array Number) -> (Array Number))] - [array-magnitude ((Array Number) -> (Array Real))] - [array-angle ((Array Number) -> (Array Real))] - [array-sqr ((Array Number) -> (Array Number))] - [array-exp ((Array Number) -> (Array Number))] - [array-sin ((Array Number) -> (Array Number))] - [array-cos ((Array Number) -> (Array Number))] - [array-tan ((Array Number) -> (Array Number))] - [array-asin ((Array Number) -> (Array Number))] - [array-acos ((Array Number) -> (Array Number))] - [array-atan ((Array Number) -> (Array Number))] - [array+ ((Array Number) (Array Number) -> (Array Number))] - [array* ((Array Number) (Array Number) -> (Array Number))] - [array- (case-> ((Array Number) -> (Array Number)) - ((Array Number) (Array Number) -> (Array Number)))] - [array/ (case-> ((Array Number) -> (Array Number)) - ((Array Number) (Array Number) -> (Array Number)))] - [array-scale ((Array Number) Number -> (Array Number))] - [array-expt ((Array Number) (Array Number) -> (Array Number))] - [array-min ((Array Real) (Array Real) -> (Array Real))] - [array-max ((Array Real) (Array Real) -> (Array Real))] - [array-inexact->exact ((Array Number) -> (Array Exact-Number))] - [array-exact->inexact ((Array Number) -> (Array Number))] ; should be Number -> Inexact-Number - [array-real->double-flonum ((Array Real) -> (Array Float))] - [array-number->float-complex ((Array Number) -> (Array Float-Complex))] - [array-real-part ((Array Number) -> (Array Real))] - [array-imag-part ((Array Number) -> (Array Real))] - [array-make-rectangular ((Array Real) (Array Real) -> (Array Number))]) +(define-syntax-rule (define-array-op1 name op) + (define-syntax-rule (name arr) (array-map op arr))) + +(define-syntax-rule (define-array-op2 name op) + (define-syntax-rule (name arr0 arr1) (array-map op arr0 arr1))) + +(define-syntax-rule (define-array-op1+ name op) + (define-syntax-rule (name arr0 arrs (... ...)) (array-map op arr0 arrs (... ...)))) + +(define-syntax-rule (define-array-op2+ name op) + (define-syntax-rule (name arr0 arr1 arrs (... ...)) (array-map op arr0 arr1 arrs (... ...)))) + +(define-syntax-rule (define-array-op name op) + (define-syntax-rule (name arrs (... ...)) (array-map op arrs (... ...)))) + +(define-syntax-rule (array-scale arr x) + (inline-array-map (λ (y) (* x y)) arr)) + +(define-array-op1 array-abs abs) +(define-array-op1 array-round round) +(define-array-op1 array-floor floor) +(define-array-op1 array-ceiling ceiling) +(define-array-op1 array-truncate truncate) +(define-array-op1 array-conjugate conjugate) +(define-array-op1 array-magnitude magnitude) +(define-array-op1 array-angle angle) +(define-array-op1 array-sqrt sqrt) +(define-array-op1 array-log log) +(define-array-op1 array-sqr sqr) +(define-array-op1 array-exp exp) +(define-array-op1 array-sin sin) +(define-array-op1 array-cos cos) +(define-array-op1 array-tan tan) +(define-array-op1 array-asin asin) +(define-array-op1 array-acos acos) +(define-array-op1 array-atan atan) +(define-array-op1 array-inexact->exact inexact->exact) +(define-array-op1 array-exact->inexact exact->inexact) +(define-array-op1 array-fl real->double-flonum) +(define-array-op1 array-fc number->float-complex) +(define-array-op1 array-real-part real-part) +(define-array-op1 array-imag-part imag-part) +(define-array-op2 array-make-rectangular make-rectangular) + +(define-array-op array+ +) +(define-array-op array* *) +(define-array-op1+ array- -) +(define-array-op1+ array/ /) + +(define-array-op2 array-expt expt) +(define-array-op1+ array-min min) +(define-array-op1+ array-max max) + +(define-array-op2+ array< <) +(define-array-op2+ array<= <=) +(define-array-op2+ array> >) +(define-array-op2+ array>= >=) +(define-array-op2+ array= =) + +(define-array-op2 array-not not) + +(define-syntax-rule (array-and arrs ...) (inline-array-map and arrs ...)) +(define-syntax-rule (array-or arrs ...) (inline-array-map or arrs ...)) +(define-syntax-rule (array-if arr0 arr1 arr2) (inline-array-map if arr0 arr1 arr2)) (provide ;; Mapping @@ -102,8 +118,8 @@ ;; Number conversions array-inexact->exact array-exact->inexact - array-real->double-flonum - array-number->float-complex + array-fl + array-fc array-real-part array-imag-part array-make-rectangular) diff --git a/collects/math/private/array/array-print.rkt b/collects/math/private/array/array-print.rkt index 670bd7d156..6bfd51de63 100644 --- a/collects/math/private/array/array-print.rkt +++ b/collects/math/private/array/array-print.rkt @@ -3,6 +3,7 @@ ;; Defines the custom printer used for array values (require racket/pretty + racket/fixnum "array-struct.rkt" "utils.rkt") @@ -66,24 +67,24 @@ (define: js : Indexes (make-vector dims 0)) ;; For each shape axis (let i-loop ([#{i : Nonnegative-Fixnum} 0]) - (cond [(i . < . dims) ; proves i : Index - (write-string "[" port) + (cond [(i . fx< . dims) ; proves i : Index + (write-string "#[" port) (define di (vector-ref ds i)) ; length of axis i ;; For each index on this axis (let ji-loop ([#{ji : Nonnegative-Fixnum} 0]) - (when (ji . < . di) ; proves ji : Index + (when (ji . fx< . di) ; proves ji : Index (vector-set! js i ji) ;; Print either nested elements or the element here - (i-loop (+ i 1)) + (i-loop (fx+ i 1)) ;; Print delimiter when not printing the last element on this axis - (when (ji . < . (- di 1)) - (cond [(and (eq? layout 'compact) (= i (- dims 1))) + (when (ji . fx< . (fx- di 1)) + (cond [(and (eq? layout 'compact) (fx= i (fx- dims 1))) ;; Keep elements on one line in compact layout (write-string " " port)] [else - ;; +1 to indent past "(", +1 to indent past the first "[", and `i' axes - (maybe-print-newline (+ 2 i))])) - (ji-loop (+ ji 1)))) + ;; +1 to indent past "(", +2 to indent past the first "#[", and `i' axes + (maybe-print-newline (+ 3 (* i 2)))])) + (ji-loop (fx+ ji 1)))) (write-string "]" port)] [else ;; Print an element diff --git a/collects/math/private/array/array-sequence.rkt b/collects/math/private/array/array-sequence.rkt index aeef7026f1..6c1c7c1f85 100644 --- a/collects/math/private/array/array-sequence.rkt +++ b/collects/math/private/array/array-sequence.rkt @@ -5,6 +5,7 @@ typed-racket/base-env/prims racket/unsafe/ops "array-struct.rkt" + "utils.rkt" (except-in "typed-array-sequence.rkt" in-array-indexes)) (require/untyped-contract @@ -56,7 +57,7 @@ [(x) (:do-in ([(ds size dims js) - (let*: ([ds : User-Indexes ds-expr] + (let*: ([ds : In-Indexes ds-expr] [ds : Indexes (check-array-shape ds (λ () (raise-argument-error 'in-array-indexes "Indexes" ds)))]) diff --git a/collects/math/private/array/array-struct.rkt b/collects/math/private/array/array-struct.rkt index 8f07efcb72..a2613e3df4 100644 --- a/collects/math/private/array/array-struct.rkt +++ b/collects/math/private/array/array-struct.rkt @@ -46,6 +46,4 @@ (syntax-parse stx [(_ e:expr) (syntax/loc stx (array/syntax array list flat-list->array e))] - [(_ e:expr T:expr) - (syntax/loc stx (array/syntax array list (inst flat-list->array T) e))] [_:id (raise-syntax-error 'array "not allowed as an expression" stx)])) diff --git a/collects/math/private/array/array-syntax.rkt b/collects/math/private/array/array-syntax.rkt index a5dfb578fc..9d3674eed0 100644 --- a/collects/math/private/array/array-syntax.rkt +++ b/collects/math/private/array/array-syntax.rkt @@ -4,41 +4,40 @@ (provide array/syntax) -(define-for-syntax (square-bracket? e-stx) - (eq? #\[ (syntax-property e-stx 'paren-shape))) +(define-for-syntax (syntax-vector-shape e-stx) + (syntax-case e-stx () + [#[] (list 0)] + [#[e0 e ...] + (let ([lst (syntax->list #'(e0 e ...))]) + (define d (length lst)) + (define ds (syntax-vector-shape (car lst))) + (if ds + (let loop ([lst (cdr lst)]) + (cond [(null? lst) (cons d ds)] + [(equal? ds (syntax-vector-shape (car lst))) + (loop (cdr lst))] + [else #f])) + #f))] + [_ null])) -(define-for-syntax (syntax-list-shape e-stx) - (define lst (syntax->list e-stx)) - (cond [(or (not lst) (not (square-bracket? e-stx))) null] - [(null? lst) (list 0)] - [else - (define d (length lst)) - (define ds (syntax-list-shape (car lst))) - (if ds - (let loop ([lst (cdr lst)]) - (cond [(null? lst) (cons d ds)] - [(equal? ds (syntax-list-shape (car lst))) - (loop (cdr lst))] - [else #f])) - #f)])) - -(define-for-syntax (syntax-list-flatten e-stx) +(define-for-syntax (syntax-vector-flatten e-stx) (reverse (let loop ([e-stx e-stx] [acc null]) - (define lst (syntax->list e-stx)) - (cond [(and lst (square-bracket? e-stx)) - (for/fold ([acc acc]) ([lst (in-list lst)]) - (loop lst acc))] - [else - (cons e-stx acc)])))) + (syntax-case e-stx () + [#[e ...] + (let ([lst (syntax->list #'(e ...))]) + (for/fold ([acc acc]) ([lst (in-list lst)]) + (loop lst acc)))] + [else + (cons e-stx acc)])))) (define-syntax (array/syntax stx) (syntax-case stx () [(_ orig-name constr ->array e) - (let ([ds (syntax-list-shape #'e)]) + (let ([ds (syntax-vector-shape #'e)]) (unless ds (raise-syntax-error (syntax->datum #'orig-name) "expected rectangular data" stx #'e)) (with-syntax ([(d ...) ds] - [(v ...) (syntax-list-flatten #'e)]) + [(v ...) (syntax-vector-flatten #'e)]) (syntax/loc stx (->array (vector d ...) (constr v ...)))))])) diff --git a/collects/math/private/array/mutable-array.rkt b/collects/math/private/array/mutable-array.rkt index c33e4b8a9f..048df3182b 100644 --- a/collects/math/private/array/mutable-array.rkt +++ b/collects/math/private/array/mutable-array.rkt @@ -1,6 +1,7 @@ #lang racket/base (require typed/untyped-utils + typed/racket/base (for-syntax racket/base syntax/parse) "array-syntax.rkt" (except-in "typed-mutable-array.rkt" @@ -26,9 +27,9 @@ flat-vector->matrix) (define-syntax (mutable-array stx) - (syntax-parse stx + (syntax-parse stx #:literals (:) [(_ e:expr) (syntax/loc stx (array/syntax mutable-array vector make-mutable-array e))] - [(_ e:expr T:expr) + [(_ e:expr : T:expr) (syntax/loc stx (array/syntax mutable-array (inst vector T) make-mutable-array e))] [_:id (raise-syntax-error 'mutable-array "not allowed as an expression" stx)])) diff --git a/collects/math/private/array/typed-array-constructors.rkt b/collects/math/private/array/typed-array-constructors.rkt index 8d447faa73..5e50d26d32 100644 --- a/collects/math/private/array/typed-array-constructors.rkt +++ b/collects/math/private/array/typed-array-constructors.rkt @@ -6,13 +6,13 @@ (provide (all-defined-out)) -(: make-array (All (A) (User-Indexes A -> (Array A)))) +(: make-array (All (A) (In-Indexes A -> (Array A)))) (define (make-array ds v) (let ([ds (check-array-shape ds (λ () (raise-argument-error 'make-array "(Vectorof Index)" 0 ds v)))]) (unsafe-build-array ds (λ (js) v)))) -(: axis-index-array (User-Indexes Integer -> (Array Index))) +(: axis-index-array (In-Indexes Integer -> (Array Index))) (define (axis-index-array ds k) (let* ([ds (check-array-shape ds (λ () (raise-argument-error 'axis-index-array "(Vectorof Index)" 0 ds k)))] @@ -21,14 +21,14 @@ (unsafe-build-array ds (λ: ([js : Indexes]) (unsafe-vector-ref js k)))] [else (raise-argument-error 'axis-index-array (format "Index < ~a" dims) 1 ds k)]))) -(: index-array (User-Indexes -> (Array Index))) +(: index-array (In-Indexes -> (Array Index))) (define (index-array ds) (let ([ds (check-array-shape ds (λ () (raise-argument-error 'index-array "(Vectorof Index)" ds)))]) (unsafe-build-array ds (λ: ([js : Indexes]) (assert (unsafe-array-index->value-index ds js) index?))))) -(: indexes-array (User-Indexes -> (Array Indexes))) +(: indexes-array (In-Indexes -> (Array Indexes))) (define (indexes-array ds) (let ([ds (check-array-shape ds (λ () (raise-argument-error 'indexes-array "(Vectorof Index)" ds)))]) diff --git a/collects/math/private/array/typed-array-indexing.rkt b/collects/math/private/array/typed-array-indexing.rkt index f2107129dd..2753fc72ed 100644 --- a/collects/math/private/array/typed-array-indexing.rkt +++ b/collects/math/private/array/typed-array-indexing.rkt @@ -27,11 +27,11 @@ (define (unsafe-array-set! arr js v) ((unsafe-settable-array-set-proc arr) js v)) - (: array-ref (All (A) ((Array A) User-Indexes -> A))) + (: array-ref (All (A) ((Array A) In-Indexes -> A))) (define (array-ref arr js) ((unsafe-array-proc arr) (check-array-indexes 'array-ref (array-shape arr) js))) - (: array-set! (All (A) ((Settable-Array A) User-Indexes A -> Void))) + (: array-set! (All (A) ((Settable-Array A) In-Indexes A -> Void))) (define (array-set! arr js v) (define ds (array-shape arr)) (define set-proc (unsafe-settable-array-set-proc arr)) @@ -42,13 +42,13 @@ ;; =================================================================================================== ;; Indexing using array of indexes -(: array-indexes-ref (All (A) ((Array A) (Array User-Indexes) -> (Array A)))) +(: array-indexes-ref (All (A) ((Array A) (Array In-Indexes) -> (Array A)))) (define (array-indexes-ref arr idxs) (define ds (array-shape idxs)) (define idxs-proc (unsafe-array-proc idxs)) (unsafe-build-array ds (λ: ([js : Indexes]) (array-ref arr (idxs-proc js))))) -(: array-indexes-set! (All (A) ((Settable-Array A) (Array User-Indexes) (Array A) -> Void))) +(: array-indexes-set! (All (A) ((Settable-Array A) (Array In-Indexes) (Array A) -> Void))) (define (array-indexes-set! arr idxs vals) (define ds (array-shape-broadcast (list (array-shape idxs) (array-shape vals)))) (let ([idxs (array-broadcast idxs ds)] @@ -230,14 +230,14 @@ ;; number of indexes should match (define num-specs (length slices)) (unless (= dims num-specs) - (error 'array-slice-ref "expected list with ~e slices; given ~e in ~e" + (error 'array-slice-ref "expected list with ~e slice specifications; given ~e in ~e" dims num-specs orig-slices)) (let-values ([(arr jss) (slices->array-axis-transform 'array-slice-ref arr slices)]) (for/fold ([arr (unsafe-array-axis-transform arr jss)]) ([na (in-list new-axes)]) (match-define (cons k dk) na) (array-axis-insert arr k dk))))) -(: slice-indexes-array (User-Indexes (Listof Slice-Spec) -> (Array Indexes))) +(: slice-indexes-array (In-Indexes (Listof Slice-Spec) -> (Array Indexes))) (define (slice-indexes-array ds slices) (array-slice-ref (indexes-array ds) slices)) diff --git a/collects/math/private/array/typed-array-pointwise.rkt b/collects/math/private/array/typed-array-pointwise.rkt index 5a9ea06391..ffd2c2a50d 100644 --- a/collects/math/private/array/typed-array-pointwise.rkt +++ b/collects/math/private/array/typed-array-pointwise.rkt @@ -1,18 +1,16 @@ #lang typed/racket/base -(require (only-in racket/math conjugate) - (for-syntax racket/base) - "array-struct.rkt" +(require "array-struct.rkt" "array-broadcast.rkt" "utils.rkt" (only-in "untyped-array-pointwise.rkt" inline-array-map)) -(provide (all-defined-out)) +(provide array-map) (: array-map (All (R A B T ...) - (case-> ((-> R) -> (Array R)) - ((A -> R) (Array A) -> (Array R)) - ((A B T ... T -> R) (Array A) (Array B) (Array T) ... T -> (Array R))))) + (case-> ((-> R) -> (Array R)) + ((A -> R) (Array A) -> (Array R)) + ((A B T ... T -> R) (Array A) (Array B) (Array T) ... T -> (Array R))))) (define array-map (case-lambda: [([f : (-> R)]) @@ -34,292 +32,3 @@ (unsafe-build-array ds (λ: ([js : Indexes]) (apply f (g0 js) (g1 js) (map (λ: ([g : (Indexes -> T)]) (g js)) gs)))))])) - -;; =================================================================================================== -;; Pointwise operation types - -(define-syntax (declare-case-type stx) - (syntax-case stx (->) - [(_ name [(A ... -> B) ...]) - (syntax/loc stx - (: name (case-> ((Array A) ... -> (Array B)) ...)))])) - -(define-syntax-rule (declare-case-types (name ...) Ts) - (begin (declare-case-type name Ts) ...)) - -(declare-case-types - (array-abs) - [(Integer -> Integer) - (Exact-Rational -> Exact-Rational) - (Float -> Float) - (Real -> Real)]) - -(declare-case-types - (array-round array-floor array-ceiling array-truncate) - [(Integer -> Integer) - (Exact-Rational -> Integer) - (Float -> Float) - (Real -> Real)]) - -(declare-case-types - (array-sqrt array-log) - [(Number -> Number)]) - -(declare-case-types - (array-conjugate array-sqr) - [(Integer -> Integer) - (Exact-Rational -> Exact-Rational) - (Float -> Float) - (Real -> Real) - (Float-Complex -> Float-Complex) - (Number -> Number)]) - -(declare-case-types - (array-magnitude) - [;(Integer -> Integer) ; should be allowed - (Exact-Rational -> Exact-Rational) - (Float -> Real) ; should be Float -> Float - (Real -> Real) - (Float-Complex -> Float) - (Number -> Real)]) - -(declare-case-types - (array-angle) - [(Real -> Real) - (Float-Complex -> Float) - (Number -> Real)]) - -(declare-case-types - (array-exp array-sin array-cos array-tan array-asin array-acos array-atan) - [(Float -> Float) - (Real -> Real) - (Float-Complex -> Float-Complex) - (Number -> Number)]) - -(declare-case-types - (array+) - [(Integer Integer -> Integer) - (Exact-Rational Exact-Rational -> Exact-Rational) - (Float Float -> Float) - (Real Float -> Float) - (Float Real -> Float) - (Real Real -> Real) - (Float-Complex Float-Complex -> Float-Complex) - (Float-Complex Number -> Float-Complex) - (Number Float-Complex -> Float-Complex) - (Number Number -> Number)]) - -(declare-case-types - (array*) - [(Integer Integer -> Integer) - (Exact-Rational Exact-Rational -> Exact-Rational) - (Float Float -> Float) - (Real Real -> Real) - (Float-Complex Float-Complex -> Float-Complex) - (Number Number -> Number)]) - -(declare-case-types - (array-) - [(Integer -> Integer) - (Exact-Rational -> Exact-Rational) - (Float -> Float) - (Real -> Real) - ;(Float-Complex -> Float-Complex) ; should be allowed - (Number -> Number) - (Integer Integer -> Integer) - (Exact-Rational Exact-Rational -> Exact-Rational) - (Float Float -> Float) - (Real Float -> Float) - (Float Real -> Float) - (Real Real -> Real) - (Float-Complex Float-Complex -> Float-Complex) - (Float-Complex Number -> Float-Complex) - (Number Float-Complex -> Float-Complex) - (Number Number -> Number)]) - -(declare-case-types - (array/) - [(Exact-Rational -> Exact-Rational) - (Float -> Float) - (Real -> Real) - ;(Float-Complex -> Float-Complex) ; should be allowed - (Number -> Number) - (Exact-Rational Exact-Rational -> Exact-Rational) - (Float Float -> Float) - (Float Real -> Float) - (Real Real -> Real) - (Float-Complex Float-Complex -> Float-Complex) - ;(Float-Complex Number -> Float-Complex) ; should be allowed - (Number Number -> Number)]) - -(: array-scale - (case-> - ((Array Integer) Integer -> (Array Integer)) - ((Array Exact-Rational) Exact-Rational -> (Array Exact-Rational)) - ((Array Float) Float -> (Array Float)) - ((Array Real) Real -> (Array Real)) - ((Array Float-Complex) Float-Complex -> (Array Float-Complex)) - ((Array Number) Number -> (Array Number)))) - -(declare-case-types - (array-expt) - [(Integer Integer -> Exact-Rational) - (Exact-Rational Integer -> Exact-Rational) - ;(Float Float -> Float-Complex) ; should be allowed - (Real Real -> Number) - (Float-Complex Float-Complex -> Float-Complex) - (Number Number -> Number)]) - -(declare-case-types - (array-min array-max) - [(Integer Integer -> Integer) - (Exact-Rational Exact-Rational -> Exact-Rational) - (Float Float -> Float) - (Real Real -> Real)]) - -(: array= ((Array Number) (Array Number) -> (Array Boolean))) - -(declare-case-types - (array< array<= array> array>=) - [(Real Real -> Boolean)]) - -(: array-not ((Array Any) -> (Array Boolean))) -(: array-and (All (A B) ((Array A) (Array B) -> (Array (U B #f))))) -(: array-or (All (A) ((Array A) (Array A) -> (Array A)))) -(: array-if (All (A) ((Array Any) (Array A) (Array A) -> (Array A)))) - -(declare-case-types - (array-inexact->exact) - [(Real -> Exact-Rational) - (Number -> Exact-Number)]) - -(declare-case-types - (array-exact->inexact) - [(Integer -> Float) - (Exact-Rational -> Float) - (Float -> Float) - (Real -> Inexact-Real) - (Float-Complex -> Float-Complex) - ;(Exact-Number -> Float-Complex) ; should be allowed - (Number -> Number) ; should be Number -> Inexact-Number - ]) - -(: array-real->double-flonum ((Array Real) -> (Array Float))) -(: array-number->float-complex ((Array Number) -> (Array Float-Complex))) - -(declare-case-types - (array-real-part) - [;(Integer -> Integer) ; should be allowed - (Exact-Rational -> Exact-Rational) - ;(Float -> Float) ; should be allowed - (Real -> Real) - (Float-Complex -> Float) - (Number -> Real)]) - -(declare-case-types - (array-imag-part) - [;(Real -> Zero) ; should be allowed - (Real -> Real) - (Float-Complex -> Float) - (Number -> Real)]) - -(declare-case-types - (array-make-rectangular) - [(Exact-Rational Exact-Rational -> Exact-Number) - (Float Float -> Float-Complex) - (Float Real -> Float-Complex) - (Real Float -> Float-Complex) - (Real Real -> Number)]) - -;; =================================================================================================== -;; Pointwise operations - -#| -The lift operators could be just functions, but then it wouldn't be possible to give the results more -precise types. For example, if `array-lift1' were a higher-order function, (array-lift1 exp) could -only have the type - - ((Array Number) -> (Array Number)) - -or the type - - ((Array Real) -> (Array Real)) - -Since `array-lift1' is a macro, (array-lift1 exp) can have the type - - (case-> ((Array Real) -> (Array Real)) - ((Array Number) -> (Array Number))) - -IOW, the macro lift operators allow us to have array-exp do the job of both array-real-exp and -array-number-exp. -|# - -(define-syntax (array-lift1 stx) - (syntax-case stx () - [(_ f) (syntax/loc stx (λ (arr) (inline-array-map f arr)))])) - -(define-syntax (array-lift2 stx) - (syntax-case stx () - [(_ f) (syntax/loc stx (λ (arr1 arr2) (inline-array-map f arr1 arr2)))])) - -(define-syntax (array-lift3 stx) - (syntax-case stx () - [(_ f) (syntax/loc stx (λ (arr1 arr2 arr3) (inline-array-map f arr1 arr2 arr3)))])) - -(define array-abs (array-lift1 abs)) -(define array-round (array-lift1 round)) -(define array-floor (array-lift1 floor)) -(define array-ceiling (array-lift1 ceiling)) -(define array-truncate (array-lift1 truncate)) - -(define array-sqr (array-lift1 (λ (z) (* z z)))) -(define array-sqrt (array-lift1 sqrt)) -(define array-conjugate (array-lift1 conjugate)) -(define array-magnitude (array-lift1 magnitude)) -(define array-angle (array-lift1 angle)) -(define array-log (array-lift1 log)) -(define array-exp (array-lift1 exp)) -(define array-sin (array-lift1 sin)) -(define array-cos (array-lift1 cos)) -(define array-tan (array-lift1 tan)) -(define array-asin (array-lift1 asin)) -(define array-acos (array-lift1 acos)) -(define array-atan (array-lift1 atan)) - -(define array+ (array-lift2 +)) -(define array* (array-lift2 *)) - -(define array- - (case-lambda - [(arr) (inline-array-map - arr)] - [(arr1 arr2) (inline-array-map - arr1 arr2)])) - -(define array/ - (case-lambda - [(arr) (inline-array-map / arr)] - [(arr1 arr2) (inline-array-map / arr1 arr2)])) - -(define (array-scale arr s) ((array-lift1 (λ (x) (* s x))) arr)) - -(define array-expt (array-lift2 expt)) -(define array-min (array-lift2 min)) -(define array-max (array-lift2 max)) - -(define array= (array-lift2 =)) -(define array< (array-lift2 <)) -(define array<= (array-lift2 <=)) -(define array> (array-lift2 >)) -(define array>= (array-lift2 >=)) - -(define array-not (array-lift1 not)) -(define array-and (array-lift2 and)) -(define array-if (array-lift3 if)) -(define array-or (array-lift2 or)) - -(define array-inexact->exact (array-lift1 inexact->exact)) -(define array-exact->inexact (array-lift1 exact->inexact)) -(define array-real->double-flonum (array-lift1 real->double-flonum)) -(define array-number->float-complex (array-lift1 (λ: ([x : Number]) (+ x 0.0+0.0i)))) -(define array-real-part (array-lift1 real-part)) -(define array-imag-part (array-lift1 imag-part)) -(define array-make-rectangular (array-lift2 make-rectangular)) diff --git a/collects/math/private/array/typed-array-sequence.rkt b/collects/math/private/array/typed-array-sequence.rkt index d8c23bd096..faae7aaa6c 100644 --- a/collects/math/private/array/typed-array-sequence.rkt +++ b/collects/math/private/array/typed-array-sequence.rkt @@ -33,7 +33,7 @@ ;; =================================================================================================== ;; Sequence of indexes -(: in-array-indexes (User-Indexes -> (Sequenceof Indexes))) +(: in-array-indexes (In-Indexes -> (Sequenceof Indexes))) (define (in-array-indexes ds) (let: ([ds : Indexes (check-array-shape ds (λ () (raise-argument-error 'in-array-indexes "Indexes" ds)))]) diff --git a/collects/math/private/array/typed-array-struct.rkt b/collects/math/private/array/typed-array-struct.rkt index c48f58077c..087d10bb0d 100644 --- a/collects/math/private/array/typed-array-struct.rkt +++ b/collects/math/private/array/typed-array-struct.rkt @@ -47,6 +47,12 @@ size ds)]))] [else (values (vector->immutable-vector ds) size strict? proc)])) +#| +(: array-procedure (All (A) ((Array A) In-Indexes -> A))) +(define (array-procedure arr js) + ((Array-unsafe-proc arr) (check-array-indexes 'array-ref (Array-shape arr) js))) +|# + (struct: (A) Array ([shape : Indexes] [size : Index] [strict? : Boolean] @@ -55,6 +61,8 @@ #:property prop:custom-print-quotable 'never #:property prop:custom-write (λ (arr port mode) ((array-custom-printer) arr 'array port mode)) #:property prop:equal+hash (list array-recur-equal? array-hash-code array-hash-code) + ;; It would be really nice to do this, but TR can't right now: + ;#:property prop:procedure array-procedure ) (define-syntax-rule (make-unsafe-array-proc ds ref) @@ -65,7 +73,7 @@ (begin-encourage-inline (define (array-dims arr) (vector-length (Array-shape arr)))) -(: build-array (All (A) (User-Indexes (Indexes -> A) -> (Array A)))) +(: build-array (All (A) (In-Indexes (Indexes -> A) -> (Array A)))) (define (build-array ds proc) (let ([ds (check-array-shape ds (λ () (raise-argument-error 'build-array "(Vectorof Index)" 0 ds proc)))]) @@ -147,7 +155,7 @@ (define lst null) (for-each-array-index ds (λ (js) (set! lst (cons (proc js) lst)))) - (write-string "[" port) + (write-string "#[" port) (unless (null? lst) (let ([lst (reverse lst)]) (recur-print (car lst) port) diff --git a/collects/math/private/array/typed-array-transform.rkt b/collects/math/private/array/typed-array-transform.rkt index e30cadb0dd..063b4c1006 100644 --- a/collects/math/private/array/typed-array-transform.rkt +++ b/collects/math/private/array/typed-array-transform.rkt @@ -11,7 +11,7 @@ ;; =================================================================================================== ;; Arbitrary transforms -(: array-transform (All (A) ((Array A) User-Indexes (Indexes -> User-Indexes) -> (Array A)))) +(: array-transform (All (A) ((Array A) In-Indexes (Indexes -> In-Indexes) -> (Array A)))) (define (array-transform arr new-ds idx-fun) (define old-ds (array-shape arr)) (define old-f (unsafe-array-proc arr)) @@ -111,7 +111,7 @@ ;; =================================================================================================== ;; Reshape -(: array-reshape (All (A) ((Array A) User-Indexes -> (Array A)))) +(: array-reshape (All (A) ((Array A) In-Indexes -> (Array A)))) (define (array-reshape arr ds) (let ([ds (check-array-shape ds (λ () (raise-argument-error 'array-reshape "(Vectorof Index)" 1 arr ds)))]) diff --git a/collects/math/private/array/typed-mutable-array.rkt b/collects/math/private/array/typed-mutable-array.rkt index cf8e03bf97..66d630195c 100644 --- a/collects/math/private/array/typed-mutable-array.rkt +++ b/collects/math/private/array/typed-mutable-array.rkt @@ -23,7 +23,7 @@ (define set-proc (make-unsafe-array-set-proc A ds (λ (j v) (unsafe-vector-set! vs j v)))) (Mutable-Array ds (vector-length vs) #t proc set-proc vs)) -(: make-mutable-array (All (A) (User-Indexes (Vectorof A) -> (Mutable-Array A)))) +(: make-mutable-array (All (A) (In-Indexes (Vectorof A) -> (Mutable-Array A)))) (define (make-mutable-array ds vs) (let* ([ds (check-array-shape ds (λ () (raise-argument-error 'make-mutable-array "(Vectorof Index)" 0 ds vs)))] diff --git a/collects/math/private/array/typed-utils.rkt b/collects/math/private/array/typed-utils.rkt index 2529c511d0..5444c7d272 100644 --- a/collects/math/private/array/typed-utils.rkt +++ b/collects/math/private/array/typed-utils.rkt @@ -12,7 +12,7 @@ (define-type (Vectorof* A) (Rec T (U A (Vectorof T)))) (define-type Indexes (Vectorof Index)) -(define-type User-Indexes (U (Vectorof Integer) Indexes)) +(define-type In-Indexes (U (Vectorof Integer) Indexes)) (begin-encourage-inline @@ -37,7 +37,7 @@ (loop (+ i 1) (* n d))] [else n]))) - (: check-array-shape (User-Indexes (-> Nothing) -> Indexes)) + (: check-array-shape (In-Indexes (-> Nothing) -> Indexes)) (define (check-array-shape ds fail) (define dims (vector-length ds)) (define: new-ds : Indexes (make-vector dims 0)) @@ -77,12 +77,12 @@ (define: empty-vectorof-index : Indexes #()) -(: raise-array-index-error (Symbol Indexes User-Indexes -> Nothing)) +(: raise-array-index-error (Symbol Indexes In-Indexes -> Nothing)) (define (raise-array-index-error name ds js) (error name "expected indexes for shape ~e; given ~e" (vector->list ds) js)) -(: array-index->value-index (Symbol Indexes User-Indexes -> Nonnegative-Fixnum)) +(: array-index->value-index (Symbol Indexes In-Indexes -> Nonnegative-Fixnum)) (define (array-index->value-index name ds js) (define (raise-index-error) (raise-array-index-error name ds js)) (define dims (vector-length ds)) @@ -96,7 +96,7 @@ [else (raise-index-error)])] [else j]))) -(: check-array-indexes (Symbol Indexes User-Indexes -> Indexes)) +(: check-array-indexes (Symbol Indexes In-Indexes -> Indexes)) (define (check-array-indexes name ds js) (define (raise-index-error) (raise-array-index-error name ds js)) (define dims (vector-length ds)) diff --git a/collects/math/private/array/untyped-array-convert.rkt b/collects/math/private/array/untyped-array-convert.rkt index 9e41126340..a7a6497e76 100644 --- a/collects/math/private/array/untyped-array-convert.rkt +++ b/collects/math/private/array/untyped-array-convert.rkt @@ -17,7 +17,7 @@ (define (maybe-list->vector vs) (and vs (list->vector vs))) - (: vector-shape (All (A) ((Vectorof* A) ((Vectorof* A) -> Boolean : A) + (: vector-shape (All (A) ((Vectorof* A) ((Vectorof* A) -> Any : A) -> (U #f (Vectorof Integer))))) (define (vector-shape vec pred?) (maybe-list->vector @@ -36,7 +36,7 @@ [else #f])) #f)])])))) - (: list-shape (All (A) ((Listof* A) ((Listof* A) -> Boolean : A) -> (U #f (Vectorof Integer))))) + (: list-shape (All (A) ((Listof* A) ((Listof* A) -> Any : A) -> (U #f (Vectorof Integer))))) (define (list-shape lst pred?) (maybe-list->vector (let: list-shape : (U #f (Listof Integer)) ([lst : (Listof* A) lst]) @@ -56,7 +56,7 @@ ;; =============================================================================================== ;; Conversion to arrays - (: first* (All (A) ((Listof* A) ((Listof* A) -> Boolean : A) -> A))) + (: first* (All (A) ((Listof* A) ((Listof* A) -> Any : A) -> A))) (define (first* lst pred?) (let/ec: return : A (let: loop : A ([lst : (Listof* A) lst]) @@ -65,7 +65,7 @@ (loop lst)) (error 'first* "no first* element")])))) - (: list*->flat-vector (All (A) ((Listof* A) Integer ((Listof* A) -> Boolean : A) -> (Vectorof A)))) + (: list*->flat-vector (All (A) ((Listof* A) Integer ((Listof* A) -> Any : A) -> (Vectorof A)))) (define (list*->flat-vector lst size pred?) (cond [(zero? size) (vector)] [else @@ -77,7 +77,7 @@ (loop lst i))])) vec])) - (: list*->array (All (A) ((Listof* A) ((Listof* A) -> Boolean : A) -> (Array A)))) + (: list*->array (All (A) ((Listof* A) ((Listof* A) -> Any : A) -> (Array A)))) (define (list*->array lst pred?) (define (raise-shape-error) ;; don't have to worry about non-Index size - can't fit in memory anyway @@ -90,7 +90,7 @@ (unsafe-mutable-array ds (list*->flat-vector lst size pred?)))] [else (raise-shape-error)])) - (: vector*->array (All (A) ((Vectorof* A) (Any -> Boolean : A) -> (Array A)))) + (: vector*->array (All (A) ((Vectorof* A) ((Vectorof* A) -> Any : A) -> (Array A)))) (define (vector*->array vec pred?) (define (raise-shape-error) ;; don't have to worry about non-Index size - can't fit in memory anyway diff --git a/collects/math/private/matrix/matrix-pointwise.rkt b/collects/math/private/matrix/matrix-pointwise.rkt index 457f176beb..d224bbe87c 100644 --- a/collects/math/private/matrix/matrix-pointwise.rkt +++ b/collects/math/private/matrix/matrix-pointwise.rkt @@ -47,8 +47,11 @@ ((Matrix Number) -> (Matrix Number)) ((Matrix Real) (Matrix Real) -> (Matrix Real)) ((Matrix Number) (Matrix Number) -> (Matrix Number)))) +(: matrix.sqr (case-> ((Matrix Real) -> (Matrix Real)) + ((Matrix Number) -> (Matrix Number)))) +(: matrix.magnitude ((Matrix Number) -> (Matrix Real))) (define matrix+ (make-matrix-pointwise2 'matrix+ array+)) (define matrix- (make-matrix-pointwise1/2 'matrix- array-)) -(define matrix.sqr array-sqr) -(define matrix.magnitude array-magnitude) +(define matrix.sqr (make-matrix-pointwise1 'matrix.sqr array-sqr)) +(define matrix.magnitude (make-matrix-pointwise1 'matrix.magnitude array-magnitude)) diff --git a/collects/math/scribblings/math-array.scrbl b/collects/math/scribblings/math-array.scrbl index c1fbb2139e..459696bff5 100644 --- a/collects/math/scribblings/math-array.scrbl +++ b/collects/math/scribblings/math-array.scrbl @@ -2,91 +2,1336 @@ @(require scribble/eval racket/sandbox - (for-label racket/base + (for-label racket/base racket/vector racket/match racket/unsafe/ops racket/string math plot - (only-in typed/racket/base Flonum Real Boolean Any Listof Integer)) + (only-in typed/racket/base + ann inst : λ: define: make-predicate + Flonum Real Boolean Any Integer Index Natural Exact-Positive-Integer + Nonnegative-Real Sequenceof Fixnum Values + All U List Vector Listof Vectorof Struct)) "utils.rkt") -@(define untyped-eval (make-untyped-math-eval)) +@(define typed-eval (make-math-eval)) +@interaction-eval[#:eval typed-eval + (require racket/match + racket/vector + racket/string + racket/sequence)] -@title[#:tag "arrays"]{Arrays} +@title[#:tag "arrays" #:style 'toc]{Arrays} @(author-neil) +@bold{Performance Warning:} Most of the array-producing functions exported by +@racketmodname[math/array] run 25-50 times slower in untyped Racket, due to the +overhead of checking higher-order contracts. We are working on it. + +For now, if you need speed, use the @racketmodname[typed/racket] language. + @defmodule[math/array] -@section{Introduction} - One of the most common ways to structure data is with an array: a grid of homogeneous, independent elements, usually consisting of rows and columns. But an array data type -is often absent from functional languages' libraries. This is probably because arrays +is usually absent from functional languages' libraries. This is probably because arrays are perceived as requiring users to operate on them using destructive updates, write loops that micromanage array elements, and in general, stray far from the declarative ideal. @margin-note{TODO: Cite Haskell array paper} -Normally, they do. However, experience in Python, and more recently Haskell, has shown -that providing the right data types and a rich collection of whole-array operations +Normally, they do. However, experience in Python, and more recently Data-Parallel Haskell, +has shown that providing the right data types and a rich collection of whole-array operations allows working effectively with arrays in a functional, declarative style. As a bonus, doing so opens the possibility of parallelizing nearly every operation. -It requires a change in definition. The new definition is this: +It requires changing how we think of arrays, starting with this definition: -@bold{An @deftech{array} is just a function with a finite, rectangular domain.} +@nested[#:style 'inset]{@bold{An @deftech{array} is a function with a finite, rectangular domain.}} -Some arrays are mutable, some are lazy, some are strict, some are sparse, and some -do not even allocate space to store their elements. All are functions that can be +Some arrays are mutable, some are lazy, some are strict, some are sparse, and most +do not even allocate contiguous space to store their elements. All are functions that can be applied to indexes to retrieve elements. +@local-table-of-contents[] + +@section{Preliminaries} + @subsection{Definitions} -The domain of an array is determined by its @deftech{shape}, a vector of numbers that -describes the extent of each dimension. +An array's domain is determined by its @deftech{shape}, a vector of @racket[Index] such as +@racket[#(4 5)], @racket[#(10 1 5 8)] or @racket[#()]. The shape's length is the number of array +dimensions, or @deftech{axes}, and its contents are the length of each axis in row-major order. +The product of the axis lengths is the array's size. In particular, an array with shape +@racket[#()] has one element. -@examples[#:eval untyped-eval - (array-shape (array [0 1 2 3])) - (array-shape (array [[0 1] [2 3] [4 5]])) - (array-shape (array 0))] +An array's @deftech{procedure} defines the function that the array represents. The procedure +returns an element when applied to a vector of indexes in the array's domain. -The function represented by the array is called its @deftech{procedure}, which accepts -vectors of indexes and returns elements. +A @deftech{strict} array is one whose procedure computes elements only by indexing a vector or +some other kind of storage. Arrays that perform non-indexing computation to return elements +are @deftech{non-strict}. Almost all array functions exported by @racket[math/array] return +non-strict arrays. Exceptions are noted in the documentation. -@examples[#:eval untyped-eval - (define arr (array [[0 1] [2 3]])) - (define proc (array-proc arr)) - (proc #(1 1)) - (array-ref arr #(1 1))] +@bold{Non-strict arrays are not lazy.} By default, arrays do not cache computed elements, but +like functions, recompute them every time they are referred to. See @secref{array:strictness} +for details. -@section{Quick Start} +A @deftech{pointwise} operation is one that operates on each array element independently, on each +corresponding pair of elements from two arrays independently, or on a corresponding collection +of elements from many arrays independently. This is usually done using @racket[array-map]. -@section{Array Types} +When a pointwise operation is performed on two arrays with different shapes, the arrays are +@deftech{broadcast} so that their shapes match. See @secref{array:broadcasting} for details. -@section{Array Constructors} +@subsection{Quick Start} -array syntax +The most direct way to construct an array is to use @racket[build-array] to specify +its @tech{shape} and @tech{procedure}: +@interaction[#:eval typed-eval + (define arr + (build-array #(4 5) (λ: ([js : Indexes]) + (match-define (vector j0 j1) js) + (+ j0 j1))))] +This creates a @tech{non-strict} array, meaning that its elements are not computed unless +referred to. One way to refer to elements is to print them: +@interaction[#:eval typed-eval + arr] +@bold{Important:} Printing @racket[arr] did not cache its elements. A non-strict array's elements +are recomputed every time they are referred to. But see @racket[array-lazy], which constructs +arrays that cache computed elements. -(: make-array (All (A) (User-Indexes A -> (Array A)))) +Arrays can be made @tech{strict} using @racket[array-strict] or @racket[array->mutable-array]: +@interaction[#:eval typed-eval + (array-strict arr) + (array->mutable-array arr)] +The difference is that @racket[(array-strict arr)] returns @racket[arr] whenever @racket[arr] is +already strict. (It therefore has a less precise return type.) +See @secref{array:strictness} for details. -(: axis-index-array (User-Indexes Integer -> (Array Index))) +Arrays can be indexed using @racket[array-ref], and settable arrays can be mutated using +@racket[array-set!]: +@interaction[#:eval typed-eval + (array-ref arr #(2 3)) + (define brr (array->mutable-array arr)) + (array-set! brr #(2 3) -1) + brr] +However, both of these activities are discouraged in favor of functional, whole-array operations. -(: index-array (User-Indexes -> (Array Index))) +An array can be sliced using a list of sequences or slice objects. The following examples are +all equivalent, keeping every row of @racket[arr] and every even-numbered column: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (in-range 0 4) (:: 0 5 2))) + (array-slice-ref arr (list ::... (:: 0 5 2))) + (array-slice-ref arr (list '(0 1 2 3) (:: 0 5 2))) + (array-slice-ref arr (list (::) (in-range 0 5 2)))] +Here, @racket[::] constructs a slice object and has semantics similar to, but not exactly like, +@racket[in-range]. The special slice object @racket[::...] means ``every row in every unspecified, +adjacent axis'' (which in this case means every row in axis 0). -(: indexes-array (User-Indexes -> (Array Indexes))) +Arrays can be mapped over and otherwise operated on @tech{pointwise}: +@interaction[#:eval typed-eval + (array-map (λ: ([n : Natural]) (* 2 n)) arr) + (array+ arr arr)] +When arrays have different shapes, they are @tech{broadcast} to the same shape before applying +the pointwise operation: +@interaction[#:eval typed-eval + (array* arr (array 2)) + (array* arr (array #[0 1 0 2 0]))] +Zero-dimensional arrays like @racket[(array 2)] can be broadcast to any shape. +See @secref{array:broadcasting} for details. -(: diagonal-array (All (A) (Integer Integer A A -> (Array A)))) +@subsection[#:tag "array:strictness"]{Non-Strictness} +Almost every function exported by @racketmodname[math/array] returns @tech{non-strict} arrays, +meaning that they compute each element every time the element is referred to. -@section{Pointwise Array Operations} +This design decision is motivated by the observation that, in functional code that operates on +arrays, the elements in most intermediate arrays are referred to exactly once. Additionally, +many arrays consist only of a single, repeated constant. -@section{Array Folds} +The problem is well-illustrated by operating on whole vectors in a functional style: +@interaction[#:eval typed-eval + (vector-map string-append + (vector-map string-append + (vector "Hello " "Hallo " "Jó napot ") + (vector "Ada" "Edsger" "John")) + (make-vector 3 "!"))] +There are two memory inefficiencies here. The first is that the inner +@racket[(vector-map string-append ...)] allocates a vector whose elements are referred to only +once. The second is @racket[(vector "!" "!" "!")], which we construct to avoid looping over +the seemingly extraneous intermediate vector. -@section{Array Transformations} +This is the same example using arrays: +@interaction[#:eval typed-eval + (array-map string-append + (array-map string-append + (array #["Hello " "Hallo " "Jó napot "]) + (array #["Ada" "Edsger" "John"])) + (make-array #(3) "!"))] +Here, because @racket[array-map] returns non-strict arrays, the inner +@racket[(array-map string-append ...)] does not create intermediate storage. Neither does +@racket[(make-array #(3) "!")]. In fact, no elements are computed until they are printed, +and none of these arrays allocates contiguous space to store its elements. -@section{Mutable Arrays} +Because the major bottleneck in functional language performance is almost always allocation and +subsequent garbage collection, avoiding allocation can significantly increase performance. +Additionally, allocating contiguous space for intermediate array elements forces synchronization +between array operations, so not doing so provides opportunities for future parallelization. -mutable-array syntax +@margin-note*{Still, it is easier to reason about non-strict array performance than lazy array + performance.} +The downside is that it is more difficult to reason about the performance characteristics of +operations on non-strict arrays. Also, when using arrays, you must decide which arrays to make +strict. (This can be done using the @racket[array-strict] function.) Fortunately, there is a +simple rule of thumb: -@section{Flonum Arrays} +@nested[#:style 'inset]{@bold{Make arrays strict when you must refer to most of their elements + more than once or twice.}} -@section{Float-Complex Arrays} +Additionally, having to name an array is a good indicator that it should be strict. In the +following example, which computes @racket[(+ (expt x x) (expt x x))] for @racket[x] from @racket[0] +to @racket[2499], every element in @racket[xrr] is computed twice: +@racketblock[(define xrr (array-expt (index-array #(50 50)) + (index-array #(50 50)))) + (define res (array+ xrr xrr))] +Having to name @racket[xrr] means we should make it strict: +@racketblock[(define xrr (array-strict + (array-expt (index-array #(50 50)) + (index-array #(50 50))))) + (define res (array+ xrr xrr))] +Doing so halves the time it takes to compute @racket[res]'s elements because each +@racket[(expt x x)] is computed only once. -@(close-eval untyped-eval) +An exception to these guidelines is non-strict arrays returned from constructors like +@racket[make-array], which barely compute anything at all. Such exceptions are noted in each +function's documentation. Another exception is returning an array from a function. In this +case, application sites should make the array strict, if necessary. + +If you cannot determine whether to make arrays strict, or are using arrays for so-called +``dynamic programming,'' you can make them lazy using @racket[array-lazy]. + +@subsection[#:tag "array:broadcasting"]{Broadcasting} + +It is often useful to apply a @tech{pointwise} operation to two or more arrays in a many-to-one +manner. Library support for this, which @racketmodname[math/array] provides, is called +@tech[#:key "broadcast"]{broadcasting}. + +@examples[#:eval typed-eval + (define diag + (diagonal-array 2 6 1 0)) + (array-shape diag) + diag + (array-shape (array 10)) + (array* diag (array 10)) + (array+ (array* diag (array 10)) + (array #[0 1 2 3 4 5]))] + +@subsubsection[#:tag "array:broadcasting:rules"]{Broadcasting Rules} + +Suppose we have two array shapes @racket[ds = (vector d0 d1 ...)] and +@racket[es = (vector e0 e1 ...)]. Broadcasting proceeds as follows: +@itemlist[#:style 'ordered + @item{The shorter shape is padded on the left with @racket[1] until it is the same length + as the longer shape.} + @item{For each axis @racket[k], @racket[dk] and @racket[ek] are compared. If @racket[dk = ek], + the result axis is @racket[dk]; if one axis is length @racket[1], the result axis + is the length of the other; otherwise fail.} + @item{Both arrays' axes are stretched by (conceptually) copying singleton axes' rows.} + ] + +Suppose we have an array @racket[drr] with shape @racket[ds = #(4 1 3)] and another array +@racket[err] with shape @racket[es = #(3 3)]. Following the rules: +@itemlist[#:style 'ordered + @item{@racket[es] is padded to get @racket[#(1 3 3)].} + @item{The result axis is derived from @racket[#(4 1 3)] and @racket[#(1 3 3)] to get + @racket[#(4 3 3)].} + @item{@racket[drr]'s second axis is stretched to length @racket[3], and @racket[err]'s new first + axis (which is length @racket[1] by rule 1) is stretched to length @racket[4].} + ] + +Conceptually, step 1 is the same as adding square brackets around @racket[err] in literal +@racket[array] syntax. For example, if @racket[err = (array #[#[0 1 2] #[3 4 5] #[6 7 8]])], +after padding its shape, it is @racket[(array #[#[#[0 1 2] #[3 4 5] #[6 7 8]]])]. + +The same example, but more concrete: +@interaction[#:eval typed-eval + (define nums + (array #[#[#["00" "01" "02"]] + #[#["10" "11" "12"]] + #[#["20" "21" "22"]] + #[#["30" "31" "32"]]])) + (array-shape nums) + (define lets + (array #[#["aa" "ab" "ac"] + #["ba" "bb" "bc"] + #["ca" "cb" "cc"]])) + (array-shape lets) + (define nums+lets (array-map string-append nums lets)) + (array-shape nums+lets) + nums+lets] +Notice how the row @racket[#["00" "01" "02"]] in @racket[nums] is repeated in the result +because @racket[nums]'s second axis was stretched during broadcasting. Also, the column +@racket[#[#["aa"] #["ba"] #["ca"]]] in @racket[lets] is repeated because @racket[lets]'s first +axis was stretched. + +@subsubsection[#:tag "array:broadcasting:control"]{Broadcasting Control} + +The parameter @racket[array-broadcasting] controls how pointwise operations @tech{broadcast} +arrays. Its default value is @racket[#t], which means that broadcasting proceeds as described +in @secref{array:broadcasting:rules}. Another possible value is @racket[#f], which allows pointwise +operations to succeed only if array shapes match exactly: +@interaction[#:eval typed-eval + (parameterize ([array-broadcasting #f]) + (array* (index-array #(3 3)) (array 10)))] + +Another option is @hyperlink["http://www.r-project.org"]{R}-style permissive broadcasting, +which allows pointwise operations to @italic{always} succeed, by repeating any axis instead +of stretching just singleton axes: +@interaction[#:eval typed-eval + (define arr10 (array-map number->string (index-array #(10)))) + (define arr3 (array-map number->string (index-array #(3)))) + arr10 + arr3 + (array-map string-append arr10 (array #["+" "-"]) arr3) + (parameterize ([array-broadcasting 'permissive]) + (array-map string-append arr10 (array #["+" "-"]) arr3))] +Notice that @racket[(array #["+" "-"])] was repeated five times, and that +@racket[arr3] was repeated three full times and once partially. + +@section{Types, Predicates and Accessors} + +@defform[(Array A)]{ +The parent array type. Its type parameter is the type of the array's elements. + +The polymorphic @racket[Array] type is @italic{covariant}, meaning that @racket[(Array A)] is a +subtype of @racket[(Array B)] if @racket[A] is a subtype of @racket[B]: +@examples[#:eval typed-eval + (define arr (array #[1 2 3 4 5])) + arr + (ann arr (Array Real)) + (ann arr (Array Any))] +Because subtyping is transitive, the @racket[(Array A)] in the preceeding subtyping rule can be +replaced with any of @racket[(Array A)]'s subtypes, including descendant types of @racket[Array]. +For example, @racket[(Mutable-Array A)] is a subtype of @racket[(Array B)] if @racket[A] is a +subtype of @racket[B]: +@examples[#:eval typed-eval + (define arr (mutable-array #[1 2 3 4 5])) + arr + (ann arr (Array Real)) + (ann arr (Array Any))] +} + +@defform[(Settable-Array A)]{ +The parent type of arrays whose elements can be mutated. Functions like @racket[array-set!] and +@racket[array-slice-set!] accept arguments of this type. Examples of subtypes are +@racket[Mutable-Array], @racket[FlArray] and @racket[FCArray]. + +This type is @italic{invariant}, meaning that @racket[(Settable-Array A)] is @bold{not} a subtype +of @racket[(Settable-Array B)] if @racket[A] and @racket[B] are different types, even if @racket[A] +is a subtype of @racket[B]: +@examples[#:eval typed-eval + (define arr (mutable-array #[1 2 3 4 5])) + arr + (ann arr (Settable-Array Integer)) + (ann arr (Settable-Array Real))] +} + +@defform[(Mutable-Array A)]{ +The type of mutable arrays. Its type parameter is the type of the array's elements. + +Arrays of this type are always strict, and store their elements in a @racket[(Vectorof A)]: +@examples[#:eval typed-eval + (define arr (mutable-array #[1 2 3 4 5])) + (array-strict? arr) + (mutable-array-data arr)] +} + +@defidform[Indexes]{ +The type of array shapes and array indexes @italic{produced} by @racketmodname[math/array] functions. +Defined as @racket[(Vectorof Index)]. +@examples[#:eval typed-eval + (array-shape (array #[#[#[0]]]))] +} + +@defidform[In-Indexes]{ +The type of array shapes and array indexes @italic{accepted} by @racketmodname[math/array] functions. +Defined as @racket[(U Indexes (Vectorof Integer))]. + +@examples[#:eval typed-eval + (define ds #(3 2)) + ds + (make-array ds (void))] + +This makes indexes-accepting functions easier to use, because it is easier to convince Typed +Racket that a vector contains @racket[Integer] elements than that a vector contains @racket[Index] +elements. + +@racket[In-Indexes] is not defined as @racket[(Vectorof Integer)] because mutable container types +like @racket[Vector] and @racket[Vectorof] are invariant. +In particular, @racket[(Vectorof Index)] is not a subtype of @racket[(Vectorof Integer)]: +@interaction[#:eval typed-eval + (define js ((inst vector Index) 3 4 5)) + js + (ann js (Vectorof Integer)) + (ann js In-Indexes)] +} + +@deftogether[(@defproc[(array? [v Any]) Boolean] + @defproc[(settable-array? [v Any]) Boolean] + @defproc[(mutable-array? [v Any]) Boolean])]{ +Predicates for the types @racket[Array], @racket[Settable-Array], and @racket[Mutable-Array]. + +Because @racket[Settable-Array] and its descendants are invariant, @racket[settable-array?] and +its descendants' predicates are generally not useful in occurrence typing. For example, if +we know we have an @racket[Array] but would like to treat it differently if it happens to be +a @racket[Mutable-Array], we are basically out of luck: +@interaction[#:eval typed-eval + (: maybe-array-data (All (A) ((Array A) -> (U #f (Vectorof A))))) + (define (maybe-array-data arr) + (cond [(mutable-array? arr) (mutable-array-data arr)] + [else #f]))] +In general, predicates with a @racket[Struct] filter do not give conditional branches access +to a struct's accessors. Because @racket[Settable-Array] and its descendants are invariant, +their predicates have @racket[Struct] filters: +@interaction[#:eval typed-eval + array? + settable-array? + mutable-array?] +} + +@defproc[(array-strict? [arr (Array A)]) Boolean]{ +Returns @racket[#t] when @racket[arr] is @tech{strict}. +@examples[#:eval typed-eval + (define arr (array #[0 1 2 3])) + (array-strict? arr) + (array-strict? (array-strict arr))] +} + +@defproc[(array-shape [arr (Array A)]) Indexes]{ +Returns @racket[arr]'s @tech{shape}, a vector of indexes that contains the lengths +of @racket[arr]'s axes. +@examples[#:eval typed-eval + (array-shape (array 0)) + (array-shape (array #[0 1])) + (array-shape (array #[#[0 1]])) + (array-shape (array #[]))] +} + +@defproc[(array-size [arr (Array A)]) Index]{ +Returns the number of elements in @racket[arr], which is the product of its axis lengths. +@examples[#:eval typed-eval + (array-size (array 0)) + (array-size (array #[0 1])) + (array-size (array #[#[0 1]])) + (array-size (array #[]))] +} + +@defproc[(array-dims [arr (Array A)]) Index]{ +Returns the number of @racket[arr]'s dimensions. Equivalent to +@racket[(vector-length (array-shape arr))]. +} + +@defproc[(mutable-array-data [arr (Mutable-Array A)]) (Vectorof A)]{ +Returns the vector of data that @racket[arr] contains. +} + +@section{Construction} + +@defform[(array #[#[...] ...])]{ +Creates an @racket[Array] from nested rows of expressions. + +The vector syntax @racket[#[...]] delimits rows. These may be nested to any depth, and must have a +rectangular shape. Using square parentheses is not required, but is encouraged to help distinguish +array contents from array shapes and other vectors. + +@examples[#:eval typed-eval + (array 0) + (array #[0 1 2 3]) + (array #[#[1 2 3] #[4 5 6]]) + (array #[#[1 2 3] #[4 5]])] +As with the @racket[list] constructor, the type chosen for the array is the narrowest type +all the elements can have. Unlike @racket[list], because @racket[array] is syntax, the only way +to change the element type is to annotate the result. +@interaction[#:eval typed-eval + (list 1 2 3) + (array #[1 2 3]) + ((inst list Real) 1 2 3) + ((inst array Real) #[1 2 3]) + (ann (array #[1 2 3]) (Array Real))] +Annotating should rarely be necessary because the @racket[Array] type is covariant. + +Normally, the datums within literal vectors are implicitly quoted. However, when used within the +@racket[array] form, the datums must be explicitly quoted. +@interaction[#:eval typed-eval + #(this is okay) + (array #[not okay]) + (array #['this 'is 'okay]) + (array #['#(an) '#(array) '#(of) '#(vectors)])] +} + +@defform/subs[(mutable-array #[#[...] ...] maybe-type-ann) + [(maybe-type-ann (code:line) (code:line : type))]]{ +Creates a @racket[Mutable-Array] from nested rows of expressions. + +The semantics are almost identical to @racket[array]'s, except the result is mutable: +@interaction[#:eval typed-eval + (define arr (mutable-array #[0 1 2 3])) + arr + (array-set! arr #(0) 10) + arr] +Because mutable arrays are invariant, this form additionally accepts a type annotation for +the array's elements: +@interaction[#:eval typed-eval + (define arr (mutable-array #[0 1 2 3] : Real)) + arr + (array-set! arr #(0) 10.0) + arr] +} + +@defproc[(make-array [ds In-Indexes] [value A]) (Array A)]{ +Returns an array with @tech{shape} @racket[ds], with every element's value as @racket[value]. +Analogous to @racket[make-vector], but the result is @tech{non-strict}. +@examples[#:eval typed-eval + (make-array #() 5) + (make-array #(1 2) 'sym) + (make-array #(4 0 2) "Invisible")] +It is useless to make a strict copy of an array returned by @racket[make-array]. +} + +@defproc[(build-array [ds In-Indexes] [proc (Indexes -> A)]) (Array A)]{ +Returns an array with @tech{shape} @racket[ds] and @tech{procedure} @racket[proc]. +Analogous to @racket[build-vector], but the result is @tech{non-strict}. +@examples[#:eval typed-eval + (eval:alts + (define: fibs : (Array Exact-Positive-Integer) + (build-array + #(10) (λ: ([js : Indexes]) + (define j (vector-ref js 0)) + (cond [(j . < . 2) 1] + [else (+ (array-ref fibs (vector (- j 1))) + (array-ref fibs (vector (- j 2))))])))) + (void)) + (eval:alts + fibs + (ann (array #[1 1 2 3 5 8 13 21 34 55]) (Array Exact-Positive-Integer)))] +Because @racket[build-array] returns a non-strict array, @racket[fibs] may refer to itself +within its definition. Of course, this naïve implementation computes its elements in time +exponential in the size of @racket[fibs]. A quick, widely applicable fix is given in +@racket[array-lazy]'s documentation. +} + +@defproc[(array-strict [arr (Array A)]) (Array A)]{ +Returns a @tech{strict} array with the same elements as @racket[arr]. If +@racket[(array-strict? arr)] is @racket[#t], returns @racket[arr]. + +Currently, if @racket[(array-strict? arr)] is @racket[#f], @racket[(array-strict arr)] returns +a new @racket[Mutable-Array]. Typed Racket code is unlikely to accidentally rely on this fact +(see @racket[mutable-array?] for the reason), but untyped Racket code could easily do so. Refrain. +The type used for new strict arrays could change to another descendant of @racket[Array] in a +future release. Use @racket[array->mutable-array] to reliably get a mutable array. +} + +@defproc[(array-lazy [arr (Array A)]) (Array A)]{ +Returns a @tech{non-strict} array with the same elements as @racket[arr], but element +computations are cached and reused. + +The example in @racket[build-array]'s documentation computes the Fibonacci numbers in exponential +time. Speeding it up to linear time only requires wrapping its definition with @racket[array-lazy]: +@interaction[#:eval typed-eval + (eval:alts + (define: fibs : (Array Exact-Positive-Integer) + (array-lazy + (build-array + #(10) (λ: ([js : Indexes]) + (define j (vector-ref js 0)) + (cond [(j . < . 2) 1] + [else (+ (array-ref fibs (vector (- j 1))) + (array-ref fibs (vector (- j 2))))]))))) + (void)) + (eval:alts + fibs + (ann (array #[1 1 2 3 5 8 13 21 34 55]) (Array Exact-Positive-Integer)))] +Printing a lazy array computes and caches all of its elements, as does applying +@racket[array-strict] to it. +} + +@defproc[(make-mutable-array [ds In-Indexes] [vs (Vectorof A)]) (Mutable-Array A)]{ +Returns a mutable array with @tech{shape} @racket[ds] and elements @racket[vs]; assumes +@racket[vs] are in row-major order. If there are too many or too few elements in @racket[vs], +@racket[(make-mutable-array ds vs)] raises an error. +@examples[#:eval typed-eval + (make-mutable-array #(3 3) #(0 1 2 3 4 5 6 7 8)) + (make-mutable-array #() #(singleton)) + (make-mutable-array #(4) #(1 2 3 4 5))] +} + +@defproc[(array->mutable-array [arr (Array A)]) (Mutable-Array A)]{ +Returns a mutable array with the same elements as @racket[arr]. + +While @racket[array-strict] may return any subtype of @racket[Array], @racket[array->mutable-array] +always returns a @racket[Mutable-Array]. Additionally, @racket[(array-strict arr)] may return +@racket[arr] if @racket[arr] is already strict, but @racket[array->mutable-array] always makes a +copy. +} + +@defproc[(mutable-array-copy [arr (Mutable-Array A)]) (Mutable-Array A)]{ +Like @racket[(array->mutable-array arr)], but is restricted to mutable arrays. It is also faster. +} + +@defproc[(indexes-array [ds In-Indexes]) (Array Indexes)]{ +Returns an array with @tech{shape} @racket[ds], with each element set to its position in the +array. +@examples[#:eval typed-eval + (indexes-array #()) + (indexes-array #(4)) + (indexes-array #(2 3)) + (indexes-array #(4 0 2))] +} + +@defproc[(index-array [ds In-Indexes]) (Array Index)]{ +Returns an array with @tech{shape} @racket[ds], with each element set to its row-major index in +the array. +@examples[#:eval typed-eval + (index-array #(2 3)) + (array-flatten (index-array #(2 3)))] +} + +@defproc[(axis-index-array [ds In-Indexes] [axis Integer]) (Array Index)]{ +Returns an array with @tech{shape} @racket[ds], with each element set to its position in axis +@racket[axis]. The axis number @racket[axis] must be nonnegative and less than the number of axes +(the length of @racket[ds]). +@examples[#:eval typed-eval + (axis-index-array #(3 3) 0) + (axis-index-array #(3 3) 1) + (axis-index-array #() 0)] +It is useless to make a strict copy of an array returned by @racket[axis-index-array]. +} + +@defproc[(diagonal-array [dims Integer] [axes-length Integer] [on-value A] [off-value A]) + (Array A)]{ +Returns a square array with @racket[dims] axes, each with length @racket[axes-length]. The elements +on the diagonal (i.e. at indexes of the form @racket[(vector j j ...)] for @racket[j < axes-length]) +have the value @racket[on-value]; the rest have the value @racket[off-value]. +@examples[#:eval typed-eval + (diagonal-array 2 7 1 0)] +} + +@section{Conversion} + +@deftogether[(@defproc[(list->array [lst (Listof A)]) (Array A)] + @defproc[(array->list [arr (Array A)]) (Listof A)])]{ +Convert lists to single-axis arrays and back. If @racket[arr] has no axes or more than one axis, +it is (conceptually) flattened before being converted to a list. +@examples[#:eval typed-eval + (list->array '(1 2 3)) + (list->array '((1 2 3) (4 5))) + (array->list (array #[1 2 3])) + (array->list (array 10)) + (array->list (array #[#[1 2 3] #[4 5 6]]))] +For conversion between nested lists and multidimensional arrays, see @racket[list*->array] and +@racket[array->list*]. +} + +@deftogether[(@defproc[(vector->array [vec (Vectorof A)]) (Array A)] + @defproc[(array->vector [arr (Array A)]) (Vectorof A)])]{ +Like @racket[list->array] and @racket[array->list], but for vectors. +} + +@defform[(Listof* A)]{ +Equivalent to @racket[(U A (Listof A) (Listof (Listof A)) ...)] if infinite unions were allowed. +This is used as an argument type to @racket[list*->array] and as the return type of +@racket[array->list*]. +} + +@defproc[(list*->array [lsts (Listof* A)] [pred? ((Listof* A) -> Any : A)]) (Array A)]{ +Converts a nested list of elements of type @racket[A] to an array. The predicate @racket[pred?] +identifies elements of type @racket[A]. The shape of @racket[lsts] must be rectangular. +@examples[#:eval typed-eval + (list*->array 'singleton symbol?) + (list*->array '(0 1 2 3) byte?) + (list*->array (list (list (list 5) (list 2 3)) + (list (list 4.0) (list 1.4 0.2 9.3))) + (make-predicate (Listof Nonnegative-Real)))] +The last example demonstrates why a predicate is required. There is no well-typed Typed Racket +function that behaves like @racket[list*->array] but does not require @racket[pred?], because +without it, there is no way to distinguish between rows and elements. +} + +@defproc[(array->list* [arr (Array A)]) (Listof* A)]{ +The inverse of @racket[list*->array]. +} + +@defform[(Vectorof* A)]{ +Like @racket[(Listof* A)], but for vectors. See @racket[vector*->array] and @racket[array->vector*]. +} + +@defproc[(vector*->array [vecs (Vectorof* A)] [pred? ((Vectorof* A) -> Any : A)]) (Array A)]{ +Like @racket[list*->array], but accepts nested vectors of elements. +@examples[#:eval typed-eval + (vector*->array 'singleton symbol?) + ((inst vector*->array Byte) #(0 1 2 3) byte?)] +As in the last example, Typed Racket often needs help inferring @racket[vector*->array]'s +type parameters. +} + +@defproc[(array->vector* [arr (Array A)]) (Vectorof* A)]{ +Like @racket[array->list*], but produces nested vectors of elements. +} + +@defproc[(array-list->array [arrs (Listof (Array A))] [axis Integer 0]) (Array A)]{ +Concatenates @racket[arrs] along axis @racket[axis] to form a new array. If the arrays have +different shapes, they are broadcast first. The axis number @racket[axis] must be nonnegative +and @italic{no greater than} the number of axes after broadcasting. +@examples[#:eval typed-eval + (array-list->array (list (array 0) (array 1) (array 2) (array 3))) + (array-list->array (list (array 0) (array 1) (array 2) (array 3)) 1) + (array-list->array (list (array #[0 1 2 3]) (array #['a 'b 'c 'd]))) + (array-list->array (list (array #[0 1 2 3]) (array '!))) + (array-list->array (list (array #[0 1 2 3]) (array '!)) 1) + ] +This function is a left inverse of @racket[array->array-list]. (It cannot be a right inverse +because broadcasting cannot be undone.) +} + +@defproc[(array->array-list [arr (Array A)] [axis Integer 0]) (Listof (Array A))]{ +Turns one axis of @racket[arr] into a list of arrays. Each array in the result has the same shape. +The axis number @racket[axis] must be nonnegative and less than the number of @racket[arr]'s axes. +@examples[#:eval typed-eval + (array->array-list (array #[0 1 2 3])) + (array->array-list (array #[#[1 2] #[10 20]])) + (array->array-list (array #[#[1 2] #[10 20]]) 1) + (array->array-list (array 10))] +} + +@subsection{Printing} + +@;{ +array-custom-printer +print-array-fields +print-array +} + +@section{Comprehensions and Sequences} + +Sometimes sequential processing is unavoidable, so @racket[math/array] provides loops and sequences. + +@deftogether[(@defform[(for/array: maybe-shape maybe-fill (for:-clause ...) maybe-type-ann + body ...+)] + @defform/subs[(for*/array: maybe-shape maybe-fill (for:-clause ...) maybe-type-ann + body ...+) + ([maybe-shape (code:line) (code:line #:shape ds)] + [maybe-fill (code:line) (code:line #:fill fill)] + [maybe-type-ann (code:line) (code:line : body-type)]) + #:contracts ([ds In-Indexes] + [fill body-type])])]{ +Creates arrays by generating elements in a @racket[for]-loop or @racket[for*]-loop. +Unlike other Typed Racket loop macros, these accept a @italic{body annotation}, which declares +the type of elements. They do not accept an annotation for the entire type of the result. +@examples[#:eval typed-eval + (for/array: ([x (in-range 3)] [y (in-range 3)]) : Integer + (+ x y)) + (for*/array: ([x (in-range 3)] [y (in-range 3)]) : Integer + (+ x y))] +The shape of the result is independent of the loop clauses: note that the last example +does not have shape @racket[#(3 3)], but shape @racket[#(9)]. To control the shape, use the +@racket[#:shape] keyword: +@interaction[#:eval typed-eval + (for*/array: #:shape #(3 3) ([x (in-range 3)] + [y (in-range 3)]) : Integer + (+ x y))] + +If the loop does not generate enough elements, the rest are filled with the @italic{first} +generated value: +@interaction[#:eval typed-eval + (for*/array: #:shape #(4) ([x (in-range 1 3)]) x)] +To change this behavior, use the @racket[#:fill] keyword: +@interaction[#:eval typed-eval + (for*/array: #:shape #(4) #:fill -1 ([x (in-range 1 3)]) x)] +In the last two examples, the array's type is @racket[(Mutable-Array Any)] because +a body annotation was not given. +} + +@deftogether[(@defform[(for/array maybe-shape maybe-fill (for-clause ...) + body ...+)] + @defform[(for*/array maybe-shape maybe-fill (for-clause ...) + body ...+)])]{ +Untyped versions of the loop macros. +} + +@defproc[(in-array [arr (Array A)]) (Sequenceof A)]{ +Returns a sequence of @racket[arr]'s elements in row-major order. +@examples[#:eval typed-eval + (define arr (array #[#[1 2] #[10 20]])) + (for/list: : (Listof Integer) ([x (in-array arr)]) x)] +} + +@defproc[(in-array-axis [arr (Array A)] [axis Integer 0]) (Sequenceof (Array A))]{ +Like @racket[array->array-list], but returns a sequence. +@examples[#:eval typed-eval + (define arr (array #[#[1 2] #[10 20]])) + (sequence->list (in-array-axis arr)) + (sequence->list (in-array-axis arr 1))] +} + +@defproc[(in-array-indexes [ds In-Indexes]) (Sequenceof Indexes)]{ +Returns a sequence of indexes for shape @racket[ds], in row-major order. +@examples[#:eval typed-eval + (for/array: #:shape #(3 3) ([js (in-array-indexes #(3 3))]) : Indexes + js) + (for*/array: #:shape #(3 3) ([j0 (in-range 3)] + [j1 (in-range 3)]) : In-Indexes + (vector j0 j1)) + (indexes-array #(3 3))] +} + +@section{Pointwise Operations} + +Most of the operations documented in this section are simple macros that apply @racket[array-map] +to a function and their array arguments. + +@defproc*[([(array-map [f (-> R)]) (Array R)] + [(array-map [f (A -> R)] [arr0 (Array A)]) (Array R)] + [(array-map [f (A B Ts ... -> R)] [arr0 (Array A)] [arr1 (Array B)] [arrs (Array Ts)] ...) + (Array R)])]{ +Composes @racket[f] with the given arrays' @tech{procedures}. When the arrays' shapes do not match, +they are @tech{broadcast} to the same shape first. If broadcasting fails, @racket[array-map] raises +an error. + +@examples[#:eval typed-eval + (array-map (λ: ([x : String]) (string-append x "!")) + (array #[#["Hello" "I"] #["Am" "Shouting"]])) + (array-map + (index-array #(3 3 3)) (array 2)) + (array-map + (index-array #(2 2)) (index-array #(3 3)))] + +Typed Racket can often derive fairly precise element types for the resulting array: +@interaction[#:eval typed-eval + (array-map * (array #[-4.3 -1.2 -0.2]) (array -2.81))] +How precise the result type is depends on the type of @racket[f]. Preserving precise result types +for lifted arithmetic operators is the main reason most pointwise operations are macro wrappers for +@racket[array-map]. + +Unlike @racket[map], @racket[array-map] can map a zero-argument function: +@interaction[#:eval typed-eval + (array-map (λ () "Whoa, Nelly!"))] +If the resulting zero-dimensional array is used in a pointwise operation with other arrays, +it will be broadcast to their shape: +@interaction[#:eval typed-eval + (array-map + (array #[1 2 3]) (array-map (λ () -10)))] + +When explicitly instantiating @racket[array-map]'s types using @racket[inst], instantiate +@racket[R] (the return type's element type) first, then the arguments' element types in order. +} + +@defform[(inline-array-map f arrs ...)]{ +Like @racket[array-map], but possibly faster. Inlining a map operation can allow Typed Racket's +optimizer to replace @racket[f] with something unchecked and type-specific (for example, +replace @racket[*] with @racket[unsafe-fl*]), at the expense of code size. +} + +@deftogether[(@defform[(array-abs arr)] + @defform[(array-round arr)] + @defform[(array-floor arr)] + @defform[(array-ceiling arr)] + @defform[(array-truncate arr)] + @defform[(array-sqrt arr)] + @defform[(array-sqr arr)] + @defform[(array-log arr)] + @defform[(array-exp arr)] + @defform[(array-sin arr)] + @defform[(array-cos arr)] + @defform[(array-tan arr)] + @defform[(array-asin arr)] + @defform[(array-acos arr)] + @defform[(array-atan arr)] + @defform[(array-inexact->exact arr)] + @defform[(array-exact->inexact arr)] + @defform[(array-fl arr)])]{ +Equivalent to @racket[(array-map f arr)], where @racket[f] is respectively +@racket[abs], +@racket[round], +@racket[floor], +@racket[ceiling], +@racket[truncate], +@racket[sqrt], +@racket[sqr], +@racket[log], +@racket[exp], +@racket[sin], +@racket[cos], +@racket[tan], +@racket[asin], +@racket[acos], +@racket[atan], +@racket[inexact->exact], +@racket[exact->inexact], +or @racket[fl]. + +Instead of @racket[array-fl], you might consider using @racket[array->flarray], which returns +a mutable array of flonums, stored unboxed in an flvector. +} + +@deftogether[(@defform[(array-magnitude arr)] + @defform[(array-angle arr)] + @defform[(array-conjugate arr)] + @defform[(array-real-part arr)] + @defform[(array-imag-part arr)] + @defform[(array-fc arr)])]{ +Equivalent to @racket[(array-map f arr)], where @racket[f] is respectively +@racket[magnitude], +@racket[angle], +@racket[conjugate], +@racket[real-part], +@racket[imag-part], +or @racket[number->float-complex]. + +Instead of @racket[array-fc], you might consider using @racket[array->fcarray], which returns +a mutable array of float-complex numbers, stored unboxed in a pair of flvectors. +} + +@deftogether[(@defform[(array+ arrs ...)] + @defform[(array* arrs ...)] + @defform[(array- arr0 arrs ...)] + @defform[(array/ arr0 arrs ...)] + @defform[(array-min arr0 arrs ...)] + @defform[(array-max arr0 arrs ...)])]{ +Equivalent to mapping arithmetic operators over arrays. Note that because @racket[(array-map f)] +returns sensible answers, so do @racket[(array+)] and @racket[(array*)]. +@examples[#:eval typed-eval + (array+ (array #[#[0.0 1.0] #[2.0 3.0]]) (array 200)) + (array+) + (array*) + (array/ (array #[2 1/2]))] +} + +@defform[(array-scale arr x)]{ +Equivalent to @racket[(array* arr (array x))], but faster. +} + +@deftogether[(@defform[(array-expt arr0 arr1)] + @defform[(array-make-rectangular arr0 arr1)])]{ +Equivalent to @racket[(array-map expt arr0 arr1)] and +@racket[(array-map make-rectangular arr0 arr1)], respectively. +} + +@deftogether[(@defform[(array< arr0 arr1 arrs ...)] + @defform[(array<= arr0 arr1 arrs ...)] + @defform[(array> arr0 arr1 arrs ...)] + @defform[(array>= arr0 arr1 arrs ...)] + @defform[(array= arr0 arr1 arrs ...)])]{ +Equivalent to @racket[(array-map f arr0 arr1 arrs ...)], where @racket[f] is respectively +@racket[<], @racket[<=], @racket[>], @racket[>=], or @racket[=]. +} + +@deftogether[(@defform[(array-not arr)] + @defform[(array-and arr ...)] + @defform[(array-or arr ...)] + @defform[(array-if cond-arr true-arr false-err)])]{ +@racket[not], @racket[and], @racket[or] and @racket[if] lifted to operate on arrays. + +The short-cutting behavior of @racket[array-and], @racket[array-or] and @racket[array-if] +can keep array arguments' elements from being referred to (and thus computed). However, +they cannot be used to distinguish base and inductive cases in a recursive function, because +the array arguments are always evaluated. For example, this function never returns: +@racketblock[(: array-factorial ((Array Integer) -> (Array Integer))) + (define (array-factorial arr) + (array-if (array<= arr (array 0)) + (array 1) + (array* arr (array-factorial (array- arr (array 1))))))] +} + +@subsection{Broadcasting} + +@;{ +array-broadcasting +array-shape-broadcast +array-broadcast +} + +@section{Indexing and Slicing} + +@defproc[(array-ref [arr (Array A)] [js In-Indexes]) A]{ +Returns the element of @racket[arr] at position @racket[js]. If any index in @racket[js] is +negative or not less than its corresponding axis length, @racket[array-ref] raises an error. +} + +@defproc[(array-set! [arr (Settable-Array A)] [js In-Indexes] [value A]) Void]{ +Sets the element of @racket[arr] at position @racket[js] to @racket[value]. +If any index in @racket[js] is negative or not less than its corresponding axis length, +@racket[array-set!] raises an error. +} + +@defproc[(array-indexes-ref [arr (Array A)] [idxs (Array In-Indexes)]) (Array A)]{ +High-level explanation: Returns multiple elements from @racket[arr] in a new array. + +Lower-level explanation: Returns an array with same shape as @racket[idxs], whose elements are +@racket[array-ref]'d from @racket[arr] using the indexes in @racket[idxs]. +@examples[#:eval typed-eval + (define arr (array #[#[1 2] #[10 20]])) + (define idxs (array #['#(0 0) '#(1 1)])) + (array-indexes-ref arr idxs)] + +Implementation-level explanation: @racket[(array-indexes-ref arr idxs)] is equivalent to +@interaction[#:eval typed-eval + (build-array (array-shape idxs) + (λ: ([js : Indexes]) + (array-ref arr (array-ref idxs js))))] +but faster. +} + +@defproc[(array-indexes-set! [arr (Settable-Array A)] [idxs (Array In-Indexes)] [vals (Array A)]) + Void]{ +Indexes @racket[arr] in the same way that @racket[array-indexes-ref] does, but mutates elements. +If @racket[idxs] and @racket[vals] do not have the same shape, they are @tech{broadcast} first. +@examples[#:eval typed-eval + (define arr (mutable-array #[#[1 2] #[10 20]])) + (define idxs (array #['#(0 0) '#(1 1)])) + (array-indexes-set! arr idxs (array -1)) + arr] +} + +@defproc[(array-slice-ref [arr (Array A)] [specs (Listof Slice-Spec)]) (Array A)]{ +Returns a transformation of @racket[arr] according to the list of slice specifications +@racket[specs]. See @secref{array:slice-specs} for documentation and examples. +} + +@defproc[(slice-indexes-array [ds In-Indexes] [specs (Listof Slice-Spec)]) (Array Indexes)]{ +Returns the indexes of the elements that would be retrieved if an array with shape @racket[ds] +were sliced according to @racket[specs]. +Equivalent to @racketblock[(array-slice-ref (indexes-array ds) specs)] +} + +@defproc[(array-slice-set! [arr (Settable-Array A)] [specs (Listof Slice-Spec)] [vals (Array A)]) + Void]{ +Like @racket[array-indexes-set!], but for slice specifications. Equivalent to +@racketblock[(let ([idxs (slice-indexes-array (array-shape arr) specs)]) + (array-indexes-set! arr idxs vals))] +} + +@subsection[#:tag "array:slice-specs"]{Slice Specifications} + +@defidform[Slice-Spec]{ +The type of a slice specification. Currently defined as +@racketblock[(U (Sequenceof Integer) Slice Slice-Dots Integer Slice-New-Axis)] +} + +Slice specifications operate on axes independently. Different types represent different +transformations: +@itemlist[@item{@racket[(Sequenceof Integer)]: pick rows from an axis by index.} + @item{@racket[Slice]: pick rows from an axis as with an @racket[in-range] sequence.} + @item{@racket[Slice-Dots]: preserve remaining adjacent axes} + @item{@racket[Integer]: remove an axis by replacing it with one of its rows.} + @item{@racket[Slice-New-Axis]: insert an axis of a given length.}] + +Create @racket[Slice] objects using @racket[::] and @racket[Slice-New-Axis] objects using +@racket[::new]. There is only one @racket[Slice-Dots] object, namely @racket[::...]. + +When slicing an array with @racket[n] axes, unless a list of slice specifications contains +@racket[::...], it must contain exactly @racket[n] slice specifications. + +The remainder of this section uses the following example array: +@interaction[#:eval typed-eval + (define arr + (build-array + #(2 3 4) + (λ: ([js : Indexes]) + (string-append* (map number->string (vector->list js)))))) + arr] + +@subsubsection{@racket[(Sequenceof Integer)]: pick rows} + +Using a sequence of integers as a slice specification picks rows from the corresponding axis. For +example, we might use lists of integers to pick @italic{every} row from every axis: +@interaction[#:eval typed-eval + (array-slice-ref arr (list '(0 1) '(0 1 2) '(0 1 2 3)))] +This simply copies the array. (However, because a sliced array is @tech{non-strict}, it does not +copy the elements.) + +More usefully, we can use sequences to swap rows on the same axis: +@interaction[#:eval typed-eval + (array-slice-ref arr (list '(1 0) '(0 1 2) '(0 1 2 3)))] +We can also remove rows: +@interaction[#:eval typed-eval + (array-slice-ref arr (list '(0 1) '(0 2) '(0 2))) + (array-slice-ref arr (list '(0 1) '(0 1 2) '()))] +Or duplicate rows: +@interaction[#:eval typed-eval + (array-slice-ref arr (list '(0 1) '(0 1 2) '(0 0 1 2 2 3)))] +However, a sequence slice specification cannot remove axes. + +Using sequence constructors like @racket[in-range], we can pick every even-indexed row in an axis: +@interaction[#:eval typed-eval + (array-slice-ref arr (list '(1 0) '(0 1 2) (in-range 0 4 2)))] +We could also use @racket[in-range] to pick every row instead of enumerating their indexes in a +list, but that would require another kind of tedium: +@interaction[#:eval typed-eval + (define ds (array-shape arr)) + (array-slice-ref arr (list (in-range (vector-ref ds 0)) + (in-range (vector-ref ds 1)) + (in-range (vector-ref ds 2))))] +The situation calls for an @racket[in-range]-like slice specification that is aware of the lengths +of the axes it is applied to. + +@subsubsection{@racket[Slice]: pick rows in a length-aware way} + +@deftogether[(@defidform[Slice] + @defproc*[([(:: [end (U #f Integer) #f]) Slice] + [(:: [start (U #f Integer)] [end (U #f Integer)] [step Integer 1]) + Slice])])]{ +As a slice specification, a @racket[Slice] object acts like the sequence object returned +by @racket[in-range], but either @racket[start] or @racket[end] may be @racket[#f]. + +If @racket[start] is @racket[#f], it is interpreted as the first valid axis index in the direction +of @racket[step]. If @racket[end] is @racket[#f], it is interpreted as the last valid axis index +in the direction of @racket[step]. + +Possibly the most common slice is @racket[(::)], equivalent to @racket[(:: #f #f 1)]. With a +positive @racket[step = 1], @racket[start] is interpreted as @racket[0] and @racket[end] as +the length of the axis. Thus, @racket[(::)] picks all rows from any axis: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::) (::) (::)))] +The slice @racket[(:: #f #f -1)] reverses an axis: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::) (::) (:: #f #f -1)))] +The slice @racket[(:: 2 #f 1)] picks every row starting from index @racket[2]: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::) (::) (:: 2 #f 1)))] +The slice @racket[(:: 1 #f 2)] picks every odd-indexed row: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::) (::) (:: 1 #f 2)))] +Notice that every example starts with two @racket[(::)]. In fact, slicing only one axis is so +common that there is a slice specification object that represents any number of @racket[(::)]. +} + +@subsubsection{@racket[Slice-Dots]: preserve remaining axes} + +@deftogether[(@defidform[Slice-Dots] + @defthing[::... Slice-Dots])]{ +As a slice specification, a @racket[Slice-Dots] object represents any number of leftover, adjacent +axes, and preserves them all. + +For example, picking every odd-indexed row of the last axis can be done by +@interaction[#:eval typed-eval + (array-slice-ref arr (list ::... (:: 1 #f 2)))] +For @racket[arr] specifically, @racket[::...] represents two @racket[(::)]. + +Slicing only the first axis while preserving the rest can be done by +@interaction[#:eval typed-eval + (array-slice-ref arr (list '(0) ::...))] +If more than one @racket[::...] appears in the list, only the first is expanded: +@interaction[#:eval typed-eval + (array-slice-ref arr (list ::... '(1) ::...)) + (array-slice-ref arr (list ::... '(1)))] +If there are no leftover axes, @racket[::...] does nothing when placed in any position: +@interaction[#:eval typed-eval + (array-slice-ref arr (list ::... '(1) '(1) '(1))) + (array-slice-ref arr (list '(1) ::... '(1) '(1))) + (array-slice-ref arr (list '(1) '(1) ::... '(1))) + (array-slice-ref arr (list '(1) '(1) '(1) ::...))] +} + +@subsubsection{@racket[Integer]: remove an axis} + +All of the slice specifications so far preserve the dimensions of the array. Removing an axis +can be done by using an integer as a slice specification. + +This example removes the first axis by collapsing it to its first row: +@interaction[#:eval typed-eval + (array-slice-ref arr (list 0 ::...))] +Removing the second axis by collapsing it to the row with index @racket[1]: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::) 1 ::...))] +Removing the second-to-last axis (which for @racket[arr] is the same as the second): +@interaction[#:eval typed-eval + (array-slice-ref arr (list ::... 1 (::)))] +All of these examples can be done using @racket[array-axis-ref], but including it in slice +specifications can be handy: +@interaction[#:eval typed-eval + (array-slice-ref arr (list ::... 1 (:: #f #f 2)))] + +@subsubsection{@racket[Slice-New-Axis]: add an axis} + +@deftogether[(@defidform[Slice-New-Axis] + @defproc[(::new [dk Integer 1]) Slice-New-Axis])]{ +As a slice specification, @racket[(::new dk)] inserts @racket[dk] into the resulting array's +shape, in the corresponding axis position. The new axis has length @racket[dk], which must be +nonnegative. + +For example, we might conceptually wrap another @racket[#[]] around an array's data: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::new) ::...))] +Or duplicate the array twice, within two new outer rows: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::new 2) ::...))] +Of course, @racket[dk = 0] is a valid new axis length, but is usually not very useful: +@interaction[#:eval typed-eval + (array-slice-ref arr (list (::) (::new 0) ::...))] +Inserting axes can also be done using @racket[array-axis-insert]. +} + +@subsubsection{Other Slice Functions} + +@deftogether[(@defproc[(slice? [v Any]) Boolean] + @defproc[(slice-start [s Slice]) (U #f Fixnum)] + @defproc[(slice-end [s Slice]) (U #f Fixnum)] + @defproc[(slice-step [s Slice]) Fixnum])]{ +The @racket[Slice] predicate and accessors. +} + +@defproc[(slice-dots? [v Any]) Boolean]{ +Returns @racket[#t] when @racket[v] is @racket[::...]. +} + +@deftogether[(@defproc[(slice-new-axis? [v Any]) Boolean] + @defproc[(slice-new-axis-length [s Slice-New-Axis]) Index])]{ +The @racket[Slice-New-Axis] predicate and accessors. +} + +@defproc[(slice->range-values [s Slice] [dk Index]) (Values Fixnum Fixnum Fixnum)]{ +Given a slice @racket[s] and an axis length @racket[dk], returns the arguments to @racket[in-range] +that would produce an equivalent slice specification. +} + +@section{Folds} + +@;{ +array-axis-fold +array-fold +array-all-sum +array-all-prod +array-all-min +array-all-max +array-axis-count +array-all-count +array-axis-andmap +array-all-andmap +array-axis-ormap +array-all-ormap +array-all-eq? +array-all-equal? +array-all-eqv? +array-all= +array-axis-sum +array-axis-prod +array-axis-min +array-axis-max +array-lift-comparison +array-axis-fft +array-fft +array-inverse-fft +array-axis-inverse-fft +} + +@section{Transformations} + +@;{ +array-transform +array-axis-permute +array-axis-swap +array-axis-insert +array-axis-ref +array-append* +array-reshape +array-flatten +} + +@section{Subtypes} + +@subsection{Flonum Arrays} + +@;{ +FlArray +flarray-scale +flarray-round +flarray-floor +flarray-map +flarray-truncate +flarray-abs +flarray-sqr +flarray-sqrt +flarray-log +flarray-exp +flarray-sin +flarray-cos +flarray-tan +flarray-asin +flarray-acos +flarray-atan +flarray+ +flarray* +flarray-data +flarray +flarray-ceiling +array->flarray +inline-flarray-map +flarray- +flarray/ +flarray-expt +flarray-min +flarray-max +flarray= +flarray< +flarray<= +flarray> +flarray>= +} + +@subsection{Float-Complex Arrays} + +@;{ +FCArray +fcarray-fft +inline-fcarray-map +fcarray-imag-data +fcarray-real-data +fcarray +array->fcarray +fcarray-scale +fcarray-sqr +fcarray-sqrt +fcarray-conjugate +fcarray-magnitude +fcarray-angle +fcarray-exp +fcarray-sin +fcarray-cos +fcarray-tan +fcarray-acos +fcarray+ +fcarray* +fcarray- +fcarray/ +fcarray-expt +fcarray-real-part +fcarray-imag-part +fcarray-make-rectangular +fcarray-map +fcarray-log +fcarray-asin +fcarray-atan +} + +@section{Unsafe Operations} + +@;{ +unsafe-array-ref +unsafe-array-set! +unsafe-array-proc +unsafe-settable-array-set-proc +unsafe-fcarray +in-unsafe-array-indexes +make-unsafe-array-set-proc +make-unsafe-array-proc +unsafe-build-array +unsafe-mutable-array +unsafe-flarray +unsafe-array-transform +} + +@;{ +Don't know whether or how to document these yet: + +parallel-array-strict +parallel-array->mutable-array +array/syntax +flat-vector->matrix +} + +@(close-eval typed-eval) diff --git a/collects/math/scribblings/rename-defines.rkt b/collects/math/scribblings/rename-defines.rkt index 29ca0c655a..b9e2f5e4e2 100644 --- a/collects/math/scribblings/rename-defines.rkt +++ b/collects/math/scribblings/rename-defines.rkt @@ -1,6 +1,7 @@ #lang racket/base -(require (for-syntax racket/base)) +(require (for-syntax racket/base) + (only-in typed/racket/base define:)) (provide rename-defines) diff --git a/collects/math/tests/array-tests.rkt b/collects/math/tests/array-tests.rkt index 0791cef6ac..c4a80a29c4 100644 --- a/collects/math/tests/array-tests.rkt +++ b/collects/math/tests/array-tests.rkt @@ -164,8 +164,8 @@ (let ([arr (build-array #(3 3) (inst vector->list Index))]) (check-equal? (array->vector* arr) #[#[(0 0) (0 1) (0 2)] - #[(1 0) (1 1) (1 2)] - #[(2 0) (2 1) (2 2)]]) + #[(1 0) (1 1) (1 2)] + #[(2 0) (2 1) (2 2)]]) (check-equal? arr (vector*->array (array->vector* arr) listof-index?))) (let ([arr (build-array #(2 2 2) (inst vector->list Index))]) @@ -193,37 +193,37 @@ (check-equal? (array 1.0) (list*->array 1.0 flonum?)) -(check-equal? (array []) +(check-equal? (array #[]) (list*->array '[] flonum?)) -(check-equal? (array [[]]) +(check-equal? (array #[#[]]) (list*->array '[[]] flonum?)) -(check-equal? (array [1.0]) +(check-equal? (array #[1.0]) (list*->array '[1.0] flonum?)) -(check-equal? (array [[1.0]]) +(check-equal? (array #[#[1.0]]) (list*->array '[[1.0]] flonum?)) -(check-equal? (array [[[1.0]]]) +(check-equal? (array #[#[#[1.0]]]) (list*->array '[[[1.0]]] flonum?)) (check-equal? (mutable-array 1.0) (list*->array 1.0 flonum?)) -(check-equal? (mutable-array []) +(check-equal? (mutable-array #[]) (list*->array '[] flonum?)) -(check-equal? (mutable-array [[]]) +(check-equal? (mutable-array #[#[]]) (list*->array '[[]] flonum?)) -(check-equal? (mutable-array [1.0]) +(check-equal? (mutable-array #[1.0]) (list*->array '[1.0] flonum?)) -(check-equal? (mutable-array [[1.0]]) +(check-equal? (mutable-array #[#[1.0]]) (list*->array '[[1.0]] flonum?)) -(check-equal? (mutable-array [[[1.0]]]) +(check-equal? (mutable-array #[#[#[1.0]]]) (list*->array '[[[1.0]]] flonum?)) ;; --------------------------------------------------------------------------------------------------- @@ -239,8 +239,8 @@ (check-exn exn? (λ () (axis-index-array #() 0))) (check-equal? (index-array #()) (array 0)) -(check-equal? (index-array #(4)) (array [0 1 2 3])) -(check-equal? (index-array #(2 2)) (array [[0 1] [2 3]])) +(check-equal? (index-array #(4)) (array #[0 1 2 3])) +(check-equal? (index-array #(2 2)) (array #[#[0 1] #[2 3]])) (check-equal? (indexes-array #(3 3)) (build-array #(3 3) (λ: ([js : Indexes]) (vector-copy js)))) @@ -249,56 +249,56 @@ (array 1.0)) (check-equal? (diagonal-array 1 0 1.0 0.0) - (array [])) + (array #[])) (check-equal? (diagonal-array 1 1 1.0 0.0) - (array [1.0])) + (array #[1.0])) (check-equal? (diagonal-array 2 1 1.0 0.0) - (array [[1.0]])) + (array #[#[1.0]])) (check-equal? (diagonal-array 2 2 1.0 0.0) - (array [[1.0 0.0] [0.0 1.0]])) + (array #[#[1.0 0.0] #[0.0 1.0]])) (check-equal? (diagonal-array 3 2 1.0 0.0) - (array [[[1.0 0.0] [0.0 0.0]] - [[0.0 0.0] [0.0 1.0]]])) + (array #[#[#[1.0 0.0] #[0.0 0.0]] + #[#[0.0 0.0] #[0.0 1.0]]])) ;; --------------------------------------------------------------------------------------------------- ;; Pointwise ;; Not much to test here because most pointwise ops are defined by the same two lifts -(check-equal? (array-sqrt (array [1.0 4.0 9.0 16.0])) - (array [1.0 2.0 3.0 4.0])) +(check-equal? (array-sqrt (array #[1.0 4.0 9.0 16.0])) + (array #[1.0 2.0 3.0 4.0])) -(check-equal? (array+ (array [1.0 2.0]) - (array [10.0 20.0])) - (array [11.0 22.0])) +(check-equal? (array+ (array #[1.0 2.0]) + (array #[10.0 20.0])) + (array #[11.0 22.0])) -(check-equal? (array-map inexact->exact (array [1.0 2.0])) - (array [1 2])) +(check-equal? (array-map inexact->exact (array #[1.0 2.0])) + (array #[1 2])) (check-equal? (array-map (inst cons Float Float) - (array [1.0 2.0]) - (array [10.0 20.0])) + (array #[1.0 2.0]) + (array #[10.0 20.0])) (make-mutable-array #(2) #[(1.0 . 10.0) (2.0 . 20.0)])) ;; --------------------------------------------------------------------------------------------------- ;; Fold -(let ([arr (array [[ 1.0 4.0 9.0 16.0] - [-1.0 -4.0 -9.0 -16.0]])]) - (check-equal? (array-axis-sum arr 0) (array [0.0 0.0 0.0 0.0])) - (check-equal? (array-axis-prod arr 0) (array [-1.0 -16.0 -81.0 -256.0])) - (check-equal? (array-axis-min arr 0) (array [-1.0 -4.0 -9.0 -16.0])) - (check-equal? (array-axis-max arr 0) (array [ 1.0 4.0 9.0 16.0])) +(let ([arr (array #[#[ 1.0 4.0 9.0 16.0] + #[-1.0 -4.0 -9.0 -16.0]])]) + (check-equal? (array-axis-sum arr 0) (array #[0.0 0.0 0.0 0.0])) + (check-equal? (array-axis-prod arr 0) (array #[-1.0 -16.0 -81.0 -256.0])) + (check-equal? (array-axis-min arr 0) (array #[-1.0 -4.0 -9.0 -16.0])) + (check-equal? (array-axis-max arr 0) (array #[ 1.0 4.0 9.0 16.0])) (check-equal? (array-axis-fold arr 0 (inst cons Float (Listof Float)) null) (list*->array '[[-1.0 1.0] [-4.0 4.0] [-9.0 9.0] [-16.0 16.0]] listof-flonum?)) - (check-equal? (array-axis-sum arr 1) (array [30.0 -30.0])) - (check-equal? (array-axis-prod arr 1) (array [576.0 576.0])) - (check-equal? (array-axis-min arr 1) (array [1.0 -16.0])) - (check-equal? (array-axis-max arr 1) (array [16.0 -1.0])) + (check-equal? (array-axis-sum arr 1) (array #[30.0 -30.0])) + (check-equal? (array-axis-prod arr 1) (array #[576.0 576.0])) + (check-equal? (array-axis-min arr 1) (array #[1.0 -16.0])) + (check-equal? (array-axis-max arr 1) (array #[16.0 -1.0])) (check-equal? (array-axis-fold arr 1 (inst cons Float (Listof Float)) null) (list*->array '[[ 16.0 9.0 4.0 1.0] [-16.0 -9.0 -4.0 -1.0]] @@ -309,18 +309,18 @@ (check-equal? (array-all-max arr) 16.0)) (let ([arr (make-array #(3 0) 0)]) - (check-equal? (array-axis-sum arr 0) (array [])) - (check-equal? (array-axis-prod arr 0) (array [])) - (check-equal? (array-axis-min arr 0) (array [])) - (check-equal? (array-axis-max arr 0) (array [])) + (check-equal? (array-axis-sum arr 0) (array #[])) + (check-equal? (array-axis-prod arr 0) (array #[])) + (check-equal? (array-axis-min arr 0) (array #[])) + (check-equal? (array-axis-max arr 0) (array #[])) (check-exn exn? (λ () (array-axis-sum arr 1))) (check-exn exn? (λ () (array-axis-prod arr 1))) (check-exn exn? (λ () (array-axis-min arr 1))) (check-exn exn? (λ () (array-axis-max arr 1))) - (check-equal? (array-axis-sum arr 1 0) (array [0 0 0])) - (check-equal? (array-axis-min arr 1 +inf.0) (array [+inf.0 +inf.0 +inf.0])) - (check-equal? (array-axis-max arr 1 -inf.0) (array [-inf.0 -inf.0 -inf.0])) - (check-equal? (array-axis-prod arr 1 1) (array [1 1 1])) + (check-equal? (array-axis-sum arr 1 0) (array #[0 0 0])) + (check-equal? (array-axis-min arr 1 +inf.0) (array #[+inf.0 +inf.0 +inf.0])) + (check-equal? (array-axis-max arr 1 -inf.0) (array #[-inf.0 -inf.0 -inf.0])) + (check-equal? (array-axis-prod arr 1 1) (array #[1 1 1])) (check-exn exn? (λ () (array-all-sum arr))) (check-exn exn? (λ () (array-all-prod arr))) (check-exn exn? (λ () (array-all-min arr))) @@ -340,29 +340,29 @@ (check-equal? (array-all-min arr) 0) (check-equal? (array-all-max arr) 0)) -(let ([arr (array [[1.0 1.0 2.0 3.0] [0.0 -1.0 2.0 3.0]])]) - (check-equal? (array-axis-count arr 0 positive?) (array [1 1 2 2])) - (check-equal? (array-axis-count arr 1 positive?) (array [4 2])) +(let ([arr (array #[#[1.0 1.0 2.0 3.0] #[0.0 -1.0 2.0 3.0]])]) + (check-equal? (array-axis-count arr 0 positive?) (array #[1 1 2 2])) + (check-equal? (array-axis-count arr 1 positive?) (array #[4 2])) (check-equal? (array-all-count arr positive?) 6)) -(let ([arr (array [[1.0 1.0 2.0 3.0] [0.0 -1.0 2.0 3.0]])]) - (check-equal? (array-axis-andmap arr 0 positive?) (array [#f #f #t #t])) - (check-equal? (array-axis-andmap arr 1 positive?) (array [#t #f])) +(let ([arr (array #[#[1.0 1.0 2.0 3.0] #[0.0 -1.0 2.0 3.0]])]) + (check-equal? (array-axis-andmap arr 0 positive?) (array #[#f #f #t #t])) + (check-equal? (array-axis-andmap arr 1 positive?) (array #[#t #f])) (check-equal? (array-all-andmap arr positive?) #f)) -(let ([arr (array [[1.0 1.0 2.0 3.0] [2.0 3.0 2.0 3.0]])]) - (check-equal? (array-axis-andmap arr 0 positive?) (array [#t #t #t #t])) - (check-equal? (array-axis-andmap arr 1 positive?) (array [#t #t])) +(let ([arr (array #[#[1.0 1.0 2.0 3.0] #[2.0 3.0 2.0 3.0]])]) + (check-equal? (array-axis-andmap arr 0 positive?) (array #[#t #t #t #t])) + (check-equal? (array-axis-andmap arr 1 positive?) (array #[#t #t])) (check-equal? (array-all-andmap arr positive?) #t)) -(let ([arr (array [[-1.0 -1.0 -2.0 -3.0] [0.0 -1.0 2.0 3.0]])]) - (check-equal? (array-axis-ormap arr 0 positive?) (array [#f #f #t #t])) - (check-equal? (array-axis-ormap arr 1 positive?) (array [#f #t])) +(let ([arr (array #[#[-1.0 -1.0 -2.0 -3.0] #[0.0 -1.0 2.0 3.0]])]) + (check-equal? (array-axis-ormap arr 0 positive?) (array #[#f #f #t #t])) + (check-equal? (array-axis-ormap arr 1 positive?) (array #[#f #t])) (check-equal? (array-all-ormap arr positive?) #t)) -(let ([arr (array [[-1.0 -1.0 -2.0 -3.0] [-2.0 -3.0 -2.0 -3.0]])]) - (check-equal? (array-axis-ormap arr 0 positive?) (array [#f #f #f #f])) - (check-equal? (array-axis-ormap arr 1 positive?) (array [#f #f])) +(let ([arr (array #[#[-1.0 -1.0 -2.0 -3.0] #[-2.0 -3.0 -2.0 -3.0]])]) + (check-equal? (array-axis-ormap arr 0 positive?) (array #[#f #f #f #f])) + (check-equal? (array-axis-ormap arr 1 positive?) (array #[#f #f])) (check-equal? (array-all-ormap arr positive?) #f)) (let ([arr (make-array #() 0.0)]) @@ -376,12 +376,12 @@ (check-equal? (array-all-ormap arr positive?) #t)) (let ([arr (make-array #(4 0) 0.0)]) - (check-equal? (array-axis-count arr 0 positive?) (array [])) - (check-equal? (array-axis-andmap arr 0 positive?) (array [])) - (check-equal? (array-axis-ormap arr 0 positive?) (array [])) - (check-equal? (array-axis-count arr 1 positive?) (array [0 0 0 0])) - (check-equal? (array-axis-andmap arr 1 positive?) (array [#t #t #t #t])) - (check-equal? (array-axis-ormap arr 1 positive?) (array [#f #f #f #f])) + (check-equal? (array-axis-count arr 0 positive?) (array #[])) + (check-equal? (array-axis-andmap arr 0 positive?) (array #[])) + (check-equal? (array-axis-ormap arr 0 positive?) (array #[])) + (check-equal? (array-axis-count arr 1 positive?) (array #[0 0 0 0])) + (check-equal? (array-axis-andmap arr 1 positive?) (array #[#t #t #t #t])) + (check-equal? (array-axis-ormap arr 1 positive?) (array #[#f #f #f #f])) (check-equal? (array-all-count arr positive?) 0) (check-equal? (array-all-andmap arr positive?) #t) (check-equal? (array-all-ormap arr positive?) #f)) @@ -394,11 +394,11 @@ (check-exn exn? (λ () (array-fft (make-array #(3) 1)))) (let ([arr (make-array #(4) 1)]) - (check array-all= (array-fft arr) (array [4 0 0 0])) + (check array-all= (array-fft arr) (array #[4 0 0 0])) (check array-all= (array-inverse-fft (array-fft arr)) arr)) (let ([arr (make-array #(2 2) 1)]) - (check array-all= (array-fft arr) (array [[4 0] [0 0]])) + (check array-all= (array-fft arr) (array #[#[4 0] #[0 0]])) (check array-all= (array-inverse-fft (array-fft arr)) arr)) ;; --------------------------------------------------------------------------------------------------- @@ -581,32 +581,32 @@ ;; --------------------------------------------------------------------------------------------------- ;; Indexing -(let ([arr (mutable-array [[0 1 2 3 4 5] - [1 2 3 4 5 6] - [2 3 4 5 6 7] - [3 4 5 6 7 8]] - (U Integer Symbol))] - [idxs (array [#(0 0) #(1 1) #(1 2) #(2 3) #(3 4) #(3 5)])] - [vals (array ['a 'b 'c 'd 'e 'f])] +(let ([arr (mutable-array #[#[0 1 2 3 4 5] + #[1 2 3 4 5 6] + #[2 3 4 5 6 7] + #[3 4 5 6 7 8]] + : (U Integer Symbol))] + [idxs (array #['#(0 0) '#(1 1) '#(1 2) '#(2 3) '#(3 4) '#(3 5)])] + [vals (array #['a 'b 'c 'd 'e 'f])] [slices (list (:: 0 4 2) (:: 2 -1 -2))]) (check-equal? (array-indexes-ref arr idxs) - (array [0 2 3 5 7 8])) + (array #[0 2 3 5 7 8])) (check-equal? (array-indexes-ref arr (slice-indexes-array (array-shape arr) slices)) (array-slice-ref arr slices)) (array-indexes-set! arr idxs vals) - (check-equal? arr (array [['a 1 2 3 4 5] - [1 'b 'c 4 5 6] - [2 3 4 'd 6 7] - [3 4 5 6 'e 'f]]))) + (check-equal? arr (array #[#['a 1 2 3 4 5] + #[1 'b 'c 4 5 6] + #[2 3 4 'd 6 7] + #[3 4 5 6 'e 'f]]))) (check-equal? (array-slice-ref (indexes-array #()) (list (::new 2))) (make-array #(2) #())) (let ([arr (indexes-array #(2))]) (check-equal? (array-slice-ref arr (list (::) (::new 2))) - (array [[#(0) #(0)] [#(1) #(1)]])) + (array #[#['#(0) '#(0)] #['#(1) '#(1)]])) (check-equal? (array-slice-ref arr (list (::new 2) (::))) - (array [[#(0) #(1)] [#(0) #(1)]])) + (array #[#['#(0) '#(1)] #['#(0) '#(1)]])) (check-equal? (array-slice-ref arr (list (::))) arr) (check-equal? (array-slice-ref arr (list ::...)) @@ -614,21 +614,21 @@ (let ([arr (index-array #(10))]) (check-equal? (array-slice-ref arr (list (:: 0 10 2))) - (array [0 2 4 6 8])) + (array #[0 2 4 6 8])) (check-equal? (array-slice-ref arr (list (:: #f 10 2))) - (array [0 2 4 6 8])) + (array #[0 2 4 6 8])) (check-equal? (array-slice-ref arr (list (:: 0 #f 2))) - (array [0 2 4 6 8])) + (array #[0 2 4 6 8])) (check-equal? (array-slice-ref arr (list (:: #f #f 2))) - (array [0 2 4 6 8])) + (array #[0 2 4 6 8])) (check-equal? (array-slice-ref arr (list (:: 9 -1 -2))) - (array [9 7 5 3 1])) + (array #[9 7 5 3 1])) (check-equal? (array-slice-ref arr (list (:: 9 #f -2))) - (array [9 7 5 3 1])) + (array #[9 7 5 3 1])) (check-equal? (array-slice-ref arr (list (:: #f -1 -2))) - (array [9 7 5 3 1])) + (array #[9 7 5 3 1])) (check-equal? (array-slice-ref arr (list (:: #f #f -2))) - (array [9 7 5 3 1])) + (array #[9 7 5 3 1])) (check-equal? (array-slice-ref arr (list 4)) (array 4)) (check-exn exn? (λ () (array-slice-ref arr (list -1)))) @@ -642,15 +642,15 @@ (check-equal? (array-slice-ref arr (list ::...)) arr) (check-equal? (array-slice-ref arr (list (:: 0 4 2) (:: 0 4 2))) - (array [[0 2] [8 10]])) + (array #[#[0 2] #[8 10]])) (check-equal? (array-slice-ref arr (list (:: 0 4 2) ::...)) - (array [[0 1 2 3] [8 9 10 11]])) + (array #[#[0 1 2 3] #[8 9 10 11]])) (check-equal? (array-slice-ref arr (list ::... (:: 0 4 2))) - (array [[0 2] [4 6] [8 10] [12 14]])) + (array #[#[0 2] #[4 6] #[8 10] #[12 14]])) (check-equal? (array-slice-ref arr (list (:: 0 4 2) 0)) - (array [0 8])) + (array #[0 8])) (check-equal? (array-slice-ref arr (list 0 (:: 0 4 2))) - (array [0 2])) + (array #[0 2])) (check-equal? (array-slice-ref arr (list (::new 0) ::...)) (make-array #(0 4 4) 0)) (check-equal? (array-slice-ref arr (list (::new 1) ::...)) @@ -667,8 +667,8 @@ #(2 4) (λ: ([js : (Vectorof Index)]) (match-define (vector j0 j1) js) (vector j1 j0))) - (array [[#(0 0) #(1 0) #(2 0) #(3 0)] - [#(0 1) #(1 1) #(2 1) #(3 1)]])) + (array #[#['#(0 0) '#(1 0) '#(2 0) '#(3 0)] + #['#(0 1) '#(1 1) '#(2 1) '#(3 1)]])) (check-exn exn? (λ () (array-strict (array-transform (indexes-array #(2 2)) #(3 3) identity)))) (check-exn exn? (λ () (array-strict (array-transform (indexes-array #(2 2)) #(2) identity)))) @@ -678,8 +678,8 @@ (λ: ([js : (Vectorof Index)]) (match-define (vector j0 j1) js) ((inst vector Index) j1 j0))) - (array [[#(0 0) #(1 0) #(2 0) #(3 0)] - [#(0 1) #(1 1) #(2 1) #(3 1)]])) + (array #[#['#(0 0) '#(1 0) '#(2 0) '#(3 0)] + #['#(0 1) '#(1 1) '#(2 1) '#(3 1)]])) ;; Permutation @@ -697,8 +697,8 @@ (check-exn exn? (λ () (array-axis-permute arr '()))) (check-equal? (array-axis-permute arr '(0 1)) arr) (check-equal? (array-axis-permute arr '(1 0)) - (array [[#(0 0) #(1 0)] - [#(0 1) #(1 1)]]))) + (array #[#['#(0 0) '#(1 0)] + #['#(0 1) '#(1 1)]]))) ;; Transposition @@ -710,28 +710,31 @@ (check-exn exn? (λ () (array-axis-swap arr 0 2))) (check-equal? (array-axis-swap arr 0 0) arr) (check-equal? (array-axis-swap arr 1 0) - (array [[#(0 0) #(1 0)] - [#(0 1) #(1 1)]]))) + (array #[#['#(0 0) '#(1 0)] + #['#(0 1) '#(1 1)]]))) (check-equal? (array-axis-swap (indexes-array #(2 2 2)) 1 2) - (array [[[#(0 0 0) #(0 1 0)] - [#(0 0 1) #(0 1 1)]] - [[#(1 0 0) #(1 1 0)] - [#(1 0 1) #(1 1 1)]]])) + (array #[#[#['#(0 0 0) '#(0 1 0)] + #['#(0 0 1) '#(0 1 1)]] + #[#['#(1 0 0) '#(1 1 0)] + #['#(1 0 1) '#(1 1 1)]]])) ;; Adding axes (let ([arr (indexes-array #())]) (check-exn exn? (λ () (array-axis-insert arr 1 1))) (check-equal? (array-axis-insert arr 0 2) - (array [#() #()]))) + (array #['#() '#()]))) (let ([arr (indexes-array #(4))]) (check-equal? (array-axis-insert arr 0 2) - (array [[#(0) #(1) #(2) #(3)] - [#(0) #(1) #(2) #(3)]])) + (array #[#['#(0) '#(1) '#(2) '#(3)] + #['#(0) '#(1) '#(2) '#(3)]])) (check-equal? (array-axis-insert arr 1 2) - (array [[#(0) #(0)] [#(1) #(1)] [#(2) #(2)] [#(3) #(3)]]))) + (array #[#['#(0) '#(0)] + #['#(1) '#(1)] + #['#(2) '#(2)] + #['#(3) '#(3)]]))) ;; Removing axes @@ -745,55 +748,55 @@ (let ([arr (indexes-array #(2 2))]) (check-equal? (array-axis-ref arr 0 0) - (array [#(0 0) #(0 1)])) + (array #['#(0 0) '#(0 1)])) (check-equal? (array-axis-ref arr 1 0) - (array [#(0 0) #(1 0)]))) + (array #['#(0 0) '#(1 0)]))) ;; Reshape, flatten (let ([arr (indexes-array #())]) (check-exn exn? (λ () (array-reshape arr #(0)))) (check-equal? (array-reshape arr #(1)) - (array [#()])) + (array #['#()])) (check-equal? (array-flatten arr) - (array [#()]))) + (array #['#()]))) (let ([arr (array-map (λ: ([js : Indexes]) (vector-ref js 0)) (indexes-array #(4)))]) (check-exn exn? (λ () (array-reshape arr #()))) (check-equal? (array-reshape arr #(2 2)) - (array [[0 1] [2 3]])) + (array #[#[0 1] #[2 3]])) (check-equal? (array-flatten arr) - (array [0 1 2 3]))) + (array #[0 1 2 3]))) ;; Append (check-exn exn? (λ () (array-append* (list)))) (check-exn exn? (λ () (array-append* (list (array 0) (array 1))))) -(check-equal? (array-append* (list (array [0]))) - (array [0])) -(check-equal? (array-append* (list (array [0]) (array [1]))) - (array [0 1])) -(check-equal? (array-append* (list (array [0]) (array 1))) - (array [0 1])) -(check-equal? (array-append* (list (array 0) (array [1]))) - (array [0 1])) -(check-equal? (array-append* (list (array [0 1 2]) (array [3 4 5]))) - (array [0 1 2 3 4 5])) -(check-exn exn? (λ () (array-append* (list (array [0]) (array [1])) 1))) +(check-equal? (array-append* (list (array #[0]))) + (array #[0])) +(check-equal? (array-append* (list (array #[0]) (array #[1]))) + (array #[0 1])) +(check-equal? (array-append* (list (array #[0]) (array 1))) + (array #[0 1])) +(check-equal? (array-append* (list (array 0) (array #[1]))) + (array #[0 1])) +(check-equal? (array-append* (list (array #[0 1 2]) (array #[3 4 5]))) + (array #[0 1 2 3 4 5])) +(check-exn exn? (λ () (array-append* (list (array #[0]) (array #[1])) 1))) -(check-equal? (array-append* (list (array [[0 1] [2 3]]) (array [[4 5] [6 7]]))) - (array [[0 1] [2 3] [4 5] [6 7]])) -(check-equal? (array-append* (list (array [[0 1] [2 3]]) (array [[4 5] [6 7]])) 1) - (array [[0 1 4 5] [2 3 6 7]])) -(check-equal? (array-append* (list (array [[0 1] [2 3]]) (array 0)) 0) - (array [[0 1] [2 3] [0 0]])) -(check-equal? (array-append* (list (array [[0 1] [2 3]]) (array 0)) 1) - (array [[0 1 0] [2 3 0]])) -(check-equal? (array-append* (list (array [[0 1 2] [3 4 5]]) (array [0 1])) 1) - (array [[0 1 2 0 1] [3 4 5 0 1]])) -(check-exn exn? (λ () (array-append* (list (array [[0 1 2] [3 4 5]]) (array [0 1])) 0))) +(check-equal? (array-append* (list (array #[#[0 1] #[2 3]]) (array #[#[4 5] #[6 7]]))) + (array #[#[0 1] #[2 3] #[4 5] #[6 7]])) +(check-equal? (array-append* (list (array #[#[0 1] #[2 3]]) (array #[#[4 5] #[6 7]])) 1) + (array #[#[0 1 4 5] #[2 3 6 7]])) +(check-equal? (array-append* (list (array #[#[0 1] #[2 3]]) (array 0)) 0) + (array #[#[0 1] #[2 3] #[0 0]])) +(check-equal? (array-append* (list (array #[#[0 1] #[2 3]]) (array 0)) 1) + (array #[#[0 1 0] #[2 3 0]])) +(check-equal? (array-append* (list (array #[#[0 1 2] #[3 4 5]]) (array #[0 1])) 1) + (array #[#[0 1 2 0 1] #[3 4 5 0 1]])) +(check-exn exn? (λ () (array-append* (list (array #[#[0 1 2] #[3 4 5]]) (array #[0 1])) 0))) ;; --------------------------------------------------------------------------------------------------- ;; Comprehensions @@ -803,7 +806,7 @@ (check-equal? (for/array: #:shape #() () : Symbol 'foo) (mutable-array 'foo)) (check-equal? (for/array: #:shape #(2) ([x (in-naturals)]) : Integer x) - (mutable-array [0 1])) + (mutable-array #[0 1])) (check-equal? (for/array: #:shape #(2 3) ([i (in-range 0 6)]) : (Vectorof Integer) (vector (quotient i 3) (remainder i 3))) (indexes-array #(2 3))) @@ -813,7 +816,7 @@ (check-equal? (for*/array: #:shape #() () : Symbol 'foo) (mutable-array 'foo)) (check-equal? (for*/array: #:shape #(2) ([x (in-naturals)]) : Integer x) - (mutable-array [0 1])) + (mutable-array #[0 1])) (check-equal? (for*/array: #:shape #(2 3) ([i (in-range 0 2)] [j (in-range 0 3)] ) : (Vectorof Integer) @@ -838,7 +841,7 @@ ;; --------------------------------------------------------------------------------------------------- ;; Sequences -(check-equal? (for/list: : (Listof Number) ([x (in-array (array [[1 2 3] [4 5 6]]))]) x) +(check-equal? (for/list: : (Listof Number) ([x (in-array (array #[#[1 2 3] #[4 5 6]]))]) x) '(1 2 3 4 5 6)) (check-equal? (for/list: : (Listof Indexes) ([js (in-array (indexes-array #()))]) js) @@ -894,26 +897,26 @@ (array->list* (array-axis-swap arr 0 1)))) (check-equal? (array-list->array empty 0) - (array [])) + (array #[])) (check-exn exn? (λ () (array-list->array empty 1))) (check-equal? (array-list->array (list (array 0) (array 1) (array 2)) 0) - (array [0 1 2])) + (array #[0 1 2])) (check-exn exn? (λ () (array-list->array (list (array 0) (array 1) (array 2)) 1))) -(check-equal? (array-list->array (list (array [0 1]) (array [2 3])) 0) - (array [[0 1] [2 3]])) -(check-equal? (array-list->array (list (array [0 1]) (array [2 3])) 1) - (array [[0 2] [1 3]])) +(check-equal? (array-list->array (list (array #[0 1]) (array #[2 3])) 0) + (array #[#[0 1] #[2 3]])) +(check-equal? (array-list->array (list (array #[0 1]) (array #[2 3])) 1) + (array #[#[0 2] #[1 3]])) -(check-equal? (array-list->array (list (array [0 1 2]) (array 0)) 0) - (array [[0 1 2] [0 0 0]])) -(check-equal? (array-list->array (list (array [0 1 2]) (array 0)) 1) - (array [[0 0] [1 0] [2 0]])) -(check-exn exn? (λ () (array-list->array (list (array [0 1 2]) (array 0)) 2))) +(check-equal? (array-list->array (list (array #[0 1 2]) (array 0)) 0) + (array #[#[0 1 2] #[0 0 0]])) +(check-equal? (array-list->array (list (array #[0 1 2]) (array 0)) 1) + (array #[#[0 0] #[1 0] #[2 0]])) +(check-exn exn? (λ () (array-list->array (list (array #[0 1 2]) (array 0)) 2))) -(check-equal? (array-list->array (list (array [[0 1] [2 3]]) (array 0)) 0) - (array [[[0 1] [2 3]] [[0 0] [0 0]]])) +(check-equal? (array-list->array (list (array #[#[0 1] #[2 3]]) (array 0)) 0) + (array #[#[#[0 1] #[2 3]] #[#[0 0] #[0 0]]])) (check-equal? (array->array-list (array-list->array empty 0) 0) empty) @@ -922,18 +925,18 @@ (check-equal? (array->array-list (array-list->array (list (array 0) (array 1) (array 2)) 0) 0) (list (array 0) (array 1) (array 2))) -(check-equal? (array->array-list (array-list->array (list (array [0 1]) (array [2 3])) 0) 0) - (list (array [0 1]) (array [2 3]))) -(check-equal? (array->array-list (array-list->array (list (array [0 1]) (array [2 3])) 1) 1) - (list (array [0 1]) (array [2 3]))) +(check-equal? (array->array-list (array-list->array (list (array #[0 1]) (array #[2 3])) 0) 0) + (list (array #[0 1]) (array #[2 3]))) +(check-equal? (array->array-list (array-list->array (list (array #[0 1]) (array #[2 3])) 1) 1) + (list (array #[0 1]) (array #[2 3]))) -(check-equal? (array->array-list (array-list->array (list (array [0 1 2]) (array 0)) 0) 0) - (list (array [0 1 2]) (array [0 0 0]))) -(check-equal? (array->array-list (array-list->array (list (array [0 1 2]) (array 0)) 1) 1) - (list (array [0 1 2]) (array [0 0 0]))) +(check-equal? (array->array-list (array-list->array (list (array #[0 1 2]) (array 0)) 0) 0) + (list (array #[0 1 2]) (array #[0 0 0]))) +(check-equal? (array->array-list (array-list->array (list (array #[0 1 2]) (array 0)) 1) 1) + (list (array #[0 1 2]) (array #[0 0 0]))) -(check-equal? (array->array-list (array-list->array (list (array [[0 1] [2 3]]) (array 0)) 0) 0) - (list (array [[0 1] [2 3]]) (array [[0 0] [0 0]]))) +(check-equal? (array->array-list (array-list->array (list (array #[#[0 1] #[2 3]]) (array 0)) 0) 0) + (list (array #[#[0 1] #[2 3]]) (array #[#[0 0] #[0 0]]))) ;; --------------------------------------------------------------------------------------------------- ;; Conditionals diff --git a/collects/math/tests/mandelbrot-test.rkt b/collects/math/tests/mandelbrot-test.rkt index 9d603e11b6..4092ddd1e6 100644 --- a/collects/math/tests/mandelbrot-test.rkt +++ b/collects/math/tests/mandelbrot-test.rkt @@ -23,21 +23,21 @@ divtime) (check-equal? (mandelbrot 0.2 20) - (array [[0 1 1 1 1 1 1 1 1 1 1 1 1 1 1] - [1 1 1 1 1 1 2 2 2 3 2 2 1 1 1] - [1 1 1 1 1 2 2 2 3 6 20 3 2 1 1] - [1 1 1 2 2 2 3 4 5 20 17 4 3 2 1] - [1 1 2 2 3 3 4 11 20 20 20 10 14 2 2] - [1 2 3 4 6 6 6 20 20 20 20 20 9 3 2] - [2 3 4 6 18 20 14 20 20 20 20 20 20 3 2] - [20 20 20 20 20 20 20 20 20 20 20 20 5 3 2] - [2 3 4 6 18 20 14 20 20 20 20 20 20 3 2] - [1 2 3 4 6 6 6 20 20 20 20 20 9 3 2] - [1 1 2 2 3 3 4 11 20 20 20 10 14 2 2] - [1 1 1 2 2 2 3 4 5 20 17 4 3 2 1] - [1 1 1 1 1 2 2 2 3 6 20 3 2 1 1] - [1 1 1 1 1 1 2 2 2 3 2 2 1 1 1] - [0 1 1 1 1 1 1 1 1 1 1 1 1 1 1]])) + (array #[#[0 1 1 1 1 1 1 1 1 1 1 1 1 1 1] + #[1 1 1 1 1 1 2 2 2 3 2 2 1 1 1] + #[1 1 1 1 1 2 2 2 3 6 20 3 2 1 1] + #[1 1 1 2 2 2 3 4 5 20 17 4 3 2 1] + #[1 1 2 2 3 3 4 11 20 20 20 10 14 2 2] + #[1 2 3 4 6 6 6 20 20 20 20 20 9 3 2] + #[2 3 4 6 18 20 14 20 20 20 20 20 20 3 2] + #[20 20 20 20 20 20 20 20 20 20 20 20 5 3 2] + #[2 3 4 6 18 20 14 20 20 20 20 20 20 3 2] + #[1 2 3 4 6 6 6 20 20 20 20 20 9 3 2] + #[1 1 2 2 3 3 4 11 20 20 20 10 14 2 2] + #[1 1 1 2 2 2 3 4 5 20 17 4 3 2 1] + #[1 1 1 1 1 2 2 2 3 6 20 3 2 1 1] + #[1 1 1 1 1 1 2 2 2 3 2 2 1 1 1] + #[0 1 1 1 1 1 1 1 1 1 1 1 1 1 1]])) (begin (require images/flomap)