304 lines
8.6 KiB
C
304 lines
8.6 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
#error *Please use a proper C-Compiler*
|
||
|
#endif
|
||
|
|
||
|
// needed for reading proper input from stdin
|
||
|
#define MAX_LINE_LEN 100
|
||
|
|
||
|
#define STUDENT_INFO "Sven", "Vogel", 1191225
|
||
|
|
||
|
// name of the file to write to on --export
|
||
|
const char *FILE_NAME = "info_export.txt";
|
||
|
|
||
|
/**
|
||
|
* @brief read some unsigned int form stdin. The value must be within and including in the range between min and max.
|
||
|
*
|
||
|
* @param min minimum value
|
||
|
* @param max maximum value
|
||
|
* @param msg message
|
||
|
* @return unsigned int
|
||
|
*/
|
||
|
unsigned int read_ul(const unsigned int min, const unsigned int max, const char* restrict msg) {
|
||
|
// buffer to fit line from stdin
|
||
|
char line[MAX_LINE_LEN];
|
||
|
// end of buffer
|
||
|
char *end = line + MAX_LINE_LEN;
|
||
|
|
||
|
unsigned int value;
|
||
|
|
||
|
do {
|
||
|
// print message
|
||
|
printf("%s", msg);
|
||
|
// flush stdout to make sure every char is written properly
|
||
|
fflush(stdout);
|
||
|
// read a line from stdin
|
||
|
fgets(line, MAX_LINE_LEN, stdin);
|
||
|
// parse line to unsigned long
|
||
|
value = strtoul(line, &end, 10);
|
||
|
|
||
|
} while (value > max || value < min);
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief read some double form stdin. The value must be within and including in the range between min and max.
|
||
|
* After each attempt to parse a meaningful value msg will be printed to stdout.
|
||
|
*
|
||
|
* @param min minimum value
|
||
|
* @param max maximum value
|
||
|
* @param msg message
|
||
|
* @return unsigned int
|
||
|
*/
|
||
|
double read_lf(const double min, const double max, const char* restrict msg) {
|
||
|
// buffer to fit line from stdin
|
||
|
char line[MAX_LINE_LEN];
|
||
|
// end of buffer
|
||
|
char *end = line + MAX_LINE_LEN;
|
||
|
|
||
|
double value;
|
||
|
|
||
|
do {
|
||
|
// print message
|
||
|
printf("%s", msg);
|
||
|
// read a line from stdin
|
||
|
fgets(line, MAX_LINE_LEN, stdin);
|
||
|
// parse line to double
|
||
|
value = strtod(line, &end);
|
||
|
|
||
|
} while (value > max || value < min);
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief read interest rate and growth rate from stdin
|
||
|
*
|
||
|
* @param interestRate
|
||
|
* @param growthRate
|
||
|
* @return true
|
||
|
* @return false
|
||
|
*/
|
||
|
void input_rate_and_growth(double *interestRate, double *growthRate) {
|
||
|
|
||
|
// input interest rate
|
||
|
*interestRate = read_lf(0.01, 10, "\tEnter interest rate [0.01; 10]: ");
|
||
|
|
||
|
// input growth rate
|
||
|
*growthRate = read_lf(0.01, 5.0, "\tEnter growth rate [0.01; 5]: ");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief fill tab with interest calculations. Each row will contain data for the next year.
|
||
|
* the columns will contain [0] := interest rate [1] := investment amount [2] := investment growth
|
||
|
*
|
||
|
* @param tab 3 column table
|
||
|
* @param duration number of years to calculate for
|
||
|
* @param investmentAmount start money
|
||
|
* @return double sum off total investments
|
||
|
*/
|
||
|
double calculate_interest_growth(double tab[][3], int duration, double investmentAmount) {
|
||
|
// declare variables
|
||
|
double interestRate;
|
||
|
double growthRate;
|
||
|
double totalInterests = 0;
|
||
|
|
||
|
input_rate_and_growth(&interestRate, &growthRate);
|
||
|
|
||
|
// initialize 1. year
|
||
|
tab[0][0] = interestRate;
|
||
|
tab[0][1] = investmentAmount;
|
||
|
tab[0][2] = investmentAmount * interestRate * 1e-2;
|
||
|
|
||
|
for(unsigned int year = 1; year < duration; year++) {
|
||
|
unsigned int lastYear = year - 1;
|
||
|
|
||
|
// investment = investment form last year + interest
|
||
|
tab[year][1] = tab[lastYear][1] + tab[lastYear][2];
|
||
|
|
||
|
// interest rate = last interest rate + interest growth
|
||
|
tab[year][0] = tab[lastYear][0] + growthRate;
|
||
|
|
||
|
// interest = investmentAmount * interest rate
|
||
|
tab[year][2] = tab[year][1] * tab[year][0] * 1e-2;
|
||
|
|
||
|
totalInterests += tab[year][2];
|
||
|
}
|
||
|
|
||
|
return totalInterests + tab[0][2];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief print the table tab as layouted for calculate_interest_growth() to stdout
|
||
|
*
|
||
|
* @param tab
|
||
|
* @param duration number of rows
|
||
|
*/
|
||
|
void output_table(double tab[][3], const unsigned int rows) {
|
||
|
printf("\n\tinterest rate [%%] investment [€] annual interest [€]\n");
|
||
|
|
||
|
unsigned int year = 0;
|
||
|
|
||
|
// print each row of the table
|
||
|
for(unsigned int row = 0; row < rows; row++) {
|
||
|
year ++;
|
||
|
printf("year %2d:% 12.2lf\t% 12.2lf\t% 10.2lf\n", year, tab[row][0], tab[row][1], tab[row][2]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief find the maximum value in arr
|
||
|
*
|
||
|
* @param arr
|
||
|
* @param len
|
||
|
* @return unsigned int index of the maximum value in arr
|
||
|
*/
|
||
|
unsigned int find_index(double arr[], unsigned int len) {
|
||
|
unsigned int current = 0;
|
||
|
double max = 0;
|
||
|
|
||
|
// look through the entire array
|
||
|
for (unsigned int x = 0; x < len; x++) {
|
||
|
// test if we have found sth bigger than before
|
||
|
if (arr[x] > max) {
|
||
|
current = x;
|
||
|
max = arr[x];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// return index of biggest found element
|
||
|
return current;
|
||
|
}
|
||
|
|
||
|
typedef struct student {
|
||
|
const char* name;
|
||
|
const char* familyName;
|
||
|
// 7-Decimal-Digit UUID number
|
||
|
uint32_t matriculationNumber;
|
||
|
} Student;
|
||
|
|
||
|
/**
|
||
|
* @brief create a new stack allocated student
|
||
|
*
|
||
|
* @param name forename of the student
|
||
|
* @param familyName
|
||
|
* @param matriculationNumber 7-digit unsigned uuid
|
||
|
* @return Student
|
||
|
*/
|
||
|
Student new_student(const char* restrict name, const char* restrict familyName, const uint32_t matriculationNumber) {
|
||
|
Student new_stud = {
|
||
|
.name = name,
|
||
|
.familyName = familyName,
|
||
|
.matriculationNumber = matriculationNumber,
|
||
|
};
|
||
|
|
||
|
return new_stud;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief writes the format of student to the file specified by FILE_NAME.
|
||
|
*
|
||
|
* @param text
|
||
|
*/
|
||
|
void export_to_disk(const char* restrict text) {
|
||
|
|
||
|
// open file in write mode only, truncating previous content to zero
|
||
|
FILE* handle = fopen(FILE_NAME, "w");
|
||
|
|
||
|
// file could not be opened
|
||
|
if (handle == NULL) {
|
||
|
fprintf(stderr, "could not open file to export\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
fprintf(handle, "%s", text);
|
||
|
|
||
|
fclose(handle);
|
||
|
|
||
|
printf("Author exported to disk. Goodbye!\n");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief fill a destination string with the format "Name <NAME>, <FAMILYNAME> Matriekelnummer: <NUMBER>"
|
||
|
*
|
||
|
* @param dest string to store in, should be 100 bytes in size
|
||
|
* @param author student to take inforamtion from
|
||
|
*/
|
||
|
void generate_string(char * restrict dest, const Student *student) {
|
||
|
sprintf(dest, "Name: %s %s, Matrikelnummer: %7d\n", student->name, student->familyName, student->matriculationNumber);
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv) {
|
||
|
|
||
|
const Student author = new_student(STUDENT_INFO);
|
||
|
|
||
|
unsigned int offerCount;
|
||
|
unsigned int years;
|
||
|
double investmentAmount;
|
||
|
|
||
|
char info[100]; // hopefully no one ever has a name longer than 100 chars
|
||
|
|
||
|
generate_string(info, &author);
|
||
|
|
||
|
// parse command line argument
|
||
|
if (argc > 1 && strcmp(argv[1], "--info") == 0) {
|
||
|
printf("%s", info);
|
||
|
|
||
|
if (argc > 2 && strcmp(argv[2], "--export") == 0) {
|
||
|
export_to_disk(info);
|
||
|
// abort, were done
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// run for ever. or until the user decides to kill it.
|
||
|
while (1) {
|
||
|
|
||
|
// input data
|
||
|
years = read_ul(1, 50, "Enter investment duration in years [1; 50]: ");
|
||
|
investmentAmount = read_lf(100, 10000, "Enter investment amount in € [100; 10000]: ");
|
||
|
offerCount = read_ul(1, 3, "Enter offer count [1; 3]: ");
|
||
|
|
||
|
// allocate table
|
||
|
double (*tab)[3] = calloc(years, sizeof(double[3]));
|
||
|
|
||
|
// allocate array for all offer sums
|
||
|
// alternative dynamic stack allocation: double totalInterests[offerCount];
|
||
|
// alternative static stack allocation: double totalInterests[50];
|
||
|
double *totalInterests = calloc(offerCount, sizeof(double));
|
||
|
|
||
|
for (int i = 0; i < offerCount; i++) {
|
||
|
printf("\n--------------------------------------------------------------\n");
|
||
|
|
||
|
// call berechne_zinswachtum()
|
||
|
double totalInterest = calculate_interest_growth(tab, years, investmentAmount);
|
||
|
|
||
|
// save this offers total interest for later
|
||
|
totalInterests[i] = totalInterest;
|
||
|
|
||
|
output_table(tab, years);
|
||
|
|
||
|
printf("\nTotal interest after 3 years: %.2lf€\n", totalInterest);
|
||
|
}
|
||
|
|
||
|
// find and print best offer
|
||
|
|
||
|
unsigned int bestOffer = find_index(totalInterests, offerCount);
|
||
|
|
||
|
printf("\n--------------------------------------------------------------\n");
|
||
|
printf("***Offer %d has the best total interest with %.2lf€ after %d years***\n\n", bestOffer + 1, totalInterests[bestOffer], years);
|
||
|
printf("\n--------------------------------------------------------------\n");
|
||
|
|
||
|
// free resources
|
||
|
free(totalInterests);
|
||
|
free(tab);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|