import geometry.*;
import tester.*;

// Filtering based on predicates.

interface IPred<T> {
    boolean apply(T t);
}

// Posn predicate: x coord equals y coord?
class PosnSameXY implements IPred<Posn> {
    PosnSameXY() {}

    // Is the given posn's x and y the same?
    public boolean apply(Posn p) {
	return p.x == p.y;
    }
}

// Posn predicate: on screen?
class PosnOnScreen implements IPred<Posn> {
    PosnOnScreen() {}
    
    // Is the given posn on the screen?
    public boolean apply(Posn p) {
	return 0 <= p.x 
	    && p.x <= 10
	    && 0 <= p.y
	    && p.y <= 10;
    }
}

interface IList<T> {
    // Filter this list keeping those that satisfy pred.
    IList<T> filter(IPred<T> p);
}

class MT<T> implements IList<T> {
    MT() {}
    // Filter this empty list keeping those that satisfy pred.
    public IList<T> filter(IPred<T> p) { return this; }
}

class Cons<T> implements IList<T> {
    T first;
    IList<T> rest;
    Cons(T first, IList<T> rest) {
	this.first = first;
	this.rest = rest;
    }

    // Filter this cons list keeping those that satisfy pred.
    public IList<T> filter(IPred<T> p) { 
	if (p.apply(this.first))
	    return new Cons<T>(this.first, this.rest.filter(p));
	else
	    return this.rest.filter(p);
    }
}

class Examples {
    Examples() {}

    Posn p1 = new Posn(3,4);
    Posn p2 = new Posn(11,11);
    Posn p3 = new Posn(2,2);

    // List of Posns with same x, y coordinate
    IList<Posn> same = new Cons<Posn>(this.p2, 
				      new Cons<Posn>(this.p3, 
						     new MT<Posn>()));
    
    // List of Posns on the screen
    IList<Posn> scrn = new Cons<Posn>(this.p1, 
				      new Cons<Posn>(this.p3, 
						     new MT<Posn>()));

    // List of Posns with same x, y coordinate AND on the screen
    IList<Posn> sameScreen = new Cons<Posn>(this.p3, new MT<Posn>());

    // All of the Posns
    IList<Posn> all = new Cons<Posn>(this.p1, this.same);

    boolean testFilter(Tester t) {
	return t.checkExpect(this.all.filter(new PosnSameXY()),
			     this.same)
	    && t.checkExpect(this.all.filter(new PosnOnScreen()),
			     this.scrn)
	    && t.checkExpect(this.all.filter(new PosnSameXY())
			     .filter(new PosnOnScreen()),
			     this.sameScreen);	           
    }
}

