From: Ben Pfaff Date: Sun, 30 Nov 2008 23:23:20 +0000 (-0800) Subject: Replace case_ordering with subcase. X-Git-Tag: v0.7.1~50^2~4 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed09f0f21add5e56e8395a5e8589cda6f96420bf;p=pspp-builds.git Replace case_ordering with subcase. The case_ordering data structure was useful for comparing cases, but that is all that it did. In fact, the same data structure can be used, at least, for extracting data from cases into arrays of values, stuffing values back into cases, and for more general comparisons than case_ordering anticipated. This commit adds those abilities to case_ordering. It also renames it to "subcase", because it is useful for more than just sorting cases, which is all that case_ordering was designed for. This commit also changes the allocation pattern for subcase from having the implementation allocate all of the memory, to having the subcase client allocate "struct subcase". This saves a memory allocation without making life harder for anyone. - Naming: the "case_ordering" name implied that it was only useful for ordering (comparing cases). - Interface: the interface --- diff --git a/src/data/automake.mk b/src/data/automake.mk index bd161dba..cb407776 100644 --- a/src/data/automake.mk +++ b/src/data/automake.mk @@ -16,8 +16,6 @@ src_data_libdata_la_SOURCES = \ src/data/calendar.h \ src/data/case-map.c \ src/data/case-map.h \ - src/data/case-ordering.c \ - src/data/case-ordering.h \ src/data/case.c \ src/data/casegrouper.c \ src/data/casegrouper.h \ @@ -88,6 +86,8 @@ src_data_libdata_la_SOURCES = \ src/data/short-names.h \ src/data/sparse-cases.c \ src/data/sparse-cases.h \ + src/data/subcase.c \ + src/data/subcase.h \ src/data/sys-file-private.c \ src/data/sys-file-private.h \ src/data/sys-file-reader.c \ diff --git a/src/data/case-ordering.c b/src/data/case-ordering.c deleted file mode 100644 index c4a716e0..00000000 --- a/src/data/case-ordering.c +++ /dev/null @@ -1,178 +0,0 @@ -/* PSPP - a program for statistical analysis. - Copyright (C) 2007 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#include - -#include - -#include -#include -#include - -#include -#include - -#include "xalloc.h" - -/* One key used for sorting. */ -struct sort_key - { - const struct variable *var; /* Variable. */ - enum sort_direction dir; /* Sort direction. */ - }; - -/* A set of criteria for ordering cases. */ -struct case_ordering - { - /* Sort keys. */ - struct sort_key *keys; - size_t key_cnt; - }; - -/* Creates and returns a new case ordering for comparing cases - that represent dictionary DICT. The case ordering initially - contains no variables, so that all cases will compare as - equal. */ -struct case_ordering * -case_ordering_create (void) -{ - struct case_ordering *co = xmalloc (sizeof *co); - co->keys = NULL; - co->key_cnt = 0; - return co; -} - -/* Creates and returns a clone of case ordering ORIG. */ -struct case_ordering * -case_ordering_clone (const struct case_ordering *orig) -{ - struct case_ordering *co = xmalloc (sizeof *co); - co->keys = xmemdup (orig->keys, orig->key_cnt * sizeof *orig->keys); - co->key_cnt = orig->key_cnt; - return co; -} - -/* Destroys case ordering CO. */ -void -case_ordering_destroy (struct case_ordering *co) -{ - if (co != NULL) - { - free (co->keys); - free (co); - } -} - -/* Compares cases A and B given case ordering CO and returns a - strcmp()-type result. */ -int -case_ordering_compare_cases (const struct ccase *a, const struct ccase *b, - const struct case_ordering *co) -{ - size_t i; - - for (i = 0; i < co->key_cnt; i++) - { - const struct sort_key *key = &co->keys[i]; - int width = var_get_width (key->var); - int cmp; - - if (width == 0) - { - double af = case_num (a, key->var); - double bf = case_num (b, key->var); - if (af == bf) - continue; - cmp = af > bf ? 1 : -1; - } - else - { - const char *as = case_str (a, key->var); - const char *bs = case_str (b, key->var); - cmp = memcmp (as, bs, width); - if (cmp == 0) - continue; - } - - return key->dir == SRT_ASCEND ? cmp : -cmp; - } - return 0; -} - -/* Adds VAR to case ordering CO as an additional sort key in sort - direction DIR. Returns true if successful, false if VAR was - already part of the ordering for CO. */ -bool -case_ordering_add_var (struct case_ordering *co, - const struct variable *var, enum sort_direction dir) -{ - struct sort_key *key; - size_t i; - - for (i = 0; i < co->key_cnt; i++) - if (var_get_case_index (co->keys[i].var) == var_get_case_index (var)) - return false; - - co->keys = xnrealloc (co->keys, co->key_cnt + 1, sizeof *co->keys); - key = &co->keys[co->key_cnt++]; - key->var = var; - key->dir = dir; - return true; -} - -/* Returns the number of variables used for ordering within - CO. */ -size_t -case_ordering_get_var_cnt (const struct case_ordering *co) -{ - return co->key_cnt; -} - -/* Returns sort variable IDX within CO. An IDX of 0 returns the - primary sort key (the one added first), an IDX of 1 returns - the secondary sort key, and so on. IDX must be less than the - number of sort variables. */ -const struct variable * -case_ordering_get_var (const struct case_ordering *co, size_t idx) -{ - assert (idx < co->key_cnt); - return co->keys[idx].var; -} - -/* Returns the sort direction for sort variable IDX within CO. */ -enum sort_direction -case_ordering_get_direction (const struct case_ordering *co, size_t idx) -{ - assert (idx < co->key_cnt); - return co->keys[idx].dir; -} - -/* Stores an array listing all of the variables used for sorting - within CO into *VARS and the number of variables into - *VAR_CNT. The caller is responsible for freeing *VARS when it - is no longer needed. */ -void -case_ordering_get_vars (const struct case_ordering *co, - const struct variable ***vars, size_t *var_cnt) -{ - size_t i; - - *var_cnt = co->key_cnt; - *vars = xnmalloc (*var_cnt, sizeof **vars); - for (i = 0; i < co->key_cnt; i++) - (*vars)[i] = co->keys[i].var; -} - diff --git a/src/data/case-ordering.h b/src/data/case-ordering.h deleted file mode 100644 index f49f2651..00000000 --- a/src/data/case-ordering.h +++ /dev/null @@ -1,57 +0,0 @@ -/* PSPP - a program for statistical analysis. - Copyright (C) 2007 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* Sort order for comparing cases. */ - -#ifndef DATA_CASE_ORDERING_H -#define DATA_CASE_ORDERING_H 1 - -#include -#include - -struct dictionary; - -/* Sort direction. */ -enum sort_direction - { - SRT_ASCEND, /* A, B, C, ..., X, Y, Z. */ - SRT_DESCEND /* Z, Y, X, ..., C, B, A. */ - }; - -/* Creation and destruction. */ -struct case_ordering *case_ordering_create (void); -struct case_ordering *case_ordering_clone (const struct case_ordering *); -void case_ordering_destroy (struct case_ordering *); - -/* Modification. */ -bool case_ordering_add_var (struct case_ordering *, - const struct variable *, enum sort_direction); - -/* Comparing cases. */ -int case_ordering_compare_cases (const struct ccase *, const struct ccase *, - const struct case_ordering *); - -/* Inspection. */ -size_t case_ordering_get_value_cnt (const struct case_ordering *); -size_t case_ordering_get_var_cnt (const struct case_ordering *); -const struct variable *case_ordering_get_var (const struct case_ordering *, - size_t); -enum sort_direction case_ordering_get_direction (const struct case_ordering *, - size_t); -void case_ordering_get_vars (const struct case_ordering *, - const struct variable ***, size_t *); - -#endif /* data/case-ordering.h */ diff --git a/src/data/casegrouper.c b/src/data/casegrouper.c index f5974a40..75925650 100644 --- a/src/data/casegrouper.c +++ b/src/data/casegrouper.c @@ -20,10 +20,10 @@ #include -#include #include #include #include +#include #include #include "xalloc.h" @@ -159,13 +159,6 @@ casegrouper_destroy (struct casegrouper *grouper) /* Casegrouper based on equal values of variables from case to case. */ -/* Casegrouper based on equal variables. */ -struct casegrouper_vars - { - const struct variable **vars; /* Variables to compare. */ - size_t var_cnt; /* Number of variables. */ - }; - static bool casegrouper_vars_same_group (const struct ccase *, const struct ccase *, void *); @@ -180,15 +173,12 @@ casegrouper_create_vars (struct casereader *reader, const struct variable *const *vars, size_t var_cnt) { - if (var_cnt > 0) + if (var_cnt > 0) { - struct casegrouper_vars *cv = xmalloc (sizeof *cv); - cv->vars = xmemdup (vars, sizeof *vars * var_cnt); - cv->var_cnt = var_cnt; - return casegrouper_create_func (reader, - casegrouper_vars_same_group, - casegrouper_vars_destroy, - cv); + struct subcase *sc = xmalloc (sizeof *sc); + subcase_init_vars (sc, vars, var_cnt); + return casegrouper_create_func (reader, casegrouper_vars_same_group, + casegrouper_vars_destroy, sc); } else return casegrouper_create_func (reader, NULL, NULL, NULL); @@ -210,39 +200,41 @@ casegrouper_create_splits (struct casereader *reader, /* Creates and returns a casegrouper that reads data from READER and breaks it into contiguous groups of cases that have equal - values for the variables used for sorting in CO. If CO is - empty (contains no sort keys), then all the cases will be put + values for the variables used for sorting in SC. If SC is + empty (contains no fields), then all the cases will be put into a single group. */ struct casegrouper * -casegrouper_create_case_ordering (struct casereader *reader, - const struct case_ordering *co) +casegrouper_create_subcase (struct casereader *reader, + const struct subcase *sc) { - const struct variable **vars; - size_t var_cnt; - struct casegrouper *grouper; - - case_ordering_get_vars (co, &vars, &var_cnt); - grouper = casegrouper_create_vars (reader, vars, var_cnt); - free (vars); - - return grouper; + if (subcase_get_n_fields (sc) > 0) + { + struct subcase *sc_copy = xmalloc (sizeof *sc); + subcase_clone (sc_copy, sc); + return casegrouper_create_func (reader, casegrouper_vars_same_group, + casegrouper_vars_destroy, sc_copy); + } + else + return casegrouper_create_func (reader, NULL, NULL, NULL); } /* "same_group" function for an equal-variables casegrouper. */ static bool casegrouper_vars_same_group (const struct ccase *a, const struct ccase *b, - void *cv_) + void *sc_) { - struct casegrouper_vars *cv = cv_; - return case_compare (a, b, cv->vars, cv->var_cnt) == 0; + struct subcase *sc = sc_; + return subcase_equal (sc, a, sc, b); } /* "destroy" for an equal-variables casegrouper. */ static void -casegrouper_vars_destroy (void *cv_) +casegrouper_vars_destroy (void *sc_) { - struct casegrouper_vars *cv = cv_; - free (cv->vars); - free (cv); + struct subcase *sc = sc_; + if (sc != NULL) + { + subcase_destroy (sc); + free (sc); + } } - diff --git a/src/data/casegrouper.h b/src/data/casegrouper.h index 8b5f7b56..f367d23c 100644 --- a/src/data/casegrouper.h +++ b/src/data/casegrouper.h @@ -27,10 +27,10 @@ #include #include -struct case_ordering; struct casereader; struct ccase; struct dictionary; +struct subcase; struct variable; struct casegrouper * @@ -45,8 +45,8 @@ struct casegrouper *casegrouper_create_vars (struct casereader *, size_t var_cnt); struct casegrouper *casegrouper_create_splits (struct casereader *, const struct dictionary *); -struct casegrouper *casegrouper_create_case_ordering (struct casereader *, - const struct case_ordering *); +struct casegrouper *casegrouper_create_subcase (struct casereader *, + const struct subcase *); bool casegrouper_get_next_group (struct casegrouper *, struct casereader **); bool casegrouper_destroy (struct casegrouper *); diff --git a/src/data/subcase.c b/src/data/subcase.c new file mode 100644 index 00000000..3e019c7c --- /dev/null +++ b/src/data/subcase.c @@ -0,0 +1,318 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include +#include +#include +#include +#include + +#include "xalloc.h" + +/* Initializes SC as a subcase that contains no fields. */ +void +subcase_init_empty (struct subcase *sc) +{ + sc->fields = NULL; + sc->n_fields = 0; + sc->n_values = 0; +} + +/* Initializes SC as a subcase with fields extracted from the + N_VARS variables in VARS, with ascending sort order. */ +void +subcase_init_vars (struct subcase *sc, + const struct variable *const *vars, size_t n_vars) +{ + size_t i; + + sc->fields = xnmalloc (n_vars, sizeof *sc->fields); + sc->n_fields = n_vars; + sc->n_values = 0; + for (i = 0; i < n_vars; i++) + { + struct subcase_field *field = &sc->fields[i]; + field->case_index = var_get_case_index (vars[i]); + field->width = var_get_width (vars[i]); + field->direction = SC_ASCEND; + sc->n_values += value_cnt_from_width (field->width); + } +} + +/* Initializes SC as a subcase with a single field extracted + from VAR, with the sort order specified by DIRECTION. */ +void +subcase_init_var (struct subcase *sc, const struct variable *var, + enum subcase_direction direction) +{ + subcase_init_empty (sc); + subcase_add_var (sc, var, direction); +} + +/* Removes all the fields from SC. */ +void +subcase_clear (struct subcase *sc) +{ + sc->n_fields = 0; + sc->n_values = 0; +} + +/* Initializes SC with the same fields as ORIG. */ +void +subcase_clone (struct subcase *sc, const struct subcase *orig) +{ + sc->fields = xmemdup (orig->fields, orig->n_fields * sizeof *orig->fields); + sc->n_fields = orig->n_fields; + sc->n_values = orig->n_values; +} + +/* Frees the memory owned by SC (but not SC itself). */ +void +subcase_destroy (struct subcase *sc) +{ + free (sc->fields); +} + +/* Add a field for VAR to SC, with DIRECTION as the sort order. + Returns true if successful, false if VAR already has a field + in SC. */ +bool +subcase_add_var (struct subcase *sc, const struct variable *var, + enum subcase_direction direction) +{ + size_t case_index = var_get_case_index (var); + struct subcase_field *field; + size_t i; + + for (i = 0; i < sc->n_fields; i++) + if (sc->fields[i].case_index == case_index) + return false; + + sc->fields = xnrealloc (sc->fields, sc->n_fields + 1, sizeof *sc->fields); + field = &sc->fields[sc->n_fields++]; + field->case_index = case_index; + field->width = var_get_width (var); + field->direction = direction; + sc->n_values += value_cnt_from_width (field->width); + return true; +} + +/* Returns true if and only if A and B are conformable, which + means that they have the same number of fields and that each + corresponding field in A and B have the same width. */ +bool +subcase_conformable (const struct subcase *a, const struct subcase *b) +{ + size_t i; + + if (a == b) + return true; + if (a->n_values != b->n_values || a->n_fields != b->n_fields) + return false; + for (i = 0; i < a->n_fields; i++) + if (a->fields[i].width != b->fields[i].width) + return false; + return true; +} + +/* Copies the fields represented by SC from C into VALUES. + VALUES must have space for at least subcase_get_n_values(SC) + array elements. */ +void +subcase_extract (const struct subcase *sc, const struct ccase *c, + union value values[]) +{ + size_t i; + + for (i = 0; i < sc->n_fields; i++) + { + const struct subcase_field *field = &sc->fields[i]; + value_copy (values, case_data_idx (c, field->case_index), field->width); + values += value_cnt_from_width (field->width); + } +} + +/* Copies the data in VALUES into the fields in C represented by + SC. VALUES must have at least subcase_get_n_values(SC) array + elements, and C must be large enough to contain all the fields + in SC. */ +void +subcase_inject (const struct subcase *sc, + const union value values[], struct ccase *c) +{ + size_t i; + + for (i = 0; i < sc->n_fields; i++) + { + const struct subcase_field *field = &sc->fields[i]; + value_copy (case_data_rw_idx (c, field->case_index), values, + field->width); + values += value_cnt_from_width (field->width); + } +} + +/* Copies the fields in SRC represented by SRC_SC into the + corresponding fields in DST respresented by DST_SC. SRC_SC + and DST_SC must be conformable (as tested by + subcase_conformable()). */ +void +subcase_copy (const struct subcase *src_sc, const struct ccase *src, + const struct subcase *dst_sc, struct ccase *dst) +{ + size_t i; + + expensive_assert (subcase_conformable (src_sc, dst_sc)); + for (i = 0; i < src_sc->n_fields; i++) + { + const struct subcase_field *src_field = &src_sc->fields[i]; + const struct subcase_field *dst_field = &dst_sc->fields[i]; + value_copy (case_data_rw_idx (dst, dst_field->case_index), + case_data_idx (src, src_field->case_index), + src_field->width); + } +} + +/* Compares the fields in A specified in A_SC against the fields + in B specified in B_SC. Returns -1, 0, or 1 if A's fields are + lexicographically less than, equal to, or greater than B's + fields, respectively. + + A_SC and B_SC must be conformable (as tested by + subcase_conformable()). */ +int +subcase_compare_3way (const struct subcase *a_sc, const struct ccase *a, + const struct subcase *b_sc, const struct ccase *b) +{ + size_t i; + + expensive_assert (subcase_conformable (a_sc, b_sc)); + for (i = 0; i < a_sc->n_fields; i++) + { + const struct subcase_field *a_field = &a_sc->fields[i]; + const struct subcase_field *b_field = &b_sc->fields[i]; + int cmp = value_compare_3way (case_data_idx (a, a_field->case_index), + case_data_idx (b, b_field->case_index), + a_field->width); + if (cmp != 0) + return a_field->direction == SC_ASCEND ? cmp : -cmp; + } + return 0; +} + +/* Compares the values in A against the values in B specified by + SC's fields. Returns -1, 0, or 1 if A's values are + lexicographically less than, equal to, or greater than B's + values, respectively. */ +int +subcase_compare_3way_xc (const struct subcase *sc, + const union value a[], const struct ccase *b) +{ + size_t i; + + for (i = 0; i < sc->n_fields; i++) + { + const struct subcase_field *field = &sc->fields[i]; + int cmp = value_compare_3way (a, case_data_idx (b, field->case_index), + field->width); + if (cmp != 0) + return field->direction == SC_ASCEND ? cmp : -cmp; + a += value_cnt_from_width (field->width); + } + return 0; +} + +/* Compares the values in A specified by SC's fields against the + values in B. Returns -1, 0, or 1 if A's values are + lexicographically less than, equal to, or greater than B's + values, respectively. */ +int +subcase_compare_3way_cx (const struct subcase *sc, + const struct ccase *a, const union value b[]) +{ + return -subcase_compare_3way_xc (sc, b, a); +} + +/* Compares the values in A against the values in B, using SC to + obtain the number and width of each value. Returns -1, 0, or + 1 if A's values are lexicographically less than, equal to, or + greater than B's values, respectively. */ +int +subcase_compare_3way_xx (const struct subcase *sc, + const union value a[], const union value b[]) +{ + size_t i; + + for (i = 0; i < sc->n_fields; i++) + { + const struct subcase_field *field = &sc->fields[i]; + size_t n_values; + int cmp; + + cmp = value_compare_3way (a, b, field->width); + if (cmp != 0) + return field->direction == SC_ASCEND ? cmp : -cmp; + + n_values = value_cnt_from_width (field->width); + a += n_values; + b += n_values; + } + return 0; +} + +/* Compares the fields in A specified in A_SC against the fields + in B specified in B_SC. Returns true if the fields' values + are equal, false otherwise. + + A_SC and B_SC must be conformable (as tested by + subcase_conformable()). */ +bool +subcase_equal (const struct subcase *a_sc, const struct ccase *a, + const struct subcase *b_sc, const struct ccase *b) +{ + return subcase_compare_3way (a_sc, a, b_sc, b) == 0; +} + +/* Compares the values in A against the values in B specified by + SC's fields. Returns true if A's values are equal to B's + values, otherwise false. */ +bool +subcase_equal_xc (const struct subcase *sc, + const union value a[], const struct ccase *b) +{ + return subcase_compare_3way_xc (sc, a, b) == 0; +} + +/* Compares the values in A specified by SC's fields against the + values in B. Returns true if A's values are equal to B's + values, otherwise false. */ +bool +subcase_equal_cx (const struct subcase *sc, + const struct ccase *a, const union value b[]) +{ + return subcase_compare_3way_cx (sc, a, b) == 0; +} + +/* Compares the values in A against the values in B, using SC to + obtain the number and width of each value. Returns true if + A's values are equal to B's values, otherwise false. */ +bool +subcase_equal_xx (const struct subcase *sc, + const union value a[], const union value b[]) +{ + return subcase_compare_3way_xx (sc, a, b) == 0; +} + diff --git a/src/data/subcase.h b/src/data/subcase.h new file mode 100644 index 00000000..d50d0748 --- /dev/null +++ b/src/data/subcase.h @@ -0,0 +1,119 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2008 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef DATA_SUBCASE_H +#define DATA_SUBCASE_H 1 + +#include +#include + +struct ccase; +union value; +struct variable; + +/* Sort order. */ +enum subcase_direction + { + SC_ASCEND, /* A, B, C, ..., X, Y, Z. */ + SC_DESCEND /* Z, Y, X, ..., C, B, A. */ + }; + +/* A value within a case. */ +struct subcase_field + { + size_t case_index; /* Starting position in the case. */ + int width; /* 0=numeric, otherwise string width. */ + enum subcase_direction direction; /* Sort order. */ + }; + +/* A subcase specifies how to draw values from a case. */ +struct subcase + { + struct subcase_field *fields; + size_t n_fields; + size_t n_values; + }; + +void subcase_init_empty (struct subcase *); +void subcase_init_vars (struct subcase *, + const struct variable *const *, size_t n_vars); +void subcase_init_var (struct subcase *, + const struct variable *, enum subcase_direction); +void subcase_clone (struct subcase *, const struct subcase *); +void subcase_clear (struct subcase *); +void subcase_destroy (struct subcase *); + +bool subcase_add_var (struct subcase *, const struct variable *, + enum subcase_direction); + +static inline bool subcase_is_empty (const struct subcase *); +static inline size_t subcase_get_n_fields (const struct subcase *); +static inline size_t subcase_get_n_values (const struct subcase *); + +static inline enum subcase_direction subcase_get_direction ( + const struct subcase *, size_t idx); + +bool subcase_conformable (const struct subcase *, const struct subcase *); + +void subcase_extract (const struct subcase *, const struct ccase *, + union value *values); +void subcase_inject (const struct subcase *, + const union value *values, struct ccase *); +void subcase_copy (const struct subcase *src_sc, const struct ccase *src, + const struct subcase *dst_sc, struct ccase *dst); + +int subcase_compare_3way (const struct subcase *a_sc, const struct ccase *a, + const struct subcase *b_sc, const struct ccase *b); +int subcase_compare_3way_xc (const struct subcase *, + const union value *a, const struct ccase *b); +int subcase_compare_3way_cx (const struct subcase *, + const struct ccase *a, const union value *b); +int subcase_compare_3way_xx (const struct subcase *, + const union value *a, const union value *b); +bool subcase_equal (const struct subcase *a_sc, const struct ccase *a, + const struct subcase *b_sc, const struct ccase *b); +bool subcase_equal_xc (const struct subcase *, + const union value *a, const struct ccase *b); +bool subcase_equal_cx (const struct subcase *, + const struct ccase *a, const union value *b); +bool subcase_equal_xx (const struct subcase *, + const union value *a, const union value *b); + +static inline enum subcase_direction +subcase_get_direction (const struct subcase *sc, size_t idx) +{ + return sc->fields[idx].direction; +} + +static inline bool +subcase_is_empty (const struct subcase *sc) +{ + return sc->n_fields == 0; +} + +static inline size_t +subcase_get_n_fields (const struct subcase *sc) +{ + return sc->n_fields; +} + +static inline size_t +subcase_get_n_values (const struct subcase *sc) +{ + return sc->n_values; +} + +#endif /* data/subcase.h */ diff --git a/src/language/stats/aggregate.c b/src/language/stats/aggregate.c index e1dcd123..02257f94 100644 --- a/src/language/stats/aggregate.c +++ b/src/language/stats/aggregate.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -29,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -143,7 +143,7 @@ enum missing_treatment struct agr_proc { /* Break variables. */ - struct case_ordering *sort; /* Sort criteria (break variable). */ + struct subcase sort; /* Sort criteria (break variables). */ const struct variable **break_vars; /* Break variables. */ size_t break_var_cnt; /* Number of break variables. */ struct ccase break_case; /* Last values of break variables. */ @@ -191,6 +191,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds) agr.dict = dict_create (); agr.src_dict = dict; + subcase_init_empty (&agr.sort); dict_set_label (agr.dict, dict_get_label (dict)); dict_set_documents (agr.dict, dict_get_documents (dict)); @@ -229,13 +230,10 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds) int i; lex_match (lexer, '='); - agr.sort = parse_case_ordering (lexer, dict, - - &saw_direction); - if (agr.sort == NULL) + if (!parse_sort_criteria (lexer, dict, &agr.sort, &agr.break_vars, + &saw_direction)) goto error; - case_ordering_get_vars (agr.sort, - &agr.break_vars, &agr.break_var_cnt); + agr.break_var_cnt = subcase_get_n_fields (&agr.sort); for (i = 0; i < agr.break_var_cnt; i++) dict_clone_var_assert (agr.dict, agr.break_vars[i], @@ -286,10 +284,10 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds) } input = proc_open (ds); - if (agr.sort != NULL && !presorted) + if (!subcase_is_empty (&agr.sort) && !presorted) { - input = sort_execute (input, agr.sort); - agr.sort = NULL; + input = sort_execute (input, &agr.sort); + subcase_clear (&agr.sort); } for (grouper = casegrouper_create_vars (input, agr.break_vars, @@ -694,7 +692,7 @@ agr_destroy (struct agr_proc *agr) { struct agr_var *iter, *next; - case_ordering_destroy (agr->sort); + subcase_destroy (&agr->sort); free (agr->break_vars); case_destroy (&agr->break_case); for (iter = agr->agr_vars; iter; iter = next) @@ -1102,7 +1100,7 @@ initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input) break; case MEDIAN: { - struct case_ordering *ordering = case_ordering_create (); + struct subcase ordering; if ( ! iter->subject) iter->subject = var_create_internal (0); @@ -1110,9 +1108,10 @@ initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input) if ( ! iter->weight) iter->weight = var_create_internal (1); - case_ordering_add_var (ordering, iter->subject, SRT_ASCEND); + subcase_init_var (&ordering, iter->subject, SC_ASCEND); + iter->writer = sort_create_writer (&ordering, 2); + subcase_destroy (&ordering); - iter->writer = sort_create_writer (ordering, 2); iter->cc = 0; } break; diff --git a/src/language/stats/examine.q b/src/language/stats/examine.q index 361a85bd..312547f3 100644 --- a/src/language/stats/examine.q +++ b/src/language/stats/examine.q @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -911,11 +911,11 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level, { /* In this case, we need to sort the data, so we create a sorting casewriter */ - struct case_ordering *up_ordering = case_ordering_create (); - - case_ordering_add_var (up_ordering, dependent_vars[v], SRT_ASCEND); - writer = sort_create_writer (up_ordering, + struct subcase up_ordering; + subcase_init_var (&up_ordering, dependent_vars[v], SC_ASCEND); + writer = sort_create_writer (&up_ordering, casereader_get_value_cnt (reader)); + subcase_destroy (&up_ordering); } else { @@ -1131,15 +1131,10 @@ run_examine (struct cmd_examine *cmd, struct casereader *input, struct casereader *group = NULL; struct casereader *level1; struct casegrouper *grouper1 = NULL; - struct case_ordering *ordering1 = case_ordering_create (); - case_ordering_add_var (ordering1, factor->indep_var[0], SRT_ASCEND); level1 = casereader_clone (input); - - level1 = sort_execute (level1, - case_ordering_clone (ordering1)); - grouper1 = casegrouper_create_case_ordering (level1, ordering1); - case_ordering_destroy (ordering1); + level1 = sort_execute_1var (level1, factor->indep_var[0]); + grouper1 = casegrouper_create_vars (level1, &factor->indep_var[0], 1); while (casegrouper_get_next_group (grouper1, &group)) { @@ -1152,16 +1147,12 @@ run_examine (struct cmd_examine *cmd, struct casereader *input, int n_groups = 0; struct casereader *group2 = NULL; struct casegrouper *grouper2 = NULL; - struct case_ordering *ordering2 = case_ordering_create (); - case_ordering_add_var (ordering2, - factor->indep_var[1], SRT_ASCEND); - group_copy = sort_execute (group_copy, - case_ordering_clone (ordering2)); - grouper2 = - casegrouper_create_case_ordering (group_copy, ordering2); + group_copy = sort_execute_1var (group_copy, + factor->indep_var[1]); - case_ordering_destroy (ordering2); + grouper2 = casegrouper_create_vars (group_copy, + &factor->indep_var[1], 1); while (casegrouper_get_next_group (grouper2, &group2)) { diff --git a/src/language/stats/rank.q b/src/language/stats/rank.q index 13facbdb..d3ee3707 100644 --- a/src/language/stats/rank.q +++ b/src/language/stats/rank.q @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -29,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -152,7 +152,7 @@ static enum mv_class exclude_values; static struct rank_spec *rank_specs; static size_t n_rank_specs; -static struct case_ordering *sc; +static struct subcase sc; static const struct variable **group_vars; static size_t n_group_vars; @@ -233,14 +233,14 @@ create_var_label (struct variable *dest_var, static bool -rank_cmd (struct dataset *ds, const struct case_ordering *sc, +rank_cmd (struct dataset *ds, const struct subcase *sc, const struct rank_spec *rank_specs, int n_rank_specs) { struct dictionary *d = dataset_dict (ds); bool ok = true; int i; - for (i = 0 ; i < case_ordering_get_var_cnt (sc) ; ++i ) + for (i = 0 ; i < subcase_get_n_fields (sc) ; ++i ) { /* Rank variable at index I in SC. */ struct casegrouper *split_grouper; @@ -253,21 +253,18 @@ rank_cmd (struct dataset *ds, const struct case_ordering *sc, while (casegrouper_get_next_group (split_grouper, &split_group)) { - struct case_ordering *ordering; + struct subcase ordering; struct casereader *ordered; struct casegrouper *by_grouper; struct casereader *by_group; - int j; /* Sort this split group by the BY variables as primary keys and the rank variable as secondary key. */ - ordering = case_ordering_create (); - for (j = 0; j < n_group_vars; j++) - case_ordering_add_var (ordering, group_vars[j], SRT_ASCEND); - case_ordering_add_var (ordering, - case_ordering_get_var (sc, i), - case_ordering_get_direction (sc, i)); - ordered = sort_execute (split_group, ordering); + subcase_init_vars (&ordering, group_vars, n_group_vars); + subcase_add_var (&ordering, src_vars[i], + subcase_get_direction (sc, i)); + ordered = sort_execute (split_group, &ordering); + subcase_destroy (&ordering); /* Rank the rank variable within this split group. */ by_grouper = casegrouper_create_vars (ordered, @@ -625,8 +622,7 @@ rank_cleanup(void) rank_specs = NULL; n_rank_specs = 0; - case_ordering_destroy (sc); - sc = NULL; + subcase_destroy (&sc); free (src_vars); src_vars = NULL; @@ -641,6 +637,7 @@ cmd_rank (struct lexer *lexer, struct dataset *ds) size_t i; n_rank_specs = 0; + subcase_init_empty (&sc); if ( !parse_rank (lexer, ds, &cmd, NULL) ) { rank_cleanup (); @@ -660,12 +657,12 @@ cmd_rank (struct lexer *lexer, struct dataset *ds) rank_specs = xmalloc (sizeof (*rank_specs)); rank_specs[0].rfunc = RANK; rank_specs[0].destvars = - xcalloc (case_ordering_get_var_cnt (sc), sizeof (struct variable *)); + xcalloc (subcase_get_n_fields (&sc), sizeof (struct variable *)); n_rank_specs = 1; } - assert ( case_ordering_get_var_cnt (sc) == n_src_vars); + assert ( subcase_get_n_fields (&sc) == n_src_vars); /* Create variables for all rank destinations which haven't already been created with INTO. @@ -773,17 +770,17 @@ cmd_rank (struct lexer *lexer, struct dataset *ds) add_transformation (ds, create_resort_key, 0, order); /* Do the ranking */ - result = rank_cmd (ds, sc, rank_specs, n_rank_specs); + result = rank_cmd (ds, &sc, rank_specs, n_rank_specs); /* Put the active file back in its original order. Delete our sort key, which we don't need anymore. */ { - struct case_ordering *ordering = case_ordering_create (); struct casereader *sorted; - case_ordering_add_var (ordering, order, SRT_ASCEND); + /* FIXME: loses error conditions. */ + proc_discard_output (ds); - sorted = sort_execute (proc_open (ds), ordering); + sorted = sort_execute_1var (proc_open (ds), order); result = proc_commit (ds) && result; dict_delete_var (dataset_dict (ds), order); @@ -808,10 +805,9 @@ rank_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_rank && lex_token (lexer) != T_ALL) return 2; - sc = parse_case_ordering (lexer, dataset_dict (ds), NULL); - if (sc == NULL) + if (!parse_sort_criteria (lexer, dataset_dict (ds), &sc, &src_vars, NULL)) return 0; - case_ordering_get_vars (sc, &src_vars, &n_src_vars); + n_src_vars = subcase_get_n_fields (&sc); if ( lex_match (lexer, T_BY) ) { @@ -845,8 +841,7 @@ parse_rank_function (struct lexer *lexer, struct dictionary *dict, struct cmd_ra rank_specs[n_rank_specs - 1].destvars = NULL; rank_specs[n_rank_specs - 1].destvars = - xcalloc (case_ordering_get_var_cnt (sc), - sizeof (struct variable *)); + xcalloc (subcase_get_n_fields (&sc), sizeof (struct variable *)); if (lex_match_id (lexer, "INTO")) { @@ -860,7 +855,7 @@ parse_rank_function (struct lexer *lexer, struct dictionary *dict, struct cmd_ra msg(SE, _("Variable %s already exists."), lex_tokid (lexer)); return 0; } - if ( var_count >= case_ordering_get_var_cnt (sc) ) + if ( var_count >= subcase_get_n_fields (&sc) ) { msg(SE, _("Too many variables in INTO clause.")); return 0; diff --git a/src/language/stats/sort-cases.c b/src/language/stats/sort-cases.c index 85acc6c2..deb8b5cb 100644 --- a/src/language/stats/sort-cases.c +++ b/src/language/stats/sort-cases.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -41,15 +41,15 @@ int cmd_sort_cases (struct lexer *lexer, struct dataset *ds) { - struct case_ordering *ordering; + struct subcase ordering; struct casereader *output; bool ok = false; lex_match (lexer, T_BY); proc_cancel_temporary_transformations (ds); - ordering = parse_case_ordering (lexer, dataset_dict (ds), NULL); - if (ordering == NULL) + subcase_init_empty (&ordering); + if (!parse_sort_criteria (lexer, dataset_dict (ds), &ordering, NULL, NULL)) return CMD_CASCADING_FAILURE; if (settings_get_testing_mode () && lex_match (lexer, '/')) @@ -69,8 +69,7 @@ cmd_sort_cases (struct lexer *lexer, struct dataset *ds) } proc_discard_output (ds); - output = sort_execute (proc_open (ds), ordering); - ordering = NULL; + output = sort_execute (proc_open (ds), &ordering); ok = proc_commit (ds); ok = proc_set_active_file_data (ds, output) && ok; @@ -78,7 +77,7 @@ cmd_sort_cases (struct lexer *lexer, struct dataset *ds) min_buffers = 64; max_buffers = INT_MAX; - case_ordering_destroy (ordering); + subcase_destroy (&ordering); return ok ? lex_end_of_command (lexer) : CMD_CASCADING_FAILURE; } diff --git a/src/language/stats/sort-criteria.c b/src/language/stats/sort-criteria.c index fd8c7c53..9ae299ee 100644 --- a/src/language/stats/sort-criteria.c +++ b/src/language/stats/sort-criteria.c @@ -20,8 +20,8 @@ #include -#include #include +#include #include #include #include @@ -30,40 +30,45 @@ #include "gettext.h" #define _(msgid) gettext (msgid) -/* Parses a list of sort keys and returns a struct sort_criteria - based on it. Returns a null pointer on error. +/* Parses a list of sort fields and appends them to ORDERING, + which the caller must already have initialized. + Returns true if successful, false on error. If SAW_DIRECTION is nonnull, sets *SAW_DIRECTION to true if at least one parenthesized sort direction was specified, false otherwise. */ -struct case_ordering * -parse_case_ordering (struct lexer *lexer, const struct dictionary *dict, - bool *saw_direction) +bool +parse_sort_criteria (struct lexer *lexer, const struct dictionary *dict, + struct subcase *ordering, + const struct variable ***vars, bool *saw_direction) { - struct case_ordering *ordering = case_ordering_create (); - const struct variable **vars = NULL; + const struct variable **local_vars = NULL; size_t var_cnt = 0; - if (saw_direction != NULL) + if (vars == NULL) + vars = &local_vars; + *vars = NULL; + + if (saw_direction != NULL) *saw_direction = false; do { - enum sort_direction direction; + size_t prev_var_cnt = var_cnt; + enum subcase_direction direction; size_t i; /* Variables. */ - free (vars); - vars = NULL; - if (!parse_variables_const (lexer, dict, &vars, &var_cnt, PV_NO_SCRATCH)) + if (!parse_variables_const (lexer, dict, vars, &var_cnt, + PV_APPEND | PV_NO_SCRATCH)) goto error; /* Sort direction. */ if (lex_match (lexer, '(')) { if (lex_match_id (lexer, "D") || lex_match_id (lexer, "DOWN")) - direction = SRT_DESCEND; + direction = SC_DESCEND; else if (lex_match_id (lexer, "A") || lex_match_id (lexer, "UP")) - direction = SRT_ASCEND; + direction = SC_ASCEND; else { msg (SE, _("`A' or `D' expected inside parentheses.")); @@ -78,21 +83,25 @@ parse_case_ordering (struct lexer *lexer, const struct dictionary *dict, *saw_direction = true; } else - direction = SRT_ASCEND; - - for (i = 0; i < var_cnt; i++) - if (!case_ordering_add_var (ordering, vars[i], direction)) - msg (SW, _("Variable %s specified twice in sort criteria."), - var_get_name (vars[i])); + direction = SC_ASCEND; + + for (i = prev_var_cnt; i < var_cnt; i++) + { + const struct variable *var = (*vars)[i]; + if (!subcase_add_var (ordering, var, direction)) + msg (SW, _("Variable %s specified twice in sort criteria."), + var_get_name (var)); + } } while (lex_token (lexer) == T_ID && dict_lookup_var (dict, lex_tokid (lexer)) != NULL); - free (vars); - return ordering; + free (local_vars); + return true; - error: - free (vars); - case_ordering_destroy (ordering); - return NULL; +error: + free (local_vars); + if (vars) + *vars = NULL; + return false; } diff --git a/src/language/stats/sort-criteria.h b/src/language/stats/sort-criteria.h index 02a4a6cb..18f79a9c 100644 --- a/src/language/stats/sort-criteria.h +++ b/src/language/stats/sort-criteria.h @@ -22,10 +22,12 @@ struct dictionary; struct lexer; +struct variable; +struct subcase; -struct case_ordering *parse_case_ordering (struct lexer *, - const struct dictionary *, - bool *saw_direction); +bool parse_sort_criteria (struct lexer *, const struct dictionary *, + struct subcase *, const struct variable ***vars, + bool *saw_direction); -#endif /* SORT_PRS_H */ +#endif /* sort-criteria.h */ diff --git a/src/language/stats/wilcoxon.c b/src/language/stats/wilcoxon.c index 7552d7ec..2f5bbea8 100644 --- a/src/language/stats/wilcoxon.c +++ b/src/language/stats/wilcoxon.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -89,7 +89,7 @@ wilcoxon_execute (const struct dataset *ds, struct casereader *r = casereader_clone (input); struct casewriter *writer; struct ccase c; - struct case_ordering *ordering = case_ordering_create (); + struct subcase ordering; variable_pair *vp = &t2s->pairs[i]; const int reader_width = weight ? 3 : 2; @@ -97,14 +97,14 @@ wilcoxon_execute (const struct dataset *ds, ws[i].sign = var_create_internal (0); ws[i].absdiff = var_create_internal (1); - case_ordering_add_var (ordering, ws[i].absdiff, SRT_ASCEND); - - r = casereader_create_filter_missing (r, *vp, 2, exclude, NULL, NULL); - writer = sort_create_writer (ordering, reader_width); + subcase_init_var (&ordering, ws[i].absdiff, SC_ASCEND); + writer = sort_create_writer (&ordering, reader_width); + subcase_destroy (&ordering); + while (casereader_read (r, &c)) { struct ccase output; diff --git a/src/math/merge.c b/src/math/merge.c index 4fc7c8dc..b8412298 100644 --- a/src/math/merge.c +++ b/src/math/merge.c @@ -21,10 +21,10 @@ #include -#include #include #include #include +#include #include #include #include @@ -41,7 +41,7 @@ struct merge_input struct merge { - struct case_ordering *ordering; + struct subcase ordering; struct merge_input inputs[MAX_MERGE_ORDER]; size_t input_cnt; size_t value_cnt; @@ -50,10 +50,10 @@ struct merge static void do_merge (struct merge *m); struct merge * -merge_create (const struct case_ordering *ordering, size_t value_cnt) +merge_create (const struct subcase *ordering, size_t value_cnt) { struct merge *m = xmalloc (sizeof *m); - m->ordering = case_ordering_clone (ordering); + subcase_clone (&m->ordering, ordering); m->input_cnt = 0; m->value_cnt = value_cnt; return m; @@ -66,7 +66,7 @@ merge_destroy (struct merge *m) { size_t i; - case_ordering_destroy (m->ordering); + subcase_destroy (&m->ordering); for (i = 0; i < m->input_cnt; i++) casereader_destroy (m->inputs[i].reader); free (m); @@ -144,8 +144,8 @@ do_merge (struct merge *m) min = 0; for (i = 1; i < m->input_cnt; i++) - if (case_ordering_compare_cases (&m->inputs[i].c, &m->inputs[min].c, - m->ordering) < 0) + if (subcase_compare_3way (&m->ordering, &m->inputs[i].c, + &m->ordering, &m->inputs[min].c) < 0) min = i; casewriter_write (w, &m->inputs[min].c); diff --git a/src/math/merge.h b/src/math/merge.h index 18322e84..8bd3384a 100644 --- a/src/math/merge.h +++ b/src/math/merge.h @@ -20,10 +20,10 @@ #include #include -struct case_ordering; +struct subcase; struct casereader; -struct merge *merge_create (const struct case_ordering *, size_t); +struct merge *merge_create (const struct subcase *, size_t); void merge_destroy (struct merge *); void merge_append (struct merge *, struct casereader *); struct casereader *merge_make_reader (struct merge *); diff --git a/src/math/sort.c b/src/math/sort.c index 10b8a12c..9e860c46 100644 --- a/src/math/sort.c +++ b/src/math/sort.c @@ -20,12 +20,12 @@ #include -#include #include #include #include #include #include +#include #include #include #include @@ -42,7 +42,7 @@ int max_buffers = INT_MAX; struct sort_writer { size_t value_cnt; - struct case_ordering *ordering; + struct subcase ordering; struct merge *merge; struct pqueue *pqueue; @@ -53,7 +53,7 @@ struct sort_writer static struct casewriter_class sort_casewriter_class; -static struct pqueue *pqueue_create (const struct case_ordering *, size_t); +static struct pqueue *pqueue_create (const struct subcase *, size_t); static void pqueue_destroy (struct pqueue *); static bool pqueue_is_full (const struct pqueue *); static bool pqueue_is_empty (const struct pqueue *); @@ -63,21 +63,19 @@ static void pqueue_pop (struct pqueue *, struct ccase *, casenumber *); static void output_record (struct sort_writer *); struct casewriter * -sort_create_writer (struct case_ordering *ordering, size_t value_cnt) +sort_create_writer (const struct subcase *ordering, size_t value_cnt) { struct sort_writer *sort; sort = xmalloc (sizeof *sort); sort->value_cnt = value_cnt; - sort->ordering = case_ordering_clone (ordering); + subcase_clone (&sort->ordering, ordering); sort->merge = merge_create (ordering, value_cnt); sort->pqueue = pqueue_create (ordering, value_cnt); sort->run = NULL; sort->run_id = 0; case_nullify (&sort->run_end); - case_ordering_destroy (ordering); - return casewriter_create (value_cnt, &sort_casewriter_class, sort); } @@ -92,8 +90,8 @@ sort_casewriter_write (struct casewriter *writer UNUSED, void *sort_, output_record (sort); next_run = (case_is_null (&sort->run_end) - || case_ordering_compare_cases (c, &sort->run_end, - sort->ordering) < 0); + || subcase_compare_3way (&sort->ordering, c, + &sort->ordering, &sort->run_end) < 0); pqueue_push (sort->pqueue, c, sort->run_id + (next_run ? 1 : 0)); } @@ -102,7 +100,7 @@ sort_casewriter_destroy (struct casewriter *writer UNUSED, void *sort_) { struct sort_writer *sort = sort_; - case_ordering_destroy (sort->ordering); + subcase_destroy (&sort->ordering); merge_destroy (sort->merge); pqueue_destroy (sort->pqueue); casewriter_destroy (sort->run); @@ -169,21 +167,34 @@ static struct casewriter_class sort_casewriter_class = }; /* Reads all the cases from INPUT. Sorts the cases according to - ORDERING. Returns the sorted cases in a new casereader, or a - null pointer if an I/O error occurs. Both INPUT and ORDERING - are destroyed upon return, regardless of success. */ + ORDERING. Returns the sorted cases in a new casereader. */ struct casereader * -sort_execute (struct casereader *input, struct case_ordering *ordering) +sort_execute (struct casereader *input, const struct subcase *ordering) { struct casewriter *output = sort_create_writer (ordering, casereader_get_value_cnt (input)); casereader_transfer (input, output); return casewriter_make_reader (output); } + +/* Reads all the cases from INPUT. Sorts the cases in ascending + order according to VARIABLE. Returns the sorted cases in a + new casereader. */ +struct casereader * +sort_execute_1var (struct casereader *input, const struct variable *var) +{ + struct subcase sc; + struct casereader *reader; + + subcase_init_var (&sc, var, SC_ASCEND); + reader = sort_execute (input, &sc); + subcase_destroy (&sc); + return reader; +} struct pqueue { - struct case_ordering *ordering; + struct subcase ordering; struct pqueue_record *records; size_t record_cnt; size_t record_cap; @@ -201,12 +212,12 @@ static int compare_pqueue_records_minheap (const void *a, const void *b, const void *pq_); static struct pqueue * -pqueue_create (const struct case_ordering *ordering, size_t value_cnt) +pqueue_create (const struct subcase *ordering, size_t value_cnt) { struct pqueue *pq; pq = xmalloc (sizeof *pq); - pq->ordering = case_ordering_clone (ordering); + subcase_clone (&pq->ordering, ordering); pq->record_cap = settings_get_workspace_cases (value_cnt); if (pq->record_cap > max_buffers) @@ -232,7 +243,7 @@ pqueue_destroy (struct pqueue *pq) pqueue_pop (pq, &c, &id); case_destroy (&c); } - case_ordering_destroy (pq->ordering); + subcase_destroy (&pq->ordering); free (pq->records); free (pq); } @@ -292,7 +303,8 @@ compare_pqueue_records_minheap (const void *a_, const void *b_, const struct pqueue *pq = pq_; int result = a->id < b->id ? -1 : a->id > b->id; if (result == 0) - result = case_ordering_compare_cases (&a->c, &b->c, pq->ordering); + result = subcase_compare_3way (&pq->ordering, &a->c, + &pq->ordering, &b->c); if (result == 0) result = a->idx < b->idx ? -1 : a->idx > b->idx; return -result; diff --git a/src/math/sort.h b/src/math/sort.h index ea2c16b2..1948d0ab 100644 --- a/src/math/sort.h +++ b/src/math/sort.h @@ -20,12 +20,16 @@ #include #include -struct case_ordering; +struct subcase; +struct variable; extern int min_buffers ; extern int max_buffers ; -struct casewriter *sort_create_writer (struct case_ordering *, size_t value_cnt); -struct casereader *sort_execute (struct casereader *, struct case_ordering *); +struct casewriter *sort_create_writer (const struct subcase *, + size_t value_cnt); +struct casereader *sort_execute (struct casereader *, const struct subcase *); +struct casereader *sort_execute_1var (struct casereader *, + const struct variable *); #endif /* math/sort.h */ diff --git a/src/ui/gui/psppire-case-file.c b/src/ui/gui/psppire-case-file.c index 2edfec89..cdb033f7 100644 --- a/src/ui/gui/psppire-case-file.c +++ b/src/ui/gui/psppire-case-file.c @@ -380,7 +380,7 @@ psppire_case_file_data_in (PsppireCaseFile *cf, casenumber casenum, gint idx, void -psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *ordering) +psppire_case_file_sort (PsppireCaseFile *cf, const struct subcase *ordering) { struct casereader *sorted_data; gint c; diff --git a/src/ui/gui/psppire-case-file.h b/src/ui/gui/psppire-case-file.h index 13cc08d4..f4822f42 100644 --- a/src/ui/gui/psppire-case-file.h +++ b/src/ui/gui/psppire-case-file.h @@ -95,9 +95,9 @@ gboolean psppire_case_file_delete_cases (PsppireCaseFile *cf, casenumber n_rows, gboolean psppire_case_file_insert_values (PsppireCaseFile *cf, gint n_values, gint where); -struct case_ordering; +struct subcase; -void psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *); +void psppire_case_file_sort (PsppireCaseFile *cf, const struct subcase *); gboolean psppire_case_file_get_case (const PsppireCaseFile *cf, casenumber casenum,