1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.rng.examples.stress;
18
19 import org.apache.commons.rng.simple.RandomSource;
20
21 import picocli.CommandLine.Command;
22 import picocli.CommandLine.Mixin;
23 import picocli.CommandLine.Option;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Formatter;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.NoSuchElementException;
32 import java.util.Scanner;
33 import java.util.concurrent.Callable;
34
35
36
37
38
39
40 @Command(name = "list",
41 description = "List random generators.")
42 class ListCommand implements Callable<Void> {
43
44 @Mixin
45 private StandardOptions reusableOptions;
46
47
48 @Option(names = {"-f", "--format"},
49 description = {"The list format (default: ${DEFAULT-VALUE}).",
50 "Valid values: ${COMPLETION-CANDIDATES}."},
51 paramLabel = "<format>")
52 private ListFormat listFormat = ListFormat.STRESS_TEST;
53
54
55 @Option(names = {"--provider"},
56 description = {"The provider type (default: ${DEFAULT-VALUE}).",
57 "Valid values: ${COMPLETION-CANDIDATES}."},
58 paramLabel = "<provider>")
59 private ProviderType providerType = ProviderType.ALL;
60
61
62 @Option(names = {"--min"},
63 description = {"The minimum entry in the provider enum (inclusive)."})
64 private int min = 0;
65
66
67 @Option(names = {"--max"},
68 description = {"The maximum entry in the provider enum (inclusive)."})
69 private int max = Integer.MAX_VALUE;
70
71
72 @Option(names = {"-p", "--prefix"},
73 description = {"The ID prefix.",
74 "Used for the stress test format."})
75 private String idPrefix = "";
76
77
78 @Option(names = {"-t", "--trials"},
79 description = {"The number of trials for each random generator.",
80 "Used for the stress test format."})
81 private int trials = 1;
82
83
84
85
86 enum ListFormat {
87
88 STRESS_TEST,
89
90 PLAIN
91 }
92
93
94
95
96 enum ProviderType {
97
98 ALL,
99
100 INT,
101
102 LONG,
103 }
104
105
106
107
108 @Override
109 public Void call() throws Exception {
110 LogUtils.setLogLevel(reusableOptions.logLevel);
111 StressTestDataList list = new StressTestDataList(idPrefix, trials);
112 if (providerType == ProviderType.INT) {
113 list = list.subsetIntSource();
114 } else if (providerType == ProviderType.LONG) {
115 list = list.subsetLongSource();
116 }
117 if (min != 0 || max != Integer.MAX_VALUE) {
118 list = list.subsetRandomSource(min, max);
119 }
120
121 final StringBuilder sb = new StringBuilder();
122 switch (listFormat) {
123 case PLAIN:
124 writePlainData(sb, list);
125 break;
126 case STRESS_TEST:
127 default:
128 writeStressTestData(sb, list);
129 break;
130 }
131
132 System.out.append(sb);
133
134 return null;
135 }
136
137
138
139
140
141
142
143
144
145
146
147 static void writePlainData(Appendable appendable,
148 Iterable<StressTestData> testData) throws IOException {
149 final String newLine = System.lineSeparator();
150 for (final StressTestData data : testData) {
151 appendable.append(data.getRandomSource().name());
152 if (data.getArgs() != null) {
153 appendable.append(' ');
154 appendable.append(Arrays.toString(data.getArgs()));
155 }
156 appendable.append(newLine);
157 }
158 }
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 static void writeStressTestData(Appendable appendable,
182 Iterable<StressTestData> testData) throws IOException {
183
184 int idWidth = 1;
185 int randomSourceWidth = 15;
186 for (final StressTestData data : testData) {
187 idWidth = Math.max(idWidth, data.getId().length());
188 randomSourceWidth = Math.max(randomSourceWidth, data.getRandomSource().name().length());
189 }
190
191 final String newLine = System.lineSeparator();
192
193 appendable.append("# Random generators list.").append(newLine);
194 appendable.append("# Any generator with no trials is ignored during testing.").append(newLine);
195 appendable.append("#").append(newLine);
196
197 String format = String.format("# %%-%ds %%-%ds trials [constructor arguments ...]%%n",
198 idWidth, randomSourceWidth);
199
200
201 @SuppressWarnings("resource")
202 final Formatter formatter = new Formatter(appendable);
203 formatter.format(format, "ID", "RandomSource");
204 format = String.format("%%-%ds %%-%ds ", idWidth + 2, randomSourceWidth);
205 for (final StressTestData data : testData) {
206 formatter.format(format, data.getId(), data.getRandomSource().name());
207 if (data.getArgs() == null) {
208 appendable.append(Integer.toString(data.getTrials()));
209 } else {
210 formatter.format("%-6d %s", data.getTrials(), Arrays.toString(data.getArgs()));
211 }
212 appendable.append(newLine);
213 }
214 formatter.flush();
215 }
216
217
218
219
220
221
222
223
224
225
226 static Iterable<StressTestData> readStressTestData(Readable readable) throws IOException {
227 final List<StressTestData> list = new ArrayList<>();
228
229
230 final HashSet<String> ids = new HashSet<>();
231
232
233 @SuppressWarnings("resource")
234 final Scanner scanner = new Scanner(readable);
235 try {
236 while (scanner.hasNextLine()) {
237
238
239
240
241
242 final String id = scanner.next();
243
244 if (id.isEmpty() || id.charAt(0) == '#') {
245 scanner.nextLine();
246 continue;
247 }
248 if (!ids.add(id)) {
249 throw new ApplicationException("Non-unique ID in strest test data: " + id);
250 }
251 final RandomSource randomSource = RandomSource.valueOf(scanner.next());
252 final int trials = scanner.nextInt();
253
254 final String arguments = scanner.nextLine().trim();
255 final Object[] args = parseArguments(randomSource, arguments);
256 list.add(new StressTestData(id, randomSource, args, trials));
257 }
258 } catch (NoSuchElementException | IllegalArgumentException ex) {
259 if (scanner.ioException() != null) {
260 throw scanner.ioException();
261 }
262 throw new ApplicationException("Failed to read stress test data", ex);
263 }
264
265 return list;
266 }
267
268
269
270
271
272
273
274
275
276
277 static Object[] parseArguments(RandomSource randomSource,
278 String arguments) {
279
280 if (arguments.isEmpty()) {
281 return null;
282 }
283
284
285
286 final int len = arguments.length();
287 if (len < 2 || arguments.charAt(0) != '[' || arguments.charAt(len - 1) != ']') {
288 throw new ApplicationException("RandomSource arguments should be an [array]: " + arguments);
289 }
290
291
292 final String[] tokens = arguments.substring(1, len - 1).split(", *");
293 final ArrayList<Object> args = new ArrayList<>();
294 for (final String token : tokens) {
295 args.add(RNGUtils.parseArgument(token));
296 }
297 return args.toArray();
298 }
299 }