changeset 273:197d23954a4e

Move some of the most commonly loaded modules up a level
author Chris Cannam
date Thu, 23 May 2013 19:33:06 +0100
parents 2ebda6646c40
children df0836b48494
files yetilab/complex.yeti yetilab/complex/type.yeti yetilab/feature/feature.yeti yetilab/feature/specdiff.yeti yetilab/matrix.yeti yetilab/matrix/matrix.yeti yetilab/matrix/matrixtype.yeti yetilab/matrix/test/speedtest.yeti yetilab/matrix/test/test_matrix.yeti yetilab/matrix/type.yeti yetilab/plot.yeti yetilab/plot/plot.yeti yetilab/signal/test/test_window.yeti yetilab/signal/window.yeti yetilab/stream/audiofile.yeti yetilab/stream/channels.yeti yetilab/stream/filter.yeti yetilab/stream/framer.yeti yetilab/stream/playback.yeti yetilab/stream/streamtype.yeti yetilab/stream/syntheticstream.yeti yetilab/stream/test/test_audiofile.yeti yetilab/stream/test/test_channels.yeti yetilab/stream/test/test_filter.yeti yetilab/stream/test/test_framer.yeti yetilab/stream/test/test_syntheticstream.yeti yetilab/stream/type.yeti yetilab/transform/fft.yeti yetilab/transform/test/test_fft.yeti yetilab/vamp.yeti yetilab/vamp/test/test_vamp.yeti yetilab/vamp/vamp.yeti yetilab/vamp/vamppost.yeti yetilab/vector.yeti yetilab/vector/blockfuncs.yeti yetilab/vector/complex.yeti yetilab/vector/complextype.yeti yetilab/vector/test/test_blockfuncs.yeti yetilab/vector/test/test_complex.yeti yetilab/vector/test/test_vector.yeti yetilab/vector/type.yeti yetilab/vector/vector.yeti yetilab/vector/vectortype.yeti
diffstat 43 files changed, 1583 insertions(+), 1583 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/complex.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,98 @@
+
+module yetilab.complex;
+
+load yetilab.vector.type;
+load yetilab.complex.type;
+
+vec = load yetilab.vector;
+
+import java.lang: ClassCastException;
+
+class Cplx(double real, double imag)
+    int getReal()
+        real,
+    int getImag()
+        imag,
+    double getMagnitude()
+        sqrt (real * real + imag * imag),
+    double getAngle()
+        Math#atan2(imag, real),
+    String toString()
+        if real == int real and imag == int imag then
+            if imag < 0 then
+                " \(int real) - \(int (-imag))i"
+            else 
+                " \(int real) + \(int imag)i"
+            fi
+        else
+            if imag < 0 then
+                " \(real) - \((-imag))i"
+            else 
+                " \(real) + \(imag)i"
+            fi
+        fi,
+    int hashCode()
+        Double#valueOf(real)#hashCode() + Double#valueOf(imag)#hashCode(),
+    boolean equals(Object other)
+        try
+            c = other unsafely_as ~Cplx;
+            c#getReal() == real and c#getImag() == imag
+        catch ClassCastException:
+            false
+        yrt,
+end;
+
+real c1 is ~Cplx -> number =
+    c1#getReal();
+
+imaginary c1 is ~Cplx -> number =
+    c1#getImag();
+
+complex re im is number -> number -> ~Cplx =
+    new Cplx(re, im);
+
+magnitude c is ~Cplx -> number =
+    c#getMagnitude();
+
+angle c is ~Cplx -> number =
+    c#getAngle();
+
+add c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
+    complex (real c1 + real c2) (imaginary c1 + imaginary c2);
+
+scale r c is number -> ~Cplx -> ~Cplx =
+    complex (r * real c) (r * imaginary c);
+
+zeros n is number -> array<~Cplx> =
+    array (map \(complex 0 0) [1..n]);
+
+magnitudes cc is list?<~Cplx> -> vector =
+    vec.fromList (map magnitude cc);
+
+angles cc is list?<~Cplx> -> vector =
+    vec.fromList (map angle cc);
+
+{
+   real,
+   imaginary,
+   complex,
+   magnitude,
+   angle,
+   add,
+   scale,
+   zeros,
+   magnitudes,
+   angles,
+} as {
+   real is cplx -> number,
+   imaginary is cplx -> number,
+   complex is number -> number -> cplx,
+   magnitude is cplx -> number,
+   angle is cplx -> number,
+   add is cplx -> cplx -> cplx,
+   scale is number -> cplx -> cplx,
+   zeros is number -> array<cplx>,
+   magnitudes is list?<cplx> -> vector,
+   angles is list?<cplx> -> vector,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/complex/type.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,7 @@
+
+module yetilab.complex.type;
+
+typedef opaque cplx = ~yetilab.Cplx;
+
+();
+
--- a/yetilab/feature/feature.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/feature/feature.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,7 +1,7 @@
 
 module yetilab.feature.feature;
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 fr = load yetilab.stream.framer;
 
 // Utility functions for feature extractors
--- a/yetilab/feature/specdiff.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/feature/specdiff.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,9 +1,9 @@
 
 module yetilab.feature.specdiff;
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 fr = load yetilab.stream.framer;
-cplx = load yetilab.vector.complex;
+cplx = load yetilab.complex;
 
 load yetilab.feature.feature;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/matrix.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,770 @@
+
+module yetilab.matrix;
+
+// A matrix is an array of vectors.
+
+// A matrix can be stored in either column-major (the default) or
+// row-major format. Storage order is an efficiency concern only:
+// every API function operating on matrix objects will return the same
+// result regardless of storage order.  (The transpose function just
+// switches the row/column order without moving the elements.)
+
+vec = load yetilab.vector;
+bf = load yetilab.vector.blockfuncs;
+
+load yetilab.vector.type;
+load yetilab.matrix.type;
+
+size m =
+    case m of
+    DenseRows r:
+        major = length r;
+        { 
+            rows = major, 
+            columns = if major > 0 then vec.length r[0] else 0 fi,
+        };
+    DenseCols c:
+        major = length c;
+        { 
+            rows = if major > 0 then vec.length c[0] else 0 fi,
+            columns = major, 
+        };
+    SparseCSR { values, indices, pointers, extent }:
+        {
+            rows = (length pointers) - 1,
+            columns = extent
+        };
+    SparseCSC { values, indices, pointers, extent }:
+        {
+            rows = extent,
+            columns = (length pointers) - 1
+        };
+    esac;
+
+width m = (size m).columns;
+height m = (size m).rows;
+
+nonZeroValues m =
+   (nz d =
+        sum
+           (map do v:
+                sum (map do n: if n == 0 then 0 else 1 fi done (vec.list v))
+                done d);
+    case m of 
+    DenseRows d: nz d;
+    DenseCols d: nz d;
+    SparseCSR d: vec.length d.values;
+    SparseCSC d: vec.length d.values;
+    esac);
+
+density m =
+   ({ rows, columns } = size m;
+    cells = rows * columns;
+    (nonZeroValues m) / cells);
+
+sparseSlice n d =
+   (start = d.pointers[n];
+    end = d.pointers[n+1];
+    { 
+        values = vec.slice d.values start end,
+        indices = slice d.indices start end,
+    });
+
+nonEmptySlices d =
+   (ne = array [];
+    for [0..length d.pointers - 2] do i:
+        if d.pointers[i] != d.pointers[i+1] then
+            push ne i
+        fi
+    done;
+    ne);
+
+fromSlice n m d =
+   (slice = sparseSlice n d;
+    var v = 0;
+    for [0..length slice.indices - 1] do i:
+        if slice.indices[i] == m then
+            v := vec.at slice.values i;
+        fi
+    done;
+    v);
+
+filledSlice n d =
+   (slice = sparseSlice n d;
+    dslice = new double[d.extent];
+    for [0..length slice.indices - 1] do i:
+        dslice[slice.indices[i]] := vec.at slice.values i;
+    done;
+    vec.vector dslice);
+
+at' m row col =
+    case m of
+    DenseRows rows: r = rows[row]; vec.at r col;
+    DenseCols cols: c = cols[col]; vec.at c row;
+    SparseCSR data: fromSlice row col data;
+    SparseCSC data: fromSlice col row data;
+    esac;
+
+getColumn j m =
+    case m of
+    DenseCols cols: cols[j];
+    SparseCSC data: filledSlice j data;
+    _: vec.fromList (map do i: at' m i j done [0..height m - 1]);
+    esac;
+
+getRow i m =
+    case m of
+    DenseRows rows: rows[i];
+    SparseCSR data: filledSlice i data; 
+    _: vec.fromList (map do j: at' m i j done [0..width m - 1]);
+    esac;
+
+asRows m =
+    map do i: getRow i m done [0 .. (height m) - 1];
+
+asColumns m =
+    map do i: getColumn i m done [0 .. (width m) - 1];
+
+isRowMajor? m =
+    case m of
+    DenseRows _: true;
+    DenseCols _: false;
+    SparseCSR _: true;
+    SparseCSC _: false;
+    esac;
+
+isSparse? m =
+    case m of
+    DenseRows _: false;
+    DenseCols _: false;
+    SparseCSR _: true;
+    SparseCSC _: true;
+    esac;
+
+typeOf m =
+    if isRowMajor? m then RowMajor ()
+    else ColumnMajor ()
+    fi;
+
+flippedTypeOf m =
+    if isRowMajor? m then ColumnMajor ()
+    else RowMajor ()
+    fi;
+
+newColumnMajorStorage { rows, columns } = 
+    if rows < 1 then array []
+    else array (map \(vec.zeros rows) [1..columns])
+    fi;
+
+zeroMatrix { rows, columns } = 
+    DenseCols (newColumnMajorStorage { rows, columns });
+
+zeroMatrixWithTypeOf m { rows, columns } = 
+    if isRowMajor? m then
+        DenseRows (newColumnMajorStorage { rows = columns, columns = rows });
+    else
+        DenseCols (newColumnMajorStorage { rows, columns });
+    fi;
+
+zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
+
+generate f { rows, columns } =
+    if rows < 1 or columns < 1 then zeroSizeMatrix ()
+    else
+        m = array (map \(new double[rows]) [1..columns]);
+        for [0..columns-1] do col:
+            for [0..rows-1] do row:
+                m[col][row] := f row col;
+            done;
+        done;
+        DenseCols (array (map vec.vector m))
+    fi;
+
+swapij =
+    map do { i, j, v }: { i = j, j = i, v } done;
+
+//!!! should use { row = , column = , value = } instead of i, j, v?
+enumerateSparse m =
+   (enumerate { values, indices, pointers } =
+        concat
+           (map do i:
+                start = pointers[i];
+                end = pointers[i+1];
+                map2 do j v: { i, j, v } done 
+                    (slice indices start end)
+                    (vec.list (vec.slice values start end))
+                done [0..length pointers - 2]);
+    case m of
+    SparseCSC d: swapij (enumerate d);
+    SparseCSR d: enumerate d;
+     _: [];
+    esac);
+
+enumerateDense m =
+   (enumerate d =
+        concat
+           (map do i:
+                vv = d[i];
+                map2 do j v: { i, j, v } done
+                    [0..vec.length vv - 1]
+                    (vec.list vv);
+                done [0..length d - 1]);
+    case m of
+    DenseCols c: swapij (enumerate c);
+    DenseRows r: enumerate r;
+     _: [];
+    esac);
+
+enumerate m =
+    if isSparse? m then enumerateSparse m else enumerateDense m fi;
+
+// Make a sparse matrix from entries whose i, j values are known to be
+// within range
+makeSparse type size data =
+   (isRow = case type of RowMajor (): true; ColumnMajor (): false esac;
+    ordered = 
+        sortBy do a b:
+            if a.maj == b.maj then a.min < b.min else a.maj < b.maj fi
+        done
+           (map
+                if isRow then
+                    do { i, j, v }: { maj = i, min = j, v } done;
+                else
+                    do { i, j, v }: { maj = j, min = i, v } done;
+                fi
+               (filter do d: d.v != 0 done data));
+    tagger = if isRow then SparseCSR else SparseCSC fi;
+    majorSize = if isRow then size.rows else size.columns fi;
+    minorSize = if isRow then size.columns else size.rows fi;
+    pointers = array [0];
+    setArrayCapacity pointers (size.rows + 1);
+    fillPointers n i data =
+        if n < majorSize then
+            case data of
+            d::rest:
+               (for [n..d-1] \(push pointers i);
+                fillPointers d (i+1) rest);
+             _:
+                for [n..majorSize-1] \(push pointers i);
+            esac;
+        fi;
+    fillPointers 0 0 (map (.maj) ordered);
+    tagger {
+        values = vec.fromList (map (.v) ordered),
+        indices = array (map (.min) ordered),
+        pointers,
+        extent = minorSize,
+    });
+
+// Make a sparse matrix from entries that may contain out-of-range
+// cells which need to be filtered out. This is the public API for
+// makeSparse and is also used to discard out-of-range cells from
+// resizedTo.
+newSparseMatrix type size data =
+    makeSparse type size
+       (filter
+            do { i, j, v }:
+                i == int i and i >= 0 and i < size.rows and 
+                j == int j and j >= 0 and j < size.columns
+            done data);
+
+toSparse m =
+    if isSparse? m then m
+    else
+        makeSparse (typeOf m) (size m) (enumerateDense m);
+    fi;
+
+toDense m =
+    if not (isSparse? m) then m
+    elif isRowMajor? m then
+        DenseRows (array (map do row: getRow row m done [0..height m - 1]));
+    else
+        DenseCols (array (map do col: getColumn col m done [0..width m - 1]));
+    fi;
+
+constMatrix n = generate do row col: n done;
+randomMatrix = generate do row col: Math#random() done;
+identityMatrix = constMatrix 1;
+
+transposed m =
+    case m of
+    DenseRows d: DenseCols d;
+    DenseCols d: DenseRows d;
+    SparseCSR d: SparseCSC d;
+    SparseCSC d: SparseCSR d;
+    esac;
+
+flipped m =
+    if isSparse? m then
+        makeSparse (flippedTypeOf m) (size m) (enumerateSparse m)
+    else
+        if isRowMajor? m then
+            generate do row col: at' m row col done (size m);
+        else
+            transposed
+               (generate do row col: at' m col row done
+                { rows = (width m), columns = (height m) });
+        fi
+    fi;
+
+toRowMajor m =
+    if isRowMajor? m then m else flipped m fi;
+
+toColumnMajor m =
+    if not isRowMajor? m then m else flipped m fi;
+
+equal'' comparator vecComparator m1 m2 =
+    // Prerequisite: m1 and m2 have same sparse-p and storage order
+   (compareVecLists vv1 vv2 = all id (map2 vecComparator vv1 vv2);
+    compareSparse d1 d2 =
+        d1.extent == d2.extent and
+        vecComparator d1.values d2.values and
+        d1.indices == d2.indices and
+        d1.pointers == d2.pointers;
+    case m1 of
+    DenseRows d1:
+        case m2 of DenseRows d2: compareVecLists d1 d2; _: false; esac;
+    DenseCols d1:
+        case m2 of DenseCols d2: compareVecLists d1 d2; _: false; esac;
+    SparseCSR d1:
+        case m2 of SparseCSR d2: compareSparse d1 d2; _: false; esac;
+    SparseCSC d1:
+        case m2 of SparseCSC d2: compareSparse d1 d2; _: false; esac;
+    esac);
+
+equal' comparator vecComparator m1 m2 =
+    if size m1 != size m2 then 
+        false
+    elif isRowMajor? m1 != isRowMajor? m2 then
+        equal' comparator vecComparator (flipped m1) m2;
+    elif isSparse? m1 != isSparse? m2 then
+        if isSparse? m1 then
+            equal' comparator vecComparator m1 (toSparse m2)
+        else
+            equal' comparator vecComparator (toSparse m1) m2
+        fi
+    else
+        equal'' comparator vecComparator m1 m2
+    fi;
+
+// Compare matrices using the given comparator for individual cells.
+// Note that matrices with different storage order but the same
+// contents are equal, although comparing them is slow.
+//!!! Document the fact that sparse matrices can only be equal if they
+// have the same set of non-zero cells (regardless of comparator used)
+equalUnder comparator =
+    equal' comparator (vec.equalUnder comparator);
+
+equal =
+    equal' (==) vec.equal;
+
+newMatrix type data = //!!! NB does not copy data
+   (tagger = case type of RowMajor (): DenseRows; ColumnMajor (): DenseCols esac;
+    if empty? data or vec.empty? (head data)
+    then zeroSizeMatrix ()
+    else tagger (array data)
+    fi);
+
+newRowVector data = //!!! NB does not copy data
+    DenseRows (array [data]);
+
+newColumnVector data = //!!! NB does not copy data
+    DenseCols (array [data]);
+
+denseLinearOp op m1 m2 =
+    if isRowMajor? m1 then
+        newMatrix (typeOf m1) 
+           (map2 do c1 c2: op c1 c2 done (asRows m1) (asRows m2));
+    else
+        newMatrix (typeOf m1) 
+           (map2 do c1 c2: op c1 c2 done (asColumns m1) (asColumns m2));
+    fi;
+
+sparseSumOrDifference op m1 m2 =
+   (h = [:];
+    for (enumerate m1) do { i, j, v }:
+        if not (i in h) then h[i] := [:] fi;
+        h[i][j] := v;
+    done;
+    for (enumerate m2) do { i, j, v }:
+        if not (i in h) then h[i] := [:] fi;
+        if j in h[i] then h[i][j] := op h[i][j] v;
+        else h[i][j] := op 0 v;
+        fi;
+    done;
+    entries = concat
+       (map do i:
+            kk = keys h[i];
+            map2 do j v: { i, j, v } done kk (map (at h[i]) kk)
+            done (keys h));
+    makeSparse (typeOf m1) (size m1) entries);
+
+sum' m1 m2 =
+    if (size m1) != (size m2)
+    then failWith "Matrices are not the same size: \(size m1), \(size m2)";
+    elif isSparse? m1 and isSparse? m2 then
+        sparseSumOrDifference (+) m1 m2;
+    else
+        denseLinearOp bf.add m1 m2;
+    fi;
+
+difference m1 m2 =
+    if (size m1) != (size m2)
+    then failWith "Matrices are not the same size: \(size m1), \(size m2)";
+    elif isSparse? m1 and isSparse? m2 then
+        sparseSumOrDifference (-) m1 m2;
+    else
+        denseLinearOp bf.subtract m1 m2;
+    fi;
+
+scaled factor m =
+    if isSparse? m then
+        makeSparse (typeOf m) (size m)
+           (map do { i, j, v }: { i, j, v = factor * v } done (enumerate m))
+    elif isRowMajor? m then
+        newMatrix (typeOf m) (map (bf.scaled factor) (asRows m));
+    else
+        newMatrix (typeOf m) (map (bf.scaled factor) (asColumns m));
+    fi;
+
+abs' m =
+    if isSparse? m then
+        makeSparse (typeOf m) (size m)
+           (map do { i, j, v }: { i, j, v = abs v } done (enumerate m))
+    elif isRowMajor? m then
+        newMatrix (typeOf m) (map bf.abs (asRows m));
+    else
+        newMatrix (typeOf m) (map bf.abs (asColumns m));
+    fi;
+
+filter f m =
+    if isSparse? m then
+        makeSparse (typeOf m) (size m)
+           (map do { i, j, v }: { i, j, v = if f v then v else 0 fi } done
+               (enumerate m))
+    else
+        vfilter = vec.fromList . (map do i: if f i then i else 0 fi done) . vec.list;
+        if isRowMajor? m then
+            newMatrix (typeOf m) (map vfilter (asRows m));
+        else
+            newMatrix (typeOf m) (map vfilter (asColumns m));
+        fi;
+    fi;
+
+sparseProductLeft size m1 m2 =
+   ({ values, indices, pointers } = case m1 of
+         SparseCSR d: d;
+         SparseCSC d: d;
+         _: failWith "sparseProductLeft called for non-sparse m1";
+         esac;
+    rows = isRowMajor? m1;
+    data = array (map \(new double[size.rows]) [1..size.columns]);
+    for [0..size.columns - 1] do j':
+        c = getColumn j' m2;
+        var p = 0;
+        for [0..length indices - 1] do ix:
+            ix == pointers[p+1] loop (p := p + 1);
+            i = if rows then p else indices[ix] fi;
+            j = if rows then indices[ix] else p fi;
+            data[j'][i] := data[j'][i] + (vec.at values ix) * (vec.at c j);
+        done;
+    done;
+    DenseCols (array (map vec.vector (list data))));
+
+sparseProductRight size m1 m2 =
+   ({ values, indices, pointers } = case m2 of
+         SparseCSR d: d;
+         SparseCSC d: d;
+         _: failWith "sparseProductLeft called for non-sparse m1";
+         esac;
+    rows = isRowMajor? m2;
+    data = array (map \(new double[size.columns]) [1..size.rows]);
+    for [0..size.rows - 1] do i':
+        r = getRow i' m1;
+        var p = 0;
+        for [0..length indices - 1] do ix:
+            ix == pointers[p+1] loop (p := p + 1);
+            i = if rows then p else indices[ix] fi;
+            j = if rows then indices[ix] else p fi;
+            data[i'][j] := data[i'][j] + (vec.at values ix) * (vec.at r i);
+        done;
+    done;
+    DenseRows (array (map vec.vector (list data))));
+
+sparseProduct size m1 m2 =
+    case m2 of
+    SparseCSC d:
+       ({ values, indices, pointers } = case m1 of
+            SparseCSR d1: d1;
+            SparseCSC d1: d1;
+            _: failWith "sparseProduct called for non-sparse matrices";
+            esac;
+        rows = isRowMajor? m1;
+        var p = 0;
+        pindices = new int[length indices];
+        for [0..length indices - 1] do ix:
+            ix == pointers[p+1] loop (p := p + 1);
+            pindices[ix] := p;
+        done;
+        entries =
+           (map do j':
+                cs = sparseSlice j' d;
+                hin = mapIntoHash
+                   (at cs.indices) (vec.at cs.values)
+                   [0..length cs.indices - 1];
+                hout = [:];
+                for [0..length indices - 1] do ix:
+                    i = if rows then pindices[ix] else indices[ix] fi;
+                    j = if rows then indices[ix] else pindices[ix] fi;
+                    if j in hin then
+                        p = (vec.at values ix) * hin[j];
+                        hout[i] := p + (if i in hout then hout[i] else 0 fi);
+                    fi;
+                done;
+                map do i:
+                    { i, j = j', v = hout[i] }
+                done (keys hout);
+            done (nonEmptySlices d));
+        makeSparse (ColumnMajor ()) size (concat entries));
+    SparseCSR _:
+        sparseProduct size m1 (flipped m2);
+     _: failWith "sparseProduct called for non-sparse matrices";
+    esac;
+
+denseProduct size m1 m2 =
+   (data = array (map \(new double[size.rows]) [1..size.columns]);
+    for [0..size.rows - 1] do i:
+        row = getRow i m1;
+        for [0..size.columns - 1] do j:
+            data[j][i] := bf.sum (bf.multiply row (getColumn j m2));
+        done;
+    done;
+    DenseCols (array (map vec.vector (list data))));
+
+product m1 m2 =
+    if (size m1).columns != (size m2).rows
+    then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns) != \((size m2).rows))";
+    else 
+        size = { rows = (size m1).rows, columns = (size m2).columns };
+        if isSparse? m1 then
+            if isSparse? m2 then
+                sparseProduct size m1 m2
+            else
+                sparseProductLeft size m1 m2
+            fi
+        elif isSparse? m2 then
+            sparseProductRight size m1 m2
+        else
+            denseProduct size m1 m2
+        fi;
+    fi;
+
+concatAgainstGrain tagger getter counter mm =
+   (n = counter (size (head mm));
+    tagger (array
+       (map do i:
+           vec.concat (map (getter i) mm)
+           done [0..n-1])));
+
+concatWithGrain tagger getter counter mm =
+    tagger (array
+       (concat
+           (map do m:
+               n = counter (size m);
+               map do i: getter i m done [0..n-1]
+               done mm)));
+
+sparseConcat direction first mm =
+   (dimension d f = if direction == d then sum (map f mm) else f first fi;
+    rows = dimension (Vertical ()) height;
+    columns = dimension (Horizontal ()) width;
+    entries ioff joff ui uj mm =
+        case mm of 
+        m::rest:
+           (map do { i, j, v }: { i = i + ioff, j = j + joff, v }
+                done (enumerate m)) ++
+           (entries
+               (ioff + ui * height m)
+               (joff + uj * width m)
+                ui uj rest);
+         _: []
+        esac;
+    makeSparse (typeOf first) { rows, columns }
+        if direction == Vertical () then entries 0 0 1 0 mm
+        else entries 0 0 0 1 mm fi);
+
+checkDimensionsFor direction first mm =
+   (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
+    n = counter (size first);
+    if not (all id (map do m: counter (size m) == n done mm)) then
+        failWith "Matrix dimensions incompatible for concat (found \(map do m: counter (size m) done mm) not all of which are \(n))";
+    fi);
+
+concat direction mm = //!!! doc: storage order is taken from first matrix in sequence
+    case length mm of
+    0: zeroSizeMatrix ();
+    1: head mm;
+    _:
+        first = head mm;
+        checkDimensionsFor direction first mm;
+        if all isSparse? mm then
+            sparseConcat direction first mm
+        else
+            row = isRowMajor? first;
+            // horizontal, row-major: against grain with rows
+            // horizontal, col-major: with grain with cols
+            // vertical, row-major: with grain with rows
+            // vertical, col-major: against grain with cols
+            case direction of
+            Horizontal ():
+                if row then concatAgainstGrain DenseRows getRow (.rows) mm;
+                else concatWithGrain DenseCols getColumn (.columns) mm;
+                fi;
+            Vertical ():
+                if row then concatWithGrain DenseRows getRow (.rows) mm;
+                else concatAgainstGrain DenseCols getColumn (.columns) mm;
+                fi;
+            esac;
+        fi;
+    esac;
+
+//!!! doc note: argument order chosen for consistency with std module slice
+rowSlice m start end = //!!! doc: storage order same as input
+    if isRowMajor? m then
+        DenseRows (array (map ((flip getRow) m) [start .. end - 1]))
+    else 
+        DenseCols (array (map do v: vec.slice v start end done (asColumns m)))
+    fi;
+
+//!!! doc note: argument order chosen for consistency with std module slice
+columnSlice m start end = //!!! doc: storage order same as input
+    if not isRowMajor? m then
+        DenseCols (array (map ((flip getColumn) m) [start .. end - 1]))
+    else 
+        DenseRows (array (map do v: vec.slice v start end done (asRows m)))
+    fi;
+
+resizedTo newsize m =
+   (if newsize == (size m) then
+        m
+    elif isSparse? m then
+        // don't call makeSparse directly: want to discard
+        // out-of-range cells
+        newSparseMatrix (typeOf m) newsize (enumerateSparse m)
+    elif (height m) == 0 or (width m) == 0 then
+        zeroMatrixWithTypeOf m newsize;
+    else
+        growrows = newsize.rows - (height m);
+        growcols = newsize.columns - (width m);
+        rowm = isRowMajor? m;
+        resizedTo newsize
+            if rowm and growrows < 0 then
+                rowSlice m 0 newsize.rows
+            elif (not rowm) and growcols < 0 then 
+                columnSlice m 0 newsize.columns
+            elif growrows < 0 then 
+                rowSlice m 0 newsize.rows
+            elif growcols < 0 then 
+                columnSlice m 0 newsize.columns
+            else
+                if growrows > 0 then
+                    concat (Vertical ())
+                       [m, zeroMatrixWithTypeOf m ((size m) with { rows = growrows })]
+                else
+                    concat (Horizontal ())
+                       [m, zeroMatrixWithTypeOf m ((size m) with { columns = growcols })]
+                fi
+            fi
+    fi);
+
+{
+    size,
+    width,
+    height,
+    density,
+    nonZeroValues,
+    at = at',
+    getColumn,
+    getRow,
+    isRowMajor?,
+    isSparse?,
+    generate,
+    constMatrix,
+    randomMatrix,
+    zeroMatrix,
+    identityMatrix,
+    zeroSizeMatrix,
+    equal,
+    equalUnder,
+    transposed,
+    flipped,
+    toRowMajor,
+    toColumnMajor,
+    toSparse,
+    toDense,
+    scaled,
+    resizedTo,
+    asRows,
+    asColumns,
+    sum = sum',
+    difference,
+    abs = abs',
+    filter,
+    product,
+    concat,
+    rowSlice,
+    columnSlice,
+    newMatrix,
+    newRowVector,
+    newColumnVector,
+    newSparseMatrix,
+    enumerate
+}
+as
+{
+//!!! check whether these are right to be .selector rather than just selector
+
+    size is matrix -> { .rows is number, .columns is number },
+    width is matrix -> number,
+    height is matrix -> number,
+    density is matrix -> number,
+    nonZeroValues is matrix -> number,
+    at is matrix -> number -> number -> number,
+    getColumn is number -> matrix -> vector,
+    getRow is number -> matrix -> vector,
+    isRowMajor? is matrix -> boolean,
+    isSparse? is matrix -> boolean,
+    generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
+    constMatrix is number -> { .rows is number, .columns is number } -> matrix,
+    randomMatrix is { .rows is number, .columns is number } -> matrix,
+    zeroMatrix is { .rows is number, .columns is number } -> matrix, 
+    identityMatrix is { .rows is number, .columns is number } -> matrix, 
+    zeroSizeMatrix is () -> matrix,
+    equal is matrix -> matrix -> boolean,
+    equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean,
+    transposed is matrix -> matrix,
+    flipped is matrix -> matrix, 
+    toRowMajor is matrix -> matrix, 
+    toColumnMajor is matrix -> matrix,
+    toSparse is matrix -> matrix,
+    toDense is matrix -> matrix,
+    scaled is number -> matrix -> matrix,
+    thresholded is number -> matrix -> matrix,
+    resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
+    asRows is matrix -> list<vector>, 
+    asColumns is matrix -> list<vector>,
+    sum is matrix -> matrix -> matrix,
+    difference is matrix -> matrix -> matrix,
+    abs is matrix -> matrix,
+    filter is (number -> boolean) -> matrix -> matrix,
+    product is matrix -> matrix -> matrix,
+    concat is (Horizontal () | Vertical ()) -> list<matrix> -> matrix,
+    rowSlice is matrix -> number -> number -> matrix, 
+    columnSlice is matrix -> number -> number -> matrix,
+    newMatrix is (ColumnMajor () | RowMajor ()) -> list<vector> -> matrix, 
+    newRowVector is vector -> matrix, 
+    newColumnVector is vector -> matrix,
+    newSparseMatrix is (ColumnMajor () | RowMajor ()) -> { .rows is number, .columns is number } -> list<{ .i is number, .j is number, .v is number }> -> matrix,
+    enumerate is matrix -> list<{ .i is number, .j is number, .v is number }>
+}
+
--- a/yetilab/matrix/matrix.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,770 +0,0 @@
-
-module yetilab.matrix.matrix;
-
-// A matrix is an array of vectors.
-
-// A matrix can be stored in either column-major (the default) or
-// row-major format. Storage order is an efficiency concern only:
-// every API function operating on matrix objects will return the same
-// result regardless of storage order.  (The transpose function just
-// switches the row/column order without moving the elements.)
-
-vec = load yetilab.vector.vector;
-bf = load yetilab.vector.blockfuncs;
-
-load yetilab.vector.vectortype;
-load yetilab.matrix.matrixtype;
-
-size m =
-    case m of
-    DenseRows r:
-        major = length r;
-        { 
-            rows = major, 
-            columns = if major > 0 then vec.length r[0] else 0 fi,
-        };
-    DenseCols c:
-        major = length c;
-        { 
-            rows = if major > 0 then vec.length c[0] else 0 fi,
-            columns = major, 
-        };
-    SparseCSR { values, indices, pointers, extent }:
-        {
-            rows = (length pointers) - 1,
-            columns = extent
-        };
-    SparseCSC { values, indices, pointers, extent }:
-        {
-            rows = extent,
-            columns = (length pointers) - 1
-        };
-    esac;
-
-width m = (size m).columns;
-height m = (size m).rows;
-
-nonZeroValues m =
-   (nz d =
-        sum
-           (map do v:
-                sum (map do n: if n == 0 then 0 else 1 fi done (vec.list v))
-                done d);
-    case m of 
-    DenseRows d: nz d;
-    DenseCols d: nz d;
-    SparseCSR d: vec.length d.values;
-    SparseCSC d: vec.length d.values;
-    esac);
-
-density m =
-   ({ rows, columns } = size m;
-    cells = rows * columns;
-    (nonZeroValues m) / cells);
-
-sparseSlice n d =
-   (start = d.pointers[n];
-    end = d.pointers[n+1];
-    { 
-        values = vec.slice d.values start end,
-        indices = slice d.indices start end,
-    });
-
-nonEmptySlices d =
-   (ne = array [];
-    for [0..length d.pointers - 2] do i:
-        if d.pointers[i] != d.pointers[i+1] then
-            push ne i
-        fi
-    done;
-    ne);
-
-fromSlice n m d =
-   (slice = sparseSlice n d;
-    var v = 0;
-    for [0..length slice.indices - 1] do i:
-        if slice.indices[i] == m then
-            v := vec.at slice.values i;
-        fi
-    done;
-    v);
-
-filledSlice n d =
-   (slice = sparseSlice n d;
-    dslice = new double[d.extent];
-    for [0..length slice.indices - 1] do i:
-        dslice[slice.indices[i]] := vec.at slice.values i;
-    done;
-    vec.vector dslice);
-
-at' m row col =
-    case m of
-    DenseRows rows: r = rows[row]; vec.at r col;
-    DenseCols cols: c = cols[col]; vec.at c row;
-    SparseCSR data: fromSlice row col data;
-    SparseCSC data: fromSlice col row data;
-    esac;
-
-getColumn j m =
-    case m of
-    DenseCols cols: cols[j];
-    SparseCSC data: filledSlice j data;
-    _: vec.fromList (map do i: at' m i j done [0..height m - 1]);
-    esac;
-
-getRow i m =
-    case m of
-    DenseRows rows: rows[i];
-    SparseCSR data: filledSlice i data; 
-    _: vec.fromList (map do j: at' m i j done [0..width m - 1]);
-    esac;
-
-asRows m =
-    map do i: getRow i m done [0 .. (height m) - 1];
-
-asColumns m =
-    map do i: getColumn i m done [0 .. (width m) - 1];
-
-isRowMajor? m =
-    case m of
-    DenseRows _: true;
-    DenseCols _: false;
-    SparseCSR _: true;
-    SparseCSC _: false;
-    esac;
-
-isSparse? m =
-    case m of
-    DenseRows _: false;
-    DenseCols _: false;
-    SparseCSR _: true;
-    SparseCSC _: true;
-    esac;
-
-typeOf m =
-    if isRowMajor? m then RowMajor ()
-    else ColumnMajor ()
-    fi;
-
-flippedTypeOf m =
-    if isRowMajor? m then ColumnMajor ()
-    else RowMajor ()
-    fi;
-
-newColumnMajorStorage { rows, columns } = 
-    if rows < 1 then array []
-    else array (map \(vec.zeros rows) [1..columns])
-    fi;
-
-zeroMatrix { rows, columns } = 
-    DenseCols (newColumnMajorStorage { rows, columns });
-
-zeroMatrixWithTypeOf m { rows, columns } = 
-    if isRowMajor? m then
-        DenseRows (newColumnMajorStorage { rows = columns, columns = rows });
-    else
-        DenseCols (newColumnMajorStorage { rows, columns });
-    fi;
-
-zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
-
-generate f { rows, columns } =
-    if rows < 1 or columns < 1 then zeroSizeMatrix ()
-    else
-        m = array (map \(new double[rows]) [1..columns]);
-        for [0..columns-1] do col:
-            for [0..rows-1] do row:
-                m[col][row] := f row col;
-            done;
-        done;
-        DenseCols (array (map vec.vector m))
-    fi;
-
-swapij =
-    map do { i, j, v }: { i = j, j = i, v } done;
-
-//!!! should use { row = , column = , value = } instead of i, j, v?
-enumerateSparse m =
-   (enumerate { values, indices, pointers } =
-        concat
-           (map do i:
-                start = pointers[i];
-                end = pointers[i+1];
-                map2 do j v: { i, j, v } done 
-                    (slice indices start end)
-                    (vec.list (vec.slice values start end))
-                done [0..length pointers - 2]);
-    case m of
-    SparseCSC d: swapij (enumerate d);
-    SparseCSR d: enumerate d;
-     _: [];
-    esac);
-
-enumerateDense m =
-   (enumerate d =
-        concat
-           (map do i:
-                vv = d[i];
-                map2 do j v: { i, j, v } done
-                    [0..vec.length vv - 1]
-                    (vec.list vv);
-                done [0..length d - 1]);
-    case m of
-    DenseCols c: swapij (enumerate c);
-    DenseRows r: enumerate r;
-     _: [];
-    esac);
-
-enumerate m =
-    if isSparse? m then enumerateSparse m else enumerateDense m fi;
-
-// Make a sparse matrix from entries whose i, j values are known to be
-// within range
-makeSparse type size data =
-   (isRow = case type of RowMajor (): true; ColumnMajor (): false esac;
-    ordered = 
-        sortBy do a b:
-            if a.maj == b.maj then a.min < b.min else a.maj < b.maj fi
-        done
-           (map
-                if isRow then
-                    do { i, j, v }: { maj = i, min = j, v } done;
-                else
-                    do { i, j, v }: { maj = j, min = i, v } done;
-                fi
-               (filter do d: d.v != 0 done data));
-    tagger = if isRow then SparseCSR else SparseCSC fi;
-    majorSize = if isRow then size.rows else size.columns fi;
-    minorSize = if isRow then size.columns else size.rows fi;
-    pointers = array [0];
-    setArrayCapacity pointers (size.rows + 1);
-    fillPointers n i data =
-        if n < majorSize then
-            case data of
-            d::rest:
-               (for [n..d-1] \(push pointers i);
-                fillPointers d (i+1) rest);
-             _:
-                for [n..majorSize-1] \(push pointers i);
-            esac;
-        fi;
-    fillPointers 0 0 (map (.maj) ordered);
-    tagger {
-        values = vec.fromList (map (.v) ordered),
-        indices = array (map (.min) ordered),
-        pointers,
-        extent = minorSize,
-    });
-
-// Make a sparse matrix from entries that may contain out-of-range
-// cells which need to be filtered out. This is the public API for
-// makeSparse and is also used to discard out-of-range cells from
-// resizedTo.
-newSparseMatrix type size data =
-    makeSparse type size
-       (filter
-            do { i, j, v }:
-                i == int i and i >= 0 and i < size.rows and 
-                j == int j and j >= 0 and j < size.columns
-            done data);
-
-toSparse m =
-    if isSparse? m then m
-    else
-        makeSparse (typeOf m) (size m) (enumerateDense m);
-    fi;
-
-toDense m =
-    if not (isSparse? m) then m
-    elif isRowMajor? m then
-        DenseRows (array (map do row: getRow row m done [0..height m - 1]));
-    else
-        DenseCols (array (map do col: getColumn col m done [0..width m - 1]));
-    fi;
-
-constMatrix n = generate do row col: n done;
-randomMatrix = generate do row col: Math#random() done;
-identityMatrix = constMatrix 1;
-
-transposed m =
-    case m of
-    DenseRows d: DenseCols d;
-    DenseCols d: DenseRows d;
-    SparseCSR d: SparseCSC d;
-    SparseCSC d: SparseCSR d;
-    esac;
-
-flipped m =
-    if isSparse? m then
-        makeSparse (flippedTypeOf m) (size m) (enumerateSparse m)
-    else
-        if isRowMajor? m then
-            generate do row col: at' m row col done (size m);
-        else
-            transposed
-               (generate do row col: at' m col row done
-                { rows = (width m), columns = (height m) });
-        fi
-    fi;
-
-toRowMajor m =
-    if isRowMajor? m then m else flipped m fi;
-
-toColumnMajor m =
-    if not isRowMajor? m then m else flipped m fi;
-
-equal'' comparator vecComparator m1 m2 =
-    // Prerequisite: m1 and m2 have same sparse-p and storage order
-   (compareVecLists vv1 vv2 = all id (map2 vecComparator vv1 vv2);
-    compareSparse d1 d2 =
-        d1.extent == d2.extent and
-        vecComparator d1.values d2.values and
-        d1.indices == d2.indices and
-        d1.pointers == d2.pointers;
-    case m1 of
-    DenseRows d1:
-        case m2 of DenseRows d2: compareVecLists d1 d2; _: false; esac;
-    DenseCols d1:
-        case m2 of DenseCols d2: compareVecLists d1 d2; _: false; esac;
-    SparseCSR d1:
-        case m2 of SparseCSR d2: compareSparse d1 d2; _: false; esac;
-    SparseCSC d1:
-        case m2 of SparseCSC d2: compareSparse d1 d2; _: false; esac;
-    esac);
-
-equal' comparator vecComparator m1 m2 =
-    if size m1 != size m2 then 
-        false
-    elif isRowMajor? m1 != isRowMajor? m2 then
-        equal' comparator vecComparator (flipped m1) m2;
-    elif isSparse? m1 != isSparse? m2 then
-        if isSparse? m1 then
-            equal' comparator vecComparator m1 (toSparse m2)
-        else
-            equal' comparator vecComparator (toSparse m1) m2
-        fi
-    else
-        equal'' comparator vecComparator m1 m2
-    fi;
-
-// Compare matrices using the given comparator for individual cells.
-// Note that matrices with different storage order but the same
-// contents are equal, although comparing them is slow.
-//!!! Document the fact that sparse matrices can only be equal if they
-// have the same set of non-zero cells (regardless of comparator used)
-equalUnder comparator =
-    equal' comparator (vec.equalUnder comparator);
-
-equal =
-    equal' (==) vec.equal;
-
-newMatrix type data = //!!! NB does not copy data
-   (tagger = case type of RowMajor (): DenseRows; ColumnMajor (): DenseCols esac;
-    if empty? data or vec.empty? (head data)
-    then zeroSizeMatrix ()
-    else tagger (array data)
-    fi);
-
-newRowVector data = //!!! NB does not copy data
-    DenseRows (array [data]);
-
-newColumnVector data = //!!! NB does not copy data
-    DenseCols (array [data]);
-
-denseLinearOp op m1 m2 =
-    if isRowMajor? m1 then
-        newMatrix (typeOf m1) 
-           (map2 do c1 c2: op c1 c2 done (asRows m1) (asRows m2));
-    else
-        newMatrix (typeOf m1) 
-           (map2 do c1 c2: op c1 c2 done (asColumns m1) (asColumns m2));
-    fi;
-
-sparseSumOrDifference op m1 m2 =
-   (h = [:];
-    for (enumerate m1) do { i, j, v }:
-        if not (i in h) then h[i] := [:] fi;
-        h[i][j] := v;
-    done;
-    for (enumerate m2) do { i, j, v }:
-        if not (i in h) then h[i] := [:] fi;
-        if j in h[i] then h[i][j] := op h[i][j] v;
-        else h[i][j] := op 0 v;
-        fi;
-    done;
-    entries = concat
-       (map do i:
-            kk = keys h[i];
-            map2 do j v: { i, j, v } done kk (map (at h[i]) kk)
-            done (keys h));
-    makeSparse (typeOf m1) (size m1) entries);
-
-sum' m1 m2 =
-    if (size m1) != (size m2)
-    then failWith "Matrices are not the same size: \(size m1), \(size m2)";
-    elif isSparse? m1 and isSparse? m2 then
-        sparseSumOrDifference (+) m1 m2;
-    else
-        denseLinearOp bf.add m1 m2;
-    fi;
-
-difference m1 m2 =
-    if (size m1) != (size m2)
-    then failWith "Matrices are not the same size: \(size m1), \(size m2)";
-    elif isSparse? m1 and isSparse? m2 then
-        sparseSumOrDifference (-) m1 m2;
-    else
-        denseLinearOp bf.subtract m1 m2;
-    fi;
-
-scaled factor m =
-    if isSparse? m then
-        makeSparse (typeOf m) (size m)
-           (map do { i, j, v }: { i, j, v = factor * v } done (enumerate m))
-    elif isRowMajor? m then
-        newMatrix (typeOf m) (map (bf.scaled factor) (asRows m));
-    else
-        newMatrix (typeOf m) (map (bf.scaled factor) (asColumns m));
-    fi;
-
-abs' m =
-    if isSparse? m then
-        makeSparse (typeOf m) (size m)
-           (map do { i, j, v }: { i, j, v = abs v } done (enumerate m))
-    elif isRowMajor? m then
-        newMatrix (typeOf m) (map bf.abs (asRows m));
-    else
-        newMatrix (typeOf m) (map bf.abs (asColumns m));
-    fi;
-
-filter f m =
-    if isSparse? m then
-        makeSparse (typeOf m) (size m)
-           (map do { i, j, v }: { i, j, v = if f v then v else 0 fi } done
-               (enumerate m))
-    else
-        vfilter = vec.fromList . (map do i: if f i then i else 0 fi done) . vec.list;
-        if isRowMajor? m then
-            newMatrix (typeOf m) (map vfilter (asRows m));
-        else
-            newMatrix (typeOf m) (map vfilter (asColumns m));
-        fi;
-    fi;
-
-sparseProductLeft size m1 m2 =
-   ({ values, indices, pointers } = case m1 of
-         SparseCSR d: d;
-         SparseCSC d: d;
-         _: failWith "sparseProductLeft called for non-sparse m1";
-         esac;
-    rows = isRowMajor? m1;
-    data = array (map \(new double[size.rows]) [1..size.columns]);
-    for [0..size.columns - 1] do j':
-        c = getColumn j' m2;
-        var p = 0;
-        for [0..length indices - 1] do ix:
-            ix == pointers[p+1] loop (p := p + 1);
-            i = if rows then p else indices[ix] fi;
-            j = if rows then indices[ix] else p fi;
-            data[j'][i] := data[j'][i] + (vec.at values ix) * (vec.at c j);
-        done;
-    done;
-    DenseCols (array (map vec.vector (list data))));
-
-sparseProductRight size m1 m2 =
-   ({ values, indices, pointers } = case m2 of
-         SparseCSR d: d;
-         SparseCSC d: d;
-         _: failWith "sparseProductLeft called for non-sparse m1";
-         esac;
-    rows = isRowMajor? m2;
-    data = array (map \(new double[size.columns]) [1..size.rows]);
-    for [0..size.rows - 1] do i':
-        r = getRow i' m1;
-        var p = 0;
-        for [0..length indices - 1] do ix:
-            ix == pointers[p+1] loop (p := p + 1);
-            i = if rows then p else indices[ix] fi;
-            j = if rows then indices[ix] else p fi;
-            data[i'][j] := data[i'][j] + (vec.at values ix) * (vec.at r i);
-        done;
-    done;
-    DenseRows (array (map vec.vector (list data))));
-
-sparseProduct size m1 m2 =
-    case m2 of
-    SparseCSC d:
-       ({ values, indices, pointers } = case m1 of
-            SparseCSR d1: d1;
-            SparseCSC d1: d1;
-            _: failWith "sparseProduct called for non-sparse matrices";
-            esac;
-        rows = isRowMajor? m1;
-        var p = 0;
-        pindices = new int[length indices];
-        for [0..length indices - 1] do ix:
-            ix == pointers[p+1] loop (p := p + 1);
-            pindices[ix] := p;
-        done;
-        entries =
-           (map do j':
-                cs = sparseSlice j' d;
-                hin = mapIntoHash
-                   (at cs.indices) (vec.at cs.values)
-                   [0..length cs.indices - 1];
-                hout = [:];
-                for [0..length indices - 1] do ix:
-                    i = if rows then pindices[ix] else indices[ix] fi;
-                    j = if rows then indices[ix] else pindices[ix] fi;
-                    if j in hin then
-                        p = (vec.at values ix) * hin[j];
-                        hout[i] := p + (if i in hout then hout[i] else 0 fi);
-                    fi;
-                done;
-                map do i:
-                    { i, j = j', v = hout[i] }
-                done (keys hout);
-            done (nonEmptySlices d));
-        makeSparse (ColumnMajor ()) size (concat entries));
-    SparseCSR _:
-        sparseProduct size m1 (flipped m2);
-     _: failWith "sparseProduct called for non-sparse matrices";
-    esac;
-
-denseProduct size m1 m2 =
-   (data = array (map \(new double[size.rows]) [1..size.columns]);
-    for [0..size.rows - 1] do i:
-        row = getRow i m1;
-        for [0..size.columns - 1] do j:
-            data[j][i] := bf.sum (bf.multiply row (getColumn j m2));
-        done;
-    done;
-    DenseCols (array (map vec.vector (list data))));
-
-product m1 m2 =
-    if (size m1).columns != (size m2).rows
-    then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns) != \((size m2).rows))";
-    else 
-        size = { rows = (size m1).rows, columns = (size m2).columns };
-        if isSparse? m1 then
-            if isSparse? m2 then
-                sparseProduct size m1 m2
-            else
-                sparseProductLeft size m1 m2
-            fi
-        elif isSparse? m2 then
-            sparseProductRight size m1 m2
-        else
-            denseProduct size m1 m2
-        fi;
-    fi;
-
-concatAgainstGrain tagger getter counter mm =
-   (n = counter (size (head mm));
-    tagger (array
-       (map do i:
-           vec.concat (map (getter i) mm)
-           done [0..n-1])));
-
-concatWithGrain tagger getter counter mm =
-    tagger (array
-       (concat
-           (map do m:
-               n = counter (size m);
-               map do i: getter i m done [0..n-1]
-               done mm)));
-
-sparseConcat direction first mm =
-   (dimension d f = if direction == d then sum (map f mm) else f first fi;
-    rows = dimension (Vertical ()) height;
-    columns = dimension (Horizontal ()) width;
-    entries ioff joff ui uj mm =
-        case mm of 
-        m::rest:
-           (map do { i, j, v }: { i = i + ioff, j = j + joff, v }
-                done (enumerate m)) ++
-           (entries
-               (ioff + ui * height m)
-               (joff + uj * width m)
-                ui uj rest);
-         _: []
-        esac;
-    makeSparse (typeOf first) { rows, columns }
-        if direction == Vertical () then entries 0 0 1 0 mm
-        else entries 0 0 0 1 mm fi);
-
-checkDimensionsFor direction first mm =
-   (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
-    n = counter (size first);
-    if not (all id (map do m: counter (size m) == n done mm)) then
-        failWith "Matrix dimensions incompatible for concat (found \(map do m: counter (size m) done mm) not all of which are \(n))";
-    fi);
-
-concat direction mm = //!!! doc: storage order is taken from first matrix in sequence
-    case length mm of
-    0: zeroSizeMatrix ();
-    1: head mm;
-    _:
-        first = head mm;
-        checkDimensionsFor direction first mm;
-        if all isSparse? mm then
-            sparseConcat direction first mm
-        else
-            row = isRowMajor? first;
-            // horizontal, row-major: against grain with rows
-            // horizontal, col-major: with grain with cols
-            // vertical, row-major: with grain with rows
-            // vertical, col-major: against grain with cols
-            case direction of
-            Horizontal ():
-                if row then concatAgainstGrain DenseRows getRow (.rows) mm;
-                else concatWithGrain DenseCols getColumn (.columns) mm;
-                fi;
-            Vertical ():
-                if row then concatWithGrain DenseRows getRow (.rows) mm;
-                else concatAgainstGrain DenseCols getColumn (.columns) mm;
-                fi;
-            esac;
-        fi;
-    esac;
-
-//!!! doc note: argument order chosen for consistency with std module slice
-rowSlice m start end = //!!! doc: storage order same as input
-    if isRowMajor? m then
-        DenseRows (array (map ((flip getRow) m) [start .. end - 1]))
-    else 
-        DenseCols (array (map do v: vec.slice v start end done (asColumns m)))
-    fi;
-
-//!!! doc note: argument order chosen for consistency with std module slice
-columnSlice m start end = //!!! doc: storage order same as input
-    if not isRowMajor? m then
-        DenseCols (array (map ((flip getColumn) m) [start .. end - 1]))
-    else 
-        DenseRows (array (map do v: vec.slice v start end done (asRows m)))
-    fi;
-
-resizedTo newsize m =
-   (if newsize == (size m) then
-        m
-    elif isSparse? m then
-        // don't call makeSparse directly: want to discard
-        // out-of-range cells
-        newSparseMatrix (typeOf m) newsize (enumerateSparse m)
-    elif (height m) == 0 or (width m) == 0 then
-        zeroMatrixWithTypeOf m newsize;
-    else
-        growrows = newsize.rows - (height m);
-        growcols = newsize.columns - (width m);
-        rowm = isRowMajor? m;
-        resizedTo newsize
-            if rowm and growrows < 0 then
-                rowSlice m 0 newsize.rows
-            elif (not rowm) and growcols < 0 then 
-                columnSlice m 0 newsize.columns
-            elif growrows < 0 then 
-                rowSlice m 0 newsize.rows
-            elif growcols < 0 then 
-                columnSlice m 0 newsize.columns
-            else
-                if growrows > 0 then
-                    concat (Vertical ())
-                       [m, zeroMatrixWithTypeOf m ((size m) with { rows = growrows })]
-                else
-                    concat (Horizontal ())
-                       [m, zeroMatrixWithTypeOf m ((size m) with { columns = growcols })]
-                fi
-            fi
-    fi);
-
-{
-    size,
-    width,
-    height,
-    density,
-    nonZeroValues,
-    at = at',
-    getColumn,
-    getRow,
-    isRowMajor?,
-    isSparse?,
-    generate,
-    constMatrix,
-    randomMatrix,
-    zeroMatrix,
-    identityMatrix,
-    zeroSizeMatrix,
-    equal,
-    equalUnder,
-    transposed,
-    flipped,
-    toRowMajor,
-    toColumnMajor,
-    toSparse,
-    toDense,
-    scaled,
-    resizedTo,
-    asRows,
-    asColumns,
-    sum = sum',
-    difference,
-    abs = abs',
-    filter,
-    product,
-    concat,
-    rowSlice,
-    columnSlice,
-    newMatrix,
-    newRowVector,
-    newColumnVector,
-    newSparseMatrix,
-    enumerate
-}
-as
-{
-//!!! check whether these are right to be .selector rather than just selector
-
-    size is matrix -> { .rows is number, .columns is number },
-    width is matrix -> number,
-    height is matrix -> number,
-    density is matrix -> number,
-    nonZeroValues is matrix -> number,
-    at is matrix -> number -> number -> number,
-    getColumn is number -> matrix -> vector,
-    getRow is number -> matrix -> vector,
-    isRowMajor? is matrix -> boolean,
-    isSparse? is matrix -> boolean,
-    generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
-    constMatrix is number -> { .rows is number, .columns is number } -> matrix,
-    randomMatrix is { .rows is number, .columns is number } -> matrix,
-    zeroMatrix is { .rows is number, .columns is number } -> matrix, 
-    identityMatrix is { .rows is number, .columns is number } -> matrix, 
-    zeroSizeMatrix is () -> matrix,
-    equal is matrix -> matrix -> boolean,
-    equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean,
-    transposed is matrix -> matrix,
-    flipped is matrix -> matrix, 
-    toRowMajor is matrix -> matrix, 
-    toColumnMajor is matrix -> matrix,
-    toSparse is matrix -> matrix,
-    toDense is matrix -> matrix,
-    scaled is number -> matrix -> matrix,
-    thresholded is number -> matrix -> matrix,
-    resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
-    asRows is matrix -> list<vector>, 
-    asColumns is matrix -> list<vector>,
-    sum is matrix -> matrix -> matrix,
-    difference is matrix -> matrix -> matrix,
-    abs is matrix -> matrix,
-    filter is (number -> boolean) -> matrix -> matrix,
-    product is matrix -> matrix -> matrix,
-    concat is (Horizontal () | Vertical ()) -> list<matrix> -> matrix,
-    rowSlice is matrix -> number -> number -> matrix, 
-    columnSlice is matrix -> number -> number -> matrix,
-    newMatrix is (ColumnMajor () | RowMajor ()) -> list<vector> -> matrix, 
-    newRowVector is vector -> matrix, 
-    newColumnVector is vector -> matrix,
-    newSparseMatrix is (ColumnMajor () | RowMajor ()) -> { .rows is number, .columns is number } -> list<{ .i is number, .j is number, .v is number }> -> matrix,
-    enumerate is matrix -> list<{ .i is number, .j is number, .v is number }>
-}
-
--- a/yetilab/matrix/matrixtype.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-
-module yetilab.matrix.matrixtype;
-
-load yetilab.vector.vectortype;
-
-typedef opaque matrix =
-    DenseRows. array<vector> | // array of rows
-    DenseCols. array<vector> | // array of columns
-    SparseCSR. {
-        .values is vector,
-        .indices is array<number>, // column index of each value
-        .pointers is array<number>, // offset of first value in each row
-        .extent is number // max possible index + 1, i.e. number of columns
-        } |
-    SparseCSC. {
-        .values is vector,
-        .indices is array<number>, // row index of each value
-        .pointers is array<number>, // offset of first value in each column
-        .extent is number // max pointers index + 1, i.e. number of rows
-        };
-
-();
-
--- a/yetilab/matrix/test/speedtest.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/matrix/test/speedtest.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,8 +1,8 @@
 
 program yetilab.matrix.test.speedtest;
 
-mat = load yetilab.matrix.matrix;
-vec = load yetilab.vector.vector;
+mat = load yetilab.matrix;
+vec = load yetilab.vector;
 
 { compare, compareUsing } = load yetilab.test.test;
 
--- a/yetilab/matrix/test/test_matrix.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/matrix/test/test_matrix.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,11 +1,11 @@
 
 module yetilab.matrix.test.test_matrix;
 
-mat = load yetilab.matrix.matrix;
-vec = load yetilab.vector.vector;
+mat = load yetilab.matrix;
+vec = load yetilab.vector;
 
-load yetilab.vector.vectortype;
-load yetilab.matrix.matrixtype;
+load yetilab.vector.type;
+load yetilab.matrix.type;
 
 import yeti.lang: FailureException;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/matrix/type.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,23 @@
+
+module yetilab.matrix.type;
+
+load yetilab.vector.type;
+
+typedef opaque matrix =
+    DenseRows. array<vector> | // array of rows
+    DenseCols. array<vector> | // array of columns
+    SparseCSR. {
+        .values is vector,
+        .indices is array<number>, // column index of each value
+        .pointers is array<number>, // offset of first value in each row
+        .extent is number // max possible index + 1, i.e. number of columns
+        } |
+    SparseCSC. {
+        .values is vector,
+        .indices is array<number>, // row index of each value
+        .pointers is array<number>, // offset of first value in each column
+        .extent is number // max pointers index + 1, i.e. number of rows
+        };
+
+();
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/plot.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,127 @@
+module yetilab.plot;
+
+vec = load yetilab.vector;
+
+import org.jzy3d.plot3d.builder: Mapper;
+import org.jzy3d.maths: Range, Coord3d;
+import org.jzy3d.chart: Chart, ChartLauncher;
+import org.jzy3d.plot3d.builder: Builder;
+import org.jzy3d.plot3d.builder.concrete: OrthonormalGrid;
+import org.jzy3d.colors.colormaps: ColorMapRainbow;
+import org.jzy3d.colors: ColorMapper, Color;
+import org.jzy3d.plot3d.rendering.canvas: Quality;
+import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;
+import org.jzy3d.plot3d.primitives: FlatLine2d, Point;
+
+newMatrixMapper matrix =
+   (class MMapper extends Mapper
+        double f(double x, double y)
+            result = matrix.getAt y x;
+            println "f(\(x),\(y)) -> \(result)";
+            result
+    end;
+    new MMapper());
+
+newMatrixLogMapper matrix =
+   (class MMapper extends Mapper
+        double f(double x, double y)
+            ln (matrix.getAt y x)
+    end;
+    new MMapper());
+
+newMapper mapFunction =
+   (class FMapper extends Mapper
+        double f(double x, double y)
+            mapFunction x y
+    end;
+    new FMapper());
+
+plotMatrix chart matrix is ~Chart -> 'a -> () =
+   (mapper = newMatrixMapper matrix;
+    size = matrix.size;
+    xrange = new Range(0, size.columns - 1);
+    yrange = new Range(0, size.rows - 1);
+    grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows);
+    println "Matrix size: \(size)";
+    surface = Builder#buildOrthonormal(grid, mapper); //??? big?
+    println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())";
+    surface#setFaceDisplayed(true);
+    surface#setWireframeDisplayed(true);
+    surface#setWireframeColor(Color#BLACK);
+    chart#getScene()#getGraph()#add(surface);
+    ());
+
+plotCurve chart depth curve is ~Chart -> number -> 'a -> () =
+   (scene = chart#getScene();
+    xx = map (.time) curve;
+    yy = map (.value) curve;
+    line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
+    line#setWireframeDisplayed(true);
+    line#setWireframeColor(Color#BLACK);
+    line#setWireframeWidth(2);
+    line#setFaceDisplayed(false);
+    scene#add(line);
+    chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
+/*
+    axes = chart#getAxeLayout();
+    axes#setXAxeLabelDisplayed(false);
+    axes#setYAxeLabelDisplayed(false);
+    axes#setZAxeLabelDisplayed(true);
+    axes#setZAxeLabel("unit goes here"); //!!!
+    axes#setYTickLabelDisplayed(false);
+*/
+    ());
+
+plotSeries chart depth { start, step, values } is ~Chart -> number -> 'a -> () =
+   (scene = chart#getScene();
+    xx = map do i: start + step * i done [0..length values - 1];
+    yy = list values;
+    line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
+    line#setWireframeDisplayed(true);
+    line#setWireframeColor(Color#BLACK);
+    line#setWireframeWidth(2);
+    line#setFaceDisplayed(false);
+    scene#add(line);
+    chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
+/*
+    axes = chart#getAxeLayout();
+    axes#setXAxeLabelDisplayed(false);
+    axes#setYAxeLabelDisplayed(false);
+    axes#setZAxeLabelDisplayed(true);
+    axes#setZAxeLabel("unit goes here"); //!!!
+    axes#setYTickLabelDisplayed(false);
+*/
+    ());
+
+plot structures =
+   (chart = new Chart(Quality#Nicest);
+    var depth = 0;
+    for structures do s:
+        case s of
+        Grid matrix:
+            plotMatrix chart matrix;
+        Curve curve:
+            plotCurve chart depth curve;
+        Series series:
+            plotSeries chart depth series;
+        Vector vector:
+            plotSeries chart depth
+                { start = 0, step = 1, values = vec.list vector };
+        other:
+            failWith "Unable to plot \(other)";
+        esac;
+        depth := depth + 1;
+    done;
+    ChartLauncher#openChart(chart);
+    chart);
+
+{
+    newMatrixMapper,
+    newMatrixLogMapper,
+    newMapper,
+    plotMatrix, 
+    plotCurve,
+    plotSeries,
+    plot,
+}
+
--- a/yetilab/plot/plot.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-module yetilab.plot.plot;
-
-vec = load yetilab.vector.vector;
-
-import org.jzy3d.plot3d.builder: Mapper;
-import org.jzy3d.maths: Range, Coord3d;
-import org.jzy3d.chart: Chart, ChartLauncher;
-import org.jzy3d.plot3d.builder: Builder;
-import org.jzy3d.plot3d.builder.concrete: OrthonormalGrid;
-import org.jzy3d.colors.colormaps: ColorMapRainbow;
-import org.jzy3d.colors: ColorMapper, Color;
-import org.jzy3d.plot3d.rendering.canvas: Quality;
-import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;
-import org.jzy3d.plot3d.primitives: FlatLine2d, Point;
-
-newMatrixMapper matrix =
-   (class MMapper extends Mapper
-        double f(double x, double y)
-            result = matrix.getAt y x;
-            println "f(\(x),\(y)) -> \(result)";
-            result
-    end;
-    new MMapper());
-
-newMatrixLogMapper matrix =
-   (class MMapper extends Mapper
-        double f(double x, double y)
-            ln (matrix.getAt y x)
-    end;
-    new MMapper());
-
-newMapper mapFunction =
-   (class FMapper extends Mapper
-        double f(double x, double y)
-            mapFunction x y
-    end;
-    new FMapper());
-
-plotMatrix chart matrix is ~Chart -> 'a -> () =
-   (mapper = newMatrixMapper matrix;
-    size = matrix.size;
-    xrange = new Range(0, size.columns - 1);
-    yrange = new Range(0, size.rows - 1);
-    grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows);
-    println "Matrix size: \(size)";
-    surface = Builder#buildOrthonormal(grid, mapper); //??? big?
-    println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())";
-    surface#setFaceDisplayed(true);
-    surface#setWireframeDisplayed(true);
-    surface#setWireframeColor(Color#BLACK);
-    chart#getScene()#getGraph()#add(surface);
-    ());
-
-plotCurve chart depth curve is ~Chart -> number -> 'a -> () =
-   (scene = chart#getScene();
-    xx = map (.time) curve;
-    yy = map (.value) curve;
-    line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
-    line#setWireframeDisplayed(true);
-    line#setWireframeColor(Color#BLACK);
-    line#setWireframeWidth(2);
-    line#setFaceDisplayed(false);
-    scene#add(line);
-    chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
-/*
-    axes = chart#getAxeLayout();
-    axes#setXAxeLabelDisplayed(false);
-    axes#setYAxeLabelDisplayed(false);
-    axes#setZAxeLabelDisplayed(true);
-    axes#setZAxeLabel("unit goes here"); //!!!
-    axes#setYTickLabelDisplayed(false);
-*/
-    ());
-
-plotSeries chart depth { start, step, values } is ~Chart -> number -> 'a -> () =
-   (scene = chart#getScene();
-    xx = map do i: start + step * i done [0..length values - 1];
-    yy = list values;
-    line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
-    line#setWireframeDisplayed(true);
-    line#setWireframeColor(Color#BLACK);
-    line#setWireframeWidth(2);
-    line#setFaceDisplayed(false);
-    scene#add(line);
-    chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
-/*
-    axes = chart#getAxeLayout();
-    axes#setXAxeLabelDisplayed(false);
-    axes#setYAxeLabelDisplayed(false);
-    axes#setZAxeLabelDisplayed(true);
-    axes#setZAxeLabel("unit goes here"); //!!!
-    axes#setYTickLabelDisplayed(false);
-*/
-    ());
-
-plot structures =
-   (chart = new Chart(Quality#Nicest);
-    var depth = 0;
-    for structures do s:
-        case s of
-        Grid matrix:
-            plotMatrix chart matrix;
-        Curve curve:
-            plotCurve chart depth curve;
-        Series series:
-            plotSeries chart depth series;
-        Vector vector:
-            plotSeries chart depth
-                { start = 0, step = 1, values = vec.list vector };
-        other:
-            failWith "Unable to plot \(other)";
-        esac;
-        depth := depth + 1;
-    done;
-    ChartLauncher#openChart(chart);
-    chart);
-
-{
-    newMatrixMapper,
-    newMatrixLogMapper,
-    newMapper,
-    plotMatrix, 
-    plotCurve,
-    plotSeries,
-    plot,
-}
-
--- a/yetilab/signal/test/test_window.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/signal/test/test_window.yeti	Thu May 23 19:33:06 2013 +0100
@@ -2,7 +2,7 @@
 module yetilab.signal.test.test_window;
 
 win = load yetilab.signal.window;
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 
 { compare, compareUsing } = load yetilab.test.test;
 
--- a/yetilab/signal/window.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/signal/window.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,7 +1,7 @@
 
 module yetilab.signal.window;
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 bf = load yetilab.vector.blockfuncs;
 
 cosineWindowSymmetric a0 a1 a2 a3 n =
--- a/yetilab/stream/audiofile.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/audiofile.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,7 +1,7 @@
 
 module yetilab.stream.audiofile;
 
-load yetilab.stream.streamtype;
+load yetilab.stream.type;
 
 import javax.sound.sampled:
      AudioSystem, AudioInputStream, AudioFormat, AudioFormat$Encoding,
@@ -12,7 +12,7 @@
 import java.nio: ByteBuffer, ByteOrder;
 
 ch = load yetilab.stream.channels;
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 
 decode8u bytes doubles n is ~byte[] -> ~double[] -> number -> () =
    (for [0..n-1] do i:
--- a/yetilab/stream/channels.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/channels.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,10 +1,10 @@
 
 module yetilab.stream.channels;
 
-vec = load yetilab.vector.vector;
-mat = load yetilab.matrix.matrix;
+vec = load yetilab.vector;
+mat = load yetilab.matrix;
 
-load yetilab.vector.vectortype;
+load yetilab.vector.type;
 
 //!!! "internal" vector function to retrieve data for read-only
 // purposes without copying
--- a/yetilab/stream/filter.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/filter.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,10 +1,10 @@
 
 module yetilab.stream.filter;
 
-mat = load yetilab.matrix.matrix;
+mat = load yetilab.matrix;
 ch = load yetilab.stream.channels;
 
-load yetilab.stream.streamtype;
+load yetilab.stream.type;
 
 minDurationOf d1 d2 =
     case d1 of 
--- a/yetilab/stream/framer.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/framer.yeti	Thu May 23 19:33:06 2013 +0100
@@ -6,12 +6,12 @@
  * overlapping) frames of data.
  */
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 bf = load yetilab.vector.blockfuncs;
 af = load yetilab.stream.audiofile;
 win = load yetilab.signal.window;
 fft = load yetilab.transform.fft;
-mat = load yetilab.matrix.matrix;
+mat = load yetilab.matrix;
 ch = load yetilab.stream.channels;
 
 blockList framesize stream =
--- a/yetilab/stream/playback.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/playback.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,7 +1,7 @@
 
 module yetilab.stream.playback;
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 af = load yetilab.stream.audiofile;
 ch = load yetilab.stream.channels;
 
--- a/yetilab/stream/streamtype.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-
-module yetilab.stream.streamtype;
-
-load yetilab.matrix.matrixtype;
-
-typedef stream =
-    {
-        position is number,
-        channels is number,
-        sampleRate is number,
-        available is Known number | Unknown () | Infinite (),
-        finished? is boolean,
-        read is number -> matrix,
-        close is () -> (),
-    };
-
-();
-
--- a/yetilab/stream/syntheticstream.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/syntheticstream.yeti	Thu May 23 19:33:06 2013 +0100
@@ -2,10 +2,10 @@
 module yetilab.stream.syntheticstream;
 
 ch = load yetilab.stream.channels;
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 
-load yetilab.vector.vectortype;
-load yetilab.stream.streamtype;
+load yetilab.vector.type;
+load yetilab.stream.type;
 
 generated sampleRate generator =
    (// generator takes sample number as arg, returns number in -1,+1 range
--- a/yetilab/stream/test/test_audiofile.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/test/test_audiofile.yeti	Thu May 23 19:33:06 2013 +0100
@@ -2,8 +2,8 @@
 module yetilab.stream.test.test_audiofile;
 
 af = load yetilab.stream.audiofile;
-vec = load yetilab.vector.vector;
-mat = load yetilab.matrix.matrix;
+vec = load yetilab.vector;
+mat = load yetilab.matrix;
 bf = load yetilab.vector.blockfuncs;
 
 ref = load yetilab.stream.test.audiofile_reference;
--- a/yetilab/stream/test/test_channels.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/test/test_channels.yeti	Thu May 23 19:33:06 2013 +0100
@@ -2,8 +2,8 @@
 module yetilab.stream.test.test_channels;
 
 ch = load yetilab.stream.channels;
-mat = load yetilab.matrix.matrix;
-vec = load yetilab.vector.vector;
+mat = load yetilab.matrix;
+vec = load yetilab.vector;
 
 { compare, compareUsing } = load yetilab.test.test;
 
--- a/yetilab/stream/test/test_filter.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/test/test_filter.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,8 +1,8 @@
 
 module yetilab.stream.test.test_filter;
 
-vec = load yetilab.vector.vector;
-mat = load yetilab.matrix.matrix;
+vec = load yetilab.vector;
+mat = load yetilab.matrix;
 syn = load yetilab.stream.syntheticstream;
 filt = load yetilab.stream.filter;
 
--- a/yetilab/stream/test/test_framer.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/test/test_framer.yeti	Thu May 23 19:33:06 2013 +0100
@@ -2,8 +2,8 @@
 module yetilab.stream.test.test_framer;
 
 fr = load yetilab.stream.framer;
-vec = load yetilab.vector.vector;
-mat = load yetilab.matrix.matrix;
+vec = load yetilab.vector;
+mat = load yetilab.matrix;
 syn = load yetilab.stream.syntheticstream;
 
 { compare, compareUsing } = load yetilab.test.test;
--- a/yetilab/stream/test/test_syntheticstream.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/stream/test/test_syntheticstream.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,8 +1,8 @@
 
 module yetilab.stream.test.test_syntheticstream;
 
-vec = load yetilab.vector.vector;
-mat = load yetilab.matrix.matrix;
+vec = load yetilab.vector;
+mat = load yetilab.matrix;
 syn = load yetilab.stream.syntheticstream;
 
 { compare, compareUsing } = load yetilab.test.test;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/type.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,18 @@
+
+module yetilab.stream.type;
+
+load yetilab.matrix.type;
+
+typedef stream =
+    {
+        position is number,
+        channels is number,
+        sampleRate is number,
+        available is Known number | Unknown () | Infinite (),
+        finished? is boolean,
+        read is number -> matrix,
+        close is () -> (),
+    };
+
+();
+
--- a/yetilab/transform/fft.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/transform/fft.yeti	Thu May 23 19:33:06 2013 +0100
@@ -3,10 +3,10 @@
 
 import edu.emory.mathcs.jtransforms.fft: DoubleFFT_1D;
 
-vec = load yetilab.vector.vector;
-complex = load yetilab.vector.complex;
+vec = load yetilab.vector;
+complex = load yetilab.complex;
 
-load yetilab.vector.complextype;
+load yetilab.complex.type;
 
 packedToComplex len p is number -> ~double[] -> array<cplx> =
    (n = len / 2;
--- a/yetilab/transform/test/test_fft.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/transform/test/test_fft.yeti	Thu May 23 19:33:06 2013 +0100
@@ -2,8 +2,8 @@
 module yetilab.transform.test.test_fft;
 
 { realForward, realInverse } = load yetilab.transform.fft;
-{ list, fromList } = load yetilab.vector.vector;
-{ complex } = load yetilab.vector.complex;
+{ list, fromList } = load yetilab.vector;
+{ complex } = load yetilab.complex;
 
 { compare } = load yetilab.test.test;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/vamp.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,336 @@
+module yetilab.vamp;
+
+import org.vamp_plugins:
+       Plugin, Plugin$InputDomain,
+       PluginLoader, PluginLoader$AdapterFlags, PluginLoader$LoadFailedException,
+       ParameterDescriptor, OutputDescriptor, OutputDescriptor$SampleType,
+       RealTime, Feature;
+
+import java.lang: UnsatisfiedLinkError;
+
+import java.util: Map, List;
+
+vec = load yetilab.vector;
+fr = load yetilab.stream.framer;
+af = load yetilab.stream.audiofile;
+mat = load yetilab.matrix;
+vamprdf = load yetilab.vamp.vamprdf;
+vamppost = load yetilab.vamp.vamppost;
+
+store = load yertle.store;
+
+realTime r is ~RealTime -> number = r#sec() + (r#nsec() / 1000000000);
+
+feature f is ~Feature -> 'a = {
+    timestamp = if f#hasTimestamp then Time (realTime f#timestamp) else Untimed () fi,
+    duration = if f#hasDuration then Time (realTime f#duration) else Untimed () fi,
+    values = vec.fromFloats f#values,
+    label = f#label,
+    };
+
+featureList fl is ~Object -> 'a =
+    if nullptr? fl then []
+    else
+        a = fl unsafely_as ~List;
+        result = array [];
+        itr = a#iterator();
+        itr#hasNext() loop (push result (feature (itr#next() unsafely_as ~Feature)));
+        list result
+    fi;
+
+featureSet fs is ~Map -> 'a =
+   (numberOf n is ~Object -> number = (n unsafely_as ~Integer)#intValue();
+    s = [:];
+    kk = list fs#keySet()#toArray();
+    for kk do k: s[numberOf k] := featureList fs#get(k) done;
+    s);
+
+stores = [:];
+
+getSingletonStoreFor loader =
+    synchronized stores do:
+        if loader in stores then
+            stores[loader]
+        else
+            s = store.newRdfStore ();
+            loader s;
+            stores[loader] := s;
+            s;
+        fi
+    done;
+
+getSystemStore () =
+    getSingletonStoreFor vamprdf.loadSystemVampRdf;
+
+getGlobalStore () = 
+    getSingletonStoreFor vamprdf.loadGlobalVampRdf;
+
+getPluginPath () =
+   (try
+        map string PluginLoader#getInstance()#getPluginPath();
+    catch UnsatisfiedLinkError e:
+        eprintln "Warning: Unable to obtain plugin path:\n\(e)";
+        [];
+    yrt);
+
+listPlugins () =
+   (try
+        map string PluginLoader#getInstance()#listPlugins();
+    catch UnsatisfiedLinkError e:
+        eprintln "Warning: Unable to obtain plugin list:\n\(e)";
+        [];
+    yrt);
+
+getKnownPluginKeys () =
+   (store = getGlobalStore ();
+    nodes = vamprdf.allPluginNodes store;
+    // ordering is random out of the store; might as well sort
+    sort (map do n: (vamprdf.pluginDataByNode store n).pluginKey done nodes));
+
+getDataForKnownPlugin key =
+    vamprdf.pluginDataByKey (getGlobalStore ()) key;
+   
+categoryOf key =
+    list PluginLoader#getInstance()#getPluginCategory(key);
+
+inputDomain d is ~Plugin$InputDomain -> 'a = 
+    if d == Plugin$InputDomain#FREQUENCY_DOMAIN then
+        FrequencyDomain ()
+    else
+        TimeDomain ()
+    fi;
+
+parameterDescriptor pd is ~ParameterDescriptor -> 'a = {
+    identifier = pd#identifier,
+    name = pd#name,
+    description = pd#description,
+    unit = pd#unit,
+    minValue = pd#minValue,
+    maxValue = pd#maxValue,
+    defaultValue = pd#defaultValue,
+    get quantize () = if pd#isQuantized then QuantizeStep pd#quantizeStep else Unquantized () fi,
+    valueNames = map string pd#valueNames
+    };
+
+sampleType t rate is ~OutputDescriptor$SampleType -> number -> 'a =
+    if t == OutputDescriptor$SampleType#OneSamplePerStep then
+        OneSamplePerStep ()
+    elif t == OutputDescriptor$SampleType#FixedSampleRate then
+        FixedSampleRate rate
+    else
+        VariableSampleRate rate
+    fi;
+
+structureOf rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = 
+   (computes = case rdfOutputData of Some d: d.computes; None (): Unknown () esac;
+    s = getSystemStore ();
+    noteIRI = case s.expand "af:Note" of IRI iri: iri; _: "" esac;
+    if od#hasFixedBinCount and od#binCount == 0 then
+        Instants ();
+    elif od#hasDuration then
+        if computes != Unknown () then
+            if computes == Event noteIRI then Notes ();
+            else Regions ();
+            fi
+        elif od#hasFixedBinCount then
+            if od#binCount > 1 then Notes ();
+            elif od#unit == "Hz" or strIndexOf (strLower od#unit) "midi" 0 >= 0 then Notes ();
+            else Regions ();
+            fi
+        else
+            Unknown ();
+        fi
+    elif od#hasFixedBinCount and od#binCount == 1 then
+        case computes of
+        Event e:
+            if strEnds? e "Segment" 
+            then Segmentation ()
+            else Curve ()
+            fi;
+        _:
+            if od#sampleType == OutputDescriptor$SampleType#OneSamplePerStep
+            then Series ()
+            else Curve ()
+            fi;
+        esac;
+    elif od#hasFixedBinCount and
+         od#sampleType != OutputDescriptor$SampleType#VariableSampleRate then
+        Grid ();
+    else
+        Unknown ();
+    fi);
+
+outputDescriptor rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = {
+    identifier = od#identifier,
+    name = od#name,
+    description = od#description,
+    get binCount () = if od#hasFixedBinCount then Fixed od#binCount else Variable () fi,
+    get valueExtents () = if od#hasKnownExtents then Known { min = od#minValue, max = od#maxValue } else Unknown () fi,
+    get valueQuantize () = if od#isQuantized then QuantizeStep od#quantizeStep else Unquantized () fi,
+    valueUnit = od#unit,
+    binNames = array (map string od#binNames),
+    sampleType = sampleType od#sampleType od#sampleRate,
+    hasDuration = od#hasDuration,
+    get computes () = case rdfOutputData of Some data: data.computes; None (): Unknown () esac,
+    get inferredStructure () = structureOf rdfOutputData od,
+    };
+
+plugin key p is string -> ~Plugin -> 'a =
+   (rdfData = vamprdf.pluginDataByKey (getSystemStore ()) key;
+    {
+    plugin = p,
+    key,
+    get apiVersion () = p#getVampApiVersion(),
+    get identifier () = p#getIdentifier(),
+    get name () = p#getName(),
+    get description () = p#getDescription(),
+    get maker () = p#getMaker(),
+    get copyright () = p#getCopyright(),
+    get version () = p#getPluginVersion(),
+    get category () = PluginLoader#getInstance()#getPluginCategory(key),
+    get hasRdfDescription () = (rdfData != None ()),
+    get infoURL () = case rdfData of Some data: data.infoURL; None (): "" esac,
+    get parameters () = array (map parameterDescriptor p#getParameterDescriptors()),
+    parameterValue identifier = p#getParameter(identifier),
+    setParameterValue identifier value = p#setParameter(identifier, value),
+    get programs () = array (map string p#getPrograms()),
+    get currentProgram () = p#getCurrentProgram(),
+    selectProgram pr = p#selectProgram(pr),
+    get inputDomain () = inputDomain p#getInputDomain(),
+    get preferredBlockSize () = p#getPreferredBlockSize(),
+    get preferredStepSize () = p#getPreferredStepSize(),
+    get minChannelCount () = p#getMinChannelCount(),
+    get maxChannelCount () = p#getMaxChannelCount(),
+    initialise { channels, hop, blockSize } = p#initialise(channels, hop, blockSize),
+    reset () = p#reset(),
+    get outputs () =
+        array case rdfData of
+        Some data: map2 outputDescriptor (map Some data.outputs) p#getOutputDescriptors();
+        None (): map (outputDescriptor (None ())) p#getOutputDescriptors();
+        esac,
+    process frame time is 'a -> ~RealTime -> 'b = 
+        featureSet p#process((map vec.floats (mat.asRows frame)) as ~float[][], 0, time),
+    getRemainingFeatures () = featureSet p#getRemainingFeatures(),
+    dispose () = p#dispose(),
+    });
+
+featuresFromSet outputNo f = if outputNo in f then f[outputNo] else [] fi;
+
+outputNumberByName p name =
+   (outputs = p.outputs;
+    case find ((== name) . (.identifier)) outputs of
+    first::rest: index first outputs;
+    _: -1;
+    esac);
+
+loadPlugin rate key =
+    try
+        OK (plugin key 
+            PluginLoader#getInstance()#loadPlugin(key, rate,
+                PluginLoader$AdapterFlags#ADAPT_INPUT_DOMAIN +
+                PluginLoader$AdapterFlags#ADAPT_CHANNEL_COUNT))
+    catch PluginLoader$LoadFailedException _:
+        Error "Failed to load Vamp plugin with key \(key)"
+    yrt;
+
+//!!! bring vector typedef into scope -- this shouldn't be necessary,
+//but see comment in processed below
+load yetilab.vector.type;
+
+processed { p, sampleRate, hop } frames count 
+    // I don't know why this type declaration is necessary. Without
+    // it, yeti records the return type as 'a and can't do anything
+    // useful with it (giving IncompatibleClassChangeError when
+    // e.g. accessing the values array)
+    is 'a -> 'b -> 'c ->
+        list<hash<number,
+                  list<{ timestamp is Time number | Untimed (), 
+                         duration  is Time number | Untimed (),
+                         label is string,
+                         values is vector
+                       }>>> =
+    case frames of
+    frame::rest:
+        p.process frame RealTime#frame2RealTime(count, sampleRate)
+        :.
+        \(processed { p, sampleRate, hop } rest (count + hop));
+    _: 
+       (rf = p.getRemainingFeatures ();
+        p.dispose ();
+        [rf]);
+    esac;
+
+converted { p, sampleRate, hop } outputNo fl =
+    map (featuresFromSet outputNo) fl;
+
+returnErrorFrom p stream text = (p.dispose (); stream.close (); failWith text);
+
+processWith key p outputNo stream =
+   (blockSize =
+        if p.preferredBlockSize == 0 then 2048
+        else p.preferredBlockSize fi;
+    stepSize =
+        if p.preferredStepSize == 0 then
+            if p.inputDomain == FrequencyDomain () then blockSize / 2
+            else blockSize fi;
+        else p.preferredStepSize fi;
+    channels = 1;
+    params = {
+        p, sampleRate = stream.sampleRate, channels = 1,
+        framesize = blockSize, blockSize, hop = stepSize
+    };
+    if p.initialise params then
+        {
+            key = key,
+            output = p.outputs[outputNo],
+            parameters = mapIntoHash id p.parameterValue
+               (map (.identifier) p.parameters),
+            config = {
+                channels, blockSize, stepSize,
+                sampleRate = stream.sampleRate
+            },
+            features = converted params outputNo
+               (processed params (fr.frames params stream) 0)
+        };
+        // If processing completed successfully, then p is
+        // disposed by processed and stream is closed by the
+        // framer
+    else
+        returnErrorFrom p stream "Failed to initialise plugin \(key) with channels = \(channels), blockSize = \(blockSize), stepSize = \(stepSize)";
+    fi);
+
+processStream key output stream =
+    case loadPlugin stream.sampleRate key of
+    OK p:
+        outputNo = outputNumberByName p output;
+        if outputNo >= 0 then
+            processWith key p outputNo stream
+        else
+            outputs = strJoin ", " (map (.identifier) p.outputs);
+            returnErrorFrom p stream "Plugin \(key) has no output named \(output) (outputs are: \(outputs))"
+        fi;
+    Error e: failWith e;
+    esac;
+
+processFile key output filename = 
+    processStream key output (af.open filename);
+
+processStreamStructured key output filename =
+    vamppost.postprocess (processStream key output filename);
+
+processFileStructured key output filename =
+    vamppost.postprocess (processFile key output filename);
+
+{
+get pluginPath = getPluginPath,
+get pluginKeys = listPlugins,
+loadPlugin,
+categoryOf,
+processStream,
+processFile,
+processStreamStructured,
+processFileStructured,
+getKnownPluginKeys,
+getDataForKnownPlugin,
+}
+
--- a/yetilab/vamp/test/test_vamp.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/vamp/test/test_vamp.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,10 +1,10 @@
 module yetilab.vamp.test.test_vamp;
 
-v = load yetilab.vamp.vamp;
+v = load yetilab.vamp;
 synthetic = load yetilab.stream.syntheticstream;
 filter = load yetilab.stream.filter;
-mat = load yetilab.matrix.matrix;
-vec = load yetilab.vector.vector;
+mat = load yetilab.matrix;
+vec = load yetilab.vector;
 
 { compare, compareUsing } = load yetilab.test.test;
 
--- a/yetilab/vamp/vamp.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,336 +0,0 @@
-module yetilab.vamp.vamp;
-
-import org.vamp_plugins:
-       Plugin, Plugin$InputDomain,
-       PluginLoader, PluginLoader$AdapterFlags, PluginLoader$LoadFailedException,
-       ParameterDescriptor, OutputDescriptor, OutputDescriptor$SampleType,
-       RealTime, Feature;
-
-import java.lang: UnsatisfiedLinkError;
-
-import java.util: Map, List;
-
-vec = load yetilab.vector.vector;
-fr = load yetilab.stream.framer;
-af = load yetilab.stream.audiofile;
-mat = load yetilab.matrix.matrix;
-vamprdf = load yetilab.vamp.vamprdf;
-vamppost = load yetilab.vamp.vamppost;
-
-store = load yertle.store;
-
-realTime r is ~RealTime -> number = r#sec() + (r#nsec() / 1000000000);
-
-feature f is ~Feature -> 'a = {
-    timestamp = if f#hasTimestamp then Time (realTime f#timestamp) else Untimed () fi,
-    duration = if f#hasDuration then Time (realTime f#duration) else Untimed () fi,
-    values = vec.fromFloats f#values,
-    label = f#label,
-    };
-
-featureList fl is ~Object -> 'a =
-    if nullptr? fl then []
-    else
-        a = fl unsafely_as ~List;
-        result = array [];
-        itr = a#iterator();
-        itr#hasNext() loop (push result (feature (itr#next() unsafely_as ~Feature)));
-        list result
-    fi;
-
-featureSet fs is ~Map -> 'a =
-   (numberOf n is ~Object -> number = (n unsafely_as ~Integer)#intValue();
-    s = [:];
-    kk = list fs#keySet()#toArray();
-    for kk do k: s[numberOf k] := featureList fs#get(k) done;
-    s);
-
-stores = [:];
-
-getSingletonStoreFor loader =
-    synchronized stores do:
-        if loader in stores then
-            stores[loader]
-        else
-            s = store.newRdfStore ();
-            loader s;
-            stores[loader] := s;
-            s;
-        fi
-    done;
-
-getSystemStore () =
-    getSingletonStoreFor vamprdf.loadSystemVampRdf;
-
-getGlobalStore () = 
-    getSingletonStoreFor vamprdf.loadGlobalVampRdf;
-
-getPluginPath () =
-   (try
-        map string PluginLoader#getInstance()#getPluginPath();
-    catch UnsatisfiedLinkError e:
-        eprintln "Warning: Unable to obtain plugin path:\n\(e)";
-        [];
-    yrt);
-
-listPlugins () =
-   (try
-        map string PluginLoader#getInstance()#listPlugins();
-    catch UnsatisfiedLinkError e:
-        eprintln "Warning: Unable to obtain plugin list:\n\(e)";
-        [];
-    yrt);
-
-getKnownPluginKeys () =
-   (store = getGlobalStore ();
-    nodes = vamprdf.allPluginNodes store;
-    // ordering is random out of the store; might as well sort
-    sort (map do n: (vamprdf.pluginDataByNode store n).pluginKey done nodes));
-
-getDataForKnownPlugin key =
-    vamprdf.pluginDataByKey (getGlobalStore ()) key;
-   
-categoryOf key =
-    list PluginLoader#getInstance()#getPluginCategory(key);
-
-inputDomain d is ~Plugin$InputDomain -> 'a = 
-    if d == Plugin$InputDomain#FREQUENCY_DOMAIN then
-        FrequencyDomain ()
-    else
-        TimeDomain ()
-    fi;
-
-parameterDescriptor pd is ~ParameterDescriptor -> 'a = {
-    identifier = pd#identifier,
-    name = pd#name,
-    description = pd#description,
-    unit = pd#unit,
-    minValue = pd#minValue,
-    maxValue = pd#maxValue,
-    defaultValue = pd#defaultValue,
-    get quantize () = if pd#isQuantized then QuantizeStep pd#quantizeStep else Unquantized () fi,
-    valueNames = map string pd#valueNames
-    };
-
-sampleType t rate is ~OutputDescriptor$SampleType -> number -> 'a =
-    if t == OutputDescriptor$SampleType#OneSamplePerStep then
-        OneSamplePerStep ()
-    elif t == OutputDescriptor$SampleType#FixedSampleRate then
-        FixedSampleRate rate
-    else
-        VariableSampleRate rate
-    fi;
-
-structureOf rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = 
-   (computes = case rdfOutputData of Some d: d.computes; None (): Unknown () esac;
-    s = getSystemStore ();
-    noteIRI = case s.expand "af:Note" of IRI iri: iri; _: "" esac;
-    if od#hasFixedBinCount and od#binCount == 0 then
-        Instants ();
-    elif od#hasDuration then
-        if computes != Unknown () then
-            if computes == Event noteIRI then Notes ();
-            else Regions ();
-            fi
-        elif od#hasFixedBinCount then
-            if od#binCount > 1 then Notes ();
-            elif od#unit == "Hz" or strIndexOf (strLower od#unit) "midi" 0 >= 0 then Notes ();
-            else Regions ();
-            fi
-        else
-            Unknown ();
-        fi
-    elif od#hasFixedBinCount and od#binCount == 1 then
-        case computes of
-        Event e:
-            if strEnds? e "Segment" 
-            then Segmentation ()
-            else Curve ()
-            fi;
-        _:
-            if od#sampleType == OutputDescriptor$SampleType#OneSamplePerStep
-            then Series ()
-            else Curve ()
-            fi;
-        esac;
-    elif od#hasFixedBinCount and
-         od#sampleType != OutputDescriptor$SampleType#VariableSampleRate then
-        Grid ();
-    else
-        Unknown ();
-    fi);
-
-outputDescriptor rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = {
-    identifier = od#identifier,
-    name = od#name,
-    description = od#description,
-    get binCount () = if od#hasFixedBinCount then Fixed od#binCount else Variable () fi,
-    get valueExtents () = if od#hasKnownExtents then Known { min = od#minValue, max = od#maxValue } else Unknown () fi,
-    get valueQuantize () = if od#isQuantized then QuantizeStep od#quantizeStep else Unquantized () fi,
-    valueUnit = od#unit,
-    binNames = array (map string od#binNames),
-    sampleType = sampleType od#sampleType od#sampleRate,
-    hasDuration = od#hasDuration,
-    get computes () = case rdfOutputData of Some data: data.computes; None (): Unknown () esac,
-    get inferredStructure () = structureOf rdfOutputData od,
-    };
-
-plugin key p is string -> ~Plugin -> 'a =
-   (rdfData = vamprdf.pluginDataByKey (getSystemStore ()) key;
-    {
-    plugin = p,
-    key,
-    get apiVersion () = p#getVampApiVersion(),
-    get identifier () = p#getIdentifier(),
-    get name () = p#getName(),
-    get description () = p#getDescription(),
-    get maker () = p#getMaker(),
-    get copyright () = p#getCopyright(),
-    get version () = p#getPluginVersion(),
-    get category () = PluginLoader#getInstance()#getPluginCategory(key),
-    get hasRdfDescription () = (rdfData != None ()),
-    get infoURL () = case rdfData of Some data: data.infoURL; None (): "" esac,
-    get parameters () = array (map parameterDescriptor p#getParameterDescriptors()),
-    parameterValue identifier = p#getParameter(identifier),
-    setParameterValue identifier value = p#setParameter(identifier, value),
-    get programs () = array (map string p#getPrograms()),
-    get currentProgram () = p#getCurrentProgram(),
-    selectProgram pr = p#selectProgram(pr),
-    get inputDomain () = inputDomain p#getInputDomain(),
-    get preferredBlockSize () = p#getPreferredBlockSize(),
-    get preferredStepSize () = p#getPreferredStepSize(),
-    get minChannelCount () = p#getMinChannelCount(),
-    get maxChannelCount () = p#getMaxChannelCount(),
-    initialise { channels, hop, blockSize } = p#initialise(channels, hop, blockSize),
-    reset () = p#reset(),
-    get outputs () =
-        array case rdfData of
-        Some data: map2 outputDescriptor (map Some data.outputs) p#getOutputDescriptors();
-        None (): map (outputDescriptor (None ())) p#getOutputDescriptors();
-        esac,
-    process frame time is 'a -> ~RealTime -> 'b = 
-        featureSet p#process((map vec.floats (mat.asRows frame)) as ~float[][], 0, time),
-    getRemainingFeatures () = featureSet p#getRemainingFeatures(),
-    dispose () = p#dispose(),
-    });
-
-featuresFromSet outputNo f = if outputNo in f then f[outputNo] else [] fi;
-
-outputNumberByName p name =
-   (outputs = p.outputs;
-    case find ((== name) . (.identifier)) outputs of
-    first::rest: index first outputs;
-    _: -1;
-    esac);
-
-loadPlugin rate key =
-    try
-        OK (plugin key 
-            PluginLoader#getInstance()#loadPlugin(key, rate,
-                PluginLoader$AdapterFlags#ADAPT_INPUT_DOMAIN +
-                PluginLoader$AdapterFlags#ADAPT_CHANNEL_COUNT))
-    catch PluginLoader$LoadFailedException _:
-        Error "Failed to load Vamp plugin with key \(key)"
-    yrt;
-
-//!!! bring vector typedef into scope -- this shouldn't be necessary,
-//but see comment in processed below
-load yetilab.vector.vectortype;
-
-processed { p, sampleRate, hop } frames count 
-    // I don't know why this type declaration is necessary. Without
-    // it, yeti records the return type as 'a and can't do anything
-    // useful with it (giving IncompatibleClassChangeError when
-    // e.g. accessing the values array)
-    is 'a -> 'b -> 'c ->
-        list<hash<number,
-                  list<{ timestamp is Time number | Untimed (), 
-                         duration  is Time number | Untimed (),
-                         label is string,
-                         values is vector
-                       }>>> =
-    case frames of
-    frame::rest:
-        p.process frame RealTime#frame2RealTime(count, sampleRate)
-        :.
-        \(processed { p, sampleRate, hop } rest (count + hop));
-    _: 
-       (rf = p.getRemainingFeatures ();
-        p.dispose ();
-        [rf]);
-    esac;
-
-converted { p, sampleRate, hop } outputNo fl =
-    map (featuresFromSet outputNo) fl;
-
-returnErrorFrom p stream text = (p.dispose (); stream.close (); failWith text);
-
-processWith key p outputNo stream =
-   (blockSize =
-        if p.preferredBlockSize == 0 then 2048
-        else p.preferredBlockSize fi;
-    stepSize =
-        if p.preferredStepSize == 0 then
-            if p.inputDomain == FrequencyDomain () then blockSize / 2
-            else blockSize fi;
-        else p.preferredStepSize fi;
-    channels = 1;
-    params = {
-        p, sampleRate = stream.sampleRate, channels = 1,
-        framesize = blockSize, blockSize, hop = stepSize
-    };
-    if p.initialise params then
-        {
-            key = key,
-            output = p.outputs[outputNo],
-            parameters = mapIntoHash id p.parameterValue
-               (map (.identifier) p.parameters),
-            config = {
-                channels, blockSize, stepSize,
-                sampleRate = stream.sampleRate
-            },
-            features = converted params outputNo
-               (processed params (fr.frames params stream) 0)
-        };
-        // If processing completed successfully, then p is
-        // disposed by processed and stream is closed by the
-        // framer
-    else
-        returnErrorFrom p stream "Failed to initialise plugin \(key) with channels = \(channels), blockSize = \(blockSize), stepSize = \(stepSize)";
-    fi);
-
-processStream key output stream =
-    case loadPlugin stream.sampleRate key of
-    OK p:
-        outputNo = outputNumberByName p output;
-        if outputNo >= 0 then
-            processWith key p outputNo stream
-        else
-            outputs = strJoin ", " (map (.identifier) p.outputs);
-            returnErrorFrom p stream "Plugin \(key) has no output named \(output) (outputs are: \(outputs))"
-        fi;
-    Error e: failWith e;
-    esac;
-
-processFile key output filename = 
-    processStream key output (af.open filename);
-
-processStreamStructured key output filename =
-    vamppost.postprocess (processStream key output filename);
-
-processFileStructured key output filename =
-    vamppost.postprocess (processFile key output filename);
-
-{
-get pluginPath = getPluginPath,
-get pluginKeys = listPlugins,
-loadPlugin,
-categoryOf,
-processStream,
-processFile,
-processStreamStructured,
-processFileStructured,
-getKnownPluginKeys,
-getDataForKnownPlugin,
-}
-
--- a/yetilab/vamp/vamppost.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/vamp/vamppost.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,7 +1,7 @@
 module yetilab.vamp.vamppost;
 
-mat = load yetilab.matrix.matrix;
-vec = load yetilab.vector.vector;
+mat = load yetilab.matrix;
+vec = load yetilab.vector;
 
 fillOneSamplePerStep config features =
    (fill' n pending features =
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/vector.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,147 @@
+
+module yetilab.vector;
+
+load yetilab.vector.type;
+
+import java.util: Arrays;
+
+//!!! This is supposed to be 100% immutable and without copying when duplicating for read only
+
+zeros n =
+    new double[n];
+
+consts m n =
+   (a = zeros n;
+    for [0..n-1] do i:
+        a[i] := m;
+    done;
+    a);
+
+ones = consts 1.0;
+
+fromList l is list?<number> -> ~double[] =
+   (arr = array(l);
+    len = length arr;
+    v = zeros len;
+    for [0..len-1] do i:
+        v[i] := arr[i];
+    done;
+    v);
+
+list' a is ~double[] -> list<number> =
+    list a;
+
+array' a is ~double[] -> array<number> =
+    array a;
+
+length' =
+    length . list';
+
+empty?' =
+    empty? . list';
+
+//!!! doc note: argument order chosen for consistency with std module function
+at' v n is ~double[] -> number -> number =
+    v[n];
+
+floats a is ~double[] -> ~float[] =
+   (len = length' a;
+    f = new float[len];
+    for [0..len-1] do i:
+        f[i] := a[i];
+    done;
+    f);
+
+fromFloats ff is ~float[] -> ~double[] =
+   (len = length (list ff);
+    a = new double[len];
+    for [0..len-1] do i:
+        a[i] := ff[i];
+    done;
+    a);
+
+equal v1 v2 =
+    list' v1 == list' v2;
+
+equalUnder comparator v1 v2 =
+    length' v1 == length' v2 and
+        all id (map2 comparator (list' v1) (list' v2));
+
+copyOf v is ~double[] -> ~double[] =
+    Arrays#copyOf(v, list' v |> length);
+
+//!!! doc note: argument order chosen for consistency with std module function
+slice v start end is ~double[] -> number -> number -> ~double[] =
+    Arrays#copyOfRange(v, start, end);
+
+resizedTo n v is number -> ~double[] -> ~double[] =
+    Arrays#copyOf(v, n);
+
+reversed v is ~double[] -> ~double[] =
+   (len = length (list v);
+    a = new double[len];
+    for [0..len-1] do i:
+        a[len-i-1] := v[i];
+    done;
+    a);
+
+concat vv is list?<~double[]> -> ~double[] =
+   (len = sum (map length' vv);
+    vout = zeros len;
+    var base = 0;
+    for vv do v: 
+        vlen = length' v;
+        for [0..vlen-1] do i: vout[base + i] := v[i] done;
+        base := base + vlen;
+    done;
+    vout);
+
+repeated v n is ~double[] -> number -> ~double[] =
+    concat (map \(v) [1..n]);
+
+{
+    zeros,
+    consts,
+    ones,
+    vector v = v,
+    primitive = copyOf,
+    floats,
+    fromFloats,
+    fromList,
+    list = list',
+    array = array',
+    length = length',
+    empty? = empty?',
+    at = at',
+    equal,
+    equalUnder,
+    slice,
+    resizedTo,
+    reversed,
+    repeated,
+    concat,
+} as {
+    zeros is number -> vector,
+    consts is number -> number -> vector,
+    ones is number -> vector,
+    vector is ~double[] -> vector,
+    primitive is vector -> ~double[],
+    floats is vector -> ~float[],
+    fromFloats is ~float[] -> vector,
+    fromList is list?<number> -> vector,
+    list is vector -> list<number>,
+    array is vector -> array<number>,
+    length is vector -> number,
+    empty? is vector -> boolean,
+    at is vector -> number -> number,
+    equal is vector -> vector -> boolean,
+    equalUnder is (number -> number -> boolean) -> vector -> vector -> boolean,
+    slice is vector -> number -> number -> vector,
+    resizedTo is number -> vector -> vector,
+    reversed is vector -> vector,
+    repeated is vector -> number -> vector,
+    concat is list?<vector> -> vector,
+}
+
+
+
--- a/yetilab/vector/blockfuncs.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/vector/blockfuncs.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,9 +1,9 @@
 
 module yetilab.vector.blockfuncs;
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 
-load yetilab.vector.vectortype;
+load yetilab.vector.type;
 
 //!!! "internal" vector function to retrieve data for read-only
 // purposes without copying
--- a/yetilab/vector/complex.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-
-module yetilab.vector.complex;
-
-load yetilab.vector.vectortype;
-load yetilab.vector.complextype;
-
-vec = load yetilab.vector.vector;
-
-import java.lang: ClassCastException;
-
-class Cplx(double real, double imag)
-    int getReal()
-        real,
-    int getImag()
-        imag,
-    double getMagnitude()
-        sqrt (real * real + imag * imag),
-    double getAngle()
-        Math#atan2(imag, real),
-    String toString()
-        if real == int real and imag == int imag then
-            if imag < 0 then
-                " \(int real) - \(int (-imag))i"
-            else 
-                " \(int real) + \(int imag)i"
-            fi
-        else
-            if imag < 0 then
-                " \(real) - \((-imag))i"
-            else 
-                " \(real) + \(imag)i"
-            fi
-        fi,
-    int hashCode()
-        Double#valueOf(real)#hashCode() + Double#valueOf(imag)#hashCode(),
-    boolean equals(Object other)
-        try
-            c = other unsafely_as ~Cplx;
-            c#getReal() == real and c#getImag() == imag
-        catch ClassCastException:
-            false
-        yrt,
-end;
-
-real c1 is ~Cplx -> number =
-    c1#getReal();
-
-imaginary c1 is ~Cplx -> number =
-    c1#getImag();
-
-complex re im is number -> number -> ~Cplx =
-    new Cplx(re, im);
-
-magnitude c is ~Cplx -> number =
-    c#getMagnitude();
-
-angle c is ~Cplx -> number =
-    c#getAngle();
-
-add c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
-    complex (real c1 + real c2) (imaginary c1 + imaginary c2);
-
-scale r c is number -> ~Cplx -> ~Cplx =
-    complex (r * real c) (r * imaginary c);
-
-zeros n is number -> array<~Cplx> =
-    array (map \(complex 0 0) [1..n]);
-
-magnitudes cc is list?<~Cplx> -> vector =
-    vec.fromList (map magnitude cc);
-
-angles cc is list?<~Cplx> -> vector =
-    vec.fromList (map angle cc);
-
-{
-   real,
-   imaginary,
-   complex,
-   magnitude,
-   angle,
-   add,
-   scale,
-   zeros,
-   magnitudes,
-   angles,
-} as {
-   real is cplx -> number,
-   imaginary is cplx -> number,
-   complex is number -> number -> cplx,
-   magnitude is cplx -> number,
-   angle is cplx -> number,
-   add is cplx -> cplx -> cplx,
-   scale is number -> cplx -> cplx,
-   zeros is number -> array<cplx>,
-   magnitudes is list?<cplx> -> vector,
-   angles is list?<cplx> -> vector,
-}
-
--- a/yetilab/vector/complextype.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-
-module yetilab.vector.complextype;
-
-typedef opaque cplx = ~Cplx;
-
-();
-
--- a/yetilab/vector/test/test_blockfuncs.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/vector/test/test_blockfuncs.yeti	Thu May 23 19:33:06 2013 +0100
@@ -3,7 +3,7 @@
 
 stdSqrt = sqrt;
 
-{ zeros, consts, ones, fromList, list } = load yetilab.vector.vector;
+{ zeros, consts, ones, fromList, list } = load yetilab.vector;
 { sum, max, min, mean, add, subtract, multiply, divideBy, scaled, abs, sqr, sqrt, rms, fftshift, ifftshift } = load yetilab.vector.blockfuncs;
 { compare } = load yetilab.test.test;
 
--- a/yetilab/vector/test/test_complex.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/vector/test/test_complex.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,11 +1,11 @@
 module yetilab.vector.test.test_complex;
 
 { real, imaginary, complex, magnitude, angle, add, scale, zeros, magnitudes, angles }
-   = load yetilab.vector.complex;
+   = load yetilab.complex;
 
 { compare } = load yetilab.test.test;
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 
 [
 
--- a/yetilab/vector/test/test_vector.yeti	Thu May 23 17:15:27 2013 +0100
+++ b/yetilab/vector/test/test_vector.yeti	Thu May 23 19:33:06 2013 +0100
@@ -1,7 +1,7 @@
 
 module yetilab.vector.test.test_vector;
 
-vec = load yetilab.vector.vector;
+vec = load yetilab.vector;
 
 { compare } = load yetilab.test.test;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/vector/type.yeti	Thu May 23 19:33:06 2013 +0100
@@ -0,0 +1,7 @@
+
+module yetilab.vector.type;
+
+typedef opaque vector = ~double[];
+
+();
+
--- a/yetilab/vector/vector.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-
-module yetilab.vector.vector;
-
-load yetilab.vector.vectortype;
-
-import java.util: Arrays;
-
-//!!! This is supposed to be 100% immutable and without copying when duplicating for read only
-
-zeros n =
-    new double[n];
-
-consts m n =
-   (a = zeros n;
-    for [0..n-1] do i:
-        a[i] := m;
-    done;
-    a);
-
-ones = consts 1.0;
-
-fromList l is list?<number> -> ~double[] =
-   (arr = array(l);
-    len = length arr;
-    v = zeros len;
-    for [0..len-1] do i:
-        v[i] := arr[i];
-    done;
-    v);
-
-list' a is ~double[] -> list<number> =
-    list a;
-
-array' a is ~double[] -> array<number> =
-    array a;
-
-length' =
-    length . list';
-
-empty?' =
-    empty? . list';
-
-//!!! doc note: argument order chosen for consistency with std module function
-at' v n is ~double[] -> number -> number =
-    v[n];
-
-floats a is ~double[] -> ~float[] =
-   (len = length' a;
-    f = new float[len];
-    for [0..len-1] do i:
-        f[i] := a[i];
-    done;
-    f);
-
-fromFloats ff is ~float[] -> ~double[] =
-   (len = length (list ff);
-    a = new double[len];
-    for [0..len-1] do i:
-        a[i] := ff[i];
-    done;
-    a);
-
-equal v1 v2 =
-    list' v1 == list' v2;
-
-equalUnder comparator v1 v2 =
-    length' v1 == length' v2 and
-        all id (map2 comparator (list' v1) (list' v2));
-
-copyOf v is ~double[] -> ~double[] =
-    Arrays#copyOf(v, list' v |> length);
-
-//!!! doc note: argument order chosen for consistency with std module function
-slice v start end is ~double[] -> number -> number -> ~double[] =
-    Arrays#copyOfRange(v, start, end);
-
-resizedTo n v is number -> ~double[] -> ~double[] =
-    Arrays#copyOf(v, n);
-
-reversed v is ~double[] -> ~double[] =
-   (len = length (list v);
-    a = new double[len];
-    for [0..len-1] do i:
-        a[len-i-1] := v[i];
-    done;
-    a);
-
-concat vv is list?<~double[]> -> ~double[] =
-   (len = sum (map length' vv);
-    vout = zeros len;
-    var base = 0;
-    for vv do v: 
-        vlen = length' v;
-        for [0..vlen-1] do i: vout[base + i] := v[i] done;
-        base := base + vlen;
-    done;
-    vout);
-
-repeated v n is ~double[] -> number -> ~double[] =
-    concat (map \(v) [1..n]);
-
-{
-    zeros,
-    consts,
-    ones,
-    vector v = v,
-    primitive = copyOf,
-    floats,
-    fromFloats,
-    fromList,
-    list = list',
-    array = array',
-    length = length',
-    empty? = empty?',
-    at = at',
-    equal,
-    equalUnder,
-    slice,
-    resizedTo,
-    reversed,
-    repeated,
-    concat,
-} as {
-    zeros is number -> vector,
-    consts is number -> number -> vector,
-    ones is number -> vector,
-    vector is ~double[] -> vector,
-    primitive is vector -> ~double[],
-    floats is vector -> ~float[],
-    fromFloats is ~float[] -> vector,
-    fromList is list?<number> -> vector,
-    list is vector -> list<number>,
-    array is vector -> array<number>,
-    length is vector -> number,
-    empty? is vector -> boolean,
-    at is vector -> number -> number,
-    equal is vector -> vector -> boolean,
-    equalUnder is (number -> number -> boolean) -> vector -> vector -> boolean,
-    slice is vector -> number -> number -> vector,
-    resizedTo is number -> vector -> vector,
-    reversed is vector -> vector,
-    repeated is vector -> number -> vector,
-    concat is list?<vector> -> vector,
-}
-
-
-
--- a/yetilab/vector/vectortype.yeti	Thu May 23 17:15:27 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-
-module yetilab.vector.vectortype;
-
-typedef opaque vector = ~double[];
-
-();
-