// saved from url
// http://www-cs.canisius.edu/PL_TUTORIALS/C++/EXAMPLES/MRD/SRC/relation.c -->
//---------------------------------- relation.c ----------------------------
#include
#include
#include
using namespace std;
#include
#include // std::min template for safe comparisons
#include "relation.h"
bool validfilename(char * filename) {
int i;
const int length = strlen(filename);
for (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
int i;
for (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 (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);
throw "bad comparision!";
return -1;
}
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);
throw "bad comparision";
return -1;
}
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);
throw "Invalid Comparison";
return -1;
}
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);
default : throw "Invalid datatype"; return -1;
}
}
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[MAXDBLLINE];
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[MAXDBLLINE];
records[numrecords-1]->makeline(temp);
addrecord (temp);
}
void Relation::addrecord (char *someline)
{
Parseline *newrec = new Parseline (someline);
addrecord(newrec);
/* if (numrecords >= MAXRECORDS) return; // later set error code
numrecords++;
// check for consistency in number of fields???
records[numrecords-1] = newrec;
*/
}
void Relation::addrecord (Parseline *newrec)
{
if (numrecords >= MAXRECORDS) return; // later set error code
// check for consistency in number of fields???
if (numfields == newrec->size()) {
numrecords++;
records[numrecords-1] = newrec;
}
else
// throw RelationExcp("addrecord: number of fields not match");
throw "Relation::addrecord: number of fields not match";
}
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[MAXLINE];
if (filename == NULL) {
throw "Relation::load(): no filename";
}
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);
if (pline.size() == 4)
addfield (pline.word(0), numfields, pline.word(2)[0],
atoi(pline.word(3)));
else
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);
//
//
if (!validfilename(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[MAXWIDTHLIST];
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[MAXLINES], newline2[MAXDBLLINE];
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);
default : throw "Invalid datatype"; return -1;
}
}
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[MAXLINE];
int sum=0, n, max, min;
double avg, sumsq=0.0;
int i;
for (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 (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[MAXLINE];
double sum=0, n, max, min;
double avg, sumsq=0.0;
int i;
for (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 (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);
else
return (Relation *) NULL;
}
Relation* Relation::addseqnums ()
{
Relation *newone;
char temp[MAXLINE];
newone = new Relation();
// Make new data dictionary
newone->addfield ("seqnum", 0, 'i', 6);
int i;
for (i=0; iaddfield (dict[i].name, i+1, dict[i].datatype,
dict[i].printwidth);
for (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[MAXLINE], numdels=0;
int i=0, j=1;
for (j=1; jword(dict[n].position), value) == 0)
return i;
return 0;
}
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;
return 0;
}
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;
return 0;
}
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,
size_t resultmaxlen)
{
char thisvalue[MAXLINE];
char format[MAXWORD];
int cb, len, j;
strcpy (thisvalue, records[recnum]->word(dict[fieldnum].position));
int width = dict[fieldnum].printwidth;
int i;
for (i=0; isetword (dict[n].position, newvalueC);
}
void Relation::setvalue (char *fieldname, int newvalueI)
{
int n = findfield (fieldname);
if (n == -1) return;
char newvalueC[MAXWORD];
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[MAXWORD];
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[MAXDBLLINE];
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[MAXDBLLINE];
records[i]->makeline(line);
newrel->addrecord (line);
}
}
return newrel;
}