@Test
  public void testNot() {
    Var a = new Var("a");

    Formula f = (a.not());
    assertTrue("!a".equals(f.toString()));
  }
  @Test
  public void testIsAtomic() {
    Var a = new Var("a");
    Var b = new Var("b");

    assertTrue(a.isAtomic()); // a literal is atomic
    assertTrue(a.not().isAtomic()); // a literal and its negation is atomic

    Formula f = (a.and(b));
    assertFalse(f.isAtomic());
    assertTrue(f.getLeft().isAtomic() == true && f.getRight().isAtomic() == true);
  }
  @Test
  public void testToString() {
    Formula emptyFormula = new Formula();
    assertTrue(emptyFormula.toString().equals(StringUtils.EMPTY));

    Var a = new Var("a");
    Var b = new Var("b");
    Var c = new Var("c");
    Var d = new Var("d");

    Formula f = (a.and(b)).or(c.and(d));
    assertTrue("(a and b) or (c and d)".equals(f.toString()));

    f = (a.not().or(b)).and(c);
    assertTrue("(!a or b) and c".equals(f.toString()));
  }
  @Test
  public void testIsPossiblySatisfiedBy() throws ContradictionException, TimeoutException {
    Var P = new Var("P");
    Var Q = new Var("Q");
    Var R = new Var("R");
    Formula f = Formula.newInstanceforSAT((P.or(Q)).and(P.not().or(R))); // (P or Q) and (~P or R)

    // Possible assignments
    // ~P ~Q ~R
    // ~P ~Q  R
    // ~P  Q ~R (solution)
    // ~P  Q  R (solution
    //  P ~Q ~R
    //  P ~Q  R (solution)
    //  P  Q ~R
    //  P  Q  R (solution)

    BidiMap map = f.getVariableMap();
    assertTrue(map.get(1).equals("P"));
    assertTrue(map.get(2).equals("Q"));
    assertTrue(map.get(3).equals("R"));

    // The following partial models should pass
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {-1})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {1})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {-1, 2})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {1, 3})));

    // These partial models should not
    assertFalse(f.isPossiblySatisfiedBy(new Model(new int[] {-2, -3})));

    // Complete models, by definition, should satisfy (if solutions)
    assertFalse(f.isPossiblySatisfiedBy(new Model(new int[] {-1, -2, -3})));
    assertFalse(f.isPossiblySatisfiedBy(new Model(new int[] {-1, -2, 3})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {-1, 2, -3})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {-1, 2, 3})));
    assertFalse(f.isPossiblySatisfiedBy(new Model(new int[] {1, -2, -3})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {1, -2, 3})));
    assertFalse(f.isPossiblySatisfiedBy(new Model(new int[] {1, 2, -3})));
    assertTrue(f.isPossiblySatisfiedBy(new Model(new int[] {1, 2, 3})));
  }
  @Test
  public void testToCNFClauses() {
    Var a = new Var("a");
    Var b = new Var("b");
    Var c = new Var("c");
    Var d = new Var("d");

    Formula f = a.or(b).or(c).or(d);
    assertTrue("a or b or c or d".equals(f.toCNF().toString()));
    List<Clause> clauses = f.toCNFClauses();
    assertTrue(clauses.size() == 1);
    assertTrue("a or b or c or d".equals(clauses.get(0).toString()));

    f = (a.and(b)).or(c.and(d));
    assertTrue("(a or c) and (b or c) and (a or d) and (b or d)".equals(f.toCNF().toString()));
    clauses = f.toCNFClauses();
    assertTrue(clauses.size() == 4);
    assertTrue(clauses.contains(new Clause(a.or(c))));
    assertTrue(clauses.contains(new Clause(b.or(c))));
    assertTrue(clauses.contains(new Clause(a.or(d))));
    assertTrue(clauses.contains(new Clause(b.or(d))));

    f = (a.and(b)).or(c).or(d);
    assertTrue("(a or c or d) and (b or c or d)".equals(f.toCNF().toString()));
    clauses = f.toCNFClauses();
    assertTrue(clauses.size() == 2);
    assertTrue(clauses.contains(new Clause(a.or(c).or(d))));
    assertTrue(clauses.contains(new Clause(b.or(c).or(d))));

    f = ((a.and(b)).or(c).or(d)).not();
    assertTrue("(!a or !b) and !c and !d".equals(f.toCNF().toString()));
    clauses = f.toCNFClauses();
    assertTrue(clauses.size() == 3);
    assertTrue(clauses.contains(new Clause((a.not()).or(b.not()))));
    assertTrue(clauses.contains(new Clause(c.not())));
    assertTrue(clauses.contains(new Clause(d.not())));

    f = (a).not().not().not().not().not().not();
    assertTrue("a".equals(f.toCNF().toString()));
    clauses = f.toCNFClauses();
    assertTrue(clauses.size() == 1);
    assertTrue(clauses.contains(new Clause(a)));

    f = (a).not().not().not().not().not();
    assertTrue("!a".equals(f.toCNF().toString()));
    clauses = f.toCNFClauses();
    assertTrue(clauses.size() == 1);
    assertTrue(clauses.contains(new Clause(a.not())));

    f = ((a).and(b)).or((a).not().and(c)).or(((b).not()).and((c).not()));
    assertTrue(
        "(a or !a or !b) and (b or !a or !b) and (a or c or !b) and (b or c or !b) and (a or !a or !c) and (b or !a or !c) and (a or c or !c) and (b or c or !c)"
            .equals(f.toCNF().toString()));
    clauses = f.toCNFClauses();
    assertTrue(clauses.size() == 8);
    assertTrue(clauses.contains(new Clause(a.or(a.not()).or(b.not()))));
    assertTrue(clauses.contains(new Clause(b.or(a.not()).or(b.not()))));
    assertTrue(clauses.contains(new Clause(a.or(c).or(b.not()))));
    assertTrue(clauses.contains(new Clause(b.or(c).or(b.not()))));
    assertTrue(clauses.contains(new Clause(a.or(a.not()).or(c.not()))));
    assertTrue(clauses.contains(new Clause(b.or(a.not()).or(c.not()))));
    assertTrue(clauses.contains(new Clause(a.or(c).or(c.not()))));
    assertTrue(clauses.contains(new Clause(b.or(c).or(c.not()))));
  }