class Point {
  double x, y;
  public Point(double x, double y) {
    this.x = x; this.y = y;
  }
}
interface Region {
  boolean contains(Point p);
  public Region subtract(Region other);
  public Region add(Region other);
}

// Inheritance -- RectRegion "inherits" the subtract and add methods from ShapeRegion
abstract class ShapeRegion implements Region {
  public Region subtract(Region other) {
    return new SubtractRegion(this, other);
  }
  public Region add(Region other) {
    return new UnionRegion(this, other);
  }
}

class RectRegion extends ShapeRegion {
  double x, y, width, height;
  public RectRegion(double x, double y, double w, double h) {
    this.x = x; this.y = y; this.width = w; this.height = h;
  }
  public boolean contains(Point p) {
    return (p.x <= this.x + width) && (p.x >= this.x)
        && (p.y <= this.y + height) && (p.y >= this.y);
  }
}
class CircRegion extends ShapeRegion {
  double x, y, radius;
  public CircRegion(double x, double y, double radius) {
    this.x = x; this.y = y; this.radius = radius;
  }
  public boolean contains(Point p) {
    return (Math.sqrt(Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2)))
      <= this.radius;
  }
}
class IntersectRegion extends ShapeRegion {
  Region reg1, reg2;
  public IntersectRegion(Region reg1, Region reg2) {
    this.reg1 = reg1;
    this.reg2 = reg2;
  }
  public boolean contains(Point p) {
    return this.reg1.contains(p) && this.reg2.contains(p);
  }
}
class UnionRegion extends ShapeRegion {
  Region reg1, reg2;
  public UnionRegion(Region reg1, Region reg2) {
    this.reg1 = reg1;
    this.reg2 = reg2;
  }
  public boolean contains(Point p) {
    return this.reg1.contains(p) || this.reg2.contains(p);
  }
}
class SubtractRegion extends ShapeRegion {
  Region reg1, reg2;
  public SubtractRegion(Region reg1, Region reg2) {
    this.reg1 = reg1;
    this.reg2 = reg2;
  }
  public boolean contains(Point p) {
    return this.reg1.contains(p) && !this.reg2.contains(p);
  }
}
class NegateRegion extends ShapeRegion {
  Region reg;
  public NegateRegion(Region reg) {
    this.reg = reg;
  }
  public boolean contains(Point p) {
    return !this.reg.contains(p);
  }
}

















public class Handout {
  // drawRegion : takes a grid (char[][]) and a Region,
  // and sets all the indices contained in the Region to 'X'
  // and all others to ' '
  public static void drawRegion(char[][] grid, Region r) {
    for(int i = 0; i < grid.length; i += 1) {
      for(int j = 0; j < grid.length; j += 1) {
        Point p = new Point((double)i, (double)j); // cast
        if(r.contains(p)) {
          grid[i][j] = 'X';
        }
        else {
          grid[i][j] = ' ';
        }
      }
    }
  }
  public static void printGrid(char[][] grid) {
    for(int i = 0; i < grid.length; i += 1) {
      for(int j = 0; j < grid.length; j += 1) {
        System.out.print(grid[j][grid.length - i - 1] + " ");
      }
      System.out.println();
    }
  }
  public static void main(String[] args) {
    char[][] grid = new char[25][25];

    Region rectangle = new RectRegion(0, 0, 25, 20);
    Region bigCircle = new CircRegion(12, 10, 8);
    Region smallCircle = new CircRegion(12, 10, 4);

// Each of the commented-out regions of code below constructs a region
// that, when drawn, looks like:

/*




X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X   X X X X X X X X X X X X
X X X X X X X X X               X X X X X X X X X
X X X X X X X                       X X X X X X X
X X X X X X                           X X X X X X
X X X X X X             X             X X X X X X
X X X X X           X X X X X           X X X X X
X X X X X         X X X X X X X         X X X X X
X X X X X         X X X X X X X         X X X X X
X X X X         X X X X X X X X X         X X X X
X X X X X         X X X X X X X         X X X X X
X X X X X         X X X X X X X         X X X X X
X X X X X           X X X X X           X X X X X
X X X X X X             X             X X X X X X
X X X X X X                           X X X X X X
X X X X X X X                       X X X X X X X
X X X X X X X X X               X X X X X X X X X
X X X X X X X X X X X X   X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X
*/

/*
    Region s = new SubtractRegion(rectangle, bigCircle);
    Region r = new UnionRegion(s, smallCircle);
*/

/*
    Region ring = new SubtractRegion(bigCircle, smallCircle);
    Region r = new SubtractRegion(rectangle, ring);
*/

/*
    Region r = new UnionRegion(
      new SubtractRegion(
        new RectRegion(0, 0, 25, 20),
        new CircRegion(12, 10, 8)),
      new CircRegion(12, 10, 4));
*/

    // This approach uses the abstract class's implementations of subtract and add
    Region r = rectangle.subtract(bigCircle).add(smallCircle);

    drawRegion(grid, r);
    printGrid(grid);
  }
}

