Change how checking for missing values works.
[pspp] / src / language / stats / aggregate.c
index f4cbaac448b3acfbd67fa81c8775176fbb6b33e0..721dba73d3e7ad598a1c7f0839ff2c046b5fc1ab 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010, 2011, 2012, 2014 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
@@ -49,6 +49,7 @@
 #include "math/sort.h"
 #include "math/statistic.h"
 
+#include "gl/c-strcase.h"
 #include "gl/minmax.h"
 #include "gl/xalloc.h"
 
@@ -92,24 +93,24 @@ struct agr_var
 /* Attributes of aggregation functions. */
 const struct agr_func agr_func_tab[] =
   {
-    {"SUM",     N_("Sum of values"),                         AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
-    {"MEAN",   N_("Mean average"),                          AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
-    {"MEDIAN", N_("Median average"),                        AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
-    {"SD",      N_("Standard deviation"),                    AGR_SV_YES, 0, -1,          {FMT_F, 8, 2}},
+    {"SUM",     N_("Sum of values"),                         AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 2 }},
+    {"MEAN",   N_("Mean average"),                          AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 2 }},
+    {"MEDIAN", N_("Median average"),                        AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 2 }},
+    {"SD",      N_("Standard deviation"),                    AGR_SV_YES, 0, -1,          { .type = FMT_F, .w = 8, .d = 2 }},
     {"MAX",     N_("Maximum value"),                         AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
     {"MIN",     N_("Minimum value"),                         AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
-    {"PGT",     N_("Percentage greater than"),               AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"PLT",     N_("Percentage less than"),                  AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"PIN",     N_("Percentage included in range"),          AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"POUT",    N_("Percentage excluded from range"),        AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 1}},
-    {"FGT",     N_("Fraction greater than"),                 AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"FLT",     N_("Fraction less than"),                    AGR_SV_YES, 1, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"FIN",     N_("Fraction included in range"),            AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"FOUT",    N_("Fraction excluded from range"),          AGR_SV_YES, 2, VAL_NUMERIC, {FMT_F, 5, 3}},
-    {"N",       N_("Number of cases"),                       AGR_SV_NO,  0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"NU",      N_("Number of cases (unweighted)"),          AGR_SV_OPT, 0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"NMISS",   N_("Number of missing values"),              AGR_SV_YES, 0, VAL_NUMERIC, {FMT_F, 7, 0}},
-    {"NUMISS",  N_("Number of missing values (unweighted)"), AGR_SV_YES, 0, VAL_NUMERIC, {FMT_F, 7, 0}},
+    {"PGT",     N_("Percentage greater than"),               AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"PLT",     N_("Percentage less than"),                  AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"PIN",     N_("Percentage included in range"),          AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"POUT",    N_("Percentage excluded from range"),        AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 1 }},
+    {"FGT",     N_("Fraction greater than"),                 AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"FLT",     N_("Fraction less than"),                    AGR_SV_YES, 1, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"FIN",     N_("Fraction included in range"),            AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"FOUT",    N_("Fraction excluded from range"),          AGR_SV_YES, 2, VAL_NUMERIC, { .type = FMT_F, .w = 5, .d = 3 }},
+    {"N",       N_("Number of cases"),                       AGR_SV_NO,  0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
+    {"NU",      N_("Number of cases (unweighted)"),          AGR_SV_OPT, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
+    {"NMISS",   N_("Number of missing values"),              AGR_SV_YES, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
+    {"NUMISS",  N_("Number of missing values (unweighted)"), AGR_SV_YES, 0, VAL_NUMERIC, { .type = FMT_F, .w = 7, .d = 0 }},
     {"FIRST",   N_("First non-missing value"),               AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
     {"LAST",    N_("Last non-missing value"),                AGR_SV_YES, 0, VAL_STRING,  {-1, -1, -1}},
     {NULL,      NULL,                                        AGR_SV_NO,  0, -1,          {-1, -1, -1}},
@@ -128,13 +129,13 @@ struct agr_proc
     /* Break variables. */
     struct subcase sort;                /* Sort criteria (break variables). */
     const struct variable **break_vars;       /* Break variables. */
-    size_t break_var_cnt;               /* Number of break variables. */
+    size_t break_n_vars;                /* Number of break variables. */
 
     enum missing_treatment missing;     /* How to treat missing values. */
     struct agr_var *agr_vars;           /* First aggregate variable. */
     struct dictionary *dict;            /* Aggregate dictionary. */
     const struct dictionary *src_dict;  /* Dict of the source */
-    int case_cnt;                       /* Counts aggregated cases. */
+    int n_cases;                        /* Counts aggregated cases. */
 
     bool add_variables;                 /* True iff the aggregated variables should
                                           be appended to the existing dictionary */
@@ -205,7 +206,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
        goto error;
     }
 
-  if ( agr.add_variables )
+  if (agr.add_variables)
     agr.dict = dict_clone (dict);
   else
     agr.dict = dict_create (dict_get_encoding (dict));
@@ -223,7 +224,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
          lex_match (lexer, T_EQUALS);
          if (!lex_match_id (lexer, "COLUMNWISE"))
            {
-             lex_error_expecting (lexer, "COLUMNWISE", NULL);
+             lex_error_expecting (lexer, "COLUMNWISE");
               goto error;
            }
          agr.missing = COLUMNWISE;
@@ -240,10 +241,10 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
           if (!parse_sort_criteria (lexer, dict, &agr.sort, &agr.break_vars,
                                     &saw_direction))
             goto error;
-          agr.break_var_cnt = subcase_get_n_fields (&agr.sort);
+          agr.break_n_vars = subcase_get_n_fields (&agr.sort);
 
          if  (! agr.add_variables)
-           for (i = 0; i < agr.break_var_cnt; i++)
+           for (i = 0; i < agr.break_n_vars; i++)
              dict_clone_var_assert (agr.dict, agr.break_vars[i]);
 
           /* BREAK must follow the options. */
@@ -271,7 +272,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
   dict_set_split_vars (agr.dict, NULL, 0);
 
   /* Initialize. */
-  agr.case_cnt = 0;
+  agr.n_cases = 0;
 
   if (out_file == NULL)
     {
@@ -296,7 +297,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
     }
 
   for (grouper = casegrouper_create_vars (input, agr.break_vars,
-                                          agr.break_var_cnt);
+                                          agr.break_n_vars);
        casegrouper_get_next_group (grouper, &group);
        casereader_destroy (group))
     {
@@ -311,7 +312,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
 
       initialize_aggregate_info (&agr);
 
-      if ( agr.add_variables )
+      if (agr.add_variables)
        placeholder = casereader_clone (group);
 
       {
@@ -332,8 +333,8 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
       else
        {
          dump_aggregate_info (&agr, output, c);
-         case_unref (c);
        }
+      case_unref (c);
     }
   if (!casegrouper_destroy (grouper))
     goto error;
@@ -452,7 +453,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       exclude = ds_chomp_byte (&function_name, '.') ? MV_SYSTEM : MV_ANY;
 
       for (function = agr_func_tab; function->name; function++)
-       if (!strcasecmp (function->name, ds_cstr (&function_name)))
+       if (!c_strcasecmp (function->name, ds_cstr (&function_name)))
          break;
       if (NULL == function->name)
        {
@@ -469,7 +470,6 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
        {
          if (function->src_vars == AGR_SV_YES)
            {
-              lex_force_match (lexer, T_LPAREN);
              goto error;
            }
        }
@@ -565,7 +565,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
          variables. */
       for (i = 0; i < n_dest; i++)
        {
-         struct agr_var *v = xzalloc (sizeof *v);
+         struct agr_var *v = XZALLOC (struct agr_var);
 
          /* Add variable to chain. */
          if (agr->agr_vars != NULL)
@@ -638,7 +638,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
 
            free (dest[i]);
            if (dest_label[i])
-              var_set_label (destvar, dest_label[i], true);
+              var_set_label (destvar, dest_label[i]);
 
            v->dest = destvar;
          }
@@ -733,7 +733,7 @@ agr_destroy (struct agr_proc *agr)
       free (iter);
     }
   if (agr->dict != NULL)
-    dict_destroy (agr->dict);
+    dict_unref (agr->dict);
 }
 \f
 /* Execution. */
@@ -754,7 +754,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
        const union value *v = case_data (input, iter->src);
         int src_width = var_get_width (iter->src);
 
-        if (var_is_value_missing (iter->src, v, iter->exclude))
+        if (var_is_value_missing (iter->src, v) & iter->exclude)
          {
            switch (iter->function)
              {
@@ -789,12 +789,11 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
 
               cout = case_create (casewriter_get_proto (iter->writer));
 
-             case_data_rw (cout, iter->subject)->f
-                = case_data (input, iter->src)->f;
+             *case_num_rw (cout, iter->subject) = case_num (input, iter->src);
 
              wv = dict_get_case_weight (agr->src_dict, input, NULL);
 
-             case_data_rw (cout, iter->weight)->f = wv;
+             *case_num_rw (cout, iter->weight) = wv;
 
              iter->cc += wv;
 
@@ -810,8 +809,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            break;
          case MAX | FSTRING:
             /* Need to do some kind of Unicode collation thingy here */
-           if (memcmp (iter->string, value_str (v, src_width), src_width) < 0)
-             memcpy (iter->string, value_str (v, src_width), src_width);
+           if (memcmp (iter->string, v->s, src_width) < 0)
+             memcpy (iter->string, v->s, src_width);
            iter->int1 = 1;
            break;
          case MIN:
@@ -819,8 +818,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case MIN | FSTRING:
-           if (memcmp (iter->string, value_str (v, src_width), src_width) > 0)
-             memcpy (iter->string, value_str (v, src_width), src_width);
+           if (memcmp (iter->string, v->s, src_width) > 0)
+             memcpy (iter->string, v->s, src_width);
            iter->int1 = 1;
            break;
          case FGT:
@@ -831,8 +830,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FGT | FSTRING:
          case PGT | FSTRING:
-            if (memcmp (iter->arg[0].c,
-                        value_str (v, src_width), src_width) < 0)
+            if (memcmp (iter->arg[0].c, v->s, src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -844,8 +842,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FLT | FSTRING:
          case PLT | FSTRING:
-            if (memcmp (iter->arg[0].c,
-                        value_str (v, src_width), src_width) > 0)
+            if (memcmp (iter->arg[0].c, v->s, src_width) > 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -857,10 +854,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FIN | FSTRING:
          case PIN | FSTRING:
-            if (memcmp (iter->arg[0].c,
-                        value_str (v, src_width), src_width) <= 0
-                && memcmp (iter->arg[1].c,
-                           value_str (v, src_width), src_width) >= 0)
+            if (memcmp (iter->arg[0].c, v->s, src_width) <= 0
+                && memcmp (iter->arg[1].c, v->s, src_width) >= 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -872,10 +867,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FOUT | FSTRING:
          case POUT | FSTRING:
-            if (memcmp (iter->arg[0].c,
-                        value_str (v, src_width), src_width) > 0
-                || memcmp (iter->arg[1].c,
-                           value_str (v, src_width), src_width) < 0)
+            if (memcmp (iter->arg[0].c, v->s, src_width) > 0
+                || memcmp (iter->arg[1].c, v->s, src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -897,7 +890,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
          case FIRST | FSTRING:
            if (iter->int1 == 0)
              {
-               memcpy (iter->string, value_str (v, src_width), src_width);
+               memcpy (iter->string, v->s, src_width);
                iter->int1 = 1;
              }
            break;
@@ -906,7 +899,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case LAST | FSTRING:
-           memcpy (iter->string, value_str (v, src_width), src_width);
+           memcpy (iter->string, v->s, src_width);
            iter->int1 = 1;
            break;
           case NMISS:
@@ -940,16 +933,16 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
 {
   struct ccase *c = case_create (dict_get_proto (agr->dict));
 
-  if ( agr->add_variables)
+  if (agr->add_variables)
     {
-      case_copy (c, 0, break_case, 0, dict_get_var_cnt (agr->src_dict));
+      case_copy (c, 0, break_case, 0, dict_get_n_vars (agr->src_dict));
     }
   else
     {
       int value_idx = 0;
       int i;
 
-      for (i = 0; i < agr->break_var_cnt; i++)
+      for (i = 0; i < agr->break_n_vars; i++)
        {
          const struct variable *v = agr->break_vars[i];
          value_copy (case_data_rw_idx (c, value_idx),
@@ -986,7 +979,7 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
            break;
          case MEDIAN:
            {
-             if ( i->writer)
+             if (i->writer)
                {
                  struct percentile *median = percentile_create (0.5, i->cc);
                  struct order_stats *os = &median->parent;
@@ -1024,7 +1017,7 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
          case MAX | FSTRING:
          case MIN | FSTRING:
            if (i->int1)
-             memcpy (value_str_rw (v, width), i->string, width);
+             memcpy (v->s, i->string, width);
            else
               value_set_missing (v, width);
            break;
@@ -1063,7 +1056,7 @@ dump_aggregate_info (const struct agr_proc *agr, struct casewriter *output, cons
          case FIRST | FSTRING:
          case LAST | FSTRING:
            if (i->int1)
-             memcpy (value_str_rw (v, width), i->string, width);
+             memcpy (v->s, i->string, width);
            else
               value_set_missing (v, width);
            break;
@@ -1118,10 +1111,10 @@ initialize_aggregate_info (struct agr_proc *agr)
             proto = caseproto_add_width (proto, 0);
             proto = caseproto_add_width (proto, 0);
 
-           if ( ! iter->subject)
+           if (! iter->subject)
              iter->subject = dict_create_internal_var (0, 0);
 
-           if ( ! iter->weight)
+           if (! iter->weight)
              iter->weight = dict_create_internal_var (1, 0);
 
             subcase_init_var (&ordering, iter->subject, SC_ASCEND);