[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Pspp-cvs] pspp Smake src/data/automake.mk src/data/case-t... [simpler-p
From: |
Ben Pfaff |
Subject: |
[Pspp-cvs] pspp Smake src/data/automake.mk src/data/case-t... [simpler-proc] |
Date: |
Sat, 14 Apr 2007 05:04:24 +0000 |
CVSROOT: /cvsroot/pspp
Module name: pspp
Branch: simpler-proc
Changes by: Ben Pfaff <blp> 07/04/14 05:04:23
Modified files:
. : Smake
src/data : automake.mk case-tmpfile.c case-tmpfile.h
case.c case.h casereader.c casereader.h
casewindow.c casewriter.c casewriter.h
datasheet.c datasheet.h
src/language : command.def
src/language/tests: automake.mk
src/libpspp : array.c array.h automake.mk range-set.c
range-set.h tower.c tower.h
src/ui/gui : automake.mk psppire-case-file.c
psppire-data-store.c psppire.c
Added files:
src/data : sparse-cases.c sparse-cases.h
src/language/tests: check-model.h check-model.q datasheet-test.c
src/libpspp : model-checker.c model-checker.h
Log message:
Make significant progress:
* Finish up and test the datasheet implementation.
* As part of that, implement a general-purpose model checker
that should be useful for testing other code.
* Enable compiling the GUI.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/pspp/Smake?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.49.2.2&r2=1.49.2.3
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/automake.mk?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.15.2.1&r2=1.15.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/case-tmpfile.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/case-tmpfile.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/case.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.10.2.1&r2=1.10.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/case.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.9.2.1&r2=1.9.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/casereader.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/casereader.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/casewindow.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.2&r2=1.1.2.3
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/casewriter.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/casewriter.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/datasheet.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.3&r2=1.1.2.4
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/datasheet.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/sparse-cases.c?cvsroot=pspp&only_with_tag=simpler-proc&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/data/sparse-cases.h?cvsroot=pspp&only_with_tag=simpler-proc&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/command.def?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.14.2.1&r2=1.14.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/tests/automake.mk?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.4.2.1&r2=1.4.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/tests/check-model.h?cvsroot=pspp&only_with_tag=simpler-proc&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/tests/check-model.q?cvsroot=pspp&only_with_tag=simpler-proc&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/language/tests/datasheet-test.c?cvsroot=pspp&only_with_tag=simpler-proc&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/array.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.10.2.1&r2=1.10.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/array.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.6&r2=1.6.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/automake.mk?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.22.2.7&r2=1.22.2.8
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/range-set.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.2&r2=1.1.2.3
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/range-set.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.3&r2=1.1.2.4
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/tower.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/tower.h?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.1.2.1&r2=1.1.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/model-checker.c?cvsroot=pspp&only_with_tag=simpler-proc&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/libpspp/model-checker.h?cvsroot=pspp&only_with_tag=simpler-proc&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/automake.mk?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.21.2.2&r2=1.21.2.3
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/psppire-case-file.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.17.2.1&r2=1.17.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/psppire-data-store.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.33.2.1&r2=1.33.2.2
http://cvs.savannah.gnu.org/viewcvs/pspp/src/ui/gui/psppire.c?cvsroot=pspp&only_with_tag=simpler-proc&r1=1.35.2.1&r2=1.35.2.2
Patches:
Index: Smake
===================================================================
RCS file: /cvsroot/pspp/pspp/Smake,v
retrieving revision 1.49.2.2
retrieving revision 1.49.2.3
diff -u -b -r1.49.2.2 -r1.49.2.3
--- Smake 20 Mar 2007 00:08:49 -0000 1.49.2.2
+++ Smake 14 Apr 2007 05:04:23 -0000 1.49.2.3
@@ -11,15 +11,18 @@
byteswap \
c-ctype \
c-strtod \
+ crypto/md4 \
dirname \
exit \
full-read \
full-write \
+ fwriteerror \
gethostname \
getline \
getlogin_r \
getopt \
gettext-h \
+ gettimeofday \
intprops \
inttostr \
linebreak \
Index: src/data/automake.mk
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/automake.mk,v
retrieving revision 1.15.2.1
retrieving revision 1.15.2.2
diff -u -b -r1.15.2.1 -r1.15.2.2
--- src/data/automake.mk 19 Mar 2007 21:36:24 -0000 1.15.2.1
+++ src/data/automake.mk 14 Apr 2007 05:04:23 -0000 1.15.2.2
@@ -65,6 +65,8 @@
src/data/scratch-writer.h \
src/data/settings.c \
src/data/settings.h \
+ src/data/sparse-cases.c \
+ src/data/sparse-cases.h \
src/data/sys-file-private.c \
src/data/sys-file-private.h \
src/data/sys-file-reader.c \
Index: src/data/case-tmpfile.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/case-tmpfile.c,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/data/case-tmpfile.c 19 Mar 2007 21:36:24 -0000 1.1.2.1
+++ src/data/case-tmpfile.c 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -79,8 +79,9 @@
#endif
static void
-mark_error (struct case_tmpfile *ctf)
+mark_error (const struct case_tmpfile *ctf_)
{
+ struct case_tmpfile *ctf = (struct case_tmpfile *) ctf_;
if (ctf->file != NULL)
{
fclose (ctf->file);
@@ -89,12 +90,12 @@
}
static bool
-seek_to_case (struct case_tmpfile *ctf, casenumber case_idx)
+do_seek (const struct case_tmpfile *ctf,
+ casenumber case_idx, size_t value_idx)
{
- if (ctf->file != NULL
- && fseeko (ctf->file,
- (off_t) sizeof (union value) * ctf->value_cnt * case_idx,
- SEEK_SET) == 0)
+ off_t value_ofs = value_idx + (off_t) ctf->value_cnt * case_idx;
+ off_t byte_ofs = sizeof (union value) * value_ofs;
+ if (ctf->file != NULL && fseeko (ctf->file, byte_ofs, SEEK_SET) == 0)
return true;
if (!case_tmpfile_error (ctf))
@@ -106,17 +107,17 @@
}
bool
-case_tmpfile_get (const struct case_tmpfile *ctf_, casenumber index,
- struct ccase *c)
+case_tmpfile_get_values (const struct case_tmpfile *ctf,
+ casenumber case_idx, size_t start_value,
+ union value values[], size_t value_cnt)
{
- struct case_tmpfile *ctf = (struct case_tmpfile *) ctf_;
- if (seek_to_case (ctf, index))
- {
- case_create (c, ctf->value_cnt);
- if (fread (case_data_all_rw (c), sizeof (union value) * ctf->value_cnt,
- 1, ctf->file) != 1)
+ assert (value_cnt < ctf->value_cnt);
+ assert (value_cnt + start_value <= ctf->value_cnt);
+
+ if (!do_seek (ctf, case_idx, start_value))
+ return false;
+ if (fread (values, sizeof (union value) * value_cnt, 1, ctf->file) != 1)
{
- case_destroy (c);
mark_error (ctf);
if (ferror (ctf->file))
error (0, errno, _("reading temporary file"));
@@ -124,29 +125,51 @@
error (0, 0, _("unexpected end of file reading temporary file"));
else
NOT_REACHED ();
+ return false;
}
- }
+ return true;
+}
- if (!case_tmpfile_error (ctf))
+bool
+case_tmpfile_get_case (const struct case_tmpfile *ctf, casenumber case_idx,
+ struct ccase *c)
+{
+ case_create (c, ctf->value_cnt);
+ if (case_tmpfile_get_values (ctf, case_idx, 0,
+ case_data_all_rw (c), ctf->value_cnt))
return true;
else
{
+ case_destroy (c);
case_nullify (c);
return false;
}
}
bool
-case_tmpfile_put (struct case_tmpfile *ctf, casenumber index,
- struct ccase *c)
+case_tmpfile_put_values (struct case_tmpfile *ctf,
+ casenumber case_idx, size_t start_value,
+ const union value values[], size_t value_cnt)
+
{
- if (seek_to_case (ctf, index)
- && fwrite (case_data_all (c), sizeof (union value) * ctf->value_cnt,
- 1, ctf->file) != 1)
+ if (!do_seek (ctf, case_idx, start_value))
+ return false;
+ if (fwrite (values, sizeof (union value) * value_cnt, 1, ctf->file) != 1)
{
mark_error (ctf);
error (0, errno, _("writing to temporary file"));
+ return false;
}
+ return true;
+}
+
+bool
+case_tmpfile_put_case (struct case_tmpfile *ctf, casenumber case_idx,
+ struct ccase *c)
+{
+ bool ok = case_tmpfile_put_values (ctf, case_idx, 0,
+ case_data_all (c), ctf->value_cnt);
case_destroy (c);
- return !case_tmpfile_error (ctf);
+ return ok;
}
+
Index: src/data/case-tmpfile.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/case-tmpfile.h,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/data/case-tmpfile.h 19 Mar 2007 21:36:24 -0000 1.1.2.1
+++ src/data/case-tmpfile.h 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -25,9 +25,16 @@
bool case_tmpfile_destroy (struct case_tmpfile *);
bool case_tmpfile_error (const struct case_tmpfile *);
-bool case_tmpfile_get (const struct case_tmpfile *, casenumber index,
- struct ccase *);
-bool case_tmpfile_put (struct case_tmpfile *, casenumber index,
- struct ccase *);
+bool case_tmpfile_get_values (const struct case_tmpfile *,
+ casenumber, size_t start_value,
+ union value[], size_t value_cnt);
+bool case_tmpfile_get_case (const struct case_tmpfile *,
+ casenumber, struct ccase *);
+
+bool case_tmpfile_put_values (struct case_tmpfile *,
+ casenumber, size_t start_value,
+ const union value[], size_t value_cnt);
+bool case_tmpfile_put_case (struct case_tmpfile *,
+ casenumber, struct ccase *);
#endif /* data/case-tmpfile.h */
Index: src/data/case.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/case.c,v
retrieving revision 1.10.2.1
retrieving revision 1.10.2.2
diff -u -b -r1.10.2.1 -r1.10.2.2
--- src/data/case.c 19 Mar 2007 21:36:24 -0000 1.10.2.1
+++ src/data/case.c 14 Apr 2007 05:04:23 -0000 1.10.2.2
@@ -95,9 +95,9 @@
if (clone != orig)
*clone = *orig;
orig->case_data->ref_cnt++;
-//#ifdef DEBUGGING
+#ifdef DEBUGGING
case_unshare (clone);
-//#endif
+#endif
}
/* Replaces DST by SRC and nullifies SRC.
@@ -210,35 +210,32 @@
}
}
-/* Copies case C to OUTPUT.
- OUTPUT_SIZE is the number of `union values' in OUTPUT,
- which must match the number of `union values' in C. */
+/* Copies VALUE_CNT values out of case C to VALUES, starting at
+ the given START_IDX. */
void
-case_to_values (const struct ccase *c, union value *output,
- size_t output_size UNUSED)
+case_copy_out (const struct ccase *c,
+ size_t start_idx, union value *values, size_t value_cnt)
{
assert (c->case_data->ref_cnt > 0);
- assert (output_size == c->case_data->value_cnt);
- assert (output != NULL || output_size == 0);
+ assert (value_cnt <= c->case_data->value_cnt);
+ assert (start_idx + value_cnt <= c->case_data->value_cnt);
- memcpy (output, c->case_data->values,
- c->case_data->value_cnt * sizeof *output);
+ memcpy (values, c->case_data->values + start_idx,
+ value_cnt * sizeof *values);
}
-/* Copies INPUT into case C.
- INPUT_SIZE is the number of `union values' in INPUT,
- which must match the number of `union values' in C. */
+/* Copies VALUE_CNT values from VALUES into case C, staring at
+ the given START_IDX. */
void
-case_from_values (struct ccase *c, const union value *input,
- size_t input_size UNUSED)
+case_copy_in (struct ccase *c,
+ size_t start_idx, const union value *values, size_t value_cnt)
{
assert (c->case_data->ref_cnt > 0);
- assert (input_size == c->case_data->value_cnt);
- assert (input != NULL || input_size == 0);
+ assert (value_cnt <= c->case_data->value_cnt);
+ assert (start_idx + value_cnt <= c->case_data->value_cnt);
- case_unshare (c);
- memcpy (c->case_data->values, input,
- c->case_data->value_cnt * sizeof *input);
+ memcpy (c->case_data->values + start_idx, values,
+ value_cnt * sizeof *values);
}
/* Returns a pointer to the `union value' used for the
Index: src/data/case.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/case.h,v
retrieving revision 1.9.2.1
retrieving revision 1.9.2.2
diff -u -b -r1.9.2.1 -r1.9.2.2
--- src/data/case.h 19 Mar 2007 21:36:24 -0000 1.9.2.1
+++ src/data/case.h 14 Apr 2007 05:04:23 -0000 1.9.2.2
@@ -60,8 +60,10 @@
const struct ccase *src, size_t src_idx,
size_t cnt);
-void case_to_values (const struct ccase *, union value *, size_t);
-void case_from_values (struct ccase *, const union value *, size_t);
+void case_copy_out (const struct ccase *,
+ size_t start_idx, union value *, size_t value_cnt);
+void case_copy_in (struct ccase *,
+ size_t start_idx, const union value *, size_t value_cnt);
const union value *case_data (const struct ccase *, const struct variable *);
double case_num (const struct ccase *, const struct variable *);
Index: src/data/casereader.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/casereader.c,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/data/casereader.c 19 Mar 2007 21:36:24 -0000 1.1.2.1
+++ src/data/casereader.c 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -166,6 +166,24 @@
return reader->case_cnt;
}
+casenumber
+casereader_count_cases (struct casereader *reader)
+{
+ casenumber case_cnt = casereader_get_case_cnt (reader);
+ if (case_cnt == CASENUMBER_INVALID)
+ {
+ struct casereader *clone;
+ struct ccase c;
+
+ case_cnt = 0;
+ clone = casereader_clone (reader);
+ for (; casereader_read (clone, &c); case_destroy (&c))
+ case_cnt++;
+ casereader_destroy (clone);
+ }
+ return case_cnt;
+}
+
size_t
casereader_get_value_cnt (struct casereader *reader)
{
Index: src/data/casereader.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/casereader.h,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/data/casereader.h 19 Mar 2007 21:36:24 -0000 1.1.2.1
+++ src/data/casereader.h 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -38,6 +38,7 @@
bool casereader_error (const struct casereader *);
casenumber casereader_get_case_cnt (struct casereader *);
+casenumber casereader_count_cases (struct casereader *);
size_t casereader_get_value_cnt (struct casereader *);
struct casereader *
Index: src/data/casewindow.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/casewindow.c,v
retrieving revision 1.1.2.2
retrieving revision 1.1.2.3
diff -u -b -r1.1.2.2 -r1.1.2.3
--- src/data/casewindow.c 28 Mar 2007 17:26:48 -0000 1.1.2.2
+++ src/data/casewindow.c 14 Apr 2007 05:04:23 -0000 1.1.2.3
@@ -263,7 +263,7 @@
casewindow_file_push_head (void *cwf_, struct ccase *c)
{
struct casewindow_file *cwf = cwf_;
- if (case_tmpfile_put (cwf->file, cwf->head, c))
+ if (case_tmpfile_put_case (cwf->file, cwf->head, c))
{
cwf->head++;
return true;
@@ -287,7 +287,7 @@
casewindow_file_get_case (void *cwf_, casenumber ofs, struct ccase *c)
{
struct casewindow_file *cwf = cwf_;
- return case_tmpfile_get (cwf->file, cwf->tail + ofs, c);
+ return case_tmpfile_get_case (cwf->file, cwf->tail + ofs, c);
}
static casenumber
Index: src/data/casewriter.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/casewriter.c,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/data/casewriter.c 19 Mar 2007 21:36:24 -0000 1.1.2.1
+++ src/data/casewriter.c 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -89,20 +89,10 @@
struct casereader *
casewriter_make_reader (struct casewriter *writer)
{
- return writer->class->convert_to_reader (writer, writer->private);
-}
-
-void
-casewriter_transfer (struct casewriter *dst, struct casewriter *src)
-{
- struct casereader *src_reader;
- struct ccase c;
-
- /* FIXME: error propagation? */
- src_reader = casewriter_make_reader (src);
- while (casereader_read (src_reader, &c))
- casewriter_write (dst, &c);
- casewriter_destroy (src);
+ struct casereader *reader;
+ reader = writer->class->convert_to_reader (writer, writer->private);
+ free (writer);
+ return reader;
}
static const struct casewriter_class casewriter_window_class;
Index: src/data/casewriter.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/casewriter.h,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/data/casewriter.h 19 Mar 2007 21:36:24 -0000 1.1.2.1
+++ src/data/casewriter.h 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -36,7 +36,6 @@
bool casewriter_error (const struct casewriter *);
struct casereader *casewriter_make_reader (struct casewriter *);
-void casewriter_transfer (struct casewriter *, struct casewriter *);
struct casewriter *
casewriter_create_translator (struct casewriter *,
Index: src/data/datasheet.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/datasheet.c,v
retrieving revision 1.1.2.3
retrieving revision 1.1.2.4
diff -u -b -r1.1.2.3 -r1.1.2.4
--- src/data/datasheet.c 1 Apr 2007 19:32:15 -0000 1.1.2.3
+++ src/data/datasheet.c 14 Apr 2007 05:04:23 -0000 1.1.2.4
@@ -21,17 +21,21 @@
#include <data/datasheet.h>
#include <stdlib.h>
+#include <string.h>
#include <data/buffered-reader.h>
#include <data/casereader.h>
-#include <data/case-tmpfile.h>
-#include <data/settings.h>
+#include <data/casewriter.h>
+#include <data/sparse-cases.h>
+#include <libpspp/array.h>
#include <libpspp/assertion.h>
+#include <libpspp/model-checker.h>
#include <libpspp/range-map.h>
#include <libpspp/range-set.h>
-#include <libpspp/sparse-array.h>
#include <libpspp/tower.h>
+#include "minmax.h"
+#include "md4.h"
#include "xalloc.h"
static struct axis *axis_create (void);
@@ -40,7 +44,11 @@
static bool axis_allocate (struct axis *, unsigned long int request,
unsigned long int *start,
unsigned long int *width);
-static void axis_extend (struct axis *, unsigned long int width);
+static void axis_make_available (struct axis *,
+ unsigned long int start,
+ unsigned long int width);
+static void axis_extend (struct axis *, unsigned long int width,
+ unsigned long int *start);
static unsigned long int axis_map (const struct axis *, unsigned long log_pos);
@@ -56,26 +64,23 @@
unsigned long int new_start,
unsigned long int cnt);
-enum rw_op
- {
- OP_READ,
- OP_WRITE
- };
-
static struct source *source_create_empty (size_t column_cnt);
-static struct source *source_create_from_casereader (struct casereader *,
- casenumber *row_cnt,
- size_t *column_cnt);
+static struct source *source_create_casereader (struct casereader *);
+static struct source *clone_source (const struct source *);
static void source_destroy (struct source *);
-static bool source_rw (struct source *, enum rw_op,
- casenumber row, size_t column,
- union value *, size_t value_cnt);
-static struct sparse_cases *sparse_cases_create (size_t column_cnt);
-static void sparse_cases_destroy (struct sparse_cases *);
-static bool sparse_cases_rw (struct sparse_cases *, enum rw_op,
+static bool source_read (const struct source *,
casenumber row, size_t column,
union value *, size_t value_cnt);
+static bool source_write (struct source *,
+ casenumber row, size_t column,
+ const union value *, size_t value_cnt);
+static bool source_write_columns (struct source *, size_t start_column,
+ const union value[], size_t value_cnt);
+static bool source_has_backing (const struct source *);
+static void source_increase_use (struct source *, size_t delta);
+static void source_decrease_use (struct source *, size_t delta);
+static bool source_in_use (const struct source *);
/* A datasheet is internally composed from a set of data files,
called "sources". The sources that make up a datasheet must
@@ -101,17 +106,17 @@
struct axis *rows;
struct range_map sources;
- /* Minimum number of columns to put in a new source we need
- new columns and none are free. By increasing it
- exponentially whenever we add a new source, we can keep
- the number of file descriptors needed by the datasheet to
- a minimum, reducing the likelihood of running out. */
+ /* Minimum number of columns to put in a new source when we
+ need new columns and none are free. We double it whenever
+ we add a new source to keep the number of file descriptors
+ needed by the datasheet to a minimum, reducing the
+ likelihood of running out. */
unsigned column_min_alloc;
};
struct source_info
{
- struct range_map_node range;
+ struct range_map_node column_range;
struct source *source;
};
@@ -119,33 +124,44 @@
datasheet_create (struct casereader *reader)
{
struct datasheet *ds;
- struct source_info *si;
- size_t column_cnt;
- casenumber row_cnt;
- ds = datasheet_create_empty ();
+ ds = xmalloc (sizeof *ds);
+ ds->columns = axis_create ();
+ ds->rows = axis_create ();
+ range_map_init (&ds->sources);
+ ds->column_min_alloc = 1;
+
+ if (reader != NULL)
+ {
+ size_t column_cnt = casereader_get_value_cnt (reader);
+ casenumber row_cnt = casereader_count_cases (reader);
+ unsigned long int column_start, row_start;
+ struct source_info *si;
/* Add source_info for READER. */
si = xmalloc (sizeof *si);
- si->source = source_create_from_casereader (reader, &row_cnt, &column_cnt);
- range_map_insert (&ds->sources, 0, column_cnt, &si->range);
+ si->source = source_create_casereader (reader);
+ source_increase_use (si->source, column_cnt);
+ range_map_insert (&ds->sources, 0, column_cnt, &si->column_range);
/* Add column and row mappings for READER. */
- axis_insert (ds->columns, 0, 0, column_cnt);
- axis_insert (ds->rows, 0, 0, row_cnt);
+ axis_extend (ds->columns, column_cnt, &column_start);
+ axis_insert (ds->columns, 0, column_start, column_cnt);
+ axis_extend (ds->rows, row_cnt, &row_start);
+ axis_insert (ds->rows, 0, row_start, row_cnt);
+
+ return ds;
+ }
return ds;
}
-struct datasheet *
-datasheet_create_empty (void)
+static void
+free_source_info (struct datasheet *ds, struct source_info *si)
{
- struct datasheet *ds = xmalloc (sizeof *ds);
- ds->columns = axis_create ();
- ds->rows = axis_create ();
- range_map_init (&ds->sources);
- ds->column_min_alloc = 1;
- return ds;
+ range_map_delete (&ds->sources, &si->column_range);
+ source_destroy (si->source);
+ free (si);
}
void
@@ -159,33 +175,30 @@
while (!range_map_is_empty (&ds->sources))
{
struct range_map_node *r = range_map_first (&ds->sources);
- struct source_info *si = range_map_data (r, struct source_info, range);
- range_map_delete (&ds->sources, r);
- source_destroy (si->source);
- free (si);
+ struct source_info *si
+ = range_map_data (r, struct source_info, column_range);
+ free_source_info (ds, si);
}
free (ds);
}
casenumber
-datasheet_get_case_cnt (const struct datasheet *ds)
+datasheet_get_row_cnt (const struct datasheet *ds)
{
return axis_get_size (ds->rows);
}
size_t
-datasheet_get_value_cnt (const struct datasheet *ds)
+datasheet_get_column_cnt (const struct datasheet *ds)
{
return axis_get_size (ds->columns);
}
void
-datasheet_insert_values (struct datasheet *ds,
+datasheet_insert_columns (struct datasheet *ds,
const union value init_values[], size_t cnt,
size_t before)
{
- casenumber row_cnt = datasheet_get_case_cnt (ds);
-
while (cnt > 0)
{
unsigned long first_phy;
@@ -195,37 +208,37 @@
{
struct source_info *si;
- axis_extend (ds->columns, MAX (cnt, ds->column_min_alloc));
- if (!axis_allocate (ds->columns, cnt, &first_phy, &phy_cnt))
- NOT_REACHED ();
- if (ds->column_min_alloc < 65536)
- ds->column_min_alloc *= 2;
+ phy_cnt = MAX (cnt, ds->column_min_alloc);
+ axis_extend (ds->columns, phy_cnt, &first_phy);
+ ds->column_min_alloc = MIN (65536, ds->column_min_alloc * 2);
si = xmalloc (sizeof *si);
si->source = source_create_empty (phy_cnt);
- range_map_insert (&ds->sources, first_phy, phy_cnt, &si->range);
+ range_map_insert (&ds->sources, first_phy, phy_cnt,
+ &si->column_range);
+ if (phy_cnt > cnt)
+ {
+ axis_make_available (ds->columns, first_phy + cnt,
+ phy_cnt - cnt);
+ phy_cnt = cnt;
+ }
}
while (phy_cnt > 0)
{
struct range_map_node *r;
struct source_info *s;
- size_t source_col_ofs;
size_t source_cnt;
- casenumber lrow;
r = range_map_lookup (&ds->sources, first_phy);
- s = range_map_data (r, struct source_info, range);
+ s = range_map_data (r, struct source_info, column_range);
source_cnt = MIN (phy_cnt, range_map_node_get_end (r) - first_phy);
axis_insert (ds->columns, before, first_phy, source_cnt);
- source_col_ofs = first_phy - range_map_node_get_start (r);
- for (lrow = 0; lrow < row_cnt; lrow++)
- {
- casenumber prow = axis_map (ds->rows, lrow);
- source_rw (s->source, true, prow, source_col_ofs,
- (union value *) init_values, source_cnt);
- }
+ source_write_columns (s->source,
+ first_phy - range_map_node_get_start (r),
+ init_values, source_cnt);
+ source_increase_use (s->source, source_cnt);
phy_cnt -= source_cnt;
first_phy += source_cnt;
@@ -237,19 +250,46 @@
}
void
-datasheet_delete_values (struct datasheet *ds, size_t start, size_t cnt)
+datasheet_delete_columns (struct datasheet *ds, size_t start, size_t cnt)
{
+ size_t lcol;
+
+ /* Free up columns for reuse. */
+ for (lcol = start; lcol < start + cnt; lcol++)
+ {
+ size_t pcol = axis_map (ds->columns, lcol);
+ struct range_map_node *r = range_map_lookup (&ds->sources, pcol);
+ struct source_info *si = range_map_data (r, struct source_info,
+ column_range);
+
+ source_decrease_use (si->source, 1);
+ if (source_has_backing (si->source))
+ {
+ if (!source_in_use (si->source))
+ free_source_info (ds, si);
+ }
+ else
+ axis_make_available (ds->columns, pcol, 1);
+ }
+
+ /* Remove columns from logical-to-physical mapping. */
axis_remove (ds->columns, start, cnt);
}
void
-datasheet_move_values (struct datasheet *ds,
+datasheet_move_columns (struct datasheet *ds,
size_t old_start, size_t new_start,
size_t cnt)
{
axis_move (ds->columns, old_start, new_start, cnt);
}
+enum rw_op
+ {
+ OP_READ,
+ OP_WRITE
+ };
+
static bool
rw_case (struct datasheet *ds, enum rw_op op,
casenumber lrow, size_t start_column, size_t column_cnt,
@@ -258,29 +298,37 @@
casenumber prow;
size_t lcol;
+ assert (lrow < datasheet_get_row_cnt (ds));
+ assert (column_cnt <= datasheet_get_column_cnt (ds));
+ assert (start_column + column_cnt <= datasheet_get_column_cnt (ds));
+
prow = axis_map (ds->rows, lrow);
- for (lcol = 0; lcol < column_cnt; lcol++)
+ for (lcol = start_column; lcol < start_column + column_cnt; lcol++)
{
size_t pcol;
struct range_map_node *r;
struct source_info *s;
- pcol = axis_map (ds->columns, lcol + start_column);
- r = range_map_lookup (&ds->sources, lcol + start_column);
- s = range_map_data (r, struct source_info, range);
- source_rw (s->source, op,
- prow, pcol - range_map_node_get_start (r),
- &data[lcol], 1);
+ pcol = axis_map (ds->columns, lcol);
+ r = range_map_lookup (&ds->sources, pcol);
+ s = range_map_data (r, struct source_info, column_range);
+ if (op == OP_READ)
+ source_read (s->source, prow, pcol - range_map_node_get_start (r),
+ data++, 1);
+ else
+ source_write (s->source, prow, pcol - range_map_node_get_start (r),
+ data++, 1);
}
return true;
}
bool
-datasheet_get_case (struct datasheet *ds, casenumber lrow, struct ccase *c)
+datasheet_get_row (const struct datasheet *ds, casenumber row, struct ccase *c)
{
- size_t column_cnt = datasheet_get_value_cnt (ds);
+ size_t column_cnt = datasheet_get_column_cnt (ds);
case_create (c, column_cnt);
- if (rw_case (ds, OP_READ, lrow, 0, column_cnt, case_data_all_rw (c)))
+ if (rw_case ((struct datasheet *) ds, OP_READ,
+ row, 0, column_cnt, case_data_all_rw (c)))
return true;
else
{
@@ -290,33 +338,33 @@
}
bool
-datasheet_put_case (struct datasheet *ds, casenumber lrow, struct ccase *c)
+datasheet_put_row (struct datasheet *ds, casenumber row, struct ccase *c)
{
- size_t column_cnt = datasheet_get_value_cnt (ds);
- bool ok = rw_case (ds, OP_WRITE, lrow, 0, column_cnt,
+ size_t column_cnt = datasheet_get_column_cnt (ds);
+ bool ok = rw_case (ds, OP_WRITE, row, 0, column_cnt,
(union value *) case_data_all (c));
case_destroy (c);
return ok;
}
bool
-datasheet_get_value (struct datasheet *ds, casenumber lrow, size_t column,
+datasheet_get_value (const struct datasheet *ds, casenumber row, size_t column,
union value *value, int width)
{
- return rw_case (ds, OP_READ, lrow, column, value_cnt_from_width (width),
- value);
+ return rw_case ((struct datasheet *) ds,
+ OP_READ, row, column, value_cnt_from_width (width), value);
}
bool
-datasheet_put_value (struct datasheet *ds, casenumber lrow, size_t column,
+datasheet_put_value (struct datasheet *ds, casenumber row, size_t column,
const union value *value, int width)
{
- return rw_case (ds, OP_WRITE, lrow, column, value_cnt_from_width (width),
+ return rw_case (ds, OP_WRITE, row, column, value_cnt_from_width (width),
(union value *) value);
}
bool
-datasheet_insert_cases (struct datasheet *ds,
+datasheet_insert_rows (struct datasheet *ds,
casenumber before, struct ccase *c,
casenumber cnt)
{
@@ -329,18 +377,17 @@
if (!axis_allocate (ds->rows, cnt, &first_phy, &phy_cnt))
{
- axis_extend (ds->rows, cnt);
- if (!axis_allocate (ds->rows, cnt, &first_phy, &phy_cnt))
- NOT_REACHED ();
+ phy_cnt = cnt;
+ axis_extend (ds->rows, cnt, &first_phy);
}
axis_insert (ds->rows, before, first_phy, phy_cnt);
for (i = 0; i < phy_cnt; i++)
- if (!datasheet_put_case (ds, before + i, &c[i]))
+ if (!datasheet_put_row (ds, before + i, &c[i]))
{
while (++i < cnt)
case_destroy (&c[i]);
- datasheet_delete_cases (ds, before - added, phy_cnt + added);
+ datasheet_delete_rows (ds, before - added, phy_cnt + added);
return false;
}
@@ -353,12 +400,28 @@
}
void
-datasheet_delete_cases (struct datasheet *ds,
+datasheet_delete_rows (struct datasheet *ds,
casenumber first, casenumber cnt)
{
+ size_t lrow;
+
+ /* Free up rows for reuse.
+ FIXME: optimize. */
+ for (lrow = first; lrow < first + cnt; lrow++)
+ axis_make_available (ds->rows, axis_map (ds->rows, lrow), 1);
+
+ /* Remove rows from logical-to-physical mapping. */
axis_remove (ds->rows, first, cnt);
}
+void
+datasheet_move_rows (struct datasheet *ds,
+ size_t old_start, size_t new_start,
+ size_t cnt)
+{
+ axis_move (ds->rows, old_start, new_start, cnt);
+}
+
static const struct buffered_reader_class datasheet_reader_class;
static struct datasheet *
@@ -373,8 +436,8 @@
datasheet_make_reader (struct datasheet *ds)
{
ds = datasheet_rename (ds);
- return buffered_reader_create (datasheet_get_value_cnt (ds),
- datasheet_get_case_cnt (ds),
+ return buffered_reader_create (datasheet_get_column_cnt (ds),
+ datasheet_get_row_cnt (ds),
&datasheet_reader_class, ds);
}
@@ -382,10 +445,10 @@
datasheet_reader_read (void *ds_, casenumber case_idx, struct ccase *c)
{
struct datasheet *ds = ds_;
- if (case_idx >= datasheet_get_case_cnt (ds))
+ if (case_idx >= datasheet_get_row_cnt (ds))
return false;
else
- return datasheet_get_case (ds, case_idx, c);
+ return datasheet_get_row (ds, case_idx, c);
}
static bool
@@ -407,7 +470,7 @@
datasheet_reader_advance (void *ds_, casenumber case_cnt)
{
struct datasheet *ds = ds_;
- datasheet_delete_cases (ds, 0, case_cnt);
+ datasheet_delete_rows (ds, 0, case_cnt);
}
static const struct buffered_reader_class datasheet_reader_class =
@@ -437,7 +500,9 @@
return tower_data (node, struct axis_group, logical);
}
-struct axis *
+static struct tower_node *make_axis_group (unsigned long int phy_start);
+
+static struct axis *
axis_create (void)
{
struct axis *axis = xmalloc (sizeof *axis);
@@ -447,7 +512,60 @@
return axis;
}
-void
+static struct axis *
+clone_axis (const struct axis *old)
+{
+ const struct tower_node *node;
+ struct axis *new;
+
+ new = xmalloc (sizeof *new);
+ tower_init (&new->log_to_phy);
+ new->available = range_set_clone (old->available, NULL);
+ new->phy_size = old->phy_size;
+
+ for (node = tower_first (&old->log_to_phy); node != NULL;
+ node = tower_next (&old->log_to_phy, node))
+ {
+ unsigned long int size = tower_node_get_height (node);
+ struct axis_group *group = tower_data (node, struct axis_group, logical);
+ tower_insert (&new->log_to_phy, size, make_axis_group (group->phy_start),
+ NULL);
+ }
+
+ return new;
+}
+
+static void
+hash_axis (const struct axis *axis, struct md4_ctx *ctx)
+{
+ const struct tower_node *tn;
+ const struct range_set_node *rsn;
+
+ for (tn = tower_first (&axis->log_to_phy); tn != NULL;
+ tn = tower_next (&axis->log_to_phy, tn))
+ {
+ struct axis_group *group = tower_data (tn, struct axis_group, logical);
+ unsigned long int phy_start = group->phy_start;
+ unsigned long int size = tower_node_get_height (tn);
+
+ md4_process_bytes (&phy_start, sizeof phy_start, ctx);
+ md4_process_bytes (&size, sizeof size, ctx);
+ }
+
+ for (rsn = range_set_first (axis->available); rsn != NULL;
+ rsn = range_set_next (axis->available, rsn))
+ {
+ unsigned long int start = range_set_node_get_start (rsn);
+ unsigned long int end = range_set_node_get_end (rsn);
+
+ md4_process_bytes (&start, sizeof start, ctx);
+ md4_process_bytes (&end, sizeof end, ctx);
+ }
+
+ md4_process_bytes (&axis->phy_size, sizeof axis->phy_size, ctx);
+}
+
+static void
axis_destroy (struct axis *axis)
{
if (axis == NULL)
@@ -466,7 +584,7 @@
free (axis);
}
-bool
+static bool
axis_allocate (struct axis *axis, unsigned long int request,
unsigned long int *start,
unsigned long int *width)
@@ -474,14 +592,22 @@
return range_set_allocate (axis->available, request, start, width);
}
-void
-axis_extend (struct axis *axis, unsigned long int width)
+static void
+axis_make_available (struct axis *axis,
+ unsigned long int start, unsigned long int width)
{
- range_set_insert (axis->available, axis->phy_size, width);
+ range_set_insert (axis->available, start, width);
+}
+
+static void
+axis_extend (struct axis *axis, unsigned long int width,
+ unsigned long int *start)
+{
+ *start = axis->phy_size;
axis->phy_size += width;
}
-unsigned long int
+static unsigned long int
axis_map (const struct axis *axis, unsigned long log_pos)
{
struct tower_node *node;
@@ -493,7 +619,7 @@
return group->phy_start + (log_pos - group_start);
}
-unsigned long
+static unsigned long
axis_get_size (const struct axis *axis)
{
return tower_height (&axis->log_to_phy);
@@ -514,10 +640,10 @@
struct tower_node *group_node;
struct axis_group *group;
- group_node = tower_lookup (&axis->log_to_phy, where, &group_start);
- if (group_node == NULL)
+ if (where >= tower_height (&axis->log_to_phy))
return NULL;
+ group_node = tower_lookup (&axis->log_to_phy, where, &group_start);
group = axis_group_from_tower_node (group_node);
if (where > group_start)
{
@@ -533,7 +659,75 @@
return &group->logical;
}
-void
+static void
+merge_axis_nodes (struct axis *axis, struct tower_node *node,
+ struct tower_node **other_node)
+{
+ struct tower *t = &axis->log_to_phy;
+ struct axis_group *group;
+ struct tower_node *next, *prev;
+
+ if (node == NULL)
+ node = tower_last (t);
+ if (node == NULL)
+ return;
+ group = axis_group_from_tower_node (node);
+
+ next = tower_next (t, node);
+ if (next != NULL)
+ {
+ struct axis_group *next_group = axis_group_from_tower_node (next);
+ unsigned long this_height = tower_node_get_height (node);
+
+ if (group->phy_start + this_height == next_group->phy_start)
+ {
+ unsigned long next_height = tower_node_get_height (next);
+ tower_resize (t, node, this_height + next_height);
+ if (other_node != NULL && *other_node == next)
+ *other_node = tower_next (t, *other_node);
+ tower_delete (t, next);
+ free (next_group);
+ }
+ }
+
+ prev = tower_prev (t, node);
+ if (prev != NULL)
+ {
+ struct axis_group *prev_group = axis_group_from_tower_node (prev);
+ unsigned long prev_height = tower_node_get_height (prev);
+
+ if (prev_group->phy_start + prev_height == group->phy_start)
+ {
+ unsigned long this_height = tower_node_get_height (node);
+ group->phy_start = prev_group->phy_start;
+ tower_resize (t, node, this_height + prev_height);
+ if (other_node != NULL && *other_node == prev)
+ *other_node = tower_next (t, *other_node);
+ tower_delete (t, prev);
+ free (prev_group);
+ }
+ }
+}
+
+static void
+check_axis_merged (const struct axis *axis UNUSED)
+{
+#if 0//ASSERT_LEVEL >= 10
+ struct tower_node *prev, *node;
+
+ for (prev = NULL, node = tower_first (&axis->log_to_phy); node != NULL;
+ prev = node, node = tower_next (&axis->log_to_phy, node))
+ if (prev != NULL)
+ {
+ struct axis_group *prev_group = axis_group_from_tower_node (prev);
+ unsigned long prev_height = tower_node_get_height (prev);
+ struct axis_group *node_group = axis_group_from_tower_node (node);
+ assert (prev_group->phy_start + prev_height != node_group->phy_start);
+ }
+#endif
+}
+
+static void
axis_insert (struct axis *axis,
unsigned long int log_start, unsigned long int phy_start,
unsigned long int cnt)
@@ -541,9 +735,11 @@
struct tower_node *before = split_axis (axis, log_start);
struct tower_node *new = make_axis_group (phy_start);
tower_insert (&axis->log_to_phy, cnt, new, before);
+ merge_axis_nodes (axis, new, NULL);
+ check_axis_merged (axis);
}
-void
+static void
axis_remove (struct axis *axis,
unsigned long int start, unsigned long int cnt)
{
@@ -556,10 +752,12 @@
next = tower_delete (&axis->log_to_phy, cur);
free (axis_group_from_tower_node (cur));
}
+ merge_axis_nodes (axis, last, NULL);
+ check_axis_merged (axis);
}
}
-void
+static void
axis_move (struct axis *axis,
unsigned long int old_start, unsigned long int new_start,
unsigned long int cnt)
@@ -567,6 +765,7 @@
if (cnt > 0 && old_start != new_start)
{
struct tower_node *old_first, *old_last, *new_first;
+ struct tower_node *merge1, *merge2;
struct tower tmp_array;
old_first = split_axis (axis, old_start);
@@ -574,61 +773,91 @@
tower_init (&tmp_array);
tower_splice (&tmp_array, NULL,
&axis->log_to_phy, old_first, old_last);
+ merge_axis_nodes (axis, old_last, NULL);
+
+ check_axis_merged (axis);
new_first = split_axis (axis, new_start);
+ merge1 = tower_first (&tmp_array);
+ merge2 = tower_last (&tmp_array);
+ if (merge2 == merge1)
+ merge2 = NULL;
tower_splice (&axis->log_to_phy, new_first, &tmp_array, old_first, NULL);
+ merge_axis_nodes (axis, merge1, &merge2);
+ merge_axis_nodes (axis, merge2, NULL);
+
+ check_axis_merged (axis);
}
}
-/* A source. */
-struct source
+/* Type of a source. */
+enum backing_type
{
- struct backing *backing;
- struct sparse_cases *overlay;
+ SOURCE_CASES,
+ SOURCE_CASEREADER
};
-struct backing
+/* A source. */
+struct source
{
- struct casereader *reader;
- struct range_set *rows;
+ size_t columns_used;
+ struct sparse_cases *data;
+ struct casereader *backing;
};
-struct source *
+static struct source *
source_create_empty (size_t column_cnt)
{
struct source *source = xmalloc (sizeof *source);
+ source->columns_used = 0;
+ source->data = sparse_cases_create (column_cnt);
source->backing = NULL;
- source->overlay = sparse_cases_create (column_cnt);
return source;
}
-struct source *
-source_create_from_casereader (struct casereader *reader,
- casenumber *row_cnt, size_t *column_cnt)
+static struct source *
+source_create_casereader (struct casereader *reader)
{
- struct source *source;
+ struct source *source
+ = source_create_empty (casereader_get_value_cnt (reader));
+ source->backing = reader;
+ return source;
+}
- *column_cnt = casereader_get_value_cnt (reader);
- *row_cnt = casereader_get_case_cnt (reader);
- if (*row_cnt == CASENUMBER_INVALID)
+/* For testing purposes only. */
+static struct source *
+clone_source (const struct source *old)
+{
+ struct source *new = xmalloc (sizeof *new);
+ new->columns_used = old->columns_used;
+ new->data = sparse_cases_clone (old->data);
+ new->backing = old->backing != NULL ? casereader_clone (old->backing) : NULL;
+ if (new->data == NULL)
{
- struct casereader *clone;
- struct ccase c;
-
- *row_cnt = 0;
- clone = casereader_clone (reader);
- for (; casereader_read (clone, &c); case_destroy (&c))
- (*row_cnt)++;
- casereader_destroy (clone);
+ source_destroy (new);
+ new = NULL;
}
+ return new;
+}
- source = source_create_empty (*column_cnt);
- source->backing = xmalloc (sizeof *source->backing);
- source->backing->reader = reader;
- source->backing->rows = range_set_create ();
- range_set_insert (source->backing->rows, 0, *row_cnt);
+static void
+source_increase_use (struct source *source, size_t delta)
+{
+ source->columns_used += delta;
+ assert (source->columns_used <= sparse_cases_get_value_cnt (source->data));
+}
- return source;
+static void
+source_decrease_use (struct source *source, size_t delta)
+{
+ source->columns_used -= delta;
+ assert (source->columns_used <= sparse_cases_get_value_cnt (source->data));
+}
+
+static bool
+source_in_use (const struct source *source)
+{
+ return source->columns_used > 0;
}
static void
@@ -636,190 +865,417 @@
{
if (source != NULL)
{
- if (source->backing != NULL)
- {
- casereader_destroy (source->backing->reader);
- range_set_destroy (source->backing->rows);
- free (source->backing);
- }
- sparse_cases_destroy (source->overlay);
+ sparse_cases_destroy (source->data);
+ casereader_destroy (source->backing);
free (source);
}
}
-bool
-source_rw (struct source *source, enum rw_op op,
+static bool
+source_read (const struct source *source,
casenumber row, size_t column,
union value *values, size_t value_cnt)
{
- assert (op == OP_READ || op == OP_WRITE);
-
- if (source->backing != NULL
- && range_set_contains (source->backing->rows, row))
+ if (source->backing == NULL || sparse_cases_contains_row (source->data, row))
+ return sparse_cases_read (source->data, row, column, values, value_cnt);
+ else
{
- /* Get the case from the reader. */
struct ccase c;
- if (!casereader_peek (source->backing->reader, row, &c))
- return false;
+ bool ok;
- if (op == OP_READ)
+ assert (source->backing != NULL);
+ ok = casereader_peek (source->backing, row, &c);
+ if (ok)
{
- /* Get the values from the case. */
- size_t i;
- for (i = 0; i < value_cnt; i++)
- values[i] = *case_data_idx (&c, column + i);
+ case_copy_out (&c, column, values, value_cnt);
case_destroy (&c);
- return true;
}
+ return ok;
+ }
+}
+
+static bool
+source_write (struct source *source,
+ casenumber row, size_t column,
+ const union value *values, size_t value_cnt)
+{
+ size_t column_cnt = sparse_cases_get_value_cnt (source->data);
+ bool ok;
+
+ if (source->backing == NULL
+ || (column == 0 && value_cnt == column_cnt)
+ || sparse_cases_contains_row (source->data, row))
+ ok = sparse_cases_write (source->data, row, column, values, value_cnt);
else
{
- /* Copy the case to the overlay, remove it from the
- backing, and fall through to the "no backing"
- case. */
- sparse_cases_rw (source->overlay, OP_WRITE, row, 0,
- (union value *)case_data_all (&c),
- case_get_value_cnt (&c));
- case_destroy (&c);
- range_set_delete (source->backing->rows, row, 1);
- if (range_set_is_empty (source->backing->rows))
+ struct ccase c;
+ ok = casereader_peek (source->backing, row, &c);
+ if (ok)
{
- casereader_destroy (source->backing->reader);
- range_set_destroy (source->backing->rows);
- free (source->backing);
- source->backing = NULL;
- }
+ case_copy_in (&c, column, values, value_cnt);
+ ok = sparse_cases_write (source->data, row, 0,
+ case_data_all (&c), column_cnt);
+ case_destroy (&c);
}
}
+ return ok;
+}
+
+static bool
+source_write_columns (struct source *source, size_t start_column,
+ const union value values[], size_t value_cnt)
+{
+ /* We don't support backing != NULL because (1) it's harder and
+ (2) source_write_columns is only called by
+ datasheet_insert_columns, which doesn't reuse columns from
+ sources that are backed by casereaders. */
+ assert (source->backing == NULL);
- return sparse_cases_rw (source->overlay, op, row, column, values, value_cnt);
+ return sparse_cases_write_columns (source->data, start_column,
+ values, value_cnt);
}
-struct sparse_cases
- {
- size_t column_cnt;
- casenumber max_memory_cases;
- struct sparse_array *memory;
- struct case_tmpfile *disk;
- };
+static bool
+source_has_backing (const struct source *s)
+{
+ return s->backing != NULL;
+}
+
+#define MAX_ROWS 4
+#define MAX_COLS 4
-static struct sparse_cases *
-sparse_cases_create (size_t column_cnt)
+static unsigned int
+hash_datasheet (const struct datasheet *ds)
{
- struct sparse_cases *sc = xmalloc (sizeof *sc);
- sc->column_cnt = column_cnt;
- sc->max_memory_cases = get_workspace_cases (column_cnt);
- sc->memory = sparse_array_create (NULL, sizeof (struct ccase));
- sc->disk = NULL;
- return sc;
+ unsigned int hash[DIV_RND_UP (20, sizeof (unsigned int))];
+ struct md4_ctx ctx;
+ struct range_map_node *r;
+
+ md4_init_ctx (&ctx);
+ hash_axis (ds->columns, &ctx);
+ hash_axis (ds->rows, &ctx);
+ for (r = range_map_first (&ds->sources); r != NULL;
+ r = range_map_next (&ds->sources, r))
+ {
+ unsigned long int start = range_map_node_get_start (r);
+ unsigned long int end = range_map_node_get_end (r);
+ md4_process_bytes (&start, sizeof start, &ctx);
+ md4_process_bytes (&end, sizeof end, &ctx);
+ /* FIXME: hash anything more? */
+ }
+ md4_process_bytes (&ds->column_min_alloc, sizeof ds->column_min_alloc,
+ &ctx);
+ md4_finish_ctx (&ctx, hash);
+ return hash[0];
}
static void
-sparse_cases_destroy (struct sparse_cases *sc)
+check_datasheet (struct mc *mc, struct datasheet *ds,
+ double array[MAX_ROWS][MAX_COLS],
+ size_t row_cnt, size_t column_cnt)
{
- if (sc != NULL)
+ if (mc_discard_dup_state (mc, hash_datasheet (ds)))
{
- if (sc->memory != NULL)
+ datasheet_destroy (ds);
+ return;
+ }
+
+ 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);
+ else
{
- unsigned long int idx;
- struct ccase *c;
- for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
- c = sparse_array_scan (sc->memory, &idx, &idx))
- case_destroy (c);
- sparse_array_destroy (sc->memory);
+ size_t row, col;
+
+ for (row = 0; row < row_cnt; row++)
+ for (col = 0; col < column_cnt; col++)
+ {
+ union value v;
+ if (!datasheet_get_value (ds, row, col, &v, 1))
+ NOT_REACHED ();
+ if (v.f != array[row][col])
+ mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: %g != %g",
+ row, col, row_cnt, column_cnt, v.f, array[row][col]);
}
- case_tmpfile_destroy (sc->disk);
- free (sc);
}
+
+ mc_add_state (mc, ds);
}
static void
-dump_sparse_cases_to_disk (struct sparse_cases *sc)
+extract_data (const struct datasheet *ds, double data[MAX_ROWS][MAX_COLS])
{
- unsigned long int idx;
- struct ccase *c;
+ size_t column_cnt = datasheet_get_column_cnt (ds);
+ size_t row_cnt = datasheet_get_row_cnt (ds);
+ size_t row, col;
- assert (sc->memory != NULL);
- assert (sc->disk == NULL);
+ for (row = 0; row < row_cnt; row++)
+ for (col = 0; col < column_cnt; col++)
+ {
+ union value v;
+ if (!datasheet_get_value (ds, row, col, &v, 1))
+ NOT_REACHED ();
+ data[row][col] = v.f;
+ }
+}
- sc->disk = case_tmpfile_create (sc->column_cnt);
+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;
+ struct range_map_node *r;
+ size_t row_cnt, column_cnt;
+ size_t row, column;
- for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
- c = sparse_array_scan (sc->memory, &idx, &idx))
- case_tmpfile_put (sc->disk, idx, c);
- sparse_array_destroy (sc->memory);
- sc->memory = NULL;
+ /* Clone ODS into DS. */
+ ds = *ds_ = xmalloc (sizeof *ds);
+ ds->columns = clone_axis (ods->columns);
+ ds->rows = clone_axis (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 = range_map_data (r, struct source_info,
+ column_range);
+ struct source_info *si = xmalloc (sizeof *si);
+ si->source = clone_source (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;
+
+ /* Clone ODATA into DATA. */
+ row_cnt = datasheet_get_row_cnt (ds);
+ column_cnt = datasheet_get_column_cnt (ds);
+ for (row = 0; row < row_cnt; row++)
+ for (column = 0; column < column_cnt; column++)
+ data[row][column] = odata[row][column];
}
static void
-case_rw (enum rw_op op, struct ccase *c, size_t c_ofs,
- union value *values, size_t value_cnt)
+datasheet_mc_init (struct mc *mc)
{
- size_t i;
- if (op == OP_WRITE)
+ struct datasheet_test_params *params = mc_get_aux (mc);
+ struct datasheet *ds;
+
+ if (params->backing_rows == 0 && params->backing_cols == 0)
{
- for (i = 0; i < value_cnt; i++)
- *case_data_rw_idx (c, c_ofs + i) = values[i];
+ ds = datasheet_create (NULL);
+ mc_name_state (mc, "empty datasheet");
+ check_datasheet (mc, ds, NULL, 0, 0);
}
else
{
- for (i = 0; i < value_cnt; i++)
- values[i] = *case_data_idx (c, c_ofs + i);
- }
-}
+ struct casewriter *writer;
+ struct casereader *reader;
+ double data[MAX_ROWS][MAX_COLS];
+ int row;
-static bool
-disk_sparse_cases_rw (struct sparse_cases *sc, enum rw_op op,
- casenumber row, size_t column,
- union value *values, size_t value_cnt)
-{
+ assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
+ assert (params->backing_cols > 0 && params->backing_cols <= MAX_COLS);
+
+ writer = mem_writer_create (params->backing_cols);
+ for (row = 0; row < params->backing_rows; row++)
+ {
struct ccase c;
+ int col;
- if (!case_tmpfile_get (sc->disk, row, &c))
- return false;
- case_rw (op, &c, column, values, value_cnt);
- if (op == OP_WRITE)
- return case_tmpfile_put (sc->disk, row, &c);
- else
+ case_create (&c, params->backing_cols);
+ for (col = 0; col < params->backing_cols; col++)
{
- case_destroy (&c);
- return true;
+ double value = params->next_value++;
+ data[row][col] = value;
+ case_data_rw_idx (&c, col)->f = value;
+ }
+ casewriter_write (writer, &c);
+ }
+ reader = casewriter_make_reader (writer);
+ assert (reader != NULL);
+
+ ds = datasheet_create (reader);
+ mc_name_state (mc, "datasheet with (%d,%d) backing",
+ params->backing_rows, params->backing_cols);
+ check_datasheet (mc, ds, data,
+ params->backing_rows, params->backing_cols);
}
}
-static bool
-memory_sparse_cases_rw (struct sparse_cases *sc, enum rw_op op,
- casenumber row, size_t column,
- union value *values, size_t value_cnt)
+static void
+datasheet_mc_mutate (struct mc *mc, const void *ods_)
{
- struct ccase *c;
+ struct datasheet_test_params *params = mc_get_aux (mc);
- c = sparse_array_get (sc->memory, row);
- if (c == NULL)
+ const struct datasheet *ods = ods_;
+ double odata[MAX_ROWS][MAX_COLS];
+ double data[MAX_ROWS][MAX_COLS];
+ size_t column_cnt = datasheet_get_column_cnt (ods);
+ size_t row_cnt = datasheet_get_row_cnt (ods);
+ size_t pos, new_pos, cnt;
+
+ extract_data (ods, odata);
+
+ for (pos = 0; pos <= column_cnt; pos++)
+ for (cnt = 0; cnt <= params->max_cols - column_cnt; cnt++)
+ if (mc_include_state (mc))
{
- if (sparse_array_count (sc->memory) < sc->max_memory_cases)
+ struct datasheet *ds;
+ union value new[MAX_COLS];
+ size_t i, j;
+
+ mc_name_state (mc, "insert %zu columns at %zu", cnt, pos);
+ clone_model (ods, odata, &ds, data);
+
+ for (i = 0; i < cnt; i++)
+ new[i].f = params->next_value++;
+
+ datasheet_insert_columns (ds, new, cnt, pos);
+
+ for (i = 0; i < row_cnt; i++)
{
- assert (op == OP_WRITE);
- c = sparse_array_insert (sc->memory, row);
- case_create (c, sc->column_cnt);
+ /* We could use an insert_range function here. */
+ memmove (&data[i][pos + cnt], &data[i][pos],
+ (column_cnt - pos) * sizeof data[i][pos]);
+ for (j = 0; j < cnt; j++)
+ data[i][pos + j] = new[j].f;
}
- else
+
+ check_datasheet (mc, ds, data, row_cnt, column_cnt + cnt);
+ }
+
+ for (pos = 0; pos < column_cnt; pos++)
+ for (cnt = 0; cnt < column_cnt - pos; cnt++)
+ if (mc_include_state (mc))
{
- dump_sparse_cases_to_disk (sc);
- return disk_sparse_cases_rw (sc, op, row, column, values, value_cnt);
+ struct datasheet *ds;
+ size_t i;
+
+ mc_name_state (mc, "delete %zu columns at %zu", cnt, pos);
+ clone_model (ods, odata, &ds, data);
+
+ datasheet_delete_columns (ds, pos, cnt);
+
+ for (i = 0; i < row_cnt; i++)
+ remove_range (&data[i], column_cnt, sizeof *data[i], pos, cnt);
+
+ check_datasheet (mc, ds, data, row_cnt, column_cnt - cnt);
}
+
+ for (pos = 0; pos < column_cnt; pos++)
+ for (cnt = 0; cnt < column_cnt - pos; cnt++)
+ for (new_pos = 0; new_pos < column_cnt - cnt; new_pos++)
+ if (mc_include_state (mc))
+ {
+ struct datasheet *ds;
+ size_t i;
+
+ clone_model (ods, odata, &ds, data);
+ mc_name_state (mc, "move %zu columns from %zu to %zu",
+ cnt, pos, new_pos);
+
+ datasheet_move_columns (ds, pos, new_pos, cnt);
+
+ for (i = 0; i < row_cnt; i++)
+ move_range (&data[i], column_cnt, sizeof data[i][0],
+ pos, new_pos, cnt);
+
+ check_datasheet (mc, ds, data, row_cnt, column_cnt);
}
- case_rw (op, c, column, values, value_cnt);
+ for (pos = 0; pos <= row_cnt; pos++)
+ for (cnt = 0; cnt <= params->max_rows - row_cnt; cnt++)
+ if (mc_include_state (mc))
+ {
+ struct datasheet *ds;
+ struct ccase c[MAX_ROWS];
+ size_t i, j;
- return true;
+ clone_model (ods, odata, &ds, data);
+ mc_name_state (mc, "insert %zu rows at %zu", cnt, pos);
+
+ for (i = 0; i < cnt; i++)
+ {
+ case_create (&c[i], column_cnt);
+ for (j = 0; j < column_cnt; j++)
+ case_data_rw_idx (&c[i], j)->f = params->next_value++;
+ }
+
+ /* We could use an insert_range function here. */
+ memmove (&data[pos + cnt], &data[pos],
+ sizeof (double[MAX_COLS]) * (row_cnt - pos));
+ for (i = 0; i < cnt; i++)
+ for (j = 0; j < column_cnt; j++)
+ data[i + pos][j] = case_num_idx (&c[i], j);
+
+ if (!datasheet_insert_rows (ds, pos, c, cnt))
+ NOT_REACHED ();
+
+ check_datasheet (mc, ds, data, row_cnt + cnt, column_cnt);
+ }
+
+ for (pos = 0; pos < row_cnt; pos++)
+ for (cnt = 0; cnt < row_cnt - pos; cnt++)
+ if (mc_include_state (mc))
+ {
+ struct datasheet *ds;
+
+ clone_model (ods, odata, &ds, data);
+ mc_name_state (mc, "delete %zu rows at %zu", cnt, pos);
+
+ datasheet_delete_rows (ds, pos, cnt);
+
+ remove_range (&data[0], row_cnt, sizeof data[0], pos, cnt);
+
+ check_datasheet (mc, ds, data, row_cnt - cnt, column_cnt);
+ }
+
+ for (pos = 0; pos < row_cnt; pos++)
+ for (cnt = 0; cnt < row_cnt - pos; cnt++)
+ for (new_pos = 0; new_pos < row_cnt - cnt; new_pos++)
+ if (mc_include_state (mc))
+ {
+ struct datasheet *ds;
+
+ clone_model (ods, odata, &ds, data);
+ mc_name_state (mc,
+ "move %zu rows from %zu to %zu", cnt, pos, new_pos);
+
+ datasheet_move_rows (ds, pos, new_pos, cnt);
+
+ move_range (&data[0], row_cnt, sizeof data[0],
+ pos, new_pos, cnt);
+
+ check_datasheet (mc, ds, data, row_cnt, column_cnt);
+ }
}
-static bool
-sparse_cases_rw (struct sparse_cases *sc, enum rw_op op,
- casenumber row, size_t column,
- union value *values, size_t value_cnt)
+static void
+datasheet_mc_destroy (void *ds_)
{
- return (sc->memory != NULL
- ? memory_sparse_cases_rw (sc, op, row, column, values, value_cnt)
- : disk_sparse_cases_rw (sc, op, row, column, values, value_cnt));
+ struct datasheet *ds = ds_;
+ datasheet_destroy (ds);
+}
+
+struct mc_results *
+datasheet_test (struct mc_options *options, void *params_)
+{
+ struct datasheet_test_params *params = params_;
+ static const struct mc_class datasheet_mc_class =
+ {
+ datasheet_mc_init,
+ datasheet_mc_mutate,
+ datasheet_mc_destroy,
+ };
+
+ params->next_value = 0;
+ params->max_rows = MIN (params->max_rows, MAX_ROWS);
+ params->max_cols = MIN (params->max_cols, MAX_COLS);
+ params->backing_rows = MIN (params->backing_rows, params->max_rows);
+ params->backing_cols = MIN (params->backing_cols, params->max_cols);
+
+ mc_options_set_aux (options, params);
+ return mc_run (&datasheet_mc_class, options);
}
Index: src/data/datasheet.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/Attic/datasheet.h,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/data/datasheet.h 19 Mar 2007 21:36:24 -0000 1.1.2.1
+++ src/data/datasheet.h 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -29,38 +29,55 @@
retrieval, as well as adding, removing, and rearranging both
rows and columns. */
-struct datasheet *datasheet_create_empty (void);
struct datasheet *datasheet_create (struct casereader *);
void datasheet_destroy (struct datasheet *);
-struct casereader *datasheet_make_reader (struct datasheet *);
-casenumber datasheet_get_case_cnt (const struct datasheet *);
-size_t datasheet_get_value_cnt (const struct datasheet *);
+struct casereader *datasheet_make_reader (struct datasheet *);
/* Columns. */
-void datasheet_insert_values (struct datasheet *,
+size_t datasheet_get_column_cnt (const struct datasheet *);
+void datasheet_insert_columns (struct datasheet *,
const union value[], size_t cnt,
size_t before);
-void datasheet_delete_values (struct datasheet *, size_t start, size_t cnt);
-void datasheet_move_values (struct datasheet *,
+void datasheet_delete_columns (struct datasheet *, size_t start, size_t cnt);
+void datasheet_move_columns (struct datasheet *,
size_t old_start, size_t new_start,
size_t cnt);
-void datasheet_reorder_values (struct datasheet *,
- size_t *ordering, size_t cnt);
/* Rows. */
-bool datasheet_insert_cases (struct datasheet *,
+casenumber datasheet_get_row_cnt (const struct datasheet *);
+bool datasheet_insert_rows (struct datasheet *,
casenumber before, struct ccase *,
casenumber cnt);
-void datasheet_delete_cases (struct datasheet *,
+void datasheet_delete_rows (struct datasheet *,
casenumber first, casenumber cnt);
+void datasheet_move_rows (struct datasheet *,
+ size_t old_start, size_t new_start,
+ size_t cnt);
/* Data. */
-bool datasheet_get_case (struct datasheet *, casenumber, struct ccase *);
-bool datasheet_put_case (struct datasheet *, casenumber, struct ccase *);
-bool datasheet_get_value (struct datasheet *, casenumber, size_t column,
+bool datasheet_get_row (const struct datasheet *, casenumber, struct ccase *);
+bool datasheet_put_row (struct datasheet *, casenumber, struct ccase *);
+bool datasheet_get_value (const struct datasheet *, casenumber, size_t column,
union value *, int width);
bool datasheet_put_value (struct datasheet *, casenumber, size_t column,
const union value *, int width);
+/* Testing. */
+struct mc_options;
+
+struct datasheet_test_params
+ {
+ /* Parameters. */
+ int max_rows;
+ int max_cols;
+ int backing_rows;
+ int backing_cols;
+
+ /* State. */
+ int next_value;
+ };
+
+struct mc_results *datasheet_test (struct mc_options *options, void *params);
+
#endif /* data/datasheet.h */
Index: src/language/command.def
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/command.def,v
retrieving revision 1.14.2.1
retrieving revision 1.14.2.2
diff -u -b -r1.14.2.1 -r1.14.2.2
--- src/language/command.def 19 Mar 2007 21:36:24 -0000 1.14.2.1
+++ src/language/command.def 14 Apr 2007 05:04:23 -0000 1.14.2.2
@@ -127,10 +127,11 @@
DEF_CMD (S_INPUT_PROGRAM, 0, "REREAD", cmd_reread)
/* Commands for testing PSPP. */
+DEF_CMD (S_ANY, F_TESTING, "DEBUG DATASHEET", cmd_debug_datasheet)
DEF_CMD (S_ANY, F_TESTING, "DEBUG EVALUATE", cmd_debug_evaluate)
+DEF_CMD (S_ANY, F_TESTING, "DEBUG FLOAT FORMAT", cmd_debug_float_format)
DEF_CMD (S_ANY, F_TESTING, "DEBUG MOMENTS", cmd_debug_moments)
DEF_CMD (S_ANY, F_TESTING, "DEBUG POOL", cmd_debug_pool)
-DEF_CMD (S_ANY, F_TESTING, "DEBUG FLOAT FORMAT", cmd_debug_float_format)
DEF_CMD (S_ANY, F_TESTING, "DEBUG XFORM FAIL", cmd_debug_xform_fail)
/* Unimplemented commands. */
Index: src/language/tests/automake.mk
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/tests/automake.mk,v
retrieving revision 1.4.2.1
retrieving revision 1.4.2.2
diff -u -b -r1.4.2.1 -r1.4.2.2
--- src/language/tests/automake.mk 19 Mar 2007 21:36:24 -0000 1.4.2.1
+++ src/language/tests/automake.mk 14 Apr 2007 05:04:23 -0000 1.4.2.2
@@ -1,6 +1,16 @@
## Process this file with automake to produce Makefile.in -*- makefile -*-
+language_tests_built_sources = \
+ src/language/tests/check-model.c
+
language_tests_sources = \
+ src/language/tests/datasheet-test.c \
+ src/language/tests/float-format.c \
src/language/tests/moments-test.c \
src/language/tests/pool-test.c \
- src/language/tests/float-format.c
+ $(language_tests_built_sources)
+
+all_q_sources += $(language_tests_built_sources:.c=.q)
+EXTRA_DIST += $(language_tests_built_sources:.c=.q)
+CLEANFILES += $(language_tests_built_sources)
+
Index: src/libpspp/array.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/array.c,v
retrieving revision 1.10.2.1
retrieving revision 1.10.2.2
diff -u -b -r1.10.2.1 -r1.10.2.2
--- src/libpspp/array.c 19 Mar 2007 21:36:24 -0000 1.10.2.1
+++ src/libpspp/array.c 14 Apr 2007 05:04:23 -0000 1.10.2.2
@@ -390,6 +390,36 @@
}
}
+/* Moves N elements in ARRAY starting at OLD_IDX, which consists
+ of COUNT elements of SIZE bytes each, so that they now start
+ at NEW_IDX, shifting around other elements as needed. */
+void
+move_range (void *array_, size_t count, size_t size,
+ size_t old_idx, size_t new_idx, size_t n)
+{
+ assert (array_ != NULL || count == 0);
+ assert (n <= count);
+ assert (old_idx + n <= count);
+ assert (new_idx + n <= count);
+
+ if (old_idx != new_idx && n > 0)
+ {
+ char *array = array_;
+ char *range = xmalloc (size * n);
+ char *new = array + new_idx * size;
+ char *old = array + old_idx * size;
+
+ memcpy (range, old, size * n);
+ if (new < old)
+ memmove (new + size * n, new, (old_idx - new_idx) * size);
+ else
+ memmove (old, old + size * n, (new_idx - old_idx) * size);
+ memcpy (new, range, size * n);
+
+ free (range);
+ }
+}
+
/* A predicate and its auxiliary data. */
struct pred_aux
{
Index: src/libpspp/array.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/array.h,v
retrieving revision 1.6
retrieving revision 1.6.2.1
diff -u -b -r1.6 -r1.6.2.1
--- src/libpspp/array.h 29 Oct 2006 11:16:07 -0000 1.6
+++ src/libpspp/array.h 14 Apr 2007 05:04:23 -0000 1.6.2.1
@@ -115,6 +115,12 @@
void move_element (void *array, size_t count, size_t size,
size_t old_idx, size_t new_idx);
+/* Moves N elements in ARRAY starting at OLD_IDX, which consists
+ of COUNT elements of SIZE bytes each, so that they now start
+ at NEW_IDX, shifting around other elements as needed. */
+void move_range (void *array, size_t count, size_t size,
+ size_t old_idx, size_t new_idx, size_t n);
+
/* Removes elements equal to ELEMENT from ARRAY, which consists
of COUNT elements of SIZE bytes each. Returns the number of
remaining elements. AUX is passed to COMPARE as auxiliary
Index: src/libpspp/automake.mk
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/automake.mk,v
retrieving revision 1.22.2.7
retrieving revision 1.22.2.8
diff -u -b -r1.22.2.7 -r1.22.2.8
--- src/libpspp/automake.mk 2 Apr 2007 13:53:49 -0000 1.22.2.7
+++ src/libpspp/automake.mk 14 Apr 2007 05:04:23 -0000 1.22.2.8
@@ -37,6 +37,8 @@
src/libpspp/integer-format.h \
src/libpspp/msg-locator.c \
src/libpspp/msg-locator.h \
+ src/libpspp/model-checker.c \
+ src/libpspp/model-checker.h \
src/libpspp/ll.c \
src/libpspp/ll.h \
src/libpspp/llx.c \
Index: src/libpspp/range-set.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/range-set.c,v
retrieving revision 1.1.2.2
retrieving revision 1.1.2.3
diff -u -b -r1.1.2.2 -r1.1.2.3
--- src/libpspp/range-set.c 31 Mar 2007 03:31:58 -0000 1.1.2.2
+++ src/libpspp/range-set.c 14 Apr 2007 05:04:23 -0000 1.1.2.3
@@ -100,6 +100,20 @@
return rs;
}
+/* Creates and returns a clone of OLD range set in the given POOL
+ (which may be null). */
+struct range_set *
+range_set_clone (const struct range_set *old, struct pool *pool)
+{
+ struct range_set *new;
+ struct range_set_node *node;
+
+ new = range_set_create_pool (pool);
+ for (node = first_node (old); node != NULL; node = next_node (old, node))
+ insert_node (new, node->start, node->end);
+ return new;
+}
+
/* Destroys range set RS. */
void
range_set_destroy (struct range_set *rs)
Index: src/libpspp/range-set.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/range-set.h,v
retrieving revision 1.1.2.3
retrieving revision 1.1.2.4
diff -u -b -r1.1.2.3 -r1.1.2.4
--- src/libpspp/range-set.h 31 Mar 2007 03:31:58 -0000 1.1.2.3
+++ src/libpspp/range-set.h 14 Apr 2007 05:04:23 -0000 1.1.2.4
@@ -32,6 +32,7 @@
struct range_set *range_set_create (void);
struct range_set *range_set_create_pool (struct pool *);
+struct range_set *range_set_clone (const struct range_set *, struct pool *);
void range_set_destroy (struct range_set *);
void range_set_insert (struct range_set *,
Index: src/libpspp/tower.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/tower.c,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/libpspp/tower.c 1 Apr 2007 19:32:15 -0000 1.1.2.1
+++ src/libpspp/tower.c 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -20,13 +20,18 @@
#include <libpspp/tower.h>
+#include <limits.h>
+
#include <libpspp/assertion.h>
#include <libpspp/compiler.h>
static struct tower_node *abt_to_tower_node (const struct abt_node *);
static struct tower_node *first_node (const struct tower *);
+static struct tower_node *last_node (const struct tower *);
static struct tower_node *next_node (const struct tower *,
const struct tower_node *);
+static struct tower_node *prev_node (const struct tower *,
+ const struct tower_node *);
static unsigned long int get_subtree_height (const struct abt_node *);
static void reaugment_tower_node (struct abt_node *,
const struct abt_node *,
@@ -38,6 +43,7 @@
tower_init (struct tower *t)
{
abt_init (&t->abt, NULL, reaugment_tower_node, NULL);
+ t->cache_bottom = ULONG_MAX;
}
/* Returns true if T contains no nodes, false otherwise. */
@@ -64,6 +70,7 @@
new->height = height;
abt_insert_before (&t->abt, under ? &under->abt_node : NULL,
&new->abt_node);
+ t->cache_bottom = ULONG_MAX;
}
/* Deletes NODE from tower T. */
@@ -72,6 +79,7 @@
{
struct tower_node *next = next_node (t, node);
abt_delete (&t->abt, &node->abt_node);
+ t->cache_bottom = ULONG_MAX;
return next;
}
@@ -83,6 +91,7 @@
assert (new_height > 0);
node->height = new_height;
abt_reaugmented (&t->abt, &node->abt_node);
+ t->cache_bottom = ULONG_MAX;
}
/* Removes nodes FIRST through LAST (exclusive) from tower SRC
@@ -110,6 +119,7 @@
abt_insert_before (&dst->abt, under ? &under->abt_node : NULL,
&first->abt_node);
}
+ dst->cache_bottom = src->cache_bottom = ULONG_MAX;
}
/* Returns the node at the given HEIGHT from the bottom of tower
@@ -118,14 +128,21 @@
of the returned node, which may be less than HEIGHT if HEIGHT
refers to the middle of a node instead of its bottom. */
struct tower_node *
-tower_lookup (const struct tower *t,
+tower_lookup (const struct tower *t_,
unsigned long height,
unsigned long *node_start)
{
+ struct tower *t = (struct tower *) t_;
struct abt_node *p;
assert (height < tower_height (t));
+ if (height >= t->cache_bottom && height - t->cache_bottom < t->cache->height)
+ {
+ *node_start = t->cache_bottom;
+ return t->cache;
+ }
+
*node_start = 0;
p = t->abt.root;
for (;;)
@@ -147,6 +164,8 @@
if (height < node_height)
{
/* Our goal height is in P. */
+ t->cache = node;
+ t->cache_bottom = *node_start;
return node;
}
else
@@ -168,6 +187,14 @@
return first_node (t);
}
+/* Returns the node at the top of tower T, or a null pointer if T
+ is empty. */
+struct tower_node *
+tower_last (const struct tower *t)
+{
+ return last_node (t);
+}
+
/* If NODE is nonnull, returns the node just above NODE in tower
T, or a null pointer if NODE is the topmost node in T.
If NODE is null, acts like tower_first. */
@@ -177,6 +204,15 @@
return node != NULL ? next_node (t, node) : first_node (t);
}
+/* If NODE is nonnull, returns the node just below NODE in tower
+ T, or a null pointer if NODE is the bottommost node in T.
+ If NODE is null, acts like tower_last. */
+struct tower_node *
+tower_prev (const struct tower *t, const struct tower_node *node)
+{
+ return node != NULL ? prev_node (t, node) : last_node (t);
+}
+
/* Returns the tower node corresponding to the given ABT_NODE. */
static struct tower_node *
abt_to_tower_node (const struct abt_node *abt_node)
@@ -184,20 +220,39 @@
return abt_data (abt_node, struct tower_node, abt_node);
}
+/* Returns the tower node corresponding to the given ABT_NODE. */
+static struct tower_node *
+abt_to_tower_node_null (const struct abt_node *abt_node)
+{
+ return abt_node != NULL ? abt_to_tower_node (abt_node) : NULL;
+}
+
/* Returns the first node in TOWER. */
static struct tower_node *
first_node (const struct tower *t)
{
- struct abt_node *abt_node = abt_first (&t->abt);
- return abt_node != NULL ? abt_to_tower_node (abt_node) : NULL;
+ return abt_to_tower_node_null (abt_first (&t->abt));
+}
+
+/* Returns the first node in TOWER. */
+static struct tower_node *
+last_node (const struct tower *t)
+{
+ return abt_to_tower_node_null (abt_last (&t->abt));
}
/* Returns the next node in TOWER after NODE. */
static struct tower_node *
next_node (const struct tower *t, const struct tower_node *node)
{
- struct abt_node *abt_node = abt_next (&t->abt, &node->abt_node);
- return abt_node != NULL ? abt_to_tower_node (abt_node) : NULL;
+ return abt_to_tower_node_null (abt_next (&t->abt, &node->abt_node));
+}
+
+/* Returns the previous node in TOWER before NODE. */
+static struct tower_node *
+prev_node (const struct tower *t, const struct tower_node *node)
+{
+ return abt_to_tower_node_null (abt_prev (&t->abt, &node->abt_node));
}
/* Returns the total height of the nodes in the subtree rooted at
Index: src/libpspp/tower.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/tower.h,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -b -r1.1.2.1 -r1.1.2.2
--- src/libpspp/tower.h 1 Apr 2007 19:32:15 -0000 1.1.2.1
+++ src/libpspp/tower.h 14 Apr 2007 05:04:23 -0000 1.1.2.2
@@ -75,6 +75,8 @@
struct tower
{
struct abt abt; /* Tree. */
+ struct tower_node *cache; /* Cache node. */
+ unsigned long int cache_bottom; /* Height of cache's bottom. */
};
void tower_init (struct tower *);
@@ -95,7 +97,10 @@
unsigned long int level,
unsigned long int *node_start);
struct tower_node *tower_first (const struct tower *);
+struct tower_node *tower_last (const struct tower *);
struct tower_node *tower_next (const struct tower *,
const struct tower_node *);
+struct tower_node *tower_prev (const struct tower *,
+ const struct tower_node *);
#endif /* libpspp/tower.h */
Index: src/ui/gui/automake.mk
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/automake.mk,v
retrieving revision 1.21.2.2
retrieving revision 1.21.2.3
diff -u -b -r1.21.2.2 -r1.21.2.3
--- src/ui/gui/automake.mk 20 Mar 2007 00:08:50 -0000 1.21.2.2
+++ src/ui/gui/automake.mk 14 Apr 2007 05:04:23 -0000 1.21.2.3
@@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in -*- makefile -*-
-#bin_PROGRAMS += src/ui/gui/psppire
+bin_PROGRAMS += src/ui/gui/psppire
src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GLADE_CFLAGS) -Wall
Index: src/ui/gui/psppire-case-file.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-case-file.c,v
retrieving revision 1.17.2.1
retrieving revision 1.17.2.2
diff -u -b -r1.17.2.1 -r1.17.2.2
--- src/ui/gui/psppire-case-file.c 19 Mar 2007 21:36:25 -0000 1.17.2.1
+++ src/ui/gui/psppire-case-file.c 14 Apr 2007 05:04:23 -0000 1.17.2.2
@@ -176,7 +176,7 @@
g_return_val_if_fail (cf, FALSE);
g_return_val_if_fail (cf->datasheet, FALSE);
- datasheet_delete_cases (cf->datasheet, first, n_cases);
+ datasheet_delete_rows (cf->datasheet, first, n_cases);
g_signal_emit (cf, signals [CASES_DELETED], 0, n_cases, first);
@@ -196,7 +196,7 @@
g_return_val_if_fail (cf->datasheet, FALSE);
case_clone (&tmp, cc);
- result = datasheet_insert_cases (cf->datasheet, posn, &tmp, 1);
+ result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
if ( result )
g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
@@ -219,10 +219,10 @@
g_return_val_if_fail (cf, FALSE);
g_return_val_if_fail (cf->datasheet, FALSE);
- posn = datasheet_get_case_cnt (cf->datasheet);
+ posn = datasheet_get_row_cnt (cf->datasheet);
case_clone (&tmp, c);
- result = datasheet_insert_cases (cf->datasheet, posn, &tmp, 1);
+ result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
@@ -238,7 +238,7 @@
if ( ! cf->datasheet)
return 0;
- return datasheet_get_case_cnt (cf->datasheet);
+ return datasheet_get_row_cnt (cf->datasheet);
}
/* Copies the IDXth value from case CASENUM into VALUE.
@@ -254,7 +254,7 @@
g_return_val_if_fail (cf, false);
g_return_val_if_fail (cf->datasheet, false);
- g_return_val_if_fail (idx < datasheet_get_value_cnt (cf->datasheet), false);
+ g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), false);
if (value == NULL)
{
@@ -291,7 +291,7 @@
g_return_val_if_fail (cf, FALSE);
g_return_val_if_fail (cf->datasheet, FALSE);
- g_return_val_if_fail (idx < datasheet_get_value_cnt (cf->datasheet), FALSE);
+ g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
ok = datasheet_put_value (cf->datasheet, casenum, idx, v, width);
if (ok)
@@ -313,7 +313,7 @@
g_return_val_if_fail (cf, FALSE);
g_return_val_if_fail (cf->datasheet, FALSE);
- g_return_val_if_fail (idx < datasheet_get_value_cnt (cf->datasheet), FALSE);
+ g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
width = fmt_var_width (fmt);
value = xallocsa (value_cnt_from_width (width) * sizeof *value);
@@ -341,7 +341,7 @@
/* FIXME: Need to have a signal to change a range of cases, instead of
calling a signal many times */
- for ( c = 0 ; c < datasheet_get_case_cnt (cf->datasheet) ; ++c )
+ for ( c = 0 ; c < datasheet_get_row_cnt (cf->datasheet) ; ++c )
g_signal_emit (cf, signals [CASE_CHANGED], 0, c);
}
@@ -356,10 +356,10 @@
g_return_val_if_fail (cf, FALSE);
if ( ! cf->datasheet )
- cf->datasheet = datasheet_create_empty ();
+ cf->datasheet = datasheet_create (NULL);
values = xcalloc (n_values, sizeof *values);
- datasheet_insert_values (cf->datasheet, values, n_values, before);
+ datasheet_insert_columns (cf->datasheet, values, n_values, before);
free (values);
return TRUE;
@@ -375,5 +375,5 @@
g_return_val_if_fail (cf, FALSE);
g_return_val_if_fail (cf->datasheet, FALSE);
- return datasheet_get_case (cf->datasheet, casenum, c);
+ return datasheet_get_row (cf->datasheet, casenum, c);
}
Index: src/ui/gui/psppire-data-store.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire-data-store.c,v
retrieving revision 1.33.2.1
retrieving revision 1.33.2.2
diff -u -b -r1.33.2.1 -r1.33.2.2
--- src/ui/gui/psppire-data-store.c 19 Mar 2007 21:36:25 -0000 1.33.2.1
+++ src/ui/gui/psppire-data-store.c 14 Apr 2007 05:04:23 -0000 1.33.2.2
@@ -454,7 +454,7 @@
/* Opportunity for optimisation exists here when creating a blank case */
- val_cnt = datasheet_get_value_cnt (ds->case_file->datasheet) ;
+ val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
case_create (&cc, val_cnt);
Index: src/ui/gui/psppire.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/ui/gui/psppire.c,v
retrieving revision 1.35.2.1
retrieving revision 1.35.2.2
diff -u -b -r1.35.2.1 -r1.35.2.2
--- src/ui/gui/psppire.c 19 Mar 2007 21:36:25 -0000 1.35.2.1
+++ src/ui/gui/psppire.c 14 Apr 2007 05:04:23 -0000 1.35.2.2
@@ -74,7 +74,7 @@
{
if ( NULL == s )
psppire_case_file_replace_datasheet (the_data_store->case_file,
- datasheet_create_empty ());
+ datasheet_create (NULL));
}
Index: src/data/sparse-cases.c
===================================================================
RCS file: src/data/sparse-cases.c
diff -N src/data/sparse-cases.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/data/sparse-cases.c 14 Apr 2007 05:04:23 -0000 1.1.2.1
@@ -0,0 +1,308 @@
+/* PSPP - computes sample statistics.
+ 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 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. */
+
+#include <config.h>
+
+#include <data/sparse-cases.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <data/settings.h>
+#include <data/case-tmpfile.h>
+#include <libpspp/assertion.h>
+#include <libpspp/range-set.h>
+#include <libpspp/sparse-array.h>
+
+#include "xalloc.h"
+
+struct sparse_cases
+ {
+ size_t column_cnt;
+ union value *default_columns;
+ casenumber max_memory_cases;
+ struct sparse_array *memory;
+ struct case_tmpfile *disk;
+ struct range_set *disk_cases;
+ };
+
+struct sparse_cases *
+sparse_cases_create (size_t column_cnt)
+{
+ struct sparse_cases *sc = xmalloc (sizeof *sc);
+ sc->column_cnt = column_cnt;
+ sc->default_columns = NULL;
+ sc->max_memory_cases = get_workspace_cases (column_cnt);
+ sc->memory = sparse_array_create (NULL, sizeof (struct ccase));
+ sc->disk = NULL;
+ sc->disk_cases = NULL;
+ return sc;
+}
+
+struct sparse_cases *
+sparse_cases_clone (const struct sparse_cases *old)
+{
+ struct sparse_cases *new = xmalloc (sizeof *new);
+
+ new->column_cnt = old->column_cnt;
+
+ if (old->default_columns != NULL)
+ new->default_columns
+ = xmemdup (old->default_columns,
+ old->column_cnt * sizeof *old->default_columns);
+ else
+ new->default_columns = NULL;
+
+ new->max_memory_cases = old->max_memory_cases;
+
+ if (old->memory != NULL)
+ {
+ unsigned long int idx;
+ struct ccase *c;
+
+ new->memory = sparse_array_create (NULL, sizeof (struct ccase));
+ for (c = sparse_array_scan (old->memory, NULL, &idx); c != NULL;
+ c = sparse_array_scan (old->memory, &idx, &idx))
+ case_clone (sparse_array_insert (new->memory, idx), c);
+ }
+ else
+ new->memory = NULL;
+
+ if (old->disk != NULL)
+ {
+ const struct range_set_node *node;
+
+ new->disk = case_tmpfile_create (old->column_cnt);
+ new->disk_cases = range_set_create ();
+ for (node = range_set_first (old->disk_cases); node != NULL;
+ node = range_set_next (old->disk_cases, node))
+ {
+ unsigned long int start = range_set_node_get_start (node);
+ unsigned long int end = range_set_node_get_end (node);
+ unsigned long int idx;
+ struct ccase c;
+
+ for (idx = start; idx < end; idx++)
+ if (!case_tmpfile_get_case (old->disk, idx, &c)
+ || !case_tmpfile_put_case (new->disk, idx, &c))
+ {
+ sparse_cases_destroy (new);
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ new->disk = NULL;
+ new->disk_cases = NULL;
+ }
+
+ return new;
+}
+
+void
+sparse_cases_destroy (struct sparse_cases *sc)
+{
+ if (sc != NULL)
+ {
+ if (sc->memory != NULL)
+ {
+ unsigned long int idx;
+ struct ccase *c;
+ for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
+ c = sparse_array_scan (sc->memory, &idx, &idx))
+ case_destroy (c);
+ sparse_array_destroy (sc->memory);
+ }
+ free (sc->default_columns);
+ case_tmpfile_destroy (sc->disk);
+ range_set_destroy (sc->disk_cases);
+ free (sc);
+ }
+}
+
+size_t
+sparse_cases_get_value_cnt (const struct sparse_cases *sc)
+{
+ return sc->column_cnt;
+}
+
+static bool
+dump_sparse_cases_to_disk (struct sparse_cases *sc)
+{
+ unsigned long int idx;
+ struct ccase *c;
+
+ assert (sc->memory != NULL);
+ assert (sc->disk == NULL);
+
+ sc->disk = case_tmpfile_create (sc->column_cnt);
+ sc->disk_cases = range_set_create ();
+
+ for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
+ c = sparse_array_scan (sc->memory, &idx, &idx))
+ {
+ if (!case_tmpfile_put_case (sc->disk, idx, c))
+ goto error;
+ range_set_insert (sc->disk_cases, idx, 1);
+ }
+ sparse_array_destroy (sc->memory);
+ sc->memory = NULL;
+ return true;
+
+ error:
+ case_tmpfile_destroy (sc->disk);
+ sc->disk = NULL;
+ range_set_destroy (sc->disk_cases);
+ sc->disk_cases = NULL;
+ return false;
+}
+
+bool
+sparse_cases_contains_row (const struct sparse_cases *sc, casenumber row)
+{
+ return (sc->memory != NULL
+ ? sparse_array_get (sc->memory, row) != NULL
+ : range_set_contains (sc->disk_cases, row));
+}
+
+bool
+sparse_cases_read (struct sparse_cases *sc, casenumber row, size_t column,
+ union value *values, size_t value_cnt)
+{
+ assert (value_cnt <= sc->column_cnt);
+ assert (column + value_cnt <= sc->column_cnt);
+
+ if (sparse_cases_contains_row (sc, row))
+ {
+ struct ccase c;
+ if (sc->memory != NULL)
+ case_clone (&c, sparse_array_get (sc->memory, row));
+ else if (!case_tmpfile_get_case (sc->disk, row, &c))
+ return false;
+ case_copy_out (&c, column, values, value_cnt);
+ case_destroy (&c);
+ }
+ else
+ {
+ assert (sc->default_columns != NULL);
+ memcpy (values, sc->default_columns + column,
+ sizeof *values * value_cnt);
+ }
+
+ return true;
+}
+
+static bool
+write_disk_case (struct sparse_cases *sc, casenumber row, size_t column,
+ const union value *values, size_t value_cnt)
+{
+ struct ccase c;
+ bool ok;
+
+ /* Get current case data. */
+ if (column == 0 && value_cnt == sc->column_cnt)
+ case_create (&c, sc->column_cnt);
+ else if (!case_tmpfile_get_case (sc->disk, row, &c))
+ return false;
+
+ /* Copy in new data. */
+ case_copy_in (&c, column, values, value_cnt);
+
+ /* Write new case. */
+ ok = case_tmpfile_put_case (sc->disk, row, &c);
+ if (ok)
+ range_set_insert (sc->disk_cases, row, 1);
+
+ return ok;
+}
+
+bool
+sparse_cases_write (struct sparse_cases *sc, casenumber row, size_t column,
+ const union value *values, size_t value_cnt)
+{
+ if (sc->memory != NULL)
+ {
+ struct ccase *c = sparse_array_get (sc->memory, row);
+ if (c == NULL)
+ {
+ if (sparse_array_count (sc->memory) >= sc->max_memory_cases)
+ {
+ if (!dump_sparse_cases_to_disk (sc))
+ return false;
+ return write_disk_case (sc, row, column, values, value_cnt);
+ }
+ c = sparse_array_insert (sc->memory, row);
+
+ case_create (c, sc->column_cnt);
+ if (sc->default_columns != NULL
+ && (column != 0 || value_cnt != sc->column_cnt))
+ case_copy_in (c, 0, sc->default_columns, sc->column_cnt);
+ }
+ case_copy_in (c, column, values, value_cnt);
+ return true;
+ }
+ else
+ return write_disk_case (sc, row, column, values, value_cnt);
+}
+
+bool
+sparse_cases_write_columns (struct sparse_cases *sc, size_t start_column,
+ const union value values[], size_t value_cnt)
+{
+ assert (value_cnt <= sc->column_cnt);
+ assert (start_column + value_cnt <= sc->column_cnt);
+
+ /* Set defaults. */
+ if (sc->default_columns == NULL)
+ sc->default_columns = xnmalloc (sc->column_cnt,
+ sizeof *sc->default_columns);
+ memcpy (sc->default_columns + start_column, values,
+ value_cnt * sizeof *sc->default_columns);
+
+ /* Set individual rows. */
+ if (sc->memory != NULL)
+ {
+ struct ccase *c;
+ unsigned long int idx;
+
+ for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
+ c = sparse_array_scan (sc->memory, &idx, &idx))
+ case_copy_in (c, start_column, values, value_cnt);
+ }
+ else
+ {
+ const struct range_set_node *node;
+
+ for (node = range_set_first (sc->disk_cases); node != NULL;
+ node = range_set_next (sc->disk_cases, node))
+ {
+ unsigned long int start = range_set_node_get_start (node);
+ unsigned long int end = range_set_node_get_end (node);
+ unsigned long int row;
+
+ for (row = start; row < end; row++)
+ case_tmpfile_put_values (sc->disk, row,
+ start_column, values, value_cnt);
+ }
+
+ if (case_tmpfile_error (sc->disk))
+ return false;
+ }
+ return true;
+}
Index: src/data/sparse-cases.h
===================================================================
RCS file: src/data/sparse-cases.h
diff -N src/data/sparse-cases.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/data/sparse-cases.h 14 Apr 2007 05:04:23 -0000 1.1.2.1
@@ -0,0 +1,40 @@
+/* PSPP - computes sample statistics.
+ 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 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. */
+
+#ifndef DATA_SPARSE_CASES_H
+#define DATA_SPARSE_CASES_H 1
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <data/case.h>
+
+struct sparse_cases *sparse_cases_create (size_t value_cnt);
+struct sparse_cases *sparse_cases_clone (const struct sparse_cases *);
+void sparse_cases_destroy (struct sparse_cases *);
+
+size_t sparse_cases_get_value_cnt (const struct sparse_cases *);
+
+bool sparse_cases_contains_row (const struct sparse_cases *, casenumber row);
+bool sparse_cases_read (struct sparse_cases *, casenumber row, size_t column,
+ union value *, size_t value_cnt);
+bool sparse_cases_write (struct sparse_cases *, casenumber row, size_t column,
+ const union value *, size_t value_cnt);
+bool sparse_cases_write_columns (struct sparse_cases *, size_t start_column,
+ const union value[], size_t value_cnt);
+
+#endif /* data/sparse-cases.h */
Index: src/language/tests/check-model.h
===================================================================
RCS file: src/language/tests/check-model.h
diff -N src/language/tests/check-model.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/language/tests/check-model.h 14 Apr 2007 05:04:23 -0000 1.1.2.1
@@ -0,0 +1,32 @@
+/* PSPP - computes sample statistics.
+ 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 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. */
+
+#ifndef LANGUAGE_TESTS_CHECK_MODEL
+#define LANGUAGE_TESTS_CHECK_MODEL 1
+
+#include <stdbool.h>
+
+struct lexer;
+struct mc_options;
+struct mc_results;
+
+bool check_model (struct lexer *lexer,
+ struct mc_results *(*checker) (struct mc_options *, void *),
+ void *aux);
+
+#endif /* check-model.h */
Index: src/language/tests/check-model.q
===================================================================
RCS file: src/language/tests/check-model.q
diff -N src/language/tests/check-model.q
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/language/tests/check-model.q 14 Apr 2007 05:04:23 -0000 1.1.2.1
@@ -0,0 +1,228 @@
+/* PSPP - computes sample statistics.
+ 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 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. */
+
+#include <config.h>
+
+#include <language/tests/check-model.h>
+
+#include <errno.h>
+
+#include <libpspp/model-checker.h>
+#include <language/lexer/lexer.h>
+
+#include "error.h"
+#include "fwriteerror.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+/* (headers) */
+
+/* (specification)
+ "CHECK MODEL" (chm_):
+ search=strategy:broad/deep/random,
+ :mxd(n:max_depth),
+ :hash(n:hash_bits);
+ queue=:limit(n:queue_limit,"%s>0"),
+ drop:newest/oldest/random;
+ seed=integer;
+ stop=:states(n:max_unique_states,"%s>0"),
+ :errors(n:max_errors),
+ :timeout(d:time_limit,"%s>0");
+ progress=progress:none/dots/fancy;
+ output=:verbosity(n:verbosity),
+ :errverbosity(n:err_verbosity),
+ :file(s:output_file).
+*/
+/* (declarations) */
+/* (functions) */
+
+static struct mc_options *parse_options (struct lexer *);
+static void print_results (const struct mc_results *, FILE *);
+
+bool
+check_model (struct lexer *lexer,
+ struct mc_results *(*checker) (struct mc_options *, void *aux),
+ void *aux)
+{
+ struct mc_options *options;
+ struct mc_results *results;
+ FILE *output_file;
+ bool ok;
+
+ options = parse_options (lexer);
+ if (options == NULL)
+ return false;
+ output_file = mc_options_get_output_file (options);
+
+ results = checker (options, aux);
+
+ print_results (results, output_file);
+
+ if (output_file != stdout && output_file != stderr)
+ {
+ if (fwriteerror (output_file) < 0)
+ {
+ /* We've already discarded the name of the output file.
+ Big deal. */
+ error (0, errno, "error closing output file");
+ }
+ }
+
+ ok = mc_results_get_error_count (results) == 0;
+ mc_results_destroy (results);
+
+ return ok;
+}
+
+static bool
+fancy_progress (struct mc *mc)
+{
+ const struct mc_results *results = mc_get_results (mc);
+ if (mc_results_get_stop_reason (results) == MC_CONTINUING)
+ fprintf (stderr, "Processed %d unique states, max depth %d, "
+ "dropped %d duplicates...\r",
+ mc_results_get_unique_state_count (results),
+ mc_results_get_max_depth_reached (results),
+ mc_results_get_duplicate_dropped_states (results));
+ else
+ putc ('\n', stderr);
+ return true;
+}
+
+static struct mc_options *
+parse_options (struct lexer *lexer)
+{
+ struct cmd_check_model cmd;
+ struct mc_options *options;
+
+ if (!parse_check_model (lexer, NULL, &cmd, NULL))
+ return NULL;
+
+ options = mc_options_create ();
+ if (cmd.strategy != -1)
+ mc_options_set_strategy (options,
+ cmd.strategy == CHM_BROAD ? MC_BROAD
+ : cmd.strategy == CHM_DEEP ? MC_DEEP
+ : cmd.strategy == CHM_RANDOM ? MC_RANDOM
+ : -1);
+ if (cmd.max_depth != NOT_LONG)
+ mc_options_set_max_depth (options, cmd.max_depth);
+ if (cmd.hash_bits != NOT_LONG)
+ {
+ int hash_bits;
+ mc_options_set_hash_bits (options, cmd.hash_bits);
+ hash_bits = mc_options_get_hash_bits (options);
+ if (hash_bits != cmd.hash_bits)
+ msg (SW, _("Hash bits adjusted to %d."), hash_bits);
+ }
+ if (cmd.queue_limit != NOT_LONG)
+ mc_options_set_queue_limit (options, cmd.queue_limit);
+ if (cmd.drop != -1)
+ {
+ enum mc_queue_limit_strategy drop
+ = (cmd.drop == CHM_NEWEST ? MC_DROP_NEWEST
+ : cmd.drop == CHM_OLDEST ? MC_DROP_OLDEST
+ : cmd.drop == CHM_RANDOM ? MC_DROP_RANDOM
+ : -1);
+ mc_options_set_queue_limit_strategy (options, drop);
+ }
+ if (cmd.sbc_search > 0)
+ mc_options_set_seed (options, cmd.n_seed[0]);
+ if (cmd.max_unique_states != NOT_LONG)
+ mc_options_set_max_unique_states (options, cmd.max_unique_states);
+ if (cmd.max_errors != NOT_LONG)
+ mc_options_set_max_errors (options, cmd.max_errors);
+ if (cmd.time_limit != SYSMIS)
+ mc_options_set_time_limit (options, cmd.time_limit);
+ if (cmd.verbosity != NOT_LONG)
+ mc_options_set_verbosity (options, cmd.verbosity);
+ if (cmd.err_verbosity != NOT_LONG)
+ mc_options_set_failure_verbosity (options, cmd.err_verbosity);
+ if (cmd.progress != -1)
+ {
+ if (cmd.progress == CHM_NONE)
+ mc_options_set_progress_usec (options, 0);
+ else if (cmd.progress == CHM_DOTS)
+ {
+ /* Nothing to do: that's the default anyway. */
+ }
+ else if (cmd.progress == CHM_FANCY)
+ mc_options_set_progress_func (options, fancy_progress);
+ }
+ if (cmd.output_file != NULL)
+ {
+ FILE *output_file = fopen (cmd.output_file, "w");
+ if (output_file == NULL)
+ {
+ error (0, errno, _("error opening \"%s\" for writing"),
+ cmd.output_file);
+ free_check_model (&cmd);
+ mc_options_destroy (options);
+ return NULL;
+ }
+ mc_options_set_output_file (options, output_file);
+ }
+
+ return options;
+}
+
+static void
+print_results (const struct mc_results *results, FILE *f)
+{
+ enum mc_stop_reason reason = mc_results_get_stop_reason (results);
+
+ fputs ("\n"
+ "MODEL CHECKING SUMMARY\n"
+ "----------------------\n\n", f);
+
+ fprintf (f, "Stopped by: %s\n",
+ reason == MC_SUCCESS ? "state space exhaustion"
+ : reason == MC_MAX_UNIQUE_STATES ? "reaching max unique states"
+ : reason == MC_MAX_ERROR_COUNT ? "reaching max error count"
+ : reason == MC_TIMEOUT ? "reaching time limit"
+ : reason == MC_INTERRUPTED ? "user interruption"
+ : "unknown reason");
+ fprintf (f, "Errors found: %d\n\n", mc_results_get_error_count (results));
+
+ fprintf (f, "Unique states checked: %d\n",
+ mc_results_get_unique_state_count (results));
+ fprintf (f, "Maximum depth reached: %d\n",
+ mc_results_get_max_depth_reached (results));
+ fprintf (f, "Mean depth reached: %.2f\n\n",
+ mc_results_get_mean_depth_reached (results));
+
+ fprintf (f, "Dropped duplicate states: %d\n",
+ mc_results_get_duplicate_dropped_states (results));
+ fprintf (f, "Dropped off-path states: %d\n",
+ mc_results_get_off_path_dropped_states (results));
+ fprintf (f, "Dropped too-deep states: %d\n",
+ mc_results_get_depth_dropped_states (results));
+ fprintf (f, "Dropped queue-overflow states: %d\n",
+ mc_results_get_queue_dropped_states (results));
+ fprintf (f, "Checked states still queued when stopped: %d\n",
+ mc_results_get_queued_unprocessed_states (results));
+ fprintf (f, "Maximum queue length reached: %d\n\n",
+ mc_results_get_max_queue_length (results));
+
+ fprintf (f, "Runtime: %.2f seconds\n",
+ mc_results_get_duration (results));
+
+ putc ('\n', f);
+}
Index: src/language/tests/datasheet-test.c
===================================================================
RCS file: src/language/tests/datasheet-test.c
diff -N src/language/tests/datasheet-test.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/language/tests/datasheet-test.c 14 Apr 2007 05:04:23 -0000 1.1.2.1
@@ -0,0 +1,87 @@
+/* PSPP - computes sample statistics.
+ 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 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. */
+
+#include <config.h>
+
+#include <data/datasheet.h>
+
+#include <language/command.h>
+#include <language/lexer/lexer.h>
+#include <language/tests/check-model.h>
+#include <libpspp/array.h>
+#include <libpspp/assertion.h>
+
+#include "error.h"
+#include "xalloc.h"
+
+static bool
+parse_coordinates (struct lexer *lexer, int *rows, int *cols)
+{
+ lex_match (lexer, '=');
+ lex_match (lexer, '(');
+
+ if (!lex_force_int (lexer))
+ return false;
+ *rows = lex_integer (lexer);
+ lex_get (lexer);
+
+ lex_match (lexer, ',');
+
+ if (!lex_force_int (lexer))
+ return false;
+ *cols = lex_integer (lexer);
+ lex_get (lexer);
+
+ lex_match (lexer, ')');
+ return true;
+}
+
+int
+cmd_debug_datasheet (struct lexer *lexer,
+ struct dataset *dataset UNUSED)
+{
+ struct datasheet_test_params params;
+ bool ok;
+
+ params.max_rows = 4;
+ params.max_cols = 4;
+ params.backing_rows = 0;
+ params.backing_cols = 0;
+
+ for (;;)
+ {
+ if (lex_match_id (lexer, "MAX"))
+ {
+ if (!parse_coordinates (lexer, ¶ms.max_rows, ¶ms.max_cols))
+ return CMD_FAILURE;
+ }
+ else if (lex_match_id (lexer, "BACKING"))
+ {
+ if (!parse_coordinates (lexer,
+ ¶ms.backing_rows, ¶ms.backing_cols))
+ return CMD_FAILURE;
+ }
+ else
+ break;
+ lex_match (lexer, '/');
+ }
+
+ ok = check_model (lexer, datasheet_test, ¶ms);
+ fprintf (stderr, "Datasheet test %s.\n", ok ? "successful" : "failed");
+ return ok ? lex_end_of_command (lexer) : CMD_FAILURE;
+}
Index: src/libpspp/model-checker.c
===================================================================
RCS file: src/libpspp/model-checker.c
diff -N src/libpspp/model-checker.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/libpspp/model-checker.c 14 Apr 2007 05:04:23 -0000 1.1.2.1
@@ -0,0 +1,1015 @@
+/* PSPP - computes sample statistics.
+ 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 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. */
+
+#include <config.h>
+
+#include <libpspp/model-checker.h>
+
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <libpspp/bit-vector.h>
+#include <libpspp/compiler.h>
+#include <libpspp/deque.h>
+#include <libpspp/str.h>
+#include <math/moments.h>
+
+#include "error.h"
+#include "minmax.h"
+#include "xalloc.h"
+
+/* Search options. */
+struct mc_options
+ {
+ enum mc_strategy strategy;
+ int max_depth;
+ int hash_bits;
+ unsigned int seed;
+
+ int queue_limit;
+ enum mc_queue_limit_strategy queue_limit_strategy;
+
+ int max_unique_states;
+ int max_errors;
+ double time_limit;
+
+ int verbosity;
+ int failure_verbosity;
+ FILE *output_file;
+
+ int progress_usec;
+ mc_progress_func *progress_func;
+
+ void *aux;
+ };
+
+static bool
+default_progress (struct mc *mc)
+{
+ if (mc_results_get_stop_reason (mc_get_results (mc)) == MC_CONTINUING)
+ putc ('.', stderr);
+ else
+ putc ('\n', stderr);
+ return true;
+}
+
+static bool
+null_progress (struct mc *mc UNUSED)
+{
+ return true;
+}
+
+struct mc_options *
+mc_options_create (void)
+{
+ struct mc_options *options = xmalloc (sizeof *options);
+
+ options->strategy = MC_BROAD;
+ options->max_depth = INT_MAX;
+ options->hash_bits = 20;
+ options->seed = 0;
+
+ options->queue_limit = 10000;
+ options->queue_limit_strategy = MC_DROP_RANDOM;
+
+ options->max_unique_states = INT_MAX;
+ options->max_errors = 1;
+ options->time_limit = 0.0;
+
+ options->verbosity = 1;
+ options->failure_verbosity = 2;
+ options->output_file = stdout;
+ options->progress_usec = 250000;
+ options->progress_func = default_progress;
+
+ options->aux = NULL;
+
+ return options;
+}
+
+struct mc_options *
+mc_options_clone (const struct mc_options *options)
+{
+ return xmemdup (options, sizeof *options);
+}
+
+void
+mc_options_destroy (struct mc_options *options)
+{
+ free (options);
+}
+
+enum mc_strategy
+mc_options_get_strategy (const struct mc_options *options)
+{
+ return options->strategy;
+}
+
+void
+mc_options_set_strategy (struct mc_options *options, enum mc_strategy strategy)
+{
+ assert (strategy == MC_BROAD
+ || strategy == MC_DEEP
+ || strategy == MC_RANDOM);
+ options->strategy = strategy;
+}
+
+unsigned int
+mc_options_get_seed (const struct mc_options *options)
+{
+ return options->seed;
+}
+
+void
+mc_options_set_seed (struct mc_options *options, unsigned int seed)
+{
+ options->seed = seed;
+}
+
+int
+mc_options_get_max_depth (const struct mc_options *options)
+{
+ return options->max_depth;
+}
+
+void
+mc_options_set_max_depth (struct mc_options *options, int max_depth)
+{
+ options->max_depth = max_depth;
+}
+
+int
+mc_options_get_hash_bits (const struct mc_options *options)
+{
+ return options->hash_bits;
+}
+
+void
+mc_options_set_hash_bits (struct mc_options *options, int hash_bits)
+{
+ assert (hash_bits >= 0);
+ options->hash_bits = MIN (hash_bits, 31);
+}
+
+int
+mc_options_get_queue_limit (const struct mc_options *options)
+{
+ return options->queue_limit;
+}
+
+void
+mc_options_set_queue_limit (struct mc_options *options, int queue_limit)
+{
+ assert (queue_limit > 0);
+ options->queue_limit = queue_limit;
+}
+
+enum mc_queue_limit_strategy
+mc_options_get_queue_limit_strategy (const struct mc_options *options)
+{
+ return options->queue_limit_strategy;
+}
+
+void
+mc_options_set_queue_limit_strategy (struct mc_options *options,
+ enum mc_queue_limit_strategy strategy)
+{
+ assert (strategy == MC_DROP_NEWEST
+ || strategy == MC_DROP_OLDEST
+ || strategy == MC_DROP_RANDOM);
+ options->queue_limit_strategy = strategy;
+}
+
+int
+mc_options_get_max_unique_states (const struct mc_options *options)
+{
+ return options->max_unique_states;
+}
+
+void
+mc_options_set_max_unique_states (struct mc_options *options,
+ int max_unique_states)
+{
+ options->max_unique_states = max_unique_states;
+}
+
+int
+mc_options_get_max_errors (const struct mc_options *options)
+{
+ return options->max_errors;
+}
+
+void
+mc_options_set_max_errors (struct mc_options *options, int max_errors)
+{
+ options->max_errors = max_errors;
+}
+
+double
+mc_options_get_time_limit (const struct mc_options *options)
+{
+ return options->time_limit;
+}
+
+void
+mc_options_set_time_limit (struct mc_options *options, double time_limit)
+{
+ assert (time_limit >= 0.0);
+ options->time_limit = time_limit;
+}
+
+int
+mc_options_get_verbosity (const struct mc_options *options)
+{
+ return options->verbosity;
+}
+
+void
+mc_options_set_verbosity (struct mc_options *options, int verbosity)
+{
+ options->verbosity = verbosity;
+}
+
+int
+mc_options_get_failure_verbosity (const struct mc_options *options)
+{
+ return options->failure_verbosity;
+}
+
+void
+mc_options_set_failure_verbosity (struct mc_options *options,
+ int failure_verbosity)
+{
+ options->failure_verbosity = failure_verbosity;
+}
+
+FILE *
+mc_options_get_output_file (const struct mc_options *options)
+{
+ return options->output_file;
+}
+
+void
+mc_options_set_output_file (struct mc_options *options,
+ FILE *output_file)
+{
+ options->output_file = output_file;
+}
+
+int
+mc_options_get_progress_usec (const struct mc_options *options)
+{
+ return options->progress_usec;
+}
+
+void
+mc_options_set_progress_usec (struct mc_options *options, int progress_usec)
+{
+ assert (progress_usec >= 0);
+ options->progress_usec = progress_usec;
+}
+
+mc_progress_func *
+mc_options_get_progress_func (const struct mc_options *options)
+{
+ return options->progress_func;
+}
+
+void
+mc_options_set_progress_func (struct mc_options *options,
+ mc_progress_func *progress_func)
+{
+ assert (options->progress_func != NULL);
+ options->progress_func = progress_func;
+}
+
+void *
+mc_options_get_aux (const struct mc_options *options)
+{
+ return options->aux;
+}
+
+void
+mc_options_set_aux (struct mc_options *options, void *aux)
+{
+ options->aux = aux;
+}
+
+static void
+check_options (const struct mc_options *options)
+{
+ assert (options->queue_limit_strategy != MC_DROP_OLDEST
+ || options->strategy != MC_RANDOM);
+}
+
+struct mc_results
+ {
+ enum mc_stop_reason stop_reason;
+ int unique_state_count;
+ int error_count;
+
+ int max_depth_reached;
+ struct moments1 *depth_moments;
+
+ int *error_path;
+ size_t error_path_len;
+
+ int duplicate_dropped_states;
+ int off_path_dropped_states;
+ int depth_dropped_states;
+ int queue_dropped_states;
+ int queued_unprocessed_states;
+ int max_queue_length;
+
+ struct timeval start;
+ struct timeval end;
+ };
+
+static struct mc_results *
+mc_results_create (void)
+{
+ struct mc_results *results = xcalloc (1, sizeof (struct mc_results));
+ results->stop_reason = MC_CONTINUING;
+ results->depth_moments = moments1_create (MOMENT_MEAN);
+ return results;
+}
+
+void
+mc_results_destroy (struct mc_results *results)
+{
+ if (results != NULL)
+ {
+ moments1_destroy (results->depth_moments);
+ free (results->error_path);
+ free (results);
+ }
+}
+
+enum mc_stop_reason
+mc_results_get_stop_reason (const struct mc_results *results)
+{
+ return results->stop_reason;
+}
+
+int
+mc_results_get_unique_state_count (const struct mc_results *results)
+{
+ return results->unique_state_count;
+}
+
+int
+mc_results_get_error_count (const struct mc_results *results)
+{
+ return results->error_count;
+}
+
+int
+mc_results_get_max_depth_reached (const struct mc_results *results)
+{
+ return results->max_depth_reached;
+}
+
+double
+mc_results_get_mean_depth_reached (const struct mc_results *results)
+{
+ double mean;
+ moments1_calculate (results->depth_moments, NULL, &mean, NULL, NULL, NULL);
+ return mean != SYSMIS ? mean : 0.0;
+}
+
+bool
+mc_results_get_last_error_path (const struct mc_results *results,
+ const int **path, size_t *path_len)
+{
+ if (results->error_count > 0)
+ {
+ *path = results->error_path;
+ *path_len = results->error_path_len;
+ return true;
+ }
+ else
+ {
+ *path = NULL;
+ *path_len = 0;
+ return false;
+ }
+}
+
+int
+mc_results_get_duplicate_dropped_states (const struct mc_results *results)
+{
+ return results->duplicate_dropped_states;
+}
+
+int
+mc_results_get_off_path_dropped_states (const struct mc_results *results)
+{
+ return results->off_path_dropped_states;
+}
+
+int
+mc_results_get_depth_dropped_states (const struct mc_results *results)
+{
+ return results->depth_dropped_states;
+}
+
+int
+mc_results_get_queue_dropped_states (const struct mc_results *results)
+{
+ return results->queue_dropped_states;
+}
+
+int
+mc_results_get_queued_unprocessed_states (const struct mc_results *results)
+{
+ return results->queued_unprocessed_states;
+}
+
+int
+mc_results_get_max_queue_length (const struct mc_results *results)
+{
+ return results->max_queue_length;
+}
+
+struct timeval
+mc_results_get_start (const struct mc_results *results)
+{
+ return results->start;
+}
+
+struct timeval
+mc_results_get_end (const struct mc_results *results)
+{
+ assert (results->stop_reason != MC_CONTINUING);
+ return results->end;
+}
+
+static double
+timeval_subtract (struct timeval x, struct timeval y)
+{
+ /* From libc.info. */
+ double difference;
+
+ /* Perform the carry for the later subtraction by updating Y. */
+ if (x.tv_usec < y.tv_usec) {
+ int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
+ y.tv_usec -= 1000000 * nsec;
+ y.tv_sec += nsec;
+ }
+ if (x.tv_usec - y.tv_usec > 1000000) {
+ int nsec = (x.tv_usec - y.tv_usec) / 1000000;
+ y.tv_usec += 1000000 * nsec;
+ y.tv_sec -= nsec;
+ }
+
+ /* Compute the time remaining to wait.
+ `tv_usec' is certainly positive. */
+ difference = (x.tv_sec - y.tv_sec) + (x.tv_usec - y.tv_usec) / 1000000.0;
+ if (x.tv_sec < y.tv_sec)
+ difference = -difference;
+ return difference;
+}
+
+double
+mc_results_get_duration (const struct mc_results *results)
+{
+ assert (results->stop_reason != MC_CONTINUING);
+ return timeval_subtract (results->end, results->start);
+}
+
+struct mc
+ {
+ const struct mc_class *class;
+ struct mc_options *options;
+ struct mc_results *results;
+
+ unsigned char *seen_states;
+
+ struct deque state_deque;
+ struct mc_state **states;
+
+ struct mc_state *state;
+ int substate;
+
+ bool named_state;
+ bool error_state;
+
+ int include_substate;
+
+ unsigned int progress;
+ unsigned int next_progress;
+ unsigned int prev_progress;
+ struct timeval prev_progress_time;
+
+ bool interrupted;
+ bool *saved_interrupted;
+ sighandler_t saved_sigint;
+ };
+
+struct mc_state
+ {
+ int depth;
+ int *sequence;
+ void *data;
+ };
+
+static void
+free_state (const struct mc_class *class, struct mc_state *state)
+{
+ if (state->data != NULL)
+ class->destroy (state->data);
+ free (state->sequence);
+ free (state);
+}
+
+static void
+stop (struct mc *mc, enum mc_stop_reason stop_reason)
+{
+ if (mc->results->stop_reason == MC_CONTINUING)
+ mc->results->stop_reason = stop_reason;
+}
+
+static struct mc_state *
+make_child_state (const struct mc *mc, void *data)
+{
+ const struct mc_state *parent = mc->state;
+ struct mc_state *new = xmalloc (sizeof *new);
+
+ new->depth = parent->depth + 1;
+
+ new->sequence = xnmalloc (new->depth, sizeof *new->sequence);
+ memcpy (new->sequence, parent->sequence,
+ (new->depth - 1) * sizeof *parent->sequence);
+ new->sequence[new->depth - 1] = mc->substate;
+
+ new->data = data;
+
+ return new;
+}
+
+static size_t
+random_queue_index (struct mc *mc)
+{
+ assert (!deque_is_empty (&mc->state_deque));
+ return deque_front (&mc->state_deque,
+ rand () % deque_count (&mc->state_deque));
+}
+
+static void
+enqueue_state (struct mc *mc, struct mc_state *new)
+{
+ size_t idx;
+
+ if (new->depth > mc->results->max_depth_reached)
+ mc->results->max_depth_reached = new->depth;
+ moments1_add (mc->results->depth_moments, new->depth, 1.0);
+
+ if (deque_count (&mc->state_deque) < mc->options->queue_limit)
+ {
+ /* Add new state to queue. */
+ if (deque_is_full (&mc->state_deque))
+ mc->states = deque_expand (&mc->state_deque,
+ mc->states, sizeof *mc->states);
+ switch (mc->options->strategy)
+ {
+ case MC_BROAD:
+ idx = deque_push_back (&mc->state_deque);
+ break;
+ case MC_DEEP:
+ idx = deque_push_front (&mc->state_deque);
+ break;
+ case MC_RANDOM:
+ if (!deque_is_empty (&mc->state_deque))
+ {
+ idx = random_queue_index (mc);
+ mc->states[deque_push_front (&mc->state_deque)]
+ = mc->states[idx];
+ }
+ else
+ idx = deque_push_front (&mc->state_deque);
+ break;
+ default:
+ NOT_REACHED ();
+ }
+ if (deque_count (&mc->state_deque) > mc->results->max_queue_length)
+ mc->results->max_queue_length = deque_count (&mc->state_deque);
+ }
+ else
+ {
+ /* Queue has reached limit, so replace an existing
+ state. */
+ assert (!deque_is_empty (&mc->state_deque));
+ mc->results->queue_dropped_states++;
+ switch (mc->options->queue_limit_strategy)
+ {
+ case MC_DROP_NEWEST:
+ free_state (mc->class, new);
+ return;
+ case MC_DROP_OLDEST:
+ switch (mc->options->strategy)
+ {
+ case MC_BROAD:
+ idx = deque_front (&mc->state_deque, 0);
+ break;
+ case MC_DEEP:
+ idx = deque_back (&mc->state_deque, 0);
+ break;
+ case MC_DROP_RANDOM:
+ default:
+ NOT_REACHED ();
+ }
+ break;
+ case MC_DROP_RANDOM:
+ idx = random_queue_index (mc);
+ break;
+ default:
+ NOT_REACHED ();
+ }
+ free_state (mc->class, mc->states[idx]);
+ }
+ mc->states[idx] = new;
+}
+
+static const char *
+state_path_string (const struct mc *mc)
+{
+ static struct string s = DS_EMPTY_INITIALIZER;
+ size_t i;
+
+ ds_clear (&s);
+ for (i = 0; i < mc->state->depth; i++)
+ ds_put_format (&s, "%d ", mc->state->sequence[i]);
+ ds_put_format (&s, "%d", mc->substate);
+
+ return ds_cstr (&s);
+}
+
+static void
+do_error_state (struct mc *mc)
+{
+ mc->results->error_count++;
+ if (mc->results->error_count >= mc->options->max_errors)
+ stop (mc, MC_MAX_ERROR_COUNT);
+
+ if (mc->options->failure_verbosity > mc->options->verbosity)
+ {
+ struct mc_options *path_options;
+ struct mc_state *state;
+
+ fprintf (mc->options->output_file, "[%s] retracing error path:\n",
+ state_path_string (mc));
+ path_options = mc_options_clone (mc->options);
+ mc_options_set_verbosity (path_options, mc->options->failure_verbosity);
+ mc_options_set_failure_verbosity (path_options, 0);
+
+ state = make_child_state (mc, NULL);
+
+ mc_results_destroy (mc_follow_path (mc->class, path_options,
+ state->sequence, state->depth));
+
+ free_state (mc->class, state);
+ putc ('\n', mc->options->output_file);
+ }
+}
+
+void
+mc_vname_state (struct mc *mc, const char *name, va_list args)
+{
+ if (mc->named_state)
+ fprintf (mc->options->output_file, " [%s] warning: duplicate call "
+ "to mc_name_state (missing call to mc_add_state?)\n",
+ state_path_string (mc));
+ mc->named_state = true;
+
+ if (mc->options->verbosity > 1)
+ {
+
+ fprintf (mc->options->output_file, " [%s] ", state_path_string (mc));
+
+ vfprintf (mc->options->output_file, name, args);
+
+ putc ('\n', mc->options->output_file);
+ }
+}
+
+void
+mc_name_state (struct mc *mc, const char *name, ...)
+{
+ va_list args;
+
+ va_start (args, name);
+ mc_vname_state (mc, name, args);
+ va_end (args);
+}
+
+static void
+next_substate (struct mc *mc)
+{
+ mc->substate++;
+ mc->named_state = false;
+
+ if (++mc->progress >= mc->next_progress)
+ {
+ struct timeval now;
+ double elapsed, delta;
+
+ if (mc->results->stop_reason == MC_CONTINUING
+ && !mc->options->progress_func (mc))
+ stop (mc, MC_INTERRUPTED);
+
+ gettimeofday (&now, NULL);
+
+ if (mc->options->time_limit > 0.0
+ && (timeval_subtract (now, mc->results->start)
+ > mc->options->time_limit))
+ stop (mc, MC_TIMEOUT);
+
+ elapsed = timeval_subtract (now, mc->prev_progress_time);
+ if (elapsed > 0.0)
+ {
+ /* Re-estimate the amount of progress to take
+ progress_usec microseconds. */
+ unsigned int progress = mc->progress - mc->prev_progress;
+ double progress_sec = mc->options->progress_usec / 1000000.0;
+ delta = progress / elapsed * progress_sec;
+ }
+ else
+ {
+ /* No measurable time at all elapsed during that amount
+ of progress. Try doubling the amount of progress
+ required. */
+ delta = (mc->progress - mc->prev_progress) * 2;
+ }
+
+ if (delta > 0.0 && delta + mc->progress + 1.0 < UINT_MAX)
+ mc->next_progress = mc->progress + delta + 1.0;
+ else
+ mc->next_progress = mc->progress + (mc->progress - mc->prev_progress);
+
+ mc->prev_progress = mc->progress;
+ mc->prev_progress_time = now;
+ }
+}
+
+bool
+mc_discard_dup_state (struct mc *mc, unsigned int hash)
+{
+ if (mc->options->hash_bits > 0)
+ {
+ hash &= (1u << mc->options->hash_bits) - 1;
+ if (TEST_BIT (mc->seen_states, hash))
+ {
+ mc->results->duplicate_dropped_states++;
+ next_substate (mc);
+ return true;
+ }
+ SET_BIT (mc->seen_states, hash);
+ }
+ return false;
+}
+
+void
+mc_add_state (struct mc *mc, void *data)
+{
+ if (!mc->named_state)
+ fprintf (mc->options->output_file, " [%s] warning: unnamed state\n",
+ state_path_string (mc));
+
+ if (mc->results->stop_reason != MC_CONTINUING)
+ {
+ /* Nothing to do. */
+ }
+ else if (mc->error_state)
+ do_error_state (mc);
+ else if (mc->include_substate >= 0
+ && mc->substate != mc->include_substate)
+ mc->results->off_path_dropped_states++;
+ else if (mc->state->depth + 1 > mc->options->max_depth)
+ mc->results->depth_dropped_states++;
+ else
+ {
+ /* This is the common case. */
+ mc->results->unique_state_count++;
+ if (mc->results->unique_state_count >= mc->options->max_unique_states)
+ stop (mc, MC_MAX_UNIQUE_STATES);
+ enqueue_state (mc, make_child_state (mc, data));
+ next_substate (mc);
+ return;
+ }
+
+ mc->error_state = false;
+ mc->class->destroy (data);
+ next_substate (mc);
+}
+
+static bool *interrupted = NULL;
+
+static void
+sigint_handler (int signum UNUSED)
+{
+ *interrupted = true;
+}
+
+static void
+init_mc (struct mc *mc, const struct mc_class *class,
+ struct mc_options *options)
+{
+ struct mc_state null_state = {0, NULL, NULL};
+
+ mc->class = class;
+
+ mc->options = options != NULL ? options : mc_options_create ();
+ check_options (mc->options);
+
+ if (mc->options->time_limit > 0.0 && mc->options->progress_usec == 0)
+ {
+ mc->options->progress_usec = 250000;
+ mc->options->progress_func = null_progress;
+ }
+
+ mc->results = mc_results_create ();
+ gettimeofday (&mc->results->start, NULL);
+
+ if (mc->options->hash_bits > 0)
+ mc->seen_states = xcalloc (1, (1u << mc->options->hash_bits) / CHAR_BIT);
+ else
+ mc->seen_states = NULL;
+ mc->states = NULL;
+ deque_init_null (&mc->state_deque);
+
+ mc->substate = 0;
+ mc->state = &null_state;
+
+ mc->include_substate = -1;
+
+ mc->named_state = false;
+ mc->error_state = false;
+
+ mc->progress = 0;
+ mc->next_progress = mc->options->progress_usec != 0 ? 100 : UINT_MAX;
+ mc->prev_progress = 0;
+ mc->prev_progress_time = mc->results->start;
+
+ if (mc->options->strategy == MC_RANDOM
+ || options->queue_limit_strategy == MC_DROP_RANDOM)
+ srand (mc->options->seed);
+
+ mc->interrupted = false;
+ mc->saved_interrupted = interrupted;
+ interrupted = &mc->interrupted;
+ mc->saved_sigint = signal (SIGINT, sigint_handler);
+
+ class->init (mc);
+}
+
+static void
+finish_mc (struct mc *mc)
+{
+ signal (SIGINT, mc->saved_sigint);
+ interrupted = mc->saved_interrupted;
+
+ stop (mc, MC_SUCCESS);
+ gettimeofday (&mc->results->end, NULL);
+
+ mc->results->queued_unprocessed_states = deque_count (&mc->state_deque);
+ while (!deque_is_empty (&mc->state_deque))
+ {
+ struct mc_state *state = mc->states[deque_pop_front (&mc->state_deque)];
+ free_state (mc->class, state);
+ }
+
+ mc->options->progress_func (mc);
+
+ free (mc->options);
+ free (mc->states);
+ free (mc->seen_states);
+}
+
+static void
+periodic_mc (struct mc *mc)
+{
+ if (mc->interrupted)
+ stop (mc, MC_INTERRUPTED);
+}
+
+struct mc_results *
+mc_run (const struct mc_class *class, struct mc_options *options)
+{
+ struct mc mc;
+
+ init_mc (&mc, class, options);
+ while (!deque_is_empty (&mc.state_deque)
+ && mc.results->stop_reason == MC_CONTINUING)
+ {
+ mc.substate = 0;
+ mc.state = mc.states[deque_pop_front (&mc.state_deque)];
+ class->mutate (&mc, mc.state->data);
+ free_state (class, mc.state);
+ periodic_mc (&mc);
+ }
+ finish_mc (&mc);
+
+ return mc.results;
+}
+
+/* Should return "void *" reached by path? */
+struct mc_results *
+mc_follow_path (const struct mc_class *class, struct mc_options *options,
+ int *path, size_t path_len)
+{
+ struct mc mc;
+ size_t i;
+
+ init_mc (&mc, class, options);
+ for (i = 1; i < path_len; i++)
+ {
+ if (deque_is_empty (&mc.state_deque))
+ {
+ printf ("No state found after %zu elements in path (out of %zu)\n",
+ i, path_len);
+ break;
+ }
+ assert (deque_count (&mc.state_deque) == 1);
+
+ mc.substate = 0;
+ mc.state = mc.states[deque_pop_front (&mc.state_deque)];
+ mc.include_substate = path[i];
+ class->mutate (&mc, mc.state->data);
+ free_state (class, mc.state);
+
+ periodic_mc (&mc);
+ }
+ finish_mc (&mc);
+
+ return mc.results;
+}
+
+void
+mc_error (struct mc *mc, const char *message, ...)
+{
+ va_list args;
+
+ if (mc->results->stop_reason != MC_CONTINUING)
+ return;
+
+ if (mc->options->verbosity > 1)
+ fputs (" ", mc->options->output_file);
+ fprintf (mc->options->output_file, "[%s] error: ", state_path_string (mc));
+ va_start (args, message);
+ vfprintf (mc->options->output_file, message, args);
+ va_end (args);
+ putc ('\n', mc->options->output_file);
+
+ mc->error_state = true;
+}
+
+bool
+mc_include_state (struct mc *mc)
+{
+ if (mc->results->stop_reason != MC_CONTINUING)
+ return false;
+ if (mc->include_substate >= 0 && mc->substate != mc->include_substate)
+ {
+ mc->substate++;
+ return false;
+ }
+ return true;
+}
+
+const struct mc_options *
+mc_get_options (struct mc *mc)
+{
+ return mc->options;
+}
+
+const struct mc_results *
+mc_get_results (struct mc *mc)
+{
+ return mc->results;
+}
+
+void *
+mc_get_aux (struct mc *mc)
+{
+ return mc_options_get_aux (mc_get_options (mc));
+}
Index: src/libpspp/model-checker.h
===================================================================
RCS file: src/libpspp/model-checker.h
diff -N src/libpspp/model-checker.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/libpspp/model-checker.h 14 Apr 2007 05:04:23 -0000 1.1.2.1
@@ -0,0 +1,150 @@
+/* PSPP - computes sample statistics.
+ 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 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. */
+
+#ifndef LIBPSPP_MODEL_CHECKER_H
+#define LIBPSPP_MODEL_CHECKER_H 1
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include <libpspp/compiler.h>
+
+struct mc;
+
+/* Provided by each client of the model checker. */
+struct mc_class
+ {
+ void (*init) (struct mc *);
+ void (*mutate) (struct mc *, const void *);
+ void (*destroy) (void *);
+ };
+
+struct mc_options *mc_options_create (void);
+struct mc_options *mc_options_clone (const struct mc_options *);
+void mc_options_destroy (struct mc_options *);
+
+/* Search strategy. */
+enum mc_strategy
+ {
+ MC_BROAD, /* Breadth-first search. */
+ MC_DEEP, /* Depth-first search. */
+ MC_RANDOM /* Randomly ordered search. */
+ };
+
+enum mc_strategy mc_options_get_strategy (const struct mc_options *);
+void mc_options_set_strategy (struct mc_options *, enum mc_strategy);
+unsigned int mc_options_get_seed (const struct mc_options *);
+void mc_options_set_seed (struct mc_options *, unsigned int seed);
+int mc_options_get_max_depth (const struct mc_options *);
+void mc_options_set_max_depth (struct mc_options *, int max_depth);
+int mc_options_get_hash_bits (const struct mc_options *);
+void mc_options_set_hash_bits (struct mc_options *, int hash_bits);
+
+enum mc_queue_limit_strategy
+ {
+ MC_DROP_NEWEST,
+ MC_DROP_OLDEST,
+ MC_DROP_RANDOM
+ };
+
+int mc_options_get_queue_limit (const struct mc_options *);
+void mc_options_set_queue_limit (struct mc_options *, int queue_limit);
+enum mc_queue_limit_strategy mc_options_get_queue_limit_strategy (
+ const struct mc_options *);
+void mc_options_set_queue_limit_strategy (struct mc_options *,
+ enum mc_queue_limit_strategy);
+
+int mc_options_get_max_unique_states (const struct mc_options *);
+void mc_options_set_max_unique_states (struct mc_options *,
+ int max_unique_states);
+int mc_options_get_max_errors (const struct mc_options *);
+void mc_options_set_max_errors (struct mc_options *, int max_errors);
+double mc_options_get_time_limit (const struct mc_options *);
+void mc_options_set_time_limit (struct mc_options *, double time_limit);
+
+int mc_options_get_verbosity (const struct mc_options *);
+void mc_options_set_verbosity (struct mc_options *, int verbosity);
+int mc_options_get_failure_verbosity (const struct mc_options *);
+void mc_options_set_failure_verbosity (struct mc_options *,
+ int failure_verbosity);
+FILE *mc_options_get_output_file (const struct mc_options *);
+void mc_options_set_output_file (struct mc_options *, FILE *);
+
+typedef bool mc_progress_func (struct mc *);
+int mc_options_get_progress_usec (const struct mc_options *);
+void mc_options_set_progress_usec (struct mc_options *, int progress_usec);
+mc_progress_func *mc_options_get_progress_func (const struct mc_options *);
+void mc_options_set_progress_func (struct mc_options *, mc_progress_func *);
+
+void *mc_options_get_aux (const struct mc_options *);
+void mc_options_set_aux (struct mc_options *, void *aux);
+
+enum mc_stop_reason
+ {
+ MC_CONTINUING,
+ MC_SUCCESS,
+ MC_MAX_UNIQUE_STATES,
+ MC_MAX_ERROR_COUNT,
+ MC_TIMEOUT,
+ MC_INTERRUPTED
+ };
+
+struct mc_results;
+
+void mc_results_destroy (struct mc_results *);
+
+enum mc_stop_reason mc_results_get_stop_reason (const struct mc_results *);
+int mc_results_get_unique_state_count (const struct mc_results *);
+int mc_results_get_error_count (const struct mc_results *);
+
+int mc_results_get_max_depth_reached (const struct mc_results *);
+double mc_results_get_mean_depth_reached (const struct mc_results *);
+
+bool mc_results_get_last_error_path (const struct mc_results *,
+ const int **path, size_t *path_len);
+
+int mc_results_get_duplicate_dropped_states (const struct mc_results *);
+int mc_results_get_off_path_dropped_states (const struct mc_results *);
+int mc_results_get_depth_dropped_states (const struct mc_results *);
+int mc_results_get_queue_dropped_states (const struct mc_results *);
+int mc_results_get_queued_unprocessed_states (const struct mc_results *);
+int mc_results_get_max_queue_length (const struct mc_results *);
+
+struct timeval mc_results_get_start (const struct mc_results *);
+struct timeval mc_results_get_end (const struct mc_results *);
+double mc_results_get_duration (const struct mc_results *);
+
+struct mc_results *mc_run (const struct mc_class *, struct mc_options *);
+struct mc_results *mc_follow_path (const struct mc_class *,
+ struct mc_options *,
+ int *path, size_t path_len);
+
+bool mc_include_state (struct mc *);
+bool mc_discard_dup_state (struct mc *, unsigned int hash);
+void mc_name_state (struct mc *, const char *, ...) PRINTF_FORMAT (2, 3);
+void mc_vname_state (struct mc *, const char *, va_list) PRINTF_FORMAT (2, 0);
+void mc_error (struct mc *, const char *, ...) PRINTF_FORMAT (2, 3);
+void mc_add_state (struct mc *, void *data);
+
+const struct mc_options *mc_get_options (struct mc *);
+const struct mc_results *mc_get_results (struct mc *);
+void *mc_get_aux (struct mc *);
+
+#endif /* libpspp/model-checker.h */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Pspp-cvs] pspp Smake src/data/automake.mk src/data/case-t... [simpler-proc],
Ben Pfaff <=