001package com.hfg.math; 002 003import com.hfg.util.collection.CollectionUtil; 004 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.List; 008 009//------------------------------------------------------------------------------ 010/** 011 * Iterator that steps through the possible combinations of a List of Lists. 012 * @author J. Alex Taylor, hairyfatguy.com 013 */ 014//------------------------------------------------------------------------------ 015// com.hfg Library 016// 017// This library is free software; you can redistribute it and/or 018// modify it under the terms of the GNU Lesser General Public 019// License as published by the Free Software Foundation; either 020// version 2.1 of the License, or (at your option) any later version. 021// 022// This library is distributed in the hope that it will be useful, 023// but WITHOUT ANY WARRANTY; without even the implied warranty of 024// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 025// Lesser General Public License for more details. 026// 027// You should have received a copy of the GNU Lesser General Public 028// License along with this library; if not, write to the Free Software 029// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 030// 031// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 032// jataylor@hairyfatguy.com 033//------------------------------------------------------------------------------ 034 035public class ListCombinationIterator<T> 036{ 037 private List<List<T>> mListOfLists; 038 private Range<Integer> mCombinationSizeRange; 039 private Integer mCurrentCombinationSize; 040 private List<List<Integer>> mCurrentPositionIndexCombinations; 041 private int mCurrentPositionIndexCombinationsIndex; 042 private List<Integer> mCurrentPositionIndices; 043 private int[] mCurrentIndices; 044 private int mSize; 045 046 //########################################################################### 047 // CONSTRUCTORS 048 //########################################################################### 049 050 //--------------------------------------------------------------------------- 051 public ListCombinationIterator(List<? extends Collection<T>> inListOfLists) 052 { 053 this(inListOfLists, inListOfLists != null ? new Range<>(inListOfLists.size(), inListOfLists.size()) : null); 054 } 055 056 //--------------------------------------------------------------------------- 057 public ListCombinationIterator(List<? extends Collection<T>> inListOfLists, Range<Integer> inCombinationSizeRange) 058 { 059 if (inListOfLists.get(0) instanceof List) 060 { 061 mListOfLists = (List<List<T>>) inListOfLists; 062 } 063 else 064 { 065 mListOfLists = new ArrayList<>(inListOfLists.size()); 066 for (Collection<T> collection : inListOfLists) 067 { 068 mListOfLists.add(new ArrayList<>(collection)); 069 } 070 } 071 mCombinationSizeRange = inCombinationSizeRange; 072 073 // Set the size - the total number of combinations 074 if (CollectionUtil.hasValues(mListOfLists)) 075 { 076 mSize = 0; 077 078 Integer minSize = (inCombinationSizeRange != null && inCombinationSizeRange.getStart() != null ? inCombinationSizeRange.getStart() : 1); 079 Integer maxSize = (inCombinationSizeRange != null && inCombinationSizeRange.getEnd() != null ? inCombinationSizeRange.getEnd() : mListOfLists.size()); 080 if (maxSize > mListOfLists.size()) 081 { 082 maxSize = mListOfLists.size(); 083 } 084 085 mCombinationSizeRange = new Range<>(); 086 mCombinationSizeRange.setStart(minSize).setEnd(maxSize); 087 088 for (int combinationSize = minSize; combinationSize <= maxSize; combinationSize++) 089 { 090 List<Integer> indexList = new ArrayList<>(inListOfLists.size()); 091 for (int i = 0; i < inListOfLists.size(); i++) 092 { 093 indexList.add(i); 094 } 095 096 List<List<Integer>> indexCombinations = Combinations.combinations(indexList, combinationSize); 097 098 for (List<Integer> indices : indexCombinations) 099 { 100 int size = 1; 101 for (int index : indices) 102 { 103 size *= inListOfLists.get(index).size(); 104 } 105 106 mSize += size; 107 } 108 } 109 } 110 } 111 112 //########################################################################### 113 // PUBLIC METHODS 114 //########################################################################### 115 116 //--------------------------------------------------------------------------- 117 private List<List<Integer>> getPositionIndexCombinations() 118 { 119 List<Integer> indexList = new ArrayList<>(mListOfLists.size()); 120 for (int i = 0; i < mListOfLists.size(); i++) 121 { 122 indexList.add(i); 123 } 124 125 return Combinations.combinations(indexList, mCurrentCombinationSize); 126 } 127 128 //--------------------------------------------------------------------------- 129 public boolean hasNext() 130 { 131 boolean hasNext = false; 132 if (CollectionUtil.hasValues(mListOfLists)) 133 { 134 if (null == mCurrentCombinationSize) 135 { 136 mCurrentCombinationSize = mCombinationSizeRange.getStart(); 137 hasNext = true; 138 } 139 else 140 { 141 while (! hasNext 142 && mCurrentCombinationSize <= mCombinationSizeRange.getEnd() 143 && mCurrentPositionIndexCombinationsIndex < mCurrentPositionIndexCombinations.size()) 144 { 145 if (null == mCurrentPositionIndexCombinations 146 || mCurrentPositionIndexCombinationsIndex < mCurrentPositionIndexCombinations.size() - 1 147 || null == mCurrentIndices) 148 { 149 hasNext = true; 150 } 151 else 152 { 153 for (int i = 0; i < mCurrentIndices.length; i++) 154 { 155 if (mCurrentIndices[i] < mListOfLists.get(mCurrentPositionIndices.get(i)).size() - 1) 156 { 157 hasNext = true; 158 break; 159 } 160 } 161 162 if (! hasNext) 163 { 164 // We've hit the end of combinations for this combination size. 165 mCurrentCombinationSize++; 166 if (mCurrentCombinationSize <= mCombinationSizeRange.getEnd()) 167 { 168 mCurrentPositionIndexCombinations = getPositionIndexCombinations(); 169 mCurrentPositionIndexCombinationsIndex = 0; 170 mCurrentPositionIndices = mCurrentPositionIndexCombinations.get(mCurrentPositionIndexCombinationsIndex); 171 mCurrentIndices = null; 172 } 173 } 174 } 175 } 176 } 177 } 178 179 return hasNext; 180 } 181 182 //--------------------------------------------------------------------------- 183 public List<T> next() 184 { 185 List<T> combination = null; 186 187 if (null == mCurrentCombinationSize 188 || mCurrentCombinationSize <= mCombinationSizeRange.getEnd()) 189 { 190 boolean combinationFound = false; 191 192 193 if (null == mCurrentCombinationSize) 194 { 195 mCurrentCombinationSize = mCombinationSizeRange.getStart(); 196 } 197 198 if (null == mCurrentPositionIndexCombinations) 199 { 200 mCurrentPositionIndexCombinations = getPositionIndexCombinations(); 201 mCurrentPositionIndexCombinationsIndex = 0; 202 mCurrentPositionIndices = mCurrentPositionIndexCombinations.get(mCurrentPositionIndexCombinationsIndex); 203 } 204 205 if (null == mCurrentIndices) 206 { 207 mCurrentIndices = new int[mCurrentCombinationSize]; 208 for (int i = 0; i < mCurrentCombinationSize; i++) 209 { 210 mCurrentIndices[i] = 0; 211 } 212 213 combinationFound = true; 214 } 215 else 216 { 217 // Iterate the indices 218 int i; 219 220 while (! combinationFound 221 && mCurrentCombinationSize <= mCombinationSizeRange.getEnd() 222 && mCurrentPositionIndexCombinationsIndex < mCurrentPositionIndexCombinations.size()) 223 { 224 for (i = 0; i < mCurrentCombinationSize; i++) 225 { 226 if (mCurrentIndices[i] < mListOfLists.get(mCurrentPositionIndices.get(i)).size() - 1) 227 { 228 mCurrentIndices[i]++; 229 break; 230 } 231 else 232 { 233 mCurrentIndices[i] = 0; 234 } 235 } 236 237 // Have we run out of combinations for this set of positions? 238 if (i < mCurrentCombinationSize) 239 { 240 combinationFound = true; 241 } 242 else 243 { 244 mCurrentPositionIndexCombinationsIndex++; 245 if (mCurrentPositionIndexCombinationsIndex >= mCurrentPositionIndexCombinations.size()) 246 { 247 mCurrentCombinationSize++; 248 if (mCurrentCombinationSize <= mCombinationSizeRange.getEnd()) 249 { 250 mCurrentPositionIndexCombinations = getPositionIndexCombinations(); 251 mCurrentPositionIndexCombinationsIndex = 0; 252 mCurrentPositionIndices = mCurrentPositionIndexCombinations.get(mCurrentPositionIndexCombinationsIndex); 253 mCurrentIndices = new int[mCurrentCombinationSize]; 254 for (int index = 0; index < mCurrentCombinationSize; index++) 255 { 256 mCurrentIndices[index] = 0; 257 } 258 259 combinationFound = true; 260 } 261 } 262 else 263 { 264 mCurrentPositionIndices = mCurrentPositionIndexCombinations.get(mCurrentPositionIndexCombinationsIndex); 265 for (int index = 0; index < mCurrentCombinationSize; index++) 266 { 267 mCurrentIndices[index] = 0; 268 } 269 270 combinationFound = true; 271 } 272 } 273 } 274 } 275 276 if (combinationFound) 277 { 278 combination = new ArrayList<>(mCurrentCombinationSize); 279 280 for (int i = 0; i < mCurrentCombinationSize; i++) 281 { 282 combination.add(mListOfLists.get(mCurrentPositionIndices.get(i)).get(mCurrentIndices[i])); 283 } 284 } 285 } 286 287 return combination; 288 } 289 290 //--------------------------------------------------------------------------- 291 public int size() 292 { 293 return mSize; 294 } 295 296 //--------------------------------------------------------------------------- 297 public List<List<T>> getAll() 298 { 299 mCurrentCombinationSize = null; 300 mCurrentPositionIndexCombinations = null; 301 mCurrentPositionIndices = null; 302 mCurrentIndices = null; 303 304 List<List<T>> combinations = new ArrayList<>(mSize); 305 while (hasNext()) 306 { 307 combinations.add(next()); 308 } 309 310 return combinations; 311 } 312}