dataset: Before deleting variables, make sure new values are added.
[pspp] / src / data / caseinit.c
index 4f7ece7e03bac3dc6950a2d977a0203ddad62c23..3efe128efd205c7f618e712a3d6386d5e127c912 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009, 2010, 2011 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
 
 #include <config.h>
 
-#include <data/caseinit.h>
+#include "data/caseinit.h"
 
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <data/case.h>
-#include <data/dictionary.h>
-#include <data/value.h>
-#include <data/variable.h>
-#include <libpspp/array.h>
-#include <libpspp/assertion.h>
-#include <libpspp/compiler.h>
+#include "data/case.h"
+#include "data/casereader.h"
+#include "data/dictionary.h"
+#include "data/value.h"
+#include "data/variable.h"
+#include "libpspp/array.h"
+#include "libpspp/assertion.h"
+#include "libpspp/compiler.h"
 
-#include "xalloc.h"
+#include "gl/xalloc.h"
 \f
 /* Initializer list: a set of values to write to locations within
    a case. */
 /* Binds a value with a place to put it. */
 struct init_value
   {
-    union value value;
     size_t case_index;
+    int width;
+    union value value;
   };
 
 /* A set of values to initialize in a case. */
 struct init_list
   {
     struct init_value *values;
-    size_t cnt;
+    size_t n;
   };
 
 /* A bitmap of the "left" status of variables. */
 enum leave_class
   {
-    LEAVE_REINIT = 0x001,       /* Reinitalize for every case. */
+    LEAVE_REINIT = 0x001,       /* Reinitialize for every case. */
     LEAVE_LEFT = 0x002          /* Keep the value from one case to the next. */
   };
 
@@ -61,13 +63,31 @@ static void
 init_list_create (struct init_list *list)
 {
   list->values = NULL;
-  list->cnt = 0;
+  list->n = 0;
+}
+
+/* Initializes NEW as a copy of OLD. */
+static struct init_list
+init_list_clone (const struct init_list *old)
+{
+  struct init_value *values = xmemdup (old->values,
+                                       old->n * sizeof *old->values);
+  for (size_t i = 0; i < old->n; i++)
+    {
+      struct init_value *iv = &values[i];
+      value_clone (&iv->value, &iv->value, iv->width);
+    }
+  return (struct init_list) { .values = values, .n = old->n };
 }
 
 /* Frees the storage associated with LIST. */
 static void
 init_list_destroy (struct init_list *list)
 {
+  struct init_value *iv;
+
+  for (iv = &list->values[0]; iv < &list->values[list->n]; iv++)
+    value_destroy (&iv->value, iv->width);
   free (list->values);
 }
 
@@ -96,7 +116,7 @@ init_list_includes (const struct init_list *list, size_t case_index)
 {
   struct init_value value;
   value.case_index = case_index;
-  return binary_search (list->values, list->cnt, sizeof *list->values,
+  return binary_search (list->values, list->n, sizeof *list->values,
                         &value, compare_init_values, NULL) != NULL;
 }
 
@@ -107,18 +127,16 @@ static void
 init_list_mark (struct init_list *list, const struct init_list *exclude,
                 enum leave_class include, const struct dictionary *d)
 {
-  size_t var_cnt = dict_get_var_cnt (d);
-  size_t i;
+  size_t n_vars = dict_get_n_vars (d);
 
   assert (list != exclude);
-  list->values = xnrealloc (list->values,
-                            list->cnt + dict_get_next_value_idx (d),
+  list->values = xnrealloc (list->values, list->n + dict_get_n_vars (d),
                             sizeof *list->values);
-  for (i = 0; i < var_cnt; i++)
+  for (size_t i = 0; i < n_vars; i++)
     {
       struct variable *v = dict_get_var (d, i);
       size_t case_index = var_get_case_index (v);
-      int offset;
+      struct init_value *iv;
 
       /* Only include the correct class. */
       if (!(include & (var_get_leave (v) ? LEAVE_LEFT : LEAVE_REINIT)))
@@ -128,24 +146,19 @@ init_list_mark (struct init_list *list, const struct init_list *exclude,
       if (exclude != NULL && init_list_includes (exclude, case_index))
         continue;
 
-      offset = 0;
-      do
-        {
-          struct init_value *iv = &list->values[list->cnt++];
-          iv->case_index = case_index++;
-          if (var_is_numeric (v))
-            iv->value.f = var_get_leave (v) ? 0 : SYSMIS;
-          else
-            memset (iv->value.s, ' ', sizeof iv->value.s);
-
-          offset += sizeof iv->value.s;
-        }
-      while (offset < var_get_width (v));
+      iv = &list->values[list->n++];
+      iv->case_index = case_index;
+      iv->width = var_get_width (v);
+      value_init (&iv->value, iv->width);
+      if (var_is_numeric (v) && var_get_leave (v))
+        iv->value.f = 0;
+      else
+        value_set_missing (&iv->value, iv->width);
     }
 
   /* Drop duplicates. */
-  list->cnt = sort_unique (list->values, list->cnt, sizeof *list->values,
-                           compare_init_values, NULL);
+  list->n = sort_unique (list->values, list->n, sizeof *list->values,
+                         compare_init_values, NULL);
 }
 
 /* Initializes data in case C to the values in the initializer
@@ -153,13 +166,10 @@ init_list_mark (struct init_list *list, const struct init_list *exclude,
 static void
 init_list_init (const struct init_list *list, struct ccase *c)
 {
-  size_t i;
+  const struct init_value *iv;
 
-  for (i = 0; i < list->cnt; i++)
-    {
-      const struct init_value *value = &list->values[i];
-      *case_data_rw_idx (c, value->case_index) = value->value;
-    }
+  for (iv = &list->values[0]; iv < &list->values[list->n]; iv++)
+    value_copy (case_data_rw_idx (c, iv->case_index), &iv->value, iv->width);
 }
 
 /* Updates the values in the initializer LIST from the data in
@@ -167,13 +177,10 @@ init_list_init (const struct init_list *list, struct ccase *c)
 static void
 init_list_update (const struct init_list *list, const struct ccase *c)
 {
-  size_t i;
+  struct init_value *iv;
 
-  for (i = 0; i < list->cnt; i++)
-    {
-      struct init_value *value = &list->values[i];
-      value->value = *case_data_idx (c, value->case_index);
-    }
+  for (iv = &list->values[0]; iv < &list->values[list->n]; iv++)
+    value_copy (&iv->value, case_data_idx (c, iv->case_index), iv->width);
 }
 \f
 /* A case initializer. */
@@ -205,6 +212,19 @@ caseinit_create (void)
   return ci;
 }
 
+/* Creates and returns a copy of OLD. */
+struct caseinit *
+caseinit_clone (struct caseinit *old)
+{
+  struct caseinit *new = xmalloc (sizeof *new);
+  *new = (struct caseinit) {
+    .preinited_values = init_list_clone (&old->preinited_values),
+    .reinit_values = init_list_clone (&old->reinit_values),
+    .left_values = init_list_clone (&old->left_values),
+  };
+  return new;
+}
+
 /* Clears the contents of case initializer CI. */
 void
 caseinit_clear (struct caseinit *ci)
@@ -253,15 +273,75 @@ void
 caseinit_init_vars (const struct caseinit *ci, struct ccase *c)
 {
   init_list_init (&ci->reinit_values, c);
+}
+
+/* Copies the left vars from CI into C. */
+void
+caseinit_restore_left_vars (struct caseinit *ci, struct ccase *c)
+{
   init_list_init (&ci->left_values, c);
 }
 
-/* Updates the left vars in CI from the data in C, so that the
-   next call to caseinit_init_vars will store those values in the
-   next case. */
+/* Copies the left vars from C into CI. */
 void
-caseinit_update_left_vars (struct caseinit *ci, const struct ccase *c)
+caseinit_save_left_vars (struct caseinit *ci, const struct ccase *c)
 {
   init_list_update (&ci->left_values, c);
 }
+\f
+struct caseinit_translator
+  {
+    struct init_list reinit_values;
+    struct caseproto *proto;
+  };
+
+static struct ccase *
+translate_caseinit (struct ccase *c, void *cit_)
+{
+  const struct caseinit_translator *cit = cit_;
+
+  c = case_unshare_and_resize (c, cit->proto);
+  init_list_init (&cit->reinit_values, c);
+  return c;
+}
+
+static bool
+translate_destroy (void *cit_)
+{
+  struct caseinit_translator *cit = cit_;
+
+  init_list_destroy (&cit->reinit_values);
+  caseproto_unref (cit->proto);
+  free (cit);
+
+  return true;
+}
+
+/* Returns a new casereader that yields each case from R, resized to match
+   OUTPUT_PROTO and initialized from CI as if with caseinit_init_vars().  Takes
+   ownership of R.
+
+   OUTPUT_PROTO must be conformable with R's prototype.  */
+struct casereader *
+caseinit_translate_casereader_to_init_vars (struct caseinit *ci,
+                                            const struct caseproto *output_proto,
+                                            struct casereader *r)
+{
+  assert (caseproto_is_conformable (casereader_get_proto (r), output_proto));
+  if (caseproto_equal (output_proto, casereader_get_proto (r))
+      && ci->reinit_values.n == 0)
+    return casereader_rename (r);
+
+  struct caseinit_translator *cit = xmalloc (sizeof *cit);
+  *cit = (struct caseinit_translator) {
+    .reinit_values = init_list_clone (&ci->reinit_values),
+    .proto = caseproto_ref (output_proto),
+  };
+
+  static const struct casereader_translator_class class = {
+    .translate = translate_caseinit,
+    .destroy = translate_destroy,
+  };
+  return casereader_translate_stateless (r, output_proto, &class, cit);
+}