(define rv (fn (arr) (get arr :ravel)))
(define sh (fn (arr) (get arr :shape)))

(apl-test
  "reduce +/ vector"
  (rv (apl-reduce apl-add (make-array (list 5) (list 1 2 3 4 5))))
  (list 15))

(apl-test
  "reduce x/ vector"
  (rv (apl-reduce apl-mul (make-array (list 4) (list 1 2 3 4))))
  (list 24))

(apl-test
  "reduce max/ vector"
  (rv (apl-reduce apl-max (make-array (list 5) (list 3 1 4 1 5))))
  (list 5))

(apl-test
  "reduce min/ vector"
  (rv (apl-reduce apl-min (make-array (list 3) (list 3 1 4))))
  (list 1))

(apl-test
  "reduce and/ all true"
  (rv (apl-reduce apl-and (make-array (list 3) (list 1 1 1))))
  (list 1))

(apl-test
  "reduce or/ with true"
  (rv (apl-reduce apl-or (make-array (list 3) (list 0 0 1))))
  (list 1))

(apl-test
  "reduce +/ single element"
  (rv (apl-reduce apl-add (make-array (list 1) (list 42))))
  (list 42))

(apl-test
  "reduce +/ scalar no-op"
  (rv (apl-reduce apl-add (apl-scalar 7)))
  (list 7))

(apl-test
  "reduce +/ shape is scalar"
  (sh (apl-reduce apl-add (make-array (list 4) (list 1 2 3 4))))
  (list))

(apl-test
  "reduce +/ matrix row sums shape"
  (sh (apl-reduce apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 2))

(apl-test
  "reduce +/ matrix row sums values"
  (rv (apl-reduce apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 6 15))

(apl-test
  "reduce max/ matrix row maxima"
  (rv (apl-reduce apl-max (make-array (list 2 3) (list 3 1 4 1 5 9))))
  (list 4 9))

(apl-test
  "reduce-first +/ vector same as reduce"
  (rv (apl-reduce-first apl-add (make-array (list 5) (list 1 2 3 4 5))))
  (list 15))

(apl-test
  "reduce-first +/ matrix col sums shape"
  (sh
    (apl-reduce-first apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 3))

(apl-test
  "reduce-first +/ matrix col sums values"
  (rv
    (apl-reduce-first apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 5 7 9))

(apl-test
  "reduce-first max/ matrix col maxima"
  (rv
    (apl-reduce-first apl-max (make-array (list 3 2) (list 1 9 2 8 3 7))))
  (list 3 9))

(apl-test
  "scan +\\ vector"
  (rv (apl-scan apl-add (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 3 6 10 15))

(apl-test
  "scan x\\ vector cumulative product"
  (rv (apl-scan apl-mul (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 2 6 24 120))

(apl-test
  "scan max\\ vector running max"
  (rv (apl-scan apl-max (make-array (list 5) (list 3 1 4 1 5))))
  (list 3 3 4 4 5))

(apl-test
  "scan min\\ vector running min"
  (rv (apl-scan apl-min (make-array (list 5) (list 3 1 4 1 5))))
  (list 3 1 1 1 1))

(apl-test
  "scan +\\ single element"
  (rv (apl-scan apl-add (make-array (list 1) (list 42))))
  (list 42))

(apl-test
  "scan +\\ scalar no-op"
  (rv (apl-scan apl-add (apl-scalar 7)))
  (list 7))

(apl-test
  "scan +\\ vector preserves shape"
  (sh (apl-scan apl-add (make-array (list 5) (list 1 2 3 4 5))))
  (list 5))

(apl-test
  "scan +\\ matrix preserves shape"
  (sh (apl-scan apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 2 3))

(apl-test
  "scan +\\ matrix row-wise"
  (rv (apl-scan apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 1 3 6 4 9 15))

(apl-test
  "scan max\\ matrix row-wise running max"
  (rv (apl-scan apl-max (make-array (list 2 3) (list 3 1 4 1 5 9))))
  (list 3 3 4 1 5 9))

(apl-test
  "scan-first +\\ vector same as scan"
  (rv (apl-scan-first apl-add (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 3 6 10 15))

(apl-test
  "scan-first +\\ scalar no-op"
  (rv (apl-scan-first apl-add (apl-scalar 9)))
  (list 9))

(apl-test
  "scan-first +\\ matrix preserves shape"
  (sh (apl-scan-first apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 2 3))

(apl-test
  "scan-first +\\ matrix col-wise"
  (rv (apl-scan-first apl-add (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 1 2 3 5 7 9))

(apl-test
  "scan-first max\\ matrix col-wise running max"
  (rv (apl-scan-first apl-max (make-array (list 3 2) (list 3 1 4 1 5 9))))
  (list 3 1 4 1 5 9))

(apl-test
  "each negate vector"
  (rv (apl-each apl-neg-m (make-array (list 3) (list 1 2 3))))
  (list -1 -2 -3))

(apl-test
  "each negate vector preserves shape"
  (sh (apl-each apl-neg-m (make-array (list 3) (list 1 2 3))))
  (list 3))

(apl-test
  "each reciprocal vector"
  (rv (apl-each apl-recip (make-array (list 3) (list 1 2 4))))
  (list 1 (/ 1 2) (/ 1 4)))

(apl-test
  "each abs vector"
  (rv (apl-each apl-abs (make-array (list 4) (list -1 2 -3 4))))
  (list 1 2 3 4))

(apl-test "each scalar" (rv (apl-each apl-neg-m (apl-scalar 5))) (list -5))

(apl-test
  "each scalar shape"
  (sh (apl-each apl-neg-m (apl-scalar 5)))
  (list))

(apl-test
  "each negate matrix shape"
  (sh (apl-each apl-neg-m (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 2 3))

(apl-test
  "each negate matrix values"
  (rv (apl-each apl-neg-m (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list -1 -2 -3 -4 -5 -6))

(apl-test
  "each-dyadic scalar+scalar"
  (rv (apl-each-dyadic apl-add (apl-scalar 3) (apl-scalar 4)))
  (list 7))

(apl-test
  "each-dyadic scalar+vector"
  (rv
    (apl-each-dyadic
      apl-add
      (apl-scalar 10)
      (make-array (list 3) (list 1 2 3))))
  (list 11 12 13))

(apl-test
  "each-dyadic vector+scalar"
  (rv
    (apl-each-dyadic
      apl-add
      (make-array (list 3) (list 1 2 3))
      (apl-scalar 10)))
  (list 11 12 13))

(apl-test
  "each-dyadic vector+vector"
  (rv
    (apl-each-dyadic
      apl-add
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 10 20 30))))
  (list 11 22 33))

(apl-test
  "each-dyadic mul matrix+matrix shape"
  (sh
    (apl-each-dyadic
      apl-mul
      (make-array (list 2 2) (list 1 2 3 4))
      (make-array (list 2 2) (list 5 6 7 8))))
  (list 2 2))

(apl-test
  "each-dyadic mul matrix+matrix values"
  (rv
    (apl-each-dyadic
      apl-mul
      (make-array (list 2 2) (list 1 2 3 4))
      (make-array (list 2 2) (list 5 6 7 8))))
  (list 5 12 21 32))

(apl-test
  "outer product mult table values"
  (rv
    (apl-outer
      apl-mul
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 1 2 3))))
  (list 1 2 3 2 4 6 3 6 9))

(apl-test
  "outer product mult table shape"
  (sh
    (apl-outer
      apl-mul
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 1 2 3))))
  (list 3 3))

(apl-test
  "outer product add table values"
  (rv
    (apl-outer
      apl-add
      (make-array (list 2) (list 1 2))
      (make-array (list 3) (list 10 20 30))))
  (list 11 21 31 12 22 32))

(apl-test
  "outer product add table shape"
  (sh
    (apl-outer
      apl-add
      (make-array (list 2) (list 1 2))
      (make-array (list 3) (list 10 20 30))))
  (list 2 3))

(apl-test
  "outer product scalar+vector shape"
  (sh
    (apl-outer apl-mul (apl-scalar 5) (make-array (list 3) (list 1 2 3))))
  (list 3))

(apl-test
  "outer product scalar+vector values"
  (rv
    (apl-outer apl-mul (apl-scalar 5) (make-array (list 3) (list 1 2 3))))
  (list 5 10 15))

(apl-test
  "outer product vector+scalar shape"
  (sh
    (apl-outer apl-mul (make-array (list 3) (list 1 2 3)) (apl-scalar 10)))
  (list 3))

(apl-test
  "outer product scalar+scalar"
  (rv (apl-outer apl-mul (apl-scalar 6) (apl-scalar 7)))
  (list 42))

(apl-test
  "outer product scalar+scalar shape"
  (sh (apl-outer apl-mul (apl-scalar 6) (apl-scalar 7)))
  (list))

(apl-test
  "outer product equality identity matrix values"
  (rv
    (apl-outer
      apl-eq
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 1 2 3))))
  (list 1 0 0 0 1 0 0 0 1))

(apl-test
  "outer product matrix+vector rank doubling shape"
  (sh
    (apl-outer
      apl-add
      (make-array (list 2 2) (list 1 2 3 4))
      (make-array (list 3) (list 10 20 30))))
  (list 2 2 3))

(apl-test
  "outer product matrix+vector rank doubling values"
  (rv
    (apl-outer
      apl-add
      (make-array (list 2 2) (list 1 2 3 4))
      (make-array (list 3) (list 10 20 30))))
  (list 11 21 31 12 22 32 13 23 33 14 24 34))

(apl-test
  "inner +.× dot product"
  (rv
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 4 5 6))))
  (list 32))

(apl-test
  "inner +.× dot product shape is scalar"
  (sh
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 4 5 6))))
  (list))

(apl-test
  "inner +.× matrix multiply 2x3 * 3x2 shape"
  (sh
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 2 3) (list 1 2 3 4 5 6))
      (make-array (list 3 2) (list 7 8 9 10 11 12))))
  (list 2 2))

(apl-test
  "inner +.× matrix multiply 2x3 * 3x2 values"
  (rv
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 2 3) (list 1 2 3 4 5 6))
      (make-array (list 3 2) (list 7 8 9 10 11 12))))
  (list 58 64 139 154))

(apl-test
  "inner +.× identity matrix 2x2"
  (rv
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 2 2) (list 1 0 0 1))
      (make-array (list 2 2) (list 5 6 7 8))))
  (list 5 6 7 8))

(apl-test
  "inner ∧.= equal vectors"
  (rv
    (apl-inner
      apl-and
      apl-eq
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 1 2 3))))
  (list 1))

(apl-test
  "inner ∧.= unequal vectors"
  (rv
    (apl-inner
      apl-and
      apl-eq
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 1 9 3))))
  (list 0))

(apl-test
  "inner +.× matrix * vector shape"
  (sh
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 2 3) (list 1 2 3 4 5 6))
      (make-array (list 3) (list 7 8 9))))
  (list 2))

(apl-test
  "inner +.× matrix * vector values"
  (rv
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 2 3) (list 1 2 3 4 5 6))
      (make-array (list 3) (list 7 8 9))))
  (list 50 122))

(apl-test
  "inner +.× vector * matrix shape"
  (sh
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3 2) (list 4 5 6 7 8 9))))
  (list 2))

(apl-test
  "inner +.× vector * matrix values"
  (rv
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3 2) (list 4 5 6 7 8 9))))
  (list 40 46))

(apl-test
  "inner +.× single-element vectors"
  (rv
    (apl-inner
      apl-add
      apl-mul
      (make-array (list 1) (list 6))
      (make-array (list 1) (list 7))))
  (list 42))

(apl-test
  "commute +⍨ scalar doubles"
  (rv (apl-commute apl-add (apl-scalar 5)))
  (list 10))

(apl-test
  "commute ×⍨ vector squares"
  (rv (apl-commute apl-mul (make-array (list 4) (list 1 2 3 4))))
  (list 1 4 9 16))

(apl-test
  "commute +⍨ vector doubles"
  (rv (apl-commute apl-add (make-array (list 3) (list 1 2 3))))
  (list 2 4 6))

(apl-test
  "commute +⍨ shape preserved"
  (sh (apl-commute apl-add (make-array (list 3) (list 1 2 3))))
  (list 3))

(apl-test
  "commute ×⍨ matrix shape preserved"
  (sh (apl-commute apl-mul (make-array (list 2 2) (list 1 2 3 4))))
  (list 2 2))

(apl-test
  "commute-dyadic -⍨ swaps subtraction"
  (rv (apl-commute-dyadic apl-sub (apl-scalar 5) (apl-scalar 3)))
  (list -2))

(apl-test
  "commute-dyadic ÷⍨ swaps division"
  (rv (apl-commute-dyadic apl-div (apl-scalar 4) (apl-scalar 12)))
  (list 3))

(apl-test
  "commute-dyadic -⍨ on vectors"
  (rv
    (apl-commute-dyadic
      apl-sub
      (make-array (list 3) (list 10 20 30))
      (make-array (list 3) (list 1 2 3))))
  (list -9 -18 -27))

(apl-test
  "commute-dyadic +⍨ commutative same result"
  (rv
    (apl-commute-dyadic
      apl-add
      (make-array (list 3) (list 1 2 3))
      (make-array (list 3) (list 10 20 30))))
  (list 11 22 33))

(apl-test
  "commute-dyadic ×⍨ commutative same result"
  (rv
    (apl-commute-dyadic
      apl-mul
      (make-array (list 3) (list 2 3 4))
      (make-array (list 3) (list 5 6 7))))
  (list 10 18 28))

(apl-test
  "compose -∘| scalar (negative abs)"
  (rv (apl-compose apl-neg-m apl-abs (apl-scalar -7)))
  (list -7))

(apl-test
  "compose -∘| vector"
  (rv
    (apl-compose apl-neg-m apl-abs (make-array (list 4) (list -1 2 -3 4))))
  (list -1 -2 -3 -4))

(apl-test
  "compose ⌊∘- (floor of negate)"
  (rv (apl-compose apl-floor apl-neg-m (make-array (list 3) (list 1 2 3))))
  (list -1 -2 -3))

(apl-test
  "compose -∘| matrix shape preserved"
  (sh
    (apl-compose apl-neg-m apl-abs (make-array (list 2 2) (list -1 2 -3 4))))
  (list 2 2))

(apl-test
  "compose-dyadic +∘- equals subtract scalar"
  (rv (apl-compose-dyadic apl-add apl-neg-m (apl-scalar 10) (apl-scalar 3)))
  (list 7))

(apl-test
  "compose-dyadic +∘- equals subtract vector"
  (rv
    (apl-compose-dyadic
      apl-add
      apl-neg-m
      (make-array (list 3) (list 10 20 30))
      (make-array (list 3) (list 1 2 3))))
  (list 9 18 27))

(apl-test
  "compose-dyadic -∘| (subtract abs)"
  (rv (apl-compose-dyadic apl-sub apl-abs (apl-scalar 10) (apl-scalar -3)))
  (list 7))

(apl-test
  "compose-dyadic ×∘- (multiply by negative)"
  (rv
    (apl-compose-dyadic
      apl-mul
      apl-neg-m
      (make-array (list 3) (list 2 3 4))
      (make-array (list 3) (list 1 2 3))))
  (list -2 -6 -12))

(apl-test
  "compose-dyadic shape preserved"
  (sh
    (apl-compose-dyadic
      apl-add
      apl-neg-m
      (make-array (list 2 3) (list 1 2 3 4 5 6))
      (make-array (list 2 3) (list 1 1 1 1 1 1))))
  (list 2 3))

(apl-test
  "power n=0 identity"
  (rv (apl-power (fn (a) (apl-add a (apl-scalar 1))) 0 (apl-scalar 5)))
  (list 5))

(apl-test
  "power increment by 3"
  (rv (apl-power (fn (a) (apl-add a (apl-scalar 1))) 3 (apl-scalar 0)))
  (list 3))

(apl-test
  "power double 4 times = 16"
  (rv (apl-power (fn (a) (apl-mul a (apl-scalar 2))) 4 (apl-scalar 1)))
  (list 16))

(apl-test
  "power on vector +5"
  (rv
    (apl-power
      (fn (a) (apl-add a (apl-scalar 1)))
      5
      (make-array (list 3) (list 1 2 3))))
  (list 6 7 8))

(apl-test
  "power on vector preserves shape"
  (sh
    (apl-power
      (fn (a) (apl-add a (apl-scalar 1)))
      5
      (make-array (list 3) (list 1 2 3))))
  (list 3))

(apl-test
  "power on matrix"
  (rv
    (apl-power
      (fn (a) (apl-mul a (apl-scalar 3)))
      2
      (make-array (list 2 2) (list 1 2 3 4))))
  (list 9 18 27 36))

(apl-test
  "power-fixed identity stops immediately"
  (rv (apl-power-fixed (fn (a) a) (make-array (list 3) (list 1 2 3))))
  (list 1 2 3))

(apl-test
  "power-fixed floor half scalar to 0"
  (rv
    (apl-power-fixed
      (fn (a) (apl-floor (apl-div a (apl-scalar 2))))
      (apl-scalar 100)))
  (list 0))

(apl-test
  "power-fixed shape preserved"
  (sh
    (apl-power-fixed (fn (a) a) (make-array (list 2 2) (list 1 2 3 4))))
  (list 2 2))

(apl-test
  "rank tally⍤1 row tallies"
  (rv (apl-rank apl-tally 1 (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 3 3))

(apl-test
  "rank tally⍤1 row tallies shape"
  (sh (apl-rank apl-tally 1 (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 2))

(apl-test
  "rank neg⍤0 vector scalar cells"
  (rv (apl-rank apl-neg-m 0 (make-array (list 3) (list 1 2 3))))
  (list -1 -2 -3))

(apl-test
  "rank neg⍤0 vector preserves shape"
  (sh (apl-rank apl-neg-m 0 (make-array (list 3) (list 1 2 3))))
  (list 3))

(apl-test
  "rank neg⍤1 matrix per-row"
  (rv (apl-rank apl-neg-m 1 (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list -1 -2 -3 -4 -5 -6))

(apl-test
  "rank neg⍤1 matrix preserves shape"
  (sh (apl-rank apl-neg-m 1 (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 2 3))

(apl-test
  "rank k>=rank fallthrough"
  (rv (apl-rank apl-tally 5 (make-array (list 4) (list 1 2 3 4))))
  (list 4))

(apl-test
  "rank tally⍤2 whole matrix tally"
  (rv
    (apl-rank
      apl-tally
      2
      (make-array (list 3 5) (list 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))))
  (list 3))

(apl-test
  "rank reverse⍤1 matrix reverse rows"
  (rv (apl-rank apl-reverse 1 (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 3 2 1 6 5 4))

(apl-test
  "rank tally⍤1 3x4 row tallies"
  (rv
    (apl-rank
      apl-tally
      1
      (make-array (list 3 4) (list 1 2 3 4 5 6 7 8 9 10 11 12))))
  (list 4 4 4))

(apl-test
  "at-replace single index"
  (rv
    (apl-at-replace
      (apl-scalar 99)
      (make-array (list 1) (list 2))
      (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 99 3 4 5))

(apl-test
  "at-replace multiple indices vector vals"
  (rv
    (apl-at-replace
      (make-array (list 2) (list 99 88))
      (make-array (list 2) (list 2 4))
      (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 99 3 88 5))

(apl-test
  "at-replace scalar broadcast"
  (rv
    (apl-at-replace
      (apl-scalar 0)
      (make-array (list 3) (list 1 3 5))
      (make-array (list 5) (list 10 20 30 40 50))))
  (list 0 20 0 40 0))

(apl-test
  "at-replace preserves shape"
  (sh
    (apl-at-replace
      (apl-scalar 99)
      (make-array (list 1) (list 2))
      (make-array (list 5) (list 1 2 3 4 5))))
  (list 5))

(apl-test
  "at-replace last index"
  (rv
    (apl-at-replace
      (apl-scalar 99)
      (make-array (list 1) (list 5))
      (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 2 3 4 99))

(apl-test
  "at-replace on matrix linear-index"
  (rv
    (apl-at-replace
      (apl-scalar 99)
      (make-array (list 1) (list 3))
      (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 1 2 99 4 5 6))

(apl-test
  "at-apply negate at indices"
  (rv
    (apl-at-apply
      apl-neg-m
      (make-array (list 3) (list 1 3 5))
      (make-array (list 5) (list 1 2 3 4 5))))
  (list -1 2 -3 4 -5))

(apl-test
  "at-apply double at index 1"
  (rv
    (apl-at-apply
      (fn (a) (apl-mul a (apl-scalar 2)))
      (make-array (list 1) (list 1))
      (make-array (list 2) (list 5 10))))
  (list 10 10))

(apl-test
  "at-apply preserves shape"
  (sh
    (apl-at-apply
      apl-neg-m
      (make-array (list 2) (list 1 3))
      (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 2 3))

(apl-test
  "at-apply on matrix linear-index"
  (rv
    (apl-at-apply
      apl-neg-m
      (make-array (list 2) (list 1 6))
      (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list -1 2 3 4 5 -6))