diff --git a/MMS24-25.c b/MMS24-25.c index e2ea698..ece8c59 100644 --- a/MMS24-25.c +++ b/MMS24-25.c @@ -16,12 +16,16 @@ #include #include #include // For variadic functions + +// UNIX Libraries +#ifdef __unix__ #include // For checking if a file exists +#endif // Header File for this implementation #include "MMS24-25.h" -// Preprocessor Definitions in case they are not defined +// #region Preprocessor Definitions in case they are not defined #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -31,8 +35,24 @@ #ifndef NULL #define NULL 0 #endif +#ifndef S_IFDIR +#define S_IFDIR 0040000 +#endif +#ifndef ssize_t +#define ssize_t long +#endif +#ifndef EOF +#define EOF -1 +#endif +// #endregion // #region Error Handling and Debugging + +/* + * Prints an error message to stderr and exits the program with EXIT_FAILURE. + * If the program is compiled with the _TESTS_NONSTOP flag, the program will + * not exit. + */ void _error(char *fmt, ...) { fprintf(stderr, "< \x1B[31;4;1mERROR\x1B[0m : \x1B[34m'"); @@ -50,6 +70,10 @@ void _error(char *fmt, ...) { #endif } +/* + * Prints a memory error message to stderr and exits the program with + * EXIT_FAILURE. + */ void _mem_error(char *fmt, ...) { fprintf(stderr, "< \x1B[31;4;1mMEM_ERROR\x1B[0m : \x1B[34m'"); @@ -65,6 +89,9 @@ void _mem_error(char *fmt, ...) { exit(EXIT_FAILURE); } +/* + * Prints a debug message to stderr. + */ void _debug(char *fmt, ...) { fprintf(stderr, "[ \x1B[37;4;1mDEBUG\x1B[0m ] \x1B[34m'"); @@ -76,6 +103,9 @@ void _debug(char *fmt, ...) { fprintf(stderr, "'\x1B[0m\n"); } +/* + * Prints a warning message to stderr. + */ void _warn(char *fmt, ...) { fprintf(stderr, "[ \x1B[33;4;1mWARN\x1B[0m ] \x1B[34m'"); @@ -88,8 +118,59 @@ void _warn(char *fmt, ...) { } // #endregion -// #region Aufgabe 1 +// #region C99 Compatibility +#if _GNU_SOURCE || _POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 +#else +/* + * Implementation of the getline function for systems that do not implement it. + */ +ssize_t getline(char **lineptr, size_t *n, FILE *stream) { + if (*lineptr == NULL) { + *n = 128; + *lineptr = (char*) malloc(*n); + if (*lineptr == NULL) + _mem_error("getline: Failed to allocate memory for *lineptr."); + } + char *pos = *lineptr; + char c; + + while ((c = fgetc(stream)) != EOF) { +// For UNIX with \n: +#ifdef __unix__ + if (c == '\n') + break; +#else +// For Windows with \r\n: + if (c == '\r') + if ((c = fgetc(stream)) == '\n') + break; + else + ungetc(c, stream); +#endif + + if ((pos - *lineptr) >= *n) { + *n *= 2; + *lineptr = (char*) realloc(*lineptr, *n); + if (*lineptr == NULL) + _mem_error("getline: Failed to reallocate memory for *lineptr."); + } + + *pos++ = c; + } + + *pos = '\0'; + +#ifdef _DEBUG_ + _debug("getline: Read line '%s' with length %ld.", *lineptr, (pos - *lineptr)); +#endif + + return feof(stream) ? -1 : (pos - *lineptr); +} +#endif +// #endregion + +// #region Aufgabe 1 /* * Interpolates a line between two points (x1, y1) and (x2, y2) and returns * the y-coordinate of the point on the line at xq. @@ -126,6 +207,13 @@ double interpolateLine( /* * Rescales an array of double values so that the minimum value is dMin and * the difference between any two values is scaled by dFactor. + * + * Given $ |pdIn| = |pdOut| = iLen $ it should be, that: + * $ min(pdIn) = dMin $, as well as + * \[ \forall i, j \in (0,\dots,iLen-1): + * (pdOut_i - pdOut_j) = (pdIn_i - pdIn_j) *dFactor \], + * which naturally results from: + * $ pdOut_i = pdIn_i *dFactor + (dMin - min(pdIn)) $. */ void rescaleDoubleArray( double *pdIn, @@ -134,16 +222,6 @@ void rescaleDoubleArray( double dFactor, double *pdOut ) { - /* - * Rescale an array of double values, so that the following rules apply: - * Given $ |pdIn| = |pdOut| = iLen $ it should be, that: - * $ min(pdIn) = dMin $, as well as - * \[ \forall i, j \in (0,\dots,iLen-1): - * (pdOut_i - pdOut_j) = (pdIn_i - pdIn_j) *dFactor \], - * which naturally results from: - * $ pdOut_i = pdIn_i *dFactor + (dMin - min(pdIn)) $. - */ - if (iLen == 0) { _warn("rescaleDoubleArray: iLen is 0"); return; @@ -170,17 +248,16 @@ void rescaleDoubleArray( } } +/* + * Generate an array of `iNumValues` double value, so that the values + * represent the Y-coordinates of a sine curve of which one full period + * occupies `iNumSamplesPerPeriod` values. + */ double *generateSineValues( int iNumValues, int iNumSamplesPerPeriod, double dAmp ) { - /* - * Generate an array of `iNumValues` double value, so that the values - * represent the Y-coordinates of a sine curve of which one full period - * occupies `iNumSamplesPerPeriod` values. - */ - // Check for invalid input if (iNumSamplesPerPeriod == 0) /* @@ -215,7 +292,7 @@ double *generateSineValues( sineArray = (double*) malloc(sizeof(double) * iNumValues); if (sineArray == NULL) - _mem_error("*generateSineValues: Failed to allocate memory for Sine Array"); + _mem_error("*generateSineValues: Failed to allocate memory for Sine Array."); for (int i=0; i= 0 && !(statBuffer.st_mode & S_IFDIR)); } +#endif +/* + * Write an array of double values to a file with the name `pcOutName`. + * Each value is separated by a linebreak. + */ void writeDoubleArray(double *pdOut, int iLen, char *pcOutName) { - /* - * Write an array of double values to a file with the name `pcOutName`. - * Each value is separated by a linebreak. - */ - // Check if the path is NULL if (pcOutName == NULL) - _error("writeDoubleArray: pcOutName is NULL"); - - // Check if the path is a valid file - if (!isValidFile(pcOutName)) - _error("writeDoubleArray: '%s' is not a valid file", pcOutName); + _error("writeDoubleArray: pcOutName is NULL."); + // Open the file FILE *file = fopen(pcOutName, "w"); if (file == NULL) + /* + * If the file could not be opened, this is a fatal error, since we + * cannot write to a file that we cannot open. + */ _error("writeDoubleArray: Error while opening file '%s'.", pcOutName); - if (pdOut == NULL) - _error("writeDoubleArray: pdOut is NULL"); - if (iLen == 0) - _warn("writeDoubleArray: iLen is 0"); + _warn("writeDoubleArray: iLen is 0."); + + if (pdOut == NULL && iLen != 0) + /* + * If the array is NULL but the length is not 0, this is a fatal error, + */ + _error("writeDoubleArray: pdOut is NULL."); for (int i = 0; i < iLen; i++) { fprintf(file, "%f\n", pdOut[i]); @@ -262,27 +345,44 @@ void writeDoubleArray(double *pdOut, int iLen, char *pcOutName) { fclose(file); } +/* + * Reads a linebreak separated list of double values from a file with the name + * `pcInName` and stores them in the array `ppdIn`. + */ int readDoubleArray(char *pcInName, double **ppdIn) { - char *line = NULL; - size_t len = 0; - ssize_t read; - int i = 0; + char *line = NULL; // Buffer for the line + size_t len = 0; // Length of the buffer; not strictly necessary + ssize_t read; // Length of the read line; not strictly necessary + int i = 0; // Counter for the number of read values/lines + // Set the output pointer to NULL before malloc-ing *ppdIn = NULL; + // Check if the path is NULL + if (pcInName == NULL) + _error("readDoubleArray: pcInName is NULL."); + +#ifdef __unix__ + // Check if the path is a valid file + if (!isValidFile(pcInName)) + _error("readDoubleArray: '%s' is not a valid file.", pcInName); +#endif + FILE *file = fopen(pcInName, "r"); if (file == NULL) - _error("readDoubleArray: Error while opening file '%s'.\n", pcInName); + _error("readDoubleArray: Error while opening file '%s'.", pcInName); while ((read = getline(&line, &len, file)) != -1) { if (*ppdIn == NULL) { + // Allocate memory for the first value *ppdIn = (double*) malloc(sizeof(double)); } else { + // Reallocate memory for any further values *ppdIn = (double*) realloc(*ppdIn, sizeof(double) *(i+1)); } if (*ppdIn == NULL) - _error("readDoubleArray: Failed to allocate or re-allocate memory for pdIn\n"); + _mem_error("readDoubleArray: Failed to allocate or re-allocate memory for pdIn."); (*ppdIn)[i] = atof(line); i++; @@ -294,12 +394,16 @@ int readDoubleArray(char *pcInName, double **ppdIn) { return i; } +/* + * Creates a new MMSignal struct and initializes all values to 0 and all + * pointers to NULL. + */ MMSignal *createSignal() { MMSignal *signal = NULL; signal = (MMSignal*) malloc(sizeof(MMSignal)); if (signal == NULL) - _error("createSignal: Failed to allocate memory for signal\n"); + _mem_error("createSignal: Failed to allocate memory for signal"); signal->pdValues = NULL; signal->iNumValues = 0; @@ -312,18 +416,21 @@ MMSignal *createSignal() { return signal; } +/* + * Creates a deep copy of a LocalExtrema struct. + */ LocalExtrema* copyLocalExtrema(LocalExtrema *pexIn) { if (pexIn == NULL) return NULL; LocalExtrema *localExtrema = (LocalExtrema*) malloc(sizeof(LocalExtrema)); if (localExtrema == NULL) - _error("copyLocalExtrema: Failed to allocate memory for localExtrema\n"); + _mem_error("copyLocalExtrema: Failed to allocate memory for localExtrema"); localExtrema->iNumLocalExtrema = pexIn->iNumLocalExtrema; localExtrema->piLocalExtremaPos = (int*) malloc(sizeof(int) *pexIn->iNumLocalExtrema); if (localExtrema->piLocalExtremaPos == NULL) - _error("copyLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos\n"); + _mem_error("copyLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos"); for (int i = 0; i < pexIn->iNumLocalExtrema; i++) { localExtrema->piLocalExtremaPos[i] = pexIn->piLocalExtremaPos[i]; @@ -332,13 +439,16 @@ LocalExtrema* copyLocalExtrema(LocalExtrema *pexIn) { return localExtrema; } +/* + * Creates a deep copy of an Extrema struct. + */ 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"); + _mem_error("copyExtrema: Failed to allocate memory for extrema"); extrema->iGlobalMinPos = pexIn->iGlobalMinPos; extrema->iGlobalMaxPos = pexIn->iGlobalMaxPos; @@ -349,6 +459,9 @@ Extrema* copyExtrema(Extrema *pexIn) { return extrema; } +/* + * Creates a deep copy of a MMSignal struct. + */ MMSignal *createSignalFromSignal(MMSignal *pmmsIn) { MMSignal *signal = createSignal(); @@ -356,13 +469,13 @@ MMSignal *createSignalFromSignal(MMSignal *pmmsIn) { // 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"); + _mem_error("createSignalFromSignal: Failed to allocate memory for signal->pdValues"); for (int i = 0; i < pmmsIn->iNumValues; i++) { signal->pdValues[i] = pmmsIn->pdValues[i]; } } else { - _warn("createSignalFromSignal: pmmsIn->pdValues is NULL\n"); + _warn("createSignalFromSignal: pmmsIn->pdValues is NULL"); signal->pdValues = NULL; } @@ -375,7 +488,7 @@ MMSignal *createSignalFromSignal(MMSignal *pmmsIn) { // Deep copy of Extrema if (pmmsIn->pexExtrema == NULL) { - _warn("createSignalFromSignal: pmmsIn->pexExtrema is NULL\n"); + _warn("createSignalFromSignal: pmmsIn->pexExtrema is NULL"); signal->pexExtrema = NULL; return signal; } @@ -385,12 +498,16 @@ MMSignal *createSignalFromSignal(MMSignal *pmmsIn) { return signal; } +/* + * Creates a new MMSignal struct with an array of `iArrayLen` values, all + * initialized to `dDefaultValue`. + */ MMSignal *createSignalWithDefault(int iArrayLen, double dDefaultValue) { MMSignal *signal = createSignal(); signal->pdValues = (double*) malloc(sizeof(double) *iArrayLen); if (signal->pdValues == NULL) - _error("createSignalWithDefault: Failed to allocate memory for signal->pdValues\n"); + _mem_error("createSignalWithDefault: Failed to allocate memory for signal->pdValues"); for (int i = 0; i < iArrayLen; i++) { signal->pdValues[i] = dDefaultValue; @@ -401,12 +518,16 @@ MMSignal *createSignalWithDefault(int iArrayLen, double dDefaultValue) { return signal; } +/* + * Creates a new MMSignal struct with an array of `iArrayLen` values, + * containing the values from the array `pdIn`. + */ MMSignal *createSignalFromDoubleArray(int iArrayLen, double *pdIn) { MMSignal *signal = createSignal(); signal->pdValues = (double*) malloc(sizeof(double) *iArrayLen); if (signal->pdValues == NULL) - _error("createSignalFromDoubleArray: Failed to allocate memory for signal->pdValues\n"); + _mem_error("createSignalFromDoubleArray: Failed to allocate memory for signal->pdValues"); for (int i = 0; i < iArrayLen; i++) { signal->pdValues[i] = pdIn[i]; @@ -417,28 +538,33 @@ MMSignal *createSignalFromDoubleArray(int iArrayLen, double *pdIn) { return signal; } +/* + * Creates a new MMSignal struct and reads the values from a file with the + * name `pcInName`. + */ MMSignal *createSignalFromFile(char *pcInName) { double *pdIn = NULL; - int iArrayLen = readDoubleArray(pcInName, &pdIn); MMSignal *signal = createSignal(); - signal->pdValues = (double*) malloc(sizeof(double) *iArrayLen); - if (signal->pdValues == NULL) - _error("createSignalFromFile: Failed to allocate memory for signal->pdValues\n"); + signal->pdValues = NULL; + signal->iNumValues = readDoubleArray(pcInName, &signal->pdValues); - for (int i = 0; i < iArrayLen; i++) { - signal->pdValues[i] = pdIn[i]; - } + if (signal->iNumValues == 0) + _warn("createSignalFromFile: signal->iNumValues is 0"); - signal->iNumValues = iArrayLen; + if (signal->pdValues == NULL && signal->iNumValues != 0) + _error("createSignalFromFile: signal->pdValues is NULL while signal->iNumValues is not %d", signal->iNumValues); return signal; } +/* + * Writes the values of a MMSignal struct to a file with the name `pcInName`. + */ void writeMMSignal(char *pcInName, MMSignal *pmmsIn) { if (pmmsIn->pdValues == NULL) - _error("writeMMSignal: pmmsIn->pdValues is NULL\n"); + _error("writeMMSignal: pmmsIn->pdValues is NULL"); writeDoubleArray( pmmsIn->pdValues, @@ -447,6 +573,9 @@ void writeMMSignal(char *pcInName, MMSignal *pmmsIn) { ); } +/* + * Deletes a LocalExtrema struct and frees the memory. + */ void deleteLocalExtrema(LocalExtrema *pexIn) { if (pexIn == NULL) return; @@ -455,6 +584,9 @@ void deleteLocalExtrema(LocalExtrema *pexIn) { free(pexIn); } +/* + * Deletes an Extrema struct and frees the memory. + */ void deleteExtrema(Extrema *pexIn) { if (pexIn == NULL) return; @@ -464,6 +596,9 @@ void deleteExtrema(Extrema *pexIn) { free(pexIn); } +/* + * Deletes a MMSignal struct and frees the memory. + */ void deleteSignal(MMSignal *pmmsIn) { if (pmmsIn == NULL) return; @@ -478,6 +613,11 @@ void deleteSignal(MMSignal *pmmsIn) { // #endregion // #region Aufgabe 2 + +/* + * Calculates the area under a curve defined by the array `pdIn` of length + * `iLen`. The area is calculated as the sum of all values in the array. + */ double calculateArea(double *pdIn, int iLen) { double area = 0; @@ -494,6 +634,11 @@ double calculateArea(double *pdIn, int iLen) { return area; } +/* + * Calculates the mean of an array of double values `pdIn` of length `iLen`. + * The mean is calculated as the area under the curve divided by the length of + * the array. + */ double calculateMean(double *pdIn, int 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 @@ -502,6 +647,11 @@ double calculateMean(double *pdIn, int iLen) { return calculateArea(pdIn, iLen) / iLen; } +/* + * Calculates the standard deviation of an array of double values `pdIn` of + * length `iLen`. The standard deviation is calculated as the square root of + * the variance. + */ 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 } \] @@ -517,6 +667,11 @@ double calculateStddev(double *pdIn, int iLen) { return sqrt(stdDev / iLen); } +/* + * Calculates the median of an array of double values `pdIn` of length `iLen`. + * The median is calculated as the middle value of the sorted array if the + * length is odd, or the average of the two middle values if the length is even. + */ double calculateMedian(double *pdIn, int iLen) { /* The median is calculated as follows: * \[ median(pdIn) = \begin{cases} @@ -532,33 +687,118 @@ double calculateMedian(double *pdIn, int iLen) { } } -LocalExtrema *initLocalExtrema(int iLen) { +/* + * Creates a new LocalExtrema struct + */ +LocalExtrema *initLocalExtrema(int *piInPos, int iLen) { LocalExtrema *localExtrema = (LocalExtrema*) malloc(sizeof(LocalExtrema)); if (localExtrema == NULL) - _error("initLocalExtrema: Failed to allocate memory for localExtrema\n"); + _mem_error("initLocalExtrema: Failed to allocate memory for localExtrema"); - localExtrema->iNumLocalExtrema = 0; + localExtrema->iNumLocalExtrema = iLen; localExtrema->piLocalExtremaPos = (int*) malloc(sizeof(int) *iLen); if (localExtrema->piLocalExtremaPos == NULL) - _error("initLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos\n"); + _mem_error("initLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos"); + + for (int i = 0; i < iLen; i++) { + localExtrema->piLocalExtremaPos[i] = piInPos[i]; + } return localExtrema; } +int getMinMaxPos(double *pdIn, int iLen, int *iMinPos, int *iMaxPos) { + *iMaxPos = 0; + *iMinPos = 0; + + for (int i = 1; i < iLen; i++) { + if (pdIn[i] > pdIn[*iMaxPos]) + *iMaxPos = i; + if (pdIn[i] < pdIn[*iMinPos]) + *iMinPos = i; + } +} + +/* + * Finds the local extrema of an array of double values `pdIn` of length `iLen`. + * The local extrema are calculated as the points where the derivative changes + * sign. + */ +void findLocalExtremaFromArray( + double *pdIn, + int iLen, + LocalExtrema **ppexLocalMax, + LocalExtrema **ppexLocalMin + ) { + int iNumLocalMax = 0; + int iNumLocalMin = 0; + int *piLocalMaxPos = NULL; + int *piLocalMinPos = NULL; + + for (int i = 1; i < iLen - 1; i++) { + if (pdIn[i] > pdIn[i-1] && pdIn[i] > pdIn[i+1]) { + iNumLocalMax++; + + if (piLocalMaxPos == NULL) + piLocalMaxPos = (int*) malloc(sizeof(int) *iNumLocalMax); + else + piLocalMaxPos = (int*) realloc(piLocalMaxPos, sizeof(int) *iNumLocalMax); + + if (piLocalMaxPos == NULL) + _mem_error("findLocalExtremaFromArray: Failed to allocate memory for piLocalMaxPos"); + + piLocalMaxPos[iNumLocalMax-1] = i; + } + + if (pdIn[i] < pdIn[i-1] && pdIn[i] < pdIn[i+1]) { + iNumLocalMin++; + + if (piLocalMinPos == NULL) + piLocalMinPos = (int*) malloc(sizeof(int) *iNumLocalMin); + else + piLocalMinPos = (int*) realloc(piLocalMinPos, sizeof(int) *iNumLocalMin); + + if (piLocalMinPos == NULL) + _mem_error("findLocalExtremaFromArray: Failed to allocate memory for piLocalMinPos"); + + piLocalMinPos[iNumLocalMin-1] = i; + } + } + + *ppexLocalMax = initLocalExtrema(piLocalMaxPos, iNumLocalMax); + *ppexLocalMin = initLocalExtrema(piLocalMinPos, iNumLocalMin); +} + +/* + * Calculates the global extrema of an array of double values `pdIn` of length + * `iLen`. The global extrema are calculated as the points where the derivative + * changes sign. + */ Extrema *initExtrema(double *pdIn, int iLen) { Extrema *extrema = (Extrema*) malloc(sizeof(Extrema)); if (extrema == NULL) - _error("initExtrema: Failed to allocate memory for extrema\n"); + _mem_error("initExtrema: Failed to allocate memory for extrema"); - extrema->pexLocalMax = initLocalExtrema(iLen); - extrema->pexLocalMin = initLocalExtrema(iLen); + // Get the global extrema + getMinMaxPos(pdIn, iLen, &extrema->iGlobalMinPos, &extrema->iGlobalMaxPos); - extrema->iGlobalMaxPos = 0; - extrema->iGlobalMinPos = 0; + // Initialize the local extrema + extrema->pexLocalMax = NULL; + extrema->pexLocalMin = NULL; + + // Find the local extrema + findLocalExtremaFromArray( + pdIn, iLen, + &extrema->pexLocalMax, + &extrema->pexLocalMin + ); return extrema; } +/* + * Initializes the features of a MMSignal struct. + */ void initMMSignalFeatures(MMSignal *pmmsIn) { pmmsIn->dArea = 0; pmmsIn->dMean = 0; @@ -567,16 +807,20 @@ void initMMSignalFeatures(MMSignal *pmmsIn) { pmmsIn->pexExtrema = initExtrema(pmmsIn->pdValues, pmmsIn->iNumValues); } +/* + * Initializes a histogram struct with the values of an array of double values + * `pdIn` of length `iLen`. The histogram is divided into `iNumBins` bins. + */ 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"); + _mem_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"); + _warn("initHistogram: iLen is 0"); histogram->dIntervalMin = pdIn[0]; histogram->dIntervalMax = pdIn[0]; @@ -587,24 +831,24 @@ Histogram *initHistogram(double *pdIn, int iLen, int iNumBins) { histogram->dIntervalMax = pdIn[i]; } - #ifdef _DEBUG_ +#ifdef _DEBUG_ _debug("initHistogram: histogram->dIntervalMin = %lf", histogram->dIntervalMin); _debug("initHistogram: histogram->dIntervalMax = %lf", histogram->dIntervalMax); - #endif +#endif histogram->dBinWidth = (histogram->dIntervalMax - histogram->dIntervalMin) / iNumBins; histogram->iNumBins = iNumBins; - #ifdef _DEBUG_ +#ifdef _DEBUG_ _debug("initHistogram: histogram->dBinWidth = %lf", histogram->dBinWidth); - #endif +#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"); + _mem_error("initHistogram: Failed to allocate memory for histogram->pdBinValues"); for (int i = 0; i < iNumBins; i++) { histogram->pdBinValues[i] = 0; @@ -622,18 +866,21 @@ Histogram *initHistogram(double *pdIn, int iLen, int iNumBins) { histogram->pdBinValues[i] /= iLen; } - #ifdef _DEBUG_ +#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 +#endif return histogram; } +/* + * Deletes a histogram struct and frees the memory. + */ void deleteHistogram(Histogram *phIn) { if (phIn == NULL) return; @@ -642,7 +889,14 @@ void deleteHistogram(Histogram *phIn) { free(phIn); } +/* + * Calculates the entropy of a histogram struct. + */ double calculateEntropy(Histogram *phisIn) { + /* The entropy is calculated as follows: + * \[ entropy(phisIn) = - \sum_{i=1}^{ |phisIn| } phisIn_i \log_2(phisIn_i) \] + */ + double entropy = 0; for (int i = 0; i < phisIn->iNumBins; i++) { @@ -658,13 +912,16 @@ double calculateEntropy(Histogram *phisIn) { // #region Aufgabe 3 +/* + * Convolve two signals `pmmsInA` and `pmmsInB` and return the result. + */ MMSignal *convoluteSignals(MMSignal *pmmsInA, MMSignal *pmmsInB) { // Calculate output length int iOutLen = pmmsInA->iNumValues + pmmsInB->iNumValues - 1; double *pdOut = (double*) malloc(sizeof(double) *iOutLen); if (pdOut == NULL) - _error("convoluteSignals: Failed to allocate memory for pdOut\n"); + _mem_error("convoluteSignals: Failed to allocate memory for pdOut"); for (int i = 0; i < iOutLen; i++) { pdOut[i] = 0; @@ -682,16 +939,18 @@ MMSignal *convoluteSignals(MMSignal *pmmsInA, MMSignal *pmmsInB) { return signal; } -// #FIXME: Check if this checks out +/* + * Calculate the n-th line of Pascal's triangle and return it as a signal. + */ MMSignal *getPascalLine(int iLinenum) { if (iLinenum <= 0) - _error("getPascalLine: iLinenum must be greater than 0\n"); + _error("getPascalLine: iLinenum must be greater than 0"); // 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"); + _mem_error("getPascalLine: Failed to allocate memory for pdValues"); for (int i = 0; i < iLinenum; i++) { pdValues[i] = 1; @@ -709,15 +968,26 @@ MMSignal *getPascalLine(int iLinenum) { // #endregion // #region Aufgabe 4 + +/* + * Computes the Discrete Fourier Transform of a signal, given as two arrays of + * real and imaginary values. The result is stored in two arrays of real and + * imaginary values. + */ void computeDFT( int iLen, double *pdRealIn, double *pdImgIn, double *pdRealOut, double *pdImgOut, int iDirection ) { + if (iDirection == 0) + _error("computeDFT: iDirection must not be 0!"); + // Check the value of iDirection - if (iDirection != -1 && iDirection != 1) - _error("computeDFT: iDirection must be either -1 or 1\n"); + if (iDirection != -1 && iDirection != 1) { + _warn("computeDFT: iDirection should be either -1 or 1! Normalizing!"); + iDirection = iDirection < 0 ? -1 : 1; + } for (int k = 0; k < iLen; k++) { pdRealOut[k] = 0; @@ -741,6 +1011,9 @@ void computeDFT( } } +/* + * Converts a signal from cartesian to polar coordinates. + */ void convertCart2Polar(double *pdRealIn, double *pdImgIn, double *pdRadiusOut, double *pdAngleOut, int iLen) { for (int i = 0; i < iLen; i++) { pdRadiusOut[i] = sqrt(pow(pdRealIn[i], 2) + pow(pdImgIn[i], 2)); @@ -748,10 +1021,14 @@ void convertCart2Polar(double *pdRealIn, double *pdImgIn, double *pdRadiusOut, d } } +/* + * Converts a signal from polar to cartesian coordinates. + */ void convertPolar2Cart(double *pdRadiusIn, double *pdAngleIn, double *pdRealOut, double *pdImgOut, int iLen) { for (int i = 0; i < iLen; i++) { pdRealOut[i] = pdRadiusIn[i] * cos(pdAngleIn[i]); pdImgOut[i] = pdRadiusIn[i] * sin(pdAngleIn[i]); } } + // #endregion diff --git a/Makefile b/Makefile index bf8468c..027277a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=gcc SOURCE_FILE=MMS24-25.c -OPTS=-lm +OPTS=-lm -std=c99 all: clean tests run diff --git a/tests.c b/tests.c index 8e66bb8..6e5d9ca 100644 --- a/tests.c +++ b/tests.c @@ -8,6 +8,17 @@ #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'");