// saved from url
// http://www-cs.canisius.edu/PL_TUTORIALS/C++/EXAMPLES/MRD/SRC/relation.c -->
//---------------------------------- relation.c ----------------------------
#include
#include
#include
#include "relation.h"
#define min(a,b) ((a)<(b)?(a):(b));
static int contains_blank (char *somestring)
{
while (*somestring)
if (*somestring++ == ' ') return 1;
return 0;
}
void relation::info ()
{
printf ("Relation loaded from file: %s\n", relfilename);
printf ("Number of records = %d\n", numrecords);
for (int i=0; inumfields; i++)
dest->addfield (source->dict[i].name, source->dict[i].position,
source->dict[i].datatype, source->dict[i].printwidth);
}
// The next version is used only for cross product and ejoin. It deals with
// the problem that the two relations may have the same field names. The
// solution is to see if the same field name appears in both relations, and
// if it does, then the second relation's field is renamed using _fieldname.
// This relies on the hope that no one used THAT name in the first relation
// or in the second. If they did, the field just can't be found.
void relation::copydict2 (relation *source1, relation *source2, relation *dest)
{
// First copy the first relation's fields over
for (int i=0; inumfields; i++)
dest->addfield (source1->dict[i].name, source1->dict[i].position,
source1->dict[i].datatype, source1->dict[i].printwidth);
// Now copy the second's over, but for each one search the dictionary
// of the first relation, and if found then rename
for (int i=0; inumfields; i++) {
int found = 0;
for (int j=0; jnumfields; j++)
if (strcmp(source1->dict[j].name, source2->dict[i].name) == 0) {
found = 1;
break;
}
if (found) {
char temp[60];
strcpy (temp, "_");
strcat (temp, source2->dict[i].name);
dest->addfield (temp,
source2->dict[i].position + source1->numfields,
source2->dict[i].datatype,
source2->dict[i].printwidth);
}
else
dest->addfield (source2->dict[i].name,
source2->dict[i].position + source1->numfields,
source2->dict[i].datatype,
source2->dict[i].printwidth);
}
}
int relation::xmatch1c (parseline *x, int pos, char *comparison,
char *fieldvalueC)
{
if (strcmp (comparison, "=") == 0)
return (strcmp (x->word(pos), fieldvalueC)==0);
if (strcmp (comparison, "!=") == 0)
return (strcmp (x->word(pos), fieldvalueC)!=0);
if (strcmp (comparison, "<") == 0)
return (strcmp (x->word(pos), fieldvalueC)<0);
if (strcmp (comparison, "<=") == 0)
return (strcmp (x->word(pos), fieldvalueC)<=0);
if (strcmp (comparison, ">") == 0)
return (strcmp (x->word(pos), fieldvalueC)>0);
if (strcmp (comparison, ">=") == 0)
return (strcmp (x->word(pos), fieldvalueC)>=0);
}
int relation::xmatch1i (parseline *x, int pos, char *comparison,
char *fieldvalueC)
{
if (strcmp (comparison, "=") == 0)
return atoi(x->word(pos)) == atoi(fieldvalueC);
if (strcmp (comparison, "!=") == 0)
return atoi(x->word(pos)) != atoi(fieldvalueC);
if (strcmp (comparison, "<") == 0)
return atoi(x->word(pos)) < atoi(fieldvalueC);
if (strcmp (comparison, "<=") == 0)
return atoi(x->word(pos)) <= atoi(fieldvalueC);
if (strcmp (comparison, ">") == 0)
return atoi(x->word(pos)) > atoi(fieldvalueC);
if (strcmp (comparison, ">=") == 0)
return atoi(x->word(pos)) >= atoi(fieldvalueC);
}
int relation::xmatch1d (parseline *x, int pos, char *comparison,
char *fieldvalueC)
{
if (strcmp (comparison, "=") == 0)
return atof(x->word(pos)) == atof(fieldvalueC);
if (strcmp (comparison, "!=") == 0)
return atof(x->word(pos)) != atof(fieldvalueC);
if (strcmp (comparison, "<") == 0)
return atof(x->word(pos)) < atof(fieldvalueC);
if (strcmp (comparison, "<=") == 0)
return atof(x->word(pos)) <= atof(fieldvalueC);
if (strcmp (comparison, ">") == 0)
return atof(x->word(pos)) > atof(fieldvalueC);
if (strcmp (comparison, ">=") == 0)
return atof(x->word(pos)) >= atof(fieldvalueC);
}
int relation::match1 (parseline *x, spec comp)
{
int n = findfield (comp.fieldname);
if (n == -1) return 0;
int pos = dict[n].position;
// See if the given field name even exists
switch (dict[n].datatype) {
case 'c': return xmatch1c (x, pos, comp.comparison, comp.fieldvalueC);
case 'i': return xmatch1i (x, pos, comp.comparison, comp.fieldvalueC);
case 'd': return xmatch1d (x, pos, comp.comparison, comp.fieldvalueC);
}
}
int relation::match (parseline *x, speclist comps)
{
int truth = 0;
for (int i=0; imakeline(line);
newone->addrecord (line);
}
return newone;
}
void relation::append (relation *other)
{
// What if the dictionaries do not match????
for (int i=0; inumrecords; i++) {
char line[2000];
other->records[i]->makeline(line);
addrecord (line);
}
}
relation* relation::copy ()
{
relation *newone;
newone = new relation();
copydict (this, newone);
for (int i=0; imakeline(line);
newone->addrecord (line);
}
return newone;
}
void relation::makewidthline (char *line)
{
line[0] = 0;
for (int i=0; iaddfield (names.word(j), j, dict[k].datatype,
dict[k].printwidth);
}
for (int i=0; iword(pos))) {
strcat (line, "\"");
strcat (line, records[i]->word(pos));
strcat (line, "\" ");
}
else {
strcat (line, records[i]->word(pos));
strcat (line, " ");
}
}
newone->addrecord(line);
}
return newone;
}
// For the default case of adding a record, we just make a copy of the
// previous record.
void relation::addrecord ()
{
char temp[2000];
records[numrecords-1]->makeline(temp);
addrecord (temp);
}
void relation::addrecord (char *someline)
{
parseline *newrec = new parseline (someline);
if (numrecords >= MAXRECORDS) return; // later set error code
numrecords++;
// check for consistency in number of fields???
records[numrecords-1] = newrec;
}
void relation::addfield (char *name, int pos, char type, int width)
{
if (numfields >= MAXFIELDS) return;
strcpy (dict[numfields].name, name); // check to see if already there??
dict[numfields].datatype = type;
dict[numfields].position = pos;
dict[numfields].printwidth = width;
numfields++;
}
void relation::load (char *filename)
{
FILE *fp;
char line[1000];
string fname(filename);
if ((fp = fopen (filename, "r")) == NULL) {
fname += ".txt";
if ((fp = fopen (fname.data(), "r")) == NULL ) {
printf ("Cannot open file %s\n", filename);
printf ("Cannot open file %s\n", fname.data());
return;
}
}
numfields = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
line[strlen(line)-1] = 0;
if (line[0] == '#') break;
parseline pline(line);
addfield (pline.word(0), numfields, pline.word(1)[0],
atoi(pline.word(2)));
}
// Now load the data records
numrecords = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
line[strlen(line)-1] = 0;
addrecord(line);
}
fclose (fp);
strcpy (relfilename, fname.data());
}
void relation::save (char *filename)
{
FILE *fp;
string fname(filename);
fname += ".txt";
if ((fp = fopen (fname.data(), "w")) == NULL) {
printf ("Cannot open file %s for saving\n", fname.data());
return;
}
// Save the data dictionary and make up the width list for saving the
// line.
char widths[500];
widths[0] = 0;
for (int i=0; ifindfield (whichf1);
if (n == -1) return (relation *) NULL;
which1 = this->dict[n].position;
n = other->findfield (whichf2);
if (n == -1) return (relation *) NULL;
which2 = other->dict[n].position;
newone = new relation();
copydict2 (this, other, newone);
for (int i=0; i < this->numrecords; i++)
for (int j=0; j< other->numrecords; j++)
if (strcmp(this->records[i]->word(which1),
other->records[j]->word(which2)) == 0) {
this->records[i]->makeline(newline);
strcat (newline, " ");
other->records[j]->makeline(newline2);
strcat (newline, newline2);
newone->addrecord (newline);
}
return newone;
}
relation* relation::cross (relation *other)
{
char newline[5000], newline2[2000];
relation *newone;
newone = new relation();
copydict2 (this, other, newone);
for (int i=0; i < this->numrecords; i++)
for (int j=0; j < other->numrecords; j++) {
this->records[i]->makeline(newline);
strcat (newline, " ");
other->records[j]->makeline(newline2);
strcat (newline, newline2);
newone->addrecord (newline);
}
return newone;
}
static int greaterthan (char *s1, char *s2, char datatype)
{
switch (datatype) {
case 'c': return strcmp(s1, s2) > 0;
case 'i': return atoi(s1) > atoi(s2);
case 'd': return atof(s1) > atof(s2);
}
}
void relation::sort (char *whichfield, bool ascending)
{
int n = findfield(whichfield);
if (n == -1) return;
int pos = dict[n].position;
char datatype = dict[n].datatype;
for (int i=0; iword(pos),
records[j]->word(pos), datatype);
if (ascending && temp) {
parseline *temp = records[i];
records[i] = records[j];
records[j] = temp;
}
if (!ascending && !temp) {
parseline *temp = records[i];
records[i] = records[j];
records[j] = temp;
}
}
}
void relation::sort ()
{
for (int i=0; imakeline(line1);
records[j]->makeline(line2);
if (strcmp (line1, line2) > 0) {
parseline *temp = records[i];
records[i] = records[j];
records[j] = temp;
}
}
}
relation* relation::istats (int pos)
{
relation *newone;
char templine[1000];
int sum=0, n, max, min;
double avg, sumsq=0.0;
for (int i=0; iword(pos));
sum += n;
if (i==0) max = n;
if (i==0) min = n;
if (n > max) max = n;
if (n < min) min = n;
}
avg = (float)sum/numrecords;
for (int i=0; iword(pos));
sumsq += (avg - n) * (avg - n);
}
sprintf (templine, "%d %d %d %d %.5f %.5f",
numrecords, sum, min, max, avg, sumsq);
newone = new relation();
newone->addfield ("num", 0, 'i', 9); // number of records in relation
newone->addfield ("sum", 1, 'i', 9); // sum of the stat field's values
newone->addfield ("min", 2, 'i', 9); // min value of stat field
newone->addfield ("max", 3, 'i', 9); // max value of stat field
newone->addfield ("avg", 4, 'd', 9); // average (floating point)
newone->addfield ("ssq", 5, 'd', 9); // sum of squares
newone->addrecord (templine);
return newone;
}
relation* relation::dstats (int pos)
{
relation *newone;
char templine[1000];
double sum=0, n, max, min;
double avg, sumsq=0.0;
for (int i=0; iword(pos));
sum += n;
if (i==0) max = n;
if (i==0) min = n;
if (n > max) max = n;
if (n < min) min = n;
}
avg = sum/numrecords;
for (int i=0; iword(pos));
sumsq += (avg - n) * (avg - n);
}
sprintf (templine, "%d %.5f %.5f %.5f %.5f %.5f",
numrecords, sum, min, max, avg, sumsq);
newone = new relation();
newone->addfield ("num", 0, 'i', 9); // number of records in relation
newone->addfield ("sum", 1, 'd', 9); // sum of the stat field's values
newone->addfield ("min", 2, 'd', 9); // min value of stat field
newone->addfield ("max", 3, 'd', 9); // max value of stat field
newone->addfield ("avg", 4, 'd', 9); // average
newone->addfield ("ssq", 5, 'd', 9); // sum of squares
newone->addrecord (templine);
return newone;
}
relation* relation::summarystats (char *whichfield)
{
int n = findfield(whichfield);
if (n == -1) return (relation *) NULL;
if (dict[n].datatype == 'i')
return istats (dict[n].position);
else if (dict[n].datatype == 'd')
return dstats (dict[n].position);
}
relation* relation::addseqnums ()
{
relation *newone;
char temp[1000];
newone = new relation();
// Make new data dictionary
newone->addfield ("seqnum", 0, 'i', 6);
for (int i=0; iaddfield (dict[i].name, i+1, dict[i].datatype,
dict[i].printwidth);
for (int i=0; imakeline(line);
sprintf (temp, "%d %s", i, line);
newone->addrecord (temp);
}
return newone;
}
void relation::showdict ()
{
for (int i=0; iword(i));
}
static int sametuples (parseline *x, parseline *y)
{
if (x->size() != y->size()) return 0;
for (int i=0; isize(); i++)
if (strcmp (x->word(i), y->word(i)) != 0) return 0;
return 1;
}
void relation::unique ()
{
if (numrecords < 2) return;
sort();
int nums[1000], numdels=0;
int i=0, j=1;
for (j=1; jword(dict[n].position), value) == 0)
return i;
}
int relation::findrecord (char *fieldname, int value)
{
int n = findfield (fieldname);
if (n == -1) return 0;
for (int i=0; iword(dict[n].position)) == value)
return i;
}
int relation::findrecord (char *fieldname, double value)
{
int n = findfield (fieldname);
if (n == -1) return 0;
for (int i=0; iword(dict[n].position)) == value)
return i;
}
void relation::setcurrent (int recnum)
{
if (recnum >= 0 && recnum < numrecords)
current = recnum;
}
void relation::advance ()
{
if (current < numrecords-1) current++;
}
bool relation::atend ()
{
return (current == numrecords-1);
}
char* relation::getvalueC (char *fieldname)
{
int n = findfield (fieldname);
if (n == -1) return (char *) NULL;
return records[current]->word(dict[n].position);
}
int relation::getvalueI (char *fieldname)
{
int n = findfield (fieldname);
if (n == -1) return 0;
return atoi(records[current]->word(dict[n].position));
}
double relation::getvalueD (char *fieldname)
{
int n = findfield (fieldname);
if (n == -1) return 0.0;
return atof(records[current]->word(dict[n].position));
}
void relation::formatvalue (int recnum, int fieldnum, char *result,
int resultmaxlen)
{
char thisvalue[1000];
char format[100];
int cb, len, j;
strcpy (thisvalue, records[recnum]->word(dict[fieldnum].position));
int width = dict[fieldnum].printwidth;
for (int i=0; isetword (dict[n].position, newvalueC);
}
void relation::setvalue (char *fieldname, int newvalueI)
{
int n = findfield (fieldname);
if (n == -1) return;
char newvalueC[100];
sprintf (newvalueC, "%d", newvalueI);
records[current]->setword (dict[n].position, newvalueC);
}
void relation::setvalue (char *fieldname, double newvalueD)
{
int n = findfield (fieldname);
if (n == -1) return;
char newvalueC[100];
sprintf (newvalueC, "%f", newvalueD);
records[current]->setword (dict[n].position, newvalueC);
}
void relation::changefieldname (char *fieldname, char *newname)
{
int n = findfield (fieldname);
if (n == -1) return;
strcpy (dict[n].name, newname);
}
void relation::changefieldwidth (char *fieldname, int newwidth)
{
int n = findfield (fieldname);
if (n == -1) return;
dict[n].printwidth = newwidth;
}
int relation::getfieldwidth (char *fieldname)
{
int n = findfield (fieldname);
if (n == -1) return -1;
return dict[n].printwidth;
}
void relation::delrecord (int recnum)
{
if (recnum >= numrecords) return;
delete records[recnum];
for (int i=recnum; imakeline(temp);
strcat (temp, small);
strcat (temp, newvalue);
delete records[recnum];
records[recnum] = new parseline (temp);
}
char relation::getfieldtype (char *fieldname)
{
int n = findfield (fieldname);
if (n == -1) return -1;
return dict[n].datatype;
}
relation* relation::difference (char *field1, relation *other, char *field2)
{
int fn1, fn2;
if ((fn1 = findfield (field1)) == -1) return (relation *) NULL;
if ((fn2 = other->findfield (field2)) == -1) return (relation *) NULL;
relation *newrel = new relation();
copydict (this, newrel);
// Go through our records and try to find a record in other with
// same value for "whichfield." If not found, then add to a third
// relation.
char value[2000];
for (int i=0; iword(fn1));
for (int j=0; jnumrecords; j++)
if (strcmp (value, other->records[j]->word(fn2)) == 0) {
found = 1;
break;
}
if (!found) {
char line[2000];
records[i]->makeline(line);
newrel->addrecord (line);
}
}
return newrel;
}