```
import java.util.*;
import java.math.BigInteger;

class Split {
// OVERVIEW: Record type for returning a split.
public /*@non_null@*/ SpeciesSet left;
public /*@non_null@*/ SpeciesSet right;

public Split (/*@non_null@*/ SpeciesSet left, /*@non_null@*/ SpeciesSet right) {
this.left = left; this.right = right;
}

public String toString () {
return "< " + left.toString () + ", " + right.toString () + ">";
}
}

public class AllTrees {
// OVERVIEW: AllTrees provides the allTreesRoot method that returns the set of all possible
//    trees that can be produced from s with a given root.

private static Split [] allSplits (SpeciesSet s)
//@ensures \nonnullelements (\result)
// EFFECTS: Returns an array of splits showing all possible ways of splitting the s into two groups.
//     e.g., allSplits ({a, b, c}) = [<{a, b, c}, {}>, <{a, b}, {c}>, <{a}, {b, c}>, <{a, c}, {b}>]
{
if (s == null) { return null; }

// There are 2^n groups, we can enumerate them using the bits
BigInteger numgroups = BigInteger.valueOf (2).pow (s.size ()); //@nowarn Null ;
//@assume numgroups != null; // ESC/Java spec for BigInteger is missing, need to assume.

int numiters = numgroups.intValue () / 2;
//@assume numiters >= 0;

Split [] result = new Split [numiters];
Species [] elements = s.toArray ();

BigInteger b = BigInteger.ZERO;
//@assume b != null;

for (int i = 0; i < numiters; i++) {
SpeciesSet left = new SpeciesSet ();
SpeciesSet right = new SpeciesSet ();

for (int bitno = 0; bitno < numgroups.bitLength () - 1; bitno++) {
//@assume bitno < elements.length ;

if (b.bitLength () >= bitno && b.testBit (bitno)) {
right.insert (elements [bitno]);
} else {
left.insert (elements [bitno]);
}
}

result[i] = new Split (left, right); //@nowarn IndexNegative, IndexTooBig
//@assume b != null
}

return result;
} //@nowarn Post // can't establish no null elements postcondition

public static /*@non_null@*/ SpeciesTreeSet allTreesRoot (SpeciesSet s, /*@non_null@*/ Species r) {
// EFFECTS: Returns the set of all trees with root r, and leaves s.
SpeciesTreeSet res = new SpeciesTreeSet ();

if (s == null || s.isEmpty ()) {
res.insert (new SpeciesTree (r, null, null));
return res;
}

Split [] splits = allSplits (s);

if (splits == null) {
System.err.println ("BUG: no splits (shouldn't happen when s is non-empty)");
System.exit (1);
}

for (int i = 0; i < splits.length; i++) {
Split current = splits[i];
SpeciesTreeSet ltrees = allTrees (current.left);
SpeciesTreeSet rtrees = allTrees (current.right);

Enumeration liter = ltrees.elements ();

if (!ltrees.isEmpty ()) {
while (liter.hasMoreElements ()) {
SpeciesTree ltree = (SpeciesTree) liter.nextElement ();

if (!rtrees.isEmpty ()) {
Enumeration riter = rtrees.elements ();

while (riter.hasMoreElements ()) {
SpeciesTree rtree = (SpeciesTree) riter.nextElement ();
res.insert (new SpeciesTree (r, ltree, rtree));
}
} else {
res.insert (new SpeciesTree (r, ltree, null));
}
}
} else {
System.err.println ("BUG: left trees cannot be empty!");
System.exit (1);
}
}

return res;
}

private static /*@non_null@*/ SpeciesTreeSet allTrees (SpeciesSet s) {
// EFFECTS: Returns the set of all possible trees that can be produced from s.

if (s == null || s.isEmpty ()) {
return new SpeciesTreeSet ();
}

SpeciesTreeSet res = new SpeciesTreeSet ();
Species [] elements = s.toArray ();

if (elements.length == 1) {
res.insert (new SpeciesTree (elements[0]));
} else {
for (int i = 0; i < elements.length; i++) {
// Create a new set with all elements except this one.
SpeciesSet rest = new SpeciesSet (s);
rest.remove (elements[i]);
res.union (allTreesRoot (rest, elements[i]));
}
}

return res;
}
}

```