changeset 123:a37635bbb2c1

Some (but not all) MathUtilities unit tests, and accompanying fixes
author Chris Cannam
date Fri, 04 Oct 2013 18:34:30 +0100
parents 2b5b37255db2
children 263181813eec
files maths/MathUtilities.cpp maths/MathUtilities.h tests/Makefile tests/TestMathUtilities.cpp
diffstat 4 files changed, 187 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/maths/MathUtilities.cpp	Fri Oct 04 16:43:44 2013 +0100
+++ b/maths/MathUtilities.cpp	Fri Oct 04 18:34:30 2013 +0100
@@ -16,6 +16,8 @@
 #include "MathUtilities.h"
 
 #include <iostream>
+#include <algorithm>
+#include <vector>
 #include <cmath>
 
 
@@ -75,54 +77,27 @@
 
 double MathUtilities::round(double x)
 {
-    double val = (double)floor(x + 0.5);
-  
-    return val;
+    if (x < 0) {
+        return -floor(-x + 0.5);
+    } else {
+        return floor(x + 0.5);
+    }
 }
 
 double MathUtilities::median(const double *src, unsigned int len)
 {
-    unsigned int i, j;
-    double tmp = 0.0;
-    double tempMedian;
-    double medianVal;
- 
-    double* scratch = new double[ len ];//Vector < double > sortedX = Vector < double > ( size );
+    if (len == 0) return 0;
+    
+    std::vector<double> scratch;
+    for (int i = 0; i < len; ++i) scratch.push_back(src[i]);
+    std::sort(scratch.begin(), scratch.end());
 
-    for ( i = 0; i < len; i++ )
-    {
-	scratch[i] = src[i];
+    int middle = len/2;
+    if (len % 2 == 0) {
+        return (scratch[middle] + scratch[middle - 1]) / 2;
+    } else {
+        return scratch[middle];
     }
-
-    for ( i = 0; i < len - 1; i++ )
-    {
-	for ( j = 0; j < len - 1 - i; j++ )
-	{
-	    if ( scratch[j + 1] < scratch[j] )
-	    {
-		// compare the two neighbors
-		tmp = scratch[j]; // swap a[j] and a[j+1]
-		scratch[j] = scratch[j + 1];
-		scratch[j + 1] = tmp;
-	    }
-	}
-    }
-    int middle;
-    if ( len % 2 == 0 )
-    {
-	middle = len / 2;
-	tempMedian = ( scratch[middle] + scratch[middle - 1] ) / 2;
-    }
-    else
-    {
-	middle = ( int )floor( len / 2.0 );
-	tempMedian = scratch[middle];
-    }
-
-    medianVal = tempMedian;
-
-    delete [] scratch;
-    return medianVal;
 }
 
 double MathUtilities::sum(const double *src, unsigned int len)
@@ -142,6 +117,8 @@
 {
     double retVal =0.0;
 
+    if (len == 0) return 0;
+
     double s = sum( src, len );
 	
     retVal =  s  / (double)len;
@@ -155,6 +132,8 @@
 {
     double sum = 0.;
 	
+    if (count == 0) return 0;
+    
     for (int i = 0; i < (int)count; ++i)
     {
         sum += src[start + i];
@@ -365,7 +344,7 @@
 bool
 MathUtilities::isPowerOfTwo(int x)
 {
-    if (x < 2) return false;
+    if (x < 1) return false;
     if (x & (x-1)) return false;
     return true;
 }
@@ -374,6 +353,7 @@
 MathUtilities::nextPowerOfTwo(int x)
 {
     if (isPowerOfTwo(x)) return x;
+    if (x < 1) return 1;
     int n = 1;
     while (x) { x >>= 1; n <<= 1; }
     return n;
@@ -383,6 +363,7 @@
 MathUtilities::previousPowerOfTwo(int x)
 {
     if (isPowerOfTwo(x)) return x;
+    if (x < 1) return 1;
     int n = 1;
     x >>= 1;
     while (x) { x >>= 1; n <<= 1; }
@@ -393,8 +374,19 @@
 MathUtilities::nearestPowerOfTwo(int x)
 {
     if (isPowerOfTwo(x)) return x;
-    int n0 = previousPowerOfTwo(x), n1 = nearestPowerOfTwo(x);
+    int n0 = previousPowerOfTwo(x), n1 = nextPowerOfTwo(x);
     if (x - n0 < n1 - x) return n0;
     else return n1;
 }
 
+int
+MathUtilities::factorial(int x)
+{
+    if (x < 0) return 0;
+    int f = 1;
+    for (int i = 1; i <= x; ++i) {
+	f = f * i;
+    }
+    return f;
+}
+
--- a/maths/MathUtilities.h	Fri Oct 04 16:43:44 2013 +0100
+++ b/maths/MathUtilities.h	Fri Oct 04 18:34:30 2013 +0100
@@ -51,11 +51,11 @@
         NormaliseUnitMax
     };
 
-    static void   normalise(double *data, int length,
-                            NormaliseType n = NormaliseUnitMax);
+    static void normalise(double *data, int length,
+                          NormaliseType n = NormaliseUnitMax);
 
-    static void   normalise(std::vector<double> &data,
-                            NormaliseType n = NormaliseUnitMax);
+    static void normalise(std::vector<double> &data,
+                          NormaliseType n = NormaliseUnitMax);
 
     // moving mean threshholding:
     static void adaptiveThreshold(std::vector<double> &data);
@@ -63,7 +63,9 @@
     static bool isPowerOfTwo(int x);
     static int nextPowerOfTwo(int x); // e.g. 1300 -> 2048, 2048 -> 2048
     static int previousPowerOfTwo(int x); // e.g. 1300 -> 1024, 2048 -> 2048
-    static int nearestPowerOfTwo(int x); // e.g. 1300 -> 1024, 1700 -> 2048
+    static int nearestPowerOfTwo(int x); // e.g. 1300 -> 1024, 12 -> 16 (not 8)
+
+    static int factorial(int x);
 };
 
 #endif
--- a/tests/Makefile	Fri Oct 04 16:43:44 2013 +0100
+++ b/tests/Makefile	Fri Oct 04 18:34:30 2013 +0100
@@ -5,10 +5,13 @@
 LDFLAGS := $(LDFLAGS) -lboost_unit_test_framework
 LIBS := ../libqm-dsp.a
 
-TESTS	:= test-window test-fft test-pvoc
+TESTS	:= test-mathutilities test-window test-fft test-pvoc
 
 all: $(TESTS)
-	for t in $(TESTS); do echo "Running $$t"; valgrind ./"$$t" || exit 1; done
+	for t in $(TESTS); do echo "Running $$t"; ./"$$t" || exit 1; done
+
+test-mathutilities: TestMathUtilities.o $(LIBS)
+	$(CXX) -o $@ $^ $(LDFLAGS)
 
 test-window: TestWindow.o $(LIBS)
 	$(CXX) -o $@ $^ $(LDFLAGS)
@@ -19,6 +22,7 @@
 test-pvoc: TestPhaseVocoder.o $(LIBS)
 	$(CXX) -o $@ $^ $(LDFLAGS)
 
+TestMathUtilities.o: $(LIBS)
 TestWindow.o: $(LIBS)
 TestFFT.o: $(LIBS)
 TestPhaseVocoder.o: $(LIBS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/TestMathUtilities.cpp	Fri Oct 04 18:34:30 2013 +0100
@@ -0,0 +1,138 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "maths/MathUtilities.h"
+
+#include <cmath>
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(TestMathUtilities)
+
+BOOST_AUTO_TEST_CASE(round)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::round(0.5), 1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(0.49), 0.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(0.99), 1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(0.01), 0.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(0.0), 0.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(100.0), 100.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(-0.2), 0.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(-0.5), -1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(-0.99), -1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(-1.0), -1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(-1.1), -1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::round(-1.5), -2.0);
+}
+
+BOOST_AUTO_TEST_CASE(mean)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::mean(0, 0), 0);
+    double d0[] = { 0, 4, 3, -1 };
+    BOOST_CHECK_EQUAL(MathUtilities::mean(d0, 4), 1.5);
+    double d1[] = { -2.6 };
+    BOOST_CHECK_EQUAL(MathUtilities::mean(d1, 1), -2.6);
+    std::vector<double> v;
+    v.push_back(0);
+    v.push_back(4);
+    v.push_back(3);
+    v.push_back(-1);
+    BOOST_CHECK_EQUAL(MathUtilities::mean(v, 0, 4), 1.5);
+    BOOST_CHECK_EQUAL(MathUtilities::mean(v, 1, 2), 3.5);
+    BOOST_CHECK_EQUAL(MathUtilities::mean(v, 3, 1), -1);
+    BOOST_CHECK_EQUAL(MathUtilities::mean(v, 3, 0), 0);
+}
+
+BOOST_AUTO_TEST_CASE(sum)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::sum(0, 0), 0);
+    double d0[] = { 0, 4, 3, -1 };
+    BOOST_CHECK_EQUAL(MathUtilities::sum(d0, 4), 6);
+    double d1[] = { -2.6 };
+    BOOST_CHECK_EQUAL(MathUtilities::sum(d1, 1), -2.6);
+}
+
+BOOST_AUTO_TEST_CASE(median)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::median(0, 0), 0);
+    double d0[] = { 0, 4, 3, -1 };
+    BOOST_CHECK_EQUAL(MathUtilities::median(d0, 4), 1.5);
+    double d1[] = { 0, 4, 3, -1, -1 };
+    BOOST_CHECK_EQUAL(MathUtilities::median(d1, 5), 0);
+    double d2[] = { 1.0, -2.0 };
+    BOOST_CHECK_EQUAL(MathUtilities::median(d2, 2), -0.5);
+    double d3[] = { -2.6 };
+    BOOST_CHECK_EQUAL(MathUtilities::median(d3, 1), -2.6);
+}
+
+BOOST_AUTO_TEST_CASE(princarg)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::princarg(M_PI), M_PI);
+    BOOST_CHECK_EQUAL(MathUtilities::princarg(-M_PI), M_PI);
+    BOOST_CHECK_EQUAL(MathUtilities::princarg(2 * M_PI), 0.0);
+    BOOST_CHECK_EQUAL(MathUtilities::princarg(5 * M_PI), M_PI);
+    BOOST_CHECK_EQUAL(MathUtilities::princarg(1.0), 1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::princarg(-1.0), -1.0);
+    BOOST_CHECK_EQUAL(MathUtilities::princarg(-10.0), -10.0 + 4 * M_PI);
+}
+
+BOOST_AUTO_TEST_CASE(isPowerOfTwo)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::isPowerOfTwo(0), false);
+    BOOST_CHECK_EQUAL(MathUtilities::isPowerOfTwo(1), true);
+    BOOST_CHECK_EQUAL(MathUtilities::isPowerOfTwo(-2), false);
+    BOOST_CHECK_EQUAL(MathUtilities::isPowerOfTwo(2), true);
+    BOOST_CHECK_EQUAL(MathUtilities::isPowerOfTwo(3), false);
+    BOOST_CHECK_EQUAL(MathUtilities::isPowerOfTwo(12), false);
+    BOOST_CHECK_EQUAL(MathUtilities::isPowerOfTwo(16), true);
+}
+
+BOOST_AUTO_TEST_CASE(nextPowerOfTwo)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::nextPowerOfTwo(0), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::nextPowerOfTwo(1), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::nextPowerOfTwo(-2), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::nextPowerOfTwo(2), 2);
+    BOOST_CHECK_EQUAL(MathUtilities::nextPowerOfTwo(3), 4);
+    BOOST_CHECK_EQUAL(MathUtilities::nextPowerOfTwo(12), 16);
+    BOOST_CHECK_EQUAL(MathUtilities::nextPowerOfTwo(16), 16);
+}
+
+BOOST_AUTO_TEST_CASE(previousPowerOfTwo)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::previousPowerOfTwo(0), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::previousPowerOfTwo(1), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::previousPowerOfTwo(-2), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::previousPowerOfTwo(2), 2);
+    BOOST_CHECK_EQUAL(MathUtilities::previousPowerOfTwo(3), 2);
+    BOOST_CHECK_EQUAL(MathUtilities::previousPowerOfTwo(12), 8);
+    BOOST_CHECK_EQUAL(MathUtilities::previousPowerOfTwo(16), 16);
+}
+
+BOOST_AUTO_TEST_CASE(nearestPowerOfTwo)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(0), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(1), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(-2), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(2), 2);
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(3), 4);
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(11), 8);
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(12), 16);
+    BOOST_CHECK_EQUAL(MathUtilities::nearestPowerOfTwo(16), 16);
+}
+
+BOOST_AUTO_TEST_CASE(factorial)
+{
+    BOOST_CHECK_EQUAL(MathUtilities::factorial(-10), 0);
+    BOOST_CHECK_EQUAL(MathUtilities::factorial(0), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::factorial(1), 1);
+    BOOST_CHECK_EQUAL(MathUtilities::factorial(2), 2);
+    BOOST_CHECK_EQUAL(MathUtilities::factorial(3), 6);
+    BOOST_CHECK_EQUAL(MathUtilities::factorial(4), 24);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+