1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.callbackparams.combine;
18
19 import java.util.IdentityHashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.Random;
23
24
25
26
27 class Default2CombinationPicker implements AllPossible2Combinations.Picker {
28
29 private final int index;
30 private final ValueWrapper[] wrappedValues;
31 private int greatestCombinationCount = 1;
32 private int nbrOf2CombinationsToSatisfy = 0;
33 private final Random random;
34
35 private int totalCount;
36
37 class CombinationCounter {
38 private int count = 0;
39
40 public CombinationCounter() {
41 ++nbrOf2CombinationsToSatisfy;
42 }
43
44 int getCount() {return count; }
45
46 void increaseCount() {
47 if (1 == ++count) {
48 if (--nbrOf2CombinationsToSatisfy < 0) {
49 throw new AssertionError("Negativ number of 2-combinations"
50 + " at total count = " + totalCount);
51 }
52 } else if (greatestCombinationCount < count) {
53 greatestCombinationCount = count;
54 }
55 }
56
57
58 public String toString() {return "" + count;}
59 }
60
61 private transient Object[] record;
62 private transient int currentRecordIndex;
63 private transient int currentCount;
64 private transient final ValueWrapper[] options, selections;
65 private transient int nbrOfOptions, nbrOfSelections;
66 private transient boolean needyFirst;
67
68 private transient boolean selectedCountCorrect;
69 private transient int selectedTotalNeeds;
70
71 class ValueWrapper {
72
73 private final Object value;
74 private final Map[] combinationCounters = new Map[index];
75
76 private int valueCount = 0;
77
78 ValueWrapper(final Object value, final Object[][] allValues) {
79 this.value = value;
80
81 for (int mapIx = 0 ; mapIx < index ; ++mapIx) {
82 combinationCounters[mapIx] = new IdentityHashMap();
83
84 final Object[] combinationsOtherValues = allValues[mapIx];
85 for (int otherValuesIx = 0
86 ; otherValuesIx < combinationsOtherValues.length
87 ; ++otherValuesIx) {
88 combinationCounters[mapIx].put(
89 combinationsOtherValues[otherValuesIx],
90 new CombinationCounter());
91 }
92 }
93 }
94
95 private void select(boolean isCountCorrect, int totalNeeds) {
96 selectedCountCorrect = isCountCorrect;
97 selectedTotalNeeds = totalNeeds;
98 selections[0] = this;
99 nbrOfSelections = 1;
100 }
101
102 private CombinationCounter combinationCount() {
103 return (CombinationCounter) combinationCounters[currentRecordIndex]
104 .get(record[currentRecordIndex]);
105 }
106
107 private int totalNeeds() {
108 int needCount = 0;
109 for (Iterator iter =
110 combinationCounters[currentRecordIndex].values().iterator()
111 ; iter.hasNext() ;) {
112 final CombinationCounter cc = (CombinationCounter) iter.next();
113
114 if (currentCount == cc.getCount()) {
115 ++needCount;
116 }
117 }
118 return needCount;
119 }
120
121 void consider() {
122 final boolean isThisCountCorrect =
123 currentCount == combinationCount().getCount();
124 if (0 == nbrOfSelections) {
125 select(isThisCountCorrect, totalNeeds());
126 return;
127 }
128
129 if (selectedCountCorrect && false == isThisCountCorrect) {
130
131 return;
132 }
133
134 final int thisTotalNeeds = totalNeeds();
135
136 if (isThisCountCorrect == selectedCountCorrect
137 && thisTotalNeeds == selectedTotalNeeds) {
138 selections[nbrOfSelections] = this;
139 ++nbrOfSelections;
140 return;
141
142 } else if (false == isThisCountCorrect) {
143 if (thisTotalNeeds < selectedTotalNeeds) {
144 select(isThisCountCorrect, thisTotalNeeds);
145 } else {
146
147 }
148 return;
149
150 } else if (false == selectedCountCorrect
151 || needyFirst && selectedTotalNeeds < thisTotalNeeds
152 || !needyFirst && thisTotalNeeds < selectedTotalNeeds) {
153 select(isThisCountCorrect, thisTotalNeeds);
154 return;
155
156 } else {
157
158 return;
159 }
160 }
161
162 int getCount() {
163 return valueCount;
164 }
165
166 void pickValue() {
167 record[index] = value;
168 for (currentRecordIndex = 0
169 ; currentRecordIndex < index ; ++currentRecordIndex) {
170 combinationCount().increaseCount();
171 }
172 record = null;
173 ++totalCount;
174 ++valueCount;
175 }
176 }
177
178 Default2CombinationPicker(
179 final int index, final Object[][] values, final Random random) {
180 this.index = index;
181 this.wrappedValues = new ValueWrapper[values[index].length];
182 this.random = random;
183
184 for (int i = 0 ; i < this.wrappedValues.length ; ++i) {
185 wrappedValues[i] = new ValueWrapper(values[index][i], values);
186 }
187 this.options = new ValueWrapper[wrappedValues.length];
188 this.selections = new ValueWrapper[wrappedValues.length];
189 }
190
191 private void setupOptions() {
192 final int countForOptions = totalCount / wrappedValues.length;
193 nbrOfOptions = 0;
194 for (int i = 0 ; i < wrappedValues.length ; ++i) {
195 final ValueWrapper vw = wrappedValues[i];
196
197 if (countForOptions == vw.getCount()) {
198 options[nbrOfOptions] = vw;
199 ++nbrOfOptions;
200 }
201 }
202
203
204
205
206
207
208
209
210
211 needyFirst = 2 * nbrOfOptions <= wrappedValues.length;
212
213 nbrOfSelections = 0;
214 }
215
216 public void pick(final Object[] record) {
217 this.record = record;
218
219 if (totalCount < wrappedValues.length) {
220 wrappedValues[totalCount].pickValue();
221 return;
222 }
223
224 setupOptions();
225 if (1 == nbrOfOptions) {
226 options[0].pickValue();
227 return;
228 }
229
230 assert 2 <= nbrOfOptions;
231 for (currentCount = all2CombinationsAreSatisfied() ? 1 : 0
232 ; currentCount < greatestCombinationCount ; ++currentCount)
233 for (currentRecordIndex = 0
234 ; currentRecordIndex < index ; ++currentRecordIndex)
235 {
236 for (int i = 0 ; i < nbrOfOptions ; ++i) {
237 options[i].consider();
238 }
239
240 if (1 == nbrOfSelections) {
241 selections[0].pickValue();
242 return;
243
244 } else {
245 assert 2 <= nbrOfSelections;
246 System.arraycopy(selections, 0, options, 0, nbrOfSelections);
247 nbrOfOptions = nbrOfSelections;
248 nbrOfSelections = 0;
249 }
250 }
251
252
253 options[random.nextInt(nbrOfOptions)].pickValue();
254 }
255
256 public boolean all2CombinationsAreSatisfied() {
257 return 0 == nbrOf2CombinationsToSatisfy;
258 }
259 }