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, array[row][col].s);
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,
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, v->s);
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'", width, v.s);
275 mc_error (mc, "%s", ds_cstr (&s));
282 /* Check that datasheet contents are correct when read through
284 ds2 = clone_datasheet (ds);
285 reader = datasheet_make_reader (ds2);
286 check_datasheet_casereader (mc, reader, array, n_rows, proto);
287 casereader_destroy (reader);
289 /* Check that datasheet contents are correct when read through
290 casereader with lazy_casereader wrapped around it. This is
291 valuable because otherwise there is no non-GUI code that
292 uses the lazy_casereader. */
293 ds2 = clone_datasheet (ds);
294 reader = lazy_casereader_create (datasheet_get_proto (ds2), n_rows,
295 lazy_callback, ds2, &serial);
296 check_datasheet_casereader (mc, reader, array, n_rows, proto);
297 if (lazy_casereader_destroy (reader, serial))
299 /* Lazy casereader was never instantiated. This will
300 only happen if there are no rows (because in that case
301 casereader_read never gets called). */
302 datasheet_destroy (ds2);
304 mc_error (mc, "lazy casereader not instantiated, but should "
305 "have been (size %zu,%zu)", n_rows, n_columns);
309 /* Lazy casereader was instantiated. This is the common
310 case, in which some casereader operation
311 (casereader_read in this case) was performed on the
313 casereader_destroy (reader);
315 mc_error (mc, "lazy casereader instantiated, but should not "
316 "have been (size %zu,%zu)", n_rows, n_columns);
319 if (mc_discard_dup_state (mc, hash_datasheet (ds)))
320 datasheet_destroy (ds);
322 mc_add_state (mc, ds);
325 /* Extracts the contents of DS into DATA. */
327 extract_data (const struct datasheet *ds, union value data[MAX_ROWS][MAX_COLS])
329 const struct caseproto *proto = datasheet_get_proto (ds);
330 size_t n_columns = datasheet_get_n_columns (ds);
331 size_t n_rows = datasheet_get_n_rows (ds);
334 assert (n_rows < MAX_ROWS);
335 assert (n_columns < MAX_COLS);
336 for (row = 0; row < n_rows; row++)
337 for (col = 0; col < n_columns; col++)
339 int width = caseproto_get_width (proto, col);
340 union value *v = &data[row][col];
341 value_init (v, width);
342 if (!datasheet_get_value (ds, row, col, v))
347 /* Copies the contents of ODATA into DATA. Each of the N_ROWS
348 rows of ODATA and DATA must have prototype PROTO. */
350 clone_data (size_t n_rows, const struct caseproto *proto,
351 union value odata[MAX_ROWS][MAX_COLS],
352 union value data[MAX_ROWS][MAX_COLS])
354 size_t n_columns = caseproto_get_n_widths (proto);
357 assert (n_rows < MAX_ROWS);
358 assert (n_columns < MAX_COLS);
359 for (row = 0; row < n_rows; row++)
360 for (col = 0; col < n_columns; col++)
362 int width = caseproto_get_width (proto, col);
363 const union value *ov = &odata[row][col];
364 union value *v = &data[row][col];
365 value_init (v, width);
366 value_copy (v, ov, width);
371 release_data (size_t n_rows, const struct caseproto *proto,
372 union value data[MAX_ROWS][MAX_COLS])
374 size_t n_columns = caseproto_get_n_widths (proto);
377 assert (n_rows < MAX_ROWS);
378 assert (n_columns < MAX_COLS);
379 for (col = 0; col < n_columns; col++)
381 int width = caseproto_get_width (proto, col);
382 if (value_needs_init (width))
383 for (row = 0; row < n_rows; row++)
384 value_destroy (&data[row][col], width);
388 /* Clones the structure and contents of ODS into *DS,
389 and the contents of ODATA into DATA. */
391 clone_model (const struct datasheet *ods,
392 union value odata[MAX_ROWS][MAX_COLS],
393 struct datasheet **ds,
394 union value data[MAX_ROWS][MAX_COLS])
396 *ds = clone_datasheet (ods);
397 clone_data (datasheet_get_n_rows (ods), datasheet_get_proto (ods),
402 value_from_param (union value *value, int width, unsigned int idx)
405 value->f = idx & 0xffff;
408 unsigned int hash = hash_int (idx, 0);
412 for (offset = 0; offset < width; offset++)
413 value->s[offset] = "ABCDEFGHIJ"[(hash >> offset) % 10];
417 /* "init" function for struct mc_class. */
419 datasheet_mc_init (struct mc *mc)
421 struct datasheet_test_params *params = mc_get_aux (mc);
422 struct datasheet *ds;
424 if (params->backing_rows == 0 && params->n_backing_cols == 0)
426 /* Create unbacked datasheet. */
427 struct caseproto *proto;
428 ds = datasheet_create (NULL);
429 mc_name_operation (mc, "empty datasheet");
430 proto = caseproto_create ();
431 check_datasheet (mc, ds, NULL, 0, proto);
432 caseproto_unref (proto);
436 /* Create datasheet with backing. */
437 struct casewriter *writer;
438 struct casereader *reader;
439 union value data[MAX_ROWS][MAX_COLS];
440 struct caseproto *proto;
443 assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
444 assert (params->n_backing_cols > 0
445 && params->n_backing_cols <= MAX_COLS);
447 proto = caseproto_create ();
448 for (col = 0; col < params->n_backing_cols; col++)
449 proto = caseproto_add_width (proto, params->backing_widths[col]);
451 writer = mem_writer_create (proto);
452 for (row = 0; row < params->backing_rows; row++)
456 c = case_create (proto);
457 for (col = 0; col < params->n_backing_cols; col++)
459 int width = params->backing_widths[col];
460 union value *value = &data[row][col];
461 value_init (value, width);
462 value_from_param (value, width, params->next_value++);
463 value_copy (case_data_rw_idx (c, col), value, width);
465 casewriter_write (writer, c);
468 reader = casewriter_make_reader (writer);
469 assert (reader != NULL);
471 ds = datasheet_create (reader);
472 mc_name_operation (mc, "datasheet with (%d,%d) backing",
473 params->backing_rows, params->n_backing_cols);
474 check_datasheet (mc, ds, data,
475 params->backing_rows, proto);
476 release_data (params->backing_rows, proto, data);
477 caseproto_unref (proto);
488 resize_cb (const union value *old_value, union value *new_value, const void *aux_)
490 const struct resize_cb_aux *aux = aux_;
492 value_from_param (new_value, aux->new_width,
493 value_hash (old_value, aux->old_width, 0));
496 /* "mutate" function for struct mc_class. */
498 datasheet_mc_mutate (struct mc *mc, const void *ods_)
500 struct datasheet_test_params *params = mc_get_aux (mc);
502 const struct datasheet *ods = ods_;
503 union value odata[MAX_ROWS][MAX_COLS];
504 union value data[MAX_ROWS][MAX_COLS];
505 const struct caseproto *oproto = datasheet_get_proto (ods);
506 size_t n_columns = datasheet_get_n_columns (ods);
507 size_t n_rows = datasheet_get_n_rows (ods);
508 size_t pos, new_pos, cnt, width_idx;
510 extract_data (ods, odata);
512 /* Insert a column in each possible position. */
513 if (n_columns < params->max_cols)
514 for (pos = 0; pos <= n_columns; pos++)
515 for (width_idx = 0; width_idx < params->n_widths; width_idx++)
516 if (mc_include_state (mc))
518 int width = params->widths[width_idx];
519 struct caseproto *proto;
520 struct datasheet *ds;
524 mc_name_operation (mc, "insert column at %zu "
525 "(from %zu to %zu columns)",
526 pos, n_columns, n_columns + 1);
527 clone_model (ods, odata, &ds, data);
529 value_init (&new, width);
530 value_from_param (&new, width, params->next_value++);
531 if (!datasheet_insert_column (ds, &new, width, pos))
532 mc_error (mc, "datasheet_insert_column failed");
533 proto = caseproto_insert_width (caseproto_ref (oproto),
536 for (i = 0; i < n_rows; i++)
538 insert_element (&data[i][0], n_columns, sizeof data[i][0],
540 value_init (&data[i][pos], width);
541 value_copy (&data[i][pos], &new, width);
543 value_destroy (&new, width);
545 check_datasheet (mc, ds, data, n_rows, proto);
546 release_data (n_rows, proto, data);
547 caseproto_unref (proto);
550 /* Resize each column to each possible new size. */
551 for (pos = 0; pos < n_columns; pos++)
552 for (width_idx = 0; width_idx < params->n_widths; width_idx++)
554 int owidth = caseproto_get_width (oproto, pos);
555 int width = params->widths[width_idx];
556 if (mc_include_state (mc))
558 struct resize_cb_aux aux;
559 struct caseproto *proto;
560 struct datasheet *ds;
563 mc_name_operation (mc, "resize column %zu (of %zu) "
564 "from width %d to %d",
565 pos, n_columns, owidth, width);
566 clone_model (ods, odata, &ds, data);
568 aux.old_width = owidth;
569 aux.new_width = width;
570 if (!datasheet_resize_column (ds, pos, width, resize_cb, &aux))
572 proto = caseproto_set_width (caseproto_ref (oproto), pos, width);
574 for (i = 0; i < n_rows; i++)
576 union value *old_value = &data[i][pos];
577 union value new_value;
578 value_init (&new_value, width);
579 resize_cb (old_value, &new_value, &aux);
580 value_swap (old_value, &new_value);
581 value_destroy (&new_value, owidth);
584 check_datasheet (mc, ds, data, n_rows, proto);
585 release_data (n_rows, proto, data);
586 caseproto_unref (proto);
590 /* Delete all possible numbers of columns from all possible
592 for (pos = 0; pos < n_columns; pos++)
593 for (cnt = 1; cnt < n_columns - pos; cnt++)
594 if (mc_include_state (mc))
596 struct caseproto *proto;
597 struct datasheet *ds;
600 mc_name_operation (mc, "delete %zu columns at %zu "
601 "(from %zu to %zu columns)",
602 cnt, pos, n_columns, n_columns - cnt);
603 clone_model (ods, odata, &ds, data);
605 datasheet_delete_columns (ds, pos, cnt);
606 proto = caseproto_remove_widths (caseproto_ref (oproto), pos, cnt);
608 for (i = 0; i < n_rows; i++)
610 for (j = pos; j < pos + cnt; j++)
611 value_destroy (&data[i][j], caseproto_get_width (oproto, j));
612 remove_range (&data[i], n_columns, sizeof *data[i], pos, cnt);
615 check_datasheet (mc, ds, data, n_rows, proto);
616 release_data (n_rows, proto, data);
617 caseproto_unref (proto);
620 /* Move all possible numbers of columns from all possible
621 existing positions to all possible new positions. */
622 for (pos = 0; pos < n_columns; pos++)
623 for (cnt = 1; cnt < n_columns - pos; cnt++)
624 for (new_pos = 0; new_pos < n_columns - cnt; new_pos++)
625 if (mc_include_state (mc))
627 struct caseproto *proto;
628 struct datasheet *ds;
631 clone_model (ods, odata, &ds, data);
632 mc_name_operation (mc, "move %zu columns (of %zu) from %zu to %zu",
633 cnt, n_columns, pos, new_pos);
635 datasheet_move_columns (ds, pos, new_pos, cnt);
637 for (i = 0; i < n_rows; i++)
638 move_range (&data[i], n_columns, sizeof data[i][0],
640 proto = caseproto_move_widths (caseproto_ref (oproto),
643 check_datasheet (mc, ds, data, n_rows, proto);
644 release_data (n_rows, proto, data);
645 caseproto_unref (proto);
648 /* Insert all possible numbers of rows in all possible
650 for (pos = 0; pos <= n_rows; pos++)
651 for (cnt = 1; cnt <= params->max_rows - n_rows; cnt++)
652 if (mc_include_state (mc))
654 struct datasheet *ds;
655 struct ccase *c[MAX_ROWS];
658 clone_model (ods, odata, &ds, data);
659 mc_name_operation (mc, "insert %zu rows at %zu "
660 "(from %zu to %zu rows)",
661 cnt, pos, n_rows, n_rows + cnt);
663 for (i = 0; i < cnt; i++)
665 c[i] = case_create (oproto);
666 for (j = 0; j < n_columns; j++)
667 value_from_param (case_data_rw_idx (c[i], j),
668 caseproto_get_width (oproto, j),
669 params->next_value++);
672 insert_range (data, n_rows, sizeof data[pos], pos, cnt);
673 for (i = 0; i < cnt; i++)
674 for (j = 0; j < n_columns; j++)
676 int width = caseproto_get_width (oproto, j);
677 value_init (&data[i + pos][j], width);
678 value_copy (&data[i + pos][j], case_data_idx (c[i], j), width);
681 if (!datasheet_insert_rows (ds, pos, c, cnt))
682 mc_error (mc, "datasheet_insert_rows failed");
684 check_datasheet (mc, ds, data, n_rows + cnt, oproto);
685 release_data (n_rows + cnt, oproto, data);
688 /* Delete all possible numbers of rows from all possible
690 for (pos = 0; pos < n_rows; pos++)
691 for (cnt = 1; cnt < n_rows - pos; cnt++)
692 if (mc_include_state (mc))
694 struct datasheet *ds;
696 clone_model (ods, odata, &ds, data);
697 mc_name_operation (mc, "delete %zu rows at %zu "
698 "(from %zu to %zu rows)",
699 cnt, pos, n_rows, n_rows - cnt);
701 datasheet_delete_rows (ds, pos, cnt);
703 release_data (cnt, oproto, &data[pos]);
704 remove_range (&data[0], n_rows, sizeof data[0], pos, cnt);
706 check_datasheet (mc, ds, data, n_rows - cnt, oproto);
707 release_data (n_rows - cnt, oproto, data);
710 /* Move all possible numbers of rows from all possible existing
711 positions to all possible new positions. */
712 for (pos = 0; pos < n_rows; pos++)
713 for (cnt = 1; cnt < n_rows - pos; cnt++)
714 for (new_pos = 0; new_pos < n_rows - cnt; new_pos++)
715 if (mc_include_state (mc))
717 struct datasheet *ds;
719 clone_model (ods, odata, &ds, data);
720 mc_name_operation (mc, "move %zu rows (of %zu) from %zu to %zu",
721 cnt, n_rows, pos, new_pos);
723 datasheet_move_rows (ds, pos, new_pos, cnt);
725 move_range (&data[0], n_rows, sizeof data[0],
728 check_datasheet (mc, ds, data, n_rows, oproto);
729 release_data (n_rows, oproto, data);
732 release_data (n_rows, oproto, odata);
735 /* "destroy" function for struct mc_class. */
737 datasheet_mc_destroy (const struct mc *mc UNUSED, void *ds_)
739 struct datasheet *ds = ds_;
740 datasheet_destroy (ds);
754 static const struct argv_option datasheet_argv_options[N_DATASHEET_OPTIONS] =
756 {"max-rows", 0, required_argument, OPT_MAX_ROWS},
757 {"max-columns", 0, required_argument, OPT_MAX_COLUMNS},
758 {"backing-rows", 0, required_argument, OPT_BACKING_ROWS},
759 {"backing-widths", 0, required_argument, OPT_BACKING_WIDTHS},
760 {"widths", 0, required_argument, OPT_WIDTHS},
761 {"help", 'h', no_argument, OPT_HELP},
764 static void usage (void);
767 datasheet_option_callback (int id, void *params_)
769 struct datasheet_test_params *params = params_;
773 params->max_rows = atoi (optarg);
776 case OPT_MAX_COLUMNS:
777 params->max_cols = atoi (optarg);
780 case OPT_BACKING_ROWS:
781 params->backing_rows = atoi (optarg);
784 case OPT_BACKING_WIDTHS:
788 params->n_backing_cols = 0;
789 for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
791 int value = atoi (w);
793 if (params->n_backing_cols >= MAX_COLS)
794 error (1, 0, "Too many widths on --backing-widths "
795 "(only %d are allowed)", MAX_COLS);
796 if (!isdigit (w[0]) || value < 0 || value > 31)
797 error (1, 0, "--backing-widths argument must be a list of 1 to "
798 "%d integers between 0 and 31 in increasing order",
800 params->backing_widths[params->n_backing_cols++] = value;
810 params->n_widths = 0;
811 for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
813 int value = atoi (w);
815 if (params->n_widths >= MAX_WIDTHS)
816 error (1, 0, "Too many widths on --widths (only %d are allowed)",
818 if (!isdigit (w[0]) || value < 0 || value > 31)
819 error (1, 0, "--widths argument must be a list of 1 to %d "
820 "integers between 0 and 31 in increasing order",
823 /* This is an artificial requirement merely to ensure
824 that there are no duplicates. Duplicates aren't a
825 real problem but they would waste time. */
827 error (1, 0, "--widths arguments must be in increasing order");
829 params->widths[params->n_widths++] = value;
831 if (params->n_widths == 0)
832 error (1, 0, "at least one value must be specified on --widths");
848 printf ("%s, for testing the datasheet implementation.\n"
849 "Usage: %s [OPTION]...\n"
850 "\nTest state space parameters (min...max, default):\n"
851 " --max-rows=N Maximum number of rows (0...5, 3)\n"
852 " --max-rows=N Maximum number of columns (0...5, 3)\n"
853 " --backing-rows=N Rows of backing store (0...max_rows, 0)\n"
854 " --backing-widths=W[,W]... Backing store widths to test (0=num)\n"
855 " --widths=W[,W]... Column widths to test, where 0=numeric,\n"
856 " other values are string widths (0,1,11)\n",
857 program_name, program_name);
859 fputs ("\nOther options:\n"
860 " --help Display this help message\n"
861 "\nReport bugs to <bug-gnu-pspp@gnu.org>\n",
867 main (int argc, char *argv[])
869 static const struct mc_class datasheet_mc_class =
873 datasheet_mc_destroy,
876 struct datasheet_test_params params;
877 struct mc_options *options;
878 struct mc_results *results;
879 struct argv_parser *parser;
883 set_program_name (argv[0]);
885 /* Default parameters. */
888 params.backing_rows = 0;
889 params.n_backing_cols = 0;
890 params.widths[0] = 0;
891 params.widths[1] = 1;
892 params.widths[2] = 11;
894 params.next_value = 1;
896 /* Parse command line. */
897 parser = argv_parser_create ();
898 options = mc_options_create ();
899 mc_options_register_argv_parser (options, parser);
900 argv_parser_add_options (parser, datasheet_argv_options, N_DATASHEET_OPTIONS,
901 datasheet_option_callback, ¶ms);
902 if (!argv_parser_run (parser, argc, argv))
904 argv_parser_destroy (parser);
905 verbosity = mc_options_get_verbosity (options);
907 /* Force parameters into allowed ranges. */
908 params.max_rows = MIN (params.max_rows, MAX_ROWS);
909 params.max_cols = MIN (params.max_cols, MAX_COLS);
910 params.backing_rows = MIN (params.backing_rows, params.max_rows);
911 params.n_backing_cols = MIN (params.n_backing_cols, params.max_cols);
912 mc_options_set_aux (options, ¶ms);
913 results = mc_run (&datasheet_mc_class, options);
915 /* Output results. */
916 success = (mc_results_get_stop_reason (results) != MC_MAX_ERROR_COUNT
917 && mc_results_get_stop_reason (results) != MC_INTERRUPTED);
918 if (verbosity > 0 || !success)
922 printf ("Parameters: --max-rows=%d --max-columns=%d --backing-rows=%d ",
923 params.max_rows, params.max_cols, params.backing_rows);
925 printf ("--backing-widths=");
926 for (i = 0; i < params.n_backing_cols; i++)
930 printf ("%d", params.backing_widths[i]);
934 printf ("--widths=");
935 for (i = 0; i < params.n_widths; i++)
939 printf ("%d", params.widths[i]);
942 mc_results_print (results, stdout);
944 mc_results_destroy (results);
946 return success ? 0 : EXIT_FAILURE;