diff --git a/MMS24-25.c b/MMS24-25.c index eda12d1..09da683 100644 --- a/MMS24-25.c +++ b/MMS24-25.c @@ -11,17 +11,16 @@ * https://git.beimgraben.net/MMS-2024-25/MMS-Loesung-2024-25.git */ -#include "MMS24-25.h" -#include "math.h" -#include -#include -#include "MMS24-25.h" #include #include +#include + +#include "MMS24-25.h" +#include "math.h" // #region Error Handling and Debugging void _error(char *fmt, ...) { - fprintf(stderr, "<\x1B[31;1m ERROR \x1B[0m: \x1B[34m'"); + fprintf(stderr, "< \x1B[31;4;1mERROR\x1B[0m : \x1B[34m'"); va_list args; va_start(args, fmt); @@ -36,7 +35,7 @@ void _error(char *fmt, ...) { } void _debug(char *fmt, ...) { - fprintf(stderr, "[\x1B[31;1m DEBUG \x1B[0m] \x1B[34m'"); + fprintf(stderr, "[ \x1B[37;4;1mDEBUG\x1B[0m ] \x1B[34m'"); va_list args; va_start(args, fmt); @@ -47,7 +46,7 @@ void _debug(char *fmt, ...) { } void _warn(char *fmt, ...) { - fprintf(stderr, "[\x1B[33;1m WARN \x1B[0m] \x1B[34m'"); + fprintf(stderr, "[ \x1B[33;4;1mWARN\x1B[0m ] \x1B[34m'"); va_list args; va_start(args, fmt); @@ -69,9 +68,9 @@ double interpolateLine( if (dX2 == dX1) _error("interpolateLine: dX1 and dX2 must differ!"); - double y = dY1 *( + double y = dY1 * ( (dX2 - dXq) / (dX2 - dX1) - ) + dY2 *( + ) + dY2 * ( (dXq - dX1) / (dX2 - dX1) ); @@ -99,8 +98,19 @@ void rescaleDoubleArray( * which naturally results from: * $ pdOut_i = pdIn_i *dFactor + dMin $ */ + // First find the old minimum + double dOldMin = pdIn[0]; + for (int i = 1; i < iLen; i++) { + if (pdIn[i] < dOldMin) + dOldMin = pdIn[i]; + } + + // Calculate the offset + double dOffset = dMin - dOldMin; + + // Rescale the array for (int i = 0; i < iLen; i++) { - pdOut[i] = pdIn[i] *dFactor + dMin; + pdOut[i] = pdIn[i] * dFactor + dOffset; } } @@ -129,13 +139,13 @@ double *generateSineValues( // Initialize the output array double *sineArray = NULL; - sineArray = (double*) malloc(sizeof(double) *iNumValues); + sineArray = (double*) malloc(sizeof(double) * iNumValues); if (sineArray == NULL) _error("*generateSineValues: Failed to allocate memory for Sine Array\n"); for (int i=0; iiNumLocalExtrema = pexIn->iNumLocalExtrema; + localExtrema->piLocalExtremaPos = (int*) malloc(sizeof(int) *pexIn->iNumLocalExtrema); + if (localExtrema->piLocalExtremaPos == NULL) + _error("copyLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos\n"); + + for (int i = 0; i < pexIn->iNumLocalExtrema; i++) { + localExtrema->piLocalExtremaPos[i] = pexIn->piLocalExtremaPos[i]; + } + + return localExtrema; +} + +Extrema* copyExtrema(Extrema *pexIn) { + if (pexIn == NULL) + return NULL; + + Extrema *extrema = (Extrema*) malloc(sizeof(Extrema)); + if (extrema == NULL) + _error("copyExtrema: Failed to allocate memory for extrema\n"); + + extrema->iGlobalMinPos = pexIn->iGlobalMinPos; + extrema->iGlobalMaxPos = pexIn->iGlobalMaxPos; + + extrema->pexLocalMax = copyLocalExtrema(pexIn->pexLocalMax); + extrema->pexLocalMin = copyLocalExtrema(pexIn->pexLocalMin); + + return extrema; +} + MMSignal *createSignalFromSignal(MMSignal *pmmsIn) { MMSignal *signal = createSignal(); - // Initialize and copy the values (deep copy) - signal->pdValues = (double*) malloc(sizeof(double) *pmmsIn->iNumValues); - if (signal->pdValues == NULL) - _error("createSignalFromSignal: Failed to allocate memory for signal->pdValues\n"); + if (pmmsIn->pdValues != NULL) { + // Initialize and copy the values (deep copy) + signal->pdValues = (double*) malloc(sizeof(double) *pmmsIn->iNumValues); + if (signal->pdValues == NULL) + _error("createSignalFromSignal: Failed to allocate memory for signal->pdValues\n"); - for (int i = 0; i < pmmsIn->iNumValues; i++) { - signal->pdValues[i] = pmmsIn->pdValues[i]; + for (int i = 0; i < pmmsIn->iNumValues; i++) { + signal->pdValues[i] = pmmsIn->pdValues[i]; + } + } else { + _warn("createSignalFromSignal: pmmsIn->pdValues is NULL\n"); + signal->pdValues = NULL; } // Copy shallow items @@ -224,38 +282,13 @@ MMSignal *createSignalFromSignal(MMSignal *pmmsIn) { signal->dMedian = pmmsIn->dMedian; // Deep copy of Extrema - signal->pexExtrema = (Extrema*) malloc(sizeof(Extrema)); - if (signal->pexExtrema == NULL) - _error("createSignalFromSignal: Failed to allocate memory for signal->pexExtrema\n"); - - signal->pexExtrema->iGlobalMinPos = pmmsIn->pexExtrema->iGlobalMinPos; - signal->pexExtrema->iGlobalMaxPos = pmmsIn->pexExtrema->iGlobalMaxPos; - - signal->pexExtrema->pexLocalMax = (LocalExtrema*) malloc(sizeof(LocalExtrema)); - if (signal->pexExtrema->pexLocalMax == NULL) - _error("createSignalFromSignal: Failed to allocate memory for signal->pexExtrema->pexLocalMax\n"); - - signal->pexExtrema->pexLocalMax->iNumLocalExtrema = pmmsIn->pexExtrema->pexLocalMax->iNumLocalExtrema; - signal->pexExtrema->pexLocalMax->piLocalExtremaPos = (int*) malloc(sizeof(int) *pmmsIn->pexExtrema->pexLocalMax->iNumLocalExtrema); - if (signal->pexExtrema->pexLocalMax->piLocalExtremaPos == NULL) - _error("createSignalFromSignal: Failed to allocate memory for signal->pexExtrema->pexLocalMax->piLocalExtremaPos\n"); - - for (int i = 0; i < pmmsIn->pexExtrema->pexLocalMax->iNumLocalExtrema; i++) { - signal->pexExtrema->pexLocalMax->piLocalExtremaPos[i] = pmmsIn->pexExtrema->pexLocalMax->piLocalExtremaPos[i]; + if (pmmsIn->pexExtrema == NULL) { + _warn("createSignalFromSignal: pmmsIn->pexExtrema is NULL\n"); + signal->pexExtrema = NULL; + return signal; } - signal->pexExtrema->pexLocalMin = (LocalExtrema*) malloc(sizeof(LocalExtrema)); - if (signal->pexExtrema->pexLocalMin == NULL) - _error("createSignalFromSignal: Failed to allocate memory for signal->pexExtrema->pexLocalMin\n"); - - signal->pexExtrema->pexLocalMin->iNumLocalExtrema = pmmsIn->pexExtrema->pexLocalMin->iNumLocalExtrema; - signal->pexExtrema->pexLocalMin->piLocalExtremaPos = (int*) malloc(sizeof(int) *pmmsIn->pexExtrema->pexLocalMin->iNumLocalExtrema); - if (signal->pexExtrema->pexLocalMin->piLocalExtremaPos == NULL) - _error("createSignalFromSignal: Failed to allocate memory for signal->pexExtrema->pexLocalMin->piLocalExtremaPos\n"); - - for (int i = 0; i < pmmsIn->pexExtrema->pexLocalMin->iNumLocalExtrema; i++) { - signal->pexExtrema->pexLocalMin->piLocalExtremaPos[i] = pmmsIn->pexExtrema->pexLocalMin->piLocalExtremaPos[i]; - } + signal->pexExtrema = copyExtrema(pmmsIn->pexExtrema); return signal; } @@ -312,6 +345,9 @@ MMSignal *createSignalFromFile(char *pcInName) { } void writeMMSignal(char *pcInName, MMSignal *pmmsIn) { + if (pmmsIn->pdValues == NULL) + _error("writeMMSignal: pmmsIn->pdValues is NULL\n"); + writeDoubleArray( pmmsIn->pdValues, pmmsIn->iNumValues, @@ -319,13 +355,32 @@ void writeMMSignal(char *pcInName, MMSignal *pmmsIn) { ); } +void deleteLocalExtrema(LocalExtrema *pexIn) { + if (pexIn == NULL) + return; + + free(pexIn->piLocalExtremaPos); + free(pexIn); +} + +void deleteExtrema(Extrema *pexIn) { + if (pexIn == NULL) + return; + + deleteLocalExtrema(pexIn->pexLocalMax); + deleteLocalExtrema(pexIn->pexLocalMin); + free(pexIn); +} + void deleteSignal(MMSignal *pmmsIn) { - free(pmmsIn->pdValues); - free(pmmsIn->pexExtrema->pexLocalMax->piLocalExtremaPos); - free(pmmsIn->pexExtrema->pexLocalMax); - free(pmmsIn->pexExtrema->pexLocalMin->piLocalExtremaPos); - free(pmmsIn->pexExtrema->pexLocalMin); - free(pmmsIn->pexExtrema); + if (pmmsIn == NULL) + return; + + if (pmmsIn->pdValues != NULL) + free(pmmsIn->pdValues); + + deleteExtrema(pmmsIn->pexExtrema); + free(pmmsIn); } // #endregion @@ -335,24 +390,31 @@ double calculateArea(double *pdIn, int iLen) { double area = 0; // #FIXME: Which one should we use according to the task? - for (int i = 0; i < iLen-1; i++) { - area += (pdIn[i] + pdIn[i+1]) / 2; + for (int i = 0; i < iLen; i++) { + /* Alternative would be: + * area += (pdIn[i] + pdIn[i+1]) / 2; + * with i < iLen-1 + * Which one to use is up to the implemention. + */ + area += pdIn[i]; } return area; } double calculateMean(double *pdIn, int iLen) { - double mean = 0; - - for (int i = 0; i < iLen; i++) { - mean += pdIn[i]; - } - - return mean / iLen; + /* Here the above defined function `calculateArea` is used to calculate the mean, since: + * \[ mean(pdIn) = \frac{1}{ |pdIn| } \sum_{i=1}^{ |pdIn| } x_i + * = \frac{area(x)}{ |pdIn| } \] + */ + return calculateArea(pdIn, iLen) / iLen; } double calculateStddev(double *pdIn, int iLen) { + /* The standard deviation is calculated as follows: + * \[ stddev(pdIn) = \sqrt{ \frac{1}{ |pdIn| } \sum_{i=1}^{ |pdIn| } (x_i - mean(pdIn))^2 } \] + */ + double mean = calculateMean(pdIn, iLen); double stdDev = 0; @@ -363,26 +425,41 @@ double calculateStddev(double *pdIn, int iLen) { return sqrt(stdDev / iLen); } +double calculateMedian(double *pdIn, int iLen) { + /* The median is calculated as follows: + * \[ median(pdIn) = \begin{cases} + * pdIn_{\frac{|pdIn|}{2}} & \text{if } |pdIn| \text{ is even} \\ + * \frac{pdIn_{\frac{|pdIn|}{2}} + pdIn_{\frac{|pdIn|}{2}+1}}{2} & \text{if } |pdIn| \text{ is odd} + * \end{cases} \] + */ + + if (iLen % 2 == 0) { + return pdIn[iLen/2]; + } else { + return (pdIn[iLen/2] + pdIn[iLen/2+1]) / 2; + } +} + +LocalExtrema *initLocalExtrema(int iLen) { + LocalExtrema *localExtrema = (LocalExtrema*) malloc(sizeof(LocalExtrema)); + if (localExtrema == NULL) + _error("initLocalExtrema: Failed to allocate memory for localExtrema\n"); + + localExtrema->iNumLocalExtrema = 0; + localExtrema->piLocalExtremaPos = (int*) malloc(sizeof(int) *iLen); + if (localExtrema->piLocalExtremaPos == NULL) + _error("initLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos\n"); + + return localExtrema; +} + Extrema *initExtrema(double *pdIn, int iLen) { Extrema *extrema = (Extrema*) malloc(sizeof(Extrema)); if (extrema == NULL) _error("initExtrema: Failed to allocate memory for extrema\n"); - extrema->pexLocalMax = (LocalExtrema*) malloc(sizeof(LocalExtrema)); - if (extrema->pexLocalMax == NULL) - _error("initExtrema: Failed to allocate memory for extrema->pexLocalMax\n"); - - extrema->pexLocalMin = (LocalExtrema*) malloc(sizeof(LocalExtrema)); - if (extrema->pexLocalMin == NULL) - _error("initExtrema: Failed to allocate memory for extrema->pexLocalMin\n"); - - extrema->pexLocalMax->piLocalExtremaPos = (int*) malloc(sizeof(int) *iLen); - if (extrema->pexLocalMax->piLocalExtremaPos == NULL) - _error("initExtrema: Failed to allocate memory for extrema->pexLocalMax->piLocalExtremaPos\n"); - - extrema->pexLocalMin->piLocalExtremaPos = (int*) malloc(sizeof(int) *iLen); - if (extrema->pexLocalMin->piLocalExtremaPos == NULL) - _error("initExtrema: Failed to allocate memory for extrema->pexLocalMin->piLocalExtremaPos\n"); + extrema->pexLocalMax = initLocalExtrema(iLen); + extrema->pexLocalMin = initLocalExtrema(iLen); extrema->iGlobalMaxPos = 0; extrema->iGlobalMinPos = 0; @@ -390,14 +467,6 @@ Extrema *initExtrema(double *pdIn, int iLen) { return extrema; } -void deleteExtrema(Extrema *pexIn) { - free(pexIn->pexLocalMax->piLocalExtremaPos); - free(pexIn->pexLocalMax); - free(pexIn->pexLocalMin->piLocalExtremaPos); - free(pexIn->pexLocalMin); - free(pexIn); -} - void initMMSignalFeatures(MMSignal *pmmsIn) { pmmsIn->dArea = 0; pmmsIn->dMean = 0; @@ -409,7 +478,13 @@ void initMMSignalFeatures(MMSignal *pmmsIn) { Histogram *initHistogram(double *pdIn, int iLen, int iNumBins) { Histogram *histogram = (Histogram*) malloc(sizeof(Histogram)); if (histogram == NULL) - _error("initHistogram: Failed to allocate memory for histogram\n"); + _error("initHistogram: Failed to allocate memory for histogram"); + + if (iNumBins == 0) + _error("initHistogram: iNumBins must be non-0"); + + if (iLen == 0) + _warn("initHistogram: iLen is 0\n"); histogram->dIntervalMin = pdIn[0]; histogram->dIntervalMax = pdIn[0]; @@ -420,26 +495,61 @@ Histogram *initHistogram(double *pdIn, int iLen, int iNumBins) { histogram->dIntervalMax = pdIn[i]; } + #ifdef _DEBUG_ + _debug("initHistogram: histogram->dIntervalMin = %lf", histogram->dIntervalMin); + _debug("initHistogram: histogram->dIntervalMax = %lf", histogram->dIntervalMax); + #endif + histogram->dBinWidth = (histogram->dIntervalMax - histogram->dIntervalMin) / iNumBins; histogram->iNumBins = iNumBins; + #ifdef _DEBUG_ + _debug("initHistogram: histogram->dBinWidth = %lf", histogram->dBinWidth); + #endif + + if (histogram->dBinWidth == 0) + histogram->dBinWidth = 1; + histogram->pdBinValues = (double*) malloc(sizeof(double) *iNumBins); if (histogram->pdBinValues == NULL) - _error("initHistogram: Failed to allocate memory for histogram->pdBinValues\n"); + _error("initHistogram: Failed to allocate memory for histogram->pdBinValues"); for (int i = 0; i < iNumBins; i++) { histogram->pdBinValues[i] = 0; } + for (int i = 0; i < iLen; i++) { + int bin = floor((pdIn[i] - histogram->dIntervalMin) / histogram->dBinWidth); + if (bin == iNumBins) + bin--; + + histogram->pdBinValues[bin]++; + } + + for (int i = 0; i < iNumBins; i++) { + histogram->pdBinValues[i] /= iLen; + } + + #ifdef _DEBUG_ + // DEBUG: print the parameters and result + _debug("initHistogram(%lf, %lf, %lf, %d)", + histogram->dIntervalMin,histogram->dIntervalMax,histogram->dBinWidth,histogram->iNumBins); + for (int i = 0; i < histogram->iNumBins; i++) { + _debug("initHistogram: histogram->pdBinValues[%d] = %lf", i, histogram->pdBinValues[i]); + } + #endif + return histogram; } void deleteHistogram(Histogram *phIn) { + if (phIn == NULL) + return; + free(phIn->pdBinValues); free(phIn); } -// #FIXME: Check if this checks out double calculateEntropy(Histogram *phisIn) { double entropy = 0; @@ -457,40 +567,49 @@ double calculateEntropy(Histogram *phisIn) { // #region Aufgabe 3 MMSignal *convoluteSignals(MMSignal *pmmsInA, MMSignal *pmmsInB) { - MMSignal *signal = createSignal(); + // Calculate output length + int iOutLen = pmmsInA->iNumValues + pmmsInB->iNumValues - 1; - if (pmmsInA->iNumValues != pmmsInB->iNumValues) - _error("convoluteSignals: Signals must have the same length\n"); + double *pdOut = (double*) malloc(sizeof(double) *iOutLen); + if (pdOut == NULL) + _error("convoluteSignals: Failed to allocate memory for pdOut\n"); - signal->pdValues = (double*) malloc(sizeof(double) *pmmsInA->iNumValues); - if (signal->pdValues == NULL) - _error("convoluteSignals: Failed to allocate memory for signal->pdValues\n"); - - for (int i = 0; i < pmmsInA->iNumValues; i++) { - signal->pdValues[i] = pmmsInA->pdValues[i] * pmmsInB->pdValues[i]; + for (int i = 0; i < iOutLen; i++) { + pdOut[i] = 0; } - signal->iNumValues = pmmsInA->iNumValues; + for (int i = 0; i < pmmsInA->iNumValues; i++) { + for (int j = 0; j < pmmsInB->iNumValues; j++) { + pdOut[i+j] += pmmsInA->pdValues[i] * pmmsInB->pdValues[j]; + } + } + + MMSignal *signal = createSignalFromDoubleArray(iOutLen, pdOut); + initMMSignalFeatures(signal); return signal; } // #FIXME: Check if this checks out MMSignal *getPascalLine(int iLinenum) { - MMSignal *signal = createSignal(); + if (iLinenum <= 0) + _error("getPascalLine: iLinenum must be greater than 0\n"); - signal->pdValues = (double*) malloc(sizeof(double) *iLinenum); - if (signal->pdValues == NULL) - _error("getPascalLine: Failed to allocate memory for signal->pdValues\n"); + // Calculate a kernel signal using pascales triangle without using the factorial + double *pdValues = NULL; + pdValues = (double*) malloc(sizeof(double) * iLinenum); + if (pdValues == NULL) + _error("getPascalLine: Failed to allocate memory for pdValues\n"); - signal->pdValues[0] = 1; - signal->pdValues[iLinenum-1] = 1; - - for (int i = 1; i < iLinenum-1; i++) { - signal->pdValues[i] = signal->pdValues[i-1] *(iLinenum-i) / i; + for (int i = 0; i < iLinenum; i++) { + pdValues[i] = 1; + for (int j = i-1; j > 0; j--) { + pdValues[j] = pdValues[j] + pdValues[j-1]; + } } - signal->iNumValues = iLinenum; + MMSignal *signal = createSignalFromDoubleArray(iLinenum, pdValues); + initMMSignalFeatures(signal); return signal; } @@ -498,10 +617,12 @@ MMSignal *getPascalLine(int iLinenum) { // #endregion // #region Aufgabe 4 -void computeDFT(int iLen, - double *pdRealIn, double *pdImgIn, - double *pdRealOut, double *pdImgOut, - int iDirection /*-1,1*/) { +void computeDFT( + int iLen, + double *pdRealIn, double *pdImgIn, + double *pdRealOut, double *pdImgOut, + int iDirection + ) { // Check the value of iDirection if (iDirection != -1 && iDirection != 1) _error("computeDFT: iDirection must be either -1 or 1\n"); @@ -509,9 +630,21 @@ void computeDFT(int iLen, for (int k = 0; k < iLen; k++) { pdRealOut[k] = 0; pdImgOut[k] = 0; + + // Compute the sum for the k-th output element for (int n = 0; n < iLen; n++) { - pdRealOut[k] += pdRealIn[n] * cos(2*M_PI *k *n / iLen) + iDirection * pdImgIn[n] * sin(2*M_PI *k *n / iLen); - pdImgOut[k] += -iDirection * pdRealIn[n] * sin(2*M_PI *k *n / iLen) + pdImgIn[n] * cos(2*M_PI *k *n / iLen); + double angle = iDirection * 2.0 * M_PI * k * n / iLen; + double cosTerm = cos(angle); + double sinTerm = sin(angle); + + pdRealOut[k] += pdRealIn[n] * cosTerm - pdImgIn[n] * sinTerm; + pdImgOut[k] += pdRealIn[n] * sinTerm + pdImgIn[n] * cosTerm; + } + + // Normalize the output + if (iDirection == -1) { + pdRealOut[k] /= iLen; + pdImgOut[k] /= iLen; } } } diff --git a/MMS24-25.h b/MMS24-25.h index 5a751d9..ef8c6bb 100644 --- a/MMS24-25.h +++ b/MMS24-25.h @@ -91,6 +91,6 @@ void computeDFT(int iLen, double *pdRealIn, double *pdImgIn, double *pdRealOut, double *pdImgOut, int iDirection /*-1,1*/); -void convertCart2Polar(double *pdRealIn, double *pdImgIn,double *pdRadiusOut, double *pdAngleOut, int iLen); -void convertPolar2Cart(double *pdRadiusIn, double *pdAngleIn, double *pdRealOut, double *pdImgOut,int iLen); +void convertCart2Polar(double *pdRealIn, double *pdImgIn, double *pdRadiusOut, double *pdAngleOut, int iLen); +void convertPolar2Cart(double *pdRadiusIn, double *pdAngleIn, double *pdRealOut, double *pdImgOut, int iLen); #endif diff --git a/Makefile b/Makefile index 1b5c3d8..c14a28a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=gcc SOURCE_FILE=MMS24-25.c -OPTS=-lm -D _TESTS_NONSTOP +OPTS=-lm -D _DEBUG_ all: clean tests run @@ -11,4 +11,4 @@ tests: $(CC) tests.c $(SOURCE_FILE) -o tests $(OPTS) run: - ./tests \ No newline at end of file + ./tests diff --git a/tests.c b/tests.c index 1d6f989..8e66bb8 100644 --- a/tests.c +++ b/tests.c @@ -44,8 +44,8 @@ void error(const char* fmt, ...) { void testInterpolation() { info("testInterpolation"); - double dRangeMin = -3; - double dRangeMax = 3; + double dRangeMin = -1; + double dRangeMax = 1; double dRangeStep = 0.33; for (double dX1=dRangeMin;dX1iNumValues != 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 != 6) { + error("Failed to calculate the correct median: %lf != 5.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 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"); +} + int main (int iArgC, char** ppcArgV){ testInterpolation(); testSineAndReadWriteDoubleArray(); + testCreateSignalWithDefault(); + testCreateSignalFromDoubleArray(); + testCreateSignalFromFile(); + testWriteMMSignal(); + testCalculateArea(); + testCalculateMean(); + testCalculateStdDev(); + testCalculateMedian(); + testCalculateEntropy(); + testConvoluteSignals(); + testGetPascalLine(); + testComputeDFT(); + testConvertCart2Polar(); + testConvertPolar2Cart(); return 0; }