// // Created by frederik on 1/11/25. // #include "MMS24-25.h" #include #include #include #include // Preprocessor Definitions in case they are not defined #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef EXIT_FAILURE #define EXIT_FAILURE 1 #endif #ifndef NULL #define NULL 0 #endif void info(char* fmt,...) { fprintf(stderr, "[\x1B[37;1m INFO \x1B[0m] \x1B[34m'"); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "'\x1B[0m\n"); } void success(char* fmt,...) { fprintf(stderr, "[\x1B[32;1m PASS \x1B[0m] \x1B[34m'"); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "'\x1B[0m\n"); } void error(const char* fmt, ...) { va_list args; fprintf(stderr, "<\x1B[31;1m ERROR \x1B[0m: \x1B[34m'"); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "'\x1B[0m >\n"); exit(-1); } void testInterpolation() { info("testInterpolation"); double dRangeMin = -1; double dRangeMax = 1; double dRangeStep = 0.33; for (double dX1=dRangeMin;dX1 (%lf, %lf); %lf", dX1, dY1, dX2, dY2, dX, dR1, dR2); } } } } } success("testInterpolation();"); } void testSineAndReadWriteDoubleArraySingle(int nValues, int dPLen, double dAmp) { info("Trying: %d, %d, %lf", nValues, dPLen, dAmp); char* fileName = NULL; fileName = malloc(100*sizeof(char)); if (fileName == NULL) { error("Could not allocate Memory for `fileName`!"); } sprintf(fileName, "sine_%d_%d_%lf.results.txt", nValues, dPLen, dAmp); double *pdSineValues = generateSineValues( nValues, dPLen, dAmp ); writeDoubleArray( pdSineValues, nValues, fileName ); success("Wrote %d values to %s", nValues, fileName); double *pdReadValues = NULL; int iReadValues = readDoubleArray( fileName, &pdReadValues ); if (iReadValues != nValues) { error("Failed to read the correct number of values: %d != %d", iReadValues, nValues); } success("Read %d values from %s", iReadValues, fileName); for (int i=0; iiNumValues != 10) { error("Failed to create signal with 10 values: %d != 10", signal->iNumValues); } for (int i=0; i<10; i++) { if (signal->pdValues[i] != M_PI) { error("Failed to create signal with default value 0 at index %d: %lf != 0", i, signal->pdValues[i]); } } success("testCreateSignalWithDefault"); deleteSignal(signal); } void testCreateSignalFromDoubleArray() { info("testCreateSignalFromDoubleArray"); double pdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; MMSignal *signal = createSignalFromDoubleArray(10, pdValues); if (signal->iNumValues != 10) { error("Failed to create signal with 10 values: %d != 10", signal->iNumValues); } for (int i=0; i<10; i++) { if (signal->pdValues[i] != pdValues[i]) { error("Failed to create signal with default value 0 at index %d: %lf != 0", i, signal->pdValues[i]); } } success("testCreateSignalFromDoubleArray"); deleteSignal(signal); } void testCreateSignalFromFile() { info("testCreateSignalFromFile"); double pdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; char* fileName = "testCreateSignalFromFile.results.txt"; writeDoubleArray(pdValues, 10, fileName); MMSignal *signal = createSignalFromFile(fileName); if (signal->iNumValues != 10) { error("Failed to create signal with 10 values: %d != 10", signal->iNumValues); } for (int i=0; i<10; i++) { if (signal->pdValues[i] != pdValues[i]) { error("Failed to create signal with default value 0 at index %d: %lf != 0", i, signal->pdValues[i]); } } success("testCreateSignalFromFile"); deleteSignal(signal); } void testWriteMMSignal() { info("testWriteMMSignal"); double pdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; char* fileName = "testWriteMMSignal.results.txt"; MMSignal *signal = createSignalFromDoubleArray(10, pdValues); writeMMSignal(fileName, signal); MMSignal *readSignal = createSignalFromFile(fileName); if (signal->iNumValues != readSignal->iNumValues) { error("Failed to read the correct number of values: %d != %d", signal->iNumValues, readSignal->iNumValues); } for (int i=0; i<10; i++) { if (signal->pdValues[i] != readSignal->pdValues[i]) { error("Failed to read the correct value at index %d: %lf != %lf", i, signal->pdValues[i], readSignal->pdValues[i]); } } deleteSignal(signal); deleteSignal(readSignal); success("testWriteMMSignal"); } void testCalculateArea() { info("testCalculateArea"); double pdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double dArea = calculateArea(pdValues, 10); if (round(dArea*1e6) != 55e6) { error("Failed to calculate the correct area: %lf != 55", dArea); } success("testCalculateArea"); } void testCalculateMean() { info("testCalculateMean"); double pdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double dMean = calculateMean(pdValues, 10); if (round(dMean*1e6) != 5.5e6) { error("Failed to calculate the correct mean: %lf != 5.5", dMean); } success("testCalculateMean"); } void testCalculateStdDev() { info("testCalculateStdDev"); double pdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double dStdDev = calculateStddev(pdValues, 10); if (round(dStdDev*1e6) != 2.872281e6) { error("Failed to calculate the correct standard deviation: %lf != 2.872281", dStdDev); } success("testCalculateStdDev"); } void testCalculateMedian() { info("testCalculateMedian"); double pdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double dMedian = calculateMedian(pdValues, 10); if (dMedian != 5.5) { error("Failed to calculate the correct median: %lf != 5.5", dMedian); } double pdValues2[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; dMedian = calculateMedian(pdValues2, 9); if (dMedian != 5) { error("Failed to calculate the correct median: %lf != 5", dMedian); } success("testCalculateMedian"); } void testCalculateEntropy() { info("testCalculateEntropy"); double pdValues[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Histogram *histogram = initHistogram(pdValues, 11, 11); double dEntropy = calculateEntropy(histogram); if (round(dEntropy*1e6) != 3.459432e6) { // Should be log2(1/11) = 3.459432 error("Failed to calculate the correct entropy: %lf != 3.459432", dEntropy); } success("testCalculateEntropy"); } void testConvoluteSignals() { info("testConvoluteSignals"); double pdValues1[] = {1, 2, 3, 4, 5}; double pdValues2[] = {1, 2, 3, 4, 5}; MMSignal *signal1 = createSignalFromDoubleArray(5, pdValues1); MMSignal *signal2 = createSignalFromDoubleArray(5, pdValues2); MMSignal *signal3 = convoluteSignals(signal1, signal2); if (signal3->iNumValues != 9) { error("Failed to calculate the correct number of values: %d != 9", signal3->iNumValues); } double pdExpectedValues[] = {1, 4, 10, 20, 35, 44, 46, 40, 25}; for (int i=0; i<9; i++) { if (signal3->pdValues[i] != pdExpectedValues[i]) { error("Failed to calculate the correct value at index %d: %lf != %lf", i, signal3->pdValues[i], pdExpectedValues[i]); } } success("testConvoluteSignals"); } void testGetPascalLine() { info("testGetPascalLine"); // For 5 MMSignal *signal = getPascalLine(5); if (signal->iNumValues != 5) { error("Failed to calculate the correct number of values: %d != 5", signal->iNumValues); } double pdExpectedValues[] = {1, 4, 6, 4, 1}; for (int i=0; i<5; i++) { if (signal->pdValues[i] != pdExpectedValues[i]) { error("Failed to calculate the correct value at index %d: %lf != %lf", i, signal->pdValues[i], pdExpectedValues[i]); } } // For 1 signal = getPascalLine(1); if (signal->iNumValues != 1) { error("Failed to calculate the correct number of values: %d != 1", signal->iNumValues); } if (signal->pdValues[0] != 1) { error("Failed to calculate the correct value at index 0: %lf != 1", signal->pdValues[0]); } // For 11 signal = getPascalLine(11); if (signal->iNumValues != 11) { error("Failed to calculate the correct number of values: %d != 11", signal->iNumValues); } double pdExpectedValues11[] = {1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1}; for (int i=0; i<11; i++) { if (signal->pdValues[i] != pdExpectedValues11[i]) { error("Failed to calculate the correct value at index %d: %lf != %lf", i, signal->pdValues[i], pdExpectedValues11[i]); } } success("testGetPascalLine"); } void testApplyPascalConvForSeriesGT40() { info("testApplyPascalForSeriesGT40"); MMSignal *signal = getPascalLine(40); MMSignal *signal2 = convoluteSignals(signal, signal); writeMMSignal("testApplyPascalForSeriesGT40.results.txt", signal2); success("testApplyPascalForSeriesGT40"); } void testComputeDFT() { info("testComputeDFT"); double pdRealIn[] = {1, 2, 3, 4, 5}; double pdImgIn[] = {1, 2, 3, 4, 5}; double pdRealOut[5]; double pdImgOut[5]; computeDFT(5, pdRealIn, pdImgIn, pdRealOut, pdImgOut, 1); for (int i=0; i<5; i++) { if (pdImgOut[i] < 0) { printf("%lf - %lfi\n", pdRealOut[i], -pdImgOut[i]); } else { printf("%lf + %lfi\n", pdRealOut[i], pdImgOut[i]); } } success("testComputeDFT"); } void testConvertCart2Polar() { // convertCart2Polar(double *pdRealIn, double *pdImgIn, double *pdRadiusOut, double *pdAngleOut, int iLen) info("testConvertCart2Polar"); double pdRealIn[] = {1, 0, 1, -1, 0}; double pdImgIn[] = {0, 1, 1, 0, -1}; double pdRadiusOut[5]; double pdAngleOut[5]; convertCart2Polar(pdRealIn, pdImgIn, pdRadiusOut, pdAngleOut, 5); for (int i=0; i<5; i++) { printf("%lf + %lfi -> r=%lf / θ=%lfπ\n", pdRealIn[i], pdImgIn[i], pdRadiusOut[i], pdAngleOut[i] / M_PI); } success("testConvertCart2Polar"); } void testConvertPolar2Cart() { // convertPolar2Cart(double *pdRadiusIn, double *pdAngleIn, double *pdRealOut, double *pdImgOut, int iLen) info("testConvertPolar2Cart"); double pdRadiusIn[] = {1, 1, 1, 1, 1}; double pdAngleIn[] = {0, M_PI/2, M_PI, 3*M_PI/2, 2*M_PI}; double pdRealOut[5]; double pdImgOut[5]; convertPolar2Cart(pdRadiusIn, pdAngleIn, pdRealOut, pdImgOut, 5); for (int i=0; i<5; i++) { printf("r=%lf / θ=%lfπ -> %lf + %lfi\n", pdRadiusIn[i], pdAngleIn[i] / M_PI, pdRealOut[i], pdImgOut[i]); } success("testConvertPolar2Cart"); } void testCalcExtrema() { info("testCalcExtrema"); double pdValues[] = {1, 2, 1, 0, -1, -2, -1, 0, 1, 5}; Extrema *extrema = initExtrema(pdValues, 10); info("*extrema: %p", extrema); info("*extrema->pexLocalMax: %p", extrema->pexLocalMax); info("*extrema->pexLocalMin: %p", extrema->pexLocalMin); info("*extrema->pexLocalMax->piLocalExtremaPos: %p", extrema->pexLocalMax->piLocalExtremaPos); info("*extrema->pexLocalMin->piLocalExtremaPos: %p", extrema->pexLocalMin->piLocalExtremaPos); if (extrema->pexLocalMax->iNumLocalExtrema != 1) { error("Failed to calculate the correct number of local maxima: %d != 1", extrema->pexLocalMax->iNumLocalExtrema); } if (extrema->pexLocalMin->iNumLocalExtrema != 1) { error("Failed to calculate the correct number of local minima: %d != 1", extrema->pexLocalMin->iNumLocalExtrema); } if (extrema->iGlobalMaxPos != 9) { error("Failed to calculate the correct global maximum position: %d != 9", extrema->iGlobalMaxPos); } if (extrema->iGlobalMinPos != 5) { error("Failed to calculate the correct global minimum position: %d != 4", extrema->iGlobalMinPos); } if (extrema->pexLocalMax->piLocalExtremaPos[0] != 1) { error("Failed to calculate the correct local maximum position: %d != 9", extrema->pexLocalMax->piLocalExtremaPos[0]); } if (extrema->pexLocalMin->piLocalExtremaPos[0] != 5) { error("Failed to calculate the correct local minimum position: %d != 4", extrema->pexLocalMin->piLocalExtremaPos[0]); } success("testCalcExtrema"); } void testInitMMSignalFeatures() { /* * Test for Signals of multiple lengths including edge cases. */ info("testInitMMSignalFeatures"); // Test for 0 values info("Test for 0 values"); MMSignal *signal = createSignalWithDefault(0, 0); initMMSignalFeatures(signal); if (signal->dArea != 0) { error("Failed to calculate the correct area: %lf != 0", signal->dArea); } if (signal->dMean != 0) { error("Failed to calculate the correct mean: %lf != 0", signal->dMean); } if (signal->dStdDev != 0) { error("Failed to calculate the correct standard deviation: %lf != 0", signal->dStdDev); } if (signal->dMedian != 0) { error("Failed to calculate the correct median: %lf != 0", signal->dMedian); } if (signal->pexExtrema->iGlobalMinPos != 0) { error("Failed to calculate the correct global minimum position: %d != 0", signal->pexExtrema->iGlobalMinPos); } if (signal->pexExtrema->iGlobalMaxPos != 0) { error("Failed to calculate the correct global maximum position: %d != 0", signal->pexExtrema->iGlobalMaxPos); } if (signal->pexExtrema->pexLocalMax->iNumLocalExtrema != 0) { error("Failed to calculate the correct number of local maxima: %d != 0", signal->pexExtrema->pexLocalMax->iNumLocalExtrema); } deleteSignal(signal); // List of 1 to 10 double pdValuesL[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; signal = createSignalFromDoubleArray(10, pdValuesL); initMMSignalFeatures(signal); if (signal->dArea != 55) { error("Failed to calculate the correct area: %lf != 55", signal->dArea); } if (signal->dMean != 5.5) { error("Failed to calculate the correct mean: %lf != 5.5", signal->dMean); } if (round(signal->dStdDev*1e6) != 2.872281e6) { error("Failed to calculate the correct standard deviation: %lf != 2.872281", signal->dStdDev); } if (signal->dMedian != 5.5) { error("Failed to calculate the correct median: %lf != 5.5", signal->dMedian); } if (signal->pexExtrema->iGlobalMinPos != 0) { error("Failed to calculate the correct global minimum position: %d != 0", signal->pexExtrema->iGlobalMinPos); } if (signal->pexExtrema->iGlobalMaxPos != 9) { error("Failed to calculate the correct global maximum position: %d != 9", signal->pexExtrema->iGlobalMaxPos); } if (signal->pexExtrema->pexLocalMax->iNumLocalExtrema != 0) { error("Failed to calculate the correct number of local maxima: %d != 1", signal->pexExtrema->pexLocalMax->iNumLocalExtrema); } deleteSignal(signal); // Test for 1 value info("Test for 1 value"); signal = createSignalWithDefault(1, 1); initMMSignalFeatures(signal); if (signal->dArea != 1) { error("Failed to calculate the correct area: %lf != 1", signal->dArea); } if (signal->dMean != 1) { error("Failed to calculate the correct mean: %lf != 1", signal->dMean); } if (signal->dStdDev != 0) { error("Failed to calculate the correct standard deviation: %lf != 0", signal->dStdDev); } if (signal->dMedian != 1) { error("Failed to calculate the correct median: %lf != 1", signal->dMedian); } if (signal->pexExtrema->iGlobalMinPos != 0) { error("Failed to calculate the correct global minimum position: %d != 0", signal->pexExtrema->iGlobalMinPos); } if (signal->pexExtrema->iGlobalMaxPos != 0) { error("Failed to calculate the correct global maximum position: %d != 0", signal->pexExtrema->iGlobalMaxPos); } if (signal->pexExtrema->pexLocalMax->iNumLocalExtrema != 0) { error("Failed to calculate the correct number of local maxima: %d != 1", signal->pexExtrema->pexLocalMax->iNumLocalExtrema); } deleteSignal(signal); // Test for 2 values info("testInitMMSignalFeatures: 2 values"); double pdValuesT[] = {1, 1}; signal = createSignalFromDoubleArray(2, pdValuesT); initMMSignalFeatures(signal); if (signal->dArea != 2) { error("Failed to calculate the correct area: %lf != 2", signal->dArea); } if (signal->dMean != 1) { error("Failed to calculate the correct mean: %lf != 1", signal->dMean); } if (signal->dStdDev != 0) { error("Failed to calculate the correct standard deviation: %lf != 0", signal->dStdDev); } if (signal->dMedian != 1) { error("Failed to calculate the correct median: %lf != 1", signal->dMedian); } if (signal->pexExtrema->iGlobalMinPos != 0) { error("Failed to calculate the correct global minimum position: %d != 0", signal->pexExtrema->iGlobalMinPos); } if (signal->pexExtrema->iGlobalMaxPos != 0) { error("Failed to calculate the correct global maximum position: %d != 0", signal->pexExtrema->iGlobalMaxPos); } if (signal->pexExtrema->pexLocalMax->iNumLocalExtrema != 0) { error("Failed to calculate the correct number of local maxima: %d != 0", signal->pexExtrema->pexLocalMax->iNumLocalExtrema); } deleteSignal(signal); // Test for 3 values with local maximum info("testInitMMSignalFeatures: 3 values with local maximum"); double pdValues[] = {1, 2, 1}; signal = createSignalFromDoubleArray(3, pdValues); initMMSignalFeatures(signal); if (signal->dArea != 4) { error("Failed to calculate the correct area: %lf != 4", signal->dArea); } if (signal->dMean != 4.0/3.0) { error("Failed to calculate the correct mean: %lf != 4/3", signal->dMean); } if (round(signal->dStdDev*1e6) != 0.471405e6) { error("Failed to calculate the correct standard deviation: %lf != 0.471405", signal->dStdDev); } if (signal->dMedian != 1) { error("Failed to calculate the correct median: %lf != 1", signal->dMedian); } if (signal->pexExtrema->iGlobalMinPos != 0) { error("Failed to calculate the correct global minimum position: %d != 2", signal->pexExtrema->iGlobalMinPos); } if (signal->pexExtrema->iGlobalMaxPos != 1) { error("Failed to calculate the correct global maximum position: %d != 1", signal->pexExtrema->iGlobalMaxPos); } if (signal->pexExtrema->pexLocalMax->iNumLocalExtrema != 1) { error("Failed to calculate the correct number of local maxima: %d != 1", signal->pexExtrema->pexLocalMax->iNumLocalExtrema); } deleteSignal(signal); // Test for 3 values with local minimum info("testInitMMSignalFeatures: 3 values with local minimum"); double pdValues2[] = {1, 0, 1}; signal = createSignalFromDoubleArray(3, pdValues2); initMMSignalFeatures(signal); if (signal->dArea != 2) { error("Failed to calculate the correct area: %lf != 2", signal->dArea); } if (signal->dMean != 2.0/3.0) { error("Failed to calculate the correct mean: %lf != 2/3", signal->dMean); } if (round(signal->dStdDev*1e6) != 0.471405e6) { error("Failed to calculate the correct standard deviation: %lf != 0.471405", signal->dStdDev); } if (signal->dMedian != 1) { error("Failed to calculate the correct median: %lf != 1", signal->dMedian); } if (signal->pexExtrema->iGlobalMinPos != 1) { error("Failed to calculate the correct global minimum position: %d != 1", signal->pexExtrema->iGlobalMinPos); } if (signal->pexExtrema->iGlobalMaxPos != 0) { error("Failed to calculate the correct global maximum position: %d != 2", signal->pexExtrema->iGlobalMaxPos); } if (signal->pexExtrema->pexLocalMin->iNumLocalExtrema != 1) { error("Failed to calculate the correct number of local minima: %d != 1", signal->pexExtrema->pexLocalMin->iNumLocalExtrema); } deleteSignal(signal); success("testInitMMSignalFeatures"); } int eqToNDecimals(double dValueA, double dValueB, int iDecimals) { return round(dValueA * pow(10, iDecimals)) == round(dValueB * pow(10, iDecimals)); } void testRescaleDoubleArraySingle( double *pdValues, double *pdExpectedValues, int iNumValues, double dMin, double dFactor ) { double pdOut[10]; rescaleDoubleArray(pdValues, iNumValues, dMin, dFactor, pdOut); for (int i=0; i%d: %lf != %lf", i-1, i, pdOut[i] - pdOut[i-1], (pdValues[i] - pdValues[i-1]) * dFactor); } } int minIndex = 0; for (int i=1; i