Merge branch 'master' into rewrite-sheet
[pspp] / src / data / casereader-translator.c
index 0557b65e8d6336452b4b88c4881d901fc289c7f2..ae22f1297e19dbe5a12e6d9e2ec49f75ce8de087 100644 (file)
@@ -15,9 +15,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-
+#include <data/val-type.h>
 #include <data/casereader.h>
 #include <data/casereader.h>
-
 #include <stdlib.h>
 
 #include <data/casereader-provider.h>
 #include <stdlib.h>
 
 #include <data/casereader-provider.h>
@@ -131,7 +130,7 @@ static void can_translate (struct ccase *input, struct ccase *output,
    by reading from SUBREADER and appending an additional value,
    generated by FUNC.  AUX is an optional parameter which
    gets passed to FUNC. FUNC will also receive N as it, which is
    by reading from SUBREADER and appending an additional value,
    generated by FUNC.  AUX is an optional parameter which
    gets passed to FUNC. FUNC will also receive N as it, which is
-   the ordinal number of the case in the reader.  DESTROY is an 
+   the ordinal number of the case in the reader.  DESTROY is an
    optional parameter used to destroy AUX.
 
    After this function is called, SUBREADER must not ever again
    optional parameter used to destroy AUX.
 
    After this function is called, SUBREADER must not ever again
@@ -211,3 +210,156 @@ casereader_create_arithmetic_sequence (struct casereader *subreader,
                                           as, free);
 }
 
                                           as, free);
 }
 
+
+\f
+
+struct casereader_append_rank
+{
+  struct casereader *clone;
+  casenumber n;
+  const struct variable *var;
+  const struct variable *weight;
+  int value_ofs;
+  casenumber n_common;
+  double mean_rank;
+  double cc;
+  distinct_func *distinct;
+  void *aux;
+  enum rank_error *err;
+  double prev_value;
+};
+
+static bool car_destroy (void *car_);
+
+static void car_translate (struct ccase *input, struct ccase *output,
+                          void *car_);
+
+/* Creates and returns a new casereader whose cases are produced
+   by reading from SUBREADER and appending an additional value,
+   which is the rank of the observation.   W is the weight variable
+   of the dictionary containing V, or NULL if there is no weight
+   variable.
+
+   The following preconditions must be met:
+
+   1.    SUBREADER must be sorted on V.
+
+   2.    The weight variables, must be non-negative.
+
+   If either of these preconditions are not satisfied, then the rank
+   variables may not be correct.  In this case, if ERR is non-null,
+   it will be set according to the erroneous conditions encountered.
+
+   If DISTINCT_CALLBACK is non-null, then  it will be called exactly
+   once for every case containing a distinct value of V.  AUX is
+   an auxilliary pointer passed to DISTINCT_CALLBACK.
+
+   After this function is called, SUBREADER must not ever again
+   be referenced directly.  It will be destroyed automatically
+   when the translating casereader is destroyed. */
+struct casereader *
+casereader_create_append_rank (struct casereader *subreader,
+                              const struct variable *v,
+                              const struct variable *w,
+                              enum rank_error *err,
+                              distinct_func *distinct_callback,
+                              void *aux
+                              )
+{
+  struct casereader_append_rank *car = xmalloc (sizeof *car);
+  car->value_ofs = casereader_get_value_cnt (subreader);
+  car->weight = w;
+  car->var = v;
+  car->n = 0;
+  car->n_common = 1;
+  car->cc = 0.0;
+  car->clone = casereader_clone (subreader);
+  car->distinct = distinct_callback;
+  car->aux = aux;
+  car->err = err;
+  car->prev_value = SYSMIS;
+
+  return casereader_create_translator (subreader, car->value_ofs + 1,
+                                       car_translate, car_destroy, car);
+}
+
+
+static bool
+car_destroy (void *car_)
+{
+  struct casereader_append_rank *car = car_;
+  casereader_destroy (car->clone);
+  free (car);
+  return true;
+}
+
+
+static void
+car_translate (struct ccase *input, struct ccase *output,  void *car_)
+{
+  struct casereader_append_rank *car = car_;
+
+  const double value = case_data (input, car->var)->f;
+
+  if ( car->prev_value != SYSMIS)
+    {
+      if (car->err && value < car->prev_value)
+       *car->err |= RANK_ERR_UNSORTED;
+    }
+
+  if ( car->n_common == 1)
+    {
+      double vxx = SYSMIS;
+      casenumber k = 0;
+      double weight = 1.0;
+      if (car->weight)
+       {
+         weight = case_data (input, car->weight)->f;
+         if ( car->err && weight < 0 )
+           *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
+       }
+
+      do
+       {
+         struct ccase c;
+         if ( ! casereader_peek (car->clone, car->n + ++k, &c))
+           break;
+         vxx = case_data (&c, car->var)->f;
+
+         if ( vxx == value)
+           {
+             if (car->weight)
+               {
+                 double w = case_data (&c, car->weight)->f;
+
+                 if ( car->err && w < 0 )
+                   *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
+
+                 weight += w;
+               }
+             else
+               weight += 1.0;
+             car->n_common++;
+           }
+         case_destroy (&c);
+       }
+      while (vxx == value);
+      car->mean_rank = car->cc + (weight + 1) / 2.0;
+      car->cc += weight;
+
+      if (car->distinct)
+       car->distinct (value, car->n_common, weight, car->aux);
+    }
+  else
+    car->n_common--;
+
+  car->n++;
+
+  case_nullify (output);
+  case_move (output, input);
+  case_resize (output, car->value_ofs + 1);
+  case_data_rw_idx (output, car->value_ofs)->f = car->mean_rank ;
+  car->prev_value = value;
+}
+
+