dither 0.2.2 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +8 -2
- data/dither.gemspec +1 -3
- data/ext/dither/Aetg.java +51 -0
- data/ext/dither/AetgPairwise.java +258 -0
- data/ext/dither/ArrayLengthComparator.java +30 -0
- data/ext/dither/CombinatoricHelper.java +143 -0
- data/ext/dither/ConstraintHandler.java +120 -0
- data/ext/dither/Dither.java +155 -0
- data/ext/dither/DitherError.java +45 -0
- data/ext/dither/IndexArrayPair.java +40 -0
- data/ext/dither/Ipog.java +405 -0
- data/ext/dither/Pair.java +61 -0
- data/ext/dither/base_constraint_handler.h +4 -1
- data/ext/dither/combinations.h +21 -12
- data/ext/dither/ipog.cc +71 -37
- data/ext/dither/ipog.h +12 -7
- data/ext/dither/simple_constraint_handler.cc +14 -2
- data/ext/dither/simple_constraint_handler.h +2 -1
- data/lib/dither.rb +1 -2
- data/lib/dither/version.rb +1 -1
- metadata +13 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d71e5bd41e308d0224d6902f1c0698739e1ea399
|
4
|
+
data.tar.gz: adef3f1c923b2611da8ed6cf47f3d7dec5ffb8d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1ca534cc0dfe49379470dfda625d115ae6af9a0e7bf1ed9ca9a4dca784b095a55e5dfcc7d290f45aeea3723797426df6a69da1ffafa43877c9a7cb010e29c7a
|
7
|
+
data.tar.gz: 4333165dad0c5a001e58acea7b3c421e691e5c90c52ae10baaf3c4a67b43acf6ef43d1ae2f20805c29b086a71a179a3137e1d12b74ec08f06c07f0fa054cbd8e
|
data/Rakefile
CHANGED
@@ -5,7 +5,13 @@ Bundler::GemHelper.install_tasks
|
|
5
5
|
require 'rspec/core/rake_task'
|
6
6
|
RSpec::Core::RakeTask.new
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
if RUBY_PLATFORM =~ /java/
|
9
|
+
require 'rake/javaextensiontask'
|
10
|
+
Rake::JavaExtensionTask.new('dither')
|
11
|
+
else
|
12
|
+
require 'rake/extensiontask'
|
13
|
+
Rake::ExtensionTask.new('dither')
|
14
|
+
end
|
10
15
|
|
11
16
|
task :default => :spec
|
17
|
+
|
data/dither.gemspec
CHANGED
@@ -23,9 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
|
24
24
|
if RUBY_PLATFORM =~ /java/
|
25
25
|
s.platform = "java"
|
26
|
-
|
27
|
-
files << "lib/dither-0.1.5.jar"
|
28
|
-
files << "lib/choco-solver-3.3.1-with-dependencies.jar"
|
26
|
+
files << "lib/dither.jar"
|
29
27
|
else
|
30
28
|
s.add_dependency "ffi", "~> 1.0"
|
31
29
|
s.extensions = 'ext/dither/extconf.rb'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
package com.github.jesg.dither;
|
2
|
+
|
3
|
+
/*
|
4
|
+
* #%L
|
5
|
+
* dither
|
6
|
+
* %%
|
7
|
+
* Copyright (C) 2015 Jason Gowan
|
8
|
+
* %%
|
9
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
* you may not use this file except in compliance with the License.
|
11
|
+
* You may obtain a copy of the License at
|
12
|
+
*
|
13
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
*
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
18
|
+
* See the License for the specific language governing permissions and
|
19
|
+
* limitations under the License.
|
20
|
+
* #L%
|
21
|
+
*/
|
22
|
+
|
23
|
+
import java.util.Iterator;
|
24
|
+
import java.util.LinkedList;
|
25
|
+
import java.util.List;
|
26
|
+
|
27
|
+
public abstract class Aetg implements Iterator<Object[]> {
|
28
|
+
|
29
|
+
public abstract boolean hasNext();
|
30
|
+
public abstract Object[] next();
|
31
|
+
|
32
|
+
public List<Object[]> toList() {
|
33
|
+
final List<Object[]> results = new LinkedList<Object[]>();
|
34
|
+
|
35
|
+
while(hasNext()) {
|
36
|
+
final Object[] result = next();
|
37
|
+
if(result != null) {
|
38
|
+
results.add(result);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
return results;
|
43
|
+
}
|
44
|
+
|
45
|
+
public Object[][] toArray() {
|
46
|
+
return toList().toArray(new Object[][]{});
|
47
|
+
}
|
48
|
+
|
49
|
+
public void remove() {}
|
50
|
+
|
51
|
+
}
|
@@ -0,0 +1,258 @@
|
|
1
|
+
package com.github.jesg.dither;
|
2
|
+
|
3
|
+
import java.util.ArrayList;
|
4
|
+
import java.util.LinkedList;
|
5
|
+
import java.util.List;
|
6
|
+
import java.util.ListIterator;
|
7
|
+
import java.util.Random;
|
8
|
+
import java.util.concurrent.Callable;
|
9
|
+
import java.util.concurrent.ExecutorService;
|
10
|
+
import java.util.concurrent.Semaphore;
|
11
|
+
import java.util.concurrent.ThreadLocalRandom;
|
12
|
+
|
13
|
+
/*
|
14
|
+
* #%L
|
15
|
+
* dither
|
16
|
+
* %%
|
17
|
+
* Copyright (C) 2015 Jason Gowan
|
18
|
+
* %%
|
19
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
20
|
+
* you may not use this file except in compliance with the License.
|
21
|
+
* You may obtain a copy of the License at
|
22
|
+
*
|
23
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
24
|
+
*
|
25
|
+
* Unless required by applicable law or agreed to in writing, software
|
26
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
27
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
28
|
+
* See the License for the specific language governing permissions and
|
29
|
+
* limitations under the License.
|
30
|
+
* #L%
|
31
|
+
True*/
|
32
|
+
|
33
|
+
class AetgPairwise extends Aetg {
|
34
|
+
|
35
|
+
private final int n = 50;
|
36
|
+
private final int[][] scratch = new int[n][];
|
37
|
+
private final int[] fitness = new int[n];
|
38
|
+
private final Object[][] params;
|
39
|
+
private final Pair[][] pairCache;
|
40
|
+
private final List<Pair[]> coverage;
|
41
|
+
private final AtegRandom random;
|
42
|
+
private final ExecutorService executor;
|
43
|
+
private final Semaphore barrier;
|
44
|
+
private final List<Callable<Void>> tasks = new ArrayList<Callable<Void>>(n);
|
45
|
+
private final Pair[][] constraints;
|
46
|
+
|
47
|
+
public AetgPairwise(final int t, final Integer seed, final Object[][] params, final Integer[][] constraintsParam, final Object[][] previouslyTested, final ExecutorService executor) {
|
48
|
+
this.params = params;
|
49
|
+
this.executor = executor;
|
50
|
+
this.barrier = new Semaphore(0);
|
51
|
+
|
52
|
+
for(int i = 0; i < n; i++) {
|
53
|
+
final int index = i;
|
54
|
+
final Semaphore localBarrier = this.barrier;
|
55
|
+
tasks.add(new Callable<Void>() {
|
56
|
+
public Void call() {
|
57
|
+
try {
|
58
|
+
generate(index);
|
59
|
+
fitness(index);
|
60
|
+
} finally {
|
61
|
+
localBarrier.release();
|
62
|
+
}
|
63
|
+
return null;
|
64
|
+
}
|
65
|
+
});
|
66
|
+
}
|
67
|
+
|
68
|
+
if(seed == null) {
|
69
|
+
this.random = new AtegRandom() {
|
70
|
+
public int nextInt(final int i) { return ThreadLocalRandom.current().nextInt(0, i); }
|
71
|
+
};
|
72
|
+
} else {
|
73
|
+
this.random = new AtegRandom() {
|
74
|
+
final Random random = new Random(seed);
|
75
|
+
public int nextInt(final int i) { return random.nextInt(i); }
|
76
|
+
};
|
77
|
+
}
|
78
|
+
|
79
|
+
this.pairCache = new Pair[params.length][];
|
80
|
+
for(int i = 0; i < params.length; i++) {
|
81
|
+
this.pairCache[i] = new Pair[params[i].length];
|
82
|
+
|
83
|
+
for(int j = 0; j < params[i].length; j++) {
|
84
|
+
this.pairCache[i][j] = new Pair(i, j);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
this.constraints = new Pair[constraintsParam.length + previouslyTested.length][];
|
89
|
+
int constraintIndex = 0;
|
90
|
+
for(final Integer[] constraint : constraintsParam) {
|
91
|
+
final List<Pair> pairs = new ArrayList<Pair>();
|
92
|
+
for(int i = 0; i < constraint.length; i++) {
|
93
|
+
if(constraint[i] == null) {
|
94
|
+
continue;
|
95
|
+
}
|
96
|
+
pairs.add(pairCache[i][constraint[i]]);
|
97
|
+
}
|
98
|
+
constraints[constraintIndex++] = pairs.toArray(new Pair[]{});
|
99
|
+
}
|
100
|
+
|
101
|
+
for(final Object[] testCase : previouslyTested) {
|
102
|
+
final Pair[] pairs = new Pair[testCase.length];
|
103
|
+
for(int i = 0; i < pairs.length; i++) {
|
104
|
+
final Object currentObject = testCase[i];
|
105
|
+
for(int j = 0; j < params[i].length; j++) {
|
106
|
+
if(currentObject.equals(params[i][j])) {
|
107
|
+
pairs[i] = pairCache[i][j];
|
108
|
+
break;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
constraints[constraintIndex++] = pairs;
|
113
|
+
}
|
114
|
+
|
115
|
+
|
116
|
+
for(int u = 0; u < n; u++) {
|
117
|
+
scratch[u] = new int[params.length];
|
118
|
+
}
|
119
|
+
|
120
|
+
final int[] arr = new int[params.length];
|
121
|
+
for(int k = 0; k < params.length; k++) {
|
122
|
+
arr[k] = k;
|
123
|
+
}
|
124
|
+
|
125
|
+
this.coverage = new LinkedList<Pair[]>();
|
126
|
+
final int[] lengths = new int[t];
|
127
|
+
for(final int[] tmp : CombinatoricHelper.getCombinations(t, arr)) {
|
128
|
+
|
129
|
+
for(int i = 0; i < lengths.length; i++) {
|
130
|
+
lengths[i] = params[tmp[i]].length;
|
131
|
+
}
|
132
|
+
|
133
|
+
final Pair[][] pairs = new Pair[tmp.length][];
|
134
|
+
for(int i = 0; i < pairs.length; i++) {
|
135
|
+
pairs[i] = this.pairCache[tmp[i]];
|
136
|
+
}
|
137
|
+
|
138
|
+
outer:
|
139
|
+
for(final List<Pair> innerPairs : CombinatoricHelper.product(pairs)) {
|
140
|
+
final Pair[] innerComb = innerPairs.toArray(new Pair[innerPairs.size()]);
|
141
|
+
|
142
|
+
for(final Pair[] constraint : constraints) {
|
143
|
+
int count = 0;
|
144
|
+
for(final Pair pair : constraint) {
|
145
|
+
for(final Pair innerCombPair : innerComb) {
|
146
|
+
if(pair.equals(innerCombPair)) {
|
147
|
+
count++;
|
148
|
+
break;
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
if(count == constraint.length) {
|
153
|
+
continue outer;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
coverage.add(innerComb);
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
|
162
|
+
public boolean hasNext() {
|
163
|
+
return !coverage.isEmpty();
|
164
|
+
}
|
165
|
+
|
166
|
+
public Object[] next() {
|
167
|
+
try {
|
168
|
+
executor.invokeAll(tasks);
|
169
|
+
barrier.acquire(n);
|
170
|
+
} catch(InterruptedException e) {
|
171
|
+
Thread.currentThread().interrupt();
|
172
|
+
return null;
|
173
|
+
}
|
174
|
+
final int i = bestFit();
|
175
|
+
if(fitness[i] <= 0) {
|
176
|
+
return null;
|
177
|
+
}
|
178
|
+
final int[] best = scratch[i];
|
179
|
+
final Object[] result = new Object[params.length];
|
180
|
+
for(int k = 0; k < result.length; k++) {
|
181
|
+
result[k] = params[k][best[k]];
|
182
|
+
}
|
183
|
+
removeMatches(i);
|
184
|
+
return result;
|
185
|
+
}
|
186
|
+
|
187
|
+
private void removeMatches(final int i) {
|
188
|
+
final int[] cases = scratch[i];
|
189
|
+
final ListIterator<Pair[]> iter = coverage.listIterator(0);
|
190
|
+
while(iter.hasNext()) {
|
191
|
+
if(match(iter.next(), cases)) {
|
192
|
+
iter.remove();
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
private void generate(final int i) {
|
198
|
+
for(int j = 0; j < params.length; j++) {
|
199
|
+
scratch[i][j] = random.nextInt(params[j].length);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
private void fitness(final int i) {
|
204
|
+
final int[] cases = scratch[i];
|
205
|
+
if(constraints.length > 0) {
|
206
|
+
for(final Pair pair : coverage.get(0)) {
|
207
|
+
cases[pair.i] = pair.j;
|
208
|
+
}
|
209
|
+
}
|
210
|
+
for(final Pair[] constraint : constraints) {
|
211
|
+
int count = 0;
|
212
|
+
for(final Pair pair : constraint) {
|
213
|
+
if(pair.j == cases[pair.i]) {
|
214
|
+
count++;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
if(count == constraint.length) {
|
218
|
+
fitness[i] = -1;
|
219
|
+
return;
|
220
|
+
}
|
221
|
+
}
|
222
|
+
int count = 0;
|
223
|
+
for(final Pair[] pairs : coverage) {
|
224
|
+
if(match(pairs, cases)) {
|
225
|
+
count++;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
fitness[i] = count;
|
229
|
+
}
|
230
|
+
|
231
|
+
private int bestFit() {
|
232
|
+
int index = 0;
|
233
|
+
int max = 0;
|
234
|
+
for(int i = 0; i < fitness.length; i++) {
|
235
|
+
final int value = fitness[i];
|
236
|
+
if(value > max) {
|
237
|
+
max = value;
|
238
|
+
index = i;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
return index;
|
242
|
+
}
|
243
|
+
|
244
|
+
private interface AtegRandom {
|
245
|
+
int nextInt(int a);
|
246
|
+
}
|
247
|
+
|
248
|
+
private static boolean match(final Pair[] pairs, final int[] cases) {
|
249
|
+
boolean flag = true;
|
250
|
+
for(final Pair pair : pairs) {
|
251
|
+
if(cases[pair.i] != pair.j) {
|
252
|
+
flag = false;
|
253
|
+
break;
|
254
|
+
}
|
255
|
+
}
|
256
|
+
return flag;
|
257
|
+
}
|
258
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
package com.github.jesg.dither;
|
2
|
+
|
3
|
+
/*
|
4
|
+
* #%L
|
5
|
+
* dither
|
6
|
+
* %%
|
7
|
+
* Copyright (C) 2015 Jason Gowan
|
8
|
+
* %%
|
9
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
* you may not use this file except in compliance with the License.
|
11
|
+
* You may obtain a copy of the License at
|
12
|
+
*
|
13
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
*
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
18
|
+
* See the License for the specific language governing permissions and
|
19
|
+
* limitations under the License.
|
20
|
+
* #L%
|
21
|
+
*/
|
22
|
+
|
23
|
+
import java.util.Comparator;
|
24
|
+
|
25
|
+
class ArrayLengthComparator implements Comparator<IndexArrayPair> {
|
26
|
+
|
27
|
+
public int compare(IndexArrayPair arg1, IndexArrayPair arg2) {
|
28
|
+
return -Integer.compare(arg1.getArr().length, arg2.getArr().length);
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,143 @@
|
|
1
|
+
package com.github.jesg.dither;
|
2
|
+
|
3
|
+
/*
|
4
|
+
* #%L
|
5
|
+
* dither
|
6
|
+
* %%
|
7
|
+
* Copyright (C) 2015 Jason Gowan
|
8
|
+
* %%
|
9
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
* you may not use this file except in compliance with the License.
|
11
|
+
* You may obtain a copy of the License at
|
12
|
+
*
|
13
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
*
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
18
|
+
* See the License for the specific language governing permissions and
|
19
|
+
* limitations under the License.
|
20
|
+
* #L%
|
21
|
+
*/
|
22
|
+
|
23
|
+
import java.util.ArrayList;
|
24
|
+
import java.util.Arrays;
|
25
|
+
import java.util.List;
|
26
|
+
|
27
|
+
class CombinatoricHelper {
|
28
|
+
|
29
|
+
public static List<int[]> getCombinations(final int n, final int[] input) {
|
30
|
+
final List<int[]> result = new ArrayList<int[]>();
|
31
|
+
final int[] scratch = new int[n];
|
32
|
+
|
33
|
+
if (n <= input.length) {
|
34
|
+
for (int i = 0; (scratch[i] = i) < n - 1; i++)
|
35
|
+
;
|
36
|
+
result.add(_getCombinations(input, scratch));
|
37
|
+
for (;;) {
|
38
|
+
int i;
|
39
|
+
for (i = n - 1; i >= 0 && scratch[i] == input.length - n + i; i--)
|
40
|
+
;
|
41
|
+
if (i < 0) {
|
42
|
+
break;
|
43
|
+
} else {
|
44
|
+
scratch[i]++;
|
45
|
+
for (++i; i < n; i++) {
|
46
|
+
scratch[i] = scratch[i - 1] + 1;
|
47
|
+
}
|
48
|
+
result.add(_getCombinations(input, scratch));
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
return result;
|
54
|
+
}
|
55
|
+
|
56
|
+
private static int[] _getCombinations(final int[] input, final int[] scratch) {
|
57
|
+
final int[] result = new int[scratch.length];
|
58
|
+
for (int i = 0; i < scratch.length; i++) {
|
59
|
+
result[i] = input[scratch[i]];
|
60
|
+
}
|
61
|
+
return result;
|
62
|
+
}
|
63
|
+
|
64
|
+
|
65
|
+
public static <T> List<List<T>> product(final T[][] args) {
|
66
|
+
final int[] tmp = new int[args.length];
|
67
|
+
for(int i = 0; i < tmp.length; i++) {
|
68
|
+
tmp[i] = args[i].length;
|
69
|
+
}
|
70
|
+
|
71
|
+
final int[][] solution = product(tmp);
|
72
|
+
final List<List<T>> results = new ArrayList<List<T>>(solution.length);
|
73
|
+
for(int i = 0; i < solution.length; i++) {
|
74
|
+
final List<T> inner = new ArrayList<T>(args.length);
|
75
|
+
results.add(i, inner);
|
76
|
+
for(int j = 0; j < args.length; j++) {
|
77
|
+
results.get(i).add(j, args[j][solution[i][j]]);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
return results;
|
81
|
+
}
|
82
|
+
|
83
|
+
public static int[][] product(final int[] ranges) {
|
84
|
+
int length = 1;
|
85
|
+
for(int i = 0; i < ranges.length; i++) {
|
86
|
+
length *= ranges[i];
|
87
|
+
--ranges[i];
|
88
|
+
}
|
89
|
+
|
90
|
+
final int[][] results = new int[length][ranges.length];
|
91
|
+
final int[] scratch = new int[ranges.length];
|
92
|
+
|
93
|
+
int k = 0;
|
94
|
+
final int max = ranges.length - 1;
|
95
|
+
for(int i = max;;) {
|
96
|
+
|
97
|
+
if(i == max) {
|
98
|
+
for(int val = 0; val <= ranges[i]; val++) {
|
99
|
+
for(int j = 0; j < scratch.length; j++) {
|
100
|
+
results[k][j] = scratch[j];
|
101
|
+
}
|
102
|
+
k++;
|
103
|
+
scratch[i]++;
|
104
|
+
}
|
105
|
+
scratch[i] = 0;
|
106
|
+
i--;
|
107
|
+
} else if(i == 0 && scratch[i] >= ranges[i]) {
|
108
|
+
return results;
|
109
|
+
} else if(scratch[i] < ranges[i]) {
|
110
|
+
scratch[i]++;
|
111
|
+
i++;
|
112
|
+
} else {
|
113
|
+
scratch[i] = -1;
|
114
|
+
i--;
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
private static int[][] _product(final int left, final int right) {
|
120
|
+
final int[][] result = new int[left * right][];
|
121
|
+
int k = 0;
|
122
|
+
for (int i = 0; i < left; i++) {
|
123
|
+
for (int j = 0; j < right; j++) {
|
124
|
+
result[k] = new int[] { i, j };
|
125
|
+
k++;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
return result;
|
129
|
+
}
|
130
|
+
|
131
|
+
private static int[][] _product(final int[][] left, final int right) {
|
132
|
+
final int[][] result = new int[left.length * right][];
|
133
|
+
int k = 0;
|
134
|
+
for (int i = 0; i < left.length; i++) {
|
135
|
+
for (int j = 0; j < right; j++) {
|
136
|
+
result[k] = Arrays.copyOf(left[i], left[i].length + 1);
|
137
|
+
result[k][left[i].length] = j;
|
138
|
+
k++;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
return result;
|
142
|
+
}
|
143
|
+
}
|