/**************************************************************** Copyright (C) 2000 Lucent Technologies All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the name of Lucent or any of its entities not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************************************************/ /* Example of complete table handlers for .bit and .tab files. */ /* Except for their names and descriptions (specified in the */ /* funcadd call at the end), they are equivalent to AMPL's */ /* builtin "tab" and "bit" handlers. */ #include "stdlib.h" #include "string.h" #include "funcadd.h" #include "arith.h" /* for Arith_Kind_ASL and Long */ #define TM(len) (*ae->Tempmem)(TI->TMI,len) static int Adjust_ampl_bit(AmplExports*, TableInfo*, FILE*, char*); static int Adjust_ampl_tab(AmplExports*, TableInfo*, FILE*, char*); static int cantopen(AmplExports *ae, TableInfo *TI, char *tname) { sprintf(TI->Errmsg = (char*)TM(strlen(tname) + 24), "Cannot open \"%s\".", tname); return DB_Error; } static char* tabnamebit(TableInfo *TI) { char *s, *tname; switch(TI->nstrings) { default: return 0; case 1: tname = TI->strings[0]; break; case 2: s = TI->strings[0]; if (strcmp(s, "lib-bit")) return 0; tname = TI->strings[1]; } return !(s = strrchr(tname, '.')) || strcmp(s,".bit") ? 0 : tname; } static char* tabnametab(AmplExports *ae, TableInfo *TI) { char *s, *tname; size_t L; switch(TI->nstrings) { default: return 0; case 1: tname = TI->strings[0]; if (strcmp(tname, "lib-tab")) break; case 0: L = strlen(tname = TI->tname); memcpy(s = (char*)TM(L+5), tname, L); strcpy(s+L, ".tab"); return s; case 2: s = TI->strings[0]; if (strcmp(s, "lib-tab")) return 0; tname = TI->strings[1]; } return !(s = strrchr(tname, '.')) || strcmp(s,".tab") ? 0 : tname; } int Write_ampl_tab(AmplExports *ae, TableInfo *TI) { DbCol *db, *dbe; FILE *f; char *Missing, **colnames, *s, **sv, *tname; int a, c, i, n, n1, nc; long j, nrows; real *r; if (!(tname = tabnametab(ae,TI))) return DB_Refuse; if (TI->flags & DBTI_flags_IN && (f = fopen(tname,"r")) && Adjust_ampl_tab(ae, TI, f, tname)) return DB_Error; if (!(f = fopen(tname,"w"))) return cantopen(ae,TI,tname); fprintf(f, "ampl.tab %d %d\n", a = TI->arity, nc = TI->ncols); n = a + nc; n1 = n - 1; colnames = TI->colnames; for(i = 0; i < n; i++) fprintf(f, "%q%c", colnames[i], i < n1 ? '\t' : '\n'); dbe = TI->cols + n - 1; nrows = TI->nrows; Missing = TI->Missing; for(j = 0; j < nrows; j++) { c = '\t'; for(db = TI->cols; db <= dbe; db++) { if (db == dbe) c = '\n'; if ((r = db->dval)) if ((sv = db->sval) && (s = sv[j])) sprint: if (s == Missing) dotprint: fprintf(f,".%c",c); else fprintf(f,"%q%c",s,c); else fprintf(f,"%.g%c",r[j],c); else if ((s = db->sval[j])) goto sprint; else goto dotprint; } } fclose(f); return DB_Done; } typedef struct Tinfo { unsigned long buflen; long line; AmplExports *ae; TableInfo *TI; char *tname; char **fields; char *buf; FILE *f; int *quoted; char *s, *se; /* scratch */ int nf; } Tinfo; static int readerr(Tinfo *ti, char *msg) { AmplExports *ae = ti->ae; TableInfo *TI = ti->TI; int len = strlen(ti->tname) + strlen(msg); sprintf(TI->Errmsg = (char*)TM(len + 32), "Error reading file %s:\n\t%s.", ti->tname, msg); fclose(ti->f); return DB_Error; } static int getnum(char *s, int *np, char **se) { *np = (int)strtol(s,se,10); return **se > ' '; } static int geteol(AmplExports *ae, FILE *f, char *b, size_t blen) { char *be = b + blen - 1; int c; for(;;) { if ((c = getc(f)) == EOF) return 1; if (b < be) *b++ = c; if (c == '\n') break; } *b = 0; return 0; } static void bgrow(char **fi, Tinfo *ti, char **bp, char **bpe) { AmplExports *ae = ti->ae; TableInfo *TI = ti->TI; size_t olen = *bpe - *fi; unsigned long nlen = 2*ti->buflen; char *b = (char*)TM(nlen); if (olen) memcpy(b, *fi, olen); *bp = b + ti->buflen; ti->buflen = nlen; *fi = ti->buf = b; *bpe = b + nlen; } #define bput(x) if (b >= be) bgrow(fi,ti,&b,&be); *b++ = x; static int getfields(Tinfo *ti) { AmplExports *ae = ti->ae; FILE *f = ti->f; TableInfo *TI = ti->TI; char *b, *be, **fi, **fie; int c, c1, *q; ++ti->line; b = ti->buf; be = b + ti->buflen; fi = ti->fields; fie = fi + ti->nf; q = ti->quoted; for(;;q++) { while((c = getc(f)) <= ' ') { if (c == EOF) { if (fi == ti->fields && ti->line > 2) { fclose(f); return 1; } eof: sprintf(b = ti->buf, "early end of file on partial line %ld", ti->line); goto ret2; } if (c == '\n') { eol: sprintf(b = ti->buf, "too few fields on line %ld", ti->line); goto ret2; } } *fi = b; if (c == '\'' || c == '"') for(*q = 1;;) { if ((c1 = getc(f)) == EOF) goto eof; if (c == c1) { if ((c1 = getc(f)) == EOF) goto eof; if (c1 != c) { if (c1 > ' ') { sprintf(b = ti->buf, "Malformed quoted string, line %ld", ti->line); goto ret2; } c = c1; break; } } if (c1 == '\n') ti->line++; bput(c1); } else { *q = 0; if (c == '.') { if ((c = getc(f)) <= ' ') { *fi = 0; goto no_bput; } bput('.'); } bput(c); while((c = getc(f)) > ' ') { bput(c); } } bput(0); no_bput: if (++fi == fie) break; if (c == '\n') goto eol; } while(c != '\n') { if ((c = getc(f)) == EOF) goto eof; if (c > ' ') { sprintf(b = ti->buf, "Too many fields on line %ld", ti->line); ret2: readerr(ti,b); return 2; } } return 0; } #undef bput static char **bletch; static int compar(const void *a, const void *b) { return strcmp(bletch[*(int*)a], bletch[*(int*)b]); } static void strsort(int n, char **nam, int *z) { int i; for(i = 0; i < n; i++) z[i] = i; if (n > 1) { bletch = nam; qsort(z, n, sizeof(int), compar); } } static int permute(int n1, int n2, int *p, char **nam1, char **nam2, int *z, int *ncp) { /* 0 < n1 <= n2 */ /* z = scratch */ int i, j, k, *z2, zi, zj; char *s; strsort(n1, nam1, z); strsort(n2, nam2, z2 = z + n1); for(i = j = 0; i < n1; i++) { s = nam1[zi = z[i]]; for(;;j++) { if (j >= n2) { notfound: if (!ncp) return zi+1; zj = -++*ncp; break; } if (!(k = strcmp(nam2[zj = z2[j]], s))) break; if (k > 0) goto notfound; } p[zi] = zj; } return 0; } int Read_ampl_tab(AmplExports *ae, TableInfo *TI) { DbCol *db; Tinfo ti; char buf[64], *s, *se, *tbuf; int a, i, j, k, nc, ncf, *p, *z; real t; if (!(ti.tname = tabnametab(ae,TI))) return DB_Refuse; if (!(ti.f = fopen(ti.tname,"r"))) return cantopen(ae,TI,ti.tname); ti.ae = ae; ti.TI = TI; if (geteol(ae, ti.f,buf,sizeof(buf))) return readerr(&ti,"missing header line"); if (strncmp(buf,"ampl.tab ",9) || getnum(buf+9,&a,&s) || getnum(s,&ncf,&s)) return readerr(&ti,"bad header line"); if (a != TI->arity) { sprintf(buf, "header gives arity %d rather than %d", a, TI->arity); return readerr(&ti, buf); } nc = TI->ncols; if (ncf < nc) { sprintf(buf, "header says only %d columns; expected at least %d", a + ncf, a + nc); return readerr(&ti, buf); } ti.nf = a + ncf; ti.fields = (char**)TM(ti.nf*(sizeof(char*) + 4*sizeof(int))); ti.quoted = (int*)(ti.fields + ti.nf); p = ti.quoted + ti.nf; z = p + ti.nf; ti.buf = (char*)TM(ti.buflen = 2000); ti.line = 1; if (getfields(&ti)) return DB_Error; /* Allow file columns to be permuted */ if ((i = permute(a, a, p, TI->colnames, ti.fields, z, 0))) { badfield: s = TI->colnames[--i]; sprintf(tbuf = (char*)TM(strlen(s) + 32), "Column %Q not found.", s); return readerr(&ti, tbuf); } if (nc) { if ((i = permute(nc, ncf, p+a, TI->colnames + a, ti.fields + a, z, 0))) { i += a; goto badfield; } for(i = 0; i < nc; i++) p[i+a] += a; } TI->nrows = 1; nc += a; db = TI->cols; while(!(k = getfields(&ti))) { for(i = 0; i < nc; i++, db++) { if (!(s = ti.fields[j = p[i]])) db->sval[0] = TI->Missing; else if (ti.quoted[j]) db->sval[0] = s; else { t = strtod(s, &se); if (!*se) {/* valid number */ db->sval[0] = 0; db->dval[0] = t; } else db->sval[0] = s; } } db = TI->cols; if ((*TI->AddRows)(TI, db, 1)) { fclose(ti.f); return DB_Error; } } return k == 1 ? DB_Done : DB_Error; } static char * ti_copy(Tinfo *ti, char *s) { AmplExports *ae; TableInfo *TI; char *rv, *t; int n = strlen(s); if (ti->se - ti->s <= n) { n += 4000; ae = ti->ae; TI = ti->TI; ti->s = (char*)TM(n); ti->se = ti->s + n; } rv = t = ti->s; while((*t++ = *s++)); ti->s = t; return rv; } static int Adjust_ampl_tab(AmplExports *ae, TableInfo *TI, FILE *f, char *tname) { DbCol *db, *db0, *db1, *dbe; Tinfo ti; char *Missing, buf[64], *s, *se, **sp, **sv, **sv1, **sv2; int a, i, j, k, nc, ncf, nh, nn; int *h, *p, *pe, *pe1, *z; real *dv, *dv1, *dv2, t; ti.ae = ae; ti.TI = TI; ti.f = f; ti.s = ti.se = 0; ti.tname = tname; if (geteol(ae, f,buf,sizeof(buf))) return readerr(&ti,"missing header line"); if (strncmp(buf,"ampl.tab ",9) || getnum(buf+9,&a,&s) || getnum(s,&ncf,&s)) return readerr(&ti,"bad header line"); if (a != TI->arity) { sprintf(buf, "header gives arity %d rather than %d", a, TI->arity); return readerr(&ti, buf); } nc = TI->ncols; ti.nf = a + ncf; k = a + nc; dv = (real*)TM(ti.nf*(2*sizeof(char*) + 2*sizeof(int) + sizeof(real)) + a*(sizeof(real) + sizeof(char*)) + (ncf + 2*k)*sizeof(int)); dv2 = dv + ti.nf; sv = (char**)(dv2 + a); sv2 = sv + ti.nf; ti.fields = sv2 + a; ti.quoted = (int*)(ti.fields + ti.nf); p = ti.quoted + ti.nf; pe = p + k; z = pe + ncf; ti.buf = (char*)TM(ti.buflen = 2000); ti.line = 1; if (getfields(&ti)) return DB_Error; /* Allow file columns to be permuted */ if ((i = permute(a, a, p, TI->colnames, ti.fields, z, 0))) { s = TI->colnames[--i]; sprintf(TI->Errmsg = (char*)TM(strlen(s) + 32), "Column %Q not found.", s); return DB_Error; } nh = 0; h = z + ti.nf; pe1 = p + a; if (nc) { permute(nc, ncf, pe1, TI->colnames + a, ti.fields + a, z, &nh); for(i = j = 0; i < nc; i++) if (pe1[i] >= 0) pe1[i] += a; else h[j++] = i + a; } for(i = 0; i < ti.nf; i++) z[i] = -1; nc += a; for(i = 0; i < nc; i++) if ((j = p[i]) >= 0) z[j] = i; nn = 0; for(i = a; i < ti.nf; i++) if (z[i] < 0) nn++; sp = TI->colnames; s = 0; /* shut up warning of non-initialization */ if (nn) { k = nc + nn; db = (DbCol*)TM(k*(sizeof(DbCol) + sizeof(char*))); memcpy(db, TI->cols, nc*sizeof(DbCol)); memset(db + nc, 0, nn*sizeof(DbCol)); TI->cols = db; sp = (char**)(db + k); memcpy(sp, TI->colnames, nc*sizeof(char*)); TI->colnames = sp; TI->ncols += nn; } j = 0; db0 = TI->cols; db1 = db0 + nc; for(i = a; i < ti.nf; i++) if (z[i] < 0) { sp[nc] = ti_copy(&ti, ti.fields[i]); pe[j++] = i; z[i] = nc++; } dbe = db0 + nc; Missing = TI->Missing; while(!(k = getfields(&ti))) { dv1 = 0; sv1 = 0; for(i = 0; i < ti.nf; i++) { if (!(s = ti.fields[i])) s = Missing; else if (!ti.quoted[i]) { t = strtod(s, &se); if (!*se) {/* valid number */ dv[i] = t; s = 0; } } sv[i] = s; } t = 0.; for(i = 0; i < a; i++) { j = z[i]; if ((s = sv[i])) { if (s == Missing) s = "."; sv1 = sv2; } else { dv1 = dv2; dv2[j] = dv[i]; } sv2[j] = s; } j = (*TI->Lookup)(dv1, sv1, TI); if (j >= 0) { pe1 = pe; for(db = db1; db < dbe; db++) { i = *pe1++; if ((s = sv[i])) { if (!db->sval) (*TI->ColAlloc)(TI, db-db0, 1); if (s != Missing) s = ti_copy(&ti, s); db->sval[j] = s; } else { if (!db->dval) (*TI->ColAlloc)(TI, db-db0, 0); db->dval[j] = dv[i]; } } } else { if (TI->nrows >= TI->maxrows) (*TI->AdjustMaxrows)(TI, 2*TI->maxrows); j = (int)TI->nrows++; for(i = 0; i < ti.nf; i++) { db = db0 + z[i]; if ((s = sv[i])) { if (!db->sval) (*TI->ColAlloc)(TI, z[i], 1); if (s != Missing) s = ti_copy(&ti, s); db->sval[j] = s; } else { if (!db->dval) (*TI->ColAlloc)(TI, z[i], 0); db->dval[j] = dv[i]; } } for(i = 0; i < nh; i++) { db = db0 + h[i]; if (!db->sval) (*TI->ColAlloc)(TI, h[i], 1); db->sval[j] = Missing; } } if (!a) { fclose(f); return 0; } } return k != 1; } #ifndef Long /* 32-bit int */ #define Long int #endif typedef struct BIntaryTable_header { char magic[12]; /* \0\nampl.bit\n\0 */ char arkind[4]; /* kind of arithmetic: "1\0\0\0" or "2\0\0\0" */ /* for little- or big-endian IEEE arithmetic */ Long arity; Long ncols; /* not counting first arity columns */ Long nrows; Long nrcols; /* number of real columns (nonzero DbCol.dval) */ Long nscols; /* number of symbolic columns (nonzero DbCol.sval) */ /* nrcols + nscols >= arity + ncols */ Long strtablen; /* length of string table following this header */ /* String table starts with an array of 1-character */ /* column descriptors: */ /* '1' = just real, '2' = just string, '3' = both. */ /* Then come arity + ncols column names. */ /* Then come the columns; for type '3', the real */ /* column comes first. String columns are arrays */ /* of Longs, 0 = NULL, 1 = Missing, >= 2 ==> next */ /* string table entry. */ } BIntaryTable_header; static int fwr(void *v, size_t vlen, Tinfo *ti, char *what) { AmplExports *ae; TableInfo *TI; int len; ae = ti->ae; if (fwrite((const char*)v,vlen, 1, ti->f) == 1) return 0; fclose(ti->f); TI = ti->TI; len = strlen(ti->tname) + strlen(what); sprintf(TI->Errmsg = (char*)TM(len + 32), "could not write %s to file %s.", what, ti->tname); return DB_Error; } static char magic[12] = "\0\nampl.bit\n"; int Write_ampl_bit(AmplExports *ae, TableInfo *TI) { BIntaryTable_header bh; DbCol *db, *dbe; FILE *f; Long *Lt, *Lt0, ns; Tinfo ti; char *Missing, *s, *s0, *s1, *se, **sp, **sp1; int i, nc; size_t Ld, Ls; unsigned long nr1; if (!(ti.tname = tabnamebit(TI))) return DB_Refuse; if (TI->flags & DBTI_flags_IN && (f = fopen(ti.tname,"rb")) && Adjust_ampl_bit(ae, TI, f, ti.tname)) return DB_Error; if (!(ti.f = fopen(ti.tname,"wb"))) return cantopen(ae,TI,ti.tname); ti.ae = ae; ti.TI = TI; memset(&bh, 0, sizeof(bh)); memcpy(bh.magic, magic, 12); i = sprintf(bh.arkind, "%d", Arith_Kind_ASL); while(i < 4) bh.arkind[i++] = 0; bh.arity = TI->arity; bh.ncols = TI->ncols; bh.nrows = (Long)TI->nrows; bh.nrcols = bh.nscols = 0; nc = (int)(bh.arity + bh.ncols); db = TI->cols; for(dbe = db + nc; db < dbe; db++) { if (db->dval) bh.nrcols++; if (db->sval) bh.nscols++; } bh.strtablen = 2*nc + 1; sp = TI->colnames; sp1 = sp + nc; while(sp < sp1) bh.strtablen += strlen(*sp++); Missing = TI->Missing; if (bh.nscols) { for(db = TI->cols; db < dbe; db++) if ((sp = db->sval)) for(sp1 = sp + bh.nrows; sp < sp1;) if ((s = *sp++) && s != Missing) bh.strtablen += strlen(s) + 1; } nr1 = bh.nrows; if (nr1 & 1) nr1++; bh.strtablen = (bh.strtablen + 7) & ~7; if (bh.nscols) { Lt0 = (Long*)TM(nr1*sizeof(Long)); Lt0[nr1-1] = 0; /* in case it's not otherwise set */ } s = s0 = (char*)TM(bh.strtablen); se = s + bh.strtablen; for(db = TI->cols; db < dbe; db++) { i = db->dval ? '1' : '0'; if (db->sval) i += 2; *s++ = i; } *s++ = 0; sp = TI->colnames; for(db = TI->cols; db < dbe; db++) for(s1 = *sp++; (*s++ = *s1); s1++); if (bh.nscols) { for(db = TI->cols; db < dbe; db++) if ((sp = db->sval)) for(sp1 = sp + bh.nrows; sp < sp1;) if ((s1 = *sp++) && s1 != Missing) while((*s++ = *s1)) s1++; } while(s < se) *s++ = 0; if (fwr(&bh, sizeof(bh), &ti, "header") || fwr(s0, bh.strtablen, &ti, "strings")) return DB_Error; Ld = bh.nrows * sizeof(real); Ls = (size_t)(nr1 * sizeof(Long)); ns = nc; for(db = TI->cols; db < dbe; db++) { if (db->dval) if (fwr(db->dval, Ld, &ti, "real column")) return DB_Error; if ((sp = db->sval)) { Lt = Lt0; for(sp1 = sp + bh.nrows; sp < sp1; Lt++) { *Lt = 0; if ((s = *sp++)) *Lt = s == Missing ? 1 : ++ns; } if (fwr(Lt0, Ls, &ti, "string column")) return DB_Error; } } fclose(ti.f); return DB_Done; } static int frd(void *v, size_t vlen, Tinfo *ti, char *what) { AmplExports *ae; TableInfo *TI; int len; ae = ti->ae; if (fread(v, vlen, 1, ti->f) == 1) return 0; TI = ti->TI; len = strlen(ti->tname) + strlen(what); sprintf(TI->Errmsg = (char*)TM(len + 32), "could not read %s from file %s.", what, ti->tname); fclose(ti->f); return DB_Error; } static void Lswap(Long *x, size_t L) { char *s = (char*)x; char *se = s + L; char *s1, *s2; int i; while(s < se) { s1 = s; s2 = s + 3; s = s2 + 1; do { i = *s1; *s1++ = *s2; *s2-- = i; } while(s1 < s2); } } static void rswap(real *x, size_t L) { char *s = (char*)x; char *se = s + L; char *s1, *s2; int i; while(s < se) { s1 = s; s2 = s + 7; s = s2 + 1; do { i = *s1; *s1++ = *s2; *s2-- = i; } while(s1 < s2); } } static void headswap(BIntaryTable_header *bh) { Lswap(&bh->arity, 6*sizeof(Long)); } int Read_ampl_bit(AmplExports *ae, TableInfo *TI) { BIntaryTable_header bh; DbCol *db; Long *si, *sie, *sinfo; Tinfo ti; char buf[64], *ct, *s, **sp, **sp1, *stab, *tbuf; int a, bswap, i, j, je, k, nc, nr, ns, *p, rs, *z, *zs; real *r, *r1, x; size_t Lr, Ls, nr1; if (!(ti.tname = tabnamebit(TI))) return DB_Refuse; if (!(ti.f = fopen(ti.tname,"rb"))) return cantopen(ae,TI,ti.tname); ti.ae = ae; ti.TI = TI; if (frd(&bh, sizeof(bh), &ti, "header")) return DB_Error; bswap = 0; x = strtod(bh.arkind,&s); if (x != Arith_Kind_ASL) { if (x == 3. - Arith_Kind_ASL) { bswap = 1; headswap(&bh); } else { sprintf(buf, "header gives unexpected arkind = %g", x); return readerr(&ti,buf); } } ti.nf = (int)(bh.arity + bh.ncols); a = (int)bh.arity; if (a != TI->arity) { sprintf(buf, "header gives arity %d rather than %d", a, TI->arity); return readerr(&ti, buf); } nc = TI->ncols; if (ti.nf < nc) { sprintf(buf, "header says only %d columns; expected at least %d", a + ti.nf, a + nc); return readerr(&ti, buf); } ti.fields = (char**)TM(bh.strtablen + ti.nf*sizeof(char*)); s = ct = stab = (char*)(ti.fields + ti.nf); if (frd(stab, bh.strtablen, &ti, "string table")) return DB_Error; while(*s++); for(i = 0; i < ti.nf; i++) { ti.fields[i] = s; while(*s++); } /* Allow file columns to be permuted */ p = (int*)TM((3*ti.nf + 1)*sizeof(int)); z = p + ti.nf; if ((i = permute(a, a, p, TI->colnames, ti.fields, z, 0))) { badfield: s = TI->colnames[--i]; sprintf(tbuf = (char*)TM(strlen(s) + 32), "Column %Q not found.", s); return readerr(&ti, tbuf); } if (nc) { if ((i = permute(nc, ti.nf - a, p+a, TI->colnames + a, ti.fields + a, z, 0))) { i += a; goto badfield; } for(i = 0; i < nc; i++) p[i+a] += a; } nc += a; zs = z + ti.nf; for(i = 0; i < ti.nf; i++) zs[i] = 0; /* This would allow duplicate column names. */ for(i = 0; i < nc; i++) zs[p[i]]++; for(i = j = 0; i < ti.nf; i++) j = zs[i] += j; zs[ti.nf] = nc; for(i = 0; i < nc; i++) z[--zs[p[i]]] = i; nr = ns = 0; for(i = j = 0; i < ti.nf;) { je = zs[++i]; while(j < je) { k = z[j++]; k = ct[k] - '0'; if (k & 1) nr++; if (k & 2) ns++; } } nr1 = (size_t) bh.nrows; if (bh.nrcols) { Lr = nr1 * sizeof(real); if (nr < bh.nrcols) nr++; /* elbow room for omitting columns */ r = (real*)TM(Lr * nr); } if (bh.nscols) { if (nr1 & 1) nr1++; Ls = nr1 * sizeof(Long); sinfo = (Long*)TM(nr1*sizeof(Long)); sie = sinfo + bh.nrows; if (ns) sp = (char**)TM(bh.nrows*ns*sizeof(char**)); } for(i = j = k = 0;;) { je = zs[++i]; rs = *ct++ - '0'; r1 = 0; sp1 = 0; if (rs & 1) { if (frd(r1 = r, Lr, &ti, "real data")) return DB_Error; if (bswap) rswap(r1, Lr); } if (rs & 2) { if (frd(si = sinfo, Ls, &ti, "symbol pointers")) return DB_Error; if (j >= je) continue; if (bswap) Lswap(si, Ls); sp1 = sp; while(si < sie) switch(*si++) { case 0: *sp++ = 0; break; case 1: *sp++ = TI->Missing; break; default: *sp++ = s; while(*s++); } } if (r1 && j < je) r += bh.nrows; while(j < je) { db = TI->cols + z[j++]; db->dval = r1; db->sval = sp1; if (++k >= nc) goto done; } } done: fclose(ti.f); if ((*TI->AddRows)(TI, TI->cols, bh.nrows)) return DB_Error; return DB_Done; } static int Adjust_ampl_bit(AmplExports*ae, TableInfo *TI, FILE *f, char *tname) { BIntaryTable_header bh; DbCol *db, *db1, *dbh; Long *si, *sie, *sinfo; Tinfo ti; char *Missing, buf[64]; char *ct, *s, *s1, **sp, **sp0, **sp00, **sp1, **spi, *stab; int a, bswap, i, j, j1, k, nc, nc0, nh, nn, nnr; int nor, nr, nr0, nrf, ns, rs; int *h, *p, *pe1, *q, *qh, *qn, *z, *zs; real *r, *r0, *r00, *r1, x; size_t Lr, Ls, nr1; ti.ae = ae; ti.TI = TI; ti.tname = tname; ti.f = f; if (frd(&bh, sizeof(bh), &ti, "header")) return DB_Error; bswap = 0; x = strtod(bh.arkind,&s); if (x != Arith_Kind_ASL) { if (x == 3. - Arith_Kind_ASL) { bswap = 1; headswap(&bh); } else { sprintf(buf, "header gives unexpected arkind = %g", x); return readerr(&ti,buf); } } ti.nf = (int)(bh.arity + bh.ncols); a = (int)bh.arity; if (a != TI->arity) { sprintf(buf, "header gives arity %d rather than %d", a, TI->arity); return readerr(&ti, buf); } nc = TI->ncols; if (ti.nf < nc) { sprintf(buf, "header says only %d columns; expected at least %d", a + ti.nf, a + nc); return readerr(&ti, buf); } ti.fields = (char**)TM(bh.strtablen + ti.nf*sizeof(char*)); s = ct = stab = (char*)(ti.fields + ti.nf); if (frd(stab, bh.strtablen, &ti, "string table")) return DB_Error; while(*s++); for(i = 0; i < ti.nf; i++) { ti.fields[i] = s; while(*s++); } /* Allow file columns to be permuted */ k = ti.nf + nc; dbh = (DbCol*)TM(a*sizeof(DbCol) + (3*k + 1 + nc)*sizeof(int)); p = (int*)(dbh + a); h = p + k; z = h + nc; if ((i = permute(a, a, p, TI->colnames, ti.fields, z, 0))) { s = TI->colnames[--i]; sprintf(TI->Errmsg = (char*)TM(strlen(s) + 32), "Column %Q not found.", s); return DB_Error; } nh = 0; pe1 = p + a; nn = 0; if (nc) { permute(nc, ti.nf - a, pe1, TI->colnames + a, ti.fields + a, z, &nh); for(i = 0; i < nc; i++) if (pe1[i] >= 0) pe1[i] += a; else h[nn++] = i + a; } nc0 = nc += a; for(i = 0; i < ti.nf; i++) z[i] = -1; for(i = 0; i < nc; i++) if ((j = p[i]) >= 0) z[j] = i; zs = z + ti.nf; for(i = a; i < nc; i++) zs[i] = -1; for(i = a; i < ti.nf; i++) if ((j = z[i]) < 0) z[i] = nc++; /* columns in bh but not in TI */ nr = ns = 0; for(i = 0; i < a;) { k = z[i++]; k = ct[k] - '0'; if (k & 1) nr++; if (k & 2) ns++; } nr1 = (size_t) bh.nrows; if (bh.nrcols) { Lr = nr1 * sizeof(real); if (!nr) nr = 1; r = r00 = (real*)TM(Lr * nr); } if (bh.nscols) { if (nr1 & 1) nr1++; Ls = nr1 * sizeof(Long); sinfo = (Long*)TM(nr1*sizeof(Long)); sie = sinfo + bh.nrows; if (!ns) ns = 1; sp = sp00 = (char**)TM(bh.nrows*ns*sizeof(char**)); } Missing = TI->Missing; for(i = 0; i < a; i++) { rs = *ct++ - '0'; r1 = 0; sp1 = 0; if (rs & 1) { if (frd(r1 = r, Lr, &ti, "real data")) return DB_Error; if (bswap) rswap(r1, Lr); } if (rs & 2) { if (frd(si = sinfo, Ls, &ti, "symbol pointers")) return DB_Error; if (bswap) Lswap(si, Ls); sp1 = sp; while(si < sie) switch(*si++) { case 0: *sp++ = 0; break; case 1: *sp++ = Missing; break; default: *sp++ = s; while(*s++); } } db = dbh + p[i]; if ((db->dval = r1)) r += bh.nrows; db->sval = sp1; } nrf = (int)bh.nrows; nr = nr0 = (int)TI->nrows; r0 = (real*)TM(a*(sizeof(real) + sizeof(char*)) + (nrf + nr)*sizeof(int)); memset(r0, 0, a*sizeof(real)); sp0 = (char**)(r0 + a); q = (int*)(sp0 + a); qh = q + nrf; for(i = 0; i < nr; i++) qh[i] = -1; for(i = 0; i < nrf; i++) { r1 = 0; sp1 = 0; db = dbh; for(j = 0; j < a; j++, db++) { if ((spi = db->sval) && (s1 = spi[i]) && s1 != Missing) { if (!sp1) memset(sp1 = sp0, 0, a*sizeof(char*)); sp1[j] = s1; } if (db->dval) { r1 = r0; r1[j] = db->dval[i]; } } if ((j = (*TI->Lookup)(r1, sp1, TI)) >= 0) qh[q[i] = j] = i; else q[i] = nr++; } if (nr > TI->maxrows) (*TI->AdjustMaxrows)(TI, nr); if ((nnr = nr - nr0)) { qn = (int*)TM(nnr*sizeof(int)); for(i = j = 0; i < nrf; i++) if (q[i] >= nr0) { qn[j++] = i; db = dbh; db1 = TI->cols; for(k = 0; k < a; k++, db++, db1++) { if ((spi = db->sval) && (s1 = spi[i]) && s1 != Missing) { if (!(sp1 = db1->sval)) sp1 = (char**) (*TI->ColAlloc)(TI, k, 1); sp1[q[i]] = s1; continue; } if (!(r1 = db1->dval)) r1 = (real*) (*TI->ColAlloc)(TI, k, 0); r1[q[i]] = db->dval[i]; } } TI->nrows = nr; dbh = TI->cols; for(i = 0; i < nn; i++) { db = dbh + (j = h[i]); if (!(sp1 = db->sval)) sp1 = (char**)(*TI->ColAlloc)(TI, j, 1); for(j = nr0; j < nr; j++) sp1[j] = Missing; } } if (nc > nc0) { db1 = (DbCol*)TM(nc*(sizeof(DbCol)+sizeof(char*))); sp1 = (char**)(db1 + nc); memcpy(db1, TI->cols, nc0*sizeof(DbCol)); memset(db1+nc0, 0, (nc-nc0)*sizeof(DbCol)); memcpy(sp1, TI->colnames, nc0*sizeof(char*)); TI->cols = db1; TI->colnames = sp1; j = 0; for(i = nc0; i < nc; i++) { while(z[j] < nc0) j++; sp1[i] = ti.fields[j++]; } TI->ncols = nc - a; } else if (!nnr) return 0; nor = 0; for(i = 0; i < nr0; i++) if (qh[i] < 0) qh[nor++] = i; dbh = TI->cols; if (nor) for(i = nc0; i < nc; i++) { db = dbh + i; if (!(sp1 = db->sval)) sp1 = (char**)(*TI->ColAlloc)(TI, i, 1); for(j = 0; j < nor; j++) sp1[qh[j]] = Missing; } r = r00; sp = sp00; for(i = a; i < ti.nf; i++) { rs = *ct++ - '0'; j = z[i]; db = dbh + j; if (rs & 1) { if (frd(r, Lr, &ti, "real data")) return DB_Error; if (bswap) rswap(r, Lr); if (j >= nc0 || nnr) { if (!(r1 = db->dval)) r1 = (real*)(*TI->ColAlloc)(TI, j, 0); k = 0; if (j >= nc0) for(; k < nrf; k++) r1[q[k]] = r[k]; else for(; k < nnr; k++) { j1 = qn[k]; r1[q[j1]] = r[j1]; } } } if (rs & 2) { if (frd(si = sinfo, Ls, &ti, "symbol pointers")) return DB_Error; if (j < nc0 && !nnr) continue; if (bswap) Lswap(si, Ls); if (!(sp1 = db->sval)) sp1 = (char**)(*TI->ColAlloc)(TI, j, 1); sp = sp00; while(si < sie) switch(*si++) { case 0: *sp++ = 0; break; case 1: *sp++ = TI->Missing; break; default: *sp++ = s; while(*s++); } k = 0; if (j >= nc0) for(; k < nrf; k++) sp1[q[k]] = sp00[k]; else for(; k < nnr; k++) { j1 = qn[k]; sp1[q[j1]] = sp00[j1]; } } } return 0; } void funcadd(AmplExports *ae) { static char bitdesc[] = "lib-bit\n" "Library file.bit (binary table) handler: one or two strings\n" "(an option 'lib-bit' and the file name, ending in \".bit\")\n" "expected before \":[...]\"."; static char tabdesc[] = "lib-tab\n" "Library file.tab (ASCII table) handler: at most one or two strings\n" "(an optional 'lib-tab' and the file name, ending in \".tab\")\n" "expected before \":[...]\";\n" "table_name.tab is assumed if there are no strings or only 'lib-tab'."; add_table_handler(Read_ampl_tab, Write_ampl_tab, tabdesc, 0, 0); add_table_handler(Read_ampl_bit, Write_ampl_bit, bitdesc, 0, 0); }