1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007 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>
20 #include "datasheet-check.h"
21 #include "model-checker.h"
26 #include <data/casereader-provider.h>
27 #include <data/casereader.h>
28 #include <data/casewriter.h>
29 #include <data/lazy-casereader.h>
30 #include <data/sparse-cases.h>
31 #include <libpspp/array.h>
32 #include <libpspp/assertion.h>
33 #include <libpspp/range-map.h>
34 #include <libpspp/range-set.h>
35 #include <libpspp/taint.h>
36 #include <libpspp/tower.h>
43 /* lazy_casereader callback function to instantiate a casereader
44 from the datasheet. */
45 static struct casereader *
46 lazy_callback (void *ds_)
48 struct datasheet *ds = ds_;
49 return datasheet_make_reader (ds);
53 /* Maximum size of datasheet supported for model checking
59 /* Checks that READER contains the ROW_CNT rows and COLUMN_CNT
60 columns of data in ARRAY, reporting any errors via MC. */
62 check_datasheet_casereader (struct mc *mc, struct casereader *reader,
63 double array[MAX_ROWS][MAX_COLS],
64 size_t row_cnt, size_t column_cnt)
66 if (casereader_get_case_cnt (reader) != row_cnt)
68 if (casereader_get_case_cnt (reader) == CASENUMBER_MAX
69 && casereader_count_cases (reader) == row_cnt)
70 mc_error (mc, "datasheet casereader has unknown case count");
72 mc_error (mc, "casereader row count (%lu) does not match "
74 (unsigned long int) casereader_get_case_cnt (reader),
77 else if (casereader_get_value_cnt (reader) != column_cnt)
78 mc_error (mc, "casereader column count (%zu) does not match "
80 casereader_get_value_cnt (reader), column_cnt);
86 for (row = 0; row < row_cnt; row++)
90 if (!casereader_read (reader, &c))
92 mc_error (mc, "casereader_read failed reading row %zu of %zu "
93 "(%zu columns)", row, row_cnt, column_cnt);
97 for (col = 0; col < column_cnt; col++)
98 if (case_num_idx (&c, col) != array[row][col])
99 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
101 row, col, row_cnt, column_cnt,
102 case_num_idx (&c, col), array[row][col]);
107 if (casereader_read (reader, &c))
108 mc_error (mc, "casereader has extra cases (expected %zu)", row_cnt);
112 /* Checks that datasheet DS contains has ROW_CNT rows, COLUMN_CNT
113 columns, and the same contents as ARRAY, reporting any
114 mismatches via mc_error. Then, adds DS to MC as a new state. */
116 check_datasheet (struct mc *mc, struct datasheet *ds,
117 double array[MAX_ROWS][MAX_COLS],
118 size_t row_cnt, size_t column_cnt)
120 struct datasheet *ds2;
121 struct casereader *reader;
122 unsigned long int serial = 0;
124 assert (row_cnt < MAX_ROWS);
125 assert (column_cnt < MAX_COLS);
127 /* If it is a duplicate hash, discard the state before checking
128 its consistency, to save time. */
129 if (mc_discard_dup_state (mc, hash_datasheet (ds)))
131 datasheet_destroy (ds);
135 /* Check contents of datasheet via datasheet functions. */
136 if (row_cnt != datasheet_get_row_cnt (ds))
137 mc_error (mc, "row count (%lu) does not match expected (%zu)",
138 (unsigned long int) datasheet_get_row_cnt (ds), row_cnt);
139 else if (column_cnt != datasheet_get_column_cnt (ds))
140 mc_error (mc, "column count (%zu) does not match expected (%zu)",
141 datasheet_get_column_cnt (ds), column_cnt);
146 for (row = 0; row < row_cnt; row++)
147 for (col = 0; col < column_cnt; col++)
150 if (!datasheet_get_value (ds, row, col, &v, 1))
152 if (v.f != array[row][col])
153 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: %g != %g",
154 row, col, row_cnt, column_cnt, v.f, array[row][col]);
158 /* Check that datasheet contents are correct when read through
160 ds2 = clone_datasheet (ds);
161 reader = datasheet_make_reader (ds2);
162 check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt);
163 casereader_destroy (reader);
165 /* Check that datasheet contents are correct when read through
166 casereader with lazy_casereader wrapped around it. This is
167 valuable because otherwise there is no non-GUI code that
168 uses the lazy_casereader. */
169 ds2 = clone_datasheet (ds);
170 reader = lazy_casereader_create (column_cnt, row_cnt,
171 lazy_callback, ds2, &serial);
172 check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt);
173 if (lazy_casereader_destroy (reader, serial))
175 /* Lazy casereader was never instantiated. This will
176 only happen if there are no rows (because in that case
177 casereader_read never gets called). */
178 datasheet_destroy (ds2);
180 mc_error (mc, "lazy casereader not instantiated, but should "
181 "have been (size %zu,%zu)", row_cnt, column_cnt);
185 /* Lazy casereader was instantiated. This is the common
186 case, in which some casereader operation
187 (casereader_read in this case) was performed on the
189 casereader_destroy (reader);
191 mc_error (mc, "lazy casereader instantiated, but should not "
192 "have been (size %zu,%zu)", row_cnt, column_cnt);
195 mc_add_state (mc, ds);
198 /* Extracts the contents of DS into DATA. */
200 extract_data (const struct datasheet *ds, double data[MAX_ROWS][MAX_COLS])
202 size_t column_cnt = datasheet_get_column_cnt (ds);
203 size_t row_cnt = datasheet_get_row_cnt (ds);
206 assert (row_cnt < MAX_ROWS);
207 assert (column_cnt < MAX_COLS);
208 for (row = 0; row < row_cnt; row++)
209 for (col = 0; col < column_cnt; col++)
212 if (!datasheet_get_value (ds, row, col, &v, 1))
214 data[row][col] = v.f;
218 /* Clones the structure and contents of ODS into *DS,
219 and the contents of ODATA into DATA. */
221 clone_model (const struct datasheet *ods, double odata[MAX_ROWS][MAX_COLS],
222 struct datasheet **ds, double data[MAX_ROWS][MAX_COLS])
224 *ds = clone_datasheet (ods);
225 memcpy (data, odata, MAX_ROWS * MAX_COLS * sizeof **data);
228 /* "init" function for struct mc_class. */
230 datasheet_mc_init (struct mc *mc)
232 struct datasheet_test_params *params = mc_get_aux (mc);
233 struct datasheet *ds;
235 if (params->backing_rows == 0 && params->backing_cols == 0)
237 /* Create unbacked datasheet. */
238 ds = datasheet_create (NULL);
239 mc_name_operation (mc, "empty datasheet");
240 check_datasheet (mc, ds, NULL, 0, 0);
244 /* Create datasheet with backing. */
245 struct casewriter *writer;
246 struct casereader *reader;
247 double data[MAX_ROWS][MAX_COLS];
250 assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
251 assert (params->backing_cols > 0 && params->backing_cols <= MAX_COLS);
253 writer = mem_writer_create (params->backing_cols);
254 for (row = 0; row < params->backing_rows; row++)
259 case_create (&c, params->backing_cols);
260 for (col = 0; col < params->backing_cols; col++)
262 double value = params->next_value++;
263 data[row][col] = value;
264 case_data_rw_idx (&c, col)->f = value;
266 casewriter_write (writer, &c);
268 reader = casewriter_make_reader (writer);
269 assert (reader != NULL);
271 ds = datasheet_create (reader);
272 mc_name_operation (mc, "datasheet with (%d,%d) backing",
273 params->backing_rows, params->backing_cols);
274 check_datasheet (mc, ds, data,
275 params->backing_rows, params->backing_cols);
279 /* "mutate" function for struct mc_class. */
281 datasheet_mc_mutate (struct mc *mc, const void *ods_)
283 struct datasheet_test_params *params = mc_get_aux (mc);
285 const struct datasheet *ods = ods_;
286 double odata[MAX_ROWS][MAX_COLS];
287 double data[MAX_ROWS][MAX_COLS];
288 size_t column_cnt = datasheet_get_column_cnt (ods);
289 size_t row_cnt = datasheet_get_row_cnt (ods);
290 size_t pos, new_pos, cnt;
292 extract_data (ods, odata);
294 /* Insert all possible numbers of columns in all possible
296 for (pos = 0; pos <= column_cnt; pos++)
297 for (cnt = 0; cnt <= params->max_cols - column_cnt; cnt++)
298 if (mc_include_state (mc))
300 struct datasheet *ds;
301 union value new[MAX_COLS];
304 mc_name_operation (mc, "insert %zu columns at %zu", cnt, pos);
305 clone_model (ods, odata, &ds, data);
307 for (i = 0; i < cnt; i++)
308 new[i].f = params->next_value++;
310 if (!datasheet_insert_columns (ds, new, cnt, pos))
311 mc_error (mc, "datasheet_insert_columns failed");
313 for (i = 0; i < row_cnt; i++)
315 insert_range (&data[i][0], column_cnt, sizeof data[i][0],
317 for (j = 0; j < cnt; j++)
318 data[i][pos + j] = new[j].f;
321 check_datasheet (mc, ds, data, row_cnt, column_cnt + cnt);
324 /* Delete all possible numbers of columns from all possible
326 for (pos = 0; pos < column_cnt; pos++)
327 for (cnt = 0; cnt < column_cnt - pos; cnt++)
328 if (mc_include_state (mc))
330 struct datasheet *ds;
333 mc_name_operation (mc, "delete %zu columns at %zu", cnt, pos);
334 clone_model (ods, odata, &ds, data);
336 datasheet_delete_columns (ds, pos, cnt);
338 for (i = 0; i < row_cnt; i++)
339 remove_range (&data[i], column_cnt, sizeof *data[i], pos, cnt);
341 check_datasheet (mc, ds, data, row_cnt, column_cnt - cnt);
344 /* Move all possible numbers of columns from all possible
345 existing positions to all possible new positions. */
346 for (pos = 0; pos < column_cnt; pos++)
347 for (cnt = 0; cnt < column_cnt - pos; cnt++)
348 for (new_pos = 0; new_pos < column_cnt - cnt; new_pos++)
349 if (mc_include_state (mc))
351 struct datasheet *ds;
354 clone_model (ods, odata, &ds, data);
355 mc_name_operation (mc, "move %zu columns from %zu to %zu",
358 datasheet_move_columns (ds, pos, new_pos, cnt);
360 for (i = 0; i < row_cnt; i++)
361 move_range (&data[i], column_cnt, sizeof data[i][0],
364 check_datasheet (mc, ds, data, row_cnt, column_cnt);
367 /* Insert all possible numbers of rows in all possible
369 for (pos = 0; pos <= row_cnt; pos++)
370 for (cnt = 0; cnt <= params->max_rows - row_cnt; cnt++)
371 if (mc_include_state (mc))
373 struct datasheet *ds;
374 struct ccase c[MAX_ROWS];
377 clone_model (ods, odata, &ds, data);
378 mc_name_operation (mc, "insert %zu rows at %zu", cnt, pos);
380 for (i = 0; i < cnt; i++)
382 case_create (&c[i], column_cnt);
383 for (j = 0; j < column_cnt; j++)
384 case_data_rw_idx (&c[i], j)->f = params->next_value++;
387 insert_range (data, row_cnt, sizeof data[pos], pos, cnt);
388 for (i = 0; i < cnt; i++)
389 for (j = 0; j < column_cnt; j++)
390 data[i + pos][j] = case_num_idx (&c[i], j);
392 if (!datasheet_insert_rows (ds, pos, c, cnt))
393 mc_error (mc, "datasheet_insert_rows failed");
395 check_datasheet (mc, ds, data, row_cnt + cnt, column_cnt);
398 /* Delete all possible numbers of rows from all possible
400 for (pos = 0; pos < row_cnt; pos++)
401 for (cnt = 0; cnt < row_cnt - pos; cnt++)
402 if (mc_include_state (mc))
404 struct datasheet *ds;
406 clone_model (ods, odata, &ds, data);
407 mc_name_operation (mc, "delete %zu rows at %zu", cnt, pos);
409 datasheet_delete_rows (ds, pos, cnt);
411 remove_range (&data[0], row_cnt, sizeof data[0], pos, cnt);
413 check_datasheet (mc, ds, data, row_cnt - cnt, column_cnt);
416 /* Move all possible numbers of rows from all possible existing
417 positions to all possible new positions. */
418 for (pos = 0; pos < row_cnt; pos++)
419 for (cnt = 0; cnt < row_cnt - pos; cnt++)
420 for (new_pos = 0; new_pos < row_cnt - cnt; new_pos++)
421 if (mc_include_state (mc))
423 struct datasheet *ds;
425 clone_model (ods, odata, &ds, data);
426 mc_name_operation (mc, "move %zu rows from %zu to %zu",
429 datasheet_move_rows (ds, pos, new_pos, cnt);
431 move_range (&data[0], row_cnt, sizeof data[0],
434 check_datasheet (mc, ds, data, row_cnt, column_cnt);
438 /* "destroy" function for struct mc_class. */
440 datasheet_mc_destroy (const struct mc *mc UNUSED, void *ds_)
442 struct datasheet *ds = ds_;
443 datasheet_destroy (ds);
446 /* Executes the model checker on the datasheet test driver with
447 the given OPTIONS and passing in the given PARAMS, which must
448 point to a modifiable "struct datasheet_test_params". If any
449 value in PARAMS is out of range, it will be adjusted into the
450 valid range before running the test.
452 Returns the results of the model checking run. */
454 datasheet_test (struct mc_options *options, void *params_)
456 struct datasheet_test_params *params = params_;
457 static const struct mc_class datasheet_mc_class =
461 datasheet_mc_destroy,
464 params->next_value = 1;
465 params->max_rows = MIN (params->max_rows, MAX_ROWS);
466 params->max_cols = MIN (params->max_cols, MAX_COLS);
467 params->backing_rows = MIN (params->backing_rows, params->max_rows);
468 params->backing_cols = MIN (params->backing_cols, params->max_cols);
470 mc_options_set_aux (options, params);
471 return mc_run (&datasheet_mc_class, options);