// Emacs: -*- C++ -*-
static char rcsid[] = "$Id: generic-tests.c,v 1.11 92/12/07 12:08:22 thoth Exp $";

// 
// $Log:	generic-tests.c,v $
// Revision 1.11  92/12/07  12:08:22  thoth
// test for new IntPoint index(unsigned) method.
// 
// Revision 1.10  92/11/11  12:12:23  thoth
// test of another restriction utility.
// 
// Revision 1.9  92/11/04  12:02:45  thoth
// test of new image extension support routines.
// 
// Revision 1.8  92/10/25  20:58:13  thoth
// changed argument passing on set restriction routines.
// made recursion termination constants have sensible names.
// 
// Revision 1.7  92/10/21  15:01:17  thoth
// added test for image restriction utility.
// 
// Revision 1.6  92/09/30  10:43:11  thoth
// infimum & supremum virtuals now inf & sup.
// 
// Revision 1.5  92/09/20  17:41:51  thoth
// fixed spelling error.
// altered lattice tests to catch more errors.
// added minkowski test.
// 
// Revision 1.4  92/09/20  14:06:03  thoth
// tests added for the new infimum and supremum methods
// 
// Revision 1.3  92/09/07  13:25:10  thoth
// new test for the IntPoint array constructor
// 
// Revision 1.2  92/08/25  15:34:46  thoth
// generic tests now check lattice properties of set operations
// new test for translation verification
// 
// Revision 1.1  92/08/23  13:30:26  thoth
// Initial revision
// 
//

#include "IntPoint.h"
#include "IntPointSet.h"
#include "IPSIter.h"

#include "generic-tests.h"

void test_union(IntPointSet a, IntPointSet b, IntPointSet ab)
{
    IntPoint	p;
    
    IPSIter	iter;
    
    iter = a;
    
    while (iter(p)) {
	if (!ab.contains(p)) {
	    cout << p;
	    cout << "##missing##\n";
	}
    }
    
    iter = b;
    
    while (iter(p)) {
	if (!ab.contains(p)) {
	    cout << p;
	    cout << "##missing##\n";
	}
    }
    
    iter = ab;
    
    while (iter(p)) {
	if (! (a.contains(p) || b.contains(p))) {
	    cout << p;
	    cout << "##extra##\n";
	}
    }

    if (! (a<=ab))
	cout << "## ! a <= a|b ##\n";
    if (! (b<=ab))
	cout << "## ! b <= a|b ##\n";

    cout << "union test complete\n";
}

void test_intersection(IntPointSet a, IntPointSet b, IntPointSet ab)
{
    IntPoint	p;
    
    IPSIter	iter;
    
    iter = a;
    
    while (iter(p)) {
	if (b.contains(p) != ab.contains(p)) {
	    cout << p;
	    cout << "##a error##\n";
	}
    }
    
    iter = b;
    
    while (iter(p)) {
	if (a.contains(p) != ab.contains(p)) {
	    cout << p;
	    cout << "##b error##\n";
	}
    }
    
    iter = ab;
    
    while (iter(p)) {
	if (! (a.contains(p) && b.contains(p))) {
	    cout << p;
	    cout << "##ab extra##\n";
	}
    }

    if (! (a>=ab))
	cout << "## ! a >= a&b ##\n";
    if (! (b>=ab))
	cout << "## ! b >= a&b ##\n";
    cout << "intersection test complete\n";
}

void test_XOR(IntPointSet a, IntPointSet b, IntPointSet ab)
{
    IntPoint	p;
    
    IPSIter	iter;
    
    iter = a;
    
    while (iter(p)) {
	if (b.contains(p) && ab.contains(p)) {
	    cout << p;
	    cout << "##a extra error##\n";
	} else if (!b.contains(p) && !ab.contains(p)) {
	    cout << p;
	    cout << "##a missing error##\n";
	}
    }
    
    iter = b;
    
    while (iter(p)) {
	if (a.contains(p) && ab.contains(p)) {
	    cout << p;
	    cout << "##b extra error##\n";
	} else if (!a.contains(p) && !ab.contains(p)) {
	    cout << p;
	    cout << "##b missing error##\n";
	}
    }
    
    iter = ab;
    
    while (iter(p)) {
	if (a.contains(p) == b.contains(p)) {
	    cout << p;
	    cout << "##ab extra##\n";
	}
    }
    
    if ( ((a/b) | (b/a)) != ab ) {
	cout << "(a/b) | (b/a) != a^b\n";
    }
    
    cout << "symmetric difference test complete\n";
}

void test_minus(IntPointSet a, IntPointSet b, IntPointSet ab)
{
    IntPoint	p;
    
    IPSIter	iter;
    
    iter = a;
    
    while (iter(p)) {
	if (b.contains(p) && ab.contains(p)) {
	    cout << p << "##extra [a] error##\n";
	} else if ( ! b.contains(p) && ! ab.contains(p) ) {
	    cout << p << "##missing [a] error##\n";
	}
    }
    
    iter = b;
    
    while (iter(p)) {
	if (ab.contains(p)) {
	    cout << p;
	    cout << "##b error##\n";
	}
    }
    
    iter = ab;
    
    while (iter(p)) {
	if (!a.contains(p) || b.contains(p)) {
	    cout << p;
	    cout << "##ab extra##\n";
	}
    }

    if (! (a>=ab))
	cout << "## ! a >= a/b ##\n";

    cout << "difference test complete\n";
}

//
//
//


static void scan_restricted_ss(IPSIter *iter, const SetStructure &ss,
		    IntPointSet source, IntPointSet trimmed)
{
    unsigned	i,j;
    IntPoint	p;
    // cerr << "( ";
    for (i=0; i<ss.nintervals(); i++) {
	ss_interval	temp(ss.retrieve_interval(i));
	// cerr << temp.count << " ";
	if (temp.substructure==SetStructure::FIRST_ONLY) {
	    // cerr << "_";
	    for (j=0; j<temp.count; j++) {
		if (!(*iter)(p)) {
		    cout << "substructure too large\n";
		    return;
		}
		if (trimmed.contains(p)) {
		    cout << "restricted pointset contains skipped point ";
		    cout << p << "\n";
		}
	    }
	} else if (temp.substructure==SetStructure::BOTH) {
	    //cerr << "*";
	    for (j=0; j<temp.count; j++) {
		if (!(*iter)(p)) {
		    cout << "substructure too large\n";
		    return;
		}
		if (!trimmed.contains(p)) {
		    cout << "restricted pointset missing point ";
		    cout << p << "\n";
		}
	    }
	} else {
	    for (j=0; j<temp.count; j++) {
		scan_restricted_ss(iter, temp.substructure, source, trimmed);
	    }
	}
	//cerr << "; ";
    }
    //cerr << " ) ";
}

void test_restriction(IntPointSet a, IntPointSet b)
{
    IntPointSet	ab(a&b);

    SetStructure	ss;
    IntPointSet ab2(intersect_with_structure(a,b,&ss));

    if (ab!=ab2) {
	cout << "restriction intersection inaccurate\n";
	return;
    }

    IPSIter	iter(a);

    scan_restricted_ss(&iter, ss, a, ab);
    IntPoint	p;
    if (iter(p)) {
	cout << "restriction structure too small\n";
    }
}

static void scan_dualrestricted_ss(IPSIter *itera, IPSIter *iterb,
				   IPSIter *iterab,
				   const SetStructure &ss)
{
    unsigned	i,j;
    IntPoint	p1,p2,p3;
    struct blah {
    blah() { cerr << "("; }
    ~blah() { cerr << ")"; }
    }; // _blah;

    for (i=0; i<ss.nintervals(); i++) {
	ss_interval	temp(ss.retrieve_interval(i));
	// cerr << temp.count << " ";
	if (temp.substructure==SetStructure::FIRST_ONLY) {
	    // cerr << "_1";
	    for (j=0; j<temp.count; j++) {
		if (!(*itera)(p1)) {
		    cout << "[a] substructure too large\n";
		    return;
		}
	    }
	} else if (temp.substructure==SetStructure::SECOND_ONLY) {
	    // cerr << "_2";
	    for (j=0; j<temp.count; j++) {
		if (!(*iterb)(p1)) {
		    cout << "[b] substructure too large\n";
		    return;
		}
	    }
	} else if (temp.substructure==SetStructure::BOTH) {
	    // cerr << "*";
	    for (j=0; j<temp.count; j++) {
		if (!(*itera)(p1)) {
		    cout << "[ab] substructure too large\n";
		    return;
		}
		if (!(*iterab)(p2)) {
		    cout << "[ab] substructure too large\n";
		    return;
		}
		if (!(*iterb)(p3)) {
		    cout << "[ab] substructure too large\n";
		    return;
		}
		if (p1 != p2 || p2 != p3) {
		    cout << "[ab] iterators out of step" << p1 << p2 << p3 << "\n";
		}
	    }
	} else {
	    for (j=0; j<temp.count; j++) {
		scan_dualrestricted_ss(itera, iterb, iterab,
				       temp.substructure);
	    }
	}
	// cerr << "; ";
    }
}

void test_dualrestriction(IntPointSet a, IntPointSet b)
{
    IntPointSet	ab(a&b);

    SetStructure	ss;
    IntPointSet ab2(intersect_with_dualstruct(a,b,&ss));

    if (ab!=ab2) {
	cout << "restriction intersection inaccurate\n";
	return;
    }

    IPSIter	itera(a);
    IPSIter	iterb(b);
    IPSIter	iterab(ab);

    scan_dualrestricted_ss(&itera, &iterb, &iterab, ss);
    IntPoint	p;
    if (itera(p) || iterb(p) || iterab(p)) {
	cout << "dualrestriction structure too small\n";
    }
}

//
//
//

static void scan_extended_ss(IPSIter *itera, IPSIter *iterb, IPSIter *iterab,
			     const SetStructure &ss)
{
    unsigned	i,j;
    IntPoint	p1,p2,p3;
    struct blah {
    blah() { cerr << "("; }
    ~blah() { cerr << ")"; }
    }; // _blah;

    for (i=0; i<ss.nintervals(); i++) {
	ss_interval	temp(ss.retrieve_interval(i));
	// cerr << temp.count << " ";
	if (temp.substructure==SetStructure::FIRST_ONLY) {
	    // cerr << "_1";
	    for (j=0; j<temp.count; j++) {
		if (!(*itera)(p1)) {
		    cout << "[a] substructure too large\n";
		    return;
		}
		if (!(*iterab)(p2)) {
		    cout << "[a] substructure too large\n";
		    return;
		}
		if (p1 != p2) {
		    cout << "[a] iterators out of step " << p1 << p2 << "\n";
		}
	    }
	} else if (temp.substructure==SetStructure::SECOND_ONLY) {
	    // cerr << "_2";
	    for (j=0; j<temp.count; j++) {
		if (!(*iterb)(p1)) {
		    cout << "[b] substructure too large\n";
		    return;
		}
		if (!(*iterab)(p2)) {
		    cout << "[b] substructure too large\n";
		    return;
		}
		if (p1 != p2) {
		    cout << "[b] iterators out of step" << p1 << p2 << "\n";
		}
	    }
	} else if (temp.substructure==SetStructure::BOTH) {
	    // cerr << "*";
	    for (j=0; j<temp.count; j++) {
		if (!(*itera)(p1)) {
		    cout << "[ab] substructure too large\n";
		    return;
		}
		if (!(*iterab)(p2)) {
		    cout << "[ab] substructure too large\n";
		    return;
		}
		if (!(*iterb)(p3)) {
		    cout << "[ab] substructure too large\n";
		    return;
		}
		if (p1 != p2 || p2 != p3) {
		    cout << "[ab] iterators out of step" << p1 << p2 << p3 << "\n";
		}
	    }
	} else {
	    for (j=0; j<temp.count; j++) {
		scan_extended_ss(itera, iterb, iterab, temp.substructure);
	    }
	}
	// cerr << "; ";
    }
}

static void pips(IntPointSet ips)
{
    IPSIter	iter(ips);
    IntPoint	p;
    while (iter(p))
	cout << p << " ";
    cout << "\n";
}

void test_extension(IntPointSet a, IntPointSet b)
{
    IntPointSet	ab(a|b);

    SetStructure	ss;
    IntPointSet ab2(union_with_structure(a,b,&ss));
#if 0
    cout << "\n";
    pips(a);
    pips(b);
    pips(ab);
    pips(ab2);
#endif
    if (ab!=ab2) {
	cout << "extension union inaccurate (card << "
	     << ab.card() << "," << ab2.card() << "\n";
	ab.output(cout);
	cout << "\n";
	ab2.output(cout);
	cout << "\n";
	return;
    }

    IPSIter	iter1(a);
    IPSIter	iter2(b);
    IPSIter	iter3(ab2);

    scan_extended_ss(&iter1, &iter2, &iter3, ss);

    IntPoint	p;
    if (iter1(p) || iter2(p) || iter3(p)) {
	cout << "extension structure too small\n";
    }
}


//
//
//


void test_offset(IntPointSet a, IntPoint x, IntPointSet ax)
{
    IPSIter	iter(a);
    IntPoint	p;

    while (iter(p)) {
	if (!ax.contains(p+x)) {
	    cout << p << " ##missing [a] error##\n";
	}
    }

    iter=ax;

    while (iter(p)) {
	if (!a.contains(p-x)) {
	    cout << p << " ##extra [ax] error##\n";
	}
    }

    cout << "offset test complete\n";
}

void test_minkowski(IntPointSet a, IntPointSet b, IntPointSet ab)
{
    IPSIter	iter1(a), iter2(b);
    IntPoint	p1, p2;

    while (iter1(p1)) {
	while (iter2(p2)) {
	    if (!ab.contains(p1+p2)) {
		cout << p1+p2 << " ##missing [ab] error##\n";
	    }
	}
	if (! ( b+p1 <= ab) ) {
	    cout << "## ! (b+" << p1 << " <= ab) ##\n";
	}
    }

    iter1 = b;
    while (iter1(p1)) {
	if (! ( a+p1 <= ab) ) {
	    cout << "## ! (a+" << p1 << " <= ab) ##\n";
	}
    }

    iter1 = ab;
    while (iter1(p1)) {
	int	found;
	found = 0;
	iter2 = a;
	while (!found && iter2(p2)) {
	    if (b.contains(p1-p2)) {
		found=1;
	    }
	}
	if (!found) {
	    cout << p1 << "##missing ab-a : b error##\n";
	}
    }

    iter1 = ab;
    while (iter1(p1)) {
	int	found;
	found = 0;
	iter2 = b;
	while (!found && iter2(p2)) {
	    if (a.contains(p1-p2)) {
		found=1;
	    }
	}
	if (!found) {
	    cout << p1 << "##missing ab-b : a error##\n";
	}
    }

    cout << "minkowski test complete\n";
}

void list_ips(ostream &o, IntPointSet ips)
{
    IPSIter	iter(ips);
    IntPoint	ip;
    while (iter(ip)) {
	o << ip << ' ';
    }
    o << "\n";
}


void printIntSet(IntPointSet ips)
{
    char	image[80][40];
    int	x,y;
    
    for (x=0; x<40; x++)
	for (y=0; y<40; y++)
	    image[x][y] = ' ';
    
    IPSIter	iter(ips);
    IntPoint	p;
    while (iter(p)) {
	image[p[0]+20][p[1]+10] = '#';
    }
    
    for (x=40; x<80; x++)
	for (y=0; y<40; y++)
	    image[x][y] = ips.contains(IntPoint(x-60,y-10)) ? '*' : '.';
    
    for (y=0; y<40; y++) {
	for (x=0; x<75; x++)
	    cout << image[x][y];
	cout << '\n';
    }
    
}


void test_iter(IntPointSet ps)
{
    IPSIter	iter(ps);
    IntPoint	p, oldp;
    unsigned	idx=0;
    IntPoint	inf, supr, min, max;
    
    while (iter(p)) {
	
	// the following is not part of the IAC++ spec, it just happens
	// to be the way we implemented iteration.
	if (idx>0 && Pointcmp(oldp,p)!=-1) {
	    cout << "iteration order error: " << oldp << ">=" << p << '\n';
	}
	
	// this IS part of the spec
	// cout << "index(" << p << ")=" << ps.index(p) << '\n';
	if (ps.index(p) != idx) {
	    cout << "Iteration order error: index("<<p<<")=" << ps.index(p)
		<< "!="<<idx<<'\n';
	}
	if (!(ps.index(idx) == p)) {
	    cout << "Iteration order error: index("<<idx<<")="<<ps.index(idx)
		 << "!="<<p<<'\n';
	}
	
	// we will set min on the first pass through
	if (min.dimensions()==0)
	    min = p;

	// max cache the last valid p
	max = p;

	if (inf.dimensions()==0) {
	    inf = p;
	    supr = p;
	} else {
	    inf = infimum(inf,p);
	    supr = supremum(supr,p);
	}

	idx++;
	oldp = p;
    }
    if (idx != ps.card()) {
	cout << "Cardinality error: ps.card()=" << ps.card()
	    << "!=" << idx << '\n';
    }
    if (min != ps.min()) {
	cout << "Minimum error: ps.min()=" << ps.min() << "!=" << min << '\n';
    }
    if (max != ps.max()) {
	cout << "Maximum error: ps.max()=" << ps.max() << "!=" << max << '\n';
    }
    if (inf != ps.inf()) {
	cout << "Infimum error: ps.inf()=" << ps.inf()
	     << "!=" << inf << '\n';
    }
    if (supr != ps.sup()) {
	cout << "Supremum error: ps.sup()=" << ps.sup()
	     << "!=" << supr << '\n';
    }
    cout << "iteration test complete\n";
}

void
test_arr_ps(IntPointSet ps, const IntPoint *arr, unsigned len)
{
  unsigned i;

  for (i=0; i<len; i++) {
#if 0
    cout << arr[i] << ' ';
#endif
    if (!ps.contains(arr[i])) {
      cout << "pointset missing "<<arr[i]<<" at index "<<i<<'\n'<<flush;
    }
  }
  cout << '\n';

  IPSIter	iter;
  iter = ps;

  IntPoint	ip;
  i=0;
  while (iter(ip)) {
    int	cmp;
#if 0
    cout << ip << ' ';
#endif
    for (i=0; i<len && 1==(cmp=Pointcmp(ip,arr[i])); i++)
      ;
    if (0!=cmp) {
      cout << "pointset extra "<<ip<<'\n'<<flush;
    }
  }

}
