MMS-Loesung-2024-25/MMS24-25.c

1122 lines
31 KiB
C

/* =========================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 <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdarg.h> // For variadic functions
// UNIX Libraries
#ifdef __unix__
#include <sys/stat.h> // 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
// #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 *dFactor + (dMin - min(pdIn)) $.
*/
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];
}
// Calculate the offset
double dOffset = dMin - dOldMin;
#ifdef _DEBUG
_debug("rescaleDoubleArray: dOldMin = %lf", dOldMin);
_debug("rescaleDoubleArray: dOffset = %lf", dOffset);
#endif
// Rescale the array
for (int i = 0; i < iLen; i++) {
pdOut[i] = pdIn[i] * dFactor + dOffset;
}
}
/*
* 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!");
// 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<iNumValues;i++){
sineArray[i] = dAmp * sin(((2*M_PI) / abs(iNumSamplesPerPeriod)) * i);
}
return sineArray;
}
// Only if UNIX Comliant
#ifdef __unix__
// Returns true if the given szFile is a valid file.
int isValidFile(const char* szFile) {
struct stat statBuffer;
return (stat(szFile, &statBuffer) >= 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");
for (int i = 0; i < pexIn->iNumLocalExtrema; i++) {
localExtrema->piLocalExtremaPos[i] = pexIn->piLocalExtremaPos[i];
}
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");
for (int i = 0; i < pmmsIn->iNumValues; i++) {
signal->pdValues[i] = pmmsIn->pdValues[i];
}
} 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");
for (int i = 0; i < iArrayLen; i++) {
signal->pdValues[i] = pdIn[i];
}
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->pdValues = NULL;
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} \]
*/
double *pdInSorted = NULL;
pdInSorted = (double*) malloc(sizeof(double) *iLen);
if (iLen == 0) {
_warn("calculateMedian: iLen is 0");
return 0;
}
if (pdInSorted == NULL)
_mem_error("calculateMedian: Failed to allocate memory for pdInSorted");
for (int i = 0; i < iLen; i++) {
pdInSorted[i] = pdIn[i];
}
// 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 *initLocalExtrema(int *piInPos, int iLen) {
LocalExtrema *localExtrema = (LocalExtrema*) malloc(sizeof(LocalExtrema));
if (localExtrema == NULL)
_mem_error("initLocalExtrema: Failed to allocate memory for localExtrema");
localExtrema->iNumLocalExtrema = iLen;
localExtrema->piLocalExtremaPos = (int*) malloc(sizeof(int) *iLen);
if (localExtrema->piLocalExtremaPos == NULL)
_mem_error("initLocalExtrema: Failed to allocate memory for localExtrema->piLocalExtremaPos");
for (int i = 0; i < iLen; i++) {
localExtrema->piLocalExtremaPos[i] = piInPos[i];
}
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
) {
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++;
#ifdef _DEBUG
_debug("findLocalExtremaFromArray: Found local maximum at (%d, %lf)", i, pdIn[i]);
#endif
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++;
#ifdef _DEBUG
_debug("findLocalExtremaFromArray: Found local minimum at (%d, %lf)", i, pdIn[i]);
#endif
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;
}
}
#ifdef _DEBUG
_debug("findLocalExtremaFromArray: Found %d local maxima", iNumLocalMax);
_debug("findLocalExtremaFromArray: Found %d local minima", iNumLocalMin);
#endif
*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)
_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");
/*if (iLen == 0) // FIXME: This should be an error in the final version
_error("initHistogram: iLen is 0");
histogram->dIntervalMin = pdIn[0];
histogram->dIntervalMax = pdIn[0];
for (int i = 0; i < iLen; i++) {
if (pdIn[i] < histogram->dIntervalMin)
histogram->dIntervalMin = pdIn[i];
if (pdIn[i] > histogram->dIntervalMax)
histogram->dIntervalMax = pdIn[i];
}*/
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;
double *pdOut = NULL; // FIXME: This should be added in the final version
pdOut = (double*) malloc(sizeof(double) *iOutLen);
if (pdOut == NULL)
_mem_error("convoluteSignals: Failed to allocate memory for pdOut");
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];
}
}
MMSignal *signal = createSignalFromDoubleArray(iOutLen, pdOut);
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
double *pdValues = NULL;
pdValues = (double*) malloc(sizeof(double) * iLinenum);
if (pdValues == NULL)
_mem_error("getPascalLine: Failed to allocate memory for pdValues");
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];
}
}
MMSignal *signal = createSignalFromDoubleArray(iLinenum, pdValues);
initMMSignalFeatures(signal);
free(pdValues);
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