GRAPH: Improve error messages and coding style.
[pspp] / src / language / stats / graph.c
1 /*
2   PSPP - a program for statistical analysis.
3   Copyright (C) 2012, 2013, 2015, 2019 Free Software Foundation, Inc.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20  * This module implements the graph command
21  */
22
23 #include <config.h>
24
25 #include <math.h>
26 #include "gl/xalloc.h"
27 #include <gsl/gsl_cdf.h>
28
29 #include "libpspp/assertion.h"
30 #include "libpspp/message.h"
31 #include "libpspp/pool.h"
32
33
34 #include "data/dataset.h"
35 #include "data/dictionary.h"
36 #include "data/casegrouper.h"
37 #include "data/casereader.h"
38 #include "data/casewriter.h"
39 #include "data/caseproto.h"
40 #include "data/subcase.h"
41
42
43 #include "data/format.h"
44
45 #include "math/chart-geometry.h"
46 #include "math/histogram.h"
47 #include "math/moments.h"
48 #include "math/sort.h"
49 #include "math/order-stats.h"
50 #include "output/charts/plot-hist.h"
51 #include "output/charts/scatterplot.h"
52 #include "output/charts/barchart.h"
53
54 #include "language/command.h"
55 #include "language/lexer/lexer.h"
56 #include "language/lexer/value-parser.h"
57 #include "language/lexer/variable-parser.h"
58 #include "language/stats/freq.h"
59 #include "language/stats/chart-category.h"
60
61 #include "gettext.h"
62 #define _(msgid) gettext (msgid)
63 #define N_(msgid) msgid
64
65 enum chart_type
66   {
67     CT_NONE,
68     CT_BAR,
69     CT_LINE,
70     CT_PIE,
71     CT_ERRORBAR,
72     CT_HILO,
73     CT_HISTOGRAM,
74     CT_SCATTERPLOT,
75     CT_PARETO
76   };
77
78 enum scatter_type
79   {
80     ST_BIVARIATE,
81     ST_OVERLAY,
82     ST_MATRIX,
83     ST_XYZ
84   };
85
86 enum  bar_type
87   {
88     CBT_SIMPLE,
89     CBT_GROUPED,
90     CBT_STACKED,
91     CBT_RANGE
92   };
93
94
95 /* Variable index for histogram case */
96 enum
97   {
98     HG_IDX_X,
99     HG_IDX_WT
100   };
101
102 struct exploratory_stats
103 {
104   double missing;
105   double non_missing;
106
107   struct moments *mom;
108
109   double minimum;
110   double maximum;
111
112   /* Total weight */
113   double cc;
114
115   /* The minimum weight */
116   double cmin;
117 };
118
119
120 struct graph
121 {
122   struct pool *pool;
123
124   size_t n_dep_vars;
125   const struct variable **dep_vars;
126   struct exploratory_stats *es;
127
128   enum mv_class dep_excl;
129   enum mv_class fctr_excl;
130
131   const struct dictionary *dict;
132
133   bool missing_pw;
134
135   /* ------------ Graph ---------------- */
136   bool normal; /* For histograms, draw the normal curve */
137
138   enum chart_type chart_type;
139   enum scatter_type scatter_type;
140   enum bar_type bar_type;
141   const struct variable *by_var[2];
142   size_t n_by_vars;
143
144   struct subcase ordering; /* Ordering for aggregation */
145   int agr; /* Index into ag_func */
146
147   /* A caseproto that contains the plot data */
148   struct caseproto *gr_proto;
149 };
150
151
152
153
154 static double
155 calc_mom1 (double acc, double x, double w)
156 {
157   return acc + x * w;
158 }
159
160 static double
161 calc_mom0 (double acc, double x UNUSED, double w)
162 {
163   return acc + w;
164 }
165
166 static double
167 pre_low_extreme (void)
168 {
169   return -DBL_MAX;
170 }
171
172 static double
173 calc_max (double acc, double x, double w UNUSED)
174 {
175   return (acc > x) ? acc : x;
176 }
177
178 static double
179 pre_high_extreme (void)
180 {
181   return DBL_MAX;
182 }
183
184 static double
185 calc_min (double acc, double x, double w UNUSED)
186 {
187   return (acc < x) ? acc : x;
188 }
189
190 static double
191 post_normalise (double acc, double cc)
192 {
193   return acc / cc;
194 }
195
196 static double
197 post_percentage (double acc, double ccc)
198 {
199   return acc / ccc * 100.0;
200 }
201
202 const struct ag_func ag_func[] =
203   {
204     {"COUNT",   N_("Count"),      0, 0, NULL, calc_mom0, 0, 0},
205     {"PCT",     N_("Percentage"), 0, 0, NULL, calc_mom0, 0, post_percentage},
206     {"CUFREQ",  N_("Cumulative Count"),   0, 1, NULL, calc_mom0, 0, 0},
207     {"CUPCT",   N_("Cumulative Percent"), 0, 1, NULL, calc_mom0, 0,
208      post_percentage},
209
210     {"MEAN",    N_("Mean"),    1, 0, NULL, calc_mom1, post_normalise, 0},
211     {"SUM",     N_("Sum"),     1, 0, NULL, calc_mom1, 0, 0},
212     {"MAXIMUM", N_("Maximum"), 1, 0, pre_low_extreme, calc_max, 0, 0},
213     {"MINIMUM", N_("Minimum"), 1, 0, pre_high_extreme, calc_min, 0, 0},
214   };
215
216 const int N_AG_FUNCS = sizeof (ag_func) / sizeof (ag_func[0]);
217
218 static bool
219 parse_function_name (struct lexer *lexer, int *agr)
220 {
221   for (size_t i = 0; i < N_AG_FUNCS; ++i)
222     {
223       if (lex_match_id (lexer, ag_func[i].name))
224         {
225           *agr = i;
226           return true;
227         }
228     }
229
230   const char *ag_func_names[N_AG_FUNCS];
231   for (size_t i = 0; i < N_AG_FUNCS; ++i)
232     ag_func_names[i] = ag_func[i].name;
233   lex_error_expecting_array (lexer, ag_func_names, N_AG_FUNCS);
234   return false;
235 }
236
237 static bool
238 parse_function (struct lexer *lexer, struct graph *graph)
239 {
240   if (!parse_function_name (lexer, &graph->agr))
241     return false;
242
243   size_t arity = ag_func[graph->agr].arity;
244   graph->n_dep_vars = arity;
245   if (arity > 0)
246     {
247       if (!lex_force_match (lexer, T_LPAREN))
248         return false;
249
250       graph->dep_vars = xcalloc (graph->n_dep_vars, sizeof (graph->dep_vars));
251       for (int v = 0; v < arity; ++v)
252         {
253           graph->dep_vars[v] = parse_variable (lexer, graph->dict);
254           if (!graph->dep_vars[v])
255             return false;
256         }
257
258       if (!lex_force_match (lexer, T_RPAREN))
259         return false;
260     }
261
262   if (!lex_force_match (lexer, T_BY))
263     return false;
264
265   graph->by_var[0] = parse_variable (lexer, graph->dict);
266   if (!graph->by_var[0])
267     return false;
268   subcase_add_var (&graph->ordering, graph->by_var[0], SC_ASCEND);
269   graph->n_by_vars++;
270
271   if (lex_match (lexer, T_BY))
272     {
273       graph->by_var[1] = parse_variable (lexer, graph->dict);
274       if (!graph->by_var[1])
275         return false;
276       subcase_add_var (&graph->ordering, graph->by_var[1], SC_ASCEND);
277       graph->n_by_vars++;
278     }
279
280   return true;
281 }
282
283 static void
284 show_scatterplot (const struct graph *cmd, struct casereader *input)
285 {
286   struct scatterplot_chart *scatterplot;
287   bool byvar_overflow = false;
288
289   char *title = (cmd->n_by_vars > 0
290                  ? xasprintf (_("%s vs. %s by %s"),
291                               var_to_string (cmd->dep_vars[1]),
292                               var_to_string (cmd->dep_vars[0]),
293                               var_to_string (cmd->by_var[0]))
294                  : xasprintf (_("%s vs. %s"),
295                               var_to_string (cmd->dep_vars[1]),
296                               var_to_string (cmd->dep_vars[0])));;
297
298   scatterplot = scatterplot_create (input,
299                                     var_to_string(cmd->dep_vars[0]),
300                                     var_to_string(cmd->dep_vars[1]),
301                                     (cmd->n_by_vars > 0) ? cmd->by_var[0]
302                                                          : NULL,
303                                     &byvar_overflow,
304                                     title,
305                                     cmd->es[0].minimum, cmd->es[0].maximum,
306                                     cmd->es[1].minimum, cmd->es[1].maximum);
307   scatterplot_chart_submit (scatterplot);
308   free (title);
309
310   if (byvar_overflow)
311     msg (MW, _("Maximum number of scatterplot categories reached. "
312                "Your BY variable has too many distinct values. "
313                "The coloring of the plot will not be correct."));
314 }
315
316 static void
317 show_histogr (const struct graph *cmd, struct casereader *input)
318 {
319   struct histogram *histogram;
320
321   if (cmd->es[0].cc <= 0)
322     {
323       casereader_destroy (input);
324       return;
325     }
326
327   /* Sturges Rule */
328   double bin_width = fabs (cmd->es[0].minimum - cmd->es[0].maximum)
329     / (1 + log2 (cmd->es[0].cc));
330   histogram = histogram_create (bin_width,
331                                 cmd->es[0].minimum, cmd->es[0].maximum);
332   if (!histogram)
333     {
334       casereader_destroy (input);
335       return;
336     }
337
338   struct ccase *c;
339   for (; (c = casereader_read (input)) != NULL; case_unref (c))
340     {
341       const double x      = case_num_idx (c, HG_IDX_X);
342       const double weight = case_num_idx (c, HG_IDX_WT);
343       moments_pass_two (cmd->es[0].mom, x, weight);
344       histogram_add (histogram, x, weight);
345     }
346   casereader_destroy (input);
347
348   const char *label = var_to_string (cmd->dep_vars[0]);
349   double n, mean, var;
350   moments_calculate (cmd->es[0].mom, &n, &mean, &var, NULL, NULL);
351   chart_submit (histogram_chart_create (histogram->gsl_hist, label, n, mean,
352                                         sqrt (var), cmd->normal));
353
354   statistic_destroy (&histogram->parent);
355 }
356
357 static void
358 cleanup_exploratory_stats (struct graph *cmd)
359 {
360   for (size_t v = 0; v < cmd->n_dep_vars; ++v)
361     moments_destroy (cmd->es[v].mom);
362 }
363
364 static bool
365 any_categorical_missing (const struct graph *cmd, const struct ccase *c)
366 {
367   for (size_t v = 0; v < cmd->n_by_vars; ++v)
368     if (var_is_value_missing (cmd->by_var[v], case_data (c, cmd->by_var[v]))
369         & cmd->fctr_excl)
370       return true;
371   return false;
372 }
373
374 static struct freq *
375 find_fcol (struct hmap *columns, const union value *value, size_t hash,
376            int width)
377 {
378   struct freq *fcol;
379   HMAP_FOR_EACH_WITH_HASH (fcol, struct freq, node, hash, columns)
380     if (value_equal (value, &fcol->values[0], width))
381       return fcol;
382   return NULL;
383 }
384
385 static void
386 run_barchart (struct graph *cmd, struct casereader *input)
387 {
388   double ccc = 0.0;
389
390   if (cmd->missing_pw == false)
391     input = casereader_create_filter_missing (input,
392                                               cmd->dep_vars,
393                                               cmd->n_dep_vars,
394                                               cmd->dep_excl,
395                                               NULL,
396                                               NULL);
397
398
399   input = sort_execute (input, &cmd->ordering);
400
401   struct freq **cells = NULL;
402   size_t n_cells = 0;
403   size_t allocated_cells = 0;
404
405   struct hmap columns = HMAP_INITIALIZER (columns);
406   assert (cmd->n_by_vars <= 2);
407   struct casegrouper *grouper = casegrouper_create_vars (input, cmd->by_var,
408                                                          cmd->n_by_vars);
409   struct casereader *group;
410   for (; casegrouper_get_next_group (grouper, &group);
411        casereader_destroy (group))
412     {
413       struct ccase *c = casereader_peek (group, 0);
414       if (any_categorical_missing (cmd, c))
415         {
416           case_unref (c);
417           continue;
418         }
419
420       if (n_cells >= allocated_cells)
421         cells = x2nrealloc (cells, &allocated_cells, sizeof *cells);
422       cells[n_cells++] = xzalloc (table_entry_size (cmd->n_by_vars));
423
424       if (ag_func[cmd->agr].cumulative && n_cells >= 2)
425         cells[n_cells - 1]->count = cells[n_cells - 2]->count;
426       else
427         cells[n_cells - 1]->count = 0;
428       if (ag_func[cmd->agr].pre)
429         cells[n_cells - 1]->count = ag_func[cmd->agr].pre();
430
431       if (cmd->n_by_vars > 1)
432         {
433           const union value *vv = case_data (c, cmd->by_var[1]);
434           const double weight = dict_get_case_weight (cmd->dict, c, NULL);
435           int v1_width = var_get_width (cmd->by_var[1]);
436           size_t hash = value_hash (vv, v1_width, 0);
437
438           struct freq *fcol = find_fcol (&columns, vv, hash, v1_width);
439           if (!fcol)
440             {
441               fcol = xzalloc (sizeof *fcol);
442               value_clone (&fcol->values[0], vv, v1_width);
443               hmap_insert (&columns, &fcol->node, hash);
444             }
445           fcol->count += weight;
446         }
447
448       for (size_t v = 0; v < cmd->n_by_vars; ++v)
449         value_clone (&cells[n_cells - 1]->values[v],
450                      case_data (c, cmd->by_var[v]),
451                      var_get_width (cmd->by_var[v]));
452       case_unref (c);
453
454       double cc = 0;
455       for (; (c = casereader_read (group)) != NULL; case_unref (c))
456         {
457           const double weight = dict_get_case_weight (cmd->dict, c, NULL);
458           const double x = (cmd->n_dep_vars > 0
459                             ? case_num (c, cmd->dep_vars[0]) : SYSMIS);
460
461           cc += weight;
462           cells[n_cells - 1]->count
463             = ag_func[cmd->agr].calc (cells[n_cells - 1]->count, x, weight);
464         }
465
466       if (ag_func[cmd->agr].post)
467         cells[n_cells - 1]->count
468           = ag_func[cmd->agr].post (cells[n_cells - 1]->count, cc);
469
470       ccc += cc;
471     }
472
473   casegrouper_destroy (grouper);
474
475   for (int i = 0; i < n_cells; ++i)
476     {
477       if (ag_func[cmd->agr].ppost)
478         {
479           struct freq *cell = cells[i];
480           if (cmd->n_by_vars > 1)
481             {
482               const union value *vv = &cell->values[1];
483
484               int v1_width = var_get_width (cmd->by_var[1]);
485               size_t hash = value_hash (vv, v1_width, 0);
486
487               struct freq *fcol = find_fcol (&columns, vv, hash, v1_width);
488               cell->count = ag_func[cmd->agr].ppost (cell->count, fcol->count);
489             }
490           else
491             cell->count = ag_func[cmd->agr].ppost (cell->count, ccc);
492         }
493     }
494
495   if (cmd->n_by_vars > 1)
496     {
497       struct freq *cell, *next;
498       HMAP_FOR_EACH_SAFE (cell, next, struct freq, node, &columns)
499         {
500           value_destroy (cell->values, var_get_width (cmd->by_var[1]));
501           free (cell);
502         }
503     }
504   hmap_destroy (&columns);
505
506   char *label = (cmd->n_dep_vars > 0
507                  ? xasprintf (_("%s of %s"),
508                               ag_func[cmd->agr].description,
509                               var_get_name (cmd->dep_vars[0]))
510                  : xstrdup (ag_func[cmd->agr].description));
511   chart_submit (barchart_create (cmd->by_var, cmd->n_by_vars, label, false,
512                                  cells, n_cells));
513   free (label);
514
515   for (int i = 0; i < n_cells; ++i)
516     free (cells[i]);
517
518   free (cells);
519 }
520
521 static void
522 run_graph (struct graph *cmd, struct casereader *input)
523 {
524   cmd->es = pool_nmalloc (cmd->pool, cmd->n_dep_vars, sizeof *cmd->es);
525   for (int v = 0; v < cmd->n_dep_vars; v++)
526     cmd->es[v] = (struct exploratory_stats) {
527       .mom = moments_create (MOMENT_KURTOSIS),
528       .cmin = DBL_MAX,
529       .maximum = -DBL_MAX,
530       .minimum =  DBL_MAX,
531     };
532
533   /* Always remove cases listwise. This is correct for the histogram because
534      there is only one variable and a simple bivariate scatterplot. */
535   input = casereader_create_filter_missing (input,
536                                             cmd->dep_vars,
537                                             cmd->n_dep_vars,
538                                             cmd->dep_excl,
539                                             NULL,
540                                             NULL);
541
542   struct casewriter *writer = autopaging_writer_create (cmd->gr_proto);
543
544   /* The case data is copied to a new writer.
545      The setup of the case depends on the chart type.
546
547      For Scatterplot:
548      - x is assumed in dep_vars[0].
549      - y is assumed in dep_vars[1].
550
551      For Histogram:
552      - x is assumed in dep_vars[0]. */
553   assert (SP_IDX_X == 0 && SP_IDX_Y == 1 && HG_IDX_X == 0);
554
555   struct ccase *c;
556   for (; (c = casereader_read (input)) != NULL; case_unref (c))
557     {
558       struct ccase *outcase = case_create (cmd->gr_proto);
559       const double weight = dict_get_case_weight (cmd->dict, c, NULL);
560       if (cmd->chart_type == CT_HISTOGRAM)
561         *case_num_rw_idx (outcase, HG_IDX_WT) = weight;
562       if (cmd->chart_type == CT_SCATTERPLOT && cmd->n_by_vars > 0)
563         value_copy (case_data_rw_idx (outcase, SP_IDX_BY),
564                     case_data (c, cmd->by_var[0]),
565                     var_get_width (cmd->by_var[0]));
566       for (int v = 0; v < cmd->n_dep_vars; v++)
567         {
568           const struct variable *var = cmd->dep_vars[v];
569           const double x = case_num (c, var);
570
571           if (var_is_value_missing (var, case_data (c, var)) & cmd->dep_excl)
572             {
573               cmd->es[v].missing += weight;
574               continue;
575             }
576
577           /* Magically v value fits to SP_IDX_X, SP_IDX_Y, HG_IDX_X. */
578           *case_num_rw_idx (outcase, v) = x;
579
580           if (x > cmd->es[v].maximum)
581             cmd->es[v].maximum = x;
582
583           if (x < cmd->es[v].minimum)
584             cmd->es[v].minimum =  x;
585
586           cmd->es[v].non_missing += weight;
587
588           moments_pass_one (cmd->es[v].mom, x, weight);
589
590           cmd->es[v].cc += weight;
591
592           if (cmd->es[v].cmin > weight)
593             cmd->es[v].cmin = weight;
594         }
595       casewriter_write (writer, outcase);
596     }
597
598   struct casereader *reader = casewriter_make_reader (writer);
599   switch (cmd->chart_type)
600     {
601     case CT_HISTOGRAM:
602       show_histogr (cmd,reader);
603       break;
604
605     case CT_SCATTERPLOT:
606       show_scatterplot (cmd,reader);
607       break;
608
609     case CT_NONE:
610     case CT_BAR:
611     case CT_LINE:
612     case CT_PIE:
613     case CT_ERRORBAR:
614     case CT_HILO:
615     case CT_PARETO:
616       NOT_REACHED ();
617     }
618
619   casereader_destroy (input);
620   cleanup_exploratory_stats (cmd);
621 }
622
623 int
624 cmd_graph (struct lexer *lexer, struct dataset *ds)
625 {
626   struct graph graph = {
627     .missing_pw = false,
628
629     .pool = pool_create (),
630
631     .dep_excl = MV_ANY,
632     .fctr_excl = MV_ANY,
633
634     .dict = dataset_dict (ds),
635
636     .chart_type = CT_NONE,
637     .scatter_type = ST_BIVARIATE,
638     .gr_proto = caseproto_create (),
639     .ordering = SUBCASE_EMPTY_INITIALIZER,
640   };
641
642   while (lex_token (lexer) != T_ENDCMD)
643     {
644       lex_match (lexer, T_SLASH);
645
646       if (lex_match_id (lexer, "HISTOGRAM"))
647         {
648           if (graph.chart_type != CT_NONE)
649             {
650               lex_next_error (lexer, -1, -1,
651                               _("Only one chart type is allowed."));
652               goto error;
653             }
654           graph.normal = false;
655           if (lex_match (lexer, T_LPAREN))
656             {
657               if (!lex_force_match_phrase (lexer, "NORMAL)"))
658                 goto error;
659
660               graph.normal = true;
661             }
662           if (!lex_force_match (lexer, T_EQUALS))
663             goto error;
664           graph.chart_type = CT_HISTOGRAM;
665           int vars_start = lex_ofs (lexer);
666           if (!parse_variables_const (lexer, graph.dict,
667                                       &graph.dep_vars, &graph.n_dep_vars,
668                                       PV_NO_DUPLICATE | PV_NUMERIC))
669             goto error;
670           if (graph.n_dep_vars > 1)
671             {
672               lex_ofs_error (lexer, vars_start, lex_ofs (lexer) - 1,
673                              _("Only one variable is allowed."));
674               goto error;
675             }
676         }
677       else if (lex_match_id (lexer, "BAR"))
678         {
679           if (graph.chart_type != CT_NONE)
680             {
681               lex_next_error (lexer, -1, -1,
682                               _("Only one chart type is allowed."));
683               goto error;
684             }
685           graph.chart_type = CT_BAR;
686           graph.bar_type = CBT_SIMPLE;
687
688           if (lex_match (lexer, T_LPAREN))
689             {
690               if (lex_match_id (lexer, "SIMPLE"))
691                 {
692                   /* This is the default anyway */
693                 }
694               else if (lex_match_id (lexer, "GROUPED"))
695                 {
696                   graph.bar_type = CBT_GROUPED;
697                   lex_next_error (lexer, -1, -1,
698                                   _("%s is not yet implemented."), "GROUPED");
699                   goto error;
700                 }
701               else if (lex_match_id (lexer, "STACKED"))
702                 {
703                   graph.bar_type = CBT_STACKED;
704                   lex_next_error (lexer, -1, -1,
705                                   _("%s is not yet implemented."), "STACKED");
706                   goto error;
707                 }
708               else if (lex_match_id (lexer, "RANGE"))
709                 {
710                   graph.bar_type = CBT_RANGE;
711                   lex_next_error (lexer, -1, -1,
712                                   _("%s is not yet implemented."), "RANGE");
713                   goto error;
714                 }
715               else
716                 {
717                   lex_error_expecting (lexer, "SIMPLE", "GROUPED",
718                                        "STACKED", "RANGE");
719                   goto error;
720                 }
721               if (!lex_force_match (lexer, T_RPAREN))
722                 goto error;
723             }
724
725           if (!lex_force_match (lexer, T_EQUALS))
726             goto error;
727
728           if (!parse_function (lexer, &graph))
729             goto error;
730         }
731       else if (lex_match_id (lexer, "SCATTERPLOT"))
732         {
733           if (graph.chart_type != CT_NONE)
734             {
735               lex_next_error (lexer, -1, -1,
736                               _("Only one chart type is allowed."));
737               goto error;
738             }
739           graph.chart_type = CT_SCATTERPLOT;
740           if (lex_match (lexer, T_LPAREN))
741             {
742               if (lex_match_id (lexer, "BIVARIATE"))
743                 {
744                   /* This is the default anyway */
745                 }
746               else if (lex_match_id (lexer, "OVERLAY"))
747                 {
748                   lex_next_error (lexer, -1, -1,
749                                   _("%s is not yet implemented."),"OVERLAY");
750                   goto error;
751                 }
752               else if (lex_match_id (lexer, "MATRIX"))
753                 {
754                   lex_next_error (lexer, -1, -1,
755                                   _("%s is not yet implemented."),"MATRIX");
756                   goto error;
757                 }
758               else if (lex_match_id (lexer, "XYZ"))
759                 {
760                   lex_next_error (lexer, -1, -1,
761                                   _("%s is not yet implemented."),"XYZ");
762                   goto error;
763                 }
764               else
765                 {
766                   lex_error_expecting (lexer, "BIVARIATE", "OVERLAY",
767                                        "MATRIX", "XYZ");
768                   goto error;
769                 }
770               if (!lex_force_match (lexer, T_RPAREN))
771                 goto error;
772             }
773           if (!lex_force_match (lexer, T_EQUALS))
774             goto error;
775
776           int vars_start = lex_ofs (lexer);
777           if (!parse_variables_const (lexer, graph.dict,
778                                       &graph.dep_vars, &graph.n_dep_vars,
779                                       PV_NO_DUPLICATE | PV_NUMERIC))
780             goto error;
781
782           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 1)
783             {
784               lex_ofs_error (lexer, vars_start, lex_ofs (lexer) - 1,
785                              _("Only one variable is allowed."));
786               goto error;
787             }
788
789           if (!lex_force_match (lexer, T_WITH))
790             goto error;
791
792           vars_start = lex_ofs (lexer);
793           if (!parse_variables_const (lexer, graph.dict,
794                                       &graph.dep_vars, &graph.n_dep_vars,
795                                       PV_NO_DUPLICATE | PV_NUMERIC | PV_APPEND))
796             goto error;
797
798           if (graph.scatter_type == ST_BIVARIATE && graph.n_dep_vars != 2)
799             {
800               lex_ofs_error (lexer, vars_start, lex_ofs (lexer) - 1,
801                              _("Only one variable is allowed."));
802               goto error;
803             }
804
805           if (lex_match (lexer, T_BY))
806             {
807               const struct variable *v = NULL;
808               if (!lex_match_variable (lexer,graph.dict,&v))
809                 {
810                   lex_error (lexer, _("Syntax error expecting variable name."));
811                   goto error;
812                 }
813               graph.by_var[0] = v;
814               graph.n_by_vars = 1;
815             }
816         }
817       else if (lex_match_id (lexer, "LINE"))
818         {
819           lex_next_error (lexer, -1, -1,
820                           _("%s is not yet implemented."),"LINE");
821           goto error;
822         }
823       else if (lex_match_id (lexer, "PIE"))
824         {
825           lex_next_error (lexer, -1, -1,
826                           _("%s is not yet implemented."),"PIE");
827           goto error;
828         }
829       else if (lex_match_id (lexer, "ERRORBAR"))
830         {
831           lex_next_error (lexer, -1, -1,
832                           _("%s is not yet implemented."),"ERRORBAR");
833           goto error;
834         }
835       else if (lex_match_id (lexer, "PARETO"))
836         {
837           lex_next_error (lexer, -1, -1,
838                           _("%s is not yet implemented."),"PARETO");
839           goto error;
840         }
841       else if (lex_match_id (lexer, "TITLE"))
842         {
843           lex_next_error (lexer, -1, -1,
844                           _("%s is not yet implemented."),"TITLE");
845           goto error;
846         }
847       else if (lex_match_id (lexer, "SUBTITLE"))
848         {
849           lex_next_error (lexer, -1, -1,
850                           _("%s is not yet implemented."),"SUBTITLE");
851           goto error;
852         }
853       else if (lex_match_id (lexer, "FOOTNOTE"))
854         {
855           lex_next_error (lexer, -1, -1,
856                           _("%s is not yet implemented."),"FOOTNOTE");
857           goto error;
858         }
859       else if (lex_match_id (lexer, "MISSING"))
860         {
861           lex_match (lexer, T_EQUALS);
862
863           while (lex_token (lexer) != T_ENDCMD
864                  && lex_token (lexer) != T_SLASH)
865             {
866               if (lex_match_id (lexer, "LISTWISE"))
867                 graph.missing_pw = false;
868               else if (lex_match_id (lexer, "VARIABLE"))
869                 graph.missing_pw = true;
870               else if (lex_match_id (lexer, "EXCLUDE"))
871                 graph.dep_excl = MV_ANY;
872               else if (lex_match_id (lexer, "INCLUDE"))
873                 graph.dep_excl = MV_SYSTEM;
874               else if (lex_match_id (lexer, "REPORT"))
875                 graph.fctr_excl = 0;
876               else if (lex_match_id (lexer, "NOREPORT"))
877                 graph.fctr_excl = MV_ANY;
878               else
879                 {
880                   lex_error_expecting (lexer, "LISTWISE", "VARIABLE",
881                                        "EXCLUDE", "INCLUDE",
882                                        "REPORT", "NOREPORT");
883                   goto error;
884                 }
885             }
886         }
887       else
888         {
889           lex_error_expecting (lexer, "HISTOGRAM", "BAR", "SCATTERPLOT", "LINE",
890                                "PIE", "ERRORBAR", "PARETO", "TITLE", "SUBTITLE",
891                                "FOOTNOTE", "MISSING");
892           goto error;
893         }
894     }
895
896   switch (graph.chart_type)
897     {
898     case CT_SCATTERPLOT:
899       /* See scatterplot.h for the setup of the case prototype */
900
901       /* x value - SP_IDX_X*/
902       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
903
904       /* y value - SP_IDX_Y*/
905       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
906       /* The by_var contains the plot categories for the different xy
907          plot colors */
908       if (graph.n_by_vars > 0) /* SP_IDX_BY */
909         graph.gr_proto = caseproto_add_width (graph.gr_proto,
910                                               var_get_width(graph.by_var[0]));
911       break;
912
913     case CT_HISTOGRAM:
914       /* x value      */
915       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
916       /* weight value */
917       graph.gr_proto = caseproto_add_width (graph.gr_proto, 0);
918       break;
919
920     case CT_BAR:
921       break;
922
923     case CT_NONE:
924       lex_error_expecting (lexer, "HISTOGRAM", "SCATTERPLOT", "BAR");
925       goto error;
926
927     default:
928       NOT_REACHED ();
929       break;
930     }
931
932   struct casegrouper *grouper = casegrouper_create_splits (proc_open (ds), graph.dict);
933   struct casereader *group;
934   while (casegrouper_get_next_group (grouper, &group))
935     {
936       if (graph.chart_type == CT_BAR)
937         run_barchart (&graph, group);
938       else
939         run_graph (&graph, group);
940     }
941   bool ok = casegrouper_destroy (grouper);
942   ok = proc_commit (ds) && ok;
943
944   subcase_uninit (&graph.ordering);
945   free (graph.dep_vars);
946   pool_destroy (graph.pool);
947   caseproto_unref (graph.gr_proto);
948
949   return CMD_SUCCESS;
950
951  error:
952   subcase_uninit (&graph.ordering);
953   caseproto_unref (graph.gr_proto);
954   free (graph.dep_vars);
955   pool_destroy (graph.pool);
956
957   return CMD_FAILURE;
958 }