<html>

<head><script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- MyFirstUnitAd -->
<ins class="adsbygoogle"
     style="display:inline-block;width:970px;height:250px"
     data-ad-client="ca-pub-5778386704669218"
     data-ad-slot="1503492166"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

<meta http-equiv="Content-Language" content="zh-cn">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>Dependency</title>
</head>

<body>



<p align="left"><span lang="en-ca"><font size="6" color="#FF0000"><b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
Dependency(canonical)</b></font></span></p>

<div align="left">
  <pre><b><font color="#ff0000" size="5">A. <span lang="en-ca">Third</span> Edition</font></b></pre>
</div>
<div align="left">
  <pre><span lang="en-ca"><b>This is the third edition of my &quot;dependency&quot; and I think it is almost the perfect version of all because it </b></span></pre>
</div>
<div align="left">
  <pre><span lang="en-ca"><b>implements almost all functions you can name---canonical cover, decomposition, lossless-join-check.</b></span></pre>
</div>
<div align="left">
  <pre><b><font color="#ff0000" size="5"><span lang="en-ca">B</span>.<span lang="en-ca"><a name="problem"></a>The problem</span></font></b></pre>
</div>
<p ALIGN="LEFT"><span lang="en-ca"><font size="2"><b>&quot;Function dependency&quot; is a 
very important issue in database theory. And what's more, it is the essence of 
all.</b></font></span></p>
<p ALIGN="LEFT"><span lang="en-ca"><font size="2"><b>Because I think it is the 
key of knowledge: the world is described by relations and relations of 
relations.</b></font></span></p>
<div align="left">
  <p ALIGN="LEFT">　</div>
<div align="left">
  <b><font color="#ff0000" size="5"><span lang="en-ca"><a name="explain"></a>C</span>.<span lang="en-ca">The
  </span></font></b><span lang="en-ca"><font size="5" color="#FF0000"><b>idea of 
  program</b></font></span></div>
<div align="left">
  　</div>
<div align="left">
  <span lang="en-ca"><font size="2"><b>There is really little job to do when you 
  obtain the right tool or you make it for yourself like me. After I </b></font></span></div>
<div align="left">
  　</div>
<div align="left">
  <span lang="en-ca"><font size="2"><b>overloaded those operators of my Set 
  class, everything become so simple. </b></font></span></div>
<div align="left">
  　</div>
<div align="left">
  <span lang="en-ca"><font size="2"><b>There is only one thing I want to remind 
  you---the &quot;forEachSubSet&quot; function expects the parameter won't be </b></font>
  </span></div>
<div align="left">
  　</div>
<div align="left">
  <span lang="en-ca"><font size="2"><b>modified.&nbsp; </b></font></span></div>
<div align="left">
  　</div>
<div align="left">
  <pre><b><font color="#ff0000" size="5">D.<span lang="en-ca"><a name="Method"></a>The </span>major functions</font></b></pre>
</div>
<div align="left">
  <pre><span lang="en-ca"><b>For the function &quot;decomposition&quot;, I don't want to export an array of sets of the decomposition. The simple reason</b></span></pre>
</div>
<div align="left">
  <pre><span lang="en-ca"><b>is who would use it this way. So, I simply &quot;cout&quot; them.</b></span></pre>
</div>
<div align="left">
  <pre>　</pre>
</div>
<div align="left">
  <pre><b><font color="#ff0000" size="5"><span lang="en-ca">E</span>.</font></b><span lang="en-ca"><font size="5" color="#FF0000"><b>Further improvement</b></font></span></pre>
</div>
<div align="left">
  <pre>　</pre>
</div>
<div align="left">
  <pre><b><font color="#ff0000" size="5"><span lang="en-ca">F</span>.</font></b><span lang="en-ca"><font size="5" color="#FF0000"><b>File listing</b></font></span></pre>
</div>
<div align="left" style="width: 898; height: 86">
  <pre><font size="3"><b>1<span lang="en-ca">. rules.h </span></b></font></pre>
  <pre><span lang="en-ca"><font size="3"><b>2</b></font></span><font size="3"><b><span lang="en-ca">. rules.</span></b></font><span lang="en-ca"><font size="3"><b>cpp</b></font></span></pre>
  <pre><span lang="en-ca"><font size="3"><b>3</b></font></span><font size="3"><b><span lang="en-ca">. set.h</span></b></font></pre>
  <pre><span lang="en-ca"><font size="3"><b>4</b></font></span><font size="3"><b><span lang="en-ca">. set.</span></b></font><span lang="en-ca"><font size="3"><b>cpp</b></font></span></pre>
  <pre><span lang="en-ca"><font size="3"><b>5</b></font></span><font size="3"><b><span lang="en-ca">. main.cpp (main)</span></b></font></pre>
</div>
<div align="left">
  <pre><span lang="en-ca"><font size="3" color="#FF0000"><b>file name: set.h</b></font></span></pre>
</div>
<pre>#ifndef SET_H
#define SET_H

#include &lt;iostream&gt;
#include &lt;bitset&gt;

using namespace std;


const int MaxAttrNumber=20;

class Set
{
	//this is a long-pain for me, I have no other way to 
	//let compiler recognize this &quot;friend&quot; function outside declaration
	friend ostream&amp; operator&lt;&lt;(ostream&amp; out, const Set&amp; dummy)	
	{
		for (int i=0; i&lt;dummy.size; i++)
		{
			if (dummy.theSet.test(i))
			{
				out&lt;&lt;'1';
			}
			else
			{
				out&lt;&lt;'0';
			}
		}
		return out;
	}
private:
	bitset&lt;MaxAttrNumber&gt; theSet;
	int size;
	int current;
public:
	void setSize(const Set&amp; dummy);
	int getSize(){ return size;}
	int next(int current) const;
	int first() const;	
	int count() const;
	Set intersection(const Set&amp; dummy) const;
	Set unionSet(const Set&amp; dummy) const;
	Set difference(const Set&amp; dummy) const;
	
	//I am crazy about operator overloading!!!:)
	Set operator-(const Set&amp; dummy) const;
	Set operator+(const Set&amp; dummy) const;
	Set operator*(const Set&amp; dummy) const;
	void operator=(const Set&amp; dummy);
	bool operator==(const Set&amp; dummy) const;
	bool operator!=(const Set&amp; dummy) const;
	bool operator&gt;(const Set&amp; dummy) const;
	bool operator&gt;=(const Set&amp; dummy) const;
	bool operator&lt;(const Set&amp; dummy) const;
	bool operator&lt;=(const Set&amp; dummy) const;
	void set(int pos);
	void forEachSubSet(Set&amp; dummy) const;//must be called before &quot;eachSub()&quot;
	bool eachSub(Set&amp; dummy) const;
	void set();
	void reset(int pos);
	void reset();
	bool test(int pos) const;
	bool isIn(const Set&amp; dummy) const;
	void setSize(int theSize) {size=theSize;}
	Set(int theSize=10);
};

#endif</pre>
<pre>　</pre>
  <pre><span lang="en-ca"><font size="3" color="#FF0000"><b>file name: set.cpp</b></font></span></pre>
<pre>#include &quot;Set.h&quot;

bool Set::isIn(const Set&amp; dummy) const
{
	for (int i=0; i&lt;size; i++)
	{
		if (theSet.test(i))
		{
			if (!dummy.test(i))//here I use Set.test() instead of set.test()
			{
				return false;
			}
		}
	}
	return true;		
}

bool Set::test(int pos) const
{
	return (pos&lt;size&amp;&amp;theSet.test(pos));
}

//current=-1;//initialize to -1 to prepare for being called
int Set::next(int current) const
{
	for (int i=current+1; i&lt;size; i++)//include situation current&gt;=size
	{
		if (theSet.test(i))
		{
			return i;
		}
	}
	return -1;//not found
}

bool Set::operator !=(const Set&amp; dummy)const
{
	return !(this-&gt;operator ==(dummy));
}

bool Set::operator &lt;(const Set&amp; dummy)const
{
	return (this-&gt;isIn(dummy)&amp;&amp;this-&gt;operator !=(dummy));
}

bool Set::operator &lt;=(const Set&amp; dummy)const
{
	return isIn(dummy);
}

bool Set::operator &gt;(const Set&amp; dummy)const
{
	return !(this-&gt;operator &lt;=(dummy));
}

bool Set::operator &gt;=(const Set&amp; dummy)const
{
	return !(this-&gt;operator &lt;(dummy));
}

bool Set::operator ==(const Set&amp; dummy)const
{
	for (int i=0; i&lt;(size&gt;dummy.size?size:dummy.size); i++)
	{
		if (test(i)^dummy.test(i))
		{
			return false;
		}
	}
	return true;
}

void Set::setSize(const Set&amp; dummy)
{
	size=dummy.size;
}

void Set::operator =(const Set&amp; dummy)
{
	size=dummy.size;
	for (int i=0; i&lt;size; i++)
	{
		if (dummy.test(i))
		{
			theSet.set(i);
		}
		else
		{
			theSet.reset(i);
		}
	}
}


Set::Set(int theSize)
{
	size=theSize;
	reset();
}

void Set::reset()
{
	for (int i=0; i&lt;size; i++)
	{
		theSet.reset(i);
	}
}

void Set::reset(int pos)
{
	if (pos&lt;size)
	{
		theSet.reset(pos);
	}
}

void Set::set()
{
	theSet.set();
}

void Set::set(int pos)
{
	theSet.set(pos);
}
	
void Set::forEachSubSet(Set&amp; dummy) const
{
	dummy.size=size;
	dummy.reset();//emptyset
}


bool Set::eachSub(Set&amp; dummy) const
{
	int index=first();//starting from very first

	while (index!=-1)//not exceeding boundery
	{
		if (dummy.test(index))
		{
			dummy.reset(index);
			index=next(index);
		}
		else
		{
			dummy.set(index);
			if (dummy&lt;*this)
			{
				return true;
			}
		}
	}
	return false;
}

int Set::first()const
{
	return next(-1);
}

int Set::count()const
{
	return theSet.count();
}

Set Set::unionSet(const Set&amp; dummy) const
{
	Set result;
	result.size=size&gt;dummy.size?size:dummy.size;
	for (int i=0; i&lt;result.size; i++)
	{
		if (test(i)||dummy.test(i))
		{
			result.set(i);
		}
	}
	return result;//this is a temparory object;
}

Set Set::difference(const Set&amp; dummy) const
{
	Set result;
	result.size=size&gt;dummy.size?size:dummy.size;
	for (int i=0; i&lt;result.size; i++)
	{
		if (test(i)&amp;&amp;!dummy.test(i))
		{
			result.set(i);
		}
	}
	return result;
}

Set Set::operator +(const Set&amp; dummy) const
{
	return unionSet(dummy);
}

Set Set::operator -(const Set&amp; dummy) const
{
	return difference(dummy);
}

Set Set::intersection(const Set&amp; dummy) const
{
	Set result;
	result.size=size&lt;dummy.size?size:dummy.size;
	for (int i=0; i&lt;result.size; i++)
	{
		if (test(i)&amp;&amp;dummy.test(i))
		{
			result.set(i);
		}
	}
	return result;
}

Set Set::operator *(const Set&amp; dummy) const
{
	return intersection(dummy);
}

</pre>
<pre>
</pre>
<pre></pre>
  <pre><span lang="en-ca"><font size="3" color="#FF0000"><b>file name: rules.h </b></font></span></pre>
<pre>#include &lt;iostream&gt;
#include &lt;bitset&gt;
#include &quot;set.h&quot;

using namespace std;

//make it simple, the first line must list all variables or attributes
//&quot;relation&quot;(A,B,C...)
const int MaxNameLength=5;
const int MaxRuleNumber=100;
const int MaxKeyNumber=10;
const int MaxComposition=10;




class Rule
{
	friend class Rules;
private:
	Set lhs;
	Set rhs;	
public:	
	Rule();
	bool test(int pos, bool isLeft);
	void lhsSet(int pos);
	void rhsSet(int pos);
	void setSize(int theSize);
	void set(int pos, bool isLeft);
	void setRule(const Set&amp; left, const Set&amp; right);
	void operator=(const Rule&amp; dummy);
	bool operator==(const Rule&amp; dummy);
};

class Rules
{
private:
	Set leftSet;
	Set rightSet;
	Set attrClosure[MaxAttrNumber];
	char attributes[MaxAttrNumber][MaxNameLength+1];
	char relationName[MaxNameLength+1];
	bool needKey;//this is needed for checklossless
	int keyCount;
	int attrCount;
	int attrFound[MaxAttrNumber];
	int numberFound;
	int searchByChar(char ch, int step);
	void ruleReading(FILE* stream);
	Rule rules[MaxRuleNumber];
	int ruleNumber;
	void displayRule(int index);
	void removeRule(int index);
	void calcClosure();
	void addRule(const Set&amp; left, const Set&amp; right);
	int extractNames(FILE* stream, char* delimeters, char endChar='\n');
	void closure(Set&amp; attrSet, int maskedRule) const;
	bool checkAttr(int index);
	void checkRule(int index);
	void split();
	void combine();
	//&quot;this&quot; relation imply &quot;dummy&quot; relation
	bool imply(const Rules&amp; dummy) const;
	void showMatrix(const Set* matrix, int row);
public:	
	void canonical();
	void decomposition();	
	Set candidateKey[MaxKeyNumber];
	void addRule(const Rule&amp; dummy);
	void readFile(const char* fileName);
	void display();
	void display(const Set&amp; attrSet);
	int findKey();
	bool checkLossless();
	bool eachKey(Set&amp; dummy);
	bool operator==(const Rules&amp; dummy) const;
	Rules();
};
</pre>
<pre>
</pre>
<pre>　</pre>

<pre><span lang="en-ca"><font size="3" color="#FF0000"><b>file name: rules.cpp </b></font></span></pre>

<pre>#include &quot;Rules.h&quot;

void Rules::decomposition()
{
	bool keyFound=false;
	Set dummy;
	findKey();
	canonical();
	for (int i=0; i&lt;ruleNumber; i++)
	{
		dummy=rules[i].lhs+rules[i].rhs;
		cout&lt;&lt;&quot;\ndecomposition #&quot;&lt;&lt;i+1&lt;&lt;&quot;:&quot;;
		display(dummy);
		cout&lt;&lt;&quot;\ndependency is:&quot;;
		displayRule(i);
		cout&lt;&lt;endl;
		for (int j=0; j&lt;keyCount; j++)
		{
			if (candidateKey[j]&lt;=dummy)
			{
				keyFound=true;//some decomposition contains some key
			}
		}
	}
	if (!keyFound)
	{
		cout&lt;&lt;&quot;\ndecomposition #&quot;&lt;&lt;ruleNumber+1&lt;&lt;&quot;:&quot;;
		display(candidateKey[0]);
		cout&lt;&lt;&quot;\nthe key has no particular dependency\n&quot;;
	}
	needKey=!keyFound;
}
	
	
//English can be ambiguous: this function really means:
//&quot;this&quot; relation imply &quot;dummy&quot; relation
bool Rules::imply(const Rules&amp; dummy) const
{
	Set left;
	//for all dependency in &quot;dummy&quot; relation...
	for (int i=0; i&lt;dummy.ruleNumber; i++)
	{
		left=dummy.rules[i].lhs;
		//if the closure of that particular dependency under
		//dependency of &quot;this&quot; relation
		//cannot contains its rhs in that dependency in &quot;dummy&quot; 
		//relation, we say...
		closure(left, -1);
		if (!(dummy.rules[i].rhs&lt;=left))
		{
			//we say &quot;dummy&quot; relation is not implied by &quot;this&quot; relation
			return false;
		}
	}
	//can we?????
	return true;
}

//the &quot;secret&quot; name for this function is &quot;equivalent&quot;
bool Rules::operator ==(const Rules&amp; dummy) const
{
	return imply(dummy)&amp;&amp;dummy.imply(*this);
}



void Rules::canonical()
{
	int i;
	bool found;
	split();
	//cout&lt;&lt;&quot;\nafter split&quot;;
	//display();
	do
	{
		i=0; 
		found=false;
		while (i&lt;ruleNumber)
		{
			if (checkAttr(i))
			{
				found=true;				
			}
			i++;
		}
	}while (found);
	//cout&lt;&lt;&quot;\nafter checkattr&quot;;
	//display();
	for (i=0; i&lt;ruleNumber; i++)
	{
		checkRule(i);
	}
	//cout&lt;&lt;&quot;\nafter check rule&quot;;
	//display();
	combine();
	
}

void Rules::combine()
{
	for (int i=0; i&lt;ruleNumber; i++)
	{
		for (int j=i+1; j&lt;ruleNumber; j++)
		{
			if (rules[i].lhs==rules[j].lhs)
			{
				rules[i].rhs=rules[i].rhs+rules[j].rhs;
				removeRule(j);
			}
		}
	}
}


void Rules::removeRule(int index)
{
	if (index&lt;ruleNumber)
	{
		ruleNumber--;
		for (int i=index; i&lt;ruleNumber; i++)
		{
			rules[i]=rules[i+1];
		}
	}	
}



void Rules::checkRule(int index)
{
	Set dummy;
	dummy=rules[index].lhs;
	closure(dummy, index);
	if (rules[index].rhs&lt;=dummy)
	{
		removeRule(index);
	}
}


bool Rules::checkAttr(int index)
{
	Set dummy;
	//for the out parameter &quot;dummy&quot;, you cannot modify it!
	rules[index].lhs.forEachSubSet(dummy);

	while (rules[index].lhs.eachSub(dummy))
	{
		Set old;	
		//so, you have to use a copy of dummy to calc closure of it
		old=dummy;
		closure(old, index);
	
		if (rules[index].rhs&lt;=old)
		{		
			rules[index].lhs=dummy;
			for (int i=0; i&lt;ruleNumber; i++)
			{
				if (i!=index)
				{
					if (rules[i]==rules[index])
					{
						//found repeat
						removeRule(index);
					}
				}
			}
			//otherwise do nothing
			return true;
		}
	}
	return false;
}


int Rules::findKey()
{
	Set theLeft, theRight;
	bool isSub;
	leftSet.setSize(attrCount);
	rightSet.setSize(attrCount);
	leftSet.set();//the universal set
	rightSet.reset();//the empty set
	for (int i=0; i&lt;ruleNumber; i++)
	{
		rightSet= rightSet+rules[i].rhs;
	}
	rightSet=leftSet-rightSet;//rightSet is the minimum part of candidate key
	leftSet=leftSet-rightSet;//leftSet is the difference of rightSet,should be added to key
	keyCount=0;
	theRight=rightSet;
	theLeft=leftSet;

	closure(theRight, -1);
	if (theRight.count()==attrCount)//the only key
	{
		candidateKey[keyCount++]=rightSet;		
	}
	else
	{
		leftSet.forEachSubSet(theLeft);
		while (leftSet.eachSub(theLeft))
		{
			isSub=false;
			theRight=rightSet;
			theRight=theRight+theLeft;
			for (int i=0; i&lt;keyCount; i++)
			{
				//display(theRight);
				//display(candidateKey[i]);
				if (candidateKey[i]&lt;theRight)
				{
					isSub=true;
					break;
				}
			}
			if (isSub)
			{
				continue;//if subset of any candidate key, no need to test
			}
			Set temp;
			temp=theRight;
			closure(temp, -1);
			if (temp.count()==attrCount)
			{
				candidateKey[keyCount++]=theRight;
			}

		}
	}	
	return keyCount;
}

void Rules::showMatrix(const Set* matrix, int row)
{
	for (int j=0; j&lt;attrCount; j++)
	{
		cout&lt;&lt;&quot;\t&quot;&lt;&lt;attributes[j];
	}
	cout&lt;&lt;&quot;\n&quot;;
	for (int i=0; i&lt;row; i++)
	{
		displayRule(i);
		cout&lt;&lt;&quot;\t&quot;&lt;&lt;matrix[i]&lt;&lt;endl;
	}
}

//this is a standard algorithm and it makes sure
//that all dependency agree with each other
//or in good English that there is some common agreement 
//between all dependency. Is this good enough? I doubt it.
bool Rules::checkLossless()
{
	int row=needKey?ruleNumber+1:ruleNumber;
	
	Set* matrix=new Set[row];
	for (int i=0; i&lt;row; i++)
	{
		if (needKey&amp;&amp;i==row-1)
		{
			matrix[i]=candidateKey[0];	
		}
		else
		{
			matrix[i]=rules[i].lhs+rules[i].rhs;
		}
	}
	showMatrix(matrix, row);
	int index;
	bool found;
	do
	{
		index=0;
		found=false;
		while (index&lt;row)
		{
			for (int j=0; j&lt;ruleNumber; j++)
			{
				if (j!=index)
				{
					if (rules[j].lhs&lt;=matrix[index])
					{
						if (!(rules[j].rhs&lt;=matrix[index]))
						{
							matrix[index]=matrix[index]+rules[j].rhs;
							found=true;
						}
					}
				}
			}
			index++;
		}
	}while(found);
	showMatrix(matrix, row);
	for (index=0; index&lt;row; index++)
	{
		if (matrix[index].count()==attrCount)
		{
			delete []matrix;
			return true;
		}
	}
	delete [] matrix;
	return false;
}


void Rules::calcClosure()
{
	for (int i=0; i&lt;attrCount; i++)
	{
		attrClosure[i].setSize(attrCount);
		attrClosure[i].reset();
		attrClosure[i].set(i);
		closure(attrClosure[i], -1);
	}
}

void  Rules::closure(Set&amp; attrSet, int maskedRule) const
{
	bool found=false;
	int i=0;
	do 
	{
		i=0;
		found=false;
		while (i&lt;ruleNumber)
		{
			if (i!=maskedRule)//this rule will not be calculated
			{
				if (rules[i].lhs&lt;=attrSet)//lhs is contained
				{
					if ((attrSet*rules[i].rhs)!=rules[i].rhs)
					{
						attrSet=attrSet+rules[i].rhs;
						found=true;
					}
				}
			}
			i++;
		}
	}
	while (found);
}

void Rules::display(const Set&amp; attrSet)
{
	bool first=true;
	cout&lt;&lt;&quot;{&quot;;
	for (int i=0; i&lt;attrCount; i++)
	{
		
		if (attrSet.test(i))
		{
			if (first)
			{
				first=false;
			}
			else
			{
				cout&lt;&lt;&quot;,&quot;;
			}
			cout&lt;&lt;attributes[i];
		}
	}
	cout&lt;&lt;&quot;}&quot;;
}

void Rules::display()
{
	cout&lt;&lt;&quot;\nnow display\n&quot;;
	cout&lt;&lt;relationName&lt;&lt;&quot;(&quot;;
	for (int i=0; i&lt;attrCount; i++)
	{
		cout&lt;&lt;attributes[i];
		if (i!=attrCount-1)
		{
			cout&lt;&lt;&quot;,&quot;;
		}
		else
		{
			cout&lt;&lt;&quot;);\n&quot;;
		}
	}
	for (i=0; i&lt;ruleNumber; i++)
	{
		displayRule(i);
	}
}


bool Rule::test(int pos, bool isLeft)
{
	if (isLeft)
	{
		return lhs.test(pos);
	}
	else
	{
		return rhs.test(pos);
	}
}


void Rules::displayRule(int index)
{
	for (int i=0; i&lt;attrCount; i++)
	{
		if (rules[index].test(i, true))
		{
			cout&lt;&lt;attributes[i];
		}
	}
	cout&lt;&lt;&quot; -&gt; &quot;;
	for (i=0; i&lt;attrCount; i++)
	{
		if (rules[index].test(i, false))
		{
			cout&lt;&lt;attributes[i];
		}
	}
	cout&lt;&lt;&quot;;\n&quot;;
}


void Rule::set(int pos, bool isLeft)
{
	if (isLeft)
	{
		lhsSet(pos);
	}
	else
	{
		rhsSet(pos);
	}
}

void Rule::rhsSet(int pos)
{
	rhs.set(pos);
}

void Rule::lhsSet(int pos)
{
	lhs.set(pos);
}

Rule::Rule()
{
	lhs.reset();
	rhs.reset();
	
}


void Rules::readFile(const char* fileName)
{
	FILE* stream;
	stream=fopen(fileName, &quot;r&quot;);
	attrCount=extractNames(stream, &quot;=,()&quot;, ';');//ignore the first relation name
	ruleReading(stream);

}

void Rules::ruleReading(FILE* stream)
{
	char ch;
	int step=0;
	ruleNumber=0;//initialize
	bool isLeft=true;
	rules[ruleNumber].setSize(attrCount);//do twice!!
	while (!feof(stream))
	{		
		ch=fgetc(stream);
		if (ch==';'||ch==',')
		{
			ruleNumber++;
			rules[ruleNumber].setSize(attrCount);//do twice!!
			isLeft=true;
		}
		else
		{
			if (ch=='-'||ch=='&gt;')
			{
				isLeft=false;
			}
			else
			{
				if (isalpha(ch))
				{
					searchByChar(ch, step);
					if (numberFound!=1)
					{
						if (step&lt;MaxNameLength)
						{
							step++;
						}
						else
						{
							cout&lt;&lt;&quot;illegal attribute stated!\n&quot;;
							exit(1);
						}
					}
					else
					{
						step=0;
						
						rules[ruleNumber].set(attrFound[0], isLeft);
					}
				}
			}
		}
	}
}

void Rule::setSize(int theSize)
{
	lhs.setSize(theSize);
	rhs.setSize(theSize);
}
	

int Rules::searchByChar(char ch, int step)
{
	if (step==0)//this is first time, and all attributes are searched
	{
		numberFound=0;
		for (int i=0; i&lt;attrCount; i++)//
		{
			if (ch==attributes[i][step])
			{
				attrFound[numberFound]=i;
				numberFound++;
			}
		}
	}
	else
	{
		int number=0;//new 'attrFound' re-counting
		for (int i=0; i&lt;numberFound; i++)
		{
			if (ch==attributes[attrFound[i]][step])
			{
				attrFound[number]=i;
				number++;
			}
		}
		numberFound=number;
	}
	return numberFound;
}


int findChar(char ch, char* str)
{
	int index=0;
	while (str[index]!='\0')
	{
		if (str[index]==ch)
		{
			return index;
		}
		index++;
	}
	return -1;
}

//extract names from a line delimetered by various chars, like ',','(',')'....
int Rules::extractNames(FILE* stream, char* delimeters, char endChar)
{
	int findChar(char ch, char* str);
	char ch;
	int nameIndex=0;
	char buffer[MaxNameLength+1];
	char* ptr=buffer;

	while (!feof(stream))
	{
		ch=getc(stream);
		if (ch==endChar)
		{
			if (ptr!=buffer)
			{
				*ptr='\0';
				strcpy(attributes[nameIndex], buffer);
			}
			break;
		}

		if (findChar(ch, delimeters)&gt;0)//deli reached
		{
			//skip the consecutive deli
			if (ptr!=buffer)
			{
				*ptr='\0';	
				if (ch=='(')//the very first one
				{
					strcpy(relationName, buffer);
				}
				else
				{
					strcpy(attributes[nameIndex], buffer);
					nameIndex++;
				}
				ptr=buffer;//restart
			}
		}
		else
		{
			*ptr=ch;
			ptr++;
		}
	}
	return nameIndex;
}		

Rules::Rules()
{
	numberFound=attrCount=0;
}
	
void Rule::operator =(const Rule&amp; dummy)
{
	lhs=dummy.lhs;
	rhs=dummy.rhs;
}

void Rules::addRule(const Rule&amp; dummy)
{
	rules[ruleNumber++]=dummy;
}

void Rules::addRule(const Set&amp; left, const Set&amp; right)
{
	rules[ruleNumber++].setRule(left, right);
}

void Rule::setRule(const Set&amp; left, const Set&amp; right)
{
	lhs=left;
	rhs=right;
}

void Rules::split()
{
	int index;
	for (int i=0; i&lt;ruleNumber; i++)
	{
		if (rules[i].rhs.count()&gt;1)
		{			
			index=rules[i].rhs.first();
			index=rules[i].rhs.next(index);//starting from second one
			while (index!=-1)
			{
				Set right;				
				right.setSize(rules[i].rhs);
				right.set(index);
				rules[i].rhs.reset(index);//remove from old rule
				addRule(rules[i].lhs, right);
				index=rules[i].rhs.next(index);
			}
		}
	}
}

bool Rule::operator ==(const Rule&amp; dummy)
{
	return (lhs==dummy.lhs&amp;&amp;rhs==dummy.rhs);
}</pre>
<pre>　</pre>

<pre><span lang="en-ca"><font size="3" color="#FF0000"><b>file name: main.cpp (main)</b></font></span></pre>

<pre>#include &quot;Rules.h&quot;
#include &quot;set.h&quot;

int main()
{
	int number;
	Rules R;
	R.readFile(&quot;d:\\rules.txt&quot;);
	R.display();
	
	
	number=R.findKey();
	for (int i=0; i&lt;number; i++)
	{
		R.display(R.candidateKey[i]);
		cout&lt;&lt;&quot;\n&quot;;
	}

	R.canonical();

	R.decomposition();
	R.checkLossless();

	return 0;
}</pre>
<pre>　</pre>

<pre><b><font color="#FF0000"><span lang="en-ca">Here goes the result of test:</span></font></b></pre>

<pre><b><font color="#FF0000"><span lang="en-ca">(My little scanner is power enough. The lossless-check function just looks for a line where all element is &quot;1&quot;.)</span></font></b></pre>

<pre>　</pre>

<pre><span lang="en-ca">
now display
R(A,B,C,D,E,H);
A -&gt; B;
DE -&gt; A;
BC -&gt; E;
BCD -&gt; A;
ADE -&gt; B;
{A,C,D,H}
{B,C,D,H}
{C,D,E,H}

decomposition #1:{A,B}
dependency is:A -&gt; B;


decomposition #2:{A,D,E}
dependency is:DE -&gt; A;


decomposition #3:{B,C,E}
dependency is:BC -&gt; E;


decomposition #4:{A,C,D,H}
the key has no particular dependency
A B C D E H
A -&gt; B;
110000
DE -&gt; A;
100110
BC -&gt; E;
011010
BCD -&gt; A;
101101
A B C D E H
A -&gt; B;
110000
DE -&gt; A;
110110
BC -&gt; E;
011010
BCD -&gt; A;
111111
Press any key to continue</span></pre>

<pre></pre>

<pre></pre>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                   
&nbsp;&nbsp;&nbsp; <a href="game24.htm">                  







                       <img src="picture/back.gif" style="border: medium none" alt="back.gif (341 bytes)" width="32" height="35"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<a href="index.htm">
<img src="picture/up.gif" style="border: medium none" alt="up.gif (335 bytes)" width="35" height="32"></a>       &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                         
<img src="picture/next.gif" style="border: medium none" alt="next.gif (337 bytes)" width="32" height="35">          


</p>

</body>

</html>