Replace case_ordering with subcase.
authorBen Pfaff <blp@gnu.org>
Sun, 30 Nov 2008 23:23:20 +0000 (15:23 -0800)
committerBen Pfaff <blp@gnu.org>
Wed, 3 Dec 2008 04:26:47 +0000 (20:26 -0800)
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

20 files changed:
src/data/automake.mk
src/data/case-ordering.c [deleted file]
src/data/case-ordering.h [deleted file]
src/data/casegrouper.c
src/data/casegrouper.h
src/data/subcase.c [new file with mode: 0644]
src/data/subcase.h [new file with mode: 0644]
src/language/stats/aggregate.c
src/language/stats/examine.q
src/language/stats/rank.q
src/language/stats/sort-cases.c
src/language/stats/sort-criteria.c
src/language/stats/sort-criteria.h
src/language/stats/wilcoxon.c
src/math/merge.c
src/math/merge.h
src/math/sort.c
src/math/sort.h
src/ui/gui/psppire-case-file.c
src/ui/gui/psppire-case-file.h

index bd161dba469147a3c2c7cfa029e99fded534c17d..cb4077765b51a3e0d519e1264008b703f48032bf 100644 (file)
@@ -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 (file)
index c4a716e..0000000
+++ /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 <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <data/case-ordering.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <data/dictionary.h>
-#include <data/variable.h>
-
-#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 (file)
index f49f265..0000000
+++ /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 <http://www.gnu.org/licenses/>. */
-
-/* Sort order for comparing cases. */
-
-#ifndef DATA_CASE_ORDERING_H
-#define DATA_CASE_ORDERING_H 1
-
-#include <stddef.h>
-#include <data/case.h>
-
-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 */
index f5974a404b0a6200729548a62a2d67afde684323..75925650f277ad8fbdf0f83b548eebb217b9138f 100644 (file)
 
 #include <stdlib.h>
 
-#include <data/case-ordering.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
 #include <data/dictionary.h>
+#include <data/subcase.h>
 #include <libpspp/taint.h>
 
 #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); 
+    }
 }
-
index 8b5f7b56ae5ba9fbd7a3090dadd4bf8caa48016c..f367d23cdb2b23af870be7f35276b4e3b6b0f66b 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 
-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 (file)
index 0000000..3e019c7
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <data/subcase.h>
+#include <stdlib.h>
+#include <data/case.h>
+#include <data/variable.h>
+#include <libpspp/assertion.h>
+
+#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 (file)
index 0000000..d50d074
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>. */
+
+#ifndef DATA_SUBCASE_H
+#define DATA_SUBCASE_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+
+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 */
index e1dcd12342ff2b42ff94dcbf472affeb58bb51c1..02257f9434f29555bcb2538f5b321a961d099788 100644 (file)
@@ -19,7 +19,6 @@
 #include <stdlib.h>
 
 #include <data/any-writer.h>
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casegrouper.h>
 #include <data/casereader.h>
@@ -29,6 +28,7 @@
 #include <data/format.h>
 #include <data/procedure.h>
 #include <data/settings.h>
+#include <data/subcase.h>
 #include <data/sys-file-writer.h>
 #include <data/variable.h>
 #include <language/command.h>
@@ -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;
index 361a85bd1e164c194a172da7c1fd8d3a792bec4d..312547f3f67132380f33d9a204d7049cfb1a670f 100644 (file)
@@ -34,9 +34,9 @@
 #include <data/casegrouper.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
-#include <data/case-ordering.h>
 #include <data/dictionary.h>
 #include <data/procedure.h>
+#include <data/subcase.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
 #include <language/command.h>
@@ -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))
                {
index 13facbdbbf59f85e43bd350578d4633cb07b880e..d3ee3707a4437112e84059270c2d16af7f11370c 100644 (file)
@@ -19,7 +19,6 @@
 #include <limits.h>
 #include <math.h>
 
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casegrouper.h>
 #include <data/casereader.h>
@@ -29,6 +28,7 @@
 #include <data/missing-values.h>
 #include <data/procedure.h>
 #include <data/short-names.h>
+#include <data/subcase.h>
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/stats/sort-criteria.h>
@@ -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;
index 85acc6c2b76696f4175cb8f69457be8660a8b3f7..deb8b5cbc97b00f1ee4cd7da54bbff8701260ffa 100644 (file)
@@ -27,7 +27,7 @@
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/message.h>
-#include <data/case-ordering.h>
+#include <data/subcase.h>
 #include <math/sort.h>
 #include <sys/types.h>
 
 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;
 }
 
index fd8c7c535f9e630b88acbfdbafeaa8367374fdcc..9ae299ee6c9e67da5513784eb266c56405e0da20 100644 (file)
@@ -20,8 +20,8 @@
 
 #include <stdlib.h>
 
-#include <data/case-ordering.h>
 #include <data/dictionary.h>
+#include <data/subcase.h>
 #include <data/variable.h>
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
 #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;
 }
index 02a4a6cbefb8c552828535978f8b37c4f3dc605d..18f79a9c89e19ff13d3cf3d763a36334e6607ff4 100644 (file)
 
 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 */
index 7552d7ece1f84e37acd189de6aaeebf4ce11ba2a..2f5bbea892745a99fb33fdbede97ce6baa29319c 100644 (file)
@@ -21,7 +21,7 @@
 #include <data/variable.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
-#include <data/case-ordering.h>
+#include <data/subcase.h>
 #include <math/sort.h>
 #include <libpspp/message.h>
 #include <xalloc.h>
@@ -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;
index 4fc7c8dcf21433a7c23692c275a0ed398c850789..b84122982e04f19fedaf1c2a0e542e58214546d0 100644 (file)
 
 #include <math/merge.h>
 
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
+#include <data/subcase.h>
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
 #include <libpspp/taint.h>
@@ -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);
index 18322e84bf7f9c2a614a2f9d666412d0b467cfe5..8bd3384a401818e1352bd7cd94a2a61cc9371695 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 
-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 *);
index 10b8a12cec59d76173a39a15532a53dbec62e306..9e860c4657a83029fc923e835f30579b39d2ea1c 100644 (file)
 
 #include <stdio.h>
 
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
 #include <data/casewriter-provider.h>
 #include <data/settings.h>
+#include <data/subcase.h>
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
 #include <math/merge.h>
@@ -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 =
   };
 \f
 /* 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;
+}
 \f
 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;
index ea2c16b2e14a8a8ed1c2fbe0f838bb92bdcd4870..1948d0abea10df18edf47fb81cd736922a99fccb 100644 (file)
 #include <stddef.h>
 #include <stdbool.h>
 
-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 */
index 2edfec89ae0a036c3791309ba432e16567839706..cdb033f7fca3adf58dd2d55c59fb243efe1a7b43 100644 (file)
@@ -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;
index 13cc08d4b7279d1c56f4357981ec61f112dffc65..f4822f42bb230120974afbfa1cddbc72f3460e1d 100644 (file)
@@ -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,