1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2009, 2010, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include <data/datasheet.h>
27 #include <data/casereader-provider.h>
28 #include <data/casereader.h>
29 #include <data/casewriter.h>
30 #include <data/lazy-casereader.h>
31 #include <libpspp/argv-parser.h>
32 #include <libpspp/array.h>
33 #include <libpspp/assertion.h>
34 #include <libpspp/hash-functions.h>
35 #include <libpspp/model-checker.h>
36 #include <libpspp/range-map.h>
37 #include <libpspp/range-set.h>
38 #include <libpspp/str.h>
39 #include <libpspp/taint.h>
40 #include <libpspp/tower.h>
46 /* lazy_casereader callback function to instantiate a casereader
47 from the datasheet. */
48 static struct casereader
*
49 lazy_callback (void *ds_
)
51 struct datasheet
*ds
= ds_
;
52 return datasheet_make_reader (ds
);
56 /* Maximum size of datasheet supported for model checking
63 struct datasheet_test_params
66 int max_rows
; /* Maximum number of rows. */
67 int max_cols
; /* Maximum number of columns. */
68 int backing_rows
; /* Number of rows of backing store. */
69 int backing_widths
[MAX_COLS
]; /* Widths of columns of backing store. */
70 int n_backing_cols
; /* Number of columns of backing store. */
71 int widths
[MAX_WIDTHS
]; /* Allowed column widths. */
75 unsigned int next_value
;
79 check_caseproto (struct mc
*mc
, const struct caseproto
*benchmark
,
80 const struct caseproto
*test
, const char *test_name
)
82 size_t n_columns
= caseproto_get_n_widths (benchmark
);
86 if (n_columns
!= caseproto_get_n_widths (test
))
88 mc_error (mc
, "%s column count (%zu) does not match expected (%zu)",
89 test_name
, caseproto_get_n_widths (test
), n_columns
);
94 for (col
= 0; col
< n_columns
; col
++)
96 int benchmark_width
= caseproto_get_width (benchmark
, col
);
97 int test_width
= caseproto_get_width (test
, col
);
98 if (benchmark_width
!= test_width
)
100 mc_error (mc
, "%s column %zu width (%d) differs from expected (%d)",
101 test_name
, col
, test_width
, benchmark_width
);
108 /* Checks that READER contains the N_ROWS rows and N_COLUMNS
109 columns of data in ARRAY, reporting any errors via MC. */
111 check_datasheet_casereader (struct mc
*mc
, struct casereader
*reader
,
112 union value array
[MAX_ROWS
][MAX_COLS
],
113 size_t n_rows
, const struct caseproto
*proto
)
115 size_t n_columns
= caseproto_get_n_widths (proto
);
117 if (!check_caseproto (mc
, proto
, casereader_get_proto (reader
),
120 else if (casereader_get_n_cases (reader
) != n_rows
)
122 if (casereader_get_n_cases (reader
) == CASENUMBER_MAX
123 && casereader_count_cases (reader
) == n_rows
)
124 mc_error (mc
, "datasheet casereader has unknown case count");
126 mc_error (mc
, "casereader row count (%lu) does not match "
128 (unsigned long int) casereader_get_n_cases (reader
),
136 for (row
= 0; row
< n_rows
; row
++)
140 c
= casereader_read (reader
);
143 mc_error (mc
, "casereader_read failed reading row %zu of %zu "
144 "(%zu columns)", row
, n_rows
, n_columns
);
148 for (col
= 0; col
< n_columns
; col
++)
150 int width
= caseproto_get_width (proto
, col
);
151 if (!value_equal (case_data_idx (c
, col
), &array
[row
][col
],
155 mc_error (mc
, "element %zu,%zu (of %zu,%zu) differs: "
157 row
, col
, n_rows
, n_columns
,
158 DBL_DIG
+ 1, case_num_idx (c
, col
),
159 DBL_DIG
+ 1, array
[row
][col
].f
);
161 mc_error (mc
, "element %zu,%zu (of %zu,%zu) differs: "
163 row
, col
, n_rows
, n_columns
,
164 width
, case_str_idx (c
, col
),
165 width
, array
[row
][col
].s
);
172 c
= casereader_read (reader
);
174 mc_error (mc
, "casereader has extra cases (expected %zu)", n_rows
);
178 /* Checks that datasheet DS contains has N_ROWS rows, N_COLUMNS
179 columns, and the same contents as ARRAY, reporting any
180 mismatches via mc_error. Then, adds DS to MC as a new state. */
182 check_datasheet (struct mc
*mc
, struct datasheet
*ds
,
183 union value array
[MAX_ROWS
][MAX_COLS
],
184 size_t n_rows
, const struct caseproto
*proto
)
186 size_t n_columns
= caseproto_get_n_widths (proto
);
187 struct datasheet
*ds2
;
188 struct casereader
*reader
;
189 unsigned long int serial
= 0;
191 assert (n_rows
< MAX_ROWS
);
192 assert (n_columns
< MAX_COLS
);
194 /* Check contents of datasheet via datasheet functions. */
195 if (!check_caseproto (mc
, proto
, datasheet_get_proto (ds
), "datasheet"))
197 /* check_caseproto emitted errors already. */
199 else if (n_rows
!= datasheet_get_n_rows (ds
))
200 mc_error (mc
, "row count (%lu) does not match expected (%zu)",
201 (unsigned long int) datasheet_get_n_rows (ds
), n_rows
);
205 bool difference
= false;
207 for (row
= 0; row
< n_rows
; row
++)
208 for (col
= 0; col
< n_columns
; col
++)
210 int width
= caseproto_get_width (proto
, col
);
211 union value
*av
= &array
[row
][col
];
214 value_init (&v
, width
);
215 if (!datasheet_get_value (ds
, row
, col
, &v
))
217 if (!value_equal (&v
, av
, width
))
220 mc_error (mc
, "element %zu,%zu (of %zu,%zu) differs: "
221 "%.*g != %.*g", row
, col
, n_rows
, n_columns
,
222 DBL_DIG
+ 1, v
.f
, DBL_DIG
+ 1, av
->f
);
224 mc_error (mc
, "element %zu,%zu (of %zu,%zu) differs: "
226 row
, col
, n_rows
, n_columns
,
231 value_destroy (&v
, width
);
238 mc_error (mc
, "expected:");
240 for (row
= 0; row
< n_rows
; row
++)
243 ds_put_format (&s
, "row %zu:", row
);
244 for (col
= 0; col
< n_columns
; col
++)
246 const union value
*v
= &array
[row
][col
];
247 int width
= caseproto_get_width (proto
, col
);
249 ds_put_format (&s
, " %g", v
->f
);
251 ds_put_format (&s
, " '%.*s'", width
, v
->s
);
253 mc_error (mc
, "%s", ds_cstr (&s
));
256 mc_error (mc
, "actual:");
258 for (row
= 0; row
< n_rows
; row
++)
261 ds_put_format (&s
, "row %zu:", row
);
262 for (col
= 0; col
< n_columns
; col
++)
264 int width
= caseproto_get_width (proto
, col
);
266 value_init (&v
, width
);
267 if (!datasheet_get_value (ds
, row
, col
, &v
))
270 ds_put_format (&s
, " %g", v
.f
);
272 ds_put_format (&s
, " '%.*s'", width
, v
.s
);
274 mc_error (mc
, "%s", ds_cstr (&s
));
281 /* Check that datasheet contents are correct when read through
283 ds2
= clone_datasheet (ds
);
284 reader
= datasheet_make_reader (ds2
);
285 check_datasheet_casereader (mc
, reader
, array
, n_rows
, proto
);
286 casereader_destroy (reader
);
288 /* Check that datasheet contents are correct when read through
289 casereader with lazy_casereader wrapped around it. This is
290 valuable because otherwise there is no non-GUI code that
291 uses the lazy_casereader. */
292 ds2
= clone_datasheet (ds
);
293 reader
= lazy_casereader_create (datasheet_get_proto (ds2
), n_rows
,
294 lazy_callback
, ds2
, &serial
);
295 check_datasheet_casereader (mc
, reader
, array
, n_rows
, proto
);
296 if (lazy_casereader_destroy (reader
, serial
))
298 /* Lazy casereader was never instantiated. This will
299 only happen if there are no rows (because in that case
300 casereader_read never gets called). */
301 datasheet_destroy (ds2
);
303 mc_error (mc
, "lazy casereader not instantiated, but should "
304 "have been (size %zu,%zu)", n_rows
, n_columns
);
308 /* Lazy casereader was instantiated. This is the common
309 case, in which some casereader operation
310 (casereader_read in this case) was performed on the
312 casereader_destroy (reader
);
314 mc_error (mc
, "lazy casereader instantiated, but should not "
315 "have been (size %zu,%zu)", n_rows
, n_columns
);
318 if (mc_discard_dup_state (mc
, hash_datasheet (ds
)))
319 datasheet_destroy (ds
);
321 mc_add_state (mc
, ds
);
324 /* Extracts the contents of DS into DATA. */
326 extract_data (const struct datasheet
*ds
, union value data
[MAX_ROWS
][MAX_COLS
])
328 const struct caseproto
*proto
= datasheet_get_proto (ds
);
329 size_t n_columns
= datasheet_get_n_columns (ds
);
330 size_t n_rows
= datasheet_get_n_rows (ds
);
333 assert (n_rows
< MAX_ROWS
);
334 assert (n_columns
< MAX_COLS
);
335 for (row
= 0; row
< n_rows
; row
++)
336 for (col
= 0; col
< n_columns
; col
++)
338 int width
= caseproto_get_width (proto
, col
);
339 union value
*v
= &data
[row
][col
];
340 value_init (v
, width
);
341 if (!datasheet_get_value (ds
, row
, col
, v
))
346 /* Copies the contents of ODATA into DATA. Each of the N_ROWS
347 rows of ODATA and DATA must have prototype PROTO. */
349 clone_data (size_t n_rows
, const struct caseproto
*proto
,
350 union value odata
[MAX_ROWS
][MAX_COLS
],
351 union value data
[MAX_ROWS
][MAX_COLS
])
353 size_t n_columns
= caseproto_get_n_widths (proto
);
356 assert (n_rows
< MAX_ROWS
);
357 assert (n_columns
< MAX_COLS
);
358 for (row
= 0; row
< n_rows
; row
++)
359 for (col
= 0; col
< n_columns
; col
++)
361 int width
= caseproto_get_width (proto
, col
);
362 const union value
*ov
= &odata
[row
][col
];
363 union value
*v
= &data
[row
][col
];
364 value_init (v
, width
);
365 value_copy (v
, ov
, width
);
370 release_data (size_t n_rows
, const struct caseproto
*proto
,
371 union value data
[MAX_ROWS
][MAX_COLS
])
373 size_t n_columns
= caseproto_get_n_widths (proto
);
376 assert (n_rows
< MAX_ROWS
);
377 assert (n_columns
< MAX_COLS
);
378 for (col
= 0; col
< n_columns
; col
++)
380 int width
= caseproto_get_width (proto
, col
);
381 if (value_needs_init (width
))
382 for (row
= 0; row
< n_rows
; row
++)
383 value_destroy (&data
[row
][col
], width
);
387 /* Clones the structure and contents of ODS into *DS,
388 and the contents of ODATA into DATA. */
390 clone_model (const struct datasheet
*ods
,
391 union value odata
[MAX_ROWS
][MAX_COLS
],
392 struct datasheet
**ds
,
393 union value data
[MAX_ROWS
][MAX_COLS
])
395 *ds
= clone_datasheet (ods
);
396 clone_data (datasheet_get_n_rows (ods
), datasheet_get_proto (ods
),
401 value_from_param (union value
*value
, int width
, unsigned int idx
)
404 value
->f
= idx
& 0xffff;
407 unsigned int hash
= hash_int (idx
, 0);
411 for (offset
= 0; offset
< width
; offset
++)
412 value
->s
[offset
] = "ABCDEFGHIJ"[(hash
>> offset
) % 10];
416 /* "init" function for struct mc_class. */
418 datasheet_mc_init (struct mc
*mc
)
420 struct datasheet_test_params
*params
= mc_get_aux (mc
);
421 struct datasheet
*ds
;
423 if (params
->backing_rows
== 0 && params
->n_backing_cols
== 0)
425 /* Create unbacked datasheet. */
426 struct caseproto
*proto
;
427 ds
= datasheet_create (NULL
);
428 mc_name_operation (mc
, "empty datasheet");
429 proto
= caseproto_create ();
430 check_datasheet (mc
, ds
, NULL
, 0, proto
);
431 caseproto_unref (proto
);
435 /* Create datasheet with backing. */
436 struct casewriter
*writer
;
437 struct casereader
*reader
;
438 union value data
[MAX_ROWS
][MAX_COLS
];
439 struct caseproto
*proto
;
442 assert (params
->backing_rows
> 0 && params
->backing_rows
<= MAX_ROWS
);
443 assert (params
->n_backing_cols
> 0
444 && params
->n_backing_cols
<= MAX_COLS
);
446 proto
= caseproto_create ();
447 for (col
= 0; col
< params
->n_backing_cols
; col
++)
448 proto
= caseproto_add_width (proto
, params
->backing_widths
[col
]);
450 writer
= mem_writer_create (proto
);
451 for (row
= 0; row
< params
->backing_rows
; row
++)
455 c
= case_create (proto
);
456 for (col
= 0; col
< params
->n_backing_cols
; col
++)
458 int width
= params
->backing_widths
[col
];
459 union value
*value
= &data
[row
][col
];
460 value_init (value
, width
);
461 value_from_param (value
, width
, params
->next_value
++);
462 value_copy (case_data_rw_idx (c
, col
), value
, width
);
464 casewriter_write (writer
, c
);
467 reader
= casewriter_make_reader (writer
);
468 assert (reader
!= NULL
);
470 ds
= datasheet_create (reader
);
471 mc_name_operation (mc
, "datasheet with (%d,%d) backing",
472 params
->backing_rows
, params
->n_backing_cols
);
473 check_datasheet (mc
, ds
, data
,
474 params
->backing_rows
, proto
);
475 release_data (params
->backing_rows
, proto
, data
);
476 caseproto_unref (proto
);
487 resize_cb (const union value
*old_value
, union value
*new_value
, const void *aux_
)
489 const struct resize_cb_aux
*aux
= aux_
;
491 value_from_param (new_value
, aux
->new_width
,
492 value_hash (old_value
, aux
->old_width
, 0));
495 /* "mutate" function for struct mc_class. */
497 datasheet_mc_mutate (struct mc
*mc
, const void *ods_
)
499 struct datasheet_test_params
*params
= mc_get_aux (mc
);
501 const struct datasheet
*ods
= ods_
;
502 union value odata
[MAX_ROWS
][MAX_COLS
];
503 union value data
[MAX_ROWS
][MAX_COLS
];
504 const struct caseproto
*oproto
= datasheet_get_proto (ods
);
505 size_t n_columns
= datasheet_get_n_columns (ods
);
506 size_t n_rows
= datasheet_get_n_rows (ods
);
507 size_t pos
, new_pos
, n
, width_idx
;
509 extract_data (ods
, odata
);
511 /* Insert a column in each possible position. */
512 if (n_columns
< params
->max_cols
)
513 for (pos
= 0; pos
<= n_columns
; pos
++)
514 for (width_idx
= 0; width_idx
< params
->n_widths
; width_idx
++)
515 if (mc_include_state (mc
))
517 int width
= params
->widths
[width_idx
];
518 struct caseproto
*proto
;
519 struct datasheet
*ds
;
523 mc_name_operation (mc
, "insert column at %zu "
524 "(from %zu to %zu columns)",
525 pos
, n_columns
, n_columns
+ 1);
526 clone_model (ods
, odata
, &ds
, data
);
528 value_init (&new, width
);
529 value_from_param (&new, width
, params
->next_value
++);
530 if (!datasheet_insert_column (ds
, &new, width
, pos
))
531 mc_error (mc
, "datasheet_insert_column failed");
532 proto
= caseproto_insert_width (caseproto_ref (oproto
),
535 for (i
= 0; i
< n_rows
; i
++)
537 insert_element (&data
[i
][0], n_columns
, sizeof data
[i
][0],
539 value_init (&data
[i
][pos
], width
);
540 value_copy (&data
[i
][pos
], &new, width
);
542 value_destroy (&new, width
);
544 check_datasheet (mc
, ds
, data
, n_rows
, proto
);
545 release_data (n_rows
, proto
, data
);
546 caseproto_unref (proto
);
549 /* Resize each column to each possible new size. */
550 for (pos
= 0; pos
< n_columns
; pos
++)
551 for (width_idx
= 0; width_idx
< params
->n_widths
; width_idx
++)
553 int owidth
= caseproto_get_width (oproto
, pos
);
554 int width
= params
->widths
[width_idx
];
555 if (mc_include_state (mc
))
557 struct resize_cb_aux aux
;
558 struct caseproto
*proto
;
559 struct datasheet
*ds
;
562 mc_name_operation (mc
, "resize column %zu (of %zu) "
563 "from width %d to %d",
564 pos
, n_columns
, owidth
, width
);
565 clone_model (ods
, odata
, &ds
, data
);
567 aux
.old_width
= owidth
;
568 aux
.new_width
= width
;
569 if (!datasheet_resize_column (ds
, pos
, width
, resize_cb
, &aux
))
571 proto
= caseproto_set_width (caseproto_ref (oproto
), pos
, width
);
573 for (i
= 0; i
< n_rows
; i
++)
575 union value
*old_value
= &data
[i
][pos
];
576 union value new_value
;
577 value_init (&new_value
, width
);
578 resize_cb (old_value
, &new_value
, &aux
);
579 value_swap (old_value
, &new_value
);
580 value_destroy (&new_value
, owidth
);
583 check_datasheet (mc
, ds
, data
, n_rows
, proto
);
584 release_data (n_rows
, proto
, data
);
585 caseproto_unref (proto
);
589 /* Delete all possible numbers of columns from all possible
591 for (pos
= 0; pos
< n_columns
; pos
++)
592 for (n
= 1; n
< n_columns
- pos
; n
++)
593 if (mc_include_state (mc
))
595 struct caseproto
*proto
;
596 struct datasheet
*ds
;
599 mc_name_operation (mc
, "delete %zu columns at %zu "
600 "(from %zu to %zu columns)",
601 n
, pos
, n_columns
, n_columns
- n
);
602 clone_model (ods
, odata
, &ds
, data
);
604 datasheet_delete_columns (ds
, pos
, n
);
605 proto
= caseproto_remove_widths (caseproto_ref (oproto
), pos
, n
);
607 for (i
= 0; i
< n_rows
; i
++)
609 for (j
= pos
; j
< pos
+ n
; j
++)
610 value_destroy (&data
[i
][j
], caseproto_get_width (oproto
, j
));
611 remove_range (&data
[i
], n_columns
, sizeof *data
[i
], pos
, n
);
614 check_datasheet (mc
, ds
, data
, n_rows
, proto
);
615 release_data (n_rows
, proto
, data
);
616 caseproto_unref (proto
);
619 /* Move all possible numbers of columns from all possible
620 existing positions to all possible new positions. */
621 for (pos
= 0; pos
< n_columns
; pos
++)
622 for (n
= 1; n
< n_columns
- pos
; n
++)
623 for (new_pos
= 0; new_pos
< n_columns
- n
; new_pos
++)
624 if (mc_include_state (mc
))
626 struct caseproto
*proto
;
627 struct datasheet
*ds
;
630 clone_model (ods
, odata
, &ds
, data
);
631 mc_name_operation (mc
, "move %zu columns (of %zu) from %zu to %zu",
632 n
, n_columns
, pos
, new_pos
);
634 datasheet_move_columns (ds
, pos
, new_pos
, n
);
636 for (i
= 0; i
< n_rows
; i
++)
637 move_range (&data
[i
], n_columns
, sizeof data
[i
][0],
639 proto
= caseproto_move_widths (caseproto_ref (oproto
),
642 check_datasheet (mc
, ds
, data
, n_rows
, proto
);
643 release_data (n_rows
, proto
, data
);
644 caseproto_unref (proto
);
647 /* Insert all possible numbers of rows in all possible
649 for (pos
= 0; pos
<= n_rows
; pos
++)
650 for (n
= 1; n
<= params
->max_rows
- n_rows
; n
++)
651 if (mc_include_state (mc
))
653 struct datasheet
*ds
;
654 struct ccase
*c
[MAX_ROWS
];
657 clone_model (ods
, odata
, &ds
, data
);
658 mc_name_operation (mc
, "insert %zu rows at %zu "
659 "(from %zu to %zu rows)",
660 n
, pos
, n_rows
, n_rows
+ n
);
662 for (i
= 0; i
< n
; i
++)
664 c
[i
] = case_create (oproto
);
665 for (j
= 0; j
< n_columns
; j
++)
666 value_from_param (case_data_rw_idx (c
[i
], j
),
667 caseproto_get_width (oproto
, j
),
668 params
->next_value
++);
671 insert_range (data
, n_rows
, sizeof data
[pos
], pos
, n
);
672 for (i
= 0; i
< n
; i
++)
673 for (j
= 0; j
< n_columns
; j
++)
675 int width
= caseproto_get_width (oproto
, j
);
676 value_init (&data
[i
+ pos
][j
], width
);
677 value_copy (&data
[i
+ pos
][j
], case_data_idx (c
[i
], j
), width
);
680 if (!datasheet_insert_rows (ds
, pos
, c
, n
))
681 mc_error (mc
, "datasheet_insert_rows failed");
683 check_datasheet (mc
, ds
, data
, n_rows
+ n
, oproto
);
684 release_data (n_rows
+ n
, oproto
, data
);
687 /* Delete all possible numbers of rows from all possible
689 for (pos
= 0; pos
< n_rows
; pos
++)
690 for (n
= 1; n
< n_rows
- pos
; n
++)
691 if (mc_include_state (mc
))
693 struct datasheet
*ds
;
695 clone_model (ods
, odata
, &ds
, data
);
696 mc_name_operation (mc
, "delete %zu rows at %zu "
697 "(from %zu to %zu rows)",
698 n
, pos
, n_rows
, n_rows
- n
);
700 datasheet_delete_rows (ds
, pos
, n
);
702 release_data (n
, oproto
, &data
[pos
]);
703 remove_range (&data
[0], n_rows
, sizeof data
[0], pos
, n
);
705 check_datasheet (mc
, ds
, data
, n_rows
- n
, oproto
);
706 release_data (n_rows
- n
, oproto
, data
);
709 /* Move all possible numbers of rows from all possible existing
710 positions to all possible new positions. */
711 for (pos
= 0; pos
< n_rows
; pos
++)
712 for (n
= 1; n
< n_rows
- pos
; n
++)
713 for (new_pos
= 0; new_pos
< n_rows
- n
; new_pos
++)
714 if (mc_include_state (mc
))
716 struct datasheet
*ds
;
718 clone_model (ods
, odata
, &ds
, data
);
719 mc_name_operation (mc
, "move %zu rows (of %zu) from %zu to %zu",
720 n
, n_rows
, pos
, new_pos
);
722 datasheet_move_rows (ds
, pos
, new_pos
, n
);
724 move_range (&data
[0], n_rows
, sizeof data
[0],
727 check_datasheet (mc
, ds
, data
, n_rows
, oproto
);
728 release_data (n_rows
, oproto
, data
);
731 release_data (n_rows
, oproto
, odata
);
734 /* "destroy" function for struct mc_class. */
736 datasheet_mc_destroy (const struct mc
*mc UNUSED
, void *ds_
)
738 struct datasheet
*ds
= ds_
;
739 datasheet_destroy (ds
);
753 static const struct argv_option datasheet_argv_options
[N_DATASHEET_OPTIONS
] =
755 {"max-rows", 0, required_argument
, OPT_MAX_ROWS
},
756 {"max-columns", 0, required_argument
, OPT_MAX_COLUMNS
},
757 {"backing-rows", 0, required_argument
, OPT_BACKING_ROWS
},
758 {"backing-widths", 0, required_argument
, OPT_BACKING_WIDTHS
},
759 {"widths", 0, required_argument
, OPT_WIDTHS
},
760 {"help", 'h', no_argument
, OPT_HELP
},
763 static void usage (void);
766 datasheet_option_callback (int id
, void *params_
)
768 struct datasheet_test_params
*params
= params_
;
772 params
->max_rows
= atoi (optarg
);
775 case OPT_MAX_COLUMNS
:
776 params
->max_cols
= atoi (optarg
);
779 case OPT_BACKING_ROWS
:
780 params
->backing_rows
= atoi (optarg
);
783 case OPT_BACKING_WIDTHS
:
787 params
->n_backing_cols
= 0;
788 for (w
= strtok (optarg
, ", "); w
!= NULL
; w
= strtok (NULL
, ", "))
790 int value
= atoi (w
);
792 if (params
->n_backing_cols
>= MAX_COLS
)
793 error (1, 0, "Too many widths on --backing-widths "
794 "(only %d are allowed)", MAX_COLS
);
795 if (!isdigit (w
[0]) || value
< 0 || value
> 31)
796 error (1, 0, "--backing-widths argument must be a list of 1 to "
797 "%d integers between 0 and 31 in increasing order",
799 params
->backing_widths
[params
->n_backing_cols
++] = value
;
809 params
->n_widths
= 0;
810 for (w
= strtok (optarg
, ", "); w
!= NULL
; w
= strtok (NULL
, ", "))
812 int value
= atoi (w
);
814 if (params
->n_widths
>= MAX_WIDTHS
)
815 error (1, 0, "Too many widths on --widths (only %d are allowed)",
817 if (!isdigit (w
[0]) || value
< 0 || value
> 31)
818 error (1, 0, "--widths argument must be a list of 1 to %d "
819 "integers between 0 and 31 in increasing order",
822 /* This is an artificial requirement merely to ensure
823 that there are no duplicates. Duplicates aren't a
824 real problem but they would waste time. */
826 error (1, 0, "--widths arguments must be in increasing order");
828 params
->widths
[params
->n_widths
++] = value
;
830 if (params
->n_widths
== 0)
831 error (1, 0, "at least one value must be specified on --widths");
847 printf ("%s, for testing the datasheet implementation.\n"
848 "Usage: %s [OPTION]...\n"
849 "\nTest state space parameters (min...max, default):\n"
850 " --max-rows=N Maximum number of rows (0...5, 3)\n"
851 " --max-rows=N Maximum number of columns (0...5, 3)\n"
852 " --backing-rows=N Rows of backing store (0...max_rows, 0)\n"
853 " --backing-widths=W[,W]... Backing store widths to test (0=num)\n"
854 " --widths=W[,W]... Column widths to test, where 0=numeric,\n"
855 " other values are string widths (0,1,11)\n",
856 program_name
, program_name
);
858 printf ("\nOther options:\n"
859 " --help Display this help message\n"
860 "\nReport bugs to <%s>\n", PACKAGE_BUGREPORT
);
865 main (int argc
, char *argv
[])
867 static const struct mc_class datasheet_mc_class
=
871 datasheet_mc_destroy
,
874 struct datasheet_test_params params
;
875 struct mc_options
*options
;
876 struct mc_results
*results
;
877 struct argv_parser
*parser
;
881 set_program_name (argv
[0]);
883 /* Default parameters. */
886 params
.backing_rows
= 0;
887 params
.n_backing_cols
= 0;
888 params
.widths
[0] = 0;
889 params
.widths
[1] = 1;
890 params
.widths
[2] = 11;
892 params
.next_value
= 1;
894 /* Parse command line. */
895 parser
= argv_parser_create ();
896 options
= mc_options_create ();
897 mc_options_register_argv_parser (options
, parser
);
898 argv_parser_add_options (parser
, datasheet_argv_options
, N_DATASHEET_OPTIONS
,
899 datasheet_option_callback
, ¶ms
);
900 if (!argv_parser_run (parser
, argc
, argv
))
902 argv_parser_destroy (parser
);
903 verbosity
= mc_options_get_verbosity (options
);
905 /* Force parameters into allowed ranges. */
906 params
.max_rows
= MIN (params
.max_rows
, MAX_ROWS
);
907 params
.max_cols
= MIN (params
.max_cols
, MAX_COLS
);
908 params
.backing_rows
= MIN (params
.backing_rows
, params
.max_rows
);
909 params
.n_backing_cols
= MIN (params
.n_backing_cols
, params
.max_cols
);
910 mc_options_set_aux (options
, ¶ms
);
911 results
= mc_run (&datasheet_mc_class
, options
);
913 /* Output results. */
914 success
= (mc_results_get_stop_reason (results
) != MC_MAX_ERROR_COUNT
915 && mc_results_get_stop_reason (results
) != MC_INTERRUPTED
);
916 if (verbosity
> 0 || !success
)
920 printf ("Parameters: --max-rows=%d --max-columns=%d --backing-rows=%d ",
921 params
.max_rows
, params
.max_cols
, params
.backing_rows
);
923 printf ("--backing-widths=");
924 for (i
= 0; i
< params
.n_backing_cols
; i
++)
928 printf ("%d", params
.backing_widths
[i
]);
932 printf ("--widths=");
933 for (i
= 0; i
< params
.n_widths
; i
++)
937 printf ("%d", params
.widths
[i
]);
940 mc_results_print (results
, stdout
);
942 mc_results_destroy (results
);
944 return success
? 0 : EXIT_FAILURE
;