public void testWordFrequency() {
    WordPredictor p = new WordPredictor();

    p.train("a big brown bear".split(" "));
    p.train("a big brown bench".split(" "));
    p.train("a big yellow banana".split(" "));

    {
      List<Prediction> pns = p.predictWord("big");
      assertEquals(1, pns.size());
      assertPrediction("brown", 100d, pns.get(0));
    }

    p.train("a big yellow duck".split(" "));

    {
      List<Prediction> pns = p.predictWord("big");
      assertEquals(2, pns.size());
      assertPrediction("brown", 50d, pns.get(0));
      assertPrediction("yellow", 50d, pns.get(1));
    }

    p.train("a big yellow daisy".split(" "));

    {
      List<Prediction> pns = p.predictWord("big");
      assertEquals(2, pns.size());
      assertPrediction("yellow", (3d / 5) * 100, pns.get(0));
      assertPrediction("brown", (2d / 5) * 100, pns.get(1));
    }
  }
  public void testEmptyInput() {
    WordPredictor p = new WordPredictor();

    {
      try {
        p.train(null);
        fail("should throw NPE on null input");
      } catch (NullPointerException e) {
        // OK
      }
    }

    p.train("a big brown bear".split(" "));
    p.train("a big brown bench".split(" "));

    {
      // Empty string should give empty predictions even after training
      assertEquals(Collections.emptyList(), p.predictWord(""));
    }

    {
      try {
        p.predictWord(null);
        fail("Should throw NPE on null input");
      } catch (NullPointerException e) {
        // OK
      }
    }
  }
  public void testReset() {
    WordPredictor p = new WordPredictor();

    p.train("a big brown bath".split(" "));
    p.train("a big brown bath".split(" "));
    {
      List<Prediction> pns = p.predictWord("a");
      assertEquals(1, pns.size());
      assertPrediction("big", 100d, pns.get(0));
    }

    p.reset();

    {
      assertEquals(Collections.emptyList(), p.predictWord("a"));
    }
  }
  public void testWordPredictor() {
    WordPredictor p = new WordPredictor();

    {
      // Should be empty before training
      assertEquals(Collections.emptyList(), p.predictWord("a"));
    }

    p.train("a big brown bear".split(" "));
    p.train("a big brown bench".split(" "));

    {
      List<Prediction> pns = p.predictWord("a");
      assertEquals(1, pns.size());
      assertPrediction("big", 100d, pns.get(0));
    }
    {
      List<Prediction> pns = p.predictWord("big");
      assertEquals(1, pns.size());
      assertPrediction("brown", 100d, pns.get(0));
    }
    {
      // Word must occur more than once to be considered interesting.
      assertEquals(Collections.emptyList(), p.predictWord("brown"));
      assertEquals(Collections.emptyList(), p.predictWord("bear"));
      assertEquals(Collections.emptyList(), p.predictWord("foo"));
    }
  }
  public void testMinFrequency() {
    WordPredictor p = new WordPredictor();

    for (int i = 0; i < 2; i++) {
      p.train("a big brown bear".split(" "));
      p.train("a big brown bench".split(" "));
      p.train("a big brown bazooka".split(" "));
      p.train("a big brown bazinga".split(" "));
      p.train("a big brown balloon".split(" "));
      p.train("a big brown boulder".split(" "));
      p.train("a big brown blanket".split(" "));
      p.train("a big brown balcony".split(" "));
      p.train("a big brown binder".split(" "));
      p.train("a big brown book".split(" "));
    }

    {
      List<Prediction> pns = p.predictWord("brown");
      assertEquals(10, pns.size());
      assertPrediction("balcony", 10d, pns.get(0));
      assertPrediction("boulder", 10d, pns.get(9));
    }

    // Add an eleventh distinct word after "brown", making none of the
    // predictions higher than the minimum threshold for being considered
    // interesting.
    p.train("a big brown bath".split(" "));
    p.train("a big brown bath".split(" "));

    {
      assertEquals(Collections.emptyList(), p.predictWord("brown"));
    }
    {
      List<Prediction> pns = p.predictWord("a");
      assertEquals(1, pns.size());
      assertPrediction("big", 100d, pns.get(0));
    }
  }