changeset 485:7bd05cfef750

Pull blockfuncs functions into vector (except for fftshift which goes into fft), drop vec.raw
author Chris Cannam
date Sat, 02 Nov 2013 14:58:44 +0000
parents 9f87e8eb1e7e
children 061dc568cfd8
files src/may/matrix.yeti src/may/signal/window.yeti src/may/stream/channels.yeti src/may/stream/filter.yeti src/may/stream/framer.yeti src/may/stream/resample.yeti src/may/stream/test/test_audiofile.yeti src/may/stream/test/test_resample.yeti src/may/test/all.yeti src/may/transform/fft.yeti src/may/transform/test/test_fft.yeti src/may/vector.yeti src/may/vector/blockfuncs.yeti src/may/vector/test/test_blockfuncs.yeti src/may/vector/test/test_vector.yeti
diffstat 15 files changed, 359 insertions(+), 385 deletions(-) [+]
line wrap: on
line diff
--- a/src/may/matrix.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/matrix.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -42,7 +42,6 @@
 module may.matrix;
 
 vec = load may.vector;
-bf = load may.vector.blockfuncs;
 
 typedef opaque matrix_t =
     DenseRows. array<vec.vector_t> | // array of rows
@@ -462,7 +461,7 @@
     elif isSparse? m1 and isSparse? m2 then
         sparseSumOrDifference (+) m1 m2;
     else
-        add2 v1 v2 = bf.add [v1,v2];
+        add2 v1 v2 = vec.add [v1,v2];
         denseLinearOp add2 m1 m2;
     fi;
 
@@ -472,7 +471,7 @@
     elif isSparse? m1 and isSparse? m2 then
         sparseSumOrDifference (-) m1 m2;
     else
-        denseLinearOp bf.subtract m1 m2;
+        denseLinearOp vec.subtract m1 m2;
     fi;
 
 scaled factor m =
@@ -480,9 +479,9 @@
         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));
+        newMatrix (typeOf m) (map (vec.scaled factor) (asRows m));
     else
-        newMatrix (typeOf m) (map (bf.scaled factor) (asColumns m));
+        newMatrix (typeOf m) (map (vec.scaled factor) (asColumns m));
     fi;
 
 abs' m =
@@ -490,9 +489,9 @@
         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));
+        newMatrix (typeOf m) (map vec.abs (asRows m));
     else
-        newMatrix (typeOf m) (map bf.abs (asColumns m));
+        newMatrix (typeOf m) (map vec.abs (asColumns m));
     fi;
 
 negative m =
@@ -500,9 +499,9 @@
         makeSparse (typeOf m) (size m)
            (map do { i, j, v }: { i, j, v = (-v) } done (enumerate m))
     elif isRowMajor? m then
-        newMatrix (typeOf m) (map bf.negative (asRows m));
+        newMatrix (typeOf m) (map vec.negative (asRows m));
     else
-        newMatrix (typeOf m) (map bf.negative (asColumns m));
+        newMatrix (typeOf m) (map vec.negative (asColumns m));
     fi;
 
 //!!! doc: filter by predicate, always returns sparse matrix
@@ -602,7 +601,7 @@
     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));
+            data[j][i] := vec.sum (vec.multiply row (getColumn j m2));
         done;
     done;
     DenseCols (array (map vec.vector (list data))));
--- a/src/may/signal/window.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/signal/window.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -1,7 +1,6 @@
 module may.signal.window;
 
 vec = load may.vector;
-bf = load may.vector.blockfuncs;
 mat = load may.matrix;
 maths = load may.mathmisc;
 
@@ -209,19 +208,19 @@
 
 //!!! doc note: recalculates window every time! not fast for multiple blocks, consider windowedFrames then
 windowed windowFunc data =
-    bf.multiply (windowFunc (vec.length data)) data;
+    vec.multiply (windowFunc (vec.length data)) data;
 
 windowedRows windowFunc matrix = 
    (w = windowFunc (mat.width matrix);
     mat.newMatrix (RowMajor ())
-       (map (bf.multiply w) (mat.asRows matrix)));
+       (map (vec.multiply w) (mat.asRows matrix)));
 
 windowedFrames windowFunc frames =
     case frames of
         []: frames;
          _: (first = head frames;
              window = windowFunc (vec.length first);
-             map (bf.multiply window) frames);
+             map (vec.multiply window) frames);
     esac;
 
 {
--- a/src/may/stream/channels.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/stream/channels.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -3,13 +3,12 @@
 
 vec = load may.vector;
 mat = load may.matrix;
-bf = load may.vector.blockfuncs;
 
 interleaved m =
-    bf.zipped (mat.asRows m);
+    vec.zipped (mat.asRows m);
 
 deinterleaved channels b =
-    mat.newMatrix (RowMajor ()) (list (bf.unzipped channels b));
+    mat.newMatrix (RowMajor ()) (list (vec.unzipped channels b));
 
 mixedDown m =  //!!!doc: average, not sum
    ({ columns, rows } = (mat.size m);
@@ -18,7 +17,7 @@
     elif rows == 1 then
         mat.getRow 0 m
     else
-        bf.divideBy rows (bf.add (mat.asRows m));
+        vec.divideBy rows (vec.add (mat.asRows m));
     fi);
 
 mixedDownFromInterleaved channels b =
@@ -26,14 +25,13 @@
         b;
     else
         columns = ((vec.length b) / channels);
-        v = vec.raw b;
         v' = new double[columns];
         for [0..channels-1] do row:
             for [0..columns-1] do col:
-                v'[col] := v'[col] + v[col * channels + row];
+                v'[col] := v'[col] + vec.at b (col * channels + row);
             done;
         done;
-        bf.divideBy channels (vec.vector v');
+        vec.divideBy channels (vec.vector v');
     fi;
 
 mixedFromInterleavedTo targetChannels channels b = 
@@ -43,14 +41,15 @@
         mixedDownFromInterleaved channels b;
     else
         columns = ((vec.length b) / channels);
-        v = vec.raw b;
         v' = new double[columns * targetChannels];
         for [0..targetChannels-1] do target:
             for [0..columns-1] do col:
                 if target < channels then
-                    v'[col * targetChannels + target] := v[col * channels + target];
+                    v'[col * targetChannels + target] := 
+                        vec.at b (col * channels + target);
                 elif channels == 1 and target == 1 then
-                    v'[col * targetChannels + target] := v[col * channels];
+                    v'[col * targetChannels + target] := 
+                        vec.at b (col * channels);
                 fi
             done
         done;
--- a/src/may/stream/filter.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/stream/filter.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -3,7 +3,6 @@
 
 mat = load may.matrix;
 vec = load may.vector;
-bf = load may.vector.blockfuncs;
 win = load may.signal.window;
 convolve = load may.stream.convolve;
 manip = load may.stream.manipulate;
@@ -16,19 +15,19 @@
     filterLength = vec.length kw;
     // First arg to sinc is the complete cycle length for the cutoff frequency
     idealFor freq =
-        bf.scaled (2 * freq / rate)
+        vec.scaled (2 * freq / rate)
            (win.sinc (rate / freq) filterLength);
     idealBandpass =
          if f1 < rate/2 then
-             if f0 > 0 then bf.subtract (idealFor f1) (idealFor f0)
+             if f0 > 0 then vec.subtract (idealFor f1) (idealFor f0)
              else idealFor f1
              fi
          else
-             if f0 > 0 then bf.subtract (win.dirac filterLength) (idealFor f0)
+             if f0 > 0 then vec.subtract (win.dirac filterLength) (idealFor f0)
              else win.dirac filterLength;
              fi;
          fi;
-    filter = bf.multiply idealBandpass kw;
+    filter = vec.multiply idealBandpass kw;
     filtered = convolve.convolvedWith [Framesize 1024]
        (mat.newMatrix (RowMajor ()) (map \filter [1..s.channels]))
         s;
--- a/src/may/stream/framer.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/stream/framer.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -7,7 +7,6 @@
  */
 
 vec = load may.vector;
-bf = load may.vector.blockfuncs;
 af = load may.stream.audiofile;
 win = load may.signal.window;
 fft = load may.transform.fft;
@@ -123,7 +122,7 @@
     var position = 0;
 
     factor = hop / (framesize/2);
-    w = bf.scaled factor (window framesize);
+    w = vec.scaled factor (window framesize);
     channels = mat.height (head frames); // so we don't need to keep a head ptr
 
     syncd = synchronized remaining;
@@ -137,7 +136,7 @@
             else
                 this = mat.resizedTo { columns = framesize, rows = channels }
                    (mat.newMatrix (RowMajor ())
-                       (map (bf.multiply w) (mat.asRows (head remaining))));
+                       (map (vec.multiply w) (mat.asRows (head remaining))));
                 remaining := tail remaining;
                 framesFor (samples - hop) (this::acc)
             fi;
@@ -177,7 +176,7 @@
 
 windowedFrames { framesize, hop, window } stream =
    (win = window framesize;
-    map (bf.multiply win) (monoFrames { framesize, hop } stream));
+    map (vec.multiply win) (monoFrames { framesize, hop } stream));
 
 frequencyDomainFrames parameters stream =
    (f = fft.realForward parameters.framesize;
--- a/src/may/stream/resample.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/stream/resample.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -3,7 +3,6 @@
 
 mat = load may.matrix;
 vec = load may.vector;
-bf = load may.vector.blockfuncs;
 win = load may.signal.window;
 manip = load may.stream.manipulate;
 convolve = load may.stream.convolve;
@@ -26,7 +25,7 @@
     length = if length > maxlength then maxlength else length fi;
     kw = win.kaiser (pp with { length });
     sw = win.sinc (n*2) length;
-    bf.multiply sw kw);
+    vec.multiply sw kw);
 
 durationAdjusterFor factor s =
     case s.available of
@@ -298,8 +297,8 @@
                 pd = phaseData[phase];
 
                 for [0..s.channels-1] do ch:
-                    rowdata[ch][i] := bf.sum
-                       (bf.multiply (mat.getRow ch buffer) pd.filter);
+                    rowdata[ch][i] := vec.sum
+                       (vec.multiply (mat.getRow ch buffer) pd.filter);
                 done;
             
                 buffer := mat.concat (Horizontal ())
@@ -330,7 +329,7 @@
             obtained = fill rowdata n;
             scaleFactor = (targetRate / g) / fparams.n;
             result = mat.newMatrix (RowMajor ())
-               (map do r: bf.scaled scaleFactor (vec.vector r) done rowdata);
+               (map do r: vec.scaled scaleFactor (vec.vector r) done rowdata);
             if obtained < n then mat.columnSlice result 0 obtained
             else result
             fi)
--- a/src/may/stream/test/test_audiofile.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/stream/test/test_audiofile.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -4,7 +4,6 @@
 af = load may.stream.audiofile;
 vec = load may.vector;
 mat = load may.matrix;
-bf = load may.vector.blockfuncs;
 
 ref = load may.stream.test.audiofile_reference;
 
@@ -29,7 +28,7 @@
     do test ref: abs (test - ref) < slack done);
 
 maxOf m =
-    bf.max (vec.fromList (map bf.max (mat.asRows m)));
+    vec.max (vec.fromList (map vec.max (mat.asRows m)));
 
 testReferenceFile rate channels bitdepth =
    (test = readAll (af.open (testfile "\(rate)-\(channels)-\(bitdepth)"));
--- a/src/may/stream/test/test_resample.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/stream/test/test_resample.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -2,7 +2,6 @@
 module may.stream.test.test_resample;
 
 vec = load may.vector;
-bf = load may.vector.blockfuncs;
 mat = load may.matrix;
 syn = load may.stream.syntheticstream;
 manip = load may.stream.manipulate;
@@ -154,7 +153,7 @@
     // Return magnitude spectrum, running fft at n points and then
     // truncating to m
     specOf sig n m =
-        vec.resizedTo m (bf.divideBy n (fft.realForwardMagnitude n sig));
+        vec.resizedTo m (vec.divideBy n (fft.realForwardMagnitude n sig));
 
     all id
        (map do { inrate, outrate }:
@@ -184,7 +183,7 @@
     // Interpolating any signal by N should give a signal in which
     // every Nth sample is the original signal
     data = vec.fromList [ 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 ];
-    data = vec.concat [ data, bf.scaled (5/4) data, bf.scaled (3/4) data, data ];
+    data = vec.concat [ data, vec.scaled (5/4) data, vec.scaled (3/4) data, data ];
     data = vec.concat [ data, data ];
     input = manip.withDuration (vec.length data) (syn.precalculatedMono 4 data);
     factor = 3;
@@ -198,7 +197,7 @@
 "interpolated-rs-misc": \(
     // Just as above, but using resampledTo instead of interpolated
     data = vec.fromList [ 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 ];
-    data = vec.concat [ data, bf.scaled (5/4) data, bf.scaled (3/4) data, data ];
+    data = vec.concat [ data, vec.scaled (5/4) data, vec.scaled (3/4) data, data ];
     data = vec.concat [ data, data ];
     input = manip.withDuration (vec.length data) (syn.precalculatedMono 4 data);
     factor = 3;
@@ -212,7 +211,7 @@
 "same-rate": \(
     // Test that resample to the original rate leaves data unchanged
     data = vec.fromList [ 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 ];
-    data = vec.concat [ data, bf.scaled (5/4) data, bf.scaled (3/4) data, data ];
+    data = vec.concat [ data, vec.scaled (5/4) data, vec.scaled (3/4) data, data ];
     data = vec.concat [ data, data ];
     input = syn.precalculatedMono 4 data;
     output = resample.resampledTo (input.sampleRate) input;
--- a/src/may/test/all.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/test/all.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -6,7 +6,6 @@
 tests = [
 "mathmisc"   : load may.mathmisc.test.test_mathmisc,
 "vector"     : load may.vector.test.test_vector,
-"blockfuncs" : load may.vector.test.test_blockfuncs,
 "complex"    : load may.complex.test.test_complex,
 "framer"     : load may.stream.test.test_framer,
 "channels"   : load may.stream.test.test_channels,
--- a/src/may/transform/fft.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/transform/fft.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -5,7 +5,6 @@
 
 vec = load may.vector;
 complex = load may.complex;
-bf = load may.vector.blockfuncs;
 
 packedToComplex len p is number -> ~double[] -> array<complex.complex_t> =
    (n = len / 2;
@@ -32,11 +31,11 @@
     v);
 
 unpackedToComplex len unp is number -> vec.vector_t -> array<complex.complex_t> =
-   (vv = bf.unzipped 2 unp;
+   (vv = vec.unzipped 2 unp;
     array (map2 complex.complex (vec.list vv[0]) (vec.list vv[1])));
 
 complexToUnpacked cc is array<complex.complex_t> -> vec.vector_t =
-    bf.zipped [ vec.fromList (map complex.real cc),
+    vec.zipped [ vec.fromList (map complex.real cc),
                 vec.fromList (map complex.imaginary cc) ];
 
 //!!! doc: n separately as below
@@ -82,11 +81,23 @@
         vec.vector arr;
     done);
 
+fftshift v =
+   (len = vec.length v;
+    half = int(len/2 + 0.5); // round up for odd-length sequences
+    vec.concat [vec.slice v half len, vec.slice v 0 half]);
+
+ifftshift v =
+   (len = vec.length v;
+    half = int(len/2); // round down for odd-length sequences
+    vec.concat [vec.slice v half len, vec.slice v 0 half]);
+
 {
 forward,
 inverse,
 realForward,
 realForwardMagnitude,
 realInverse,
+fftshift,
+ifftshift,
 }
 
--- a/src/may/transform/test/test_fft.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/transform/test/test_fft.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -1,8 +1,8 @@
 
 module may.transform.test.test_fft;
 
-{ forward, inverse, realForward, realForwardMagnitude, realInverse } = load may.transform.fft;
-{ list, fromList } = load may.vector;
+{ forward, inverse, realForward, realForwardMagnitude, realInverse, fftshift, ifftshift } = load may.transform.fft;
+{ list, fromList, zeros } = load may.vector;
 { complex, magnitude, real, imaginary } = load may.complex;
 
 { compare } = load may.test.test;
@@ -67,6 +67,18 @@
     testFFTComplex [1,0,-1,0] [0,1,0,-1] [0,4,0,0] [0,0,0,0];
 ),
 
+"fftshift": \(
+    compare ((list . fftshift . zeros) 0) [] and 
+        compare ((list . fftshift . fromList) [1,2,3,4]) [3,4,1,2] and
+        compare ((list . fftshift . fromList) [1,2,3,4,5]) [4,5,1,2,3]
+),
+
+"ifftshift": \(
+    compare ((list . ifftshift . zeros) 0) [] and 
+        compare ((list . ifftshift . fromList) [3,4,1,2]) [1,2,3,4] and
+        compare ((list . ifftshift . fromList) [4,5,1,2,3]) [1,2,3,4,5]
+),
+
 ] is hash<string, () -> boolean>;
 
 
--- a/src/may/vector.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/vector.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -16,9 +16,12 @@
 module may.vector;
 
 import java.util: Arrays;
+import may.bits: VectorBits;
+
+{ ceil } = load may.mathmisc;
 
 /// Return a vector of n zeros.
-zeros n =
+zeros n is number -> ~double[] =
     new double[n];
 
 /// Return a vector of length n, containing all m.
@@ -130,7 +133,7 @@
 /// Return a single new vector that contains the contents of all the
 /// given vectors, in order. (Unlike the standard module list concat
 /// function, this one cannot be lazy.)
-concat vv is list?<~double[]> -> ~double[] =
+concat' vv is list?<~double[]> -> ~double[] =
     case vv of
         [v]: v;
         v0::rest:
@@ -150,7 +153,139 @@
 /// vector, repeated n times. The vector will therefore have length n
 /// times the length of v.
 repeated v n is ~double[] -> number -> ~double[] =
-    concat (map \(v) [1..n]);
+    concat' (map \(v) [1..n]);
+
+sum' v is ~double[] -> number = 
+    VectorBits#sum(v);
+
+max' v is ~double[] -> number = 
+   (var mx = 0;
+    for [0..length v - 1] do i:
+        if i == 0 or v[i] > mx then
+            mx := v[i];
+        fi
+    done;
+    mx);
+
+maxindex v is ~double[] -> number =
+   (var mx = 0;
+    var mi = -1;
+    for [0..length v - 1] do i:
+        if i == 0 or v[i] > mx then
+            mx := v[i];
+            mi := i;
+        fi
+    done;
+    mi);
+
+min' v is ~double[] -> number = 
+   (var mn = 0;
+    for [0..length v - 1] do i:
+        if i == 0 or v[i] < mn then
+            mn := v[i];
+        fi
+    done;
+    mn);
+
+minindex v is ~double[] -> number =
+   (var mn = 0;
+    var mi = -1;
+    for [0..length v - 1] do i:
+        if i == 0 or v[i] < mn then
+            mn := v[i];
+            mi := i;
+        fi
+    done;
+    mi);
+
+mean v is ~double[] -> number =
+    case length v of
+        0: 0;
+        len: sum' v / len
+    esac;
+
+add vv is list?<~double[]> -> ~double[] =
+   (len = head (sort (map length' vv));
+    out = new double[len];
+    for vv do v:
+        VectorBits#addTo(out, v, len);
+    done;
+    out);
+
+subtract v1 v2 is ~double[] -> ~double[] -> ~double[] =
+   (len = if length v1 < length v2 then length v1 else length v2 fi;
+    out = new double[len];
+    for [0..len-1] do i:
+        out[i] := v1[i] - v2[i]
+    done;
+    out);
+
+multiply b1 b2 is ~double[] -> ~double[] -> ~double[] = 
+    VectorBits#multiply(b1, b2);
+
+scaled n v is number -> ~double[] -> ~double[] =
+    if n == 1 then v
+    else VectorBits#scaled(v, n);
+    fi;
+
+divideBy n v is number -> ~double[] -> ~double[] =
+    // Not just "scaled (1/n)" -- this way we get exact rationals. In fact
+    // the unit test for this function will fail if we use scaled (1/n)
+    if n == 1 then v
+    else fromList (map (/ n) (list' v));
+    fi;
+
+sqr v =
+    multiply v v;
+
+rms =
+    sqrt . mean . sqr;
+
+abs' =
+    fromList . (map abs) . list';
+
+negative =
+    fromList . (map (0-)) . list';
+
+sqrt' =
+    fromList . (map sqrt) . list';
+
+unityNormalised v is ~double[] -> ~double[] = 
+   (m = max' (abs' v);
+    if m != 0 then
+        divideBy m v;
+    else
+        v;
+    fi);
+
+zipped vv is list?<~double[]> -> ~double[] =
+    case vv of
+    [v]: v;
+    first::rest:
+       (len = length' first;
+        if len != min' (fromList (map length' vv)) then
+            failWith "Vectors must all be of the same length";
+        fi;
+        fromList
+           (concat
+              (map do i:
+                   map do v:
+                       at' v i
+                       done vv
+                   done [0..len-1])));
+     _: zeros 0;
+    esac;
+
+unzipped n v is number -> ~double[] -> array<~double[]> =
+    if n == 1 then array [v]
+    else 
+        len = length' v;
+        vv = array (map \(new double[ceil(len / n)]) [1..n]);
+        for [0..len-1] do x:
+            vv[x % n][int (x / n)] := at' v x;
+        done;
+        array vv;
+    fi;
 
 typedef opaque vector_t = ~double[];
 
@@ -174,8 +309,26 @@
     resizedTo,
     reverse,
     repeated,
-    concat,
-    raw = id,
+    concat = concat',
+    sum = sum',
+    mean,
+    add,
+    subtract,
+    multiply,
+    divideBy,
+    scaled,
+    abs = abs',
+    negative,
+    sqr,
+    sqrt = sqrt',
+    rms,
+    max = max',
+    min = min',
+    maxindex,
+    minindex,
+    unityNormalised,
+    zipped,
+    unzipped,
 } as {
     zeros is number -> vector_t,
     consts is number -> number -> vector_t,
@@ -197,8 +350,24 @@
     reverse is vector_t -> vector_t,
     repeated is vector_t -> number -> vector_t,
     concat is list?<vector_t> -> vector_t,
-    raw is vector_t -> ~double[],
+    sum is vector_t -> number,
+    mean is vector_t -> number,
+    add is list?<vector_t> -> vector_t,
+    subtract is vector_t -> vector_t -> vector_t,
+    multiply is vector_t -> vector_t -> vector_t, 
+    divideBy is number -> vector_t -> vector_t, 
+    scaled is number -> vector_t -> vector_t,
+    abs is vector_t -> vector_t,
+    negative is vector_t -> vector_t,
+    sqr is vector_t -> vector_t,
+    sqrt is vector_t -> vector_t,
+    rms is vector_t -> number,
+    max is vector_t -> number,
+    min is vector_t -> number,
+    maxindex is vector_t -> number,
+    minindex is vector_t -> number,
+    unityNormalised is vector_t -> vector_t,
+    zipped is list?<vector_t> -> vector_t,
+    unzipped is number -> vector_t -> array<vector_t>,
 }
 
-
-
--- a/src/may/vector/blockfuncs.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-
-module may.vector.blockfuncs;
-
-vec = load may.vector;
-
-{ ceil } = load may.mathmisc;
-
-import may.bits: VectorBits;
-
-sum' v = VectorBits#sum(vec.raw v);
-
-max' v = 
-   (dat = vec.raw v;
-    var mx = 0;
-    for [0..length dat - 1] do i:
-        if i == 0 or dat[i] > mx then
-            mx := dat[i];
-        fi
-    done;
-    mx);
-
-maxindex v =
-   (dat = vec.raw v;
-    var mx = 0;
-    var mi = -1;
-    for [0..length dat - 1] do i:
-        if i == 0 or dat[i] > mx then
-            mx := dat[i];
-            mi := i;
-        fi
-    done;
-    mi);
-
-min' v = 
-   (dat = vec.raw v;
-    var mn = 0;
-    for [0..length dat - 1] do i:
-        if i == 0 or dat[i] < mn then
-            mn := dat[i];
-        fi
-    done;
-    mn);
-
-minindex v =
-   (dat = vec.raw v;
-    var mn = 0;
-    var mi = -1;
-    for [0..length dat - 1] do i:
-        if i == 0 or dat[i] < mn then
-            mn := dat[i];
-            mi := i;
-        fi
-    done;
-    mi);
-
-mean v =
-    case vec.length v of
-        0: 0;
-        len: sum' v / len
-    esac;
-
-add bb =
-   (len = head (sort (map vec.length bb));
-    vv = map vec.raw bb;
-    out = new double[len];
-    for vv do v:
-        VectorBits#addTo(out, v, len);
-    done;
-    vec.vector out);
-
-subtract b1 b2 =
-   (v1 = vec.raw b1;
-    v2 = vec.raw b2;
-    len = if length v1 < length v2 then length v1 else length v2 fi;
-    out = new double[len];
-    for [0..len-1] do i:
-        out[i] := v1[i] - v2[i]
-    done;
-    vec.vector out);
-
-multiply b1 b2 = 
-    vec.vector VectorBits#multiply(vec.raw b1, vec.raw b2);
-
-scaled n v =
-    if n == 1 then v
-    else vec.vector VectorBits#scaled(vec.raw v, n);
-    fi;
-
-divideBy n v =
-    // Not just "scaled (1/n)" -- this way we get exact rationals. In fact
-    // the unit test for this function will fail if we use scaled (1/n)
-    if n == 1 then v
-    else vec.fromList (map (/ n) (vec.list v));
-    fi;
-
-sqr v =
-    multiply v v;
-
-rms =
-    sqrt . mean . sqr;
-
-abs' =
-    vec.fromList . (map abs) . vec.list;
-
-negative =
-    vec.fromList . (map (0-)) . vec.list;
-
-sqrt' =
-    vec.fromList . (map sqrt) . vec.list;
-
-unityNormalised v = 
-   (m = max' (abs' v);
-    if m != 0 then
-        divideBy m v;
-    else
-        v;
-    fi);
-
-fftshift v =
-   (len = vec.length v;
-    half = int(len/2 + 0.5); // round up for odd-length sequences
-    vec.concat [vec.slice v half len, vec.slice v 0 half]);
-
-ifftshift v =
-   (len = vec.length v;
-    half = int(len/2); // round down for odd-length sequences
-    vec.concat [vec.slice v half len, vec.slice v 0 half]);
-
-zipped vv =
-    case vv of
-    [v]: v;
-    first::rest:
-       (len = vec.length first;
-        if len != min' (vec.fromList (map vec.length vv)) then
-            failWith "Vectors must all be of the same length";
-        fi;
-        vec.fromList
-           (concat
-              (map do i:
-                   map do v:
-                       vec.at v i
-                       done vv
-                   done [0..len-1])));
-     _: vec.zeros 0;
-    esac;
-
-unzipped n v =
-    if n == 1 then array [v]
-    else 
-        len = vec.length v;
-        vv = array (map \(new double[ceil(len / n)]) [1..n]);
-        for [0..len-1] do x:
-            vv[x % n][int (x / n)] := vec.at v x;
-        done;
-        array (map vec.vector vv);
-    fi;
-
-{
-sum is vec.vector_t -> number = sum',
-mean is vec.vector_t -> number,
-add is list?<vec.vector_t> -> vec.vector_t,
-subtract is vec.vector_t -> vec.vector_t -> vec.vector_t,
-multiply is vec.vector_t -> vec.vector_t -> vec.vector_t, 
-divideBy is number -> vec.vector_t -> vec.vector_t, 
-scaled is number -> vec.vector_t -> vec.vector_t,
-abs is vec.vector_t -> vec.vector_t = abs',
-negative is vec.vector_t -> vec.vector_t,
-sqr is vec.vector_t -> vec.vector_t,
-sqrt is vec.vector_t -> vec.vector_t = sqrt',
-rms is vec.vector_t -> number,
-max is vec.vector_t -> number = max',
-min is vec.vector_t -> number = min',
-maxindex is vec.vector_t -> number,
-minindex is vec.vector_t -> number,
-unityNormalised is vec.vector_t -> vec.vector_t,
-fftshift is vec.vector_t -> vec.vector_t,
-ifftshift is vec.vector_t -> vec.vector_t,
-zipped is list?<vec.vector_t> -> vec.vector_t,
-unzipped is number -> vec.vector_t -> array<vec.vector_t>,
-}
-
-
-        
--- a/src/may/vector/test/test_blockfuncs.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-
-module may.vector.test.test_blockfuncs;
-
-stdSqrt = sqrt;
-
-{ zeros, consts, ones, fromList, list } = load may.vector;
-{ sum, max, min, maxindex, minindex, mean, add, subtract, multiply, divideBy, scaled, abs, negative, sqr, sqrt, rms, unityNormalised, fftshift, ifftshift, zipped, unzipped } = load may.vector.blockfuncs;
-{ compare } = load may.test.test;
-
-[
-
-"sum": \(
-    compare ((sum . zeros) 0) 0 and
-        compare ((sum . zeros) 5) 0 and
-        compare ((sum . ones) 5) 5 and
-        compare ((sum . fromList) [1,-2,3,0]) 2
-),
-
-"max": \(
-    compare ((max . fromList) [1,-2,3,0]) 3 and
-        compare ((max . fromList) [-1,-2,-3]) (-1) and
-        compare ((max . fromList) [4,1]) 4 and
-        compare ((max . fromList) []) 0
-),
-
-"min": \(
-    compare ((min . fromList) [1,-2,3,0]) (-2) and
-        compare ((min . fromList) [-1,-2,-3]) (-3) and
-        compare ((min . fromList) [4,1]) 1 and
-        compare ((min . fromList) []) 0
-),
-
-"maxindex": \(
-    compare ((maxindex . fromList) [1,-2,3,0]) 2 and
-        compare ((maxindex . fromList) [-1,-2,-3]) 0 and
-        compare ((maxindex . fromList) [4,1]) 0 and
-        compare ((maxindex . fromList) []) (-1)
-),
-
-"minindex": \(
-    compare ((minindex . fromList) [1,-2,3,0]) 1 and
-        compare ((minindex . fromList) [-1,-2,-3]) 2 and
-        compare ((minindex . fromList) [4,1]) 1 and
-        compare ((minindex . fromList) []) (-1)
-),
-
-"mean": \(
-    compare ((mean . zeros) 0) 0 and
-        compare ((mean . zeros) 5) 0 and
-        compare ((mean . ones) 5) 1 and
-        compare ((mean . fromList) [1,-2,3,0]) 0.5
-),
-
-"add": \(
-    compare (list (add [zeros 0, ones 5])) [] and
-        compare (list (add [consts 3 4, fromList [1,2,3] ])) [4,5,6] and
-        compare (list (add [consts (-3) 4, fromList [1,2,3] ])) [-2,-1,0] 
-),
-
-"subtract": \(
-    compare (list (subtract (zeros 0) (ones 5))) [] and
-        compare (list (subtract (consts 3 4) (fromList [1,2,3]))) [2,1,0] and
-        compare (list (subtract (consts (-3) 4) (fromList [1,2,3]))) [-4,-5,-6]
-),
-
-"multiply": \(
-    compare (list (multiply (zeros 0) (ones 5))) [] and
-        compare (list (multiply (consts (-3) 4) (fromList [1,2,3]))) [-3,-6,-9]
-),
-
-"divideBy": \(
-    compare (list (divideBy 5 (ones 0))) [] and
-        compare (list (divideBy 5 (fromList [1,2,-3]))) [0.2,0.4,-0.6]
-),
-
-"scaled": \(
-    compare (list (scaled 5 (ones 0))) [] and
-        compare (list (scaled 5 (fromList [1,2,-3]))) [5,10,-15]
-),
-
-"abs": \(
-    compare (list (abs (ones 0))) [] and
-        compare (list (abs (fromList [1,2,-3]))) [1,2,3]
-),
-
-"negative": \(
-    compare (list (negative (ones 0))) [] and
-        compare (list (negative (fromList [1,2,-3]))) [-1,-2,3]
-),
-
-"sqr": \(
-    compare ((list . sqr . zeros) 0) [] and
-        compare ((list . sqr . ones) 5) [1,1,1,1,1] and
-        compare ((list . sqr . fromList) [0.5,-2,3,0]) [0.25,4,9,0]
-),
-
-"sqrt": \(
-    compare ((list . sqrt . zeros) 0) [] and
-        compare ((list . sqrt . ones) 5) [1,1,1,1,1] and
-        compare ((list . sqrt . fromList) [0.25,4,9,0]) [0.5,2,3,0]
-),
-
-"rms": \(
-    compare ((rms . zeros) 0) 0 and
-        compare ((rms . ones) 5) 1 and
-        compare ((rms . fromList) [-1,2,2]) (stdSqrt 3)
-),
-
-"unityNormalised": \(
-    compare ((list . unityNormalised . fromList) [1,-2,3,0]) [1/3,-2/3,1,0] and
-        compare ((list . unityNormalised . fromList) [-1,-2,-3]) [-1/3,-2/3,-1] and
-        compare ((list . unityNormalised . fromList) [4,1]) [1,1/4] and
-        compare ((list . unityNormalised . fromList) []) []
-),
-
-"fftshift": \(
-    compare ((list . fftshift . zeros) 0) [] and 
-        compare ((list . fftshift . fromList) [1,2,3,4]) [3,4,1,2] and
-        compare ((list . fftshift . fromList) [1,2,3,4,5]) [4,5,1,2,3]
-),
-
-"ifftshift": \(
-    compare ((list . ifftshift . zeros) 0) [] and 
-        compare ((list . ifftshift . fromList) [3,4,1,2]) [1,2,3,4] and
-        compare ((list . ifftshift . fromList) [4,5,1,2,3]) [1,2,3,4,5]
-),
-
-"zipped": \(
-    compare (list (zipped (map fromList []))) [] and
-        compare (list (zipped (map fromList [[],[]]))) [] and
-        compare (list (zipped (map fromList [[1,2]]))) [1,2] and
-        compare (list (zipped (map fromList [[1,2],[3,4],[5,6]]))) [1,3,5,2,4,6]
-),
-
-"unzipped": \(
-    compare (map list (unzipped 1 (fromList []))) [[]] and
-        compare (map list (unzipped 1 (fromList [1,2,3]))) [[1,2,3]] and
-        compare (map list (unzipped 3 (fromList [1,3,5,2,4,6]))) [[1,2],[3,4],[5,6]]
-),
-
-] is hash<string, () -> boolean>;
-
-
--- a/src/may/vector/test/test_vector.yeti	Fri Nov 01 22:12:37 2013 +0000
+++ b/src/may/vector/test/test_vector.yeti	Sat Nov 02 14:58:44 2013 +0000
@@ -124,6 +124,123 @@
         vec.equal (vec.concat [v,w,v]) (vec.fromList [1,2,3,4,5,6,1,2,3])
 ),
 
+"sum": \(
+    compare ((vec.sum . vec.zeros) 0) 0 and
+        compare ((vec.sum . vec.zeros) 5) 0 and
+        compare ((vec.sum . vec.ones) 5) 5 and
+        compare ((vec.sum . vec.fromList) [1,-2,3,0]) 2
+),
+
+"max": \(
+    compare ((vec.max . vec.fromList) [1,-2,3,0]) 3 and
+        compare ((vec.max . vec.fromList) [-1,-2,-3]) (-1) and
+        compare ((vec.max . vec.fromList) [4,1]) 4 and
+        compare ((vec.max . vec.fromList) []) 0
+),
+
+"min": \(
+    compare ((vec.min . vec.fromList) [1,-2,3,0]) (-2) and
+        compare ((vec.min . vec.fromList) [-1,-2,-3]) (-3) and
+        compare ((vec.min . vec.fromList) [4,1]) 1 and
+        compare ((vec.min . vec.fromList) []) 0
+),
+
+"maxindex": \(
+    compare ((vec.maxindex . vec.fromList) [1,-2,3,0]) 2 and
+        compare ((vec.maxindex . vec.fromList) [-1,-2,-3]) 0 and
+        compare ((vec.maxindex . vec.fromList) [4,1]) 0 and
+        compare ((vec.maxindex . vec.fromList) []) (-1)
+),
+
+"minindex": \(
+    compare ((vec.minindex . vec.fromList) [1,-2,3,0]) 1 and
+        compare ((vec.minindex . vec.fromList) [-1,-2,-3]) 2 and
+        compare ((vec.minindex . vec.fromList) [4,1]) 1 and
+        compare ((vec.minindex . vec.fromList) []) (-1)
+),
+
+"mean": \(
+    compare ((vec.mean . vec.zeros) 0) 0 and
+        compare ((vec.mean . vec.zeros) 5) 0 and
+        compare ((vec.mean . vec.ones) 5) 1 and
+        compare ((vec.mean . vec.fromList) [1,-2,3,0]) 0.5
+),
+
+"add": \(
+    compare (vec.list (vec.add [vec.zeros 0, vec.ones 5])) [] and
+        compare (vec.list (vec.add [vec.consts 3 4, vec.fromList [1,2,3] ])) [4,5,6] and
+        compare (vec.list (vec.add [vec.consts (-3) 4, vec.fromList [1,2,3] ])) [-2,-1,0] 
+),
+
+"subtract": \(
+    compare (vec.list (vec.subtract (vec.zeros 0) (vec.ones 5))) [] and
+        compare (vec.list (vec.subtract (vec.consts 3 4) (vec.fromList [1,2,3]))) [2,1,0] and
+        compare (vec.list (vec.subtract (vec.consts (-3) 4) (vec.fromList [1,2,3]))) [-4,-5,-6]
+),
+
+"multiply": \(
+    compare (vec.list (vec.multiply (vec.zeros 0) (vec.ones 5))) [] and
+        compare (vec.list (vec.multiply (vec.consts (-3) 4) (vec.fromList [1,2,3]))) [-3,-6,-9]
+),
+
+"divideBy": \(
+    compare (vec.list (vec.divideBy 5 (vec.ones 0))) [] and
+        compare (vec.list (vec.divideBy 5 (vec.fromList [1,2,-3]))) [0.2,0.4,-0.6]
+),
+
+"scaled": \(
+    compare (vec.list (vec.scaled 5 (vec.ones 0))) [] and
+        compare (vec.list (vec.scaled 5 (vec.fromList [1,2,-3]))) [5,10,-15]
+),
+
+"abs": \(
+    compare (vec.list (vec.abs (vec.ones 0))) [] and
+        compare (vec.list (vec.abs (vec.fromList [1,2,-3]))) [1,2,3]
+),
+
+"negative": \(
+    compare (vec.list (vec.negative (vec.ones 0))) [] and
+        compare (vec.list (vec.negative (vec.fromList [1,2,-3]))) [-1,-2,3]
+),
+
+"sqr": \(
+    compare ((vec.list . vec.sqr . vec.zeros) 0) [] and
+        compare ((vec.list . vec.sqr . vec.ones) 5) [1,1,1,1,1] and
+        compare ((vec.list . vec.sqr . vec.fromList) [0.5,-2,3,0]) [0.25,4,9,0]
+),
+
+"sqrt": \(
+    compare ((vec.list . vec.sqrt . vec.zeros) 0) [] and
+        compare ((vec.list . vec.sqrt . vec.ones) 5) [1,1,1,1,1] and
+        compare ((vec.list . vec.sqrt . vec.fromList) [0.25,4,9,0]) [0.5,2,3,0]
+),
+
+"rms": \(
+    compare ((vec.rms . vec.zeros) 0) 0 and
+        compare ((vec.rms . vec.ones) 5) 1 and
+        compare ((vec.rms . vec.fromList) [-1,2,2]) (sqrt 3)
+),
+
+"unityNormalised": \(
+    compare ((vec.list . vec.unityNormalised . vec.fromList) [1,-2,3,0]) [1/3,-2/3,1,0] and
+        compare ((vec.list . vec.unityNormalised . vec.fromList) [-1,-2,-3]) [-1/3,-2/3,-1] and
+        compare ((vec.list . vec.unityNormalised . vec.fromList) [4,1]) [1,1/4] and
+        compare ((vec.list . vec.unityNormalised . vec.fromList) []) []
+),
+
+"zipped": \(
+    compare (vec.list (vec.zipped (map vec.fromList []))) [] and
+        compare (vec.list (vec.zipped (map vec.fromList [[],[]]))) [] and
+        compare (vec.list (vec.zipped (map vec.fromList [[1,2]]))) [1,2] and
+        compare (vec.list (vec.zipped (map vec.fromList [[1,2],[3,4],[5,6]]))) [1,3,5,2,4,6]
+),
+
+"unzipped": \(
+    compare (map vec.list (vec.unzipped 1 (vec.fromList []))) [[]] and
+        compare (map vec.list (vec.unzipped 1 (vec.fromList [1,2,3]))) [[1,2,3]] and
+        compare (map vec.list (vec.unzipped 3 (vec.fromList [1,3,5,2,4,6]))) [[1,2],[3,4],[5,6]]
+),
+
 ] is hash<string, () -> boolean>;