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 b = b.add (BigInteger.ONE); //@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; } }