/* An interface to a list of strings.  We will implement
   this interface so that we never _change_ existing
   StringLists, only create new ones (this can be useful
   practically, and will help us out pedagogically). */

interface StringList {
  /* Returns the number of strings in the list */
  int length();

  /* Returns a new StringList that contains the String s
   * first, followed by the existing elements */
  StringList add(String s);

  /* Returns true if the string is contained in the list,
   * false otherwise */
  boolean contains(String s);

  /* Returns a new list with all occurrences of the given string removed */
  StringList remove(String s);

  /* Returns the number of times s appears in the list */
  int count(String s);

  /* Returns the item at the given index, or null
   * if not found */
  String get(int index);
}

class SLLink implements StringList {
  String value;
  StringList rest;
  public SLLink(String value, StringList rest) {
    this.value = value;
    this.rest = rest;
  }
  public StringList add(String s) {
    return new SLLink(s, this);
  }

  public int length() {
    return 1 + this.rest.length();
  }

  public boolean contains(String s) {
    return this.value.equals(s) || this.rest.contains(s);
  }

  public StringList remove(String s) {
    if(s.equals(this.value)) { return this.rest.remove(s); }
    else {
      return new SLLink(this.value, this.rest.remove(s));
    }
  }

  public int count(String s) {
    if(this.value.equals(s)) {
      return 1 + this.rest.count(s);
    }
    else {
      return this.rest.count(s);
    }
  }

  public String get(int index) {
    if(index <= 0) { return this.value; }
    else { return this.rest.get(index - 1); }
  }

  public String toString() {
    return "SLLink(\"" + this.value + "\", " + this.rest.toString() + ")";
  }
}

class SLEmpty implements StringList {
  public int length() {
    return 0;
  }
  public boolean contains(String s) {
    return false;
  }
  public int count(String s) {
    return 0;
  }
  public StringList remove(String s) {
    return this;
  }
  public StringList add(String s) {
    return new SLLink(s, this);
  }
  public String get(int index) {
    return null;
  }
  public String toString() {
    return "SLEmpty";
  }
}

public class Links {
  public static void main(String args[]) {
    StringList s1 = new SLLink("a", new SLLink("b", new SLEmpty())); 
    StringList s2 = new SLLink("x", new SLLink("y",
      new SLLink("z", s1)));
    System.out.println(s1.toString() + " " + s1.length());
    System.out.println(s2.toString() + " " + s2.length());
    // A: 2  B: 3  C: 4  D: 5  E: 6
    System.out.println(new SLEmpty().length());

// Implement contains() in both classes

    System.out.println(s1.contains("y"));
    System.out.println(s2.contains("y"));
    System.out.println(s2.contains("a"));

/* What should these three lines print (once we have a
 * correct implementation of contains)?

  A: true        B: true
     true           false
     true           true

  C: false       D: false
     true           true
     true           false

*/
     
  }
}


