import java.io.*;
import java.util.*;
 
public class SandBox
{
  public SandBox()
  {
    sand = new char[MAX];
    status = new int[MAX];
    statusReverse = new int[MAX];
    rangeBegin = MAX - 1;
    rangeEnd = 0;
    nWords = 0;
  }

  void addWord(String s, int loc)
  {
    if (loc < 0 || (loc + s.length() - 1) >= MAX) {
      System.out.println("PALINDROME TOO BIG\n");
      return;
    }
    // System.out.println("adding " + s + " at " + loc);

    nWords++;

    for (int i = 0; i < s.length(); i++) {
      int sandLoc = loc + i;
      if (sandLoc < rangeBegin || sandLoc > rangeEnd) {
	status[sandLoc] = TAKEN;
	statusReverse[sandLoc] = LOOSE;
	sand[sandLoc] = s.charAt(i);
      }
      else {
	if (sand[sandLoc] != s.charAt(i))
	  System.out.println("INTERNAL INCONSISTENCY spot1");
	if (status[sandLoc] != LOOSE)
	  System.out.println("INTERNAL INCONSISTENCY spot2");
	if (statusReverse[sandLoc] != TAKEN && 
	    statusReverse[sandLoc] != WORDSTART)
	  System.out.println("INTERNAL INCONSISTENCY spot3");
	status[sandLoc] = TAKEN;
      }
    }
    status[loc] = WORDSTART;

    if (rangeBegin > loc)
      rangeBegin = loc;
    if (rangeEnd < (loc + s.length() - 1))
      rangeEnd = (loc + s.length() - 1);
  }

  void addWordReverse(String s, int loc)
  {
    if (loc >= MAX || (loc - s.length() + 1) < 0) {
      System.out.println("PALINDROME TOO BIG\n");
      return;
    }
    // System.out.println("reverseadding " + s + " at " + loc);

    nWords++;

    for (int i = 0; i < s.length(); i++) {
      int sandLoc = loc - i;
      if (sandLoc < rangeBegin || sandLoc > rangeEnd) {
	statusReverse[sandLoc] = TAKEN;
	status[sandLoc] = LOOSE;
	sand[sandLoc] = s.charAt(i);
      }
      else {
	if (sand[sandLoc] != s.charAt(i))
	  System.out.println("INTERNAL INCONSISTENCY spot4 " + sand[sandLoc]
			     + " " + s.charAt(i) + " " + sandLoc + " " + i);
	if (statusReverse[sandLoc] != LOOSE)
	  System.out.println("INTERNAL INCONSISTENCY spot5");
	if (status[sandLoc] != TAKEN && status[sandLoc] != WORDSTART)
	  System.out.println("INTERNAL INCONSISTENCY spot6");
	statusReverse[sandLoc] = TAKEN;
      }
    }
    statusReverse[loc] = WORDSTART;

    if (rangeBegin > loc - s.length() + 1)
      rangeBegin = loc - s.length() + 1;
    if (rangeEnd < loc)
      rangeEnd = loc;
  }

  void print()
  {
    if (Complete()) {
      printComplete();
      if (CompleteReverse())    // if both, print both ways
	printCompleteReverse();
    }
    else if (CompleteReverse()) {
      printCompleteReverse();
    }
    else {  // not complete
      printIncomplete();
    }
  }

  void printIncomplete()
  {
    for (int i = rangeBegin; i <= rangeEnd; i++) {
      if (status[i] == LOOSE)
	System.out.print("[" + sand[i] + "] ");
      else if (status[i] == TAKEN)
	System.out.print(sand[i]);
      else if (status[i] == WORDSTART)
	System.out.print(" " + sand[i]);
      else
	System.out.println("\nINTERNAL INCONSISTENCY spot7");
    }

    System.out.print(" || ");

    for (int i = rangeEnd; i >= rangeBegin; i--) {
      if (statusReverse[i] == LOOSE)
	System.out.print("[" + sand[i] + "] ");
      else if (statusReverse[i] == TAKEN)
	System.out.print(sand[i]);
      else if (statusReverse[i] == WORDSTART)
	System.out.print(" " + sand[i]);
      else
	System.out.println("\nINTERNAL INCONSISTENCY spot8");
    }

    System.out.println("");
  }

  void printComplete()
  {
    for (int i = rangeBegin; i <= rangeEnd-1; i++) {
      if (status[i] == LOOSE)
	System.out.println("\nINTERNAL INCONSISTENCY spot12");
      else if (status[i] == TAKEN)
	System.out.print(sand[i]);
      else if (status[i] == WORDSTART)
	System.out.print(" " + sand[i]);
    }

    if (status[rangeEnd] == TAKEN)
      System.out.print(sand[rangeEnd]);
    else if (status[rangeEnd] == WORDSTART)
      System.out.print(" " + sand[rangeEnd]);

    if (statusReverse[rangeEnd] != LOOSE)
      System.out.print(" " + sand[rangeEnd]);

    if (status[rangeEnd] == LOOSE && statusReverse[rangeEnd] == LOOSE)
	System.out.println("\nINTERNAL INCONSISTENCY spot18");

    for (int i = rangeEnd-1; i >= rangeBegin; i--) {
      if (statusReverse[i] == LOOSE)
	System.out.println("\nINTERNAL INCONSISTENCY spot13");
      else if (statusReverse[i] == TAKEN)
	System.out.print(sand[i]);
      else if (statusReverse[i] == WORDSTART)
	System.out.print(" " + sand[i]);
    }

    System.out.println("");
  }

  void printCompleteReverse()
  {
    for (int i = rangeEnd; i >= rangeBegin+1; i--) {
      if (statusReverse[i] == LOOSE)
	System.out.println("\nINTERNAL INCONSISTENCY spot14");
      else if (statusReverse[i] == TAKEN)
	System.out.print(sand[i]);
      else if (statusReverse[i] == WORDSTART)
	System.out.print(" " + sand[i]);
    }

    if (statusReverse[rangeBegin] == TAKEN)
      System.out.print(sand[rangeBegin]);
    else if (statusReverse[rangeBegin] == WORDSTART)
      System.out.print(" " + sand[rangeBegin]);

    if (status[rangeBegin] != LOOSE)
      System.out.print(" " + sand[rangeBegin]);

    if (status[rangeBegin] == LOOSE && statusReverse[rangeBegin] == LOOSE)
	System.out.println("\nINTERNAL INCONSISTENCY spot17");

    for (int i = rangeBegin+1; i <= rangeEnd; i++) {
      if (status[i] == LOOSE)
	System.out.println("\nINTERNAL INCONSISTENCY spot15");
      else if (status[i] == TAKEN)
	System.out.print(sand[i]);
      else if (status[i] == WORDSTART)
	System.out.print(" " + sand[i]);
    }

    System.out.println("");
  }

  String getString()
  {
    return new String(sand, rangeBegin, rangeEnd - rangeBegin + 1);
  }

  int length()
  {
    if (rangeBegin > rangeEnd)
      return 0;
    else
      return rangeEnd - rangeBegin + 1;
  }

  Vacancy Island()
  {
    int left, right;

    for(left=rangeBegin; left <= rangeEnd && status[left] == LOOSE; left++) ;
    if (left > rangeEnd) return new Vacancy("", 0);
    for(; left <= rangeEnd && status[left] != LOOSE; left++) ;
    if (left > rangeEnd) return new Vacancy("", 0);
    for(right=left; right <= rangeEnd && status[right] == LOOSE; right++) ;
    if (right > rangeEnd) return new Vacancy("", 0);
    // at this point, the island is from left to right-1
    String s = new String(sand, left, right-left);
    return new Vacancy(s, left);
  }

  Vacancy IslandReverse()
  {
    int left, right;

    for(right=rangeEnd; right >= rangeBegin && 
	  statusReverse[right] == LOOSE; right--) ;
    if (right < rangeBegin) return new Vacancy("", 0);
    for(; right >= rangeBegin && statusReverse[right] != LOOSE; right--) ;
    if (right < rangeBegin) return new Vacancy("", 0);
    for(left=right; left >= rangeBegin && 
	  statusReverse[left] == LOOSE; left--) ;
    if (left < rangeBegin) return new Vacancy("", 0);
    // at this point, the island is from right down to left+1
    String s = new String(sand, left+1, right-left);
    s = new String(new StringBuffer(s).reverse());
    return new Vacancy(s, right);
  }

  Vacancy RightEdge()
  {
    int right, left;
    left = right = rangeEnd;
    for (; left >= rangeBegin && status[left] == LOOSE; left--) ;
    // at this point, the edge is from left+1 to right
    String s = new String(sand, left+1, right-left) ;
    return new Vacancy(s, left+1);
  }

  Vacancy RightEdgeReverse()
  {
    int right, left;
    left = right = rangeBegin;
    for (; right <= rangeEnd && statusReverse[right] == LOOSE; right++) ;
    // at this point, the edge is from right-1 down to left
    String s = new String(sand, left, right-left) ;
    s = new String(new StringBuffer(s).reverse());
    return new Vacancy(s, right-1);
  }

  Vacancy LeftEdge()
  {
    int right, left;
    left = right = rangeBegin;
    for (; right <= rangeEnd && status[right] == LOOSE; right++) ;
    // at this point, the edge is from left to right-1
    String s = new String(sand, left, right-left);
    return new Vacancy(s, right-1);
  }

  Vacancy LeftEdgeReverse()
  {
    int right, left;
    left = right = rangeEnd;
    for (; left >= rangeBegin && statusReverse[left] == LOOSE; left--) ;
    // at this point, the edge is from right down to left+1 
    String s = new String(sand, left+1, right-left);
    s = new String(new StringBuffer(s).reverse());
    return new Vacancy(s, left+1);
  }

  int numwords()
  {
    return nWords;
  }

  // check completed palindrome
  //	both directions
  //	allow for missing edge letter
  boolean Complete()
  {
    return (Island().length() == 0 &&
	    IslandReverse().length() == 0 &&
	    LeftEdge().length() == 0 &&
	    RightEdgeReverse().length() == 0 &&
	    ((RightEdge().length() < 2 &&
	      LeftEdgeReverse().length() == 0) ||
	     (RightEdge().length() == 0 &&
	      LeftEdgeReverse().length() < 2)));
  }

  boolean CompleteReverse()
  {
    return (Island().length() == 0 &&
	    IslandReverse().length() == 0 &&
	    RightEdge().length() == 0 &&
	    LeftEdgeReverse().length() == 0 &&
	    ((LeftEdge().length() < 2 &&
	      RightEdgeReverse().length() == 0) ||
	     (LeftEdge().length() == 0 &&
	      RightEdgeReverse().length() < 2)));
  }

  SandBox copy()
  {
    // note: only spend time copying characters between rangeBegin and rangeEnd

    SandBox dup = new SandBox();
    dup.rangeBegin = rangeBegin;
    dup.rangeEnd = rangeEnd;
    dup.nWords = nWords;
    for (int i=rangeBegin; i <= rangeEnd; i++) {
      dup.sand[i] = sand[i];
      dup.status[i] = status[i];
      dup.statusReverse[i] = statusReverse[i];
    }

    return dup;
  }

  char getLetter(int i)
  {
    return( sand[rangeBegin + i] );
  }

  boolean used(int i)
  {
    return status[rangeBegin + i] != LOOSE;
  }

  boolean usedReverse(int i)
  {
    return statusReverse[rangeBegin + i] != LOOSE;
  }

  private char[] sand;
  private int[] status;   // LOOSE, TAKEN or WORDSTART
  private int[] statusReverse;
  private int rangeBegin, rangeEnd; // Where characters have been added
  private int nWords;

  final static int MAX = 300;
  final static int LOOSE = 1;      // not part of a word
  final static int TAKEN = 2;      // part of a word
  final static int WORDSTART = 3;  // first letter of a word
}
