#include #include #include #include #include extern char **argvec; #define DFMT "$%d.%02d" #define PRINTDOLLAR(x) ((x)/100),((x)%100) #define SDFMT "$%s%d.%02d" #define PRINTSDOLLAR(x) (((x)<0)?"-":""),(abs((x))/100),(abs((x))%100) typedef struct fsplit FSPLIT; typedef struct call CALL; typedef struct callset CALLSET; struct fsplit { double a; double m; } ; #define FSS(x) ((x).a+(x).m) struct call { int amt; unsigned int exempt_gst : 1; unsigned int exempt_tvq : 1; unsigned int a_pays : 1; unsigned int m_pays : 1; unsigned int extradisc : 1; unsigned int nodisc : 1; } ; struct callset { FSPLIT total; FSPLIT gst_taxable; FSPLIT tvq_taxable; } ; static int other_amt; static int other_gst; static int other_tvq; static int line_amt; static int line_gst; static int line_tvq; static int bill_amt; static int bill_gst; static int bill_tvq; static CALLSET na_reg; static CALLSET na_extra; static CALLSET na_no; static int na_gst; static int na_tvq; static FSPLIT even = { 1, 1 }; static int fnei(double f, int i) { f = fabs(f-i); return(f>1e-4); } static void panic(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); abort(); } #define nint nint_ /* some header is declaring it, growl */ static int nint(double x) { return((x<0)?(x-.5):(x+.5)); } static int roundoff(double x) { int rv; rv = nint(x); printf("[%g->%d]\n",x,rv); return(rv); } static int get(void) { int c; c = getchar(); if (c == EOF) exit(1); return(c); } static void unget(int c) { ungetc(c,stdin); } static void flush_line(void) { while (get() != '\n') ; } static int get_amount(int *vp, const char *help, unsigned int flags, const char *fmt, ...) #define GA_FLUSHLINE 0x00000001 #define GA_MUSTHAVE 0x00000002 #define GA_DEFZERO 0x00000004 #define GA_DEFNOCHG 0x00000008 #define GA_SIGNED 0x00000010 { int v; int postdot; int c; va_list ap; int neg; top:; va_start(ap,fmt); vprintf(fmt,ap); va_end(ap); fflush(stdout); v = 0; neg = 1; postdot = -1; #define DIGITS '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9' c = get(); switch (c) { case '-': neg = -1; /* fall through */ case '+': if (flags & GA_SIGNED) { if (0) { case DIGITS: case '.': unget(c); } case '$': while (1) { c = get(); switch (c) { case '$': break; case DIGITS: v = (v * 10) + (c - '0'); if (postdot >= 0) postdot ++; break; case '.': if (postdot < 0) { postdot = 0; } else { printf("Multiple dots in amount\n"); flush_line(); goto top; } break; default: unget(c); if (postdot != 2) { if (postdot < 0) { printf("Missing . in amount\n"); } else { printf("Other than two digits after . in amount\n"); } flush_line(); goto top; } *vp = v * neg; if (flags & GA_FLUSHLINE) flush_line(); return(1); break; } } } /* fall through */ default: if (help) printf("%s",help); flush_line(); goto top; break; case '\n': if (flags & GA_MUSTHAVE) { if (help) printf("%s",help); goto top; } if (flags & GA_DEFZERO) { *vp = 0; return(1); } if (flags & GA_DEFNOCHG) { return(1); } return(0); break; } #undef DIGITS } static int readcall(const char *prompt, CALL *callp) { int c; char exempt; char who; char disc; if (0) { top:; printf("Try that over.\n"); } if (! get_amount(&callp->amt,"\ Enter amount, optionally B or E for tax exemption status, * if the call is\n\ one of the Intermax 5%-extra numbers or % for no discount, and A, M, or S\n\ for Anne, Mouse, or Shared, or a blank line to end.\n",0,"%s: ",prompt)) { return(0); } exempt = ' '; who = ' '; disc = ' '; while (1) { c = getchar(); switch (c) { case EOF: exit(1); break; case '\n': switch (exempt) { case ' ': callp->exempt_gst = 0; callp->exempt_tvq = 0; break; case 'B': callp->exempt_gst = 0; callp->exempt_tvq = 1; break; case 'E': callp->exempt_gst = 1; callp->exempt_tvq = 1; break; default: abort(); break; } switch (who) { case ' ': printf("Must use A, M, or S to describe who pays\n"); goto top; break; case 'A': callp->a_pays = 1; callp->m_pays = 0; break; case 'M': callp->a_pays = 0; callp->m_pays = 1; break; case 'S': callp->a_pays = 1; callp->m_pays = 1; break; default: abort(); break; } callp->extradisc = 0; callp->nodisc = 0; switch (disc) { case ' ': break; case '*': callp->extradisc = 1; break; case '%': callp->nodisc = 1; break; default: abort(); break; } return(1); break; case 'b': case 'B': exempt = 'B'; break; case 'e': case 'E': exempt = 'E'; break; case 'a': case 'A': who = 'A'; break; case 'm': case 'M': who = 'M'; break; case 's': case 'S': who = 'S'; break; case '*': disc = '*'; break; case '%': disc = '%'; break; case ' ': break; default: printf("Don't know what `%c' is supposed to mean\n",c); goto top; break; } } } static void add_call_to_fsplit(CALL *c, FSPLIT *s) { if (c->a_pays && c->m_pays) { s->a += c->amt / 2.0; s->m += c->amt / 2.0; } else if (c->a_pays) { s->a += c->amt; } else { s->m += c->amt; } } static void zero_callset(CALLSET *cs) { cs->total.a = 0; cs->total.m = 0; cs->gst_taxable.a = 0; cs->gst_taxable.m = 0; cs->tvq_taxable.a = 0; cs->tvq_taxable.m = 0; } static void distrib_discount(double damt, CALLSET *cs) { double f; f = (FSS(cs->total) - damt) / (double)FSS(cs->total); cs->total.a *= f; cs->total.m *= f; cs->gst_taxable.a *= f; cs->gst_taxable.m *= f; cs->tvq_taxable.a *= f; cs->tvq_taxable.m *= f; } static void read_calls(void) { CALL call; int ncalls; int totreg; int totextra; int totno; zero_callset(&na_reg); zero_callset(&na_extra); zero_callset(&na_no); totreg = 0; totextra = 0; totno = 0; ncalls = 0; while (readcall("Long distance call",&call)) { if (call.extradisc) { totextra += call.amt; add_call_to_fsplit(&call,&na_extra.total); if (! call.exempt_gst) add_call_to_fsplit(&call,&na_extra.gst_taxable); if (! call.exempt_tvq) add_call_to_fsplit(&call,&na_extra.tvq_taxable); } else if (call.nodisc) { totno += call.amt; add_call_to_fsplit(&call,&na_no.total); if (! call.exempt_gst) add_call_to_fsplit(&call,&na_no.gst_taxable); if (! call.exempt_tvq) add_call_to_fsplit(&call,&na_no.tvq_taxable); } else { totreg += call.amt; add_call_to_fsplit(&call,&na_reg.total); if (! call.exempt_gst) add_call_to_fsplit(&call,&na_reg.gst_taxable); if (! call.exempt_tvq) add_call_to_fsplit(&call,&na_reg.tvq_taxable); } ncalls ++; } if (ncalls == 0) return; if (fnei(FSS(na_reg.total),totreg)) panic("Total mismatch 1 in read_callset"); if (fnei(FSS(na_extra.total),totextra)) panic("Total mismatch 2 in read_callset"); if (fnei(FSS(na_no.total),totno)) panic("Total mismatch 3 in read_callset"); if (totreg+totextra+totno == 0) return; printf("Call total "DFMT"\n",PRINTDOLLAR(totreg+totextra+totno)); if (totextra) { int damt_20; damt_20 = nint(totextra*.2); get_amount(&damt_20,0,GA_DEFNOCHG|GA_FLUSHLINE,"20%% Intermax discount ["DFMT"]: ",PRINTDOLLAR(damt_20)); distrib_discount(damt_20,&na_extra); } if (totreg) { int damt_15; damt_15 = nint(totreg*.15); get_amount(&damt_15,0,GA_DEFNOCHG|GA_FLUSHLINE,"15%% Intermax discount ["DFMT"]: ",PRINTDOLLAR(damt_15)); distrib_discount(damt_15,&na_reg); } na_gst = roundoff((FSS(na_reg.gst_taxable)+FSS(na_extra.gst_taxable)+FSS(na_no.gst_taxable))*.07); get_amount(&na_gst,0,GA_DEFNOCHG|GA_FLUSHLINE,"Calls GST ["DFMT"]: ",PRINTDOLLAR(na_gst)); na_tvq = roundoff((FSS(na_reg.tvq_taxable)+FSS(na_extra.tvq_taxable)+FSS(na_no.tvq_taxable))*1.07*.065); get_amount(&na_tvq,0,GA_DEFNOCHG|GA_FLUSHLINE,"Calls TVQ ["DFMT"]: ",PRINTDOLLAR(na_tvq)); } static void readbill(void) { int a; get_amount(&line_amt,0,GA_MUSTHAVE|GA_FLUSHLINE,"Basic services amount: "); line_gst = roundoff(line_amt*.07); get_amount(&line_gst,0,GA_DEFNOCHG|GA_FLUSHLINE,"Basic services GST ["DFMT"]: ",PRINTDOLLAR(line_gst)); line_tvq = roundoff((line_amt+line_gst)*.065); get_amount(&line_tvq,0,GA_DEFNOCHG|GA_FLUSHLINE,"Basic services TVQ ["DFMT"]: ",PRINTDOLLAR(line_tvq)); while (get_amount(&a,0,GA_SIGNED|GA_FLUSHLINE,"Other charge(+)/credit(-) (blank line to end): ")) other_amt += a; printf("Total other amount: "SDFMT"\n",PRINTSDOLLAR(other_amt)); if (other_amt) { other_gst = (other_amt < 0) ? -roundoff(-other_amt*.07) : roundoff(other_amt*.07); get_amount(&other_gst,0,GA_SIGNED|GA_DEFNOCHG|GA_FLUSHLINE,"Other GST ["SDFMT"]: ",PRINTSDOLLAR(other_gst)); other_tvq = (other_amt+other_gst < 0) ? -roundoff(-(other_gst+other_amt)*.065) : roundoff((other_gst+other_amt)*.065) ; get_amount(&other_tvq,0,GA_SIGNED|GA_DEFNOCHG|GA_FLUSHLINE,"Other TVQ ["SDFMT"]: ",PRINTSDOLLAR(other_tvq)); } else { other_gst = 0; other_tvq = 0; } read_calls(); #define S(x) FSS(x.total) bill_amt = nint(line_amt+other_amt+FSS(na_reg.total)+FSS(na_extra.total)+FSS(na_no.total)); #undef S bill_gst = nint(line_gst+other_gst+na_gst); bill_tvq = nint(line_tvq+other_tvq+na_tvq); get_amount(&bill_amt,0,GA_DEFNOCHG|GA_FLUSHLINE,"Bill total ["DFMT"]: ",PRINTDOLLAR(bill_amt)); get_amount(&bill_gst,0,GA_DEFNOCHG|GA_FLUSHLINE,"Bill GST ["DFMT"]: ",PRINTDOLLAR(bill_gst)); get_amount(&bill_tvq,0,GA_DEFNOCHG|GA_FLUSHLINE,"Bill TVQ ["DFMT"]: ",PRINTDOLLAR(bill_tvq)); } static void amount_add(FSPLIT *add, FSPLIT *into) { into->a += add->a; into->m += add->m; } static void distrib_amount_add(double amount, FSPLIT *split, FSPLIT *addto) { double fss; if (amount == 0) return; fss = FSS(*split); if (fss == 0) abort(); addto->a += (amount * split->a) / fss; addto->m += (amount * split->m) / fss; } static void crunch(void) { FSPLIT tot; FSPLIT fstmp; int bill_total; tot.a = 0; tot.m = 0; distrib_amount_add(line_amt+line_gst+line_tvq,&even,&tot); distrib_amount_add(other_amt,&even,&tot); distrib_amount_add(other_gst,&even,&tot); distrib_amount_add(other_tvq,&even,&tot); amount_add(&na_reg.total,&tot); amount_add(&na_extra.total,&tot); amount_add(&na_no.total,&tot); fstmp.a = na_reg.gst_taxable.a + na_extra.gst_taxable.a + na_no.gst_taxable.a; fstmp.m = na_reg.gst_taxable.m + na_extra.gst_taxable.m + na_no.gst_taxable.m; distrib_amount_add(na_gst,&fstmp,&tot); fstmp.a = na_reg.tvq_taxable.a + na_extra.tvq_taxable.a + na_no.tvq_taxable.a; fstmp.m = na_reg.tvq_taxable.m + na_extra.tvq_taxable.m + na_no.tvq_taxable.m; distrib_amount_add(na_tvq,&fstmp,&tot); bill_total = bill_amt + bill_gst + bill_tvq; if (FSS(tot) == 0) { printf("Sorry, total is zero, can't distribute\n"); } else { int a; if (fnei(FSS(tot),bill_total)) { printf("Total mismatch: bill_total %d, FSS(tot) %g\n",bill_total,FSS(tot)); } a = nint(tot.a*bill_total/FSS(tot)); printf("Anne total = "SDFMT"\n",PRINTSDOLLAR(a)); printf("Mouse total = "SDFMT"\n",PRINTSDOLLAR(bill_total-a)); printf("Bill total = "SDFMT"\n",PRINTSDOLLAR(bill_total)); } } int main(void); int main(void) { readbill(); crunch(); exit(0); }