X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fdatasheet.c;h=da85d963c17dfb166ff93ab1e7882f1ee8105d42;hb=a94d5054363ca725a6810780b3621f064956929a;hp=63db4f36d38356293cd93d60b8e40db7408f3519;hpb=c532b2e8445401a2613391cda5ab485f9055b484;p=pspp-builds.git diff --git a/src/data/datasheet.c b/src/data/datasheet.c index 63db4f36..da85d963 100644 --- a/src/data/datasheet.c +++ b/src/data/datasheet.c @@ -1,20 +1,18 @@ -/* PSPP - computes sample statistics. +/* PSPP - a program for statistical analysis. Copyright (C) 2007 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include @@ -26,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -150,8 +149,7 @@ static bool rw_case (struct datasheet *ds, enum rw_op op, /* Creates and returns a new datasheet. If READER is nonnull, then the datasheet initially contains - the contents of READER. READER become owned by the datasheet - and the caller must not directly reference it again. */ + the contents of READER. */ struct datasheet * datasheet_create (struct casereader *reader) { @@ -357,6 +355,8 @@ datasheet_delete_columns (struct datasheet *ds, size_t start, size_t cnt) { size_t lcol; + assert ( start + cnt <= axis_get_size (ds->columns) ); + /* Free up columns for reuse. */ for (lcol = start; lcol < start + cnt; lcol++) { @@ -429,6 +429,7 @@ bool datasheet_get_value (const struct datasheet *ds, casenumber row, size_t column, union value *value, int width) { + assert ( row >= 0 ); return rw_case ((struct datasheet *) ds, OP_READ, row, column, value_cnt_from_width (width), value); } @@ -1258,6 +1259,95 @@ hash_datasheet (const struct datasheet *ds) return hash[0]; } +/* Clones the structure and contents of ODS into a new datasheet, + and returns the new datasheet. */ +static struct datasheet * +clone_datasheet (const struct datasheet *ods) +{ + struct datasheet *ds; + struct range_map_node *r; + + ds = xmalloc (sizeof *ds); + ds->columns = axis_clone (ods->columns); + ds->rows = axis_clone (ods->rows); + range_map_init (&ds->sources); + for (r = range_map_first (&ods->sources); r != NULL; + r = range_map_next (&ods->sources, r)) + { + const struct source_info *osi = source_info_from_range_map (r); + struct source_info *si = xmalloc (sizeof *si); + si->source = source_clone (osi->source); + range_map_insert (&ds->sources, range_map_node_get_start (r), + range_map_node_get_width (r), &si->column_range); + } + ds->column_min_alloc = ods->column_min_alloc; + ds->taint = taint_create (); + + return ds; +} + +/* lazy_casereader callback function to instantiate a casereader + from the datasheet. */ +static struct casereader * +lazy_callback (void *ds_) +{ + struct datasheet *ds = ds_; + return datasheet_make_reader (ds); +} + +/* Checks that READER contains the ROW_CNT rows and COLUMN_CNT + columns of data in ARRAY, reporting any errors via MC. */ +static void +check_datasheet_casereader (struct mc *mc, struct casereader *reader, + double array[MAX_ROWS][MAX_COLS], + size_t row_cnt, size_t column_cnt) +{ + if (casereader_get_case_cnt (reader) != row_cnt) + { + if (casereader_get_case_cnt (reader) == CASENUMBER_MAX + && casereader_count_cases (reader) == row_cnt) + mc_error (mc, "datasheet casereader has unknown case count"); + else + mc_error (mc, "casereader row count (%lu) does not match " + "expected (%zu)", + (unsigned long int) casereader_get_case_cnt (reader), + row_cnt); + } + else if (casereader_get_value_cnt (reader) != column_cnt) + mc_error (mc, "casereader column count (%zu) does not match " + "expected (%zu)", + casereader_get_value_cnt (reader), column_cnt); + else + { + struct ccase c; + size_t row; + + for (row = 0; row < row_cnt; row++) + { + size_t col; + + if (!casereader_read (reader, &c)) + { + mc_error (mc, "casereader_read failed reading row %zu of %zu " + "(%zu columns)", row, row_cnt, column_cnt); + return; + } + + for (col = 0; col < column_cnt; col++) + if (case_num_idx (&c, col) != array[row][col]) + mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: " + "%g != %g", + row, col, row_cnt, column_cnt, + case_num_idx (&c, col), array[row][col]); + + case_destroy (&c); + } + + if (casereader_read (reader, &c)) + mc_error (mc, "casereader has extra cases (expected %zu)", row_cnt); + } +} + /* Checks that datasheet DS contains has ROW_CNT rows, COLUMN_CNT columns, and the same contents as ARRAY, reporting any mismatches via mc_error. Then, adds DS to MC as a new state. */ @@ -1266,6 +1356,10 @@ check_datasheet (struct mc *mc, struct datasheet *ds, double array[MAX_ROWS][MAX_COLS], size_t row_cnt, size_t column_cnt) { + struct datasheet *ds2; + struct casereader *reader; + unsigned long int serial = 0; + assert (row_cnt < MAX_ROWS); assert (column_cnt < MAX_COLS); @@ -1277,12 +1371,13 @@ check_datasheet (struct mc *mc, struct datasheet *ds, return; } + /* Check contents of datasheet via datasheet functions. */ if (row_cnt != datasheet_get_row_cnt (ds)) mc_error (mc, "row count (%lu) does not match expected (%zu)", (unsigned long int) datasheet_get_row_cnt (ds), row_cnt); else if (column_cnt != datasheet_get_column_cnt (ds)) - mc_error (mc, "column count (%lu) does not match expected (%zu)", - (unsigned long int) datasheet_get_column_cnt (ds), column_cnt); + mc_error (mc, "column count (%zu) does not match expected (%zu)", + datasheet_get_column_cnt (ds), column_cnt); else { size_t row, col; @@ -1299,6 +1394,43 @@ check_datasheet (struct mc *mc, struct datasheet *ds, } } + /* Check that datasheet contents are correct when read through + casereader. */ + ds2 = clone_datasheet (ds); + reader = datasheet_make_reader (ds2); + check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt); + casereader_destroy (reader); + + /* Check that datasheet contents are correct when read through + casereader with lazy_casereader wrapped around it. This is + valuable because otherwise there is no non-GUI code that + uses the lazy_casereader. */ + ds2 = clone_datasheet (ds); + reader = lazy_casereader_create (column_cnt, row_cnt, + lazy_callback, ds2, &serial); + check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt); + if (lazy_casereader_destroy (reader, serial)) + { + /* Lazy casereader was never instantiated. This will + only happen if there are no rows (because in that case + casereader_read never gets called). */ + datasheet_destroy (ds2); + if (row_cnt != 0) + mc_error (mc, "lazy casereader not instantiated, but should " + "have been (size %zu,%zu)", row_cnt, column_cnt); + } + else + { + /* Lazy casereader was instantiated. This is the common + case, in which some casereader operation + (casereader_read in this case) was performed on the + lazy casereader. */ + casereader_destroy (reader); + if (row_cnt == 0) + mc_error (mc, "lazy casereader instantiated, but should not " + "have been (size %zu,%zu)", row_cnt, column_cnt); + } + mc_add_state (mc, ds); } @@ -1326,29 +1458,9 @@ extract_data (const struct datasheet *ds, double data[MAX_ROWS][MAX_COLS]) and the contents of ODATA into DATA. */ static void clone_model (const struct datasheet *ods, double odata[MAX_ROWS][MAX_COLS], - struct datasheet **ds_, double data[MAX_ROWS][MAX_COLS]) + struct datasheet **ds, double data[MAX_ROWS][MAX_COLS]) { - struct datasheet *ds; - struct range_map_node *r; - - /* Clone ODS into DS. */ - ds = *ds_ = xmalloc (sizeof *ds); - ds->columns = axis_clone (ods->columns); - ds->rows = axis_clone (ods->rows); - range_map_init (&ds->sources); - for (r = range_map_first (&ods->sources); r != NULL; - r = range_map_next (&ods->sources, r)) - { - const struct source_info *osi = source_info_from_range_map (r); - struct source_info *si = xmalloc (sizeof *si); - si->source = source_clone (osi->source); - range_map_insert (&ds->sources, range_map_node_get_start (r), - range_map_node_get_width (r), &si->column_range); - } - ds->column_min_alloc = ods->column_min_alloc; - ds->taint = taint_create (); - - /* Clone ODATA into DATA. */ + *ds = clone_datasheet (ods); memcpy (data, odata, MAX_ROWS * MAX_COLS * sizeof **data); }