Commit 0603af00 authored by bbguimaraes's avatar bbguimaraes
Browse files

21st_century_c: tenth chapter

parent 31823e48
CFLAGS = -I../2 -I../9 `pkg-config --cflags glib-2.0` -Wall -g -std=gnu11
LDLIBS = -lm `pkg-config --libs glib-2.0`
OBJECTS = \
sum_to_nan \
stopif_test \
varad \
safe_sum \
vectorize \
boxes \
papersizes \
errortuple \
olden_varargs \
macro_varargs \
ideal \
amort_use \
show_tree \
charct
all: $(OBJECTS)
amort_use: amort_interface.o amortize.o
show_tree: process_dir.o
charct: unictr.o process_dir.o ../9/string_utilities.o
../9/string_utilities.o:
make -C ../9 string_utilities.o
test:
./sum_to_nan
./stopif_test
./varad
./safe_sum
./vectorize
./boxes
./papersizes
./errortuple && exit 1 || echo ok
./olden_varargs .
./macro_varargs .
./ideal
./amort_use
./show_tree ..
./charct
clean:
rm -f $(OBJECTS) amort_interface.o amortize.o process_dir.o unictr.o \
../9/string_utilities.o
#include "stopif.h"
#include <stdio.h>
#include "amortize.h"
amortization_s amortize_prep(amortization_s in) {
Stopif(
!in.amount || in.amount < 0 || in.selloff_month < 0
|| in.selloff_year < 0,
return (amortization_s) { .error="Invalid input" },
"Invalid input. Returning zeros.");
int months = in.months;
if(!months) {
if(in.years) months = in.years * 12;
else months = 12 * 30; // home loan
}
int selloff_month = in.selloff_month;
if(!selloff_month && in.selloff_year)
selloff_month = in.selloff_year * 12;
amortization_s out = in;
out.rate = out.rate || 4.5;
out.interest = amortize(
in.amount, out.rate, in.inflation, months, selloff_month,
in.extra_payoff, in.show_table, &(out.interest_pv),
&(out.years_to_payoff), &(out.monthly_payment));
return out;
}
#include <stdio.h>
#include "amortize.h"
int main() {
printf("A typical loan:\n");
amortization_s nopayments = amortization(.amount=200000, .inflation=3);
printf(
"You flushed real $%g down the toilet, or $%g in present value.\n",
nopayments.interest, nopayments.interest_pv);
amortization_s a_hundred = amortization(
.amount=200000, .inflation=3, .show_table=0, .extra_payoff=100);
printf(
"Paying an extra $100/month, you lose only $%g (PV), and the loan is"
" paid off in %g years.\n",
a_hundred.interest_pv, a_hundred.years_to_payoff);
printf(
"If you sell off in ten years, you pay $%g in interest (PV).\n",
amortization(
.amount=200000, .inflation=3, .show_table=0,
.selloff_year=10).interest_pv);
}
#include <math.h> // pow
#include <stdio.h>
#include "amortize.h"
double amortize(
double amt, double rate, double inflation, int months,
int selloff_month, double extra_payoff, int verbose,
double * interest_pv, double * duration, double * monthly_payment) {
double total_interest = 0;
*interest_pv = 0;
double mrate = rate / 1200;
// The monthly rate is fixed, but the proportion going to interest changes.
*monthly_payment = amt * mrate / (1 - pow(1 + mrate, -months));
if(verbose) printf("Your total monthly payment: %g\n\n", *monthly_payment);
int end_month = (selloff_month && selloff_month < months)
? selloff_month : months;
if(verbose) printf("yr/mon\tPrinc.\tInt.\t| Pv Princ.\tPv Int.\tRatio\n");
int m;
for(m = 0; m < end_month && amt > 0; ++m) {
amt -= extra_payoff;
double interest_payment = amt * mrate;
double principal_payment = *monthly_payment - interest_payment;
if(amt <= 0)
principal_payment = interest_payment = 0;
amt -= principal_payment;
double deflator = pow(1 + inflation / 100, -m / 12.);
*interest_pv += interest_payment * deflator;
total_interest += interest_payment;
if(verbose) printf(
"%i/%i\t%7.5g\t%7.5g\t| %7.5g\t %7.5g\t%7.5g\n",
m / 12, m - 12 * (m / 12) + 1, principal_payment, interest_payment,
principal_payment * deflator, interest_payment * deflator,
principal_payment / interest_payment);
}
*duration = m / 12.;
return total_interest;
}
double amortize(
double amt, double rate, double inflation, int months, int selloff_month,
double extra_payoff, int verbose, double *interest_pv, double *duration,
double * monthly_payment);
typedef struct {
double amount, years, rate, selloff_year, extra_payoff, inflation;
int months, selloff_month;
_Bool show_table;
double interest, interest_pv, monthly_payment, years_to_payoff;
char * error;
} amortization_s;
/**
* Calculate the inflation-adjusted amount of interest you would pay over the
* life of an amortized loan, such as a mortgage.
*
* \li \c amount The dollar value of the loan. No default -- if unspecified, I
* print an error and return zeros.
* \li \c months The number of months in the loan. Default: zero, but see years.
* \li \c years If you do not specify months, you can specify the number of
* years. E.g., 10.5=ten years, six months. Default: 30 (a typical U.S.
* mortgage).
* \li \c rate The interest rate of the load, expressed in annual percentage
* rate (APR). Default: 4.5 (i.e., 4.5%), which is typical for the current (US
* 2012) housing market.
* \li \c inflation The inflation rate as an annual percent, for calculating the
* present value of money. Default: 0, meaning no present-value adjustment. A
* rate of about 3 has been typical for the last few decades in the US.
* \li \c selloff_month At this month, the loan is paid off (e.g., you resell
* the house). Default: zero (meaning no selloff).
* \li \c selloff_year If selloff_month == 0 and this is positive, the year of
* selloff. Default: zero (meaning no selloff).
* \li \c extra_payoff Additional monthly principal payment. Default: zero.
* \li \c show_table If nonzero, display a table of payments. If zero, display
* nothing (just return the total interest). Default: 1.
*
* All inputs but \c extra_apyoff and \c inflation must be nonnegative.
*
* \return an \c amortization_s structure, with all of the above values set as
* per your input, plus:
*
* \li \c interest Total cash paid in interest.
* \li \c interest_pv Total interest paid, with present-value adjustment for
* inflation.
* \li \c monthly_payment The fixed monthly payment (for a mortgage, taxes and
* interest get added to this).
* \li \c years_to_payoff Normally the duration or selloff date, but if you make
* early payments, the loan is paid off sooner.
* \li \c error If <tt>error != NULL</tt>, something went wrong and the results
* are invalid.
*/
#define amortization(...) \
amortize_prep((amortization_s) { .show_table=1, __VA_ARGS__ })
amortization_s amortize_prep(amortization_s in);
#include <stdio.h>
typedef struct {
char * name;
int left, right, up, down;
} direction_s;
void this_row(direction_s d); // these functions are below
void draw_box(direction_s d);
int main() {
direction_s D = { .name="left", .left=1 };
draw_box(D);
D = (direction_s) { "upper right", .up=1, .right=1 };
draw_box(D);
draw_box((direction_s) {});
}
void this_row(direction_s d) {
printf(
d.left ? "*..\n"
: d.right ?"..*\n"
: ".*.\n");
}
void draw_box(direction_s d) {
printf("%s:\n", (d.name ? d.name : "a box"));
d.up ? this_row(d) : printf("...\n");
(!d.up && !d.down) ? this_row(d) : printf("...\n");
d.down ? this_row(d) : printf("...\n");
printf("\n");
}
#define _GNU_SOURCE // get stdio.h to define asprintf
#include "string_utilities.h" // string_from_file
#include "process_dir.h"
#include "unictr.h"
#include <glib.h>
#include <stdlib.h> // free
void hash_a_file(filestruct path) {
GHashTable * hash = path.data;
char * sf = string_from_file(path.fullname);
if(!sf) return;
char * sf_copy = sf;
if(g_utf8_validate(sf, -1, NULL)) {
gunichar uc;
while((uc = g_utf8_get_char(sf)) != '\0') {
hash_a_character(uc, hash);
sf = g_utf8_next_char(sf);
}
}
free(sf_copy);
}
int main(int argc, char ** argv) {
GHashTable * hash;
hash = new_unicode_counting_hash();
char * start = NULL;
if(argc > 1) asprintf(&start, "%s", argv[1]);
printf("Hashing %s\n", start ? start : "the current directory");
process_dir(.name=start, .file_action=hash_a_file, .data=hash);
g_hash_table_foreach(hash, printone, NULL);
}
#include <stdio.h>
#include <math.h> // NAN
#define make_err_s(intype, shortname) \
typedef struct { \
intype value; \
char const * error; \
} shortname##_err_s;
make_err_s(double, double)
make_err_s(int, int)
make_err_s(char *, string)
double_err_s free_fall_energy(double time, double mass) {
double_err_s out = {}; // initialize to all zeros.
out.error =
time < 0 ? "negative time"
: mass < 0 ? "negative mass"
: isnan(time) ? "NaN time"
: isnan(mass) ? "NaN mass"
: NULL;
if(out.error) return out;
double velocity = 9.8 * time;
out.value = mass * velocity / 2.;
return out;
}
#define Check_err(checkme, return_val) \
if(checkme.error) { \
fprintf(stderr, "error: %s\n", checkme.error); \
return return_val; \
}
int main() {
double notime = 0, fraction = 0;
double_err_s energy = free_fall_energy(1, 1);
printf(energy.error);
Check_err(energy, 1);
printf("Energy after one second: %g Joules\n", energy.value);
energy = free_fall_energy(2, 1);
Check_err(energy, 1);
printf("Energy after two seconds: %g Joules\n", energy.value);
energy = free_fall_energy(notime / fraction, 1);
Check_err(energy, 1);
printf("Energy after 0/0 seconds: %g Joules\n", energy.value);
return 0;
}
#define _GNU_SOURCE // cause stdio.h to include vasprintf
#include <stdio.h> // printf, vasprintf
#include <stdlib.h> // system
#include <assert.h>
#define System_w_printf(outval, ...) { \
char * string_for_systemf; \
asprintf(&string_for_systemf, __VA_ARGS__); \
outval = system(string_for_systemf); \
free(string_for_systemf); \
}
int main(int argc, char ** argv) {
assert(argc == 2);
int out;
System_w_printf(out, "ls %s", argv[1]);
return out;
}
#define _GNU_SOURCE // cause stdio.h to include vasprintf
#include <stdio.h> // printf, vasprintf
#include <stdarg.h> // va_start, va_end
#include <stdlib.h> // system, free
#include <assert.h>
int system_w_printf(char const * fmt, ...) \
__attribute__ ((format (printf,1,2)));
int system_w_printf(char const * fmt, ...) {
char * cmd;
va_list argp;
va_start(argp, fmt);
vasprintf(&cmd, fmt, argp);
va_end(argp);
int out = system(cmd);
free(cmd);
return out;
}
int main(int argc, char ** argv) {
assert(argc == 2);
return system_w_printf("ls %s", argv[1]);
}
#include <stdio.h>
#include <strings.h> // strcasecmp
#include <math.h> // NAN
typedef struct {
double width, height;
} size_s;
size_s width_height(char * papertype) {
return
!strcasecmp(papertype, "A4")
? (size_s) { .width=210, .height=297 }
: !strcasecmp(papertype, "Letter")
? (size_s) { .width=216, .height=279 }
: !strcasecmp(papertype, "Legal")
? (size_s) { .width=216, .height=356 }
: (size_s) { .width=NAN, .height=NAN };
}
int main() {
size_s a4size = width_height("a4");
printf("width = %g, height = %g\n", a4size.width, a4size.height);
}
#include "process_dir.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <dirent.h> // struct dirent
#include <stdlib.h> // free
int process_dir_r(filestruct level) {
if(!level.fullname) {
if(level.name) level.fullname = level.name;
else level.fullname = ".";
}
int errct = 0;
DIR * current = opendir(level.fullname);
if(!current) return 1;
struct dirent * entry;
while((entry = readdir(current))) {
if(entry->d_name[0] == '.') continue;
filestruct next_level = level;
next_level.name = entry->d_name;
asprintf(&next_level.fullname, "%s/%s", level.fullname, entry->d_name);
if(entry->d_type == DT_DIR) {
++next_level.depth;
if(level.directory_action) level.directory_action(next_level);
errct += process_dir_r(next_level);
} else if(entry->d_type == DT_REG && level.file_action) {
level.file_action(next_level);
errct += next_level.error;
}
free(next_level.fullname);
}
closedir(current);
return errct;
}
struct filestruct;
typedef void (* level_fn)(struct filestruct path);
typedef struct filestruct {
char * name, * fullname;
level_fn directory_action, file_action;
int depth, error;
void * data;
} filestruct;
/**
* I get the contents of the given directory, run \c file_action on each file,
* and for each directory run \c dir_action and recurse into the directory. Note
* that this makes the traversal `depth first'.
*
* Your function will take in a \c filestruct, qv. Note that there is a \c error
* element, which you can set to one to indicate error.
*
* Inputs are designated initializers, and may include:
*
* \li \c .name The current file or directory name.
* \li \c .fullname The path of the current file or directory.
* \li \c .directory_action A function that takes in a \c filestruct. I will
* call it with an appropriately-set \c filestruct for every directory (just
* before the files in the directory are processed).
* \li \c .file_action Like the \c directory_action, but the function I will
* call for every non-directoryy file.
* \li \c .data A void pointer to be passed in to your functions.
*
* \return 0=OK, otherwise the count of directories that failed + errors thrown
* by your scripts.
* Your functions May change the \c data element of the \c filestruct.
*
* Sample usage:
* \code
* void dirp(filestruct in) { printf("Directory: <%s>\n", in.name); }
* void filep(filestruct in) { printf("File: %s\n", in.name); }
*
* // list files, but not directories, in current dir:
* process_dir(.file_action=filep);
* // show everything in my home directory:
* process_dir(.name="/home/b", .file_action=filep, .directory_action=dirp);
* \endcode
*/
#define process_dir(...) process_dir_r((filestruct) { __VA_ARGS__ })
int process_dir_r(filestruct level);
#include <math.h> // NAN
#include <stdio.h>
double sum_array(double in []) {
double out = 0;
for(int i = 0; !isnan(in[i]); ++i)
out += in[i];
return out;
}
#define sum(...) sum_array((double []) { __VA_ARGS__, NAN })
int main() {
double two_and_two = sum(2, 2);
printf("2 + 2 = %g\n", two_and_two);
printf("(2 + 2) * 3 = %g\n", sum(two_and_two, two_and_two, two_and_two));
printf("sum(asst) = %g\n", sum(3.1415, two_and_two, 3, 8, 98.4));
}
#include <stdio.h>
#include "process_dir.h"
void print_dir(filestruct in) {
for(int i = 0; i < in.depth - 1; ++i) printf(" ");
printf("├ %s\n", in.name);
for(int i = 0; i < in.depth - 1; ++i) printf(" ");
printf("└───┐\n");
}
void print_file(filestruct in) {
for(int i = 0; i < in.depth; ++i) printf(" ");
printf("│ %s\n", in.name);
}
int main(int argc, char ** argv) {
char * start = (argc > 1) ? argv[1] : ".";
printf("Tree for %s:\n", start ? start : "the current directory");
process_dir(
.name=start, .file_action=print_file, .directory_action=print_dir);
}
../2/stopif_test.c
\ No newline at end of file
#include <math.h> // NAN
#include <stdio.h>
double sum(double in []) {
double out = 0;
for(int i = 0; !isnan(in[i]); ++i)
out += in[i];
return out;
}
int main() {
double list [] = { 1.1, 2.2, 3.3, NAN };
printf("sum: %g\n", sum(list));
printf("sum: %g\n", sum((double []) { 1.1, 2.2, 3.3, NAN }));
}
#include "string_utilities.h"
#include "process_dir.h"
#include "unictr.h"
#include <glib.h>
#include <stdlib.h> // calloc, malloc
typedef struct {
int count;
} count_s;
void hash_a_character(gunichar uc, GHashTable * hash) {
count_s * ct = g_hash_table_lookup(hash, &uc);
if(!ct) {
ct = calloc(1, sizeof(count_s));
gunichar * newchar = malloc(sizeof(gunichar));
*newchar = uc;
g_hash_table_insert(hash, newchar, ct);
}
++ct->count;
}
void printone(void * key_in, void * val_in, void * ignored) {
gunichar const * key = key_in;
count_s const * val = val_in;
char utf8[7];
utf8[g_unichar_to_utf8(*key, utf8)] = '\0';
printf("%s\t%i\n", utf8, val->count);
}
static gboolean equal_chars(void const * a_in, void const * b_in) {
const gunichar * a = a_in;
const gunichar * b = b_in;
return *a == *b;
}
GHashTable * new_unicode_counting_hash() {
return g_hash_table_new(g_str_hash, equal_chars);
}
#include <glib.h>
void hash_a_character(gunichar uc, GHashTable * hash);
void printone(void * key_in, void * val_in, void *xx);
GHashTable * new_unicode_counting_hash();
#include <stdio.h>
#define forloop(i, loopmax, ...) \
for(int i = 0; i < loopmax; ++i) { __VA_ARGS__ }
int main() {
int sum = 0;
forloop(i, 10, sum += i; printf("sum to %i: %i\n", i, sum);)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment