/* * Solve resistor networks. * * Reads a resistor network from stdin (see below for the input * language). Solves for the voltage at each node that doesn't have * its voltage specified and the current through each resistor; output * consists of all nodes' voltages, the net current into or out of * each voltage-specified node, and the current through each resistor. * * Input consists of three kinds of clauses: voltage specs, resistor * specs, and comments. Nodes are named with arbitrary strings (the * only restriction on them is that they may not contain whitespace or * NUL; there is no quoting mechanism). There is no explicit node * declaration; nodes are created by referring to them. * * A voltage specification consists of a V, optional whitespace, the * name of the node, whitespace, and the voltage at that node. * * A resistor specification consists of an R, optional whitespace, a * node name, whitespace, another node name, whitespace, and a * resistance. The resistance may have a suffix K or M to indicate * multiplication by 1000 or 1000000; no whitespace may appear between * the number and the suffix in this case. * * A comment consists of a #, followed by all the rest of the text on * the same line. Note that comments cannot begin partway through a * voltage or resistor spec; # is recognized as a comment introducer * only at the points where V and R are recognized as voltage and * resistor spec introducers. * * We then write three kinds of equations: * * For each resistor, the voltage difference across it equals its * resistance times the current through it. * * For each voltage-spec node, its voltage equals the specified * voltage. * * For each non-voltage-spec node, the currents through the * resistors connected to it sum to zero. * * This gives us a set of linear equations whose unknowns are the * voltages at non-voltage-spec nodes and the currents through the * resistors. We solve them. This solving can fail if we either have * zero resistance between two voltage-spec nodes (which leads to a * divide-by-zero when determining the current through it) or if we * have a part of the circuit with no path to any voltage-spec nodes * (in which case every node in that part of the circuit has * indeterminate voltage). * * Assuming neither of those cases occurs, we get currents for each * resistor and voltages for each node out of the equations. It's * then just a matter of computing net current for voltage-specified * nodes and printing the output. * * Positive current through a resistor corresponds to end 0 having a * larger voltage than end 1. To put it another way, for each * resistor, I * R = V[0] - V[1]. */ #include #include #include #include #include #include extern const char *__progname; typedef union num NUM; typedef struct node NODE; typedef struct resistor RESISTOR; typedef struct resistorlist RESISTORLIST; typedef struct numops NUMOPS; struct numops { const char *name; NUM (*int_to)(int); void (*print)(NUM); int (*absgt)(NUM, NUM); int (*tiny)(NUM); NUM (*add)(NUM, NUM); NUM (*sub)(NUM, NUM); NUM (*mul)(NUM, NUM); NUM (*div)(NUM, NUM); NUM (*get)(void); } ; #define NUMOPS_INIT(name) { \ #name, \ &ops_##name##_int_to, \ &ops_##name##_print, \ &ops_##name##_absgt, \ &ops_##name##_tiny, \ &ops_##name##_add, \ &ops_##name##_sub, \ &ops_##name##_mul, \ &ops_##name##_div, \ &ops_##name##_get, \ } union num { struct { long long int n; long long int d; } rf; double f; } ; struct node { NODE *link; char *name; int v; RESISTORLIST *rl; unsigned int flags; #define NF_VSPEC 0x00000001 NUM voltage; } ; struct resistorlist { RESISTORLIST *link; RESISTOR *r; int end; } ; struct resistor { RESISTOR *link; NUM resistance; NODE *ends[2]; int v; } ; static NODE *nodes; static RESISTOR *resistors; static int nv; static NUM **m; /* NUM m[nv+1][nv] */ static int *ve; static int iline; static int *ugb; static int uga; static int ugn; static int verbose; static const NUMOPS numops[]; static const NUMOPS *ops = 0; static int setops(const char *s) { int i; for (i=0;numops[i].name;i++) { if (! strcmp(s,numops[i].name)) { ops = &numops[i]; return(1); } } return(0); } static void handleargs(int ac, char **av) { int errs; int skip; int i; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: unrecognized argument `%s'\n",__progname,*av); errs = 1; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-v")) { verbose = 1; continue; } if (!strcmp(*av,"-num")) { WANTARG(); if (setops(av[skip])) continue; fprintf(stderr,"%s: bad number format `%s'\n",__progname,av[skip]); fprintf(stderr,"%s: supported formats:",__progname); for (i=0;numops[i].name;i++) fprintf(stderr," %s",numops[i].name); fprintf(stderr,"\n"); errs = 1; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); if (ops == 0) setops("float"); } static void init(void) { nodes = 0; resistors = 0; } static int gc(void) { int c; while (1) { if (ugn > 0) { c = ugb[--ugn]; } else { c = getchar(); } if (c == '\n') iline ++; if (c) return(c); } } static void ugc(int c) { if (ugn >= uga) ugb = realloc(ugb,(uga=ugn+8)*sizeof(*ugb)); ugb[ugn++] = c; if (c == '\n') iline --; } static int skipws(void) { int c; while (1) { c = gc(); if (! isspace(c)) return(c); } } static void unxeof(const char *) __attribute__((__noreturn__)); static void unxeof(const char *what) { fprintf(stderr,"%s: line %d: got EOF when expecting %s\n",__progname,iline,what); exit(1); } static NODE *get_node(void) { static char *b = 0; static int a = 0; int n; int c; NODE *rv; static void save(int ch) { if (n >= a) b = realloc(b,a=n+8); b[n++] = ch; } n = 0; c = skipws(); if (c == EOF) unxeof("a node name"); while (1) { if (isspace(c) || (c == EOF)) break; save(c); c = gc(); } ugc(c); save('\0'); for (rv=nodes;rv;rv=rv->link) if (!strcmp(b,rv->name)) return(rv); rv = malloc(sizeof(NODE)); rv->link = nodes; nodes = rv; rv->name = strdup(b); rv->rl = 0; rv->flags = 0; return(rv); } static unsigned long long int absll(long long int v) { return((v<0)?-v:v); } static NUM ops_rf_int_to(int v) { return((NUM){.rf={.n=v,.d=1}}); } static NUM rf__normalize(NUM v) { unsigned long long int a; unsigned long long int b; unsigned long long int r; if (v.rf.n == 0) return(ops_rf_int_to(0)); a = absll(v.rf.n); b = absll(v.rf.d); while (a) { r = b % a; b = a; a = r; } if (v.rf.d < 0) { v.rf.n = - v.rf.n; v.rf.d = - v.rf.d; } return((NUM){.rf={.n=v.rf.n/(long long int)b,.d=v.rf.d/(long long int)b}}); } static void ops_rf_print(NUM n) { if (n.rf.d == 1) printf("%lld",n.rf.n); else printf("%lld/%lld",n.rf.n,n.rf.d); } static int ops_rf_absgt(NUM a, NUM b) { return(absll(a.rf.n*b.rf.d)>absll(b.rf.n*a.rf.d)); } static int ops_rf_tiny(NUM v) { return(v.rf.d>(1000000*absll(v.rf.n))); } static NUM ops_rf_add(NUM a, NUM b) { return(rf__normalize((NUM){.rf={.n=(a.rf.n*b.rf.d)+(b.rf.n*a.rf.d),.d=a.rf.d*b.rf.d}})); } static NUM ops_rf_sub(NUM a, NUM b) { return(rf__normalize((NUM){.rf={.n=(a.rf.n*b.rf.d)-(b.rf.n*a.rf.d),.d=a.rf.d*b.rf.d}})); } static NUM ops_rf_mul(NUM a, NUM b) { return(rf__normalize((NUM){.rf={.n=a.rf.n*b.rf.n,.d=a.rf.d*b.rf.d}})); } static NUM ops_rf_div(NUM a, NUM b) { return(rf__normalize((NUM){.rf={.n=a.rf.n*b.rf.d,.d=a.rf.d*b.rf.n}})); } static NUM ops_rf_get(void) { long long int n; long long int d; int sawslash; int neg; int nd; int c; int dv; n = 0; d = 0; sawslash = 0; neg = 0; nd = 0; c = skipws(); ugc(c); while <"more"> (1) { c = gc(); switch (c) { case '0': dv = 0; if (0) { case '1': dv = 1; } if (0) { case '2': dv = 2; } if (0) { case '3': dv = 3; } if (0) { case '4': dv = 4; } if (0) { case '5': dv = 5; } if (0) { case '6': dv = 6; } if (0) { case '7': dv = 7; } if (0) { case '8': dv = 8; } if (0) { case '9': dv = 9; } nd ++; if (sawslash) { d = (d * 10) + dv; } else { n = (n * 10) + dv; } break; case '/': if (sawslash) break <"more">; sawslash = 1; nd = 0; break; case '-': if (nd || neg || sawslash) break <"more">; neg = 1; break; default: break <"more">; } } ugc(c); if (nd < 1) { fprintf(stderr,"%s: line %d: missing number\n",__progname,iline); exit(1); } if (neg) n = - n; return(sawslash?rf__normalize((NUM){.rf={.n=n,.d=d}}):(NUM){.rf={.n=n,.d=1}}); } static NUM ops_float_int_to(int v) { return((NUM){.f=v}); } static void ops_float_print(NUM n) { printf("%g",n.f); } static int ops_float_absgt(NUM a, NUM b) { return(fabs(a.f)>fabs(b.f)); } static int ops_float_tiny(NUM a) { return(fabs(a.f)<1e-10); } static NUM ops_float_add(NUM a, NUM b) { return((NUM){.f=(a.f+b.f)}); } static NUM ops_float_sub(NUM a, NUM b) { return((NUM){.f=(a.f-b.f)}); } static NUM ops_float_mul(NUM a, NUM b) { return((NUM){.f=(a.f*b.f)}); } static NUM ops_float_div(NUM a, NUM b) { return((NUM){.f=(a.f/b.f)}); } static NUM ops_float_get(void) { int c; double v; double f; int sawdot; int nexp; int exp; int dv; int neg; int eneg; int nd; v = 0; sawdot = 0; nexp = 0; neg = 0; eneg = 0; nd = 0; c = skipws(); ugc(c); while <"more"> (1) { c = gc(); switch (c) { case '0': dv = 0; if (0) { case '1': dv = 1; } if (0) { case '2': dv = 2; } if (0) { case '3': dv = 3; } if (0) { case '4': dv = 4; } if (0) { case '5': dv = 5; } if (0) { case '6': dv = 6; } if (0) { case '7': dv = 7; } if (0) { case '8': dv = 8; } if (0) { case '9': dv = 9; } nd ++; if (nexp) { exp = (exp * 10) + dv; nexp ++; } else if (sawdot) { v += dv * (f /= 10); } else { v = (v * 10) + dv; } break; case '-': if (nexp) { if ((nexp > 1) || eneg) break <"more">; eneg = 1; } else { if ((nd > 0) || sawdot || neg) break <"more">; neg = 1; } break; case '.': if (sawdot || nexp) break <"more">; sawdot = 1; f = 1; break; case 'e': case 'E': if (nd == 0) break <"more">; nexp = 1; exp = 0; break; default: break <"more">; } } ugc(c); if (nexp == 1) { fprintf(stderr,"%s: line %d: missing exponent in number\n",__progname,iline); exit(1); } if (nd == 0) { fprintf(stderr,"%s: line %d: missing number\n",__progname,iline); exit(1); } if (nexp) v *= pow(10,eneg?-exp:exp); if (neg) v = - v; return((NUM){.f=v}); } static const NUMOPS numops[] = { NUMOPS_INIT(float), NUMOPS_INIT(rf), { 0 } }; static NUM int_to_num(int v) { return((*ops->int_to)(v)); } static void num_print(NUM v) { (*ops->print)(v); } static int num_absgt(NUM a, NUM b) { return((*ops->absgt)(a,b)); } static int num_tiny(NUM v) { return((*ops->tiny)(v)); } static NUM num_add(NUM a, NUM b) { return((*ops->add)(a,b)); } static NUM num_sub(NUM a, NUM b) { return((*ops->sub)(a,b)); } static NUM num_mul(NUM a, NUM b) { return((*ops->mul)(a,b)); } static NUM num_div(NUM a, NUM b) { return((*ops->div)(a,b)); } static NUM get_value(void) { return((*ops->get)()); } static void load_voltage(void) { NODE *n; NUM v; n = get_node(); v = get_value(); if (n->flags & NF_VSPEC) { fprintf(stderr,"%s: line %d: voltage already specified for node %s\n",__progname,iline,n->name); exit(1); } n->flags |= NF_VSPEC; n->voltage = v; } static void load_resistor(void) { NODE *n0; NODE *n1; NUM v; RESISTOR *r; RESISTORLIST *l; int c; n0 = get_node(); n1 = get_node(); v = get_value(); c = gc(); switch (c) { case 'k': case 'K': v = num_mul(v,int_to_num(1000)); break; case 'm': case 'M': v = num_mul(v,int_to_num(1000000)); break; default: ugc(c); break; } r = malloc(sizeof(RESISTOR)); r->link = resistors; resistors = r; r->resistance = v; r->ends[0] = n0; r->ends[1] = n1; l = malloc(sizeof(RESISTORLIST)); l->link = n0->rl; n0->rl = l; l->r = r; l->end = 0; l = malloc(sizeof(RESISTORLIST)); l->link = n1->rl; n1->rl = l; l->r = r; l->end = 1; } static void load_comment(void) { int c; do c = gc(); while ((c != '\n') && (c != EOF)); } static void load(void) { int c; iline = 1; ugb = 0; uga = 0; ugn = 0; while (1) { c = skipws(); if (c == EOF) break; switch (c) { case 'V': load_voltage(); break; case 'R': load_resistor(); break; case '#': load_comment(); break; default: fprintf(stderr,"%s: line %d: bad clause type character `%c'\n",__progname,iline,c); exit(1); break; } } } static void setup(void) { NODE *n; RESISTOR *r; RESISTORLIST *l; int vx; int i; int j; nv = 0; for (n=nodes;n;n=n->link) n->v = nv ++; for (r=resistors;r;r=r->link) r->v = nv ++; if (nv < 1) { fprintf(stderr,"%s: empty network!\n",__progname); exit(1); } m = malloc((nv+1)*sizeof(NUM *)); m[0] = malloc((nv+1)*nv*sizeof(NUM)); for (i=0;i<=nv;i++) { if (i) m[i] = m[i-1] + nv; for (j=nv-1;j>=0;j--) m[i][j] = int_to_num(0); } ve = malloc(nv*sizeof(int)); for (i=nv-1;i>=0;i--) ve[i] = -1; vx = 0; for (n=nodes;n;n=n->link) { if (n->flags & NF_VSPEC) { m[n->v][n->v] = int_to_num(1); m[nv][n->v] = n->voltage; } else { for (l=n->rl;l;l=l->link) { m[l->r->v][n->v] = int_to_num(l->end?1:-1); } } } for (r=resistors;r;r=r->link) { m[r->v][r->v] = r->resistance; m[r->ends[0]->v][r->v] = int_to_num(-1); m[r->ends[1]->v][r->v] = int_to_num(1); } } static void dump(void) { int x; int y; if (! verbose) return; for (y=0;y=0;x--) { dump(); if (verbose) printf("\n"); maxy = -1; for (y=nv-1;y>=0;y--) { if (done[y]) continue; if ((maxy < 0) || num_absgt(m[x][y],m[x][maxy])) maxy = y; } if (maxy < 0) abort(); done[maxy] = 1; p = m[x][maxy]; if (verbose) { printf("pivot [%d][%d] = ",x,maxy); num_print(p); printf("\n\n"); } if (num_tiny(p)) { printf("ill-conditioned\n"); exit(1); } ve[x] = maxy; for (i=nv+1-1;i>=0;i--) m[i][maxy] = num_div(m[i][maxy],p); for (y=nv-1;y>=0;y--) { if (y == maxy) continue; v = m[x][y]; for (i=nv+1-1;i>=0;i--) { m[i][y] = num_sub(m[i][y],num_mul(v,m[i][maxy])); } } } } static void answer(void) { NODE *n; RESISTOR *r; NUM i; RESISTORLIST *l; int j; for (j=nv-1;j>=0;j--) if ((ve[j] < 0) || (ve[j] >= nv)) abort(); for (n=nodes;n;n=n->link) { printf("Node %s: voltage ",n->name); num_print(m[nv][ve[n->v]]); if (n->flags & NF_VSPEC) { i = int_to_num(0); for (l=n->rl;l;l=l->link) { if (l->end) { i = num_add(i,m[nv][ve[l->r->v]]); } else { i = num_sub(i,m[nv][ve[l->r->v]]); } } printf(" (specified), current "); num_print(i); } printf("\n"); } for (r=resistors;r;r=r->link) { printf("Resistor %s <-> %s: resistance ",r->ends[0]->name,r->ends[1]->name); num_print(r->resistance); printf(" current "); num_print(m[nv][ve[r->v]]); printf("\n"); } } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); init(); load(); setup(); solve(); dump(); answer(); return(0); }