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>
47 /* lazy_casereader callback function to instantiate a casereader
48 from the datasheet. */
49 static struct casereader *
50 lazy_callback (void *ds_)
52 struct datasheet *ds = ds_;
53 return datasheet_make_reader (ds);
57 /* Maximum size of datasheet supported for model checking
64 struct datasheet_test_params
67 int max_rows; /* Maximum number of rows. */
68 int max_cols; /* Maximum number of columns. */
69 int backing_rows; /* Number of rows of backing store. */
70 int backing_widths[MAX_COLS]; /* Widths of columns of backing store. */
71 int n_backing_cols; /* Number of columns of backing store. */
72 int widths[MAX_WIDTHS]; /* Allowed column widths. */
76 unsigned int next_value;
80 check_caseproto (struct mc *mc, const struct caseproto *benchmark,
81 const struct caseproto *test, const char *test_name)
83 size_t n_columns = caseproto_get_n_widths (benchmark);
87 if (n_columns != caseproto_get_n_widths (test))
89 mc_error (mc, "%s column count (%zu) does not match expected (%zu)",
90 test_name, caseproto_get_n_widths (test), n_columns);
95 for (col = 0; col < n_columns; col++)
97 int benchmark_width = caseproto_get_width (benchmark, col);
98 int test_width = caseproto_get_width (test, col);
99 if (benchmark_width != test_width)
101 mc_error (mc, "%s column %zu width (%d) differs from expected (%d)",
102 test_name, col, test_width, benchmark_width);
109 /* Checks that READER contains the N_ROWS rows and N_COLUMNS
110 columns of data in ARRAY, reporting any errors via MC. */
112 check_datasheet_casereader (struct mc *mc, struct casereader *reader,
113 union value array[MAX_ROWS][MAX_COLS],
114 size_t n_rows, const struct caseproto *proto)
116 size_t n_columns = caseproto_get_n_widths (proto);
118 if (!check_caseproto (mc, proto, casereader_get_proto (reader),
121 else if (casereader_get_case_cnt (reader) != n_rows)
123 if (casereader_get_case_cnt (reader) == CASENUMBER_MAX
124 && casereader_count_cases (reader) == n_rows)
125 mc_error (mc, "datasheet casereader has unknown case count");
127 mc_error (mc, "casereader row count (%lu) does not match "
129 (unsigned long int) casereader_get_case_cnt (reader),
137 for (row = 0; row < n_rows; row++)
141 c = casereader_read (reader);
144 mc_error (mc, "casereader_read failed reading row %zu of %zu "
145 "(%zu columns)", row, n_rows, n_columns);
149 for (col = 0; col < n_columns; col++)
151 int width = caseproto_get_width (proto, col);
152 if (!value_equal (case_data_idx (c, col), &array[row][col],
156 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
158 row, col, n_rows, n_columns,
159 DBL_DIG + 1, case_num_idx (c, col),
160 DBL_DIG + 1, array[row][col].f);
162 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
164 row, col, n_rows, n_columns,
165 width, case_str_idx (c, col),
166 width, value_str (&array[row][col], width));
173 c = casereader_read (reader);
175 mc_error (mc, "casereader has extra cases (expected %zu)", n_rows);
179 /* Checks that datasheet DS contains has N_ROWS rows, N_COLUMNS
180 columns, and the same contents as ARRAY, reporting any
181 mismatches via mc_error. Then, adds DS to MC as a new state. */
183 check_datasheet (struct mc *mc, struct datasheet *ds,
184 union value array[MAX_ROWS][MAX_COLS],
185 size_t n_rows, const struct caseproto *proto)
187 size_t n_columns = caseproto_get_n_widths (proto);
188 struct datasheet *ds2;
189 struct casereader *reader;
190 unsigned long int serial = 0;
192 assert (n_rows < MAX_ROWS);
193 assert (n_columns < MAX_COLS);
195 /* Check contents of datasheet via datasheet functions. */
196 if (!check_caseproto (mc, proto, datasheet_get_proto (ds), "datasheet"))
198 /* check_caseproto emitted errors already. */
200 else if (n_rows != datasheet_get_n_rows (ds))
201 mc_error (mc, "row count (%lu) does not match expected (%zu)",
202 (unsigned long int) datasheet_get_n_rows (ds), n_rows);
206 bool difference = false;
208 for (row = 0; row < n_rows; row++)
209 for (col = 0; col < n_columns; col++)
211 int width = caseproto_get_width (proto, col);
212 union value *av = &array[row][col];
215 value_init (&v, width);
216 if (!datasheet_get_value (ds, row, col, &v))
218 if (!value_equal (&v, av, width))
221 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
222 "%.*g != %.*g", row, col, n_rows, n_columns,
223 DBL_DIG + 1, v.f, DBL_DIG + 1, av->f);
225 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
227 row, col, n_rows, n_columns,
228 width, value_str (&v, width),
229 width, value_str (av, width));
232 value_destroy (&v, width);
239 mc_error (mc, "expected:");
241 for (row = 0; row < n_rows; row++)
244 ds_put_format (&s, "row %zu:", row);
245 for (col = 0; col < n_columns; col++)
247 const union value *v = &array[row][col];
248 int width = caseproto_get_width (proto, col);
250 ds_put_format (&s, " %g", v->f);
252 ds_put_format (&s, " '%.*s'", width, value_str (v, width));
254 mc_error (mc, "%s", ds_cstr (&s));
257 mc_error (mc, "actual:");
259 for (row = 0; row < n_rows; row++)
262 ds_put_format (&s, "row %zu:", row);
263 for (col = 0; col < n_columns; col++)
265 int width = caseproto_get_width (proto, col);
267 value_init (&v, width);
268 if (!datasheet_get_value (ds, row, col, &v))
271 ds_put_format (&s, " %g", v.f);
273 ds_put_format (&s, " '%.*s'",
274 width, value_str (&v, width));
276 mc_error (mc, "%s", ds_cstr (&s));
283 /* Check that datasheet contents are correct when read through
285 ds2 = clone_datasheet (ds);
286 reader = datasheet_make_reader (ds2);
287 check_datasheet_casereader (mc, reader, array, n_rows, proto);
288 casereader_destroy (reader);
290 /* Check that datasheet contents are correct when read through
291 casereader with lazy_casereader wrapped around it. This is
292 valuable because otherwise there is no non-GUI code that
293 uses the lazy_casereader. */
294 ds2 = clone_datasheet (ds);
295 reader = lazy_casereader_create (datasheet_get_proto (ds2), n_rows,
296 lazy_callback, ds2, &serial);
297 check_datasheet_casereader (mc, reader, array, n_rows, proto);
298 if (lazy_casereader_destroy (reader, serial))
300 /* Lazy casereader was never instantiated. This will
301 only happen if there are no rows (because in that case
302 casereader_read never gets called). */
303 datasheet_destroy (ds2);
305 mc_error (mc, "lazy casereader not instantiated, but should "
306 "have been (size %zu,%zu)", n_rows, n_columns);
310 /* Lazy casereader was instantiated. This is the common
311 case, in which some casereader operation
312 (casereader_read in this case) was performed on the
314 casereader_destroy (reader);
316 mc_error (mc, "lazy casereader instantiated, but should not "
317 "have been (size %zu,%zu)", n_rows, n_columns);
320 if (mc_discard_dup_state (mc, hash_datasheet (ds)))
321 datasheet_destroy (ds);
323 mc_add_state (mc, ds);
326 /* Extracts the contents of DS into DATA. */
328 extract_data (const struct datasheet *ds, union value data[MAX_ROWS][MAX_COLS])
330 const struct caseproto *proto = datasheet_get_proto (ds);
331 size_t n_columns = datasheet_get_n_columns (ds);
332 size_t n_rows = datasheet_get_n_rows (ds);
335 assert (n_rows < MAX_ROWS);
336 assert (n_columns < MAX_COLS);
337 for (row = 0; row < n_rows; row++)
338 for (col = 0; col < n_columns; col++)
340 int width = caseproto_get_width (proto, col);
341 union value *v = &data[row][col];
342 value_init (v, width);
343 if (!datasheet_get_value (ds, row, col, v))
348 /* Copies the contents of ODATA into DATA. Each of the N_ROWS
349 rows of ODATA and DATA must have prototype PROTO. */
351 clone_data (size_t n_rows, const struct caseproto *proto,
352 union value odata[MAX_ROWS][MAX_COLS],
353 union value data[MAX_ROWS][MAX_COLS])
355 size_t n_columns = caseproto_get_n_widths (proto);
358 assert (n_rows < MAX_ROWS);
359 assert (n_columns < MAX_COLS);
360 for (row = 0; row < n_rows; row++)
361 for (col = 0; col < n_columns; col++)
363 int width = caseproto_get_width (proto, col);
364 const union value *ov = &odata[row][col];
365 union value *v = &data[row][col];
366 value_init (v, width);
367 value_copy (v, ov, width);
372 release_data (size_t n_rows, const struct caseproto *proto,
373 union value data[MAX_ROWS][MAX_COLS])
375 size_t n_columns = caseproto_get_n_widths (proto);
378 assert (n_rows < MAX_ROWS);
379 assert (n_columns < MAX_COLS);
380 for (col = 0; col < n_columns; col++)
382 int width = caseproto_get_width (proto, col);
383 if (value_needs_init (width))
384 for (row = 0; row < n_rows; row++)
385 value_destroy (&data[row][col], width);
389 /* Clones the structure and contents of ODS into *DS,
390 and the contents of ODATA into DATA. */
392 clone_model (const struct datasheet *ods,
393 union value odata[MAX_ROWS][MAX_COLS],
394 struct datasheet **ds,
395 union value data[MAX_ROWS][MAX_COLS])
397 *ds = clone_datasheet (ods);
398 clone_data (datasheet_get_n_rows (ods), datasheet_get_proto (ods),
403 value_from_param (union value *value, int width, unsigned int idx)
406 value->f = idx & 0xffff;
409 unsigned int hash = hash_int (idx, 0);
410 uint8_t *string = value_str_rw (value, width);
414 for (offset = 0; offset < width; offset++)
415 string[offset] = "ABCDEFGHIJ"[(hash >> offset) % 10];
419 /* "init" function for struct mc_class. */
421 datasheet_mc_init (struct mc *mc)
423 struct datasheet_test_params *params = mc_get_aux (mc);
424 struct datasheet *ds;
426 if (params->backing_rows == 0 && params->n_backing_cols == 0)
428 /* Create unbacked datasheet. */
429 struct caseproto *proto;
430 ds = datasheet_create (NULL);
431 mc_name_operation (mc, "empty datasheet");
432 proto = caseproto_create ();
433 check_datasheet (mc, ds, NULL, 0, proto);
434 caseproto_unref (proto);
438 /* Create datasheet with backing. */
439 struct casewriter *writer;
440 struct casereader *reader;
441 union value data[MAX_ROWS][MAX_COLS];
442 struct caseproto *proto;
445 assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
446 assert (params->n_backing_cols > 0
447 && params->n_backing_cols <= MAX_COLS);
449 proto = caseproto_create ();
450 for (col = 0; col < params->n_backing_cols; col++)
451 proto = caseproto_add_width (proto, params->backing_widths[col]);
453 writer = mem_writer_create (proto);
454 for (row = 0; row < params->backing_rows; row++)
458 c = case_create (proto);
459 for (col = 0; col < params->n_backing_cols; col++)
461 int width = params->backing_widths[col];
462 union value *value = &data[row][col];
463 value_init (value, width);
464 value_from_param (value, width, params->next_value++);
465 value_copy (case_data_rw_idx (c, col), value, width);
467 casewriter_write (writer, c);
470 reader = casewriter_make_reader (writer);
471 assert (reader != NULL);
473 ds = datasheet_create (reader);
474 mc_name_operation (mc, "datasheet with (%d,%d) backing",
475 params->backing_rows, params->n_backing_cols);
476 check_datasheet (mc, ds, data,
477 params->backing_rows, proto);
478 release_data (params->backing_rows, proto, data);
479 caseproto_unref (proto);
490 resize_cb (const union value *old_value, union value *new_value, const void *aux_)
492 const struct resize_cb_aux *aux = aux_;
494 value_from_param (new_value, aux->new_width,
495 value_hash (old_value, aux->old_width, 0));
498 /* "mutate" function for struct mc_class. */
500 datasheet_mc_mutate (struct mc *mc, const void *ods_)
502 struct datasheet_test_params *params = mc_get_aux (mc);
504 const struct datasheet *ods = ods_;
505 union value odata[MAX_ROWS][MAX_COLS];
506 union value data[MAX_ROWS][MAX_COLS];
507 const struct caseproto *oproto = datasheet_get_proto (ods);
508 size_t n_columns = datasheet_get_n_columns (ods);
509 size_t n_rows = datasheet_get_n_rows (ods);
510 size_t pos, new_pos, cnt, width_idx;
512 extract_data (ods, odata);
514 /* Insert a column in each possible position. */
515 if (n_columns < params->max_cols)
516 for (pos = 0; pos <= n_columns; pos++)
517 for (width_idx = 0; width_idx < params->n_widths; width_idx++)
518 if (mc_include_state (mc))
520 int width = params->widths[width_idx];
521 struct caseproto *proto;
522 struct datasheet *ds;
526 mc_name_operation (mc, "insert column at %zu "
527 "(from %zu to %zu columns)",
528 pos, n_columns, n_columns + 1);
529 clone_model (ods, odata, &ds, data);
531 value_init (&new, width);
532 value_from_param (&new, width, params->next_value++);
533 if (!datasheet_insert_column (ds, &new, width, pos))
534 mc_error (mc, "datasheet_insert_column failed");
535 proto = caseproto_insert_width (caseproto_ref (oproto),
538 for (i = 0; i < n_rows; i++)
540 insert_element (&data[i][0], n_columns, sizeof data[i][0],
542 value_init (&data[i][pos], width);
543 value_copy (&data[i][pos], &new, width);
545 value_destroy (&new, width);
547 check_datasheet (mc, ds, data, n_rows, proto);
548 release_data (n_rows, proto, data);
549 caseproto_unref (proto);
552 /* Resize each column to each possible new size. */
553 for (pos = 0; pos < n_columns; pos++)
554 for (width_idx = 0; width_idx < params->n_widths; width_idx++)
556 int owidth = caseproto_get_width (oproto, pos);
557 int width = params->widths[width_idx];
558 if (mc_include_state (mc))
560 struct resize_cb_aux aux;
561 struct caseproto *proto;
562 struct datasheet *ds;
565 mc_name_operation (mc, "resize column %zu (of %zu) "
566 "from width %d to %d",
567 pos, n_columns, owidth, width);
568 clone_model (ods, odata, &ds, data);
570 aux.old_width = owidth;
571 aux.new_width = width;
572 if (!datasheet_resize_column (ds, pos, width, resize_cb, &aux))
574 proto = caseproto_set_width (caseproto_ref (oproto), pos, width);
576 for (i = 0; i < n_rows; i++)
578 union value *old_value = &data[i][pos];
579 union value new_value;
580 value_init (&new_value, width);
581 resize_cb (old_value, &new_value, &aux);
582 value_swap (old_value, &new_value);
583 value_destroy (&new_value, owidth);
586 check_datasheet (mc, ds, data, n_rows, proto);
587 release_data (n_rows, proto, data);
588 caseproto_unref (proto);
592 /* Delete all possible numbers of columns from all possible
594 for (pos = 0; pos < n_columns; pos++)
595 for (cnt = 1; cnt < n_columns - pos; cnt++)
596 if (mc_include_state (mc))
598 struct caseproto *proto;
599 struct datasheet *ds;
602 mc_name_operation (mc, "delete %zu columns at %zu "
603 "(from %zu to %zu columns)",
604 cnt, pos, n_columns, n_columns - cnt);
605 clone_model (ods, odata, &ds, data);
607 datasheet_delete_columns (ds, pos, cnt);
608 proto = caseproto_remove_widths (caseproto_ref (oproto), pos, cnt);
610 for (i = 0; i < n_rows; i++)
612 for (j = pos; j < pos + cnt; j++)
613 value_destroy (&data[i][j], caseproto_get_width (oproto, j));
614 remove_range (&data[i], n_columns, sizeof *data[i], pos, cnt);
617 check_datasheet (mc, ds, data, n_rows, proto);
618 release_data (n_rows, proto, data);
619 caseproto_unref (proto);
622 /* Move all possible numbers of columns from all possible
623 existing positions to all possible new positions. */
624 for (pos = 0; pos < n_columns; pos++)
625 for (cnt = 1; cnt < n_columns - pos; cnt++)
626 for (new_pos = 0; new_pos < n_columns - cnt; new_pos++)
627 if (mc_include_state (mc))
629 struct caseproto *proto;
630 struct datasheet *ds;
633 clone_model (ods, odata, &ds, data);
634 mc_name_operation (mc, "move %zu columns (of %zu) from %zu to %zu",
635 cnt, n_columns, pos, new_pos);
637 datasheet_move_columns (ds, pos, new_pos, cnt);
639 for (i = 0; i < n_rows; i++)
640 move_range (&data[i], n_columns, sizeof data[i][0],
642 proto = caseproto_move_widths (caseproto_ref (oproto),
645 check_datasheet (mc, ds, data, n_rows, proto);
646 release_data (n_rows, proto, data);
647 caseproto_unref (proto);
650 /* Insert all possible numbers of rows in all possible
652 for (pos = 0; pos <= n_rows; pos++)
653 for (cnt = 1; cnt <= params->max_rows - n_rows; cnt++)
654 if (mc_include_state (mc))
656 struct datasheet *ds;
657 struct ccase *c[MAX_ROWS];
660 clone_model (ods, odata, &ds, data);
661 mc_name_operation (mc, "insert %zu rows at %zu "
662 "(from %zu to %zu rows)",
663 cnt, pos, n_rows, n_rows + cnt);
665 for (i = 0; i < cnt; i++)
667 c[i] = case_create (oproto);
668 for (j = 0; j < n_columns; j++)
669 value_from_param (case_data_rw_idx (c[i], j),
670 caseproto_get_width (oproto, j),
671 params->next_value++);
674 insert_range (data, n_rows, sizeof data[pos], pos, cnt);
675 for (i = 0; i < cnt; i++)
676 for (j = 0; j < n_columns; j++)
678 int width = caseproto_get_width (oproto, j);
679 value_init (&data[i + pos][j], width);
680 value_copy (&data[i + pos][j], case_data_idx (c[i], j), width);
683 if (!datasheet_insert_rows (ds, pos, c, cnt))
684 mc_error (mc, "datasheet_insert_rows failed");
686 check_datasheet (mc, ds, data, n_rows + cnt, oproto);
687 release_data (n_rows + cnt, oproto, data);
690 /* Delete all possible numbers of rows from all possible
692 for (pos = 0; pos < n_rows; pos++)
693 for (cnt = 1; cnt < n_rows - pos; cnt++)
694 if (mc_include_state (mc))
696 struct datasheet *ds;
698 clone_model (ods, odata, &ds, data);
699 mc_name_operation (mc, "delete %zu rows at %zu "
700 "(from %zu to %zu rows)",
701 cnt, pos, n_rows, n_rows - cnt);
703 datasheet_delete_rows (ds, pos, cnt);
705 release_data (cnt, oproto, &data[pos]);
706 remove_range (&data[0], n_rows, sizeof data[0], pos, cnt);
708 check_datasheet (mc, ds, data, n_rows - cnt, oproto);
709 release_data (n_rows - cnt, oproto, data);
712 /* Move all possible numbers of rows from all possible existing
713 positions to all possible new positions. */
714 for (pos = 0; pos < n_rows; pos++)
715 for (cnt = 1; cnt < n_rows - pos; cnt++)
716 for (new_pos = 0; new_pos < n_rows - cnt; new_pos++)
717 if (mc_include_state (mc))
719 struct datasheet *ds;
721 clone_model (ods, odata, &ds, data);
722 mc_name_operation (mc, "move %zu rows (of %zu) from %zu to %zu",
723 cnt, n_rows, pos, new_pos);
725 datasheet_move_rows (ds, pos, new_pos, cnt);
727 move_range (&data[0], n_rows, sizeof data[0],
730 check_datasheet (mc, ds, data, n_rows, oproto);
731 release_data (n_rows, oproto, data);
734 release_data (n_rows, oproto, odata);
737 /* "destroy" function for struct mc_class. */
739 datasheet_mc_destroy (const struct mc *mc UNUSED, void *ds_)
741 struct datasheet *ds = ds_;
742 datasheet_destroy (ds);
756 static const struct argv_option datasheet_argv_options[N_DATASHEET_OPTIONS] =
758 {"max-rows", 0, required_argument, OPT_MAX_ROWS},
759 {"max-columns", 0, required_argument, OPT_MAX_COLUMNS},
760 {"backing-rows", 0, required_argument, OPT_BACKING_ROWS},
761 {"backing-widths", 0, required_argument, OPT_BACKING_WIDTHS},
762 {"widths", 0, required_argument, OPT_WIDTHS},
763 {"help", 'h', no_argument, OPT_HELP},
766 static void usage (void);
769 datasheet_option_callback (int id, void *params_)
771 struct datasheet_test_params *params = params_;
775 params->max_rows = atoi (optarg);
778 case OPT_MAX_COLUMNS:
779 params->max_cols = atoi (optarg);
782 case OPT_BACKING_ROWS:
783 params->backing_rows = atoi (optarg);
786 case OPT_BACKING_WIDTHS:
790 params->n_backing_cols = 0;
791 for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
793 int value = atoi (w);
795 if (params->n_backing_cols >= MAX_COLS)
796 error (1, 0, "Too many widths on --backing-widths "
797 "(only %d are allowed)", MAX_COLS);
798 if (!isdigit (w[0]) || value < 0 || value > 31)
799 error (1, 0, "--backing-widths argument must be a list of 1 to "
800 "%d integers between 0 and 31 in increasing order",
802 params->backing_widths[params->n_backing_cols++] = value;
812 params->n_widths = 0;
813 for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
815 int value = atoi (w);
817 if (params->n_widths >= MAX_WIDTHS)
818 error (1, 0, "Too many widths on --widths (only %d are allowed)",
820 if (!isdigit (w[0]) || value < 0 || value > 31)
821 error (1, 0, "--widths argument must be a list of 1 to %d "
822 "integers between 0 and 31 in increasing order",
825 /* This is an artificial requirement merely to ensure
826 that there are no duplicates. Duplicates aren't a
827 real problem but they would waste time. */
829 error (1, 0, "--widths arguments must be in increasing order");
831 params->widths[params->n_widths++] = value;
833 if (params->n_widths == 0)
834 error (1, 0, "at least one value must be specified on --widths");
850 printf ("%s, for testing the datasheet implementation.\n"
851 "Usage: %s [OPTION]...\n"
852 "\nTest state space parameters (min...max, default):\n"
853 " --max-rows=N Maximum number of rows (0...5, 3)\n"
854 " --max-rows=N Maximum number of columns (0...5, 3)\n"
855 " --backing-rows=N Rows of backing store (0...max_rows, 0)\n"
856 " --backing-widths=W[,W]... Backing store widths to test (0=num)\n"
857 " --widths=W[,W]... Column widths to test, where 0=numeric,\n"
858 " other values are string widths (0,1,11)\n",
859 program_name, program_name);
861 fputs ("\nOther options:\n"
862 " --help Display this help message\n"
863 "\nReport bugs to <bug-gnu-pspp@gnu.org>\n",
869 main (int argc, char *argv[])
871 static const struct mc_class datasheet_mc_class =
875 datasheet_mc_destroy,
878 struct datasheet_test_params params;
879 struct mc_options *options;
880 struct mc_results *results;
881 struct argv_parser *parser;
885 set_program_name (argv[0]);
887 /* Default parameters. */
890 params.backing_rows = 0;
891 params.n_backing_cols = 0;
892 params.widths[0] = 0;
893 params.widths[1] = 1;
894 params.widths[2] = 11;
896 params.next_value = 1;
898 /* Parse comand line. */
899 parser = argv_parser_create ();
900 options = mc_options_create ();
901 mc_options_register_argv_parser (options, parser);
902 argv_parser_add_options (parser, datasheet_argv_options, N_DATASHEET_OPTIONS,
903 datasheet_option_callback, ¶ms);
904 if (!argv_parser_run (parser, argc, argv))
906 argv_parser_destroy (parser);
907 verbosity = mc_options_get_verbosity (options);
909 /* Force parameters into allowed ranges. */
910 params.max_rows = MIN (params.max_rows, MAX_ROWS);
911 params.max_cols = MIN (params.max_cols, MAX_COLS);
912 params.backing_rows = MIN (params.backing_rows, params.max_rows);
913 params.n_backing_cols = MIN (params.n_backing_cols, params.max_cols);
914 mc_options_set_aux (options, ¶ms);
915 results = mc_run (&datasheet_mc_class, options);
917 /* Output results. */
918 success = (mc_results_get_stop_reason (results) != MC_MAX_ERROR_COUNT
919 && mc_results_get_stop_reason (results) != MC_INTERRUPTED);
920 if (verbosity > 0 || !success)
924 printf ("Parameters: --max-rows=%d --max-columns=%d --backing-rows=%d ",
925 params.max_rows, params.max_cols, params.backing_rows);
927 printf ("--backing-widths=");
928 for (i = 0; i < params.n_backing_cols; i++)
932 printf ("%d", params.backing_widths[i]);
936 printf ("--widths=");
937 for (i = 0; i < params.n_widths; i++)
941 printf ("%d", params.widths[i]);
944 mc_results_print (results, stdout);
946 mc_results_destroy (results);
948 return success ? 0 : EXIT_FAILURE;