1 /*
2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
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 #include "libgretl.h"
21 #include "plotspec.h"
22 
23 struct plotbars_ {
24     int n;         /* number of available start-stop "bar" pairs */
25     double t1;     /* current min. value on time axis */
26     double t2;     /* current max. value on time axis */
27     double ymin;   /* current min. value on y axis */
28     double ymax;   /* current max. value on y axis */
29     double **dx;   /* start-stop time pairs */
30 };
31 
32 /* for handling "recession bars" or similar */
33 static int n_bars_shown (double xmin, double xmax, plotbars *bars);
34 static void print_bars_header (GPT_SPEC *spec, FILE *fp);
35 static void print_plotbars (GPT_SPEC *spec, FILE *fp);
36 
37 static void print_filledcurve_color (FILE *fp);
38 
plotspec_new(void)39 GPT_SPEC *plotspec_new (void)
40 {
41     GPT_SPEC *spec;
42     int i;
43 
44     spec = malloc(sizeof *spec);
45     if (spec == NULL) {
46 	return NULL;
47     }
48 
49     spec->datacols = 0;
50     spec->heredata = 0;
51 
52     spec->lines = NULL;
53     spec->n_lines = 0;
54 
55     spec->labels = NULL;
56     spec->n_labels = 0;
57 
58     spec->arrows = NULL;
59     spec->n_arrows = 0;
60 
61     spec->literal = NULL;
62     spec->n_literal = 0;
63 
64     for (i=0; i<5; i++) {
65 	spec->titles[i] = NULL;
66     }
67 
68     *spec->xvarname = '\0';
69     *spec->yvarname = '\0';
70 
71     spec->xticstr = NULL;
72     spec->x2ticstr = NULL;
73 
74     spec->timefmt[0] = 0;
75     spec->xfmt[0] = 0;
76     spec->xtics[0] = 0;
77     spec->mxtics[0] = 0;
78     spec->yfmt[0] = 0;
79     spec->ytics[0] = 0;
80     spec->fname[0] = 0;
81     spec->keyspec = GP_KEY_LEFT_TOP;
82 
83     for (i=0; i<5; i++) {
84 	spec->range[i][0] = NADBL;
85 	spec->range[i][1] = NADBL;
86 	if (i < 3) {
87 	    spec->logbase[i] = 0.0;
88 	}
89     }
90 
91     spec->b_ols = NULL;
92     spec->b_quad = NULL;
93     spec->b_cub = NULL;
94     spec->b_inv = NULL;
95     spec->b_log = NULL;
96     spec->b_linlog = NULL;
97 
98     spec->code = PLOT_REGULAR;
99     spec->flags = 0;
100     spec->fit = PLOT_FIT_NONE;
101     spec->fp = NULL;
102     spec->data = NULL;
103     spec->auxdata = NULL;
104     spec->markers = NULL;
105     spec->n_markers = 0;
106     spec->scale = 1.0;
107     spec->labeled = NULL;
108     spec->ptr = NULL;
109     spec->bars = NULL;
110     spec->fontstr = NULL;
111     spec->reglist = NULL;
112     spec->nobs = 0;
113     spec->okobs = 0;
114     spec->pd = 0;
115     spec->nbars = 0;
116     spec->boxwidth = 0;
117     spec->fillfrac = 0;
118     spec->samples = 0;
119     spec->border = GP_BORDER_DEFAULT;
120     spec->border_lc[0] = '\0';
121     spec->bmargin = 0;
122 
123     spec->termtype = GP_TERM_NONE;
124 
125     return spec;
126 }
127 
plotbars_free(plotbars * bars)128 static void plotbars_free (plotbars *bars)
129 {
130     if (bars != NULL) {
131 	doubles_array_free(bars->dx, bars->n);
132 	free(bars);
133     }
134 }
135 
free_plotspec_lines(GPT_SPEC * spec)136 static void free_plotspec_lines (GPT_SPEC *spec)
137 {
138     int i;
139 
140     for (i=0; i<spec->n_lines; i++) {
141 	free(spec->lines[i].ustr);
142 	free(spec->lines[i].mcols);
143     }
144 
145     free(spec->lines);
146 }
147 
plotspec_destroy(GPT_SPEC * spec)148 void plotspec_destroy (GPT_SPEC *spec)
149 {
150     if (spec == NULL) {
151 	return;
152     }
153 
154     if (spec->lines != NULL) {
155 	free_plotspec_lines(spec);
156     }
157 
158     if (spec->labels != NULL) {
159 	free(spec->labels);
160     }
161 
162     if (spec->arrows != NULL) {
163 	free(spec->arrows);
164     }
165 
166     if (spec->data != NULL) {
167 	gretl_matrix_free(spec->data);
168     }
169 
170     if (spec->auxdata != NULL) {
171 	gretl_matrix_free(spec->auxdata);
172     }
173 
174     if (spec->reglist != NULL) {
175 	free(spec->reglist);
176     }
177 
178     if (spec->literal != NULL) {
179 	strings_array_free(spec->literal, spec->n_literal);
180     }
181 
182     if (spec->markers != NULL) {
183 	strings_array_free(spec->markers, spec->n_markers);
184     }
185 
186     if (spec->labeled != NULL) {
187 	free(spec->labeled);
188     }
189 
190     if (spec->bars != NULL) {
191 	plotbars_free(spec->bars);
192     }
193 
194     if (spec->fontstr != NULL) {
195 	free(spec->fontstr);
196     }
197 
198     if (spec->xticstr != NULL) {
199 	free(spec->xticstr);
200     }
201 
202     if (spec->x2ticstr != NULL) {
203 	free(spec->x2ticstr);
204     }
205 
206     gretl_matrix_free(spec->b_ols);
207     gretl_matrix_free(spec->b_quad);
208     gretl_matrix_free(spec->b_cub);
209     gretl_matrix_free(spec->b_inv);
210     gretl_matrix_free(spec->b_log);
211     gretl_matrix_free(spec->b_linlog);
212 
213     free(spec);
214 }
215 
plotspec_get_xt1_xt2(const GPT_SPEC * spec,double * xt1,double * xt2)216 static void plotspec_get_xt1_xt2 (const GPT_SPEC *spec,
217 				  double *xt1,
218 				  double *xt2)
219 {
220     if (spec->data != NULL) {
221 	*xt1 = spec->data->val[0];
222 	*xt2 = spec->data->val[spec->nobs - 1];
223     }
224 }
225 
226 static gp_style_spec style_specs[] = {
227     { GP_STYLE_LINES,        "lines",        N_("lines") },
228     { GP_STYLE_POINTS,       "points",       N_("points") },
229     { GP_STYLE_LINESPOINTS,  "linespoints",  N_("lines/points") },
230     { GP_STYLE_IMPULSES,     "impulses",     N_("impulses") },
231     { GP_STYLE_DOTS,         "dots",         N_("dots") },
232     { GP_STYLE_STEPS,        "steps",        N_("steps") },
233     { GP_STYLE_BOXES,        "boxes",        N_("boxes") },
234     { GP_STYLE_ERRORBARS,    "errorbars",    N_("error bars") },
235     { GP_STYLE_FILLEDCURVE,  "filledcurve",  N_("filled curve") },
236     { GP_STYLE_CANDLESTICKS, "candlesticks", N_("candlesticks") },
237     { 0, NULL, NULL }
238 };
239 
gp_line_style_display_name(int t)240 const char *gp_line_style_display_name (int t)
241 {
242     int i;
243 
244     for (i=0; style_specs[i].id != 0; i++) {
245 	if (t == style_specs[i].id) {
246 	    return style_specs[i].trname;
247 	}
248     }
249 
250     return N_("lines");
251 }
252 
gp_line_style_name(int t)253 static const char *gp_line_style_name (int t)
254 {
255     int i;
256 
257     for (i=0; style_specs[i].id != 0; i++) {
258 	if (t == style_specs[i].id) {
259 	    return style_specs[i].name;
260 	}
261     }
262 
263     return "lines";
264 }
265 
gp_style_index_from_name(const char * s)266 int gp_style_index_from_name (const char *s)
267 {
268     int i;
269 
270     for (i=0; style_specs[i].id != 0; i++) {
271 	if (!strcmp(s, style_specs[i].name)) {
272 	    return style_specs[i].id;
273 	}
274     }
275 
276     /* allowed abbreviations */
277 
278     if (!strcmp(s, "l")) {
279 	return GP_STYLE_LINES;
280     } else if (!strcmp(s, "p")) {
281 	return GP_STYLE_POINTS;
282     } else if (!strcmp(s, "lp")) {
283 	return GP_STYLE_LINESPOINTS;
284     } else if (!strcmp(s, "i")) {
285 	return GP_STYLE_IMPULSES;
286     } else if (!strcmp(s, "b")) {
287 	return GP_STYLE_BOXES;
288     } else if (!strncmp(s, "can", 3)) {
289 	return GP_STYLE_CANDLESTICKS;
290     }
291 
292     /* fallback */
293     return GP_STYLE_AUTO;
294 }
295 
gp_style_index_from_display_name(const char * s)296 int gp_style_index_from_display_name (const char *s)
297 {
298     int i;
299 
300     for (i=0; style_specs[i].id != 0; i++) {
301 	if (!strcmp(s, _(style_specs[i].trname))) {
302 	    return style_specs[i].id;
303 	}
304     }
305 
306     return GP_STYLE_LINES;
307 }
308 
get_style_spec(int t)309 gp_style_spec *get_style_spec (int t)
310 {
311     int i;
312 
313     for (i=0; style_specs[i].id != 0; i++) {
314 	if (t == style_specs[i].id) {
315 	    return &style_specs[i];
316 	}
317     }
318 
319     return NULL;
320 }
321 
322 static gp_key_spec key_specs[] = {
323     { GP_KEY_LEFT_TOP,     N_("left top") },
324     { GP_KEY_RIGHT_TOP,    N_("right top") },
325     { GP_KEY_LEFT_BOTTOM,  N_("left bottom") },
326     { GP_KEY_RIGHT_BOTTOM, N_("right bottom") },
327     { GP_KEY_OUTSIDE,      N_("outside") },
328     { GP_KEY_NONE,         N_("none") },
329     /* in reverse order */
330     { GP_KEY_LEFT_TOP,     N_("top left") },
331     { GP_KEY_RIGHT_TOP,    N_("top right") },
332     { GP_KEY_LEFT_BOTTOM,  N_("bottom left") },
333     { GP_KEY_RIGHT_BOTTOM, N_("bottom right") },
334     { -1,                  NULL }
335 };
336 
gp_keypos_string(int t)337 static const char *gp_keypos_string (int t)
338 {
339     int i;
340 
341     for (i=0; key_specs[i].str != NULL; i++) {
342 	if (t == key_specs[i].id) {
343 	    return key_specs[i].str;
344 	}
345     }
346 
347     return N_("none");
348 }
349 
print_keypos_string(int t,FILE * fp)350 void print_keypos_string (int t, FILE *fp)
351 {
352     const char *s = gp_keypos_string(t);
353 
354     if (!strcmp(s, "none")) {
355 	fputs("set nokey\n", fp);
356     } else {
357 	fprintf(fp, "set key %s\n", s);
358     }
359 }
360 
gp_keypos_from_name(const char * s)361 int gp_keypos_from_name (const char *s)
362 {
363     int i;
364 
365     for (i=0; key_specs[i].id >= 0; i++) {
366 	if (!strcmp(s, key_specs[i].str)) {
367 	    return key_specs[i].id;
368 	}
369     }
370 
371     return GP_KEY_NONE;
372 }
373 
gp_keypos_from_display_name(const char * s)374 int gp_keypos_from_display_name (const char *s)
375 {
376     int i;
377 
378     for (i=0; key_specs[i].id >= 0; i++) {
379 	if (!strcmp(s, _(key_specs[i].str))) {
380 	    return key_specs[i].id;
381 	}
382     }
383 
384     return GP_KEY_NONE;
385 }
386 
get_keypos_spec(int t)387 gp_key_spec *get_keypos_spec (int t)
388 {
389     int i;
390 
391     for (i=0; key_specs[i].id >= 0; i++) {
392 	if (t == key_specs[i].id) {
393 	    return &key_specs[i];
394 	}
395     }
396 
397     return NULL;
398 }
399 
plotspec_add_line(GPT_SPEC * spec)400 int plotspec_add_line (GPT_SPEC *spec)
401 {
402     GPT_LINE *lines;
403     int n = spec->n_lines;
404 
405     lines = realloc(spec->lines, (n + 1) * sizeof *lines);
406     if (lines == NULL) {
407 	return E_ALLOC;
408     }
409 
410     spec->lines = lines;
411     spec->n_lines += 1;
412 
413     lines[n].varnum = 0;
414     lines[n].style = 0;
415     lines[n].pscale = 1.0;
416     lines[n].rgb[0] = '\0';
417     lines[n].yaxis = 1;
418     lines[n].type = LT_AUTO;
419     lines[n].ptype = 0;
420     lines[n].dtype = 0;
421     lines[n].width = 1.0;
422     lines[n].ncols = 0;
423     lines[n].whiskwidth = 0;
424     lines[n].flags = 0;
425 
426     lines[n].title = NULL;
427     lines[n].formula = NULL;
428     lines[n].ustr = NULL;
429     lines[n].mcols = NULL;
430 
431     return 0;
432 }
433 
copy_line_content(GPT_LINE * targ,GPT_LINE * src)434 static void copy_line_content (GPT_LINE *targ, GPT_LINE *src)
435 {
436     targ->varnum = src->varnum;
437     targ->style = src->style;
438     targ->pscale = src->pscale;
439     strcpy(targ->rgb, src->rgb);
440     targ->yaxis = src->yaxis;
441     targ->type = src->type;
442     targ->ptype = src->ptype;
443     targ->dtype = src->dtype;
444     targ->width = src->width;
445     targ->ncols = src->ncols;
446     targ->whiskwidth = src->whiskwidth;
447     targ->flags = src->flags;
448 
449     if (src->title != NULL) {
450 	g_free(targ->title);
451 	targ->title = g_strdup(src->title);
452     }
453     if (src->formula != NULL) {
454 	g_free(targ->formula);
455 	targ->formula = g_strdup(src->formula);
456     }
457     if (src->ustr != NULL) {
458 	free(targ->ustr);
459 	targ->ustr = gretl_strdup(src->ustr);
460     }
461     if (src->mcols != NULL) {
462 	free(targ->mcols);
463 	targ->mcols = gretl_list_copy(src->mcols);
464     }
465 }
466 
plotspec_delete_line(GPT_SPEC * spec,int i)467 int plotspec_delete_line (GPT_SPEC *spec, int i)
468 {
469     GPT_LINE *lines = spec->lines;
470     int j, n = spec->n_lines;
471 
472     if (i < 0 || i >= n) {
473 	return E_DATA;
474     }
475 
476     g_free(lines[i].title);
477     g_free(lines[i].formula);
478     free(lines[i].ustr);
479     free(lines[i].mcols);
480 
481     /* move the remaining line contents down */
482     for (j=i; j<n-1; j++) {
483 	copy_line_content(&lines[j], &lines[j+1]);
484     }
485 
486     spec->n_lines -= 1;
487 
488     lines = realloc(spec->lines, (n - 1) * sizeof *lines);
489     if (lines == NULL) {
490 	return E_ALLOC;
491     }
492 
493     spec->lines = lines;
494 
495     return 0;
496 }
497 
plotspec_clone_lines(GPT_SPEC * spec,int * err)498 GPT_LINE *plotspec_clone_lines (GPT_SPEC *spec, int *err)
499 {
500     GPT_LINE *lines = NULL;
501     int i;
502 
503     if (spec->n_lines == 0) {
504 	/* no-op */
505 	return NULL;
506     }
507 
508     lines = calloc(spec->n_lines, sizeof *lines);
509 
510     if (lines == NULL) {
511 	*err = E_ALLOC;
512     } else {
513 	for (i=0; i<spec->n_lines; i++) {
514 	    lines[i].ustr = NULL;
515 	    lines[i].mcols = NULL;
516 	    copy_line_content(&lines[i], &spec->lines[i]);
517 	}
518     }
519 
520     return lines;
521 }
522 
plotspec_max_line_width(GPT_SPEC * spec)523 int plotspec_max_line_width (GPT_SPEC *spec)
524 {
525     int i, lw = 0;
526 
527     for (i=0; i<spec->n_lines; i++) {
528 	if (spec->lines[i].width > lw) {
529 	    lw = spec->lines[i].width;
530 	}
531     }
532 
533     return lw;
534 }
535 
plotspec_add_label(GPT_SPEC * spec)536 int plotspec_add_label (GPT_SPEC *spec)
537 {
538     GPT_LABEL *labels;
539     int n = spec->n_labels;
540 
541     labels = realloc(spec->labels, (n + 1) * sizeof *labels);
542     if (labels == NULL) {
543 	return E_ALLOC;
544     }
545 
546     spec->labels = labels;
547     spec->n_labels += 1;
548 
549     labels[n].text[0] = '\0';
550     labels[n].pos[0] = NADBL;
551     labels[n].pos[1] = NADBL;
552     labels[n].just = GP_JUST_LEFT;
553 
554     return 0;
555 }
556 
copy_label_content(GPT_LABEL * targ,GPT_LABEL * src)557 static void copy_label_content (GPT_LABEL *targ, GPT_LABEL *src)
558 {
559     strcpy(targ->text, src->text);
560     targ->pos[0] = src->pos[0];
561     targ->pos[1] = src->pos[1];
562     targ->just = src->just;
563 }
564 
plotspec_delete_label(GPT_SPEC * spec,int i)565 int plotspec_delete_label (GPT_SPEC *spec, int i)
566 {
567     GPT_LABEL *labels = spec->labels;
568     int j, n = spec->n_labels;
569     int err = 0;
570 
571     if (i < 0 || i >= n) {
572 	return E_DATA;
573     }
574 
575     for (j=i; j<n-1; j++) {
576 	copy_label_content(&labels[j], &labels[j+1]);
577     }
578 
579     spec->n_labels -= 1;
580 
581     if (spec->n_labels == 0) {
582 	free(spec->labels);
583 	spec->labels = NULL;
584     } else {
585 	labels = realloc(spec->labels, (n - 1) * sizeof *labels);
586 	if (labels == NULL) {
587 	    err = E_ALLOC;
588 	} else {
589 	    spec->labels = labels;
590 	}
591     }
592 
593     return err;
594 }
595 
plotspec_clone_labels(GPT_SPEC * spec,int * err)596 GPT_LABEL *plotspec_clone_labels (GPT_SPEC *spec, int *err)
597 {
598     GPT_LABEL *labels = NULL;
599     int i;
600 
601     if (spec->n_labels == 0) {
602 	/* no-op */
603 	return NULL;
604     }
605 
606     labels = malloc(spec->n_labels * sizeof *labels);
607     if (labels == NULL) {
608 	*err = E_ALLOC;
609     } else {
610 	for (i=0; i<spec->n_labels; i++) {
611 	    copy_label_content(&labels[i], &spec->labels[i]);
612 	}
613     }
614 
615     return labels;
616 }
617 
plotspec_add_arrow(GPT_SPEC * spec)618 int plotspec_add_arrow (GPT_SPEC *spec)
619 {
620     GPT_ARROW *arrows;
621     int n = spec->n_arrows;
622 
623     arrows = realloc(spec->arrows, (n + 1) * sizeof *arrows);
624     if (arrows == NULL) {
625 	return E_ALLOC;
626     }
627 
628     spec->arrows = arrows;
629     spec->n_arrows += 1;
630     arrows[n].x0 = 0;
631     arrows[n].y0 = 0;
632     arrows[n].x1 = 0;
633     arrows[n].y1 = 0;
634     arrows[n].flags = 0;
635 
636     return 0;
637 }
638 
copy_arrow_content(GPT_ARROW * targ,GPT_ARROW * src)639 static void copy_arrow_content (GPT_ARROW *targ, GPT_ARROW *src)
640 {
641     targ->x0 = src->x0;
642     targ->y0 = src->y0;
643     targ->x1 = src->x1;
644     targ->y1 = src->y1;
645     targ->flags = src->flags;
646 }
647 
plotspec_delete_arrow(GPT_SPEC * spec,int i)648 int plotspec_delete_arrow (GPT_SPEC *spec, int i)
649 {
650     GPT_ARROW *arrows = spec->arrows;
651     int j, n = spec->n_arrows;
652     int err = 0;
653 
654     if (i < 0 || i >= n) {
655 	return E_DATA;
656     }
657 
658     for (j=i; j<n-1; j++) {
659 	copy_arrow_content(&arrows[j], &arrows[j+1]);
660     }
661 
662     spec->n_arrows -= 1;
663 
664     if (spec->n_arrows == 0) {
665 	free(spec->arrows);
666 	spec->arrows = NULL;
667     } else {
668 	arrows = realloc(spec->arrows, (n - 1) * sizeof *arrows);
669 	if (arrows == NULL) {
670 	    err = E_ALLOC;
671 	} else {
672 	    spec->arrows = arrows;
673 	}
674     }
675 
676     return err;
677 }
678 
plotspec_clone_arrows(GPT_SPEC * spec,int * err)679 GPT_ARROW *plotspec_clone_arrows (GPT_SPEC *spec, int *err)
680 {
681     GPT_ARROW *arrows = NULL;
682     int i;
683 
684     if (spec->n_arrows == 0) {
685 	/* no-op */
686 	return NULL;
687     }
688 
689     arrows = malloc(spec->n_arrows * sizeof *arrows);
690     if (arrows == NULL) {
691 	*err = E_ALLOC;
692     } else {
693 	for (i=0; i<spec->n_arrows; i++) {
694 	    copy_arrow_content(&arrows[i], &spec->arrows[i]);
695 	}
696     }
697 
698     return arrows;
699 }
700 
escape_quotes(const char * s)701 static char *escape_quotes (const char *s)
702 {
703     if (strchr(s, '"') == NULL) {
704 	return NULL;
705     } else {
706 	int qcount = 0;
707 	char *ret, *r;
708 	const char *p = s;
709 
710 	while (*p) {
711 	    if (*p == '"') qcount++;
712 	    p++;
713 	}
714 
715 	ret = malloc(strlen(s) + 1 + qcount);
716 	if (ret == NULL) {
717 	    return NULL;
718 	}
719 
720 	r = ret;
721 	while (*s) {
722 	    if (*s == '"') {
723 		*r++ = '\\';
724 		*r++ = '"';
725 	    } else {
726 		*r++ = *s;
727 	    }
728 	    s++;
729 	}
730 	*r = 0;
731 
732 	return ret;
733     }
734 }
735 
gp_justification_string(int j)736 const char *gp_justification_string (int j)
737 {
738     if (j == GP_JUST_LEFT) {
739 	return N_("left");
740     } else if (j == GP_JUST_CENTER) {
741 	return N_("center");
742     } else if (j == GP_JUST_RIGHT) {
743 	return N_("right");
744     } else {
745 	return N_("left");
746     }
747 }
748 
print_plot_labelspec(const GPT_LABEL * lbl,FILE * fp)749 static void print_plot_labelspec (const GPT_LABEL *lbl, FILE *fp)
750 {
751     char *label = escape_quotes(lbl->text);
752 
753     fprintf(fp, "set label \"%s\" ", (label != NULL)?
754 	    label : lbl->text);
755 
756     gretl_push_c_numeric_locale();
757 
758     fprintf(fp, "at %g,%g %s front\n",
759 	    lbl->pos[0], lbl->pos[1],
760 	    gp_justification_string(lbl->just));
761 
762     gretl_pop_c_numeric_locale();
763 
764     if (label != NULL) {
765 	free(label);
766     }
767 }
768 
print_plot_arrow(const GPT_ARROW * arrow,FILE * fp)769 static void print_plot_arrow (const GPT_ARROW *arrow, FILE *fp)
770 {
771     gretl_push_c_numeric_locale();
772     fprintf(fp, "set arrow from %g,%g to %g,%g %s",
773 	    arrow->x0, arrow->y0, arrow->x1, arrow->y1,
774 	    (arrow->flags & GP_ARROW_HEAD)? "head" : "nohead");
775     if (arrow->flags & GP_ARROW_DOTS) {
776 	fputs(" lt 0", fp);
777     }
778     fputc('\n', fp);
779     gretl_pop_c_numeric_locale();
780 }
781 
print_polar_labels(const GPT_SPEC * spec,FILE * fp)782 static int print_polar_labels (const GPT_SPEC *spec, FILE *fp)
783 {
784     double x, y;
785     int t;
786 
787     fputs("# printing data labels\n", fp);
788 
789     gretl_push_c_numeric_locale();
790 
791     for (t=0; t<spec->nobs; t++) {
792 	if (spec->labeled != NULL && !spec->labeled[t]) {
793 	    /* printing only specified labels */
794 	    continue;
795 	}
796 	if (sscanf(spec->markers[t], "%lf,%lf", &x, &y) == 2) {
797 	    fprintf(fp, "set label \"%.3f,%.3f\" at %.3f,%.3f\n",
798 		    x, y, x, y + .04);
799 	}
800     }
801 
802     gretl_pop_c_numeric_locale();
803 
804     return 0;
805 }
806 
usable_obs(const double * x,const double * y0,const double * y1,int t,const double ** py)807 static int usable_obs (const double *x, const double *y0,
808 		       const double *y1, int t,
809 		       const double **py)
810 {
811     *py = y0;
812 
813     if (!na(x[t]) && !na(y0[t])) {
814 	return 1;
815     } else if (!na(x[t]) && y1 != NULL && !na(y1[t])) {
816 	*py = y1;
817 	return 1;
818     } else {
819 	return 0;
820     }
821 }
822 
plotspec_line_is_formula(const GPT_SPEC * spec,int i)823 int plotspec_line_is_formula (const GPT_SPEC *spec, int i)
824 {
825     int ret = 0;
826 
827     if (i >= 0 && i < spec->n_lines) {
828 	GPT_LINE *line = &spec->lines[i];
829 
830 	if (!string_is_blank(line->formula)) {
831 	    ret = 1;
832 	} else if (line->flags & GP_LINE_USER) {
833 	    ret = 1;
834 	} else if (i == 1 && (spec->flags & GPT_AUTO_FIT)) {
835 	    ret = 1;
836 	}
837     }
838 
839     return ret;
840 }
841 
print_data_labels(const GPT_SPEC * spec,FILE * fp)842 static int print_data_labels (const GPT_SPEC *spec, FILE *fp)
843 {
844     const double *x, *y, *y0;
845     const double *y1 = NULL;
846     double xrange, yrange;
847     double xmin, xmax;
848     double ymin, ymax;
849     double yoff;
850     int nlines;
851     int i, t;
852 
853     if (spec->markers == NULL) {
854 	/* "can't happen" */
855 	return 1;
856     }
857 
858     if (spec->flags & GPT_POLAR) {
859 	return print_polar_labels(spec, fp);
860     }
861 
862     nlines = 0;
863     for (i=0; i<spec->n_lines; i++) {
864 	if (!plotspec_line_is_formula(spec, i)) {
865 	    nlines++;
866 	}
867     }
868     if (nlines > 2 || spec->lines[0].ncols != 2) {
869 	return 1;
870     }
871 
872     x = spec->data->val;
873     y0 = x + spec->nobs;
874 
875     if (spec->code == PLOT_FACTORIZED) {
876 	y1 = y0 + spec->nobs;
877     }
878 
879     xmin = spec->range[GP_X_RANGE][0];
880     xmax = spec->range[GP_X_RANGE][1];
881     xrange = (na(xmin) || na(xmax)) ? 0.0 : xmax - xmin;
882 
883     ymin = spec->range[GP_Y_RANGE][0];
884     ymax = spec->range[GP_Y_RANGE][1];
885     yrange = (na(ymin) || na(ymax)) ? 0.0 : ymax - ymin;
886 
887     if (xrange == 0.0 || yrange == 0.0) {
888 	ymin = 1.0e+50; ymax = -1.0e+50;
889 	xmin = 1.0e+50; xmax = -1.0e+50;
890 
891 	for (t=0; t<spec->nobs; t++) {
892 	    if (usable_obs(x, y0, y1, t, &y)) {
893 		if (yrange == 0.0) {
894 		    if (y[t] < ymin) {
895 			ymin = y[t];
896 		    }
897 		    if (y[t] > ymax) {
898 			ymax = y[t];
899 		    }
900 		}
901 		if (xrange == 0.0) {
902 		    if (x[t] < xmin) {
903 			xmin = x[t];
904 		    }
905 		    if (x[t] > xmax) {
906 			xmax = x[t];
907 		    }
908 		}
909 	    }
910 	}
911 	if (yrange == 0.0) {
912 	    yrange = ymax - ymin;
913 	}
914 	if (xrange == 0.0) {
915 	    xrange = xmax - xmin;
916 	}
917     }
918 
919     yoff = 0.03 * yrange;
920 
921     fputs("# printing data labels\n", fp);
922 
923     gretl_push_c_numeric_locale();
924 
925     for (t=0; t<spec->nobs; t++) {
926 	double xoff = 0.0;
927 
928 	if (spec->labeled != NULL && !spec->labeled[t]) {
929 	    /* printing only specified labels */
930 	    continue;
931 	}
932 
933 	if (usable_obs(x, y0, y1, t, &y)) {
934 	    if (x[t] > .90 * xrange) {
935 		xoff = -.02 * xrange;
936 	    }
937 	    fprintf(fp, "set label \"%s\" at %.8g,%.8g\n", spec->markers[t],
938 		    x[t] + xoff, y[t] + yoff);
939 	}
940     }
941 
942     gretl_pop_c_numeric_locale();
943 
944     return 0;
945 }
946 
print_auto_fit_string(FitType fit,FILE * fp)947 void print_auto_fit_string (FitType fit, FILE *fp)
948 {
949     if (fit == PLOT_FIT_OLS) {
950 	fputs("# plot includes automatic fit: OLS\n", fp);
951     } else if (fit == PLOT_FIT_QUADRATIC) {
952 	fputs("# plot includes automatic fit: quadratic\n", fp);
953     } else if (fit == PLOT_FIT_CUBIC) {
954 	fputs("# plot includes automatic fit: cubic\n", fp);
955     } else if (fit == PLOT_FIT_INVERSE) {
956 	fputs("# plot includes automatic fit: inverse\n", fp);
957     } else if (fit == PLOT_FIT_LOESS) {
958 	fputs("# plot includes automatic fit: loess\n", fp);
959     } else if (fit == PLOT_FIT_LOGLIN) {
960 	fputs("# plot includes automatic fit: semilog\n", fp);
961     } else if (fit == PLOT_FIT_LINLOG) {
962 	fputs("# plot includes automatic fit: linlog\n", fp);
963     }
964 }
965 
print_plot_ranges_etc(const GPT_SPEC * spec,FILE * fp)966 void print_plot_ranges_etc (const GPT_SPEC *spec, FILE *fp)
967 {
968     const char *rstrs[] = {
969 	"x", "y", "y2", "t", "x2"
970     };
971     int i;
972 
973     gretl_push_c_numeric_locale();
974 
975     for (i=0; i<5; i++) {
976 	if (i < 3 && spec->logbase[i] > 0.0) {
977 	    fprintf(fp, "set logscale %s %g\n", rstrs[i], spec->logbase[i]);
978 	}
979 
980 	if (na(spec->range[i][0]) || na(spec->range[i][1]) ||
981 	    spec->range[i][0] == spec->range[i][1]) {
982 	    continue;
983 	}
984 
985 	if ((i == GP_Y2_RANGE && !(spec->flags & GPT_Y2AXIS)) ||
986 	    (i == GP_T_RANGE && !(spec->flags & GPT_PARAMETRIC))) {
987 	    continue;
988 	}
989 
990 	fprintf(fp, "set %srange [%.10g:%.10g]\n", rstrs[i],
991 		spec->range[i][0], spec->range[i][1]);
992 
993 	if (i == 4 && spec->code == PLOT_PROB_DIST && spec->samples == 0) {
994 	    int ns = spec->range[i][1] - spec->range[i][0] + 1;
995 
996 	    fprintf(fp, "set samples %d\n", ns);
997 	}
998     }
999 
1000     if (spec->boxwidth > 0 && spec->boxwidth < 1) {
1001 	fprintf(fp, "set boxwidth %g relative\n", (double) spec->boxwidth);
1002     } else if (spec->boxwidth < 0 && spec->boxwidth > -1) {
1003 	fprintf(fp, "set boxwidth %g absolute\n", (double) -spec->boxwidth);
1004     }
1005 
1006     gretl_pop_c_numeric_locale();
1007 }
1008 
blank_user_line(GPT_LINE * line)1009 static int blank_user_line (GPT_LINE *line)
1010 {
1011     return ((line->flags & GP_LINE_USER) &&
1012 	    string_is_blank(line->formula));
1013 }
1014 
print_user_lines_info(const GPT_SPEC * spec,FILE * fp)1015 static void print_user_lines_info (const GPT_SPEC *spec, FILE *fp)
1016 {
1017     int i, n = 0;
1018 
1019     for (i=0; i<spec->n_lines; i++) {
1020 	if (spec->lines[i].flags & GP_LINE_USER) {
1021 	    n++;
1022 	}
1023     }
1024 
1025     if (n > 0) {
1026 	fprintf(fp, "# %d user-defined lines: ", n);
1027 	for (i=0; i<spec->n_lines; i++) {
1028 	    if (spec->lines[i].flags & GP_LINE_USER) {
1029 		fprintf(fp, "%d ", i);
1030 	    }
1031 	}
1032 	fputc('\n', fp);
1033     }
1034 }
1035 
more_lines(const GPT_SPEC * spec,int i,int skipline)1036 static int more_lines (const GPT_SPEC *spec, int i, int skipline)
1037 {
1038     if (i == spec->n_lines - 1) {
1039 	/* at last line */
1040 	return 0;
1041     } else {
1042 	int j;
1043 
1044 	for (j=i+1; j<spec->n_lines; j++) {
1045 	    if (j != skipline && !blank_user_line(&spec->lines[j])) {
1046 		return 1;
1047 	    }
1048 	}
1049 	return 0;
1050     }
1051 }
1052 
print_linestyle(const GPT_SPEC * spec,int targ,int src,FILE * fp)1053 static void print_linestyle (const GPT_SPEC *spec,
1054 			     int targ, int src,
1055 			     FILE *fp)
1056 {
1057     GPT_LINE *line;
1058     int done = 0;
1059 
1060     fprintf(fp, "set linetype %d ", targ + 1);
1061 
1062     if (src < spec->n_lines) {
1063 	line = &spec->lines[src];
1064 	if (*line->rgb != '\0' && line->style != GP_STYLE_FILLEDCURVE) {
1065 	    fprintf(fp, "lc rgb \"%s\"\n", line->rgb);
1066 	    done = 1;
1067 	}
1068     }
1069 
1070     if (!done) {
1071 	/* hmm, should we be doing this? */
1072 	gretlRGB color = get_graph_color(targ);
1073 	char cstr[8];
1074 
1075 	print_rgb_hash(cstr, color);
1076 	fprintf(fp, "lc rgb \"%s\"\n", cstr);
1077     }
1078 }
1079 
write_styles_from_plotspec(const GPT_SPEC * spec,FILE * fp)1080 static void write_styles_from_plotspec (const GPT_SPEC *spec, FILE *fp)
1081 {
1082     char cstr[8];
1083     int i;
1084 
1085     if (frequency_plot_code(spec->code)) {
1086 	gretlRGB color = get_boxcolor();
1087 
1088 	print_rgb_hash(cstr, color);
1089 	fprintf(fp, "set linetype 1 lc rgb \"%s\"\n", cstr);
1090 	fputs("set linetype 2 lc rgb \"#000000\"\n", fp);
1091     } else if (spec->code == PLOT_RQ_TAU) {
1092 	fputs("set linetype 1 lc rgb \"#000000\"\n", fp);
1093 	for (i=1; i<N_GP_LINETYPES; i++) {
1094 	    print_linestyle(spec, i, i, fp);
1095 	}
1096     } else if (spec->code == PLOT_BOXPLOTS) {
1097 	/* the first two elements use the same color */
1098 	print_linestyle(spec, 0, 0, fp);
1099 	print_linestyle(spec, 1, 2, fp);
1100     } else {
1101 	/* note: handle the case where "line 0" is filledcurve,
1102 	   the color of which is handled separately
1103 	*/
1104 	int offset = 0;
1105 
1106 	if (spec->lines[0].style == GP_STYLE_FILLEDCURVE) {
1107 	    offset = 1;
1108 	}
1109 	for (i=0; i<N_GP_LINETYPES; i++) {
1110 	    print_linestyle(spec, i, i + offset, fp);
1111 	}
1112     }
1113 }
1114 
maybe_print_point_info(const GPT_LINE * line,FILE * fp)1115 static void maybe_print_point_info (const GPT_LINE *line, FILE *fp)
1116 {
1117     if (line->style == GP_STYLE_POINTS ||
1118 	line->style == GP_STYLE_LINESPOINTS) {
1119 	if (line->ptype != 0) {
1120 	    fprintf(fp, " pt %d", line->ptype);
1121 	}
1122 	if (line->pscale != 1.0) {
1123 	    fprintf(fp, " ps %g", (double) line->pscale);
1124 	}
1125     }
1126 
1127     if (line->style == GP_STYLE_LINES ||
1128 	line->style == GP_STYLE_LINESPOINTS) {
1129 	if (line->dtype > 1) {
1130 	    fprintf(fp, " dt %d", line->dtype);
1131 	}
1132     }
1133 }
1134 
1135 #define show_fit(s) (s->fit == PLOT_FIT_OLS ||		\
1136                      s->fit == PLOT_FIT_QUADRATIC ||	\
1137 		     s->fit == PLOT_FIT_CUBIC ||	\
1138                      s->fit == PLOT_FIT_INVERSE ||	\
1139                      s->fit == PLOT_FIT_LOESS ||	\
1140 		     s->fit == PLOT_FIT_LOGLIN ||	\
1141 		     s->fit == PLOT_FIT_LINLOG)
1142 
gp_line_data_columns(GPT_SPEC * spec,int i)1143 int gp_line_data_columns (GPT_SPEC *spec, int i)
1144 {
1145     if (spec->lines[i].flags & GP_LINE_AUXDATA) {
1146 	return 0;
1147     } else {
1148 	return spec->lines[i].ncols;
1149     }
1150 }
1151 
plotspec_print_data(GPT_SPEC * spec,int skipline,int * miss,FILE * fp)1152 static void plotspec_print_data (GPT_SPEC *spec,
1153 				 int skipline,
1154 				 int *miss,
1155 				 FILE *fp)
1156 {
1157     double *x[5];
1158     int started_data_lines = 0;
1159     int i, j, t;
1160 
1161     for (i=0; i<spec->n_lines; i++) {
1162 	int ncols = gp_line_data_columns(spec, i);
1163 
1164 	if (ncols == 0 || i == skipline) {
1165 	    /* no (regular) data to print */
1166 	    continue;
1167 	}
1168 
1169 	if (!started_data_lines) {
1170 	    x[0] = spec->data->val;
1171 	    /* see below for subsequent adjustment of x[1] */
1172 	    x[1] = x[0] + spec->nobs;
1173 	    started_data_lines = 1;
1174 	}
1175 
1176 	x[2] = x[1] + spec->nobs;
1177 	x[3] = x[2] + spec->nobs;
1178 	x[4] = x[3] + spec->nobs;
1179 
1180 	for (t=0; t<spec->nobs; t++) {
1181 	    /* print x-axis value */
1182 	    if (na(x[0][t])) {
1183 		write_gp_dataval(NADBL, fp, 0);
1184 		*miss = 1;
1185 	    } else if (spec->flags & GPT_TIMEFMT) {
1186 		fprintf(fp, "%.0f ", x[0][t]);
1187 	    } else {
1188 		fprintf(fp, "%.10g ", x[0][t]);
1189 	    }
1190 	    /* print y-axis value(s) */
1191 	    for (j=1; j<ncols; j++) {
1192 		if (na(x[j][t])) {
1193 		    *miss = 1;
1194 		}
1195 		write_gp_dataval(x[j][t], fp, 0);
1196 	    }
1197 	    /* append markers if applicable */
1198 	    if (spec->markers != NULL && i == 0) {
1199 		fprintf(fp, " # %s", spec->markers[t]);
1200 	    }
1201 	    fputc('\n', fp);
1202 	}
1203 
1204 	fputs("e\n", fp);
1205 
1206 	x[1] += (ncols - 1) * spec->nobs;
1207     }
1208 
1209     if (spec->auxdata != NULL) {
1210 	int rows = gretl_matrix_rows(spec->auxdata);
1211 	int cols = gretl_matrix_cols(spec->auxdata);
1212 
1213 	fprintf(fp, "# auxdata %d %d\n", rows, cols);
1214 
1215 	for (i=0; i<rows; i++) {
1216 	    for (j=0; j<cols; j++) {
1217 		fprintf(fp, "%.10g ", gretl_matrix_get(spec->auxdata, i, j));
1218 	    }
1219 	    fputc('\n', fp);
1220 	}
1221 	fputs("e\n", fp);
1222     }
1223 }
1224 
plotspec_print_heredata(GPT_SPEC * spec,int * miss,FILE * fp)1225 static void plotspec_print_heredata (GPT_SPEC *spec,
1226 				     int *miss,
1227 				     FILE *fp)
1228 {
1229     gretl_matrix *m = NULL;
1230     double mti;
1231     int i, t;
1232 
1233     if (spec->auxdata != NULL) {
1234 	int r = spec->auxdata->rows;
1235 	int c = spec->auxdata->cols;
1236 	int j;
1237 
1238 	fprintf(fp, "# auxdata %d %d\n", r, c);
1239 	fputs("$aux << EOA\n", fp);
1240 
1241 	for (i=0; i<r; i++) {
1242 	    for (j=0; j<c; j++) {
1243 		fprintf(fp, "%.10g ", gretl_matrix_get(spec->auxdata, i, j));
1244 	    }
1245 	    fputc('\n', fp);
1246 	}
1247 	fputs("EOA\n", fp);
1248     }
1249 
1250     if (spec->data == NULL || spec->datacols == 0) {
1251 	return;
1252     }
1253 
1254     m = spec->data;
1255 
1256     fputs("$data << EOD\n", fp);
1257 
1258     for (t=0; t<spec->nobs; t++) {
1259 	for (i=0; i<m->cols; i++) {
1260 	    mti = gretl_matrix_get(m, t, i);
1261 	    if (na(mti)) {
1262 		write_gp_dataval(NADBL, fp, 0);
1263 		*miss = 1;
1264 	    } else if (i == 0 && (spec->flags & GPT_TIMEFMT)) {
1265 		fprintf(fp, "%.0f ", mti);
1266 	    } else {
1267 		fprintf(fp, "%.10g ", mti);
1268 	    }
1269 	}
1270 	if (spec->markers != NULL) {
1271 	    fprintf(fp, " # %s", spec->markers[t]);
1272 	}
1273 	fputc('\n', fp);
1274     }
1275 
1276     fputs("EOD\n", fp);
1277 }
1278 
plotspec_print(GPT_SPEC * spec,FILE * fp)1279 int plotspec_print (GPT_SPEC *spec, FILE *fp)
1280 {
1281     int png = get_png_output(spec);
1282     int mono = (spec->flags & GPT_MONO);
1283     double xt1 = 0, xt2 = 0;
1284     char src[16] = "'-'";
1285     int skipline = -1;
1286     int ycol, any_y2 = 0;
1287     int anydata = 0;
1288     int i, k, miss = 0;
1289 
1290     if (spec->pd > 0) {
1291 	fprintf(fp, "# timeseries %d", spec->pd);
1292 	if (spec->flags & GPT_LETTERBOX) {
1293 	    fputs(" (letterbox)\n", fp);
1294 	} else {
1295 	    fputc('\n', fp);
1296 	}
1297     }
1298 
1299     if (spec->scale != 1.0) {
1300 	gretl_push_c_numeric_locale();
1301 	fprintf(fp, "# scale = %.1f\n", spec->scale);
1302 	gretl_pop_c_numeric_locale();
1303     }
1304 
1305     if (mono) {
1306 	fputs("set mono\n", fp);
1307     } else {
1308 	write_styles_from_plotspec(spec, fp);
1309     }
1310 
1311     if (!string_is_blank(spec->titles[0])) {
1312 	fprintf(fp, "set title \"%s\"\n", spec->titles[0]);
1313     }
1314 
1315     if (!string_is_blank(spec->titles[1])) {
1316 	fprintf(fp, "set xlabel \"%s\"\n", spec->titles[1]);
1317     }
1318 
1319     if (!string_is_blank(spec->titles[2])) {
1320 	fprintf(fp, "set ylabel \"%s\"\n", spec->titles[2]);
1321     }
1322 
1323     if ((spec->flags & GPT_Y2AXIS) && !string_is_blank(spec->titles[3])) {
1324 	fprintf(fp, "set y2label \"%s\"\n", spec->titles[3]);
1325     }
1326 
1327     if (!string_is_blank(spec->titles[4])) {
1328 	fprintf(fp, "set x2label \"%s\"\n", spec->titles[4]);
1329     }
1330 
1331     for (i=0; i<spec->n_labels; i++) {
1332 	if (!string_is_blank(spec->labels[i].text)) {
1333 	    print_plot_labelspec(&spec->labels[i], fp);
1334 	}
1335     }
1336 
1337     for (i=0; i<spec->n_arrows; i++) {
1338 	print_plot_arrow(&spec->arrows[i], fp);
1339     }
1340 
1341     if (spec->flags & GPT_XZEROAXIS) {
1342 	fputs("set xzeroaxis\n", fp);
1343     }
1344 
1345     if (spec->flags & GPT_YZEROAXIS) {
1346 	fputs("set yzeroaxis\n", fp);
1347     }
1348 
1349     if (spec->flags & (GPT_GRID_Y | GPT_GRID_X)) {
1350 	if (!(spec->flags & GPT_GRID_X)) {
1351 	    fputs("set grid ytics\n", fp);
1352 	} else if (!(spec->flags & GPT_GRID_Y)) {
1353 	    fputs("set grid xtics\n", fp);
1354 	} else {
1355 	    /* default, both */
1356 	    fputs("set grid\n", fp);
1357 	}
1358     }
1359 
1360     if (spec->flags & GPT_PARAMETRIC) {
1361 	fputs("set parametric\n", fp);
1362 	if (spec->samples > 0) {
1363 	    fprintf(fp, "set samples %d\n", spec->samples);
1364 	}
1365     }
1366 
1367     if (spec->flags & GPT_POLAR) {
1368 	fputs("unset ytics\n", fp);
1369 	fputs("set size square\n", fp);
1370 	fputs("set polar\n", fp);
1371     }
1372 
1373     gnuplot_missval_string(fp);
1374 
1375     if (spec->keyspec == GP_KEY_NONE) {
1376 	fputs("set nokey\n", fp);
1377     } else {
1378 	fprintf(fp, "set key %s\n", gp_keypos_string(spec->keyspec));
1379     }
1380 
1381     print_plot_ranges_etc(spec, fp);
1382 
1383     /* using time format for x-axis? */
1384     if (*spec->timefmt != '\0') {
1385 	fputs("set xdata time\n", fp);
1386 	fprintf(fp, "set timefmt \"%s\"\n", spec->timefmt);
1387     }
1388 
1389     /* special x and/or y format? */
1390     if (*spec->xfmt != '\0') {
1391 	fprintf(fp, "set format x \"%s\"\n", spec->xfmt);
1392     }
1393     if (*spec->yfmt != '\0') {
1394 	fprintf(fp, "set format y \"%s\"\n", spec->yfmt);
1395     }
1396 
1397     /* customized xtics? */
1398     if (!strcmp(spec->xtics, "none")) {
1399 	fputs("set noxtics\n", fp);
1400     } else {
1401 	if (!string_is_blank(spec->xtics)) {
1402 	    /* may contain "nomirror" */
1403 	    fprintf(fp, "set xtics %s\n", spec->xtics);
1404 	}
1405 	if (spec->xticstr != NULL) {
1406 	    fprintf(fp, "set xtics %s\n", spec->xticstr);
1407 	}
1408 	if (!string_is_blank(spec->mxtics)) {
1409 	    fprintf(fp, "set mxtics %s\n", spec->mxtics);
1410 	}
1411     }
1412 
1413     /* using x2tics? */
1414     if (spec->x2ticstr != NULL) {
1415 	fprintf(fp, "set x2tics %s\n", spec->x2ticstr);
1416     }
1417 
1418     /* customized ytics? */
1419     if (!strcmp(spec->ytics, "none")) {
1420 	fputs("set noytics\n", fp);
1421     } else {
1422 	if (!string_is_blank(spec->ytics)) {
1423 	    fprintf(fp, "set ytics %s\n", spec->ytics);
1424 	}
1425     }
1426 
1427     if (spec->flags & GPT_Y2AXIS) {
1428 	/* using two y axes */
1429 	fputs("set ytics nomirror\n", fp);
1430 	fputs("set y2tics\n", fp);
1431     } else if (spec->border != GP_BORDER_DEFAULT) {
1432 	/* suppressing all or part of border */
1433 	if (spec->border == 0) {
1434 	    fputs("unset border\n", fp);
1435 	} else if (spec->border_lc[0] != '\0') {
1436 	    fprintf(fp, "set border %d lc rgb \"%s\"\n",
1437 		    spec->border, spec->border_lc);
1438 	} else {
1439 	    fprintf(fp, "set border %d\n", spec->border);
1440 	}
1441 	if (string_is_blank(spec->xtics)) {
1442 	    fputs("set xtics nomirror\n", fp);
1443 	}
1444 	if (string_is_blank(spec->ytics)) {
1445 	    fputs("set ytics nomirror\n", fp);
1446 	}
1447     }
1448 
1449     if (spec->bmargin > 0) {
1450 	fprintf(fp, "set bmargin %d\n", spec->bmargin);
1451     }
1452 
1453     /* in case of plots that are editable (see gui client), it is
1454        important to write out the comment string that identifies the
1455        sort of graph, so that it will be recognized by type when
1456        it is redisplayed */
1457 
1458     write_plot_type_string(spec->code, spec->flags, fp);
1459 
1460     if (spec->n_literal > 0) {
1461 	fprintf(fp, "# literal lines = %d\n", spec->n_literal);
1462 	for (i=0; i<spec->n_literal; i++) {
1463 	    if (spec->literal[i] != NULL && *spec->literal[i] != '\0') {
1464 		fprintf(fp, "%s\n", spec->literal[i]);
1465 	    } else {
1466 		fputs("# empty line!\n", fp);
1467 	    }
1468 	}
1469     }
1470 
1471     if (show_fit(spec)) {
1472 	print_auto_fit_string(spec->fit, fp);
1473     }
1474 
1475     if (spec->bars != NULL) {
1476 	plotspec_get_xt1_xt2(spec, &xt1, &xt2);
1477 	spec->nbars = n_bars_shown(xt1, xt2, spec->bars);
1478 	if (spec->nbars > 0) {
1479 	    fprintf(fp, "# n_bars = %d\n", spec->nbars);
1480 	}
1481     }
1482 
1483     print_user_lines_info(spec, fp);
1484 
1485     if ((spec->code == PLOT_FREQ_SIMPLE ||
1486 	 spec->code == PLOT_FREQ_NORMAL ||
1487 	 spec->code == PLOT_FREQ_GAMMA)) {
1488 	if (mono) {
1489 	    fputs("set style fill solid 0.3\n", fp);
1490 	} else {
1491 	    fputs("set style fill solid 0.6\n", fp);
1492 	}
1493     } else if (spec->fillfrac > 0) {
1494 	gretl_push_c_numeric_locale();
1495 	fprintf(fp, "set style fill solid %g\n", (double) spec->fillfrac);
1496 	gretl_pop_c_numeric_locale();
1497     }
1498 
1499     if (spec->flags & GPT_PRINT_MARKERS) {
1500 	print_data_labels(spec, fp);
1501     }
1502 
1503     if (spec->flags & GPT_FIT_HIDDEN) {
1504 	skipline = 1;
1505     }
1506 
1507     for (i=0; i<spec->n_lines; i++) {
1508 	if (i == skipline || blank_user_line(&spec->lines[i])) {
1509 	    continue;
1510 	}
1511 	if ((spec->flags & GPT_Y2AXIS) && spec->lines[i].yaxis != 1) {
1512 	    any_y2 = 1;
1513 	    break;
1514 	}
1515     }
1516 
1517     if ((spec->bars != NULL && spec->nbars > 0) ||
1518 	spec->data != NULL || spec->auxdata != NULL) {
1519 	anydata = 1;
1520     }
1521 
1522     if (spec->heredata && anydata) {
1523 	fputs("# start inline data\n", fp);
1524 	if (spec->bars != NULL && spec->nbars > 0) {
1525 	    print_plotbars(spec, fp);
1526 	}
1527 	if (spec->data != NULL || spec->auxdata != NULL) {
1528 	    plotspec_print_heredata(spec, &miss, fp);
1529 	}
1530 	fputs("# end inline data\n", fp);
1531 	strcpy(src, "$data");
1532     }
1533 
1534     fputs("plot \\\n", fp);
1535 
1536     gretl_push_c_numeric_locale();
1537 
1538     if (spec->nbars > 0) {
1539 	print_bars_header(spec, fp);
1540     }
1541 
1542     ycol = 2;
1543 
1544     for (i=0; i<spec->n_lines; i++) {
1545 	GPT_LINE *line = &spec->lines[i];
1546 
1547 	if (i == skipline || blank_user_line(line)) {
1548 	    if (i < spec->n_lines - 1) {
1549 		continue;
1550 	    } else {
1551 		break;
1552 	    }
1553 	}
1554 
1555 	if (!string_is_blank(line->formula)) {
1556 	    fprintf(fp, "%s ", line->formula);
1557 	} else if (line->ustr != NULL) {
1558 	    fprintf(fp, "%s using %s ", src, line->ustr);
1559 	} else if (line->mcols != NULL) {
1560 	    int j;
1561 
1562 	    fprintf(fp, "%s using ", src);
1563 	    for (j=1; j<=line->mcols[0]; j++) {
1564 		fprintf(fp, "%d", line->mcols[j]);
1565 		fputc(j < line->mcols[0] ? ':' : ' ', fp);
1566 	    }
1567 	} else {
1568 	    fprintf(fp, "%s using 1", src);
1569 	    if (line->ncols == 5) {
1570 		/* Note: boxplot candlesticks, hard-wired! */
1571 		fprintf(fp, ":%d:%d:%d:%d", ycol+1, ycol, ycol+3, ycol+2);
1572 	    } else if (line->ncols == 2) {
1573 		if (line->flags & GP_LINE_BOXDATA) {
1574 		    /* boxplot median, hard-wired */
1575 		    fprintf(fp, ":%d:%d:%d:%d", ycol, ycol, ycol, ycol);
1576 		} else {
1577 		    fprintf(fp, ":%d", ycol);
1578 		}
1579 	    } else {
1580 		for (k=2; k<=line->ncols; k++) {
1581 		    fprintf(fp, ":%d", ycol + k - 2);
1582 		}
1583 	    }
1584 	    fputc(' ', fp);
1585 	}
1586 
1587 	if ((spec->flags & GPT_Y2AXIS) && line->yaxis != 1) {
1588 	    fprintf(fp, "axes x1y%d ", line->yaxis);
1589 	}
1590 
1591 	if (string_is_blank(line->title)) {
1592 	    fputs("notitle ", fp);
1593 	} else {
1594 	    fprintf(fp, "title \"%s", line->title);
1595 	    if (any_y2 && strchr(line->title, '(') == NULL) {
1596 		if (line->yaxis == 1) {
1597 		    fprintf(fp, " (%s)\" ", _("left"));
1598 		} else {
1599 		    fprintf(fp, " (%s)\" ", _("right"));
1600 		}
1601 	    } else {
1602 		fputs("\" ", fp);
1603 	    }
1604 	}
1605 
1606 	if (line->style == GP_STYLE_FILLEDCURVE && line->type == LT_AUTO) {
1607 	    if (*line->rgb != '\0') {
1608 		fprintf(fp, "lc rgb \"%s\" ", line->rgb);
1609 	    } else {
1610 		print_filledcurve_color(fp);
1611 	    }
1612 	}
1613 
1614 	if (line->style != GP_STYLE_AUTO) {
1615 	    fprintf(fp, "w %s", gp_line_style_name(line->style));
1616 	}
1617 
1618 	if (line->type != LT_AUTO) {
1619 	    fprintf(fp, " lt %d", line->type);
1620 	} else if (spec->nbars > 0 && line->style != GP_STYLE_FILLEDCURVE) {
1621 	    fprintf(fp, " lt %d", i + 1);
1622 	}
1623 
1624 	maybe_print_point_info(line, fp);
1625 
1626 	if (line->style != GP_STYLE_FILLEDCURVE) {
1627 	    if (line->width == 1.0 && spec->scale > 1.0) {
1628 		fprintf(fp, " lw 2");
1629 	    } else if (line->width != 1) {
1630 		fprintf(fp, " lw %g", (double) line->width);
1631 	    }
1632 	}
1633 
1634 	if (line->whiskwidth > 0) {
1635 	    fprintf(fp, " whiskerbars %g", (double) line->whiskwidth);
1636 	}
1637 
1638 	if (spec->heredata) {
1639 	    ycol += line->ncols - 1;
1640 	}
1641 
1642 	if (more_lines(spec, i, skipline)) {
1643 	    fputs(", \\", fp);
1644 	}
1645 
1646 	fputc('\n', fp);
1647     }
1648 
1649     /* supply the data to gnuplot inline, old style */
1650 
1651     if (!spec->heredata && anydata) {
1652 	if (spec->bars != NULL && spec->nbars > 0) {
1653 	    print_plotbars(spec, fp);
1654 	}
1655 	if (spec->data != NULL || spec->auxdata != NULL) {
1656 	    plotspec_print_data(spec, skipline, &miss, fp);
1657 	}
1658     }
1659 
1660     gretl_pop_c_numeric_locale();
1661 
1662     if (png) {
1663 	write_plot_bounding_box_request(fp);
1664     }
1665 
1666     return miss;
1667 }
1668 
set_loess_fit(GPT_SPEC * spec,int d,double q,gretl_matrix * x,gretl_matrix * y,gretl_matrix * yh)1669 static int set_loess_fit (GPT_SPEC *spec, int d, double q, gretl_matrix *x,
1670 			  gretl_matrix *y, gretl_matrix *yh)
1671 {
1672     int i, t, T = gretl_vector_get_length(y);
1673     const double *src;
1674     gretl_matrix *m;
1675 
1676     m = gretl_matrix_alloc(T, 3);
1677     if (m == NULL) {
1678 	return E_DATA;
1679     }
1680 
1681     gretl_matrix_free(spec->data);
1682     spec->data = m;
1683 
1684     for (i=0; i<3; i++) {
1685 	src = i == 0 ? x->val : i == 1 ? y->val : yh->val;
1686 	for (t=0; t<T; t++) {
1687 	    gretl_matrix_set(m, t, i, src[t]);
1688 	}
1689     }
1690 
1691     spec->data = m;
1692     spec->datacols = 3;
1693     spec->nobs = spec->okobs = T;
1694 
1695     g_free(spec->lines[1].title);
1696     spec->lines[1].title = g_strdup_printf(_("loess fit, d = %d, q = %g"), d, q);
1697     spec->lines[1].pscale = 1.0;
1698     spec->lines[1].style = GP_STYLE_LINES;
1699     spec->lines[1].ncols = 2;
1700 
1701     spec->fit = PLOT_FIT_LOESS;
1702 
1703     return 0;
1704 }
1705 
set_plotfit_formula(GPT_LINE * line,FitType f,const double * b,double x0,double pd)1706 static void set_plotfit_formula (GPT_LINE *line, FitType f, const double *b,
1707 				 double x0, double pd)
1708 {
1709     gchar *s = NULL;
1710     char x[32];
1711 
1712     g_free(line->formula);
1713     line->formula = NULL;
1714     gretl_push_c_numeric_locale();
1715 
1716     /* If x0 is not NA that indicates that we've calculated a fit
1717        against time (t = 1,2,3,...,T), and we have to transform the
1718        equation so that it produces correct fitted values given
1719        x-data in the form year[.fraction].
1720     */
1721     if (!na(x0)) {
1722 	if (pd > 1) {
1723 	    sprintf(x, "(%.1f*(x-%.8g)+1)", pd, x0);
1724 	} else {
1725 	    sprintf(x, "(x-%.8g+1)", x0);
1726 	}
1727     } else {
1728 	strcpy(x, "x");
1729     }
1730 
1731     if (f == PLOT_FIT_OLS) {
1732 	s = g_strdup_printf("%.8g + %.8g*%s", b[0], b[1], x);
1733     } else if (f == PLOT_FIT_QUADRATIC) {
1734 	s = g_strdup_printf("%.8g + %.8g*%s + %.8g*%s**2", b[0], b[1], x, b[2], x);
1735     } else if (f == PLOT_FIT_CUBIC) {
1736 	s = g_strdup_printf("%.8g + %.8g*%s + %.8g*%s**2 + %.8g*%s**3",
1737 			    b[0], b[1], x, b[2], x, b[3], x);
1738     } else if (f == PLOT_FIT_INVERSE) {
1739 	s = g_strdup_printf("%.8g + %.8g/%s", b[0], b[1], x);
1740     } else if (f == PLOT_FIT_LOGLIN) {
1741 	s = g_strdup_printf("exp(%.8g + %.8g*%s)", b[0], b[1], x);
1742     } else if (f == PLOT_FIT_LINLOG) {
1743 	if (!na(x0)) {
1744 	    s = g_strdup_printf("%.8g + %.8g*log%s", b[0], b[1], x);
1745 	} else {
1746 	    s = g_strdup_printf("%.8g + %.8g*log(x)", b[0], b[1]);
1747 	}
1748     }
1749 
1750     gretl_pop_c_numeric_locale();
1751     line->formula = s;
1752 }
1753 
set_plotfit_line(GPT_LINE * line,FitType f,const double * b,double x0,double pd)1754 void set_plotfit_line (GPT_LINE *line, FitType f, const double *b,
1755 		       double x0, double pd)
1756 {
1757     char xc = (na(x0)) ? 'X' : 't';
1758     gchar *s = NULL;
1759 
1760     g_free(line->title);
1761     line->title = NULL;
1762 
1763     /* first compose the key string for the fitted line */
1764     if (f == PLOT_FIT_OLS) {
1765 	s = g_strdup_printf("Y = %#.3g %c %#.3g%c", b[0],
1766 			    (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1767     } else if (f == PLOT_FIT_QUADRATIC) {
1768 	s = g_strdup_printf("Y = %#.3g %c %#.3g%c %c %#.3g%c^2", b[0],
1769 			    (b[1] > 0)? '+' : '-', fabs(b[1]), xc,
1770 			    (b[2] > 0)? '+' : '-', fabs(b[2]), xc);
1771     } else if (f == PLOT_FIT_CUBIC) {
1772 	s = g_strdup_printf("Y = %#.3g %c %#.3g%c %c %#.3g%c^2 %c %#.3g%c^3",
1773 			    b[0], (b[1] > 0)? '+' : '-', fabs(b[1]), xc,
1774 			    (b[2] > 0)? '+' : '-', fabs(b[2]), xc,
1775 			    (b[3] > 0)? '+' : '-', fabs(b[3]), xc);
1776     } else if (f == PLOT_FIT_INVERSE) {
1777 	s = g_strdup_printf("Y = %#.3g %c %#.3g(1/%c)", b[0],
1778 			    (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1779     } else if (f == PLOT_FIT_LOGLIN) {
1780 	s = g_strdup_printf("logY = %#.3g %c %#.3g%c", b[0],
1781 			    (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1782 	if (xc == 't' && (pd == 1 || pd == 4 || pd == 12)) {
1783 	    /* display annual growth rate in title */
1784 	    double g = 100 * (pow(exp(b[1]), pd) - 1);
1785 	    gchar *gstr, *tmp;
1786 
1787 	    gstr = g_strdup_printf("\\n(%s %.2f%%)", _("annual growth"), g);
1788 	    tmp = g_strdup_printf("%s%s", s, gstr);
1789 	    g_free(s);
1790 	    s = tmp;
1791 	    g_free(gstr);
1792 	}
1793     } else if (f == PLOT_FIT_LINLOG) {
1794 	s = g_strdup_printf("Y = %#.3g %c %#.3glog(%c)", b[0],
1795 			    (b[1] > 0)? '+' : '-', fabs(b[1]), xc);
1796     }
1797 
1798     line->title = s;
1799 
1800     /* then set the formula itself */
1801     set_plotfit_formula(line, f, b, x0, pd);
1802 }
1803 
plotspec_set_fitted_line(GPT_SPEC * spec,FitType f,double x0)1804 static void plotspec_set_fitted_line (GPT_SPEC *spec, FitType f,
1805 				      double x0)
1806 {
1807     double pd = spec->pd;
1808     const double *b;
1809 
1810     if (f == PLOT_FIT_OLS) {
1811 	b = spec->b_ols->val;
1812     } else if (f == PLOT_FIT_QUADRATIC) {
1813 	b = spec->b_quad->val;
1814     } else if (f == PLOT_FIT_CUBIC) {
1815 	b = spec->b_cub->val;
1816     } else if (f == PLOT_FIT_INVERSE) {
1817 	b = spec->b_inv->val;
1818     } else if (f == PLOT_FIT_LOGLIN) {
1819 	b = spec->b_log->val;
1820     } else if (f == PLOT_FIT_LINLOG) {
1821 	b = spec->b_linlog->val;
1822     } else {
1823 	return;
1824     }
1825 
1826     set_plotfit_line(&spec->lines[1], f, b, x0, pd);
1827 
1828     spec->fit = f;
1829     spec->lines[1].pscale = 1.0;
1830     spec->lines[1].style = GP_STYLE_LINES;
1831     spec->lines[1].ncols = 0;
1832 }
1833 
plotspec_add_fit(GPT_SPEC * spec,FitType f)1834 int plotspec_add_fit (GPT_SPEC *spec, FitType f)
1835 {
1836     gretl_matrix *y = NULL;
1837     gretl_matrix *X = NULL;
1838     gretl_matrix *b = NULL;
1839     gretl_matrix *yh = NULL;
1840     const double *px = spec->data->val;
1841     const double *py;
1842     int T = spec->okobs;
1843     double x0 = NADBL;
1844     double xt, q = 0.5;
1845     int d = 1;
1846     int i, t, k;
1847     int err = 0;
1848 
1849     if ((spec->flags & GPT_TS) && f != PLOT_FIT_LOESS) {
1850 	if (spec->pd == 1 || spec->pd == 4 || spec->pd == 12) {
1851 	    /* Annual, quarterly or monthly data: we'll estimate
1852 	       the fit against t = 1,2,3,...,T to give a more
1853 	       comprehensible displayed equation. So we have
1854 	       to record the initial x-value (e.g. 2008.25) to
1855 	       get the fitted line right on the plot.
1856 	    */
1857 	    x0 = px[0];
1858 	}
1859     }
1860 
1861     if ((f == PLOT_FIT_OLS && spec->b_ols != NULL) ||
1862 	(f == PLOT_FIT_QUADRATIC && spec->b_quad != NULL) ||
1863 	(f == PLOT_FIT_CUBIC && spec->b_cub != NULL) ||
1864 	(f == PLOT_FIT_INVERSE && spec->b_inv != NULL) ||
1865 	(f == PLOT_FIT_LOGLIN && spec->b_log != NULL) ||
1866 	(f == PLOT_FIT_LINLOG && spec->b_linlog != NULL)) {
1867 	/* just activate existing setup */
1868 	plotspec_set_fitted_line(spec, f, x0);
1869 	return 0;
1870     }
1871 
1872     if (f == PLOT_FIT_OLS || f == PLOT_FIT_INVERSE ||
1873 	f == PLOT_FIT_LOGLIN || f == PLOT_FIT_LINLOG) {
1874 	k = 2;
1875     } else if (f == PLOT_FIT_QUADRATIC) {
1876 	k = 3;
1877     } else if (f == PLOT_FIT_CUBIC) {
1878 	k = 4;
1879     } else if (f == PLOT_FIT_LOESS) {
1880 	k = 1;
1881     } else {
1882 	return E_DATA;
1883     }
1884 
1885     y = gretl_column_vector_alloc(T);
1886     X = gretl_matrix_alloc(T, k);
1887     if (y == NULL || X == NULL) {
1888 	err = E_ALLOC;
1889 	goto bailout;
1890     }
1891 
1892     if (f != PLOT_FIT_LOESS) {
1893 	b = gretl_column_vector_alloc(k);
1894 	if (b == NULL) {
1895 	    err = E_ALLOC;
1896 	    goto bailout;
1897 	}
1898     }
1899 
1900     px = spec->data->val;
1901     py = px + spec->nobs;
1902 
1903     i = 0;
1904     for (t=0; t<spec->nobs; t++) {
1905 	if (!na(x0)) {
1906 	    /* use a 1-based time index */
1907 	    xt = t + 1;
1908 	} else {
1909 	    xt = px[t];
1910 	}
1911 	if (!na(py[t]) && !na(xt)) {
1912 	    if (f == PLOT_FIT_LOGLIN) {
1913 		/* y must be positive */
1914 		if (py[t] > 0.0) {
1915 		    y->val[i] = log(py[t]);
1916 		} else {
1917 		    gretl_errmsg_set(_("Non-positive values encountered"));
1918 		    err = E_DATA;
1919 		    goto bailout;
1920 		}
1921 	    } else {
1922 		y->val[i] = py[t];
1923 	    }
1924 	    if (f == PLOT_FIT_LOESS) {
1925 		gretl_matrix_set(X, i, 0, xt);
1926 	    } else {
1927 		gretl_matrix_set(X, i, 0, 1.0);
1928 		if (f == PLOT_FIT_LINLOG) {
1929 		    /* x must be positive */
1930 		    if (xt > 0.0) {
1931 			gretl_matrix_set(X, i, 1, log(xt));
1932 		    } else {
1933 			gretl_errmsg_set(_("Non-positive values encountered"));
1934 			err = E_DATA;
1935 			goto bailout;
1936 		    }
1937 		} else if (f == PLOT_FIT_INVERSE) {
1938 		    gretl_matrix_set(X, i, 1, 1.0 / xt);
1939 		} else {
1940 		    gretl_matrix_set(X, i, 1, xt);
1941 		}
1942 		if (f == PLOT_FIT_QUADRATIC || f == PLOT_FIT_CUBIC) {
1943 		    gretl_matrix_set(X, i, 2, xt * xt);
1944 		}
1945 		if (f == PLOT_FIT_CUBIC) {
1946 		    gretl_matrix_set(X, i, 3, xt * xt * xt);
1947 		}
1948 	    }
1949 	    i++;
1950 	}
1951     }
1952 
1953     if (f == PLOT_FIT_LOESS) {
1954 	err = sort_pairs_by_x(X, y, NULL, spec->markers);
1955 	if (!err) {
1956 	    yh = loess_fit(X, y, d, q, OPT_R, &err);
1957 	}
1958     } else if (f == PLOT_FIT_LOGLIN) {
1959 	double s2;
1960 
1961 	err = gretl_matrix_ols(y, X, b, NULL, NULL, &s2);
1962 	if (!err) {
1963 	    b->val[0] += s2 / 2;
1964 	}
1965     } else {
1966 	err = gretl_matrix_ols(y, X, b, NULL, NULL, NULL);
1967     }
1968 
1969     if (!err && spec->n_lines == 1) {
1970 	err = plotspec_add_line(spec);
1971     }
1972 
1973     if (!err) {
1974 	if (f == PLOT_FIT_OLS) {
1975 	    spec->b_ols = b;
1976 	    b = NULL;
1977 	} else if (f == PLOT_FIT_QUADRATIC) {
1978 	    spec->b_quad = b;
1979 	    b = NULL;
1980 	} else if (f == PLOT_FIT_CUBIC) {
1981 	    spec->b_cub = b;
1982 	    b = NULL;
1983 	} else if (f == PLOT_FIT_INVERSE) {
1984 	    spec->b_inv = b;
1985 	    b = NULL;
1986 	} else if (f == PLOT_FIT_LOGLIN) {
1987 	    spec->b_log = b;
1988 	    b = NULL;
1989 	} else if (f == PLOT_FIT_LINLOG) {
1990 	    spec->b_linlog = b;
1991 	    b = NULL;
1992 	}
1993     }
1994 
1995     if (!err) {
1996 	if (f == PLOT_FIT_LOESS) {
1997 	    set_loess_fit(spec, d, q, X, y, yh);
1998 	    g_free(spec->lines[1].formula);
1999 	    spec->lines[1].formula = NULL;
2000 	} else {
2001 	    plotspec_set_fitted_line(spec, f, x0);
2002 	}
2003     }
2004 
2005  bailout:
2006 
2007     gretl_matrix_free(y);
2008     gretl_matrix_free(X);
2009     gretl_matrix_free(b);
2010     gretl_matrix_free(yh);
2011 
2012 #if 0
2013     fprintf(stderr, "plotspec_add_fit: returning %d\n", err);
2014 #endif
2015 
2016     return err;
2017 }
2018 
2019 /* next: apparatus for producing shaded bars in time-series
2020    plots */
2021 
2022 #define BDEBUG 0
2023 
plotbars_new(int n)2024 static plotbars *plotbars_new (int n)
2025 {
2026     plotbars *bars = malloc(sizeof *bars);
2027 
2028     if (bars != NULL) {
2029 	bars->dx = doubles_array_new(n, 2);
2030 	if (bars->dx == NULL) {
2031 	    free(bars);
2032 	    bars = NULL;
2033 	} else {
2034 	    bars->n = n;
2035 	    bars->t1 = bars->t2 = 0;
2036 	    bars->ymin = bars->ymax = 0;
2037 	}
2038     }
2039 
2040     return bars;
2041 }
2042 
2043 /* Read and check a plain text "plotbars" file.  Such a file may
2044    contain comment lines starting with '#'; other than comments, each
2045    line should contain a pair of space-separated date strings in gretl
2046    monthly date format (YYYY:MM).  These strings represent,
2047    respectively, the start and end of an episode of some kind
2048    (paradigm case: peak and trough of business cycle).  Each pair may
2049    be used to draw a vertical bar on a time-series plot. The file can
2050    contain any number of such pairs.  An example is provided in
2051    <prefix>/share/data/plotbars/nber.txt.
2052 
2053    The dates are converted internally into the format, year plus
2054    decimal fraction of year, and may be used when plotting
2055    annual, quarterly or monthly time series.
2056 */
2057 
parse_bars_file(const char * fname,int * err)2058 static plotbars *parse_bars_file (const char *fname,
2059 				  int *err)
2060 {
2061     FILE *fp;
2062     plotbars *bars = NULL;
2063     char line[128];
2064     int ncolon = 0;
2065     int ndash = 0;
2066     int nother = 0;
2067     int d1, d2, d3, d4;
2068 
2069     fp = gretl_fopen(fname, "r");
2070     if (fp == NULL) {
2071 	*err = E_FOPEN;
2072 	return NULL;
2073     }
2074 
2075 #if BDEBUG > 1
2076     fprintf(stderr, "parse_bars_file: '%s'\n", fname);
2077 #endif
2078 
2079     /* first count the data lines */
2080 
2081     while (fgets(line, sizeof line, fp)) {
2082 	if (*line == '#' || string_is_blank(line)) {
2083 	    continue;
2084 	} else if (sscanf(line, "%d:%d %d:%d", &d1, &d2, &d3, &d4) == 4) {
2085 	    ncolon++;
2086 	} else if (sscanf(line, "%d-%d %d-%d", &d1, &d2, &d3, &d4) == 4) {
2087 	    ndash++;
2088 	} else {
2089 	    nother++;
2090 	}
2091     }
2092 
2093     /* initial check and allocation */
2094 
2095     if (nother > 0) {
2096 	/* non-comment, non-blank, non-parseable cruft found */
2097 	*err = E_DATA;
2098     } else if (ncolon == 0 && ndash == 0) {
2099 	/* no parseable data found */
2100 	*err = E_DATA;
2101     } else if (ncolon > 0 && ndash > 0) {
2102 	/* unparseable mash-up found */
2103 	*err = E_DATA;
2104     } else {
2105 	bars = plotbars_new(ncolon + ndash);
2106 	if (bars == NULL) {
2107 	    *err = E_ALLOC;
2108 	}
2109     }
2110 
2111     if (*err == 0) {
2112 	double x0, x1;
2113 	int i = 0;
2114 
2115 	rewind(fp);
2116 
2117 	/* now read, check, convert and record the date pairs */
2118 
2119 	while (fgets(line, sizeof line, fp) && !*err) {
2120 	    if (*line == '#' || string_is_blank(line)) {
2121 		continue;
2122 	    }
2123 	    if (ncolon) {
2124 		sscanf(line, "%d:%d %d:%d", &d1, &d2, &d3, &d4);
2125 	    } else {
2126 		sscanf(line, "%d-%d %d-%d", &d1, &d2, &d3, &d4);
2127 	    }
2128 	    x0 = d1 + (d2 - 1.0) / 12;
2129 	    x1 = d3 + (d4 - 1.0) / 12;
2130 #if BDEBUG > 1
2131 	    fprintf(stderr, "%.4f %.4f\n", x0, x1);
2132 #endif
2133 	    if (x1 < x0) {
2134 		*err = E_DATA;
2135 	    } else {
2136 		bars->dx[i][0] = x0;
2137 		bars->dx[i][1] = x1;
2138 		i++;
2139 	    }
2140 	}
2141     }
2142 
2143     fclose(fp);
2144 
2145     if (*err == E_DATA) {
2146 	gretl_errmsg_set(_("Dates file does not conform to the specification"));
2147     }
2148 
2149     if (*err && bars != NULL) {
2150 	plotbars_free(bars);
2151 	bars = NULL;
2152     }
2153 
2154     return bars;
2155 }
2156 
2157 /* output data representing vertical shaded bars: each
2158    bar is represented by a pair of rows */
2159 
print_plotbars(GPT_SPEC * spec,FILE * fp)2160 static void print_plotbars (GPT_SPEC *spec, FILE *fp)
2161 {
2162     plotbars *bars = spec->bars;
2163     double y0 = bars->ymin;
2164     double y1 = bars->ymax;
2165     int i, started, stopped;
2166 
2167     if (spec->heredata) {
2168 	fputs("$bars << EOB\n", fp);
2169     }
2170 
2171     for (i=0; i<bars->n; i++) {
2172 	if (bars->dx[i][1] < bars->t1) {
2173 	    continue;
2174 	}
2175 	if (bars->dx[i][0] > bars->t2) {
2176 	    break;
2177 	}
2178 	started = stopped = 0;
2179 	if (bars->dx[i][0] >= bars->t1) {
2180 	    /* start is in range */
2181 	    fprintf(fp, "%.3f %.10g %.10g\n", bars->dx[i][0], y0, y1);
2182 	    started = 1;
2183 	}
2184 	if (bars->dx[i][1] <= bars->t2) {
2185 	    /* stop is in range */
2186 	    if (!started) {
2187 		/* but the start was not */
2188 		fprintf(fp, "%.3f %.10g %.10g\n", bars->t1, y0, y1);
2189 	    }
2190 	    fprintf(fp, "%.3f %.10g %.10g\n", bars->dx[i][1], y0, y1);
2191 	    if (!spec->heredata) {
2192 		fputs("e\n", fp);
2193 	    }
2194 	    stopped = 1;
2195 	}
2196 	if (started && !stopped) {
2197 	    /* truncated */
2198 	    fprintf(fp, "%.3f %.10g %.10g\n", bars->t2, y0, y1);
2199 	    if (!spec->heredata) {
2200 		fputs("e\n", fp);
2201 	    }
2202 	}
2203     }
2204 
2205     if (spec->heredata) {
2206 	fputs("EOB\n", fp);
2207     }
2208 }
2209 
2210 /* output special plot lines for creating vertical shaded bars
2211    in a time-series graph: the corresponding data will take
2212    the form x:ymin:ymax */
2213 
print_bars_header(GPT_SPEC * spec,FILE * fp)2214 static void print_bars_header (GPT_SPEC *spec, FILE *fp)
2215 {
2216     char cstr[8];
2217     int i;
2218 
2219     print_rgb_hash(cstr, get_shadecolor());
2220 
2221     if (spec->heredata) {
2222 	int j = 0;
2223 
2224 	for (i=0; i<spec->nbars; i++) {
2225 	    fprintf(fp, "'$bars' every ::%d::%d using 1:2:3 notitle lc rgb \"%s\" "
2226 		    "w filledcurve, \\\n", j, j+1, cstr);
2227 	    j += 2;
2228 	}
2229     } else {
2230 	for (i=0; i<spec->nbars; i++) {
2231 	    fprintf(fp, "'-' using 1:2:3 notitle lc rgb \"%s\" w filledcurve, \\\n",
2232 		    cstr);
2233 	}
2234     }
2235 }
2236 
print_filledcurve_color(FILE * fp)2237 static void print_filledcurve_color (FILE *fp)
2238 {
2239     char cstr[8];
2240 
2241     print_rgb_hash(cstr, get_shadecolor());
2242     fprintf(fp, "lc rgb \"%s\" ", cstr);
2243 }
2244 
2245 /* given the info in @bars, calculate how many of its start-stop
2246    pairs fall (at least partially) within the current x-axis
2247    range */
2248 
n_bars_shown(double xmin,double xmax,plotbars * bars)2249 static int n_bars_shown (double xmin, double xmax, plotbars *bars)
2250 {
2251     double **dx = bars->dx;
2252     int i, n = 0;
2253 
2254     for (i=0; i<bars->n; i++) {
2255 	if (dx[i][1] < xmin) {
2256 	    continue;
2257 	} else if (dx[i][0] > xmax) {
2258 	    break;
2259 	} else if (dx[i][0] >= xmin || dx[i][1] <= xmax) {
2260 	    n++;
2261 	}
2262     }
2263 
2264     bars->t1 = xmin;
2265     bars->t2 = xmax;
2266 
2267 #if BDEBUG
2268     fprintf(stderr, "n_bars_shown: n = %d\n", n);
2269 #endif
2270 
2271     return n;
2272 }
2273 
2274 /* Given the limits of the data area of a plot, @xmin et al,
2275    and the name of a plain text file containing "bars"
2276    information in the form of a set of start-stop pairs,
2277    try attaching this information to @spec. We fail if
2278    there's something wrong with the info in the file, or
2279    if none of the bars defined therein would be visible
2280    in the current range, @xmin to @xmax.
2281 */
2282 
plotspec_add_bars_info(GPT_SPEC * spec,double xmin,double xmax,double ymin,double ymax,const char * fname)2283 int plotspec_add_bars_info (GPT_SPEC *spec,
2284 			    double xmin, double xmax,
2285 			    double ymin, double ymax,
2286 			    const char *fname)
2287 {
2288     plotbars *bars = NULL;
2289     int err = 0;
2290 
2291     if (spec->bars != NULL) {
2292 	/* clear out any stale info */
2293 	plotbars_free(bars);
2294 	spec->bars = NULL;
2295 	spec->nbars = 0;
2296     }
2297 
2298     bars = parse_bars_file(fname, &err);
2299 
2300     if (!err) {
2301 	int n = n_bars_shown(xmin, xmax, bars);
2302 
2303 #if BDEBUG
2304 	fprintf(stderr, "plotspec_add_bars_info:\n"
2305 		" xmin=%g, xmax=%g, n=%d, ymin=%g, ymax=%g\n",
2306 		xmin, xmax, n, ymin, ymax);
2307 #endif
2308 	if (n > 0) {
2309 	    spec->bars = bars;
2310 	    bars->ymin = ymin;
2311 	    bars->ymax = ymax;
2312 	    spec->nbars = n;
2313 	} else {
2314 	    plotbars_free(bars);
2315 	}
2316     }
2317 
2318     return err;
2319 }
2320 
2321 /* the following is a public interface because it's also
2322    used when reconstituting @spec from a gnuplot command
2323    file.
2324 */
2325 
plotspec_allocate_bars(GPT_SPEC * spec)2326 int plotspec_allocate_bars (GPT_SPEC *spec)
2327 {
2328     int err = 0;
2329 
2330     spec->bars = plotbars_new(spec->nbars);
2331     if (spec->bars == NULL) {
2332 	err = E_ALLOC;
2333     }
2334 
2335     return err;
2336 }
2337 
2338 /* for use when reconstituting @spec from a gnuplot
2339    command file: set the start/stop values for a
2340    particular bar @i.
2341 */
2342 
plotspec_set_bar_info(GPT_SPEC * spec,int i,double t1,double t2)2343 int plotspec_set_bar_info (GPT_SPEC *spec, int i,
2344 			   double t1, double t2)
2345 {
2346     if (i < spec->bars->n) {
2347 	spec->bars->dx[i][0] = t1;
2348 	spec->bars->dx[i][1] = t2;
2349 	return 0;
2350     } else {
2351 	return E_DATA;
2352     }
2353 }
2354 
plotspec_set_bars_limits(GPT_SPEC * spec,double t1,double t2,double ymin,double ymax)2355 void plotspec_set_bars_limits (GPT_SPEC *spec,
2356 			       double t1, double t2,
2357 			       double ymin, double ymax)
2358 {
2359     if (spec->bars != NULL) {
2360 	spec->bars->t1 = t1;
2361 	spec->bars->t2 = t2;
2362 	spec->bars->ymin = ymin;
2363 	spec->bars->ymax = ymax;
2364     }
2365 }
2366 
plotspec_remove_bars(GPT_SPEC * spec)2367 void plotspec_remove_bars (GPT_SPEC *spec)
2368 {
2369     plotbars_free(spec->bars);
2370     spec->bars = NULL;
2371     spec->nbars = 0;
2372 }
2373