/* =========================STYLE-GUIDELINES=========================== * All comments use MD-Syntax; * Formulas are written in KaTeX/TeX Syntax. * We adhere by the `kernel.org` style-guidelines. * See: https://www.kernel.org/doc/html/v4.10/process/coding-style.html * =============================AUTHORS================================ * Created by Frederik Beimgraben, Minh Dan Cam and Lea Hornberger in * the winter term of 2024/25 at the faculty of computer science at * Reutlingen University. * After grading the full source code will be available at: * https://git.beimgraben.net/MMS-2024-25/MMS-Loesung-2024-25.git */ // Standard Libraries #include #include #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" // #region 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 #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'"); // Pass the arguments to the vfprintf function va_list args; va_start(args, fmt); // Write to stderr so that the output does not interfere with the output vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "'\x1B[0m >\n"); #ifndef _TESTS_NONSTOP exit(EXIT_FAILURE); #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'"); // Pass the arguments to the vfprintf function va_list args; va_start(args, fmt); // Write to stderr so that the output does not interfere with the output vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "'\x1B[0m >\n"); exit(EXIT_FAILURE); } /* * Prints a debug message to stderr. */ void _debug(char *fmt, ...) { fprintf(stderr, "[ \x1B[37;4;1mDEBUG\x1B[0m ] \x1B[34m'"); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); 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'"); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "'\x1B[0m\n"); } // #endregion /* * Copies an array of doubles *fast*. */ void _copy_double_array(double *dst, double *src, int len) { memcpy(dst, src, len * sizeof(double)); } /* * Copies an array of integers *fast*. */ void _copy_int_array(int *dst, int *src, int len) { memcpy(dst, src, len * sizeof(int)); } // #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. */ double interpolateLine( double dX1, double dY1, double dX2, double dY2, double dXq ) { if (dX2 == dX1) /* * If the two x-coordinates are equal, the line is either vertical or * could have any slope. In this case, we cannot interpolate the line. */ _error("interpolateLine: dX1 and dX2 must differ!"); double y = dY1 * ( (dX2 - dXq) / (dX2 - dX1) ) + dY2 * ( (dXq - dX1) / (dX2 - dX1) ); #ifdef _DEBUG // DEBUG: print the parameters and result _debug("interpolateLine(%lf, %lf, %lf, %lf, %lf) -> %lf", dX1,dY1,dX2,dY2,dXq,y); #endif return y; } /* * 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 + min(pdIn)) * dFactor + dMin $. */ void rescaleDoubleArray( double *pdIn, int iLen, double dMin, double dFactor, double *pdOut ) { if (iLen == 0) { _warn("rescaleDoubleArray: iLen is 0"); return; } // First find the old minimum double dOldMin = pdIn[0]; for (int i = 1; i < iLen; i++) { if (pdIn[i] < dOldMin) dOldMin = pdIn[i]; } _debug("dOldMin: %lf", dOldMin); // Rescale the array for (int i = 0; i < iLen; i++) { pdOut[i] = (pdIn[i] - dOldMin) * dFactor + dMin; } } /* * Generate an array of `iNumValues` double values, 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 ) { // Check for invalid input if (iNumSamplesPerPeriod == 0) /* * This is a fatal error, since we cannot generate a sine wave without * a period */ _error("*generateSineValues: iNumSamplesPerPeriod must be non-0!"); if (iNumSamplesPerPeriod < 0) /* * This would result in a flipped sine wave; Wont lead to a fatal error * but is still invalid; Warn the user. */ _warn("*generateSineValues: iNumSamplesPerPeriod is less than 0! It will be normalized!"); if (iNumValues < 0) /* * It is not possible to generate a negative number of values; This is * a fatal error. */ _error("*generateSineValues: iNumValues must be equal to or greater than 0!"); if (iNumValues == 0) { /* * This would result in an empty array; Won't lead to a fatal error but * is still invalid; Warn the user. */ _warn("*generateSineValues: iNumValues is 0!"); return NULL; } // Initialize the output array double *sineArray = NULL; sineArray = (double*) malloc(sizeof(double) * iNumValues); if (sineArray == NULL) _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) { // Check if the path is NULL if (pcOutName == NULL) _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 (iLen == 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]); } 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; // 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'.", 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) _mem_error("readDoubleArray: Failed to allocate or re-allocate memory for pdIn."); (*ppdIn)[i] = atof(line); i++; } free(line); fclose(file); 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) _mem_error("createSignal: Failed to allocate memory for signal"); signal->pdValues = NULL; signal->iNumValues = 0; signal->dArea = 0; signal->dMean = 0; signal->dStdDev = 0; signal->dMedian = 0; signal->pexExtrema = NULL; 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) _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) _mem_error("copyLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos"); _copy_int_array(localExtrema->piLocalExtremaPos, pexIn->piLocalExtremaPos, pexIn->iNumLocalExtrema); 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) _mem_error("copyExtrema: Failed to allocate memory for extrema"); extrema->iGlobalMinPos = pexIn->iGlobalMinPos; extrema->iGlobalMaxPos = pexIn->iGlobalMaxPos; extrema->pexLocalMax = copyLocalExtrema(pexIn->pexLocalMax); extrema->pexLocalMin = copyLocalExtrema(pexIn->pexLocalMin); return extrema; } /* * Creates a deep copy of a MMSignal struct. */ MMSignal *createSignalFromSignal(MMSignal *pmmsIn) { MMSignal *signal = createSignal(); if (pmmsIn->pdValues != NULL) { // Initialize and copy the values (deep copy) signal->pdValues = (double*) malloc(sizeof(double) *pmmsIn->iNumValues); if (signal->pdValues == NULL) _mem_error("createSignalFromSignal: Failed to allocate memory for signal->pdValues"); _copy_double_array(signal->pdValues, pmmsIn->pdValues, pmmsIn->iNumValues); } else { if (pmmsIn->iNumValues != 0) _error("createSignalFromSignal: pmmsIn->pdValues is NULL while pmmsIn->iNumValues is not %d", pmmsIn->iNumValues); _warn("createSignalFromSignal: pmmsIn->pdValues is NULL"); signal->pdValues = NULL; } // Copy shallow items signal->iNumValues = pmmsIn->iNumValues; signal->dArea = pmmsIn->dArea; signal->dMean = pmmsIn->dMean; signal->dStdDev = pmmsIn->dStdDev; signal->dMedian = pmmsIn->dMedian; // Deep copy of Extrema if (pmmsIn->pexExtrema == NULL) { _warn("createSignalFromSignal: pmmsIn->pexExtrema is NULL"); signal->pexExtrema = NULL; return signal; } signal->pexExtrema = copyExtrema(pmmsIn->pexExtrema); 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) _mem_error("createSignalWithDefault: Failed to allocate memory for signal->pdValues"); for (int i = 0; i < iArrayLen; i++) { signal->pdValues[i] = dDefaultValue; } signal->iNumValues = iArrayLen; 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) _mem_error("createSignalFromDoubleArray: Failed to allocate memory for signal->pdValues"); _copy_double_array(signal->pdValues, pdIn, iArrayLen); signal->iNumValues = iArrayLen; 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; MMSignal *signal = createSignal(); signal->iNumValues = readDoubleArray(pcInName, &signal->pdValues); if (signal->iNumValues == 0) _warn("createSignalFromFile: signal->iNumValues is 0"); 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"); writeDoubleArray( pmmsIn->pdValues, pmmsIn->iNumValues, pcInName ); } /* * Deletes a LocalExtrema struct and frees the memory. */ void deleteLocalExtrema(LocalExtrema *pexIn) { if (pexIn == NULL) return; free(pexIn->piLocalExtremaPos); free(pexIn); } /* * Deletes an Extrema struct and frees the memory. */ void deleteExtrema(Extrema *pexIn) { if (pexIn == NULL) return; deleteLocalExtrema(pexIn->pexLocalMax); deleteLocalExtrema(pexIn->pexLocalMin); free(pexIn); } /* * Deletes a MMSignal struct and frees the memory. */ void deleteSignal(MMSignal *pmmsIn) { if (pmmsIn == NULL) return; if (pmmsIn->pdValues != NULL) free(pmmsIn->pdValues); deleteExtrema(pmmsIn->pexExtrema); free(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; 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; } /* * 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 * = \frac{area(x)}{ |pdIn| } \] */ if (iLen == 0) { _warn("calculateMean: iLen is 0"); return 0; } 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 } \] */ if (iLen == 0) { _warn("calculateStddev: iLen is 0"); return 0; } double mean = calculateMean(pdIn, iLen); double stdDev = 0; for (int i = 0; i < iLen; i++) { stdDev += pow(pdIn[i] - mean, 2); } return sqrt(stdDev / iLen); } /* * Compares two double values for the qsort function. */ int compareDoubles(const void* a, const void* b) { double val_a = * ( (double*) a ); double val_b = * ( (double*) b ); if ( val_a == val_b ) return 0; else if ( val_a < val_b ) return -1; else return 1; } /* * 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} * 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 == 0) { _warn("calculateMedian: iLen is 0"); return 0; } double *pdInSorted = NULL; pdInSorted = (double*) malloc(sizeof(double) * iLen); if (pdInSorted == NULL) _mem_error("calculateMedian: Failed to allocate memory for pdInSorted"); _copy_double_array(pdInSorted, pdIn, iLen); // Sort the array using stdlib's qsort function qsort(pdInSorted, iLen, sizeof(double), compareDoubles); double median; if (iLen % 2 == 0) { median = (pdInSorted[iLen/2 - 1] + pdInSorted[iLen/2]) / 2; } else { median = pdInSorted[iLen/2]; } free(pdInSorted); return median; } /* * Creates a new LocalExtrema struct */ LocalExtrema *createLocalExtrema() { LocalExtrema *localExtrema = (LocalExtrema*) malloc(sizeof(LocalExtrema)); if (localExtrema == NULL) _mem_error("initLocalExtrema: Failed to allocate memory for localExtrema"); localExtrema->iNumLocalExtrema = 0; localExtrema->piLocalExtremaPos = NULL; return localExtrema; } void getMinMaxPos(double *pdIn, int iLen, int *iMinPos, int *iMaxPos) { *iMaxPos = 0; *iMinPos = 0; if (iLen == 0) { _warn("getMinMaxPos: iLen is 0"); return; } 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 ) { LocalExtrema *pexLocalMax = createLocalExtrema(); LocalExtrema *pexLocalMin = createLocalExtrema(); int **ppiLocalMaxPos = &pexLocalMax->piLocalExtremaPos; int **ppiLocalMinPos = &pexLocalMin->piLocalExtremaPos; int *iNumLocalMax = &pexLocalMax->iNumLocalExtrema; int *iNumLocalMin = &pexLocalMin->iNumLocalExtrema; for (int i = 1; i < iLen - 1; i++) { if (pdIn[i] > pdIn[i-1] && pdIn[i] > pdIn[i+1]) { (*iNumLocalMax)++; #ifdef _DEBUG _debug("findLocalExtremaFromArray: Found local maximum at (%d, %lf)", i, pdIn[i]); #endif if (*ppiLocalMaxPos == NULL) *ppiLocalMaxPos = (int*) malloc(sizeof(int) * (*iNumLocalMax)); else *ppiLocalMaxPos = (int*) realloc(*ppiLocalMaxPos, sizeof(int) * (*iNumLocalMax)); if (*ppiLocalMaxPos == NULL) _mem_error("findLocalExtremaFromArray: Failed to allocate memory for piLocalMaxPos"); *ppiLocalMaxPos[(*iNumLocalMax)-1] = i; } if (pdIn[i] < pdIn[i-1] && pdIn[i] < pdIn[i+1]) { (*iNumLocalMin)++; #ifdef _DEBUG _debug("findLocalExtremaFromArray: Found local minimum at (%d, %lf)", i, pdIn[i]); #endif if (*ppiLocalMinPos == NULL) *ppiLocalMinPos = (int*) malloc(sizeof(int) * (*iNumLocalMin)); else *ppiLocalMinPos = (int*) realloc(*ppiLocalMinPos, sizeof(int) * (*iNumLocalMin)); if (*ppiLocalMinPos == NULL) _mem_error("findLocalExtremaFromArray: Failed to allocate memory for piLocalMinPos"); *ppiLocalMinPos[(*iNumLocalMin)-1] = i; } } #ifdef _DEBUG _debug("findLocalExtremaFromArray: Found %d local maxima", *iNumLocalMax); _debug("findLocalExtremaFromArray: Found %d local minima", *iNumLocalMin); #endif *ppexLocalMax = pexLocalMax; *ppexLocalMin = pexLocalMin; } /* * 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) _mem_error("initExtrema: Failed to allocate memory for extrema"); // Get the global extrema getMinMaxPos(pdIn, iLen, &extrema->iGlobalMinPos, &extrema->iGlobalMaxPos); #ifdef _DEBUG if (iLen > 0) { _debug("initExtrema: Found global minimum at (%d, %lf)", extrema->iGlobalMinPos, pdIn[extrema->iGlobalMinPos]); _debug("initExtrema: Found global maximum at (%d, %lf)", extrema->iGlobalMaxPos, pdIn[extrema->iGlobalMaxPos]); } #endif // 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 = calculateArea(pmmsIn->pdValues, pmmsIn->iNumValues); pmmsIn->dMean = calculateMean(pmmsIn->pdValues, pmmsIn->iNumValues); pmmsIn->dStdDev = calculateStddev(pmmsIn->pdValues, pmmsIn->iNumValues); pmmsIn->dMedian = calculateMedian(pmmsIn->pdValues, pmmsIn->iNumValues); 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) _mem_error("initHistogram: Failed to allocate memory for histogram"); if (iNumBins == 0) _error("initHistogram: iNumBins must be non-0"); int minPos, maxPos; getMinMaxPos(pdIn, iLen, &minPos, &maxPos); histogram->dIntervalMin = pdIn[minPos]; histogram->dIntervalMax = pdIn[maxPos]; #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 = NULL; // FIXME: This should be added in the final version histogram->pdBinValues = (double*) malloc(sizeof(double) *iNumBins); if (histogram->pdBinValues == NULL) _mem_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; } /* * Deletes a histogram struct and frees the memory. */ void deleteHistogram(Histogram *phIn) { if (phIn == NULL) return; free(phIn->pdBinValues); 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++) { if (phisIn->pdBinValues[i] != 0) { entropy += phisIn->pdBinValues[i] * log2(phisIn->pdBinValues[i]); } } return -entropy; } // #endregion // #region Aufgabe 3 /* * Convolve two signals `pmmsInA` and `pmmsInB` and return the result. * Actually this should be named `convolveSignals` but the task specifies * `convoluteSignals`. */ MMSignal *convoluteSignals(MMSignal *pmmsInA, MMSignal *pmmsInB) { // Calculate output length int iOutLen = pmmsInA->iNumValues + pmmsInB->iNumValues - 1; // Create the output signal to write directly to the destination MMSignal *signal = createSignalWithDefault(iOutLen, 0); double *pdOut = signal->pdValues; for (int i = 0; i < iOutLen; i++) { pdOut[i] = 0; } 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]; } } // Initialize the features of the signal initMMSignalFeatures(signal); return signal; } /* * 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"); // Calculate a kernel signal using pascales triangle without using the factorial MMSignal *signal = createSignalWithDefault(iLinenum, 1); double *pdValues = signal->pdValues; if (pdValues == NULL) _mem_error("getPascalLine: Failed to allocate memory for pdValues"); for (int i = 0; i < iLinenum; i++) { for (int j = i-1; j > 0; j--) { pdValues[j] += pdValues[j-1]; } } initMMSignalFeatures(signal); return signal; } // #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) { _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; pdImgOut[k] = 0; // Compute the sum for the k-th output element for (int n = 0; n < iLen; n++) { 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; } } } /* * 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)); pdAngleOut[i] = atan2(pdImgIn[i], pdRealIn[i]); } } /* * 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