package bjc.utils.funcutils; import java.util.function.Consumer; import java.util.function.Function; import bjc.utils.data.GenHolder; import bjc.utils.funcdata.FunctionalList; /** * Utilities for manipulating FunctionalLists that don't belong in the * class itself * * @author ben * */ public class ListUtils { private static final int MAX_NTRIESPART = 50; /** * Implements a single group partitioning pass on a list * * @author ben * * @param * The type of element in the list being partitioned */ private static final class GroupPartIteration implements Consumer { private FunctionalList> ret; private GenHolder> currPart; private FunctionalList rejects; private GenHolder numInCurrPart; private int nPerPart; private Function eleCount; public GroupPartIteration(FunctionalList> ret, GenHolder> currPart, FunctionalList rejects, GenHolder numInCurrPart, int nPerPart, Function eleCount) { this.ret = ret; this.currPart = currPart; this.rejects = rejects; this.numInCurrPart = numInCurrPart; this.nPerPart = nPerPart; this.eleCount = eleCount; } @Override public void accept(E val) { if (numInCurrPart.unwrap((vl) -> vl >= nPerPart)) { ret.add(currPart.unwrap((vl) -> vl)); currPart.transform((vl) -> new FunctionalList<>()); numInCurrPart.transform((vl) -> 0); } else { int vCount = eleCount.apply(val); if (numInCurrPart .unwrap((vl) -> (vl + vCount) >= nPerPart)) { rejects.add(val); } else { currPart.unwrap((vl) -> vl.add(val)); numInCurrPart.transform((vl) -> vl + vCount); } } } } /** * Partition a list into a list of lists, where each element can count * for more than one element in a partition * * @param * The type of elements in the list to partition * * @param list * The list to partition * @param eleCount * The function to determine the count for each element for * @param nPerPart * The number of elements to put in each partition * @return A list partitioned according to the above rules */ public static FunctionalList> groupPartition( FunctionalList list, Function eleCount, int nPerPart) { /* * List that holds our results */ FunctionalList> ret = new FunctionalList<>(); /* * List that holds current partition */ GenHolder> currPart = new GenHolder<>(new FunctionalList<>()); /* * List that holds elements rejected during current pass */ FunctionalList rejects = new FunctionalList<>(); /* * The effective number of elements in the current partitition */ GenHolder numInCurrPart = new GenHolder<>(0); /* * Run up to a certain number of passes */ for (int nIterations = 0; nIterations < MAX_NTRIESPART && !rejects.isEmpty(); nIterations++) { list.forEach(new GroupPartIteration<>(ret, currPart, rejects, numInCurrPart, nPerPart, eleCount)); if (rejects.isEmpty()) { // Nothing was rejected, so we're done return ret; } } throw new IllegalArgumentException("Heuristic (more than " + MAX_NTRIESPART + " iterations of partitioning) detected unpartitionable list " + list.toString() + "\nThe following elements were not partitioned: " + rejects.toString() + "\nCurrent group in formation: " + currPart.unwrap((vl) -> vl.toString()) + "\nPreviously formed groups: " + ret.toString()); } }