// File ShapeTest.java

import java.util.Vector;
import java.util.Iterator;

public class ShapeTest 
{   public static void main(String[] args)
    {   Point p1 = new Point(1.0,1.0);
        Point p2 = new Point(5.0,5.0);
        ColoredPoint q1 = new ColoredPoint(p1,Color.RED);
        ColoredPoint q2 = new ColoredPoint(p2,Color.ORANGE);
        Circle c1 = new Circle(p1,1.0);
        Circle c2 = new Circle(q2,1.0);
        System.out.println(p1.getClass().getName());
        p1.print();
        q2.print();
        c1.print();
        Shape s1 = p1;
        s1.print();
        Point p3 = c2.getCenter();
        if (p3 instanceof ColoredPoint)
        {   ((ColoredPoint) p3).setColor(Color.YELLOW);   }
        c2.print();
        System.out.println(c1.area());
        System.out.println(q1.area());
        System.out.println(p1 == p1);
        System.out.println(p1.equals(p1));
        System.out.println(q1.equals(p1));
        System.out.println(p1.equals(q1));
        p3 = new Point(p1);
        System.out.println(p1 == p3);
   }
}


abstract class Shape
{   public abstract boolean equals(Object s);
    //  returns true if this Shape equals Object s

    public double area() {   return 0.0;   }
    //  returns area of this Shape

    public abstract String getString();
    // returns info about the Shape to the screen

    public void print() {   System.out.println(getString());   }
}


class Point extends Shape
{   public Point(double x, double y) { _xCoord = x;  _yCoord = y; }
    // a point on a plane at coordinates (x,y)
    
    public Point(Point p) { _xCoord = p._xCoord;  _yCoord = p._yCoord; }

    public boolean equals(Object s)
    {  if (s instanceof Point)
       {    Point ps = (Point) s;
            return (_xCoord == ps.getx() && _yCoord == ps.gety());
       }  
       else
           return false;
    }

    public String getString()
    {   return ("Point(" + getx() + "," + gety() + ")"); }

    public double getx() {  return _xCoord;  }
    public double gety() {  return _yCoord;  }

    private double _xCoord;
    private double _yCoord;
}


class ColoredPoint extends Point 
{
    public ColoredPoint(Point p, int c)
    {   super(p);
        if (p instanceof ColoredPoint)
            setColor( ((ColoredPoint) p).getColor() ); 
        else
            setColor(c);   
    }
 
    public void setColor(int c) {  _theColor = c;  } // should check range

    public boolean equals(Object s)
    {  if (s instanceof ColoredPoint)
       {  ColoredPoint ts = (ColoredPoint) s;
          return (_theColor == ts._theColor && super.equals(ts));
       }  
       else
           return false;
    }
   
    public String getString()
    {   return (Color.getString(_theColor) + super.getString()); }  

    public int getColor() {  return _theColor;  }

    private int _theColor = Color.NONE;
}


class Color
{   public static final int NONE   = 0; 
    public static final int RED    = 1;
    public static final int ORANGE = 2;
    public static final int YELLOW = 3;

    public static String getString(int c) 
    {   switch (c) 
        {   case RED:     return "Red";
            case ORANGE:  return "Orange";
            case YELLOW:  return "Yellow";
        }
        return "Colorless";
    }
}


class Circle extends Shape
{   public Circle(Point c, double r)
    //  a circle with_centerpoint c and radius r
    {   _center = c;  _radius = r;   }

    public boolean equals(Object s)
    {  if (s instanceof Circle)
       {   Circle cs = (Circle) s;
           return (_radius == cs._radius && _center.equals(cs._center));
       }  
       else
           return false;
    }

    public double area() {  return (Math.PI * _radius * _radius);  }
   
    public String getString()
    {  return ("Circle at " +_center.getString() + " with radius " + _radius);
    } 

    public Point getCenter() {   return _center;   }

    private Point _center;
    private double _radius;
}


interface Selector
{   public boolean selects(Object s);  }


class Choose implements Selector
{   public boolean selects(Object s)
    {   return !(s instanceof Point);  }
}

class MultiShape extends Shape
{   public MultiShape () { _theShapes = new Vector();  }

    public int size() { return _theShapes.size();  }

    public Iterator iterator() { return _theShapes.iterator();  }

    public void add(Shape s) { boolean b = _theShapes.add(s);  }

    public MultiShape filter(Selector chooser)
    {   Iterator iter = _theShapes.iterator();
        MultiShape ms = new MultiShape();
        while ( iter.hasNext() )
        {   Shape s = (Shape) iter.next();
            if (chooser.selects(s))
                ms.add(s);
        }
        return ms;
    }

    public boolean equals(Object ss)
    {   if (ss instanceof MultiShape)
        {   MultiShape ms = (MultiShape) ss;
            if ( ms.size() == size() )
            {   Iterator iter = ms.iterator();
                while ( iter.hasNext() )
                {   Shape s = (Shape) iter.next();
                    if ( ! _theShapes.contains(s) )
                        return false;
                }
                return true;
            }
        }
        return false;
    }

    public double area()
    {   Iterator iter = _theShapes.iterator();
        double ar = 0.0;
        while ( iter.hasNext() )        
            ar += ((Shape) iter.next()).area();
        return ar;
    }

    public String getString()
    {   Iterator iter = _theShapes.iterator();
        String str = "MultiShape[\n";
        while ( iter.hasNext() )        
        {   str += "\t" + ((Shape) iter.next()).getString() + "\n"; }
        return (str + "]");
    }

    private Vector _theShapes;
}

class Rectangle extends Shape
{   public Rectangle(Point ll, Point ur)
    {  _lowerLeft = ll;  _upperRight = ur;   }

    public boolean equals ( Object s )
    {   if (s instanceof Rectangle)
        {   Rectangle r = (Rectangle) s;
            return ( _lowerLeft.equals(r._lowerLeft) && 
                     _upperRight.equals(r._upperRight) );
        }
        else
	    return false;
    }

    public double area()
    {   double area = (_upperRight.getx() - _lowerLeft.getx()) *
                      (_upperRight.gety() - _lowerLeft.gety()) ;
	return Math.abs(area);
    }

    public String getString()
    {   return "Rectangle with lower left corner at " +
               _lowerLeft.getString() + " and upper right corner at "
	       + _upperRight,getString();

    }

    Point _lowerLeft;
    Point _upperRight;
}
