"Fossies" - the Fresh Open Source Software Archive

Member "gretl-2020b/lib/src/graphing.c" (7 Apr 2020, 206885 Bytes) of package /linux/misc/gretl-2020b.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "graphing.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2020a_vs_2020b.

    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 /* graphing.c for gretl */
   21 
   22 #include "libgretl.h"
   23 #include "var.h"
   24 #include "system.h"
   25 #include "libset.h"
   26 #include "matrix_extra.h"
   27 #include "forecast.h"
   28 #include "plotspec.h"
   29 #include "usermat.h"
   30 #include "gretl_panel.h"
   31 #include "missing_private.h"
   32 #include "gretl_string_table.h"
   33 #include "uservar.h"
   34 #include "gretl_midas.h"
   35 #include "boxplots.h"
   36 
   37 #ifdef WIN32
   38 # include "gretl_win32.h"
   39 #endif
   40 
   41 #include <unistd.h>
   42 #include <errno.h>
   43 
   44 #define GP_DEBUG 0
   45 
   46 #ifdef WIN32
   47 # include <windows.h>
   48 #else
   49 # include <signal.h>
   50 # if HAVE_SYS_WAIT_H
   51 #  include <sys/wait.h>
   52 # endif
   53 # ifndef WEXITSTATUS
   54 #  define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
   55 # endif
   56 # ifndef WIFEXITED
   57 #  define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
   58 # endif
   59 #endif /* ! _WIN32 */
   60 
   61 /* length of buffer for "set term ..." */
   62 #define TERMLEN 256
   63 
   64 static char gnuplot_path[MAXLEN];
   65 static int gp_small_font_size;
   66 static double default_png_scale = 1.0;
   67 static int xwide = 0;
   68 
   69 static char ad_hoc_font[64];
   70 
   71 typedef struct gnuplot_info_ gnuplot_info;
   72 
   73 struct gnuplot_info_ {
   74     GptFlags flags;
   75     FitType fit;
   76     int *list;
   77     int t1;
   78     int t2;
   79     double xrange;
   80     char timefmt[16];
   81     char xtics[64];
   82     char xfmt[16];
   83     char yfmt[16];
   84     const char *yformula;
   85     const double *x;
   86     gretl_matrix *dvals;
   87     int *withlist;
   88     int band;
   89     double ybase;
   90 };
   91 
   92 enum {
   93     W_POINTS,
   94     W_LINES,
   95     W_IMPULSES,
   96     W_LP,
   97     W_BOXES,
   98     W_STEPS
   99 };
  100 
  101 #define MAX_LETTERBOX_LINES 8
  102 
  103 #define ts_plot(g)      ((g)->flags & GPT_TS)
  104 
  105 #if GP_DEBUG
  106 static void print_gnuplot_flags (int flags, int revised);
  107 #endif
  108 
  109 struct plot_type_info {
  110     PlotType ptype;
  111     const char *pstr;
  112 };
  113 
  114 struct plot_type_info ptinfo[] = {
  115     { PLOT_REGULAR,        NULL },
  116     { PLOT_CORRELOGRAM,    "correlogram" },
  117     { PLOT_CUSUM,          "CUSUM test" },
  118     { PLOT_FORECAST,       "forecasts with 95 pc conf. interval" },
  119     { PLOT_FREQ_SIMPLE,    "frequency plot (simple)" },
  120     { PLOT_FREQ_NORMAL,    "frequency plot (against normal)" },
  121     { PLOT_FREQ_GAMMA,     "frequency plot (against gamma)" },
  122     { PLOT_GARCH,          "GARCH residual plot" },
  123     { PLOT_HURST,          "rescaled range plot" },
  124     { PLOT_IRFBOOT,        "impulse response plot with quantiles" },
  125     { PLOT_KERNEL,         "kernel density plot" },
  126     { PLOT_LEVERAGE,       "leverage/influence plot" },
  127     { PLOT_MULTI_SCATTER,  "multiple scatterplots" },
  128     { PLOT_PERIODOGRAM,    "periodogram" },
  129     { PLOT_RANGE_MEAN,     "range-mean plot" },
  130     { PLOT_H_TEST,         "sampling distribution" },
  131     { PLOT_PROB_DIST,      "probability distribution" },
  132     { PLOT_TRI_GRAPH,      "TRAMO / X12A tri-graph" },
  133     { PLOT_ROOTS,          "roots plot" },
  134     { PLOT_ELLIPSE,        "confidence ellipse plot" },
  135     { PLOT_MULTI_IRF,      "multiple impulse responses" },
  136     { PLOT_PANEL,          "multiple panel plots" },
  137     { PLOT_BI_GRAPH,       "double time-series plot" },
  138     { PLOT_MANY_TS,        "multiple timeseries" },
  139     { PLOT_RQ_TAU,         "tau sequence plot" },
  140     { PLOT_FACTORIZED,     "factorized scatter" },
  141     { PLOT_BOXPLOTS,       "boxplots" },
  142     { PLOT_CURVE,          "curve" },
  143     { PLOT_QQ,             "QQ plot" },
  144     { PLOT_USER,           "user-defined plot" },
  145     { PLOT_XCORRELOGRAM,   "cross-correlogram" },
  146     { PLOT_BAR,            "bars" },
  147     { PLOT_STACKED_BAR,    "stacked-bars" },
  148     { PLOT_3D,             "3-D plot" },
  149     { PLOT_BAND,           "band plot" },
  150     { PLOT_HEATMAP,        "heatmap" },
  151     { PLOT_TYPE_MAX,       NULL }
  152 };
  153 
  154 enum {
  155     BP_REGULAR,
  156     BP_BLOCKMAT
  157 };
  158 
  159 static int graph_list_adjust_sample (int *list,
  160                      gnuplot_info *ginfo,
  161                      const DATASET *dset,
  162                      int listmin);
  163 static void clear_gpinfo (gnuplot_info *gi);
  164 static void make_time_tics (gnuplot_info *gi,
  165                 const DATASET *dset,
  166                 int many, char *xlabel,
  167                 PRN *prn);
  168 static void get_multiplot_layout (int n, int tseries,
  169                   int *rows, int *cols);
  170 static int plot_with_band (int mode,
  171                gnuplot_info *gi,
  172                const char *literal,
  173                DATASET *dset,
  174                gretlopt opt);
  175 
  176 static char *gretl_emf_term_line (char *term_line,
  177                   PlotType ptype,
  178                   GptFlags flags);
  179 
  180 #ifndef WIN32
  181 
  182 #define SPAWN_DEBUG 0
  183 
  184 /**
  185  * gnuplot_test_command:
  186  * @cmd: gnuplot command string.
  187  *
  188  * See if the installed version of gnuplot will accept a given
  189  * command.
  190  *
  191  * Returns: 0 if gnuplot successfully handles the given command,
  192  * 1 on error.
  193  */
  194 
  195 int gnuplot_test_command (const char *cmd)
  196 {
  197     int ok, ret = 1;
  198     int child_pid = 0, sinp = 0, serr = 0;
  199     GError *error = NULL;
  200     gchar *argv[] = {
  201     NULL,
  202     NULL
  203     };
  204 
  205     if (*gnuplot_path == '\0') {
  206     strcpy(gnuplot_path, gretl_gnuplot_path());
  207     }
  208 
  209     argv[0] = gnuplot_path;
  210 
  211     ok = g_spawn_async_with_pipes (NULL,
  212                    argv,
  213                    NULL,
  214                    G_SPAWN_SEARCH_PATH |
  215                    G_SPAWN_STDOUT_TO_DEV_NULL |
  216                    G_SPAWN_DO_NOT_REAP_CHILD,
  217                    NULL,
  218                    NULL,
  219                    &child_pid,
  220                    &sinp,
  221                    NULL,
  222                    &serr,
  223                    &error);
  224 
  225 # if SPAWN_DEBUG
  226     fprintf(stderr, "Testing gnuplot command '%s'\n", cmd);
  227     fprintf(stderr, "ok=%d, child_pid=%d, sinp=%d\n",
  228         ok, child_pid, sinp);
  229 # endif
  230 
  231     if (ok) {
  232     char errbuf[128];
  233     int test, status;
  234     int errbytes = 0;
  235 
  236     errbytes += write(sinp, cmd, strlen(cmd));
  237     errbytes += write(sinp, "\n", 1);
  238     close(sinp);
  239     test = waitpid(child_pid, &status, 0);
  240 # if SPAWN_DEBUG
  241     fprintf(stderr, "waitpid returned %d, WIFEXITED %d, "
  242         "WEXITSTATUS %d\n", test, WIFEXITED(status),
  243         WEXITSTATUS(status));
  244 # endif
  245     if (test == child_pid && WIFEXITED(status)) {
  246         ret = WEXITSTATUS(status);
  247     }
  248     errbytes = read(serr, errbuf, sizeof errbuf - 1);
  249     if (errbytes > 0) {
  250         errbuf[errbytes] = '\0';
  251         if (strstr(errbuf, "not find/open font")) {
  252 # if SPAWN_DEBUG
  253         fprintf(stderr, "%s\n", errbuf);
  254 # endif
  255         if (strstr(cmd, "font") != NULL) {
  256             ret = 1;
  257         }
  258         }
  259     }
  260     close(serr);
  261     } else {
  262     fprintf(stderr, "error: '%s'\n", error->message);
  263     g_error_free(error);
  264     }
  265 
  266 # if SPAWN_DEBUG
  267     fprintf(stderr, "gnuplot test: ret = %d\n", ret);
  268 # endif
  269 
  270     return ret;
  271 }
  272 
  273 double gnuplot_version (void)
  274 {
  275     static double vnum = 0.0;
  276 
  277     if (vnum == 0.0) {
  278     gboolean ok;
  279     gchar *sout = NULL;
  280     gchar *argv[] = {
  281         NULL,
  282         NULL,
  283         NULL
  284     };
  285 
  286     if (*gnuplot_path == '\0') {
  287         strcpy(gnuplot_path, gretl_gnuplot_path());
  288     }
  289 
  290     argv[0] = gnuplot_path;
  291     argv[1] = "--version";
  292 
  293     ok = g_spawn_sync (NULL,
  294                argv,
  295                NULL,
  296                G_SPAWN_SEARCH_PATH |
  297                G_SPAWN_STDERR_TO_DEV_NULL,
  298                NULL,
  299                NULL,
  300                &sout,
  301                NULL,
  302                NULL,
  303                NULL);
  304 
  305     if (ok && sout != NULL) {
  306         if (!strncmp(sout, "gnuplot ", 8)) {
  307         /* e.g. "gnuplot 5.0 patchlevel 0" */
  308         char *s = strstr(sout, "patchlevel");
  309         int plev;
  310 
  311         vnum = dot_atof(sout + 8);
  312         if (s != NULL && sscanf(s + 10, "%d", &plev) == 1) {
  313             vnum += plev / 100.0;
  314         }
  315         }
  316         g_free(sout);
  317     }
  318     }
  319 
  320     return vnum;
  321 }
  322 
  323 #else /* MS Windows */
  324 
  325 double gnuplot_version (void)
  326 {
  327     /* As of early 2020, the packages for Windows
  328        include gnuplot 5.2 */
  329     return 5.2;
  330 }
  331 
  332 #endif /* MS Windows or not */
  333 
  334 static int gp_list_pos (const char *s, const int *list,
  335             const DATASET *dset)
  336 {
  337     int k;
  338 
  339     if (integer_string(s)) {
  340     k = atoi(s);
  341     } else {
  342     k = current_series_index(dset, s);
  343     }
  344 
  345     return in_gretl_list(list, k);
  346 }
  347 
  348 static int plot_ci = GNUPLOT;
  349 
  350 void set_effective_plot_ci (int ci)
  351 {
  352     plot_ci = ci;
  353 }
  354 
  355 /* When we get from the user something like
  356 
  357    --with-lines=foo,bar
  358 
  359    this indicates that the "with lines" format should be
  360    applied to selected y-axis variables, not all.
  361 */
  362 
  363 static int gp_set_non_point_info (gnuplot_info *gi,
  364                   const int *list,
  365                   const DATASET *dset,
  366                   gretlopt opt)
  367 {
  368     const char *s = get_optval_string(plot_ci, opt);
  369     int withval = W_POINTS;
  370     int i, imax = gi->withlist[0];
  371 
  372     if (opt == OPT_O) {
  373     withval = W_LINES;
  374     } else if (opt == OPT_M) {
  375     withval = W_IMPULSES;
  376     } else if (opt == OPT_P) {
  377     withval = W_LP;
  378     } else if (opt == OPT_B) {
  379     withval = W_BOXES;
  380     } else if (opt & OPT_Q) {
  381     withval = W_STEPS;
  382     }
  383 
  384     if (s == NULL) {
  385     /* spec applies to all members of list */
  386     for (i=1; i<=imax; i++) {
  387         if (gi->withlist[i] == W_POINTS) {
  388         gi->withlist[i] = withval;
  389         }
  390     }
  391     } else if (strchr(s, ',') != NULL) {
  392     /* spec has multiple components */
  393     gchar **strs = g_strsplit(s, ",", 0);
  394     int j;
  395 
  396     for (j=0; strs[j]!=NULL; j++) {
  397         i = gp_list_pos(strs[j], list, dset);
  398         if (i > 0 && i <= imax) {
  399         gi->withlist[i] = withval;
  400         }
  401     }
  402     g_strfreev(strs);
  403     } else {
  404     /* just one component */
  405     i = gp_list_pos(s, list, dset);
  406     if (i > 0 && i <= imax) {
  407         gi->withlist[i] = withval;
  408     }
  409     }
  410 
  411     return 0;
  412 }
  413 
  414 static int plain_lines_spec (gretlopt opt)
  415 {
  416     if ((opt & OPT_O) && !(opt & (OPT_M | OPT_B | OPT_P | OPT_Q))) {
  417     return get_optval_string(plot_ci, OPT_O) == NULL;
  418     } else {
  419     return 0;
  420     }
  421 }
  422 
  423 static int plain_impulses_spec (gretlopt opt)
  424 {
  425     if ((opt & OPT_M) && !(opt & (OPT_O | OPT_B | OPT_P | OPT_Q))) {
  426     return get_optval_string(plot_ci, OPT_M) == NULL;
  427     } else {
  428     return 0;
  429     }
  430 }
  431 
  432 static int plain_steps_spec (gretlopt opt)
  433 {
  434     if ((opt & OPT_Q) && !(opt & (OPT_O | OPT_M | OPT_B | OPT_P))) {
  435     return get_optval_string(plot_ci, OPT_Q) == NULL;
  436     } else {
  437     return 0;
  438     }
  439 }
  440 
  441 static int get_fit_type (gnuplot_info *gi)
  442 {
  443     const char *ftype = get_optval_string(plot_ci, OPT_F);
  444     int err = 0;
  445 
  446     if (ftype == NULL || *ftype == '\0') {
  447     err = E_DATA;
  448     } else if (!strcmp(ftype, "none")) {
  449     gi->flags |= GPT_FIT_OMIT;
  450     } else if (!strcmp(ftype, "linear")) {
  451     gi->fit = PLOT_FIT_OLS;
  452     } else if (!strcmp(ftype, "quadratic")) {
  453     gi->fit = PLOT_FIT_QUADRATIC;
  454     } else if (!strcmp(ftype, "cubic")) {
  455     gi->fit = PLOT_FIT_CUBIC;
  456     } else if (!strcmp(ftype, "inverse")) {
  457     gi->fit = PLOT_FIT_INVERSE;
  458     } else if (!strcmp(ftype, "loess")) {
  459     gi->fit = PLOT_FIT_LOESS;
  460     } else if (!strcmp(ftype, "semilog")) {
  461     gi->fit = PLOT_FIT_LOGLIN;
  462     } else if (!strcmp(ftype, "linlog")) {
  463     gi->fit = PLOT_FIT_LINLOG;
  464     } else {
  465     err = invalid_field_error(ftype);
  466     }
  467 
  468     return err;
  469 }
  470 
  471 static void maybe_record_font_choice (gretlopt opt)
  472 {
  473     const char *s = get_optval_string(plot_ci, opt);
  474 
  475     if (s != NULL) {
  476     ad_hoc_font[0] = '\0';
  477     strcat(ad_hoc_font, s);
  478     gretl_charsub(ad_hoc_font, ',', ' ');
  479     }
  480 }
  481 
  482 static int get_gp_flags (gnuplot_info *gi, gretlopt opt,
  483              const int *list, const DATASET *dset)
  484 {
  485     int n_yvars = list[0] - 1;
  486     int err = 0;
  487 
  488     gi->flags = 0;
  489 
  490     if (opt & OPT_N) {
  491     /* --band */
  492     if (opt & OPT_T) {
  493         /* --time-series */
  494         gi->flags |= (GPT_TS | GPT_IDX);
  495         /* there's no xvar in @list */
  496         n_yvars++;
  497     }
  498     gi->flags |= GPT_FIT_OMIT;
  499     gi->band = 1;
  500     goto linespec;
  501     }
  502 
  503     if (opt & OPT_W) {
  504     /* --font=<fontspec> */
  505     maybe_record_font_choice(OPT_W);
  506     }
  507 
  508     if (opt & OPT_L) {
  509     /* log y axis */
  510     const char *sbase = get_optval_string(GNUPLOT, OPT_L);
  511 
  512     gi->flags |= GPT_LOGY;
  513     gi->ybase = 10;
  514     if (sbase != NULL) {
  515         gi->ybase = atof(sbase);
  516         if (gi->ybase <= 0) {
  517         gi->ybase = 10;
  518         }
  519     }
  520     }
  521 
  522     if (opt & OPT_S) {
  523     /* the old --suppress-fitted option may still be used
  524        internally, for some plot types */
  525     gi->flags |= GPT_FIT_OMIT;
  526     }
  527 
  528     if (opt & OPT_R) {
  529     /* internal option for residual plot */
  530     gi->flags |= GPT_RESIDS;
  531     } else if (opt & OPT_A) {
  532     /* internal option for fitted-actual plot */
  533     gi->flags |= GPT_FA;
  534     }
  535 
  536     if (opt & OPT_Z) {
  537     /* --dummy */
  538     gi->flags |= GPT_DUMMY;
  539     } else if (opt & OPT_C) {
  540     /* --control */
  541     gi->flags |= GPT_XYZ;
  542     } else {
  543     if (opt & OPT_T) {
  544         /* --time-series */
  545         gi->flags |= GPT_IDX;
  546         /* there's no xvar in @list */
  547         n_yvars++;
  548     }
  549     }
  550 
  551  linespec:
  552 
  553     if (plain_lines_spec(opt)) {
  554     /* just using lines */
  555     gi->flags |= GPT_LINES;
  556     } else if (plain_impulses_spec(opt)) {
  557     /* just using impulses */
  558     gi->flags |= GPT_IMPULSES;
  559     } else if (plain_steps_spec(opt)) {
  560     /* just using steps */
  561     gi->flags |= GPT_STEPS;
  562     } else if (opt & (OPT_M | OPT_O | OPT_P | OPT_B)) {
  563     /* for handling per-variable "plot with" options */
  564     gi->withlist = gretl_list_new(n_yvars);
  565     }
  566 
  567     if (gi->withlist != NULL) {
  568     if (opt & OPT_M) {
  569         /* --with-impulses */
  570         gp_set_non_point_info(gi, list, dset, OPT_M);
  571     }
  572     if (opt & OPT_O) {
  573         /* --with-lines */
  574         gp_set_non_point_info(gi, list, dset, OPT_O);
  575     }
  576     if (opt & OPT_P) {
  577         /* --with-lp */
  578         gp_set_non_point_info(gi, list, dset, OPT_P);
  579     }
  580     if (opt & OPT_B) {
  581         /* --with-boxes */
  582         gp_set_non_point_info(gi, list, dset, OPT_B);
  583     }
  584     }
  585 
  586     if (opt & OPT_G) {
  587     /* internal option, saving as icon */
  588     gi->flags |= GPT_ICON;
  589     }
  590 
  591     gi->fit = PLOT_FIT_NONE;
  592 
  593     if (!(gi->flags & GPT_FIT_OMIT) && n_yvars == 1) {
  594     if (opt & OPT_F) {
  595         /* the --fit=fitspec option */
  596         err = get_fit_type(gi);
  597     }
  598     }
  599 
  600     if (xwide) {
  601     /* access file-scope global */
  602     gi->flags |= GPT_XW;
  603     xwide = 0;
  604     }
  605 
  606 #if GP_DEBUG
  607     if (gi->flags) {
  608     print_gnuplot_flags(gi->flags, 0);
  609     }
  610 #endif
  611 
  612     return err;
  613 }
  614 
  615 #define GP_USE_NAN 1
  616 
  617 /* With gnuplot 5, if we represent NAs by "?" (flagging that we're
  618    doing so with 'set datafile missing "?"')  then in "with-lines"
  619    plots the lines are continuous across the missing values (joining
  620    the successive non-missing values), so giving no visual clue that
  621    there's anything missing. If we want lines to have gaps in case of
  622    missing values we need to write "NaN" rather than "?" into the
  623    gnuplot data block. See the gnuplot 5 doc for "missing":
  624 
  625    "Gnuplot makes a distinction between missing data and invalid data
  626    (e.g. "NaN", 1/0.).  For example invalid data causes a gap in a
  627    line drawn through sequential data points; missing data does not."
  628 
  629    This represents a change from gnuplot 4 behavior, which I didn't
  630    notice for quite a while. But I think we want the gaps, hence
  631    the following function.
  632 
  633    Allin, 2018-08-12
  634 */
  635 
  636 #if GP_USE_NAN
  637 static const char *gpna = "NaN";
  638 #else
  639 static const char *gpna = "?";
  640 #endif
  641 
  642 void write_gp_dataval (double x, FILE *fp, int final)
  643 {
  644     if (final) {
  645     if (na(x)) {
  646         fprintf(fp, "%s\n", gpna);
  647     } else {
  648         fprintf(fp, "%.10g\n", x);
  649     }
  650     } else {
  651     if (na(x)) {
  652         fprintf(fp, "%s ", gpna);
  653     } else {
  654         fprintf(fp, "%.10g ", x);
  655     }
  656     }
  657 }
  658 
  659 static void printvars (FILE *fp, int t,
  660                const int *list,
  661                const DATASET *dset,
  662                gnuplot_info *gi,
  663                const char *label,
  664                double offset)
  665 {
  666     const double *x = (gi != NULL)? gi->x : NULL;
  667     double xt;
  668     int i;
  669 
  670     if (x != NULL) {
  671     xt = x[t] + offset;
  672     if (gi->flags & GPT_TIMEFMT) {
  673         fprintf(fp, "%.0f ", xt);
  674     } else {
  675         fprintf(fp, "%.10g ", xt);
  676     }
  677     }
  678 
  679     for (i=1; i<=list[0]; i++) {
  680     xt = dset->Z[list[i]][t];
  681     if (!na(xt) && x == NULL && i == 1) {
  682         /* the x variable */
  683         xt += offset;
  684     }
  685     write_gp_dataval(xt, fp, 0);
  686     }
  687 
  688     if (label != NULL) {
  689     fprintf(fp, "# %s", label);
  690     }
  691 
  692     fputc('\n', fp);
  693 }
  694 
  695 static int factor_check (gnuplot_info *gi, const DATASET *dset)
  696 {
  697     int err = 0;
  698     int v3 = 0;
  699 
  700     if (gi->list[0] != 3) {
  701     err = E_DATA;
  702     } else {
  703     v3 = gi->list[3];
  704     if (!series_is_discrete(dset, v3) &&
  705         !gretl_isdiscrete(gi->t1, gi->t2, dset->Z[v3])) {
  706         err = E_DATA;
  707     }
  708     }
  709 
  710     if (err) {
  711     gretl_errmsg_set(_("You must supply three variables, the last of "
  712                "which is discrete"));
  713     } else {
  714     const double *d = dset->Z[v3] + gi->t1;
  715     int T = gi->t2 - gi->t1 + 1;
  716 
  717     gi->dvals = gretl_matrix_values(d, T, OPT_S, &err);
  718     }
  719 
  720     return err;
  721 }
  722 
  723 #ifndef WIN32
  724 
  725 int gnuplot_has_wxt (void)
  726 {
  727     static int err = -1;
  728 
  729     if (err == -1) {
  730     err = gnuplot_test_command("set term wxt");
  731     }
  732 
  733     return !err;
  734 }
  735 
  736 static int gnuplot_has_x11 (void)
  737 {
  738     static int err = -1;
  739 
  740     if (err == -1) {
  741     err = gnuplot_test_command("set term x11");
  742     }
  743 
  744     return !err;
  745 }
  746 
  747 static int gnuplot_has_qt (void)
  748 {
  749     static int err = -1;
  750 
  751     if (err == -1) {
  752     err = gnuplot_test_command("set term qt");
  753     }
  754 
  755     return !err;
  756 }
  757 
  758 static int gnuplot_has_tikz (void)
  759 {
  760     static int err = -1;
  761 
  762     if (err == -1) {
  763     err = gnuplot_test_command("set term tikz");
  764     }
  765 
  766     return !err;
  767 }
  768 
  769 #else
  770 
  771 int gnuplot_has_wxt (void)
  772 {
  773     /* There's no WxWidgets support in the current
  774        Windows build of gnuplot 5
  775     */
  776     return 0;
  777 }
  778 
  779 static int gnuplot_has_tikz (void)
  780 {
  781     /* There's no Lua/TikZ support in the current
  782        Windows build of gnuplot 5
  783     */
  784     return 0;
  785 }
  786 
  787 #endif /* !WIN32 or WIN32 */
  788 
  789 /* apparatus for handling plot colors */
  790 
  791 static const gretlRGB default_color[N_GP_COLORS] = {
  792     { 0xff, 0x00, 0x00 },
  793     { 0x00, 0x00, 0xff },
  794     { 0x00, 0xcc, 0x00 }, /* full-intensity green is not easily legible */
  795     { 0xbf, 0x25, 0xb2 },
  796     { 0x8f, 0xaa, 0xb3 },
  797     { 0xff, 0xa5, 0x00 },
  798     { 0x5f, 0x6b, 0x84 }, /* box fill */
  799     { 0xdd, 0xdd, 0xdd }, /* shade fill */
  800 };
  801 
  802 static gretlRGB user_color[N_GP_COLORS] = {
  803     { 0xff, 0x00, 0x00 },
  804     { 0x00, 0x00, 0xff },
  805     { 0x00, 0xcc, 0x00 },
  806     { 0xbf, 0x25, 0xb2 },
  807     { 0x8f, 0xaa, 0xb3 },
  808     { 0xff, 0xa5, 0x00 },
  809     { 0x5f, 0x6b, 0x84 },
  810     { 0xdd, 0xdd, 0xdd }
  811 };
  812 
  813 void print_rgb_hash (char *s, const gretlRGB *color)
  814 {
  815     sprintf(s, "#%02x%02x%02x", color->r, color->g, color->b);
  816 }
  817 
  818 void gretl_rgb_get (gretlRGB *color, const char *s)
  819 {
  820     int n, r, g, b;
  821 
  822     n = sscanf(s, "#%2x%2x%2x", &r, &g, &b);
  823 
  824     if (n == 3) {
  825     color->r = r;
  826     color->g = g;
  827     color->b = b;
  828     } else {
  829     color->r = color->g = color->b = 0;
  830     }
  831 }
  832 
  833 void print_palette_string (char *s)
  834 {
  835     char colstr[8];
  836     int i;
  837 
  838     *s = '\0';
  839 
  840     for (i=0; i<N_GP_COLORS; i++) {
  841     sprintf(colstr, "x%02x%02x%02x", user_color[i].r, user_color[i].g,
  842         user_color[i].b);
  843     strcat(s, colstr);
  844     if (i < N_GP_COLORS - 1) {
  845         strcat(s, " ");
  846     }
  847     }
  848 }
  849 
  850 const gretlRGB *get_graph_color (int i)
  851 {
  852     return (i >= 0 && i < N_GP_COLORS)? &user_color[i] : NULL;
  853 }
  854 
  855 void set_graph_palette (int i, gretlRGB color)
  856 {
  857     if (i >= 0 && i < N_GP_COLORS) {
  858     user_color[i] = color;
  859     } else {
  860     fprintf(stderr, "Out of bounds color index %d\n", i);
  861     }
  862 }
  863 
  864 void set_graph_palette_from_string (int i, const char *s)
  865 {
  866     int err = 0;
  867 
  868     if (i >= 0 && i < N_GP_COLORS) {
  869     unsigned int x[3];
  870 
  871     if (sscanf(s + 1, "%02x%02x%02x", &x[0], &x[1], &x[2]) == 3) {
  872         user_color[i].r = x[0];
  873         user_color[i].g = x[1];
  874         user_color[i].b = x[2];
  875     } else {
  876         err = 1;
  877     }
  878     } else {
  879     err = 1;
  880     }
  881 
  882     if (err) {
  883     fprintf(stderr, "Error in set_graph_palette_from_string(%d, '%s')\n",
  884         i, s);
  885     }
  886 }
  887 
  888 void graph_palette_reset (int i)
  889 {
  890     if (i >= 0 && i < N_GP_COLORS) {
  891     user_color[i] = default_color[i];
  892     }
  893 }
  894 
  895 /* Given a string @s such as "Sans 8" or "Bodoni MT 12", write
  896    the name part into @name and the point-size part into @psz.
  897    Return 2 if we got both a name and a size, 1 if we just
  898    got a name, 0 if we got nothing.
  899 */
  900 
  901 int split_graph_fontspec (const char *s, char *name, int *psz)
  902 {
  903     int i, k = 0, n = strlen(s);
  904     int nf = 0;
  905 
  906     for (i=n-1; i>0; i--) {
  907     if (isdigit(s[i])) k++;
  908     else break;
  909     }
  910 
  911     if (k > 0) {
  912     /* got a size */
  913     char ptstr[8];
  914 
  915     *ptstr = *name = '\0';
  916     strncat(ptstr, s + n - k, k);
  917     *psz = atoi(ptstr);
  918     strncat(name, s, n - k - 1);
  919     nf = 2;
  920     } else if (*s != '\0') {
  921     nf = 1;
  922     strcpy(name, s);
  923     }
  924 
  925     return nf;
  926 }
  927 
  928 static void maybe_set_small_font (int nplots)
  929 {
  930     gp_small_font_size = (nplots > 4)? 6 : 0;
  931 }
  932 
  933 static void write_png_font_string (char *fstr,
  934                    char *ad_hoc_fontspec,
  935                    PlotType ptype,
  936                    const char *grfont,
  937                    double scale)
  938 {
  939     int adhoc = 0;
  940 
  941     if (grfont == NULL) {
  942     if (ad_hoc_font[0] != '\0') {
  943         adhoc = 1;
  944         grfont = ad_hoc_font;
  945     } else {
  946         grfont = gretl_png_font();
  947     }
  948     }
  949 
  950     if (*grfont == '\0') {
  951     grfont = getenv("GRETL_PNG_GRAPH_FONT");
  952     }
  953 
  954     if (*grfont == '\0') {
  955     *fstr = '\0';
  956     return;
  957     } else {
  958     char fname[128];
  959     int nf, fsize = 0;
  960 
  961     nf = split_graph_fontspec(grfont, fname, &fsize);
  962     if (nf == 2) {
  963         if (maybe_big_multiplot(ptype) && gp_small_font_size > 0) {
  964         fsize = gp_small_font_size;
  965         }
  966         if (scale > 1.0) {
  967         fsize = round(scale * fsize);
  968         }
  969         sprintf(fstr, " font \"%s,%d\"", fname, fsize);
  970     } else if (nf == 1) {
  971         sprintf(fstr, " font \"%s\"", fname);
  972     }
  973     if (adhoc) {
  974         strcpy(ad_hoc_fontspec, grfont);
  975     }
  976     /* ensure this setting doesn't outstay its welcome */
  977     ad_hoc_font[0] = '\0';
  978     }
  979 }
  980 
  981 /* for gnuplot pdfcairo, epscairo output */
  982 
  983 static void write_other_font_string (char *fstr, int stdsize)
  984 {
  985     if (ad_hoc_font[0] != '\0') {
  986     char fname[128];
  987     int nf, fsize = 0;
  988 
  989     nf = split_graph_fontspec(ad_hoc_font, fname, &fsize);
  990     if (nf == 2) {
  991         sprintf(fstr, "%s,%d", fname, fsize);
  992     } else if (nf == 1) {
  993         sprintf(fstr, "%s,%d", fname, stdsize);
  994     }
  995     ad_hoc_font[0] = '\0';
  996     } else {
  997     sprintf(fstr, "sans,%d", stdsize);
  998     }
  999 }
 1000 
 1001 void write_plot_line_styles (int ptype, FILE *fp)
 1002 {
 1003     char cstr[12];
 1004     int i;
 1005 
 1006     if (ptype == PLOT_3D) {
 1007     for (i=0; i<2; i++) {
 1008         print_rgb_hash(cstr, &user_color[i]);
 1009         fprintf(fp, "set linetype %d lc rgb \"%s\"\n", i+1, cstr);
 1010     }
 1011     } else if (ptype == PLOT_BOXPLOTS) {
 1012     for (i=0; i<2; i++) {
 1013         print_rgb_hash(cstr, &user_color[i+1]);
 1014         fprintf(fp, "set linetype %d lc rgb \"%s\"\n", i+1, cstr);
 1015     }
 1016     } else if (frequency_plot_code(ptype)) {
 1017     print_rgb_hash(cstr, &user_color[BOXCOLOR]);
 1018     fprintf(fp, "set linetype 1 lc rgb \"%s\"\n", cstr);
 1019     fputs("set linetype 2 lc rgb \"#000000\"\n", fp);
 1020     } else if (ptype == PLOT_RQ_TAU) {
 1021     fputs("set linetype 1 lc rgb \"#000000\"\n", fp);
 1022     for (i=1; i<BOXCOLOR; i++) {
 1023         print_rgb_hash(cstr, &user_color[i]);
 1024         fprintf(fp, "set linetype %d lc rgb \"%s\"\n", i+1, cstr);
 1025     }
 1026     } else if (ptype != PLOT_HEATMAP) {
 1027     for (i=0; i<BOXCOLOR; i++) {
 1028         print_rgb_hash(cstr, &user_color[i]);
 1029         fprintf(fp, "set linetype %d lc rgb \"%s\"\n", i+1, cstr);
 1030     }
 1031     }
 1032 }
 1033 
 1034 #ifdef WIN32
 1035 
 1036 /* Here we're looking at a path, @src, that we're going
 1037    to write into a gnuplot script, as (part of) the
 1038    plot output filename or the plot bounding-box
 1039    filename. Since we write "set encoding utf8" into the
 1040    preamble to our plot scripts we need to ensure that
 1041    the path is in fact in UTF-8 (even on MS Windows).
 1042    We also want to ensure use of forward slashes in this
 1043    context.
 1044 */
 1045 
 1046 static int adjust_filename (char *targ, const char *src,
 1047                 int ensure_utf8)
 1048 {
 1049     int err = 0;
 1050 
 1051     *targ = '\0';
 1052 
 1053     if (ensure_utf8 && !g_utf8_validate(src, -1, NULL)) {
 1054     GError *gerr = NULL;
 1055     gchar *tmp;
 1056     gsize sz;
 1057 
 1058     tmp = g_locale_to_utf8(src, -1, NULL, &sz, &gerr);
 1059     if (tmp != NULL) {
 1060         strcpy(targ, tmp);
 1061         g_free(tmp);
 1062     } else {
 1063         err = 1;
 1064         if (gerr != NULL) {
 1065         gretl_errmsg_set(gerr->message);
 1066         g_error_free(gerr);
 1067         }
 1068     }
 1069     } else {
 1070     /* OK, @src is assumed to be OK already */
 1071     strcpy(targ, src);
 1072     }
 1073 
 1074     if (!err) {
 1075     while (*targ) {
 1076         if (*targ == '\\') *targ = '/';
 1077         targ++;
 1078     }
 1079     }
 1080 
 1081     return err;
 1082 }
 1083 
 1084 #endif
 1085 
 1086 /* Get gnuplot to print the dimensions of a PNG plot, in terms
 1087    of both pixels and data bounds (gnuplot >= 4.4.0).
 1088 */
 1089 
 1090 int write_plot_bounding_box_request (FILE *fp)
 1091 {
 1092 #ifdef WIN32
 1093     char buf[FILENAME_MAX];
 1094     int err;
 1095 
 1096     err = adjust_filename(buf, gretl_dotdir(), 1);
 1097     if (!err) {
 1098     fprintf(fp, "set print \"%sgretltmp.png.bounds\"\n", buf);
 1099     } else {
 1100     return err;
 1101     }
 1102 #else
 1103     fprintf(fp, "set print \"%sgretltmp.png.bounds\"\n", gretl_dotdir());
 1104 #endif
 1105 
 1106     fputs("print \"pixel_bounds: \", GPVAL_TERM_XMIN, GPVAL_TERM_XMAX, "
 1107       "GPVAL_TERM_YMIN, GPVAL_TERM_YMAX\n", fp);
 1108     fputs("print \"data_bounds: \", GPVAL_X_MIN, GPVAL_X_MAX, "
 1109       "GPVAL_Y_MIN, GPVAL_Y_MAX\n", fp);
 1110 
 1111     return 0;
 1112 }
 1113 
 1114 static int do_plot_bounding_box (void)
 1115 {
 1116     FILE *fp = gretl_fopen(gretl_plotfile(), "a");
 1117     int err = 0;
 1118 
 1119     if (fp != NULL) {
 1120     err = write_plot_bounding_box_request(fp);
 1121     fclose(fp);
 1122     } else {
 1123     err = E_FOPEN;
 1124     }
 1125 
 1126     return err;
 1127 }
 1128 
 1129 static void maybe_set_eps_pdf_dims (char *s, PlotType ptype, GptFlags flags)
 1130 {
 1131     double w = 0, h = 0;
 1132 
 1133     if (flags & GPT_LETTERBOX) {
 1134     /* for time series */
 1135     w = (5.0 * GP_LB_WIDTH) / GP_WIDTH;
 1136     h = (3.5 * GP_LB_HEIGHT) / GP_HEIGHT;
 1137     } else if (flags & GPT_XL) {
 1138     /* large */
 1139     w = (5.0 * GP_XL_WIDTH) / GP_WIDTH;
 1140     h = (3.5 * GP_XL_HEIGHT) / GP_HEIGHT;
 1141     } else if (flags & GPT_XXL) {
 1142     /* extra large */
 1143     w = h = (5.0 * GP_XXL_WIDTH) / GP_WIDTH;
 1144     } else if (flags & GPT_XW) {
 1145     /* extra wide */
 1146     w = (5.0 * GP_XW_WIDTH) / GP_WIDTH;
 1147     h = 3.5;
 1148     } else if (ptype == PLOT_ROOTS || ptype == PLOT_QQ) {
 1149     /* square plots */
 1150     w = h = 3.5;
 1151     }
 1152 
 1153     if (w > 0 && h > 0) {
 1154     char size_str[32];
 1155 
 1156     gretl_push_c_numeric_locale();
 1157     sprintf(size_str, " size %.2f,%.2f", w, h);
 1158     gretl_pop_c_numeric_locale();
 1159     strcat(s, size_str);
 1160     }
 1161 }
 1162 
 1163 static void append_gp_encoding (char *s)
 1164 {
 1165     strcat(s, "\nset encoding utf8");
 1166 }
 1167 
 1168 /* In @pdf_term_line and @eps_term_line: should "dashed" be
 1169    appended when "mono" is specified? Try experimenting?
 1170 */
 1171 
 1172 static char *gretl_pdf_term_line (char *term_line,
 1173                   PlotType ptype,
 1174                   GptFlags flags)
 1175 {
 1176     char font_string[128];
 1177     int ptsize;
 1178 
 1179     ptsize = (ptype == PLOT_MULTI_SCATTER)? 6 : 12;
 1180 
 1181     *font_string = '\0';
 1182     write_other_font_string(font_string, ptsize);
 1183 
 1184     sprintf(term_line, "set term pdfcairo noenhanced font \"%s\"",
 1185         font_string);
 1186 
 1187     maybe_set_eps_pdf_dims(term_line, ptype, flags);
 1188     append_gp_encoding(term_line);
 1189 
 1190     return term_line;
 1191 }
 1192 
 1193 static char *gretl_eps_term_line (char *term_line,
 1194                   PlotType ptype,
 1195                   GptFlags flags)
 1196 {
 1197     char font_string[128];
 1198     int ptsize;
 1199 
 1200     ptsize = (ptype == PLOT_MULTI_SCATTER)? 6 : 12;
 1201 
 1202     *font_string = '\0';
 1203     write_other_font_string(font_string, ptsize);
 1204 
 1205     sprintf(term_line, "set term epscairo noenhanced font \"%s\"",
 1206         font_string);
 1207 
 1208     maybe_set_eps_pdf_dims(term_line, ptype, flags);
 1209     append_gp_encoding(term_line);
 1210 
 1211     return term_line;
 1212 }
 1213 
 1214 static char *gretl_tex_term_line (char *term_line,
 1215                   PlotType ptype,
 1216                   GptFlags flags)
 1217 {
 1218     if (gnuplot_has_tikz()) {
 1219     strcpy(term_line, "set term tikz");
 1220     } else {
 1221     strcpy(term_line, "set term cairolatex");
 1222     }
 1223 
 1224     append_gp_encoding(term_line);
 1225 
 1226     return term_line;
 1227 }
 1228 
 1229 void plot_get_scaled_dimensions (int *width, int *height, double scale)
 1230 {
 1231     *width *= scale;
 1232     *height *= scale;
 1233 
 1234     /* PNG: round up to an even number of pixels if need be */
 1235     if (*width % 2) *width += 1;
 1236     if (*height % 2) *height += 1;
 1237 }
 1238 
 1239 static void write_png_size_string (char *s, PlotType ptype,
 1240                    GptFlags flags, double scale)
 1241 {
 1242     int w = GP_WIDTH, h = GP_HEIGHT;
 1243 
 1244     if (flags & GPT_LETTERBOX) {
 1245     /* time series plots */
 1246     w = GP_LB_WIDTH;
 1247     h = GP_LB_HEIGHT;
 1248     } else if (flags & GPT_XL) {
 1249     /* large */
 1250     w = GP_XL_WIDTH;
 1251     h = GP_XL_HEIGHT;
 1252     } else if (flags & GPT_XXL) {
 1253     /* extra large */
 1254     w = GP_XXL_WIDTH;
 1255     h = GP_XXL_HEIGHT;
 1256     } else if (flags & GPT_XW) {
 1257     /* extra wide */
 1258     w = GP_XW_WIDTH;
 1259     h = GP_HEIGHT;
 1260     } else if (ptype == PLOT_ROOTS || ptype == PLOT_QQ) {
 1261     /* square plots */
 1262     w = h = GP_SQ_SIZE;
 1263     }
 1264 
 1265     if (scale != 1.0) {
 1266     plot_get_scaled_dimensions(&w, &h, scale);
 1267     }
 1268 
 1269     *s = '\0';
 1270 
 1271     sprintf(s, " size %d,%d", w, h);
 1272 }
 1273 
 1274 static char *real_png_term_line (char *term_line,
 1275                  PlotType ptype,
 1276                  GptFlags flags,
 1277                  const char *specfont,
 1278                  double scale)
 1279 {
 1280     char ad_hoc_fontspec[128];
 1281     char font_string[140];
 1282     char size_string[16];
 1283 
 1284     *font_string = *size_string = *ad_hoc_fontspec = '\0';
 1285 
 1286     write_png_font_string(font_string, ad_hoc_fontspec,
 1287               ptype, specfont, scale);
 1288     write_png_size_string(size_string, ptype, flags, scale);
 1289 
 1290     sprintf(term_line, "set term pngcairo%s%s noenhanced",
 1291         font_string, size_string);
 1292 
 1293     append_gp_encoding(term_line);
 1294 
 1295     if (*ad_hoc_fontspec != '\0') {
 1296     strcat(term_line, "\n# fontspec: ");
 1297     strcat(term_line, ad_hoc_fontspec);
 1298     }
 1299 
 1300 #if GP_DEBUG
 1301     fprintf(stderr, "png term line:\n'%s'\n", term_line);
 1302 #endif
 1303 
 1304     return term_line;
 1305 }
 1306 
 1307 static char *gretl_png_term_line (char *term_line,
 1308                   PlotType ptype,
 1309                   GptFlags flags)
 1310 {
 1311     double s = default_png_scale;
 1312 
 1313     return real_png_term_line(term_line, ptype, flags, NULL, s);
 1314 }
 1315 
 1316 /**
 1317  * gretl_gnuplot_term_line:
 1318  * @ttype: code for the gnuplot "terminal" type.
 1319  * @ptype: indication of the sort of plot to be made, which
 1320  * may make a difference to the color palette chosen.
 1321  * @flags: plot option flags.
 1322  * @font: if non-NULL, try to respect a specified font.
 1323  *
 1324  * Constructs a suitable line for sending to gnuplot to invoke
 1325  * the specified "terminal".
 1326  *
 1327  * Returns: a static char * pointer.
 1328  */
 1329 
 1330 const char *gretl_gnuplot_term_line (TermType ttype,
 1331                      PlotType ptype,
 1332                      GptFlags flags,
 1333                      const char *font)
 1334 {
 1335     static char term_line[TERMLEN];
 1336 
 1337     *term_line = '\0';
 1338 
 1339     if (font != NULL && ad_hoc_font[0] == '\0') {
 1340     strcpy(ad_hoc_font, font);
 1341     }
 1342 
 1343     if (ttype == GP_TERM_PNG) {
 1344     double s = default_png_scale;
 1345 
 1346     real_png_term_line(term_line, ptype, flags, NULL, s);
 1347     } else if (ttype == GP_TERM_PDF) {
 1348     gretl_pdf_term_line(term_line, ptype, flags);
 1349     } else if (ttype == GP_TERM_EPS) {
 1350     gretl_eps_term_line(term_line, ptype, flags);
 1351     } else if (ttype == GP_TERM_EMF) {
 1352     gretl_emf_term_line(term_line, ptype, flags);
 1353     } else if (ttype == GP_TERM_SVG) {
 1354     strcpy(term_line, "set term svg noenhanced");
 1355     append_gp_encoding(term_line);
 1356     } else if (ttype == GP_TERM_FIG) {
 1357     strcpy(term_line, "set term fig");
 1358     append_gp_encoding(term_line);
 1359     } else if (ttype == GP_TERM_TEX) {
 1360     gretl_tex_term_line(term_line, ptype, flags);
 1361     }
 1362 
 1363     return term_line;
 1364 }
 1365 
 1366 const char *get_png_line_for_plotspec (const GPT_SPEC *spec)
 1367 {
 1368     static char term_line[TERMLEN];
 1369 
 1370     real_png_term_line(term_line, spec->code, spec->flags,
 1371                spec->fontstr, spec->scale);
 1372     return term_line;
 1373 }
 1374 
 1375 void gnuplot_png_set_default_scale (double s)
 1376 {
 1377     if (s >= 0.5 && s <= 2.0) {
 1378     default_png_scale = s;
 1379     }
 1380 }
 1381 
 1382 static void write_emf_font_string (char *fstr)
 1383 {
 1384     const char *src = NULL;
 1385     int stdsize = 16;
 1386     int adhoc = 0;
 1387 
 1388     if (ad_hoc_font[0] != '\0') {
 1389     src = ad_hoc_font;
 1390     adhoc = 1;
 1391     } else {
 1392     src = gretl_png_font();
 1393     }
 1394 
 1395     if (src != NULL) {
 1396     char fname[128];
 1397     int nf, fsize = 0;
 1398 
 1399     nf = split_graph_fontspec(src, fname, &fsize);
 1400     if (nf == 2) {
 1401         if (adhoc) {
 1402         /* go with what the user specified */
 1403         sprintf(fstr, "font \"%s,%d\"", fname, fsize);
 1404         } else {
 1405         /* adjust size to avoid tiny text? */
 1406         fsize = (fsize <= 8)? 12 : 16;
 1407         sprintf(fstr, "font \"%s,%d\"", fname, fsize);
 1408         }
 1409     } else if (nf == 1) {
 1410         sprintf(fstr, "font \"%s,%d\"", fname, stdsize);
 1411     }
 1412     ad_hoc_font[0] = '\0';
 1413     } else {
 1414     sprintf(fstr, "font \"sans,%d\"", stdsize);
 1415     }
 1416 }
 1417 
 1418 static char *gretl_emf_term_line (char *term_line,
 1419                   PlotType ptype,
 1420                   GptFlags flags)
 1421 {
 1422     char font_string[140];
 1423 
 1424     *font_string = '\0';
 1425     write_emf_font_string(font_string);
 1426 
 1427     if (flags & GPT_MONO) {
 1428     strcat(term_line, "set term emf dash noenhanced ");
 1429     } else {
 1430     strcat(term_line, "set term emf color noenhanced ");
 1431     }
 1432 
 1433     if (*font_string != '\0') {
 1434     strcat(term_line, font_string);
 1435     }
 1436 
 1437     append_gp_encoding(term_line);
 1438 
 1439     return term_line;
 1440 }
 1441 
 1442 /**
 1443  * plot_type_from_string:
 1444  * @str: initial comment line from plot file.
 1445  *
 1446  * Returns: the special plot code corresponding to the initial
 1447  * comment string in the plot file, or %PLOT_REGULAR if no special
 1448  * comment is recognized.
 1449  */
 1450 
 1451 PlotType plot_type_from_string (const char *str)
 1452 {
 1453     int i, len, ret = PLOT_REGULAR;
 1454 
 1455     for (i=1; ptinfo[i].pstr != NULL; i++) {
 1456     len = strlen(ptinfo[i].pstr);
 1457     if (!strncmp(str + 2, ptinfo[i].pstr, len)) {
 1458         ret = ptinfo[i].ptype;
 1459         break;
 1460     }
 1461     }
 1462 
 1463     return ret;
 1464 }
 1465 
 1466 int write_plot_type_string (PlotType ptype, GptFlags flags, FILE *fp)
 1467 {
 1468     int i, ret = 0;
 1469 
 1470     for (i=1; i<PLOT_TYPE_MAX; i++) {
 1471     if (ptype == ptinfo[i].ptype) {
 1472         if (flags & GPT_XL) {
 1473         fprintf(fp, "# %s (large)\n", ptinfo[i].pstr);
 1474         } else if (flags & GPT_XXL) {
 1475         fprintf(fp, "# %s (extra-large)\n", ptinfo[i].pstr);
 1476         } else if (flags & GPT_XW) {
 1477         fprintf(fp, "# %s (extra-wide)\n", ptinfo[i].pstr);
 1478         } else {
 1479         fprintf(fp, "# %s\n", ptinfo[i].pstr);
 1480         }
 1481         ret = 1;
 1482         break;
 1483     }
 1484     }
 1485 
 1486     if (ret == 0 && (flags & GPT_XW)) {
 1487     fputs("# extra-wide\n", fp);
 1488     }
 1489 
 1490     if (get_local_decpoint() == ',') {
 1491     /* is this right? */
 1492     fputs("set decimalsign ','\n", fp);
 1493     }
 1494 
 1495     return ret;
 1496 }
 1497 
 1498 static void print_term_string (int ttype, PlotType ptype,
 1499                    GptFlags flags, FILE *fp)
 1500 {
 1501     char term_line[TERMLEN];
 1502 
 1503     *term_line = '\0';
 1504 
 1505     if (ttype == GP_TERM_EPS) {
 1506     gretl_eps_term_line(term_line, ptype, flags);
 1507     } else if (ttype == GP_TERM_PDF) {
 1508     gretl_pdf_term_line(term_line, ptype, flags);
 1509     } else if (ttype == GP_TERM_PNG) {
 1510     gretl_png_term_line(term_line, ptype, flags);
 1511     } else if (ttype == GP_TERM_EMF) {
 1512     gretl_emf_term_line(term_line, ptype, flags);
 1513     } else if (ttype == GP_TERM_FIG) {
 1514     strcpy(term_line, "set term fig\nset encoding utf8");
 1515     } else if (ttype == GP_TERM_SVG) {
 1516     strcpy(term_line, "set term svg noenhanced\nset encoding utf8");
 1517     } else if (ttype == GP_TERM_TEX) {
 1518     gretl_tex_term_line(term_line, ptype, flags);
 1519     }
 1520 
 1521     if (*term_line != '\0') {
 1522     fprintf(fp, "%s\n", term_line);
 1523     if (flags & GPT_MONO) {
 1524         fputs("set mono\n", fp);
 1525     } else {
 1526         write_plot_line_styles(ptype, fp);
 1527     }
 1528     }
 1529 }
 1530 
 1531 static int gretl_plot_count;
 1532 static int this_term_type;
 1533 
 1534 /* recorder for filename given via --output=foo */
 1535 static char gnuplot_outname[FILENAME_MAX];
 1536 
 1537 static int set_term_type_from_fname (const char *fname)
 1538 {
 1539     if (has_suffix(fname, ".eps")) {
 1540     this_term_type = GP_TERM_EPS;
 1541     } else if (has_suffix(fname, ".ps")) {
 1542     this_term_type = GP_TERM_EPS;
 1543     } else if (has_suffix(fname, ".pdf")) {
 1544     this_term_type = GP_TERM_PDF;
 1545     } else if (has_suffix(fname, ".png")) {
 1546     this_term_type = GP_TERM_PNG;
 1547     } else if (has_suffix(fname, ".fig")) {
 1548     this_term_type = GP_TERM_FIG;
 1549     } else if (has_suffix(fname, ".emf")) {
 1550     this_term_type = GP_TERM_EMF;
 1551     } else if (has_suffix(fname, ".svg")) {
 1552     this_term_type = GP_TERM_SVG;
 1553     } else if (has_suffix(fname, ".tex")) {
 1554     this_term_type = GP_TERM_TEX;
 1555     }
 1556 
 1557     return this_term_type;
 1558 }
 1559 
 1560 int specified_gp_output_format (void)
 1561 {
 1562     return this_term_type;
 1563 }
 1564 
 1565 void reset_plot_count (void)
 1566 {
 1567     gretl_plot_count = 0;
 1568 }
 1569 
 1570 /* if @path is non-NULL we use it, otherwise we make a path
 1571    using @dotdir and "gretltmp.png"
 1572 */
 1573 
 1574 int write_plot_output_line (const char *path, FILE *fp)
 1575 {
 1576 #ifdef WIN32
 1577     char buf[FILENAME_MAX];
 1578     int err = 0;
 1579 
 1580     if (path == NULL) {
 1581     err = adjust_filename(buf, gretl_dotdir(), 1);
 1582     if (!err) {
 1583         fprintf(fp, "set output \"%sgretltmp.png\"\n", buf);
 1584     }
 1585     } else {
 1586     err = adjust_filename(buf, path, 1);
 1587     if (!err) {
 1588         fprintf(fp, "set output \"%s\"\n", buf);
 1589     }
 1590     }
 1591 
 1592     return err;
 1593 #else
 1594     if (path == NULL) {
 1595     fprintf(fp, "set output \"%sgretltmp.png\"\n", gretl_dotdir());
 1596     } else {
 1597     fprintf(fp, "set output \"%s\"\n", path);
 1598     }
 1599 
 1600     return 0;
 1601 #endif
 1602 }
 1603 
 1604 static FILE *gp_set_up_batch (char *fname,
 1605                   PlotType ptype,
 1606                   GptFlags flags,
 1607                   const char *optname,
 1608                   int *err)
 1609 {
 1610     int fmt = GP_TERM_NONE;
 1611     FILE *fp = NULL;
 1612 
 1613     if (optname != NULL) {
 1614     /* user gave --output=<filename> */
 1615     fmt = set_term_type_from_fname(optname);
 1616     if (fmt) {
 1617         /* input needs processing */
 1618         strcpy(gnuplot_outname, optname);
 1619         gretl_maybe_prepend_dir(gnuplot_outname);
 1620         sprintf(fname, "%sgpttmp.XXXXXX", gretl_dotdir());
 1621         fp = gretl_mktemp(fname, "w");
 1622     } else {
 1623         /* just passing gnuplot commands through */
 1624         this_term_type = GP_TERM_PLT;
 1625         strcpy(fname, optname);
 1626         gretl_maybe_prepend_dir(fname);
 1627         fp = gretl_fopen(fname, "w");
 1628     }
 1629     } else {
 1630     /* auto-constructed gnuplot commands filename */
 1631     this_term_type = GP_TERM_PLT;
 1632     sprintf(fname, "gpttmp%02d.plt", ++gretl_plot_count);
 1633     gretl_maybe_prepend_dir(fname);
 1634     fp = gretl_fopen(fname, "w");
 1635     }
 1636 
 1637     if (fp == NULL) {
 1638     *err = E_FOPEN;
 1639     } else {
 1640     set_gretl_plotfile(fname);
 1641     if (*gnuplot_outname != '\0') {
 1642         /* write terminal/style/output lines */
 1643         print_term_string(fmt, ptype, flags, fp);
 1644         write_plot_output_line(gnuplot_outname, fp);
 1645     } else {
 1646         /* just write style lines */
 1647         write_plot_line_styles(ptype, fp);
 1648     }
 1649     if (get_local_decpoint() == ',') {
 1650         fputs("set decimalsign ','\n", fp);
 1651     }
 1652     }
 1653 
 1654     return fp;
 1655 }
 1656 
 1657 /* Set-up for an "interactive" plot: we open a file in the user's
 1658    dotdir into which gnuplot commands will be written.  If we're
 1659    running the GUI program this command file will eventually be used
 1660    to create a PNG file for display in a gretl window; otherwise
 1661    (gretlcli) the commands will eventually be sent to gnuplot for
 1662    "direct" display (e.g. using the x11 or windows terminal).
 1663 
 1664    In this function we just open the file for writing; if in GUI
 1665    mode insert a suitable PNG terminal line and output spec line;
 1666    and write some header-type material including our line style
 1667    specifications.
 1668 */
 1669 
 1670 static FILE *gp_set_up_interactive (char *fname, PlotType ptype,
 1671                     GptFlags flags, int *err)
 1672 {
 1673     int gui = gretl_in_gui_mode();
 1674     FILE *fp = NULL;
 1675 
 1676     if (gui) {
 1677     /* the filename should be unique */
 1678     sprintf(fname, "%sgpttmp.XXXXXX", gretl_dotdir());
 1679     fp = gretl_mktemp(fname, "w");
 1680     } else {
 1681     /* gretlcli: no need for uniqueness */
 1682     sprintf(fname, "%sgpttmp.plt", gretl_dotdir());
 1683     fp = gretl_fopen(fname, "w");
 1684     }
 1685 
 1686     if (fp == NULL) {
 1687     *err = E_FOPEN;
 1688     } else {
 1689     set_gretl_plotfile(fname);
 1690     if (gui) {
 1691         /* set up for PNG output */
 1692         fprintf(fp, "%s\n", gretl_gnuplot_term_line(GP_TERM_PNG, ptype,
 1693                             flags, NULL));
 1694         if (default_png_scale != 1.0) {
 1695         gretl_push_c_numeric_locale();
 1696         fprintf(fp, "# scale = %.1f\n", default_png_scale);
 1697         fputs("# auto linewidth\n", fp);
 1698         gretl_pop_c_numeric_locale();
 1699         }
 1700         write_plot_output_line(NULL, fp);
 1701     } else {
 1702         fputs("set termoption noenhanced\n", fp);
 1703     }
 1704     write_plot_type_string(ptype, flags, fp);
 1705     write_plot_line_styles(ptype, fp);
 1706     }
 1707 
 1708     return fp;
 1709 }
 1710 
 1711 #ifndef WIN32
 1712 
 1713 static int gnuplot_too_old (void)
 1714 {
 1715     static double gpv;
 1716     int ret = 0;
 1717 
 1718     if (gpv == 0.0) {
 1719     gpv = gnuplot_version();
 1720     }
 1721 
 1722     if (gpv == 0.0) {
 1723     /* couldn't get version number? */
 1724     gretl_errmsg_set("Gnuplot is broken or too old: must be >= version 5.0");
 1725     ret = 1;
 1726     } else if (gpv < 5.0) {
 1727     gretl_errmsg_sprintf("Gnuplot %g is too old: must be >= version 5.0", gpv);
 1728     ret = 1;
 1729     }
 1730 
 1731     return ret;
 1732 }
 1733 
 1734 #endif
 1735 
 1736 static int got_display_option (const char *s)
 1737 {
 1738     return s != NULL && !strcmp(s, "display");
 1739 }
 1740 
 1741 static int got_none_option (const char *s)
 1742 {
 1743     return s != NULL && !strcmp(s, "none");
 1744 }
 1745 
 1746 static const char *plot_output_option (PlotType p, int *pci)
 1747 {
 1748     int ci = plot_ci;
 1749     const char *s;
 1750 
 1751     /* set a more specific command index based on
 1752        the plot type, if applicable */
 1753 
 1754     if (p == PLOT_MULTI_SCATTER) {
 1755     ci = SCATTERS;
 1756     } else if (p == PLOT_BOXPLOTS) {
 1757     ci = BXPLOT;
 1758     } else if (p == PLOT_FORECAST) {
 1759     ci = FCAST;
 1760     } else if (p == PLOT_CORRELOGRAM) {
 1761     ci = CORRGM;
 1762     } else if (p == PLOT_XCORRELOGRAM) {
 1763     ci = XCORRGM;
 1764     } else if (p == PLOT_PERIODOGRAM) {
 1765     ci = PERGM;
 1766     } else if (p == PLOT_HURST) {
 1767     ci = HURST;
 1768     } else if (p == PLOT_QQ) {
 1769     ci = QQPLOT;
 1770     } else if (p == PLOT_RANGE_MEAN) {
 1771     ci = RMPLOT;
 1772     } else if (p == PLOT_LEVERAGE) {
 1773     ci = LEVERAGE;
 1774     } else if (p == PLOT_FREQ_SIMPLE ||
 1775            p == PLOT_FREQ_NORMAL ||
 1776            p == PLOT_FREQ_GAMMA) {
 1777     ci = FREQ;
 1778     } else if (p == PLOT_HEATMAP) {
 1779     ci = CORR;
 1780     }
 1781 
 1782     s = get_optval_string(ci, OPT_U);
 1783     if (s != NULL && *s == '\0') {
 1784     s = NULL;
 1785     }
 1786 
 1787     if (pci != NULL) {
 1788     *pci = ci;
 1789     }
 1790 
 1791     return s;
 1792 }
 1793 
 1794 /**
 1795  * open_plot_input_file:
 1796  * @ptype: indication of the sort of plot to be made.
 1797  * @flags: may inflect some characteristics of plot.
 1798  * @err: location to receive error code.
 1799  *
 1800  * Opens a file into which gnuplot commands will be written.
 1801  * Depending on the prospective use of the stream, some
 1802  * header-type material may be written into it (the primary
 1803  * case being when we're going to produce PNG output
 1804  * for display in the gretl GUI). The prospective use is
 1805  * figured out based on the program state, @ptype and
 1806  * @flags.
 1807  *
 1808  * Returns: writable stream on success, %NULL on failure.
 1809  */
 1810 
 1811 FILE *open_plot_input_file (PlotType ptype, GptFlags flags, int *err)
 1812 {
 1813     char fname[FILENAME_MAX] = {0};
 1814     const char *optname = NULL;
 1815     int ci, interactive = 0;
 1816     FILE *fp = NULL;
 1817 
 1818     /* ensure we have 'gnuplot_path' in place (file-scope static var) */
 1819     if (*gnuplot_path == '\0') {
 1820     strcpy(gnuplot_path, gretl_gnuplot_path());
 1821     }
 1822 
 1823 #ifndef WIN32
 1824     if (gnuplot_too_old()) {
 1825     *err = E_EXTERNAL;
 1826     return NULL;
 1827     }
 1828 #endif
 1829 
 1830     /* initialize */
 1831     this_term_type = GP_TERM_NONE;
 1832     *gnuplot_outname = '\0';
 1833 
 1834     /* check for --output=whatever option */
 1835     optname = plot_output_option(ptype, &ci);
 1836 
 1837     if (got_display_option(optname)) {
 1838     /* --output=display specified */
 1839     interactive = 1;
 1840     } else if (optname != NULL) {
 1841     /* --output=filename specified */
 1842     interactive = 0;
 1843     } else if (flags & GPT_ICON) {
 1844     interactive = 1;
 1845     } else {
 1846     /* default */
 1847     interactive = !gretl_in_batch_mode();
 1848     }
 1849 
 1850 #if GP_DEBUG
 1851     fprintf(stderr, "optname = '%s', interactive = %d\n",
 1852         optname, interactive);
 1853 #endif
 1854 
 1855     if (interactive) {
 1856     fp = gp_set_up_interactive(fname, ptype, flags, err);
 1857     } else {
 1858     fp = gp_set_up_batch(fname, ptype, flags, optname, err);
 1859     }
 1860 
 1861 #if GP_DEBUG
 1862     fprintf(stderr, "open_gp_stream_full: '%s'\n", gretl_plotfile());
 1863 #endif
 1864 
 1865     return fp;
 1866 }
 1867 
 1868 /* For plot-types that are generated by commands that also produce
 1869    textual output: based on the program state and command option,
 1870    figure out if a plot is actually wanted or not. In interactive mode
 1871    the answer will be Yes unless --plot=none has been given; in batch
 1872    mode the answer will be No unless --plot=display or --plot=fname
 1873    has been given.
 1874 */
 1875 
 1876 int gnuplot_graph_wanted (PlotType ptype, gretlopt opt)
 1877 {
 1878     const char *optname = NULL;
 1879     int ret = 0;
 1880 
 1881     if (opt & OPT_U) {
 1882     /* check for --plot=whatever option */
 1883     optname = plot_output_option(ptype, NULL);
 1884     }
 1885 
 1886     if (got_none_option(optname)) {
 1887     /* --plot=none specified */
 1888     ret = 0;
 1889     } else if (optname != NULL) {
 1890     /* --plot=display or --plot=fname specified */
 1891     ret = 1;
 1892     } else {
 1893     /* defaults */
 1894     ret = !gretl_in_batch_mode();
 1895     }
 1896 
 1897     return ret;
 1898 }
 1899 
 1900 /**
 1901  * gnuplot_cleanup:
 1902  *
 1903  * Removes any temporary gnuplot input file written in
 1904  * the user's dot directory.
 1905  */
 1906 
 1907 void gnuplot_cleanup (void)
 1908 {
 1909     const char *p, *fname = gretl_plotfile();
 1910 
 1911     p = strstr(fname, "gpttmp");
 1912 
 1913     if (p != NULL) {
 1914     int pnum;
 1915 
 1916     if (sscanf(p, "gpttmp%d.plt", &pnum) == 0) {
 1917         gretl_remove(fname);
 1918     }
 1919     }
 1920 }
 1921 
 1922 static int graph_file_written;
 1923 
 1924 int graph_written_to_file (void)
 1925 {
 1926     return graph_file_written;
 1927 }
 1928 
 1929 static void remove_old_png (char *buf)
 1930 {
 1931     sprintf(buf, "%sgretltmp.png", gretl_dotdir());
 1932     remove(buf);
 1933 }
 1934 
 1935 /*
 1936  * gnuplot_make_graph:
 1937  *
 1938  * Executes gnuplot to produce a graph: in the gretl GUI
 1939  * in interactive mode this will be a PNG file. In the
 1940  * CLI program in interactive mode there will be a direct
 1941  * call to gnuplot to display the graph. In batch mode
 1942  * the type of file written depends on the options selected
 1943  * by the user.
 1944  *
 1945  * Returns: 0 on success, non-zero on error.
 1946  */
 1947 
 1948 static int gnuplot_make_graph (void)
 1949 {
 1950     char buf[MAXLEN];
 1951     const char *fname = gretl_plotfile();
 1952     int gui = gretl_in_gui_mode();
 1953     int fmt, err = 0;
 1954 
 1955     graph_file_written = 0;
 1956     fmt = specified_gp_output_format();
 1957 
 1958     if (fmt == GP_TERM_PLT) {
 1959     /* no-op: just the gnuplot commands are wanted */
 1960     graph_file_written = 1;
 1961     return 0;
 1962     } else if (fmt == GP_TERM_NONE && gui) {
 1963     do_plot_bounding_box();
 1964     /* ensure we don't get stale output */
 1965     remove_old_png(buf);
 1966     }
 1967 
 1968 #ifdef WIN32
 1969     if (gui || fmt) {
 1970     sprintf(buf, "\"%s\" \"%s\"", gretl_gnuplot_path(), fname);
 1971     } else {
 1972     /* gretlcli, interactive */
 1973     sprintf(buf, "\"%s\" -persist \"%s\"", gretl_gnuplot_path(), fname);
 1974     }
 1975     err = gretl_spawn(buf);
 1976 #else /* !WIN32 */
 1977     if (gui || fmt) {
 1978     sprintf(buf, "%s \"%s\"", gretl_gnuplot_path(), fname);
 1979     } else {
 1980     /* gretlcli, interactive */
 1981     sprintf(buf, "%s -persist \"%s\"", gretl_gnuplot_path(), fname);
 1982     }
 1983     err = gretl_spawn(buf);
 1984 #endif
 1985 
 1986 #if GP_DEBUG
 1987     fprintf(stderr, "gnuplot_make_graph:\n"
 1988         " command='%s', fmt = %d, err = %d\n", buf, fmt, err);
 1989 #endif
 1990 
 1991     if (fmt) {
 1992     /* got a user-specified output format */
 1993     if (err) {
 1994         /* leave the bad file for diagnostic purposes */
 1995         fprintf(stderr, "err = %d: bad file is '%s'\n", err, fname);
 1996     } else {
 1997         /* remove the temporary input file */
 1998         remove(fname);
 1999         set_gretl_plotfile(gnuplot_outname);
 2000         graph_file_written = 1;
 2001     }
 2002     }
 2003 
 2004     return err;
 2005 }
 2006 
 2007 /**
 2008  * finalize_plot_input_file:
 2009  * @fp: stream to which gnuplot commands have been written.
 2010  *
 2011  * Closes @fp and attempts to "make" the graph that it specifies.
 2012  *
 2013  * Returns: 0 on success, non-zero code on error.
 2014  */
 2015 
 2016 int finalize_plot_input_file (FILE *fp)
 2017 {
 2018     int err;
 2019 
 2020     if (fp != NULL) {
 2021     fclose(fp);
 2022     err = gnuplot_make_graph();
 2023     if (!err) {
 2024         /* for the benefit of gretl_cmd_exec() in interact.c,
 2025            indicate that we actually produced a plot */
 2026         set_plot_produced();
 2027     }
 2028     } else {
 2029     err = 1;
 2030     }
 2031 
 2032     return err;
 2033 }
 2034 
 2035 /**
 2036  * finalize_3d_plot_input_file:
 2037  * @fp: stream to which gnuplot commands have been written.
 2038  *
 2039  * Closes @fp and alerts libgretl to the fact that an interactive
 2040  * 3-D plot is wanted.
 2041  *
 2042  * Returns: 0 on success, non-zero code on error.
 2043  */
 2044 
 2045 int finalize_3d_plot_input_file (FILE *fp)
 2046 {
 2047     int err = 0;
 2048 
 2049     if (fp != NULL) {
 2050     fclose(fp);
 2051     graph_file_written = 1;
 2052     } else {
 2053     err = 1;
 2054     }
 2055 
 2056     return err;
 2057 }
 2058 
 2059 enum {
 2060     GTITLE_VLS,
 2061     GTITLE_RESID,
 2062     GTITLE_AF,
 2063     GTITLE_AFV
 2064 } graph_titles;
 2065 
 2066 static void make_gtitle (gnuplot_info *gi, int code,
 2067              const char *s1, const char *s2,
 2068              FILE *fp)
 2069 {
 2070     char depvar[VNAMELEN];
 2071     char title[128];
 2072 
 2073     switch (code) {
 2074     case GTITLE_VLS:
 2075     if (gi->fit == PLOT_FIT_OLS) {
 2076         sprintf(title, _("%s versus %s (with least squares fit)"),
 2077             s1, s2);
 2078     } else if (gi->fit == PLOT_FIT_INVERSE) {
 2079         sprintf(title, _("%s versus %s (with inverse fit)"),
 2080             s1, s2);
 2081     } else if (gi->fit == PLOT_FIT_QUADRATIC) {
 2082         sprintf(title, _("%s versus %s (with quadratic fit)"),
 2083             s1, s2);
 2084     } else if (gi->fit == PLOT_FIT_CUBIC) {
 2085         sprintf(title, _("%s versus %s (with cubic fit)"),
 2086             s1, s2);
 2087     }
 2088     break;
 2089     case GTITLE_RESID:
 2090     if (strncmp(s1, "residual for ", 13) == 0 &&
 2091         gretl_scan_varname(s1 + 13, depvar) == 1) {
 2092         sprintf(title, _("Regression residuals (= observed - fitted %s)"),
 2093             depvar);
 2094     }
 2095     break;
 2096     case GTITLE_AF:
 2097     sprintf(title, _("Actual and fitted %s"), s1);
 2098     break;
 2099     case GTITLE_AFV:
 2100     if (s2 == NULL || (gi->flags & GPT_TS)) {
 2101         sprintf(title, _("Actual and fitted %s"), s1);
 2102     } else {
 2103         sprintf(title, _("Actual and fitted %s versus %s"), s1, s2);
 2104     }
 2105     break;
 2106     default:
 2107     *title = '\0';
 2108     break;
 2109     }
 2110 
 2111     if (*title != '\0') {
 2112     fprintf(fp, "set title \"%s\"\n", title);
 2113     }
 2114 }
 2115 
 2116 static void print_axis_label (char axis, const char *s, FILE *fp)
 2117 {
 2118     if (strchr(s, '\'')) {
 2119     fprintf(fp, "set %clabel \"%s\"\n", axis, s);
 2120     } else {
 2121     fprintf(fp, "set %clabel '%s'\n", axis, s);
 2122     }
 2123 }
 2124 
 2125 static int literal_line_out (const char *s, int len,
 2126                  FILE *fp)
 2127 {
 2128     char *q, *p = malloc(len + 1);
 2129     int n, warn = 0;
 2130 
 2131     if (p != NULL) {
 2132     *p = '\0';
 2133     strncat(p, s, len);
 2134     q = p + strspn(p, " \t");
 2135     n = strlen(q);
 2136     if (n > 0) {
 2137         if (!strncmp(q, "set term", 8)) {
 2138         warn = 1;
 2139         } else {
 2140         fputs(q, fp);
 2141         if (q[n-1] != '\n') {
 2142             fputc('\n', fp);
 2143         }
 2144         }
 2145     }
 2146     free(p);
 2147     }
 2148 
 2149     return warn;
 2150 }
 2151 
 2152 static int gnuplot_literal_from_string (const char *s,
 2153                     FILE *fp)
 2154 {
 2155     const char *p;
 2156     int wi, warn = 0;
 2157 
 2158     s += strspn(s, " \t{");
 2159     p = s;
 2160 
 2161     fputs("# start literal lines\n", fp);
 2162 
 2163     while (*s && *s != '}') {
 2164     if (*s == ';') {
 2165         wi = literal_line_out(p, s - p, fp);
 2166         if (wi && !warn) {
 2167         warn = 1;
 2168         }
 2169         p = s + 1;
 2170     }
 2171     s++;
 2172     }
 2173 
 2174     fputs("# end literal lines\n", fp);
 2175 
 2176     return warn;
 2177 }
 2178 
 2179 /* Alternative (undocumented!) means of supplying "literal"
 2180    lines to the "gnuplot" command (as opposed to the time-
 2181    honored "{...}" mechanism). Syntax is
 2182 
 2183    gnuplot <args> --tweaks=<name-of-array-of-strings>
 2184 
 2185    FIXME: document this or get rid of it! Although this
 2186    mechanism has something going for it, maybe it's too
 2187    late to substitute it for the old method.
 2188 */
 2189 
 2190 static char **literal_strings_from_opt (int ci, int *ns,
 2191                     int *real_ns)
 2192 {
 2193     const char *aname = get_optval_string(ci, OPT_K);
 2194     char **S = NULL;
 2195 
 2196     *ns = *real_ns = 0;
 2197 
 2198     if (aname != NULL) {
 2199     GretlType type;
 2200     gretl_array *A;
 2201     int i;
 2202 
 2203     A = user_var_get_value_and_type(aname, &type);
 2204 
 2205     if (A != NULL && type == GRETL_TYPE_ARRAY) {
 2206         S = gretl_array_get_strings(A, ns);
 2207         if (*ns > 0) {
 2208         for (i=0; i<*ns; i++) {
 2209             if (S[i] != NULL && S[i][0] != '\0') {
 2210             *real_ns += 1;
 2211             }
 2212         }
 2213         }
 2214     }
 2215     }
 2216 
 2217     return S;
 2218 }
 2219 
 2220 static int gnuplot_literal_from_opt (int ci, FILE *fp)
 2221 {
 2222     char *s, **S;
 2223     int ns, real_ns;
 2224     int warn = 0;
 2225 
 2226     S = literal_strings_from_opt(ci, &ns, &real_ns);
 2227 
 2228     if (real_ns > 0) {
 2229     int i, n;
 2230 
 2231     fputs("# start literal lines\n", fp);
 2232 
 2233     for (i=0; i<ns; i++) {
 2234         if (S[i] != NULL) {
 2235         s = S[i];
 2236         s += strspn(s, " \t");
 2237         n = strlen(s);
 2238         if (n > 0) {
 2239             if (!strncmp(s, "set term", 8)) {
 2240             warn = 1;
 2241             } else {
 2242             fputs(s, fp);
 2243             if (s[n-1] != '\n') {
 2244                 fputc('\n', fp);
 2245             }
 2246             }
 2247         }
 2248         }
 2249     }
 2250 
 2251     fputs("# end literal lines\n", fp);
 2252     }
 2253 
 2254     return warn;
 2255 }
 2256 
 2257 int print_gnuplot_literal_lines (const char *s, int ci,
 2258                  gretlopt opt, FILE *fp)
 2259 {
 2260     if (s != NULL && *s != '\0') {
 2261     gnuplot_literal_from_string(s, fp);
 2262     } else if (opt & OPT_K) {
 2263     gnuplot_literal_from_opt(ci, fp);
 2264     }
 2265 
 2266     return 0;
 2267 }
 2268 
 2269 static void print_extra_literal_lines (char **S,
 2270                        int ns,
 2271                        FILE *fp)
 2272 {
 2273     int i, n;
 2274 
 2275     for (i=0; i<ns; i++) {
 2276     if (S[i] != NULL) {
 2277         n = strlen(S[i]);
 2278         if (n > 0) {
 2279         fputs(S[i], fp);
 2280         if (S[i][n-1] != '\n') {
 2281             fputc('\n', fp);
 2282         }
 2283         }
 2284     }
 2285     }
 2286 }
 2287 
 2288 static int loess_plot (gnuplot_info *gi, const char *literal,
 2289                const DATASET *dset)
 2290 {
 2291     gretl_matrix *y = NULL;
 2292     gretl_matrix *x = NULL;
 2293     gretl_matrix *yh = NULL;
 2294     int xno, yno = gi->list[1];
 2295     const double *yvar = dset->Z[yno];
 2296     const double *xvar;
 2297     FILE *fp = NULL;
 2298     char title[96];
 2299     int t, T, d = 1;
 2300     double q = 0.5;
 2301     int err = 0;
 2302 
 2303     if (gi->x != NULL) {
 2304     xno = 0;
 2305     xvar = gi->x;
 2306     } else {
 2307     xno = gi->list[2];
 2308     xvar = dset->Z[xno];
 2309     }
 2310 
 2311     err = graph_list_adjust_sample(gi->list, gi, dset, 2);
 2312     if (!err && gi->list[0] > 2) {
 2313     err = E_DATA;
 2314     }
 2315     if (err) {
 2316     return err;
 2317     }
 2318 
 2319     fp = open_plot_input_file(PLOT_REGULAR, gi->flags, &err);
 2320     if (err) {
 2321     return E_FOPEN;
 2322     }
 2323 
 2324     err = gretl_plotfit_matrices(yvar, xvar, PLOT_FIT_LOESS,
 2325                  gi->t1, gi->t2, &y, &x);
 2326 
 2327     if (!err) {
 2328     err = sort_pairs_by_x(x, y, NULL, NULL); /* markers! */
 2329     }
 2330 
 2331     if (!err) {
 2332     yh = loess_fit(x, y, d, q, OPT_R, &err);
 2333     }
 2334 
 2335     if (err) {
 2336     fclose(fp);
 2337     goto bailout;
 2338     }
 2339 
 2340     if (xno > 0) {
 2341     const char *s1 = series_get_graph_name(dset, yno);
 2342     const char *s2 = series_get_graph_name(dset, xno);
 2343 
 2344     sprintf(title, _("%s versus %s (with loess fit)"), s1, s2);
 2345     print_keypos_string(GP_KEY_LEFT_TOP, fp);
 2346     fprintf(fp, "set title \"%s\"\n", title);
 2347     print_axis_label('y', s1, fp);
 2348     print_axis_label('x', s2, fp);
 2349     } else {
 2350     print_keypos_string(GP_KEY_LEFT_TOP, fp);
 2351     print_axis_label('y', series_get_graph_name(dset, yno), fp);
 2352     }
 2353 
 2354     print_auto_fit_string(PLOT_FIT_LOESS, fp);
 2355 
 2356     print_gnuplot_literal_lines(literal, GNUPLOT, OPT_NONE, fp);
 2357 
 2358     fputs("plot \\\n", fp);
 2359     fputs(" '-' using 1:2 notitle w points, \\\n", fp);
 2360     sprintf(title, _("loess fit, d = %d, q = %g"), d, q);
 2361     fprintf(fp, " '-' using 1:2 title \"%s\" w lines\n", title);
 2362 
 2363     T = gretl_vector_get_length(yh);
 2364 
 2365     gretl_push_c_numeric_locale();
 2366 
 2367     for (t=0; t<T; t++) {
 2368     fprintf(fp, "%.10g %.10g\n", x->val[t], y->val[t]);
 2369     }
 2370     fputs("e\n", fp);
 2371 
 2372     for (t=0; t<T; t++) {
 2373     fprintf(fp, "%.10g %.10g\n", x->val[t], yh->val[t]);
 2374     }
 2375     fputs("e\n", fp);
 2376 
 2377     gretl_pop_c_numeric_locale();
 2378 
 2379     err = finalize_plot_input_file(fp);
 2380 
 2381  bailout:
 2382 
 2383     gretl_matrix_free(y);
 2384     gretl_matrix_free(x);
 2385     gretl_matrix_free(yh);
 2386     clear_gpinfo(gi);
 2387 
 2388     return err;
 2389 }
 2390 
 2391 static int get_fitted_line (gnuplot_info *gi,
 2392                 const DATASET *dset,
 2393                 char *targ)
 2394 {
 2395     gretl_matrix *y = NULL;
 2396     gretl_matrix *X = NULL;
 2397     gretl_matrix *b = NULL;
 2398     gretl_matrix *V = NULL;
 2399     const double *yvar, *xvar = NULL;
 2400     double x0, s2 = 0, *ps2 = NULL;
 2401     int allow_err = 0;
 2402     int err = 0;
 2403 
 2404     if (gi->x != NULL && (dset->pd == 1 || dset->pd == 4 || dset->pd == 12)) {
 2405     /* starting value of time index */
 2406     x0 = gi->x[gi->t1];
 2407     } else {
 2408     xvar = dset->Z[gi->list[2]];
 2409     x0 = NADBL;
 2410     }
 2411 
 2412     yvar = dset->Z[gi->list[1]];
 2413 
 2414     if (gi->fit == PLOT_FIT_NONE) {
 2415     /* Doing first-time automatic OLS: we want to check for
 2416        statistical significance of the slope coefficient
 2417        to see if it's worth drawing the fitted line, so
 2418        we have to allocate the variance matrix; otherwise
 2419        we only need the coefficients.
 2420     */
 2421     V = gretl_matrix_alloc(2, 2);
 2422     if (V == NULL) {
 2423         return E_ALLOC;
 2424     }
 2425     ps2 = &s2;
 2426     /* if the fit attempt is automatic we'll allow it to
 2427        fail without aborting the plot */
 2428     allow_err = 1;
 2429     }
 2430 
 2431     err = gretl_plotfit_matrices(yvar, xvar, gi->fit,
 2432                  dset->t1, dset->t2,
 2433                  &y, &X);
 2434 
 2435     if (!err) {
 2436     int  k = 2;
 2437 
 2438     if (gi->fit == PLOT_FIT_CUBIC) {
 2439         k = 4;
 2440     } else if (gi->fit == PLOT_FIT_QUADRATIC) {
 2441         k = 3;
 2442     }
 2443     b = gretl_column_vector_alloc(k);
 2444     if (b == NULL) {
 2445         err = E_ALLOC;
 2446     } else if (gi->fit == PLOT_FIT_LOGLIN) {
 2447         ps2 = &s2;
 2448     }
 2449     }
 2450 
 2451     if (!err) {
 2452     err = gretl_matrix_ols(y, X, b, V, NULL, ps2);
 2453     }
 2454 
 2455     if (!err && gi->fit == PLOT_FIT_NONE) {
 2456     /* the "automatic" case */
 2457     double pv, v = gretl_matrix_get(V, 1, 1);
 2458     int T = gretl_vector_get_length(y);
 2459 
 2460     pv = student_pvalue_2(T - 2, b->val[1] / sqrt(v));
 2461     /* show the line if the two-tailed p-value for the slope coeff
 2462        is less than 0.1, otherwise discard it */
 2463     if (pv < 0.10) {
 2464         gi->fit = PLOT_FIT_OLS;
 2465     }
 2466     }
 2467 
 2468     if (!err && gi->fit != PLOT_FIT_NONE) {
 2469     char title[MAXTITLE], formula[GP_MAXFORMULA];
 2470     double pd = dset->pd;
 2471 
 2472     if (gi->fit == PLOT_FIT_LOGLIN) {
 2473         b->val[0] += s2 / 2;
 2474     }
 2475 
 2476     set_plotfit_line(title, formula, gi->fit, b->val, x0, pd);
 2477     sprintf(targ, "%s title \"%s\" w lines\n", formula, title);
 2478     gi->flags |= GPT_AUTO_FIT;
 2479     }
 2480 
 2481     gretl_matrix_free(y);
 2482     gretl_matrix_free(X);
 2483     gretl_matrix_free(b);
 2484     gretl_matrix_free(V);
 2485 
 2486     if (err && allow_err) {
 2487     err = 0;
 2488     }
 2489 
 2490     return err;
 2491 }
 2492 
 2493 /* support the "fit" options for a single time-series plot */
 2494 
 2495 static int time_fit_plot (gnuplot_info *gi, const char *literal,
 2496               const DATASET *dset)
 2497 {
 2498     int yno = gi->list[1];
 2499     const double *yvar = dset->Z[yno];
 2500     FILE *fp = NULL;
 2501     char fitline[128] = {0};
 2502     PRN *prn;
 2503     int t, err = 0;
 2504 
 2505     if (gi->x == NULL) {
 2506     return E_DATA;
 2507     }
 2508 
 2509     err = graph_list_adjust_sample(gi->list, gi, dset, 1);
 2510     if (err) {
 2511     return err;
 2512     }
 2513 
 2514     err = get_fitted_line(gi, dset, fitline);
 2515     if (err) {
 2516     return err;
 2517     }
 2518 
 2519     gi->flags |= GPT_LETTERBOX;
 2520 
 2521     fp = open_plot_input_file(PLOT_REGULAR, gi->flags, &err);
 2522     if (err) {
 2523     return err;
 2524     }
 2525 
 2526     prn = gretl_print_new_with_stream(fp);
 2527 
 2528     if (prn != NULL) {
 2529     make_time_tics(gi, dset, 0, NULL, prn);
 2530     gretl_print_detach_stream(prn);
 2531     gretl_print_destroy(prn);
 2532     }
 2533 
 2534     print_keypos_string(GP_KEY_LEFT_TOP, fp);
 2535     print_axis_label('y', series_get_graph_name(dset, yno), fp);
 2536 
 2537     print_auto_fit_string(gi->fit, fp);
 2538 
 2539     print_gnuplot_literal_lines(literal, GNUPLOT, OPT_NONE, fp);
 2540 
 2541     fputs("plot \\\n", fp);
 2542     fputs(" '-' using 1:2 notitle w lines, \\\n", fp);
 2543 
 2544     gretl_push_c_numeric_locale();
 2545 
 2546     fprintf(fp, " %s", fitline);
 2547 
 2548     for (t=gi->t1; t<=gi->t2; t++) {
 2549     if (gi->flags & GPT_TIMEFMT) {
 2550         fprintf(fp, "%.0f %.10g\n", gi->x[t], yvar[t]);
 2551     } else {
 2552         fprintf(fp, "%.10g %.10g\n", gi->x[t], yvar[t]);
 2553     }
 2554     }
 2555     fputs("e\n", fp);
 2556 
 2557     gretl_pop_c_numeric_locale();
 2558 
 2559     err = finalize_plot_input_file(fp);
 2560 
 2561     clear_gpinfo(gi);
 2562 
 2563     return err;
 2564 }
 2565 
 2566 static int check_tic_labels (double vmin, double vmax,
 2567                  gnuplot_info *gi,
 2568                  char axis)
 2569 {
 2570     char s1[32], s2[32];
 2571     int d, err = 0;
 2572 
 2573     for (d=6; d<12; d++) {
 2574     sprintf(s1, "%.*g", d, vmin);
 2575     sprintf(s2, "%.*g", d, vmax);
 2576     if (strcmp(s1, s2)) {
 2577         break;
 2578     }
 2579     }
 2580 
 2581     if (d > 6) {
 2582     if (axis == 'x') {
 2583         sprintf(gi->xfmt, "%% .%dg", d+1);
 2584         if (vmax > vmin) {
 2585         sprintf(gi->xtics, "%.*g %#.6g", d+1, vmin,
 2586             (vmax - vmin)/ 4.0);
 2587         }
 2588     } else {
 2589         sprintf(gi->yfmt, "%% .%dg", d+1);
 2590     }
 2591     }
 2592 
 2593     return err;
 2594 }
 2595 
 2596 static void check_y_tics (gnuplot_info *gi, const double **Z,
 2597               FILE *fp)
 2598 {
 2599     double ymin, ymax;
 2600 
 2601     *gi->yfmt = '\0';
 2602 
 2603     gretl_minmax(gi->t1, gi->t2, Z[gi->list[1]], &ymin, &ymax);
 2604     check_tic_labels(ymin, ymax, gi, 'y');
 2605 
 2606     if (*gi->yfmt != '\0') {
 2607     fprintf(fp, "set format y \"%s\"\n", gi->yfmt);
 2608     }
 2609 }
 2610 
 2611 /* Find the minimum and maximum x-axis values and construct the gnuplot
 2612    x-range.  We have to be a bit careful here to include only values
 2613    that will actually display on the plot, i.e. x-values that are
 2614    accompanied by at least one non-missing y-axis value.
 2615 
 2616    In the case of a "factorized" plot the factor variable must also
 2617    be non-missing in order to include a given data point.
 2618 
 2619    We also have to avoid creating an "empty x range" that will choke
 2620    gnuplot.
 2621 */
 2622 
 2623 static void print_x_range_from_list (gnuplot_info *gi,
 2624                      const DATASET *dset,
 2625                      const int *list,
 2626                      FILE *fp)
 2627 {
 2628     const double *x, *d = NULL;
 2629     int k, l0 = list[0];
 2630 
 2631     if (gi->flags & GPT_DUMMY) {
 2632     /* the factor variable comes last and the x variable
 2633        is in second-last place */
 2634     d = dset->Z[list[l0]];
 2635     k = l0 - 1;
 2636     } else {
 2637     /* the x variable comes last in the list */
 2638     k = l0;
 2639     }
 2640 
 2641     x = dset->Z[list[k]];
 2642 
 2643     if (gretl_isdummy(gi->t1, gi->t2, x)) {
 2644     fputs("set xrange [-1:2]\n", fp);
 2645     fputs("set xtics (\"0\" 0, \"1\" 1)\n", fp);
 2646     gi->xrange = 3;
 2647     } else {
 2648     double xmin, xmin0 = NADBL;
 2649     double xmax, xmax0 = NADBL;
 2650     int t, i, vy, obs_ok;
 2651 
 2652     for (t=gi->t1; t<=gi->t2; t++) {
 2653         obs_ok = 0;
 2654         if (!na(x[t]) && (d == NULL || !na(d[t]))) {
 2655         for (i=1; i<k; i++) {
 2656             vy = list[i];
 2657             if (!na(dset->Z[vy][t])) {
 2658             /* got x obs and at least one y obs */
 2659             obs_ok = 1;
 2660             break;
 2661             }
 2662         }
 2663         }
 2664         if (obs_ok) {
 2665         if (na(xmin0) || x[t] < xmin0) {
 2666             xmin0 = x[t];
 2667         }
 2668         if (na(xmax0) || x[t] > xmax0) {
 2669             xmax0 = x[t];
 2670         }
 2671         }
 2672     }
 2673 
 2674     gi->xrange = xmax0 - xmin0;
 2675 
 2676     if (gi->xrange == 0.0) {
 2677         /* construct a non-empty range */
 2678         xmin = xmin0 - 0.5;
 2679         xmax = xmin0 + 0.5;
 2680     } else {
 2681         xmin = xmin0 - gi->xrange * .025;
 2682         if (xmin0 >= 0.0 && xmin < 0.0) {
 2683         xmin = 0.0;
 2684         }
 2685         xmax = xmax0 + gi->xrange * .025;
 2686     }
 2687 
 2688     fprintf(fp, "set xrange [%.10g:%.10g]\n", xmin, xmax);
 2689     gi->xrange = xmax - xmin;
 2690     check_tic_labels(xmin0, xmax0, gi, 'x');
 2691     }
 2692 }
 2693 
 2694 static void print_x_range (gnuplot_info *gi, FILE *fp)
 2695 {
 2696     if (gretl_isdummy(gi->t1, gi->t2, gi->x)) {
 2697     fputs("set xrange [-1:2]\n", fp);
 2698     fputs("set xtics (\"0\" 0, \"1\" 1)\n", fp);
 2699     gi->xrange = 3;
 2700     } else {
 2701     double xmin0, xmin, xmax0, xmax;
 2702 
 2703     gretl_minmax(gi->t1, gi->t2, gi->x, &xmin0, &xmax0);
 2704     gi->xrange = xmax0 - xmin0;
 2705     xmin = xmin0 - gi->xrange * .025;
 2706     if (xmin0 >= 0.0 && xmin < 0.0) {
 2707         xmin = 0.0;
 2708     }
 2709     xmax = xmax0 + gi->xrange * .025;
 2710     fprintf(fp, "set xrange [%.10g:%.10g]\n", xmin, xmax);
 2711     gi->xrange = xmax - xmin;
 2712     }
 2713 }
 2714 
 2715 /* two or more y vars plotted against some x: test to see if we want
 2716    to use two y axes */
 2717 
 2718 static void
 2719 check_for_yscale (gnuplot_info *gi, const double **Z, int *oddman)
 2720 {
 2721     double ymin[6], ymax[6];
 2722     double ratio;
 2723     int lmax = gi->list[0];
 2724     int i, j, oddcount;
 2725 
 2726 #if GP_DEBUG
 2727     fprintf(stderr, "gnuplot: doing check_for_yscale, listlen %d\n",
 2728         gi->list[0]);
 2729 #endif
 2730 
 2731     if (gi->flags & GPT_IDX) {
 2732     /* do this only if we haven't added a 0 at the end of
 2733        the list */
 2734     if (gi->list[lmax] != 0) {
 2735         lmax++;
 2736     }
 2737     }
 2738 
 2739     /* find minima, maxima of the y-axis vars */
 2740     for (i=1; i<lmax; i++) {
 2741     gretl_minmax(gi->t1, gi->t2, Z[gi->list[i]],
 2742              &ymin[i-1], &ymax[i-1]);
 2743     }
 2744 
 2745     gi->flags &= ~GPT_Y2AXIS;
 2746 
 2747     for (i=lmax-1; i>0; i--) {
 2748     oddcount = 0;
 2749     for (j=1; j<lmax; j++) {
 2750         if (j != i) {
 2751         ratio = ymax[i-1] / ymax[j-1];
 2752         if (ratio > 5.0 || ratio < 0.2) {
 2753             gi->flags |= GPT_Y2AXIS;
 2754             oddcount++;
 2755         }
 2756         }
 2757     }
 2758     if (oddcount == lmax - 2) {
 2759         /* series at list position i differs considerably in scale
 2760            from all the others in the list */
 2761         *oddman = i;
 2762         break;
 2763     }
 2764     }
 2765 
 2766     if (*oddman == 0) {
 2767     gi->flags &= ~GPT_Y2AXIS;
 2768     }
 2769 }
 2770 
 2771 static int print_gp_dummy_data (gnuplot_info *gi,
 2772                 const DATASET *dset,
 2773                 FILE *fp)
 2774 {
 2775     const double *d = dset->Z[gi->list[3]];
 2776     const double *y = dset->Z[gi->list[1]];
 2777     double xt, yt;
 2778     int i, t, n;
 2779 
 2780     n = gretl_vector_get_length(gi->dvals);
 2781 
 2782     for (i=0; i<n; i++) {
 2783     for (t=gi->t1; t<=gi->t2; t++) {
 2784         if (gi->x != NULL) {
 2785         xt = gi->x[t];
 2786         } else {
 2787         xt = dset->Z[gi->list[2]][t];
 2788         if (na(xt)) {
 2789             continue;
 2790         }
 2791         }
 2792         if (na(d[t])) {
 2793         continue;
 2794         }
 2795         yt = (d[t] == gi->dvals->val[i])? y[t] : NADBL;
 2796         if (na(yt)) {
 2797         fprintf(fp, "%.10g %s\n", xt, gpna);
 2798         } else {
 2799         fprintf(fp, "%.10g %.10g", xt, yt);
 2800         if (!(gi->flags & GPT_TS)) {
 2801             if (dset->markers) {
 2802             fprintf(fp, " # %s", dset->S[t]);
 2803             } else if (dataset_is_time_series(dset)) {
 2804             char obs[OBSLEN];
 2805 
 2806             ntodate(obs, t, dset);
 2807             fprintf(fp, " # %s", obs);
 2808             }
 2809         }
 2810         fputc('\n', fp);
 2811         }
 2812     }
 2813     fputs("e\n", fp);
 2814     }
 2815 
 2816     return 0;
 2817 }
 2818 
 2819 /* for printing panel time-series graph: insert a discontinuity
 2820    between the panel units */
 2821 
 2822 static void
 2823 maybe_print_panel_jot (int t, const DATASET *dset, FILE *fp)
 2824 {
 2825     char obs[OBSLEN];
 2826     int maj, min;
 2827 
 2828     ntodate(obs, t, dset);
 2829     sscanf(obs, "%d:%d", &maj, &min);
 2830     if (maj > 1 && min == 1) {
 2831     fprintf(fp, "%g %s\n", t + 0.5, gpna);
 2832     }
 2833 }
 2834 
 2835 /* sanity check for totally empty graph */
 2836 
 2837 static int
 2838 all_graph_data_missing (const int *list, int t, const double **Z)
 2839 {
 2840     int i;
 2841 
 2842     for (i=1; i<=list[0]; i++) {
 2843     if (!na(Z[list[i]][t])) {
 2844         return 0;
 2845     }
 2846     }
 2847 
 2848     return 1;
 2849 }
 2850 
 2851 static int use_impulses (gnuplot_info *gi)
 2852 {
 2853     if (gi->withlist != NULL) {
 2854     int i;
 2855 
 2856     for (i=1; i<=gi->withlist[0]; i++) {
 2857         if (gi->withlist[i] == W_IMPULSES) {
 2858         return 1;
 2859         }
 2860     }
 2861     }
 2862 
 2863     return 0;
 2864 }
 2865 
 2866 static int use_lines (gnuplot_info *gi)
 2867 {
 2868     if (gi->withlist != NULL) {
 2869     int i;
 2870 
 2871     for (i=1; i<=gi->withlist[0]; i++) {
 2872         if (gi->withlist[i] == W_LINES) {
 2873         return 1;
 2874         }
 2875     }
 2876     }
 2877 
 2878     return 0;
 2879 }
 2880 
 2881 /* list of series IDs for which we should skip observations
 2882    with NAs when printing the plot data */
 2883 static int *na_skiplist;
 2884 
 2885 static void print_gp_data (gnuplot_info *gi, const DATASET *dset,
 2886                FILE *fp)
 2887 {
 2888     int n = gi->t2 - gi->t1 + 1;
 2889     double offset = 0.0;
 2890     int datlist[3];
 2891     int lmax, ynum = 2;
 2892     int nomarkers = 0;
 2893     int i, t;
 2894 
 2895     /* multi impulse plot? calculate offset for lines */
 2896     if (use_impulses(gi) && gi->list[0] > 2) {
 2897     offset = 0.10 * gi->xrange / n;
 2898     }
 2899 
 2900     if (gi->x != NULL) {
 2901     lmax = gi->list[0] - 1;
 2902     datlist[0] = 1;
 2903     ynum = 1;
 2904     } else {
 2905     lmax = gi->list[0] - 1;
 2906     datlist[0] = 2;
 2907     datlist[1] = gi->list[gi->list[0]];
 2908     }
 2909 
 2910     if (use_impulses(gi) || use_lines(gi)) {
 2911     nomarkers = 1;
 2912     }
 2913 
 2914     /* loop across the variables, printing x then y[i] for each i */
 2915 
 2916     for (i=1; i<=lmax; i++) {
 2917     double xoff = offset * (i - 1);
 2918 
 2919     datlist[ynum] = gi->list[i];
 2920 
 2921     for (t=gi->t1; t<=gi->t2; t++) {
 2922         const char *label = NULL;
 2923         char obs[OBSLEN];
 2924 
 2925         if (in_gretl_list(na_skiplist, datlist[ynum]) &&
 2926         na(dset->Z[datlist[ynum]][t])) {
 2927         continue;
 2928         } else if (gi->x == NULL &&
 2929         all_graph_data_missing(gi->list, t, (const double **) dset->Z)) {
 2930         continue;
 2931         }
 2932         if (!(gi->flags & GPT_TS) && i == 1) {
 2933         if (dset->markers) {
 2934             label = dset->S[t];
 2935         } else if (!nomarkers && dataset_is_time_series(dset)) {
 2936             ntodate(obs, t, dset);
 2937             label = obs;
 2938         }
 2939         }
 2940         if ((gi->flags & GPT_TS) && dset->structure == STACKED_TIME_SERIES) {
 2941         maybe_print_panel_jot(t, dset, fp);
 2942         }
 2943         printvars(fp, t, datlist, dset, gi, label, xoff);
 2944     }
 2945 
 2946     fputs("e\n", fp);
 2947     }
 2948 }
 2949 
 2950 static int
 2951 gpinfo_init (gnuplot_info *gi, gretlopt opt, const int *list,
 2952          const char *literal, const DATASET *dset)
 2953 {
 2954     int l0 = list[0];
 2955     int err = 0;
 2956 
 2957     gi->withlist = NULL;
 2958     gi->yformula = NULL;
 2959     gi->x = NULL;
 2960     gi->list = NULL;
 2961     gi->dvals = NULL;
 2962     gi->band = 0;
 2963 
 2964     err = get_gp_flags(gi, opt, list, dset);
 2965     if (err) {
 2966     return err;
 2967     }
 2968 
 2969     if (gi->band) {
 2970     /* force single y-axis in "band" case */
 2971     opt |= OPT_Y;
 2972     }
 2973 
 2974     if (gi->fit == PLOT_FIT_NONE) {
 2975     gi->flags |= GPT_TS; /* may be renounced later */
 2976     }
 2977 
 2978     if (dset->t2 - dset->t1 + 1 <= 0) {
 2979     /* null sample range */
 2980     return E_DATA;
 2981     }
 2982 
 2983     gi->t1 = dset->t1;
 2984     gi->t2 = dset->t2;
 2985     gi->xrange = 0.0;
 2986     gi->timefmt[0] = '\0';
 2987     gi->xtics[0] = '\0';
 2988     gi->xfmt[0] = '\0';
 2989     gi->yfmt[0] = '\0';
 2990 
 2991     if (l0 < 2 && !(gi->flags & GPT_IDX)) {
 2992     return E_ARGS;
 2993     }
 2994 
 2995     if ((gi->flags & GPT_DUMMY) && (gi->flags & GPT_IDX)) {
 2996     return E_BADOPT;
 2997     }
 2998 
 2999     gi->list = gretl_list_copy(list);
 3000     if (gi->list == NULL) {
 3001     return E_ALLOC;
 3002     }
 3003 
 3004     if ((l0 > 2 || (l0 > 1 && (gi->flags & GPT_IDX))) &&
 3005     l0 < 7 && !(gi->flags & GPT_RESIDS) && !(gi->flags & GPT_FA)
 3006     && !(gi->flags & GPT_DUMMY)  & !(opt & OPT_Y)) {
 3007     /* FIXME GPT_XYZ ? */
 3008     /* allow probe for using two y axes */
 3009 #if GP_DEBUG
 3010     fprintf(stderr, "l0 = %d, setting y2axis probe\n", l0);
 3011 #endif
 3012     gi->flags |= GPT_Y2AXIS;
 3013     }
 3014 
 3015     if ((gi->flags & GPT_FA) && literal != NULL &&
 3016     !strncmp(literal, "yformula: ", 10)) {
 3017     /* fitted vs actual plot with fitted given by formula */
 3018     gi->yformula = literal + 10;
 3019     }
 3020 
 3021     if (literal != NULL && strstr(literal, "set style data")) {
 3022     gi->flags |= GPT_DATA_STYLE;
 3023     }
 3024 
 3025 #if GP_DEBUG
 3026     if (gi->flags) {
 3027     print_gnuplot_flags(gi->flags, 1);
 3028     }
 3029 #endif
 3030 
 3031     return 0;
 3032 }
 3033 
 3034 static void clear_gpinfo (gnuplot_info *gi)
 3035 {
 3036     free(gi->list);
 3037     gretl_matrix_free(gi->dvals);
 3038     free(gi->withlist);
 3039 }
 3040 
 3041 #if GP_DEBUG
 3042 
 3043 static void print_gnuplot_flags (int flags, int revised)
 3044 {
 3045     if (revised) {
 3046     fprintf(stderr, "*** gnuplot flags after initial revision:\n");
 3047     } else {
 3048     fprintf(stderr, "*** gnuplot() called with flags:\n");
 3049     }
 3050 
 3051     if (flags & GPT_RESIDS) {
 3052     fprintf(stderr, " GPT_RESIDS\n");
 3053     }
 3054     if (flags & GPT_FA) {
 3055     fprintf(stderr, " GPT_FA\n");
 3056     }
 3057     if (flags & GPT_DUMMY) {
 3058     fprintf(stderr, " GPT_DUMMY\n");
 3059     }
 3060     if (flags & GPT_XYZ) {
 3061     fprintf(stderr, " GPT_XYZ\n");
 3062     }
 3063     if (flags & GPT_FIT_OMIT) {
 3064     fprintf(stderr, " GPT_FIT_OMIT\n");
 3065     }
 3066     if (flags & GPT_DATA_STYLE) {
 3067     fprintf(stderr, " GPT_DATA_STYLE\n");
 3068     }
 3069     if (flags & GPT_IDX) {
 3070     fprintf(stderr, " GPT_IDX\n");
 3071     }
 3072     if (flags & GPT_TS) {
 3073     fprintf(stderr, " GPT_TS\n");
 3074     }
 3075     if (flags & GPT_Y2AXIS) {
 3076     fprintf(stderr, " GPT_Y2AXIS\n");
 3077     }
 3078     if (flags & GPT_AUTO_FIT) {
 3079     fprintf(stderr, " GPT_AUTO_FIT\n");
 3080     }
 3081     if (flags & GPT_FIT_HIDDEN) {
 3082     fprintf(stderr, " GPT_FIT_HIDDEN\n");
 3083     }
 3084 }
 3085 
 3086 #endif
 3087 
 3088 static void set_lwstr (const DATASET *dset, int v, char *s)
 3089 {
 3090     if (default_png_scale > 1.0) {
 3091     strcpy(s, " lw 2");
 3092     } else {
 3093     *s = '\0';
 3094     }
 3095 }
 3096 
 3097 static void set_withstr (gnuplot_info *gi, int i, char *str)
 3098 {
 3099     if (gi->flags & GPT_DATA_STYLE) {
 3100     *str = '\0';
 3101     } else if (gi->withlist != NULL) {
 3102     int withval = W_POINTS;
 3103 
 3104     if (i > 0 && i <= gi->withlist[0]) {
 3105         withval = gi->withlist[i];
 3106     }
 3107 
 3108     if (withval == W_LINES) {
 3109         strcpy(str, "w lines");
 3110     } else if (withval == W_IMPULSES) {
 3111         strcpy(str, "w impulses");
 3112     } else if (withval == W_LP) {
 3113         strcpy(str, "w linespoints");
 3114     } else if (withval == W_BOXES) {
 3115         strcpy(str, "w boxes");
 3116     } else if (withval == W_STEPS) {
 3117         strcpy(str, "w steps");
 3118     } else {
 3119         strcpy(str, "w points");
 3120     }
 3121     } else if (gi->flags & GPT_LINES) {
 3122         strcpy(str, "w lines");
 3123     } else if (gi->flags & GPT_IMPULSES) {
 3124     strcpy(str, "w impulses");
 3125     } else if (gi->flags & GPT_STEPS) {
 3126     strcpy(str, "w steps");
 3127     } else {
 3128     strcpy(str, "w points");
 3129     }
 3130 }
 3131 
 3132 static int graph_list_adjust_sample (int *list,
 3133                      gnuplot_info *ginfo,
 3134                      const DATASET *dset,
 3135                      int listmin)
 3136 {
 3137     int t1min = ginfo->t1;
 3138     int t2max = ginfo->t2;
 3139     int i, t, vi, t_ok;
 3140     int err = 0;
 3141 
 3142     for (t=t1min; t<=t2max; t++) {
 3143     t_ok = 0;
 3144     for (i=1; i<=list[0]; i++) {
 3145         vi = list[i];
 3146         if (vi > 0 && !na(dset->Z[vi][t])) {
 3147         t_ok = 1;
 3148         break;
 3149         }
 3150     }
 3151     if (t_ok) {
 3152         break;
 3153     }
 3154     t1min++;
 3155     }
 3156 
 3157     for (t=t2max; t>t1min; t--) {
 3158     t_ok = 0;
 3159     for (i=1; i<=list[0]; i++) {
 3160         vi = list[i];
 3161         if (vi > 0 && !na(dset->Z[vi][t])) {
 3162         t_ok = 1;
 3163         break;
 3164         }
 3165     }
 3166     if (t_ok) {
 3167         break;
 3168     }
 3169     t2max--;
 3170     }
 3171 
 3172     if (t2max > t1min) {
 3173     for (i=1; i<=list[0]; i++) {
 3174         int all_missing = 1;
 3175 
 3176         vi = list[i];
 3177         for (t=t1min; t<=t2max; t++) {
 3178         if (!na(dset->Z[vi][t])) {
 3179             all_missing = 0;
 3180             break;
 3181         }
 3182         }
 3183         if (all_missing) {
 3184         gretl_list_delete_at_pos(list, i);
 3185         i--;
 3186         }
 3187     }
 3188     }
 3189 
 3190     ginfo->t1 = t1min;
 3191     ginfo->t2 = t2max;
 3192 
 3193     if (ginfo->t1 >= ginfo->t2 || list[0] < listmin) {
 3194     err = E_MISSDATA;
 3195     }
 3196 
 3197     return err;
 3198 }
 3199 
 3200 /* Check whether mktime() works for the starting date.
 3201    Note that it won't work on Windows for dates prior to
 3202    1970 since Microsoft doesn't handle negative time_t
 3203    values.
 3204 */
 3205 
 3206 static int timefmt_useable (const DATASET *dset)
 3207 {
 3208     char *test, datestr[OBSLEN];
 3209     struct tm t = {0};
 3210     int ok = 0;
 3211 
 3212     if (sample_size(dset) > 300) {
 3213     /* not a good idea? */
 3214     return 0;
 3215     }
 3216 
 3217     errno = 0;
 3218 
 3219     if (dset->S != NULL) {
 3220     strcpy(datestr, dset->S[dset->t1]);
 3221     } else {
 3222     calendar_date_string(datestr, dset->t1, dset);
 3223     }
 3224 
 3225     test = strptime(datestr, "%Y-%m-%d", &t);
 3226     if (test != NULL && *test == '\0') {
 3227     mktime(&t);
 3228     ok = (errno == 0);
 3229     }
 3230 
 3231     errno = 0;
 3232 
 3233     return ok;
 3234 }
 3235 
 3236 static int maybe_add_plotx (gnuplot_info *gi, int time_fit,
 3237                 const DATASET *dset)
 3238 {
 3239     gretlopt xopt = OPT_NONE;
 3240     int k = gi->list[0];
 3241     int add0 = 0;
 3242 
 3243     /* are we really doing a time-series plot? */
 3244     if (k > 1 && !strcmp(dset->varname[gi->list[k]], "time")) {
 3245     ; /* yes */
 3246     } else if (gi->flags & GPT_IDX) {
 3247     add0 = 1; /* yes */
 3248     } else {
 3249     /* no: get out */
 3250     gi->flags &= ~GPT_TS;
 3251     return 0;
 3252     }
 3253 
 3254     if (!time_fit) {
 3255     if (dated_daily_data(dset) || dated_weekly_data(dset)) {
 3256         if (timefmt_useable(dset)) {
 3257         /* experimental */
 3258         gi->flags |= GPT_TIMEFMT;
 3259         xopt = OPT_T;
 3260         }
 3261     }
 3262     }
 3263 
 3264     gi->x = gretl_plotx(dset, xopt);
 3265     if (gi->x == NULL) {
 3266     return E_ALLOC;
 3267     }
 3268 
 3269     /* a bit ugly, but add a dummy list entry for
 3270        the 'virtual' plot variable */
 3271     if (add0) {
 3272     gretl_list_append_term(&gi->list, 0);
 3273     if (gi->list == NULL) {
 3274         return E_ALLOC;
 3275     }
 3276     }
 3277 
 3278 #if GP_DEBUG
 3279     fprintf(stderr, "maybe_add_plotx: gi->x at %p\n",
 3280         (void *) gi->x);
 3281     printlist(gi->list, "gi->list");
 3282 #endif
 3283 
 3284     return 0;
 3285 }
 3286 
 3287 void gnuplot_missval_string (FILE *fp)
 3288 {
 3289     fputs("set datafile missing \"?\"\n", fp);
 3290 }
 3291 
 3292 static void graph_month_name (char *mname, int m)
 3293 {
 3294     struct tm mt;
 3295 
 3296     mt.tm_sec = 0;
 3297     mt.tm_min = 0;
 3298     mt.tm_hour = 0;
 3299     mt.tm_mday = 1;
 3300     mt.tm_mon = m - 1;
 3301     mt.tm_year = 100;
 3302 
 3303     strftime(mname, 7, "%b", &mt);
 3304 
 3305     if (!g_utf8_validate(mname, -1, NULL)) {
 3306     /* we might be in a non-UTF-8 locale */
 3307     gchar *tmp;
 3308     gsize bytes;
 3309 
 3310     tmp = g_locale_to_utf8(mname, -1, NULL, &bytes, NULL);
 3311     if (tmp != NULL) {
 3312         strcpy(mname, tmp);
 3313         g_free(tmp);
 3314     }
 3315     }
 3316 }
 3317 
 3318 /* for short daily time-series plots: write month names
 3319    into the xtics */
 3320 
 3321 static void make_named_month_tics (const gnuplot_info *gi, double yrs,
 3322                    PRN *prn)
 3323 {
 3324     double t0 = gi->x[gi->t1];
 3325     double t1 = gi->x[gi->t2];
 3326     double x, tw = 1.0/12;
 3327     int i, m, n = 0;
 3328     char mname[16];
 3329     int notfirst = 0;
 3330     int scale = (int) (yrs * 1.5);
 3331 
 3332     if (scale == 0) {
 3333     scale = 1;
 3334     }
 3335 
 3336     t0 += (1.0 - (t0 - floor(t0)) * 12.0) / 12.0;
 3337     for (x=t0; x<t1; x+=tw) n++;
 3338 
 3339     x = (t0 - floor(t0)) * 12;
 3340     m = 1 + ((x - floor(x) > .8)? ceil(x) : floor(x));
 3341     if (m > 12) m -= 12;
 3342 
 3343     pputs(prn, "set xtics (");
 3344     x = t0;
 3345 
 3346     gretl_push_c_numeric_locale();
 3347 
 3348     for (i=0; i<n; i++) {
 3349     if (m == 1) {
 3350         if (notfirst) {
 3351         pputs(prn, ", ");
 3352         }
 3353         pprintf(prn, "\"%4.0f\" %.10g", x, x);
 3354         notfirst = 1;
 3355     } else if ((scale == 1) || (m % scale == 1)) {
 3356         graph_month_name(mname, m);
 3357         if (notfirst) {
 3358         pputs(prn, ", ");
 3359         }
 3360         pprintf(prn, "\"%s\" %.10g", mname, x);
 3361         notfirst = 1;
 3362     }
 3363     m++;
 3364     x += tw;
 3365     if (m > 12) m -= 12;
 3366     }
 3367 
 3368     gretl_pop_c_numeric_locale();
 3369 
 3370     pputs(prn, ")\n");
 3371 }
 3372 
 3373 static int continues_unit (const DATASET *dset, int t)
 3374 {
 3375     return t / dset->pd == (t-1) / dset->pd;
 3376 }
 3377 
 3378 /* Below: we're making a combined time series plot for panel data.
 3379    That is, time series for unit 1, followed by time series for unit
 3380    2, etc.  We'd like to show tic marks to represent the start of each
 3381    unit's time series, but we have to watch out for the case where
 3382    there are "too many" units -- we don't want a dense fudge of marks
 3383    on the x-axis.  In that case we put a tic mark only for every k'th
 3384    unit.
 3385 */
 3386 
 3387 static void make_panel_unit_tics (const DATASET *dset,
 3388                   gnuplot_info *gi,
 3389                   PRN *prn)
 3390 {
 3391     int maxtics, ticskip;
 3392     double ntics;
 3393     int printed;
 3394     int u, t, n;
 3395 
 3396     pputs(prn, "set xtics (");
 3397 
 3398     gretl_push_c_numeric_locale();
 3399 
 3400     /* how many panel units are included in the plot? */
 3401     maxtics = gi->t2 / dset->pd - gi->t1 / dset->pd + 1;
 3402 
 3403     ntics = maxtics;
 3404     while (ntics > 20) {
 3405     ntics /= 1.5;
 3406     }
 3407 
 3408     ticskip = maxtics / ceil(ntics);
 3409 
 3410     if (ticskip == 1 && ntics < maxtics) {
 3411     /* otherwise we'll get an incomplete scale */
 3412     ntics = maxtics;
 3413     }
 3414 
 3415     n = printed = 0;
 3416     u = gi->t1 / dset->pd;
 3417 
 3418     for (t=gi->t1; t<=gi->t2 && printed<ntics; t++) {
 3419     if (t == gi->t1 || !continues_unit(dset, t)) {
 3420         u++;
 3421         if (n % ticskip == 0) {
 3422         pprintf(prn, "\"%d\" %.10g", u, gi->x[t]);
 3423         if (++printed < ntics) {
 3424             pputs(prn, ", ");
 3425         }
 3426         }
 3427         n++;
 3428     }
 3429     }
 3430 
 3431     gretl_pop_c_numeric_locale();
 3432 
 3433     pputs(prn, ")\n");
 3434 }
 3435 
 3436 static void make_calendar_tics (const DATASET *dset,
 3437                 const gnuplot_info *gi,
 3438                 PRN *prn)
 3439 {
 3440     int T = gi->t2 - gi->t1 + 1;
 3441     double yrs;
 3442 
 3443     if (dset->pd == 52) {
 3444     yrs = T / 52.0;
 3445     } else {
 3446     yrs = T / (dset->pd * 52.0);
 3447     }
 3448 
 3449     if (yrs <= 3) {
 3450     make_named_month_tics(gi, yrs, prn);
 3451     } else if (yrs < 6) {
 3452     /* don't show ugly "fractions of years" */
 3453     pputs(prn, "set xtics 1\n");
 3454     if (yrs < 3) {
 3455         /* put monthly minor tics */
 3456         pputs(prn, "set mxtics 12\n");
 3457     } else if (yrs < 5) {
 3458         /* quarterly minor tics */
 3459         pputs(prn, "set mxtics 4\n");
 3460     }
 3461     }
 3462 }
 3463 
 3464 static int multiple_groups (const DATASET *dset, int t1, int t2)
 3465 {
 3466     int ret = 0;
 3467 
 3468     if (dataset_is_panel(dset)) {
 3469     ret = (t2 / dset->pd > t1 / dset->pd);
 3470     }
 3471 
 3472     return ret;
 3473 }
 3474 
 3475 static int single_year_sample (const DATASET *dset,
 3476                    int t1, int t2)
 3477 {
 3478     char obs[OBSLEN];
 3479     int y1, y2;
 3480 
 3481     ntodate(obs, t1, dset);
 3482     y1 = atoi(obs);
 3483     ntodate(obs, t2, dset);
 3484     y2 = atoi(obs);
 3485 
 3486     return y2 == y1;
 3487 }
 3488 
 3489 /* special tics for time series plots */
 3490 
 3491 static void make_time_tics (gnuplot_info *gi,
 3492                 const DATASET *dset,
 3493                 int many, char *xlabel,
 3494                 PRN *prn)
 3495 {
 3496     if (many) {
 3497     pprintf(prn, "# multiple timeseries %d\n", dset->pd);
 3498     } else {
 3499     pprintf(prn, "# timeseries %d", dset->pd);
 3500     gi->flags |= GPT_LETTERBOX;
 3501     pputs(prn, " (letterbox)\n");
 3502     }
 3503 
 3504     if (gi->flags & GPT_TIMEFMT) {
 3505     pputs(prn, "set xdata time\n");
 3506     strcpy(gi->timefmt, "%s");
 3507     pprintf(prn, "set timefmt \"%s\"\n", gi->timefmt);
 3508     if (single_year_sample(dset, gi->t1, gi->t2)) {
 3509         strcpy(gi->xfmt, "%m-%d");
 3510     } else {
 3511         strcpy(gi->xfmt, "%y-%m-%d"); /* two-digit year */
 3512     }
 3513     pprintf(prn, "set format x \"%s\"\n", gi->xfmt);
 3514     pputs(prn, "set xtics rotate by -45\n");
 3515     return;
 3516     }
 3517 
 3518     if (dset->pd == 4 && (gi->t2 - gi->t1) / 4 < 8) {
 3519     pputs(prn, "set xtics nomirror 0,1\n");
 3520     pputs(prn, "set mxtics 4\n");
 3521     } else if (dset->pd == 12 && (gi->t2 - gi->t1) / 12 < 8) {
 3522     pputs(prn, "set xtics nomirror 0,1\n");
 3523     pputs(prn, "set mxtics 12\n");
 3524     } else if (dated_daily_data(dset) || dated_weekly_data(dset)) {
 3525     make_calendar_tics(dset, gi, prn);
 3526     } else if (multiple_groups(dset, gi->t1, gi->t2)) {
 3527     make_panel_unit_tics(dset, gi, prn);
 3528     if (xlabel != NULL) {
 3529         strcpy(xlabel, _("time series by group"));
 3530     }
 3531     }
 3532 }
 3533 
 3534 /* Handle the use of a matrix in the context of the "plot" command
 3535    block, or create a plot directly from a matrix and a plot list.
 3536 */
 3537 
 3538 int matrix_plot (gretl_matrix *m, const int *list, const char *literal,
 3539          gretlopt opt)
 3540 {
 3541     DATASET *dset = NULL;
 3542     int *plotlist = NULL;
 3543     int pmax, err = 0;
 3544 
 3545     if (gretl_is_null_matrix(m)) {
 3546     return E_DATA;
 3547     }
 3548 
 3549     if (list != NULL && list[0] == 0) {
 3550     dset = gretl_dataset_from_matrix(m, NULL, OPT_B, &err);
 3551     } else {
 3552     dset = gretl_dataset_from_matrix(m, list, OPT_B, &err);
 3553     }
 3554 
 3555     if (err) {
 3556     return err;
 3557     }
 3558 
 3559     pmax = dset->v - 1;
 3560 
 3561     if (pmax <= 0) {
 3562     err = E_DATA;
 3563     } else {
 3564     plotlist = gretl_consecutive_list_new(1, pmax);
 3565     if (plotlist == NULL) {
 3566         err = E_ALLOC;
 3567     }
 3568     }
 3569 
 3570     if (!err) {
 3571     if (opt & OPT_N) {
 3572         gnuplot_info gi;
 3573 
 3574         err = gpinfo_init(&gi, opt, plotlist, literal, dset);
 3575         if (!err) {
 3576         err = maybe_add_plotx(&gi, 0, dset);
 3577         }
 3578         if (!err) {
 3579         err = plot_with_band(BP_BLOCKMAT, &gi, literal,
 3580                      dset, opt);
 3581         }
 3582     } else {
 3583         err = gnuplot(plotlist, literal, dset, opt);
 3584     }
 3585     }
 3586 
 3587     destroy_dataset(dset);
 3588     free(plotlist);
 3589 
 3590     return err;
 3591 }
 3592 
 3593 static int plotlist_is_group_invariant (const int *list, const DATASET *dset)
 3594 {
 3595     int i;
 3596 
 3597     for (i=1; i<=list[0]; i++) {
 3598     if (!series_is_group_invariant(dset, list[i])) {
 3599         return 0;
 3600     }
 3601     }
 3602 
 3603     return 1;
 3604 }
 3605 
 3606 static int panel_group_invariant_plot (const int *plotlist,
 3607                        const char *literal,
 3608                        DATASET *dset,
 3609                        gretlopt opt)
 3610 {
 3611     DATASET orig = *dset;
 3612     int err;
 3613 
 3614     /* limit sample to first group */
 3615     dset->t1 = 0;
 3616     dset->t2 = dset->pd - 1;
 3617 
 3618     /* and mark as time-series data */
 3619     if (dset->panel_pd > 0) {
 3620     dset->pd = dset->panel_pd;
 3621     dset->sd0 = dset->panel_sd0;
 3622     dset->structure = TIME_SERIES;
 3623     } else {
 3624     dset->structure = SPECIAL_TIME_SERIES;
 3625     dset->pd = 1;
 3626     }
 3627 
 3628     err = gnuplot(plotlist, literal, dset, opt);
 3629 
 3630     /* put everything back as it was */
 3631     *dset = orig;
 3632 
 3633     return err;
 3634 }
 3635 
 3636 /**
 3637  * gnuplot:
 3638  * @plotlist: list of variables to plot, by ID number.
 3639  * @literal: commands to be passed to gnuplot.
 3640  * @dset: dataset struct.
 3641  * @opt: option flags.
 3642  *
 3643  * Writes a gnuplot plot file to display the values of the
 3644  * variables in @list and calls gnuplot to make the graph.
 3645  *
 3646  * Returns: 0 on successful completion, non-zero code on error.
 3647  */
 3648 
 3649 int gnuplot (const int *plotlist, const char *literal,
 3650          const DATASET *dset, gretlopt opt)
 3651 {
 3652     PRN *prn = NULL;
 3653     FILE *fp = NULL;
 3654     int *list = NULL;
 3655     char s1[MAXDISP] = {0};
 3656     char s2[MAXDISP] = {0};
 3657     char xlabel[MAXDISP] = {0};
 3658     char withstr[16] = {0};
 3659     char lwstr[8] = {0};
 3660     char keystr[48] = {0};
 3661     char fit_line[128] = {0};
 3662     int time_fit = 0;
 3663     int oddman = 0;
 3664     int many = 0;
 3665     int set_xrange = 1;
 3666     PlotType ptype;
 3667     gnuplot_info gi;
 3668     int i, err = 0;
 3669 
 3670     gretl_error_clear();
 3671 
 3672     if ((opt & OPT_T) && (opt & OPT_F)) {
 3673     if (plotlist[0] > 1 || !dataset_is_time_series(dset)) {
 3674         return E_BADOPT;
 3675     } else {
 3676         time_fit = 1;
 3677     }
 3678     }
 3679 
 3680     if (literal != NULL && strstr(literal, "set xdata time")) {
 3681     set_xrange = 0;
 3682     }
 3683 
 3684     if (dataset_is_panel(dset) &&
 3685     plotlist_is_group_invariant(plotlist, dset)) {
 3686     return panel_group_invariant_plot(plotlist, literal,
 3687                       (DATASET *) dset, opt);
 3688     }
 3689 
 3690 #if GP_DEBUG
 3691     printlist(plotlist, "gnuplot: plotlist");
 3692     fprintf(stderr, "incoming plot range: obs %d to %d\n", dset->t1, dset->t2);
 3693 #endif
 3694 
 3695     err = gpinfo_init(&gi, opt, plotlist, literal, dset);
 3696     if (err) {
 3697     goto bailout;
 3698     }
 3699 
 3700 #if GP_DEBUG
 3701     fprintf(stderr, "after gpinfo_init: gi.fit = %d\n", gi.fit);
 3702 #endif
 3703 
 3704     err = maybe_add_plotx(&gi, time_fit, dset);
 3705     if (err) {
 3706     goto bailout;
 3707     }
 3708 
 3709     if (gi.fit == PLOT_FIT_LOESS) {
 3710     return loess_plot(&gi, literal, dset);
 3711     }
 3712 
 3713     if (time_fit) {
 3714     return time_fit_plot(&gi, literal, dset);
 3715     }
 3716 
 3717     if (gi.band) {
 3718     return plot_with_band(BP_REGULAR, &gi, literal,
 3719                   (DATASET *) dset, opt);
 3720     }
 3721 
 3722     if (gi.list[0] > MAX_LETTERBOX_LINES + 1) {
 3723     many = 1;
 3724     }
 3725 
 3726     /* convenience pointer */
 3727     list = gi.list;
 3728 
 3729     /* set x-axis label for non-time series plots */
 3730     if (!(gi.flags & GPT_TS)) {
 3731     int v = (gi.flags & GPT_DUMMY)? list[2] : list[list[0]];
 3732 
 3733     strcpy(xlabel, series_get_graph_name(dset, v));
 3734     }
 3735 
 3736     prn = gretl_print_new(GRETL_PRINT_BUFFER, &err);
 3737     if (err) {
 3738     goto bailout;
 3739     }
 3740 
 3741     /* adjust sample range, and reject if it's empty */
 3742     err = graph_list_adjust_sample(list, &gi, dset, 2);
 3743     if (err) {
 3744     goto bailout;
 3745     }
 3746 
 3747     /* add a regression line if appropriate */
 3748     if (!use_impulses(&gi) && !(gi.flags & GPT_FIT_OMIT) && list[0] == 2 &&
 3749     !(gi.flags & GPT_TS) && !(gi.flags & GPT_RESIDS)) {
 3750     err = get_fitted_line(&gi, dset, fit_line);
 3751     if (err) {
 3752         goto bailout;
 3753     } else {
 3754         const char *xname = dset->varname[list[2]];
 3755         const char *yname = dset->varname[list[1]];
 3756 
 3757         if (*xname != '\0' && *yname != '\0') {
 3758         pprintf(prn, "# X = '%s' (%d)\n", xname, list[2]);
 3759         pprintf(prn, "# Y = '%s' (%d)\n", yname, list[1]);
 3760         }
 3761     }
 3762     }
 3763 
 3764     ptype = PLOT_REGULAR;
 3765 
 3766     /* separation by dummy: create special vars */
 3767     if (gi.flags & GPT_DUMMY) {
 3768     err = factor_check(&gi, dset);
 3769     if (err) {
 3770         goto bailout;
 3771     }
 3772     ptype = PLOT_FACTORIZED;
 3773     }
 3774 
 3775     /* special tics for time series plots */
 3776     if (gi.flags & GPT_TS) {
 3777     make_time_tics(&gi, dset, many, xlabel, prn);
 3778     }
 3779 
 3780     /* open file and, if that goes OK, dump the prn into it
 3781        after writing the header
 3782     */
 3783     fp = open_plot_input_file(ptype, gi.flags, &err);
 3784     if (err) {
 3785     gretl_print_destroy(prn);
 3786     goto bailout;
 3787     }
 3788 
 3789     fputs(gretl_print_get_buffer(prn), fp);
 3790     gretl_print_destroy(prn);
 3791 
 3792     print_axis_label('x', xlabel, fp);
 3793     fputs("set xzeroaxis\n", fp);
 3794     gnuplot_missval_string(fp);
 3795 
 3796     if (gi.flags & GPT_LOGY) {
 3797     fprintf(fp, "set logscale y %g\n", gi.ybase);
 3798     }
 3799 
 3800     /* key: default to left top */
 3801     strcpy(keystr, "set key left top\n");
 3802 
 3803     if (list[0] == 1) {
 3804     /* only one variable (time series) */
 3805     print_axis_label('y', series_get_graph_name(dset, list[1]), fp);
 3806     strcpy(keystr, "set nokey\n");
 3807     } else if (list[0] == 2) {
 3808     /* plotting two variables */
 3809     int no_key = 1;
 3810 
 3811     if (gi.flags & GPT_AUTO_FIT) {
 3812         print_auto_fit_string(gi.fit, fp);
 3813         if (gi.flags & GPT_FA) {
 3814         make_gtitle(&gi, GTITLE_AFV, series_get_graph_name(dset, list[1]),
 3815                 series_get_graph_name(dset, list[2]), fp);
 3816         } else {
 3817         make_gtitle(&gi, GTITLE_VLS, series_get_graph_name(dset, list[1]),
 3818                 xlabel, fp);
 3819         }
 3820         no_key = 0;
 3821     }
 3822     if (gi.flags & GPT_RESIDS && !(gi.flags & GPT_DUMMY)) {
 3823         const char *vlabel = series_get_label(dset, list[1]);
 3824 
 3825         make_gtitle(&gi, GTITLE_RESID, vlabel == NULL ? "residual" : vlabel,
 3826             NULL, fp);
 3827         fprintf(fp, "set ylabel '%s'\n", _("residual"));
 3828     } else {
 3829         print_axis_label('y', series_get_graph_name(dset, list[1]), fp);
 3830     }
 3831     if (no_key) {
 3832         strcpy(keystr, "set nokey\n");
 3833     }
 3834     } else if ((gi.flags & GPT_RESIDS) && (gi.flags & GPT_DUMMY)) {
 3835     const char *vlabel = series_get_label(dset, list[1]);
 3836 
 3837     make_gtitle(&gi, GTITLE_RESID, vlabel == NULL ? "residual" : vlabel,
 3838             NULL, fp);
 3839     fprintf(fp, "set ylabel '%s'\n", _("residual"));
 3840     } else if (gi.flags & GPT_FA) {
 3841     if (list[3] == dset->v - 1) {
 3842         /* x var is just time or index: is this always right? */
 3843         make_gtitle(&gi, GTITLE_AF, series_get_graph_name(dset, list[2]),
 3844             NULL, fp);
 3845     } else {
 3846         make_gtitle(&gi, GTITLE_AFV, series_get_graph_name(dset, list[2]),
 3847             series_get_graph_name(dset, list[3]), fp);
 3848     }
 3849     print_axis_label('y', series_get_graph_name(dset, list[2]), fp);
 3850     }
 3851 
 3852     if (many) {
 3853     strcpy(keystr, "set key outside\n");
 3854     }
 3855 
 3856     fputs(keystr, fp);
 3857 
 3858     gretl_push_c_numeric_locale();
 3859 
 3860     if (set_xrange) {
 3861     if (gi.x != NULL) {
 3862         print_x_range(&gi, fp);
 3863     } else {
 3864         print_x_range_from_list(&gi, dset, list, fp);
 3865     }
 3866     }
 3867 
 3868     if (!(gi.flags & GPT_TIMEFMT) && *gi.xfmt != '\0' && *gi.xtics != '\0') {
 3869     /* remedial handling of broken tics */
 3870     fprintf(fp, "set format x \"%s\"\n", gi.xfmt);
 3871     fprintf(fp, "set xtics %s\n", gi.xtics);
 3872     }
 3873 
 3874     if (gi.flags & GPT_Y2AXIS) {
 3875     check_for_yscale(&gi, (const double **) dset->Z, &oddman);
 3876     if (gi.flags & GPT_Y2AXIS) {
 3877         fputs("set ytics nomirror\n", fp);
 3878         fputs("set y2tics\n", fp);
 3879     }
 3880     } else if (gi.yformula == NULL && list[0] == 2) {
 3881     check_y_tics(&gi, (const double **) dset->Z, fp);
 3882     }
 3883 
 3884 #if GP_DEBUG
 3885     fprintf(stderr, "literal = '%s', yformula = '%s'\n", literal,
 3886         gi.yformula);
 3887 #endif
 3888 
 3889     if (gi.yformula != NULL) {
 3890     /* cut out the "dummy" yvar that is in fact represented
 3891        by a formula rather than raw data */
 3892     list[1] = list[2];
 3893     list[2] = list[3];
 3894     list[0] = 2;
 3895     } else {
 3896     print_gnuplot_literal_lines(literal, GNUPLOT, opt, fp);
 3897     }
 3898 
 3899     /* now print the 'plot' lines */
 3900     fputs("plot \\\n", fp);
 3901     if (gi.flags & GPT_Y2AXIS) {
 3902     /* using two y axes */
 3903     int lmax = list[0];
 3904 
 3905     if ((gi.flags & GPT_IDX) && list[lmax] != 0) {
 3906         lmax++;
 3907     }
 3908     for (i=1; i<lmax; i++) {
 3909         set_lwstr(dset, list[i], lwstr);
 3910         set_withstr(&gi, i, withstr);
 3911         fprintf(fp, " '-' using 1:2 axes %s title \"%s (%s)\" %s%s%s",
 3912             (i == oddman)? "x1y2" : "x1y1",
 3913             series_get_graph_name(dset, list[i]),
 3914             (i == oddman)? _("right") : _("left"),
 3915             withstr,
 3916             lwstr,
 3917             (i == lmax - 1)? "\n" : ", \\\n");
 3918     }
 3919     } else if (gi.flags & GPT_DUMMY) {
 3920     /* plot shows separation by discrete variable */
 3921     int nd = gretl_vector_get_length(gi.dvals);
 3922     int dv = list[3];
 3923     series_table *st;
 3924 
 3925     strcpy(s1, (gi.flags & GPT_RESIDS)? _("residual") :
 3926            series_get_graph_name(dset, list[1]));
 3927     strcpy(s2, series_get_graph_name(dset, dv));
 3928     st = series_get_string_table(dset, dv);
 3929 
 3930     for (i=0; i<nd; i++) {
 3931         double di = gretl_vector_get(gi.dvals, i);
 3932 
 3933         if (st != NULL) {
 3934         fprintf(fp, " '-' using 1:2 title \"%s (%s=%s)\" w points",
 3935             s1, s2, series_table_get_string(st, di));
 3936         } else {
 3937         fprintf(fp, " '-' using 1:2 title \"%s (%s=%g)\" w points",
 3938             s1, s2, di);
 3939         }
 3940         if (i < nd - 1) {
 3941         fputs(", \\\n", fp);
 3942         } else {
 3943         fputc('\n', fp);
 3944         }
 3945     }
 3946     } else if (gi.yformula != NULL) {
 3947     /* we have a formula to plot, not just data */
 3948     fprintf(fp, " '-' using 1:2 title \"%s\" w points, \\\n", _("actual"));
 3949     fprintf(fp, "%s title '%s' w lines\n", gi.yformula, _("fitted"));
 3950     } else if (gi.flags & GPT_FA) {
 3951     /* this is a fitted vs actual plot */
 3952     /* try reversing here: 2014-09-22 */
 3953     int tmp = list[1];
 3954 
 3955     list[1] = list[2];
 3956     list[2] = tmp;
 3957     set_withstr(&gi, 1, withstr);
 3958     fprintf(fp, " '-' using 1:2 title \"%s\" %s, \\\n", _("actual"), withstr);
 3959     fprintf(fp, " '-' using 1:2 title \"%s\" %s\n", _("fitted"), withstr);
 3960     } else {
 3961     /* all other cases */
 3962     int lmax = list[0] - 1;
 3963 
 3964     for (i=1; i<=lmax; i++)  {
 3965         set_lwstr(dset, list[i], lwstr);
 3966         if (list[0] == 2 && !(gi.flags & GPT_TIMEFMT)) {
 3967         *s1 = '\0';
 3968         } else {
 3969         strcpy(s1, series_get_graph_name(dset, list[i]));
 3970         }
 3971         set_withstr(&gi, i, withstr);
 3972         fprintf(fp, " '-' using 1:2 title \"%s\" %s%s", s1, withstr, lwstr);
 3973         if (i < lmax || (gi.flags & GPT_AUTO_FIT)) {
 3974             fputs(", \\\n", fp);
 3975         } else {
 3976             fputc('\n', fp);
 3977         }
 3978     }
 3979     }
 3980 
 3981     if (*fit_line != '\0') {
 3982         fputs(fit_line, fp);
 3983     }
 3984 
 3985     /* print the data to be graphed */
 3986     if (gi.flags & GPT_DUMMY) {
 3987     print_gp_dummy_data(&gi, dset, fp);
 3988     } else {
 3989     print_gp_data(&gi, dset, fp);
 3990     }
 3991 
 3992     gretl_pop_c_numeric_locale();
 3993 
 3994     err = finalize_plot_input_file(fp);
 3995 
 3996  bailout:
 3997 
 3998     clear_gpinfo(&gi);
 3999 
 4000     return err;
 4001 }
 4002 
 4003 int theil_forecast_plot (const int *plotlist, const DATASET *dset,
 4004              gretlopt opt)
 4005 {
 4006     FILE *fp = NULL;
 4007     gnuplot_info gi;
 4008     int vx, vy;
 4009     int err = 0;
 4010 
 4011     gretl_error_clear();
 4012 
 4013     if (plotlist[0] != 2) {
 4014     return E_DATA;
 4015     }
 4016 
 4017     err = gpinfo_init(&gi, opt | OPT_S, plotlist, NULL, dset);
 4018     if (err) {
 4019     goto bailout;
 4020     }
 4021 
 4022     /* ensure the time-series flag is unset */
 4023     gi.flags &= ~GPT_TS;
 4024 
 4025     err = graph_list_adjust_sample(gi.list, &gi, dset, 1);
 4026     if (err) {
 4027     goto bailout;
 4028     }
 4029 
 4030     fp = open_plot_input_file(PLOT_REGULAR, gi.flags, &err);
 4031     if (err) {
 4032     goto bailout;
 4033     }
 4034 
 4035     vx = gi.list[2];
 4036     vy = gi.list[1];
 4037 
 4038     print_axis_label('x', series_get_graph_name(dset, vx), fp);
 4039     print_axis_label('y', series_get_graph_name(dset, vy), fp);
 4040 
 4041     fputs("set xzeroaxis\n", fp);
 4042     gnuplot_missval_string(fp);
 4043     fputs("set key left top\n", fp);
 4044 
 4045     gretl_push_c_numeric_locale();
 4046 
 4047     print_x_range_from_list(&gi, dset, gi.list, fp);
 4048 
 4049     fputs("plot \\\n", fp);
 4050     fputs(" '-' using 1:2 notitle w points, \\\n", fp);
 4051     fprintf(fp, " x title \"%s\" w lines\n", _("actual = predicted"));
 4052 
 4053     print_gp_data(&gi, dset, fp);
 4054 
 4055     gretl_pop_c_numeric_locale();
 4056 
 4057     err = finalize_plot_input_file(fp);
 4058 
 4059  bailout:
 4060 
 4061     clear_gpinfo(&gi);
 4062 
 4063     return err;
 4064 }
 4065 
 4066 /* Try to determine a suitable tic-increment for the automatic
 4067    x-axis in the "scatters" context". We don't want the
 4068    increment to be so small that the tic labels pile up on
 4069    each other.
 4070 */
 4071 
 4072 static int scatters_incr (int T, const DATASET *dset)
 4073 {
 4074     int incr, ntics;
 4075 
 4076     if (dset->pd == 1) {
 4077     incr = T / 6;
 4078     } else {
 4079     incr = T / (4 * dset->pd);
 4080     /* safeguard */
 4081     if (incr == 0) {
 4082         incr = 1;
 4083     }
 4084     }
 4085 
 4086     ntics = T / incr;
 4087     if (ntics > 10) {
 4088     ntics = 10;
 4089     incr = T / ntics;
 4090     }
 4091 
 4092     return incr;
 4093 }
 4094 
 4095 static void scatters_set_timefmt (const DATASET *dset,
 4096                   const double *obs,
 4097                   FILE *fp)
 4098 {
 4099     double T = obs[dset->t2] - obs[dset->t1];
 4100     int ntics = 6;
 4101 
 4102     fputs("set xdata time\n", fp);
 4103     fputs("set timefmt \"%s\"\n", fp);
 4104     if (single_year_sample(dset, dset->t1, dset->t2)) {
 4105     fputs("set format x \"%m-%d\"\n", fp);
 4106     } else {
 4107     fputs("set format x \"%Y-%m-%d\"\n", fp);
 4108     }
 4109     fputs("set xtics rotate by -45\n", fp);
 4110     fprintf(fp, "set xtics %g\n", round(T/ntics));
 4111 }
 4112 
 4113 /**
 4114  * multi_scatters:
 4115  * @list: list of variables to plot, by ID number.
 4116  * @dset: dataset struct.
 4117  * @opt: can include %OPT_O to use lines, %OPT_U to
 4118  * direct output to a named file.
 4119  *
 4120  * Writes a gnuplot plot file to display up to 16 small graphs
 4121  * based on the variables in @list, and calls gnuplot to make
 4122  * the graph.
 4123  *
 4124  * Returns: 0 on successful completion, error code on error.
 4125  */
 4126 
 4127 int multi_scatters (const int *list, const DATASET *dset,
 4128             gretlopt opt)
 4129 {
 4130     GptFlags flags = 0;
 4131     int xvar = 0, yvar = 0;
 4132     const double *x = NULL;
 4133     const double *y = NULL;
 4134     const double *obs = NULL;
 4135     int rows, cols, tseries = 0;
 4136     int use_timefmt = 0;
 4137     int *plotlist = NULL;
 4138     int pos, nplots = 0;
 4139     FILE *fp = NULL;
 4140     int i, t, err = 0;
 4141 
 4142     if (opt & OPT_O) {
 4143     flags |= GPT_LINES;
 4144     }
 4145 
 4146     pos = gretl_list_separator_position(list);
 4147 
 4148     if (pos == 0) {
 4149     /* plot against time or index */
 4150     plotlist = gretl_list_copy(list);
 4151     flags |= GPT_LINES;
 4152     if (dataset_is_time_series(dset)) {
 4153         tseries = 1;
 4154         if (calendar_data(dset)) {
 4155         use_timefmt = 1;
 4156         }
 4157     }
 4158     obs = gretl_plotx(dset, use_timefmt ? OPT_T : OPT_S);
 4159     if (obs == NULL) {
 4160         return E_ALLOC;
 4161     }
 4162     } else if (pos > 2) {
 4163     /* plot several yvars against one xvar */
 4164     plotlist = gretl_list_new(pos - 1);
 4165     xvar = list[list[0]];
 4166     x = dset->Z[xvar];
 4167     } else {
 4168     /* plot one yvar against several xvars */
 4169     plotlist = gretl_list_new(list[0] - pos);
 4170     yvar = list[1];
 4171     y = dset->Z[yvar];
 4172     }
 4173 
 4174     if (plotlist == NULL) {
 4175     return E_ALLOC;
 4176     }
 4177 
 4178     if (yvar) {
 4179     for (i=1; i<=plotlist[0]; i++) {
 4180         plotlist[i] = list[i + pos];
 4181     }
 4182     } else if (xvar) {
 4183     for (i=1; i<pos; i++) {
 4184         plotlist[i] = list[i];
 4185     }
 4186     }
 4187 
 4188     /* max 16 plots */
 4189     if (plotlist[0] > 16) {
 4190     plotlist[0] = 16;
 4191     }
 4192 
 4193     nplots = plotlist[0];
 4194 
 4195     if (nplots > 1) {
 4196     get_multiplot_layout(nplots, tseries, &rows, &cols);
 4197     if (use_timefmt) {
 4198         gp_small_font_size = nplots > 2 ? 6 : 0;
 4199     } else {
 4200         maybe_set_small_font(nplots);
 4201     }
 4202     if (nplots > 12) {
 4203         flags |= GPT_XXL;
 4204     } else if (nplots > 9) {
 4205         flags |= GPT_XL;
 4206     }
 4207     }
 4208 
 4209     fp = open_plot_input_file(PLOT_MULTI_SCATTER, flags, &err);
 4210     if (err) {
 4211     return err;
 4212     }
 4213 
 4214     if (nplots > 1) {
 4215     fprintf(fp, "set multiplot layout %d,%d\n", rows, cols);
 4216     }
 4217     fputs("set nokey\n", fp);
 4218 
 4219     if (opt & OPT_K) {
 4220     /* --tweaks=foo */
 4221     print_gnuplot_literal_lines(NULL, SCATTERS, opt, fp);
 4222     }
 4223 
 4224     gretl_push_c_numeric_locale();
 4225 
 4226     if (use_timefmt) {
 4227     fprintf(fp, "set xrange [%.12g:%.12g]\n", obs[dset->t1], obs[dset->t2]);
 4228     scatters_set_timefmt(dset, obs, fp);
 4229     } else if (obs != NULL) {
 4230     double startdate = obs[dset->t1];
 4231     double enddate = obs[dset->t2];
 4232     int incr, T = dset->t2 - dset->t1 + 1;
 4233 
 4234     fprintf(fp, "set xrange [%g:%g]\n", floor(startdate), ceil(enddate));
 4235     incr = scatters_incr(T, dset);
 4236     if (incr > 0) {
 4237         fprintf(fp, "set xtics %g, %d\n", ceil(startdate), incr);
 4238     }
 4239     } else {
 4240     /* avoid having points sticking to the axes */
 4241     fputs("set offsets graph 0.02, graph 0.02, graph 0.02, graph 0.02\n", fp);
 4242     fputs("set noxtics\nset noytics\n", fp);
 4243     }
 4244 
 4245     fputs("set xzeroaxis\n", fp);
 4246 
 4247     for (i=0; i<nplots; i++) {
 4248     int j = plotlist[i+1];
 4249 
 4250     if (obs != NULL) {
 4251         fputs("set noxlabel\n", fp);
 4252         fputs("set noylabel\n", fp);
 4253         fprintf(fp, "set title '%s'\n", series_get_graph_name(dset, j));
 4254     } else {
 4255         fprintf(fp, "set xlabel '%s'\n",
 4256             (yvar)? dset->varname[j] :
 4257             dset->varname[xvar]);
 4258         fprintf(fp, "set ylabel '%s'\n",
 4259             (yvar)? dset->varname[yvar] :
 4260             dset->varname[j]);
 4261     }
 4262     fputs("plot '-' using 1:2", fp);
 4263     if (flags & GPT_LINES) {
 4264         fputs(" with lines", fp);
 4265     }
 4266     fputc('\n', fp);
 4267 
 4268     for (t=dset->t1; t<=dset->t2; t++) {
 4269         double xt = yvar ? dset->Z[j][t] : xvar ? x[t] : obs[t];
 4270         double yt = yvar ? y[t] : dset->Z[j][t];
 4271 
 4272         write_gp_dataval(xt, fp, 0);
 4273         write_gp_dataval(yt, fp, 1);
 4274     }
 4275     fputs("e\n", fp);
 4276     }
 4277 
 4278     gretl_pop_c_numeric_locale();
 4279 
 4280     if (nplots > 1) {
 4281     fputs("unset multiplot\n", fp);
 4282     }
 4283 
 4284     free(plotlist);
 4285 
 4286     return finalize_plot_input_file(fp);
 4287 }
 4288 
 4289 static int matrix_plotx_ok (const gretl_matrix *m, const DATASET *dset,
 4290                 int *pt1, int *pt2, int *ppd)
 4291 {
 4292     if (dset == NULL) {
 4293     return 0;
 4294     } else if (m->rows == dset->n) {
 4295     return 1;
 4296     } else {
 4297     int t1 = gretl_matrix_get_t1(m);
 4298     int t2 = gretl_matrix_get_t2(m);
 4299 
 4300     if (t2 > t1 && t2 < dset->n) {
 4301         *pt1 = t1;
 4302         *pt2 = t2;
 4303         *ppd = dset->pd;
 4304         return 1;
 4305     }
 4306     }
 4307 
 4308     return 0;
 4309 }
 4310 
 4311 static const double *matrix_col (const gretl_matrix *m, int j)
 4312 {
 4313     const double *x = m->val;
 4314 
 4315     return x + (j-1) * m->rows;
 4316 }
 4317 
 4318 static void plot_colname (char *s, const char **colnames, int j)
 4319 {
 4320     if (colnames != NULL) {
 4321     const char *name = colnames[j-1];
 4322 
 4323     *s = '\0';
 4324     if (strlen(name) >= 16) {
 4325         strncat(s, name, 14);
 4326         strcat(s, "~");
 4327     } else {
 4328         strncat(s, name, 15);
 4329     }
 4330     } else {
 4331     sprintf(s, "col %d", j);
 4332     }
 4333 }
 4334 
 4335 static double get_obsx (const double *obs, int t, int s)
 4336 {
 4337     return (obs != NULL)? obs[t] : s;
 4338 }
 4339 
 4340 /**
 4341  * matrix_scatters:
 4342  * @m: matrix containing data to plot.
 4343  * @list: list of columns to plot, or NULL.
 4344  * @dset: dataset pointer, or NULL.
 4345  * @opt: can include %OPT_O to use lines, %OPT_U to
 4346  * direct output to a named file.
 4347  *
 4348  * Writes a gnuplot plot file to display up to 16 small graphs
 4349  * based on the data in @m, and calls gnuplot to make
 4350  * the graph.
 4351  *
 4352  * Returns: 0 on successful completion, error code on error.
 4353  */
 4354 
 4355 int matrix_scatters (const gretl_matrix *m, const int *list,
 4356              const DATASET *dset, gretlopt opt)
 4357 {
 4358     GptFlags flags = 0;
 4359     const double *x = NULL;
 4360     const double *y = NULL;
 4361     const double *obs = NULL;
 4362     const char **colnames = NULL;
 4363     FILE *fp = NULL;
 4364     int *plotlist = NULL;
 4365     int need_list = 0;
 4366     int rows, cols;
 4367     int xcol = 0, ycol = 0;
 4368     int t1 = 0, t2 = 0, pd = 1;
 4369     int pos = 0, nplots = 0;
 4370     int simple_obs = 0;
 4371     int i, t, err = 0;
 4372 
 4373     if (gretl_is_null_matrix(m)) {
 4374     return E_DATA;
 4375     }
 4376 
 4377     if (opt & OPT_O) {
 4378     flags |= GPT_LINES;
 4379     }
 4380 
 4381     if (list != NULL) {
 4382     for (i=1; i<=list[0]; i++) {
 4383         if (list[i] == LISTSEP) {
 4384         pos = i;
 4385         } else if (list[i] < 1 || list[i] > m->cols) {
 4386         err = E_INVARG;
 4387         break;
 4388         }
 4389     }
 4390     }
 4391 
 4392     if (err) {
 4393     return err;
 4394     }
 4395 
 4396     t1 = 0;
 4397     t2 = m->rows - 1;
 4398 
 4399     if (pos == 0) {
 4400     /* plot against time or index */
 4401     if (matrix_plotx_ok(m, dset, &t1, &t2, &pd)) {
 4402         obs = gretl_plotx(dset, OPT_NONE);
 4403         if (obs == NULL) {
 4404         return E_ALLOC;
 4405         }
 4406     } else {
 4407         simple_obs = 1;
 4408     }
 4409     if (list != NULL && list[0] > 0) {
 4410         need_list = 1;
 4411         plotlist = gretl_list_copy(list);
 4412     }
 4413     flags |= GPT_LINES;
 4414     } else if (pos > 2) {
 4415     /* plot several yvars against one xvar */
 4416     need_list = 1;
 4417     plotlist = gretl_list_new(pos - 1);
 4418     xcol = list[list[0]];
 4419     x = matrix_col(m, xcol);
 4420     } else {
 4421     /* plot one yvar against several xvars */
 4422     need_list = 1;
 4423     plotlist = gretl_list_new(list[0] - pos);
 4424     ycol = list[1];
 4425     y = matrix_col(m, ycol);
 4426     }
 4427 
 4428     if (need_list && plotlist == NULL) {
 4429     return E_ALLOC;
 4430     }
 4431 
 4432     if (plotlist != NULL) {
 4433     if (y != NULL) {
 4434         for (i=1; i<=plotlist[0]; i++) {
 4435         plotlist[i] = list[i + pos];
 4436         }
 4437     } else if (x != NULL) {
 4438         for (i=1; i<pos; i++) {
 4439         plotlist[i] = list[i];
 4440         }
 4441     }
 4442     /* max 16 plots */
 4443     if (plotlist[0] > 16) {
 4444         plotlist[0] = 16;
 4445     }
 4446     nplots = plotlist[0];
 4447     } else {
 4448     nplots = (m->cols > 16)? 16 : m->cols;
 4449     }
 4450 
 4451     get_multiplot_layout(nplots, 0, &rows, &cols);
 4452     maybe_set_small_font(nplots);
 4453 
 4454     if (nplots > 12) {
 4455     flags |= GPT_XXL;
 4456     } else if (nplots > 9) {
 4457     flags |= GPT_XL;
 4458     }
 4459 
 4460     fp = open_plot_input_file(PLOT_MULTI_SCATTER, flags, &err);
 4461     if (err) {
 4462     return err;
 4463     }
 4464 
 4465     colnames = gretl_matrix_get_colnames(m);
 4466 
 4467     fprintf(fp, "set multiplot layout %d,%d\n", rows, cols);
 4468     fputs("set xzeroaxis\n", fp);
 4469     fputs("set nokey\n", fp);
 4470 
 4471     gretl_push_c_numeric_locale();
 4472 
 4473     if (obs != NULL) {
 4474     double startdate = obs[t1];
 4475     double enddate = obs[t2];
 4476     int incr, T = t2 - t1 + 1;
 4477 
 4478     fprintf(fp, "set xrange [%g:%g]\n", floor(startdate), ceil(enddate));
 4479 
 4480     incr = (pd == 1)? (T / 6) : (T / (4 * pd));
 4481     if (incr > 0) {
 4482         fprintf(fp, "set xtics %g, %d\n", ceil(startdate), incr);
 4483     }
 4484     } else if (simple_obs) {
 4485     int incr = m->rows / 6;
 4486 
 4487     fprintf(fp, "set xrange [0:%d]\n", m->rows - 1);
 4488     if (incr > 0) {
 4489         fprintf(fp, "set xtics 0, %d\n", incr);
 4490     }
 4491     } else {
 4492     fputs("set noxtics\nset noytics\n", fp);
 4493     }
 4494 
 4495     if (obs != NULL || simple_obs) {
 4496     fputs("set noxlabel\n", fp);
 4497     fputs("set noylabel\n", fp);
 4498     }
 4499 
 4500     for (i=0; i<nplots; i++) {
 4501     int j = (plotlist == NULL)? (i+1) : plotlist[i+1];
 4502     const double *zj = matrix_col(m, j);
 4503     char label[16];
 4504 
 4505     if (obs != NULL || simple_obs) {
 4506         plot_colname(label, colnames, j);
 4507         fprintf(fp, "set title '%s'\n", label);
 4508     } else {
 4509         plot_colname(label, colnames, (y != NULL)? j : xcol);
 4510         fprintf(fp, "set xlabel '%s'\n", label);
 4511         plot_colname(label, colnames, (y != NULL)? ycol : j);
 4512         fprintf(fp, "set ylabel '%s'\n", label);
 4513     }
 4514 
 4515     fputs("plot '-' using 1:2", fp);
 4516     if (flags & GPT_LINES) {
 4517         fputs(" with lines", fp);
 4518     }
 4519     fputc('\n', fp);
 4520 
 4521     for (t=t1; t<=t2; t++) {
 4522         int s = t - t1;
 4523         double xt = ycol ? zj[s] : xcol ? x[s] : get_obsx(obs, t, s);
 4524         double yt = (y != NULL)? y[s] : zj[s];
 4525 
 4526         write_gp_dataval(xt, fp, 0);
 4527         write_gp_dataval(yt, fp, 1);
 4528     }
 4529     fputs("e\n", fp);
 4530     }
 4531 
 4532     gretl_pop_c_numeric_locale();
 4533 
 4534     fputs("unset multiplot\n", fp);
 4535 
 4536     free(plotlist);
 4537 
 4538     return finalize_plot_input_file(fp);
 4539 }
 4540 
 4541 static FILE *get_3d_input_file (int *err)
 4542 {
 4543     FILE *fp = NULL;
 4544     char fname[MAXLEN];
 4545 
 4546     sprintf(fname, "%sgpttmp.plt", gretl_dotdir());
 4547     fp = gretl_fopen(fname, "w");
 4548 
 4549     if (fp == NULL) {
 4550     *err = E_FOPEN;
 4551     } else {
 4552     set_gretl_plotfile(fname);
 4553     }
 4554 
 4555     return fp;
 4556 }
 4557 
 4558 static gchar *maybe_get_surface (const int *list,
 4559                  DATASET *dset,
 4560                  gretlopt opt)
 4561 {
 4562     MODEL smod;
 4563     double umin, umax, vmin, vmax;
 4564     int olslist[5];
 4565     gchar *ret = NULL;
 4566 
 4567     olslist[0] = 4;
 4568     olslist[1] = list[3];
 4569     olslist[2] = 0;
 4570     olslist[3] = list[2];
 4571     olslist[4] = list[1];
 4572 
 4573     gretl_minmax(dset->t1, dset->t2, dset->Z[list[2]], &umin, &umax);
 4574     gretl_minmax(dset->t1, dset->t2, dset->Z[list[1]], &vmin, &vmax);
 4575 
 4576     smod = lsq(olslist, dset, OLS, OPT_A);
 4577 
 4578     if (!smod.errcode && !na(smod.fstt) &&
 4579     (snedecor_cdf_comp(smod.dfn, smod.dfd, smod.fstt) < .10 || (opt & OPT_A))) {
 4580     double uadj = (umax - umin) * 0.02;
 4581     double vadj = (vmax - vmin) * 0.02;
 4582 
 4583     ret = g_strdup_printf("[u=%g:%g] [v=%g:%g] "
 4584                   "%g+(%g)*u+(%g)*v notitle",
 4585                   umin - uadj, umax + uadj,
 4586                   vmin - vadj, vmax + vadj,
 4587                   smod.coeff[0], smod.coeff[1],
 4588                   smod.coeff[2]);
 4589     }
 4590 
 4591     clear_model(&smod);
 4592 
 4593     return ret;
 4594 }
 4595 
 4596 /**
 4597  * gnuplot_3d:
 4598  * @list: list of variables to plot, by ID number: Y, X, Z
 4599  * @literal: literal command(s) to pass to gnuplot (or NULL)
 4600  * @dset: pointer to dataset.
 4601  * @opt: may include OPT_A to force display of fitted surface;
 4602  * may include OPT_I to force an interactive (rotatable) plot.
 4603  * Note that OPT_I may be removed on output if a suitable
 4604  * gnuplot terminal is not present.
 4605  *
 4606  * Writes a gnuplot plot file to display a 3D plot (Z on
 4607  * the vertical axis, X and Y on base plane).
 4608  *
 4609  * Returns: 0 on successful completion, error code on error.
 4610  */
 4611 
 4612 int gnuplot_3d (int *list, const char *literal,
 4613         DATASET *dset, gretlopt *opt)
 4614 {
 4615     FILE *fp = NULL;
 4616     int t, t1 = dset->t1, t2 = dset->t2;
 4617     int save_t1 = dset->t1, save_t2 = dset->t2;
 4618     int lo = list[0];
 4619     int datlist[4];
 4620     int interactive = (*opt & OPT_I);
 4621     const char *term = NULL;
 4622     gchar *surface = NULL;
 4623     int err = 0;
 4624 
 4625     if (lo != 3) {
 4626     fprintf(stderr, "gnuplot_3d needs three variables (only)\n");
 4627     return E_DATA;
 4628     }
 4629 
 4630     list_adjust_sample(list, &t1, &t2, dset, NULL);
 4631 
 4632     /* if resulting sample range is empty, complain */
 4633     if (t1 >= t2) {
 4634     return E_MISSDATA;
 4635     }
 4636 
 4637 #ifndef WIN32
 4638     if (interactive) {
 4639     /* On Windows we let the gnuplot terminal default to
 4640        "win"; on other systems we need a suitable
 4641        terminal for interactive 3-D display.
 4642     */
 4643     if (gnuplot_has_wxt()) {
 4644         term = "wxt size 640,420 noenhanced";
 4645     } else if (gnuplot_has_x11()) {
 4646         term = "x11";
 4647     } else if (gnuplot_has_qt()) {
 4648         term = "qt";
 4649     } else {
 4650         *opt &= ~OPT_I;
 4651         interactive = 0;
 4652     }
 4653     }
 4654 #endif
 4655 
 4656     if (interactive) {
 4657     fp = get_3d_input_file(&err);
 4658     } else {
 4659     fp = open_plot_input_file(PLOT_3D, 0, &err);
 4660     }
 4661 
 4662     if (err) {
 4663     return err;
 4664     }
 4665 
 4666     dset->t1 = t1;
 4667     dset->t2 = t2;
 4668 
 4669     if (interactive) {
 4670     if (term != NULL) {
 4671         fprintf(fp, "set term %s\n", term);
 4672     }
 4673     write_plot_line_styles(PLOT_3D, fp);
 4674     }
 4675 
 4676     gretl_push_c_numeric_locale();
 4677 
 4678     print_axis_label('x', series_get_graph_name(dset, list[2]), fp);
 4679     print_axis_label('y', series_get_graph_name(dset, list[1]), fp);
 4680     print_axis_label('z', series_get_graph_name(dset, list[3]), fp);
 4681 
 4682     gnuplot_missval_string(fp);
 4683 
 4684     print_gnuplot_literal_lines(literal, GNUPLOT, *opt, fp);
 4685 
 4686     surface = maybe_get_surface(list, dset, *opt);
 4687 
 4688     if (surface != NULL) {
 4689     fprintf(fp, "splot %s, \\\n'-' notitle w p\n", surface);
 4690     g_free(surface);
 4691     } else {
 4692     fputs("splot '-' notitle w p\n", fp);
 4693     }
 4694 
 4695     datlist[0] = 3;
 4696     datlist[1] = list[2];
 4697     datlist[2] = list[1];
 4698     datlist[3] = list[3];
 4699 
 4700     for (t=t1; t<=t2; t++) {
 4701     const char *label = NULL;
 4702 
 4703     if (dset->markers) {
 4704         label = dset->S[t];
 4705     }
 4706     printvars(fp, t, datlist, dset, NULL, label, 0.0);
 4707     }
 4708     fputs("e\n", fp);
 4709 
 4710     gretl_pop_c_numeric_locale();
 4711 
 4712     dset->t1 = save_t1;
 4713     dset->t2 = save_t2;
 4714 
 4715     if (interactive) {
 4716     fputs("pause mouse close\n", fp);
 4717     fclose(fp);
 4718     } else {
 4719     err = finalize_plot_input_file(fp);
 4720     }
 4721 
 4722     return err;
 4723 }
 4724 
 4725 /**
 4726  * open_3d_plot_input_file:
 4727  * @iact: on input, non-zero if an interactive plot is
 4728  * preferred, 0 otherwise; on output, non-zero if interactive
 4729  * status can be supported, 0 otherwise.
 4730  *
 4731  * Writes a gnuplot plot file to display a 3D plot
 4732  * (interactive if requested and feasible).
 4733  *
 4734  * Returns: FILE pointer on success, NULL on error.
 4735  */
 4736 
 4737 FILE *open_3d_plot_input_file (int *iact)
 4738 {
 4739     const char *term = NULL;
 4740     FILE *fp = NULL;
 4741     int err = 0;
 4742 
 4743     if (*iact != 0) {
 4744 #ifndef WIN32
 4745     /* On Windows we let the gnuplot terminal default to
 4746        "win"; on other operating systems we need a suitable
 4747        terminal for interactive 3-D display.
 4748     */
 4749     if (gnuplot_has_wxt()) {
 4750         term = "wxt size 640,420 noenhanced";
 4751     } else if (gnuplot_has_x11()) {
 4752         term = "x11";
 4753     } else if (gnuplot_has_qt()) {
 4754         term = "qt";
 4755     } else {
 4756         /* can't do it? */
 4757         *iact = 0;
 4758     }
 4759 #endif
 4760     }
 4761 
 4762     if (*iact != 0) {
 4763     fp = get_3d_input_file(&err);
 4764     } else {
 4765     fp = open_plot_input_file(PLOT_3D, 0, &err);
 4766     }
 4767 
 4768     if (*iact) {
 4769     if (term != NULL) {
 4770         fprintf(fp, "set term %s\n", term);
 4771     }
 4772     write_plot_line_styles(PLOT_3D, fp);
 4773     }
 4774 
 4775     return fp;
 4776 }
 4777 
 4778 static void print_freq_test_label (char *s, int teststat,
 4779                    double v, double pv)
 4780 {
 4781     gretl_pop_c_numeric_locale();
 4782     if (teststat == GRETL_STAT_Z) {
 4783     sprintf(s, "z = %.3f [%.4f]", v, pv);
 4784     } else if (teststat == GRETL_STAT_NORMAL_CHISQ) {
 4785     sprintf(s, "%s(2) = %.3f [%.4f]", _("Chi-square"), v, pv);
 4786     }
 4787     gretl_push_c_numeric_locale();
 4788 }
 4789 
 4790 static void print_freq_dist_label (char *s, int dist, double x, double y)
 4791 {
 4792     int dcomma = 0;
 4793     char test[8];
 4794 
 4795     gretl_pop_c_numeric_locale();
 4796 
 4797     sprintf(test, "%g", 0.5);
 4798     if (strchr(test, ',')) {
 4799     dcomma = 1;
 4800     }
 4801 
 4802     if (dist == D_NORMAL) {
 4803     sprintf(s, "N(%.5g%c%.5g)", x,
 4804         ((dcomma)? ' ' : ','), y);
 4805     } else if (dist == D_GAMMA) {
 4806     sprintf(s, "gamma(%.5g%c%.5g)", x,
 4807         ((dcomma)? ' ' : ','), y);
 4808     }
 4809 
 4810     gretl_push_c_numeric_locale();
 4811 }
 4812 
 4813 /* Below: a fix for the case where the y-range is by default
 4814    degenerate, in which case gnuplot produces a graph OK, but
 4815    issues a warning and returns non-zero.
 4816 */
 4817 
 4818 static void maybe_set_yrange (FreqDist *freq, double lambda, FILE *fp)
 4819 {
 4820     double ymin = 1.0e+200;
 4821     double ymax = -1.0e+200;
 4822     int i;
 4823 
 4824     for (i=0; i<freq->numbins; i++) {
 4825     if (freq->f[i] > ymax) {
 4826         ymax = freq->f[i];
 4827     }
 4828     if (freq->f[i] < ymin) {
 4829         ymin = freq->f[i];
 4830     }
 4831     }
 4832 
 4833     if (ymax == ymin) {
 4834     fprintf(fp, "set yrange [%.10g:%.10g]\n", ymax * lambda * 0.99,
 4835         ymax * lambda * 1.01);
 4836     } else {
 4837     fprintf(fp, "set yrange [0.0:%.10g]\n", ymax * lambda * 1.1);
 4838     }
 4839 }
 4840 
 4841 static double discrete_minskip (FreqDist *freq)
 4842 {
 4843     double s, ms = freq->midpt[1] - freq->midpt[0];
 4844     int i;
 4845 
 4846     for (i=2; i<freq->numbins; i++) {
 4847     s = freq->midpt[i] - freq->midpt[i-1];
 4848     if (s < ms) {
 4849         ms = s;
 4850     }
 4851     }
 4852 
 4853     return ms;
 4854 }
 4855 
 4856 /**
 4857  * plot_freq:
 4858  * @freq: pointer to frequency distribution struct.
 4859  * @dist: probability distribution code.
 4860  *
 4861  * Plot the actual frequency distribution for a variable versus a
 4862  * theoretical distribution: Gaussian, gamma or none.
 4863  *
 4864  * Returns: 0 on successful completion, error code on error.
 4865  */
 4866 
 4867 int plot_freq (FreqDist *freq, DistCode dist, gretlopt opt)
 4868 {
 4869     double alpha = 0.0, beta = 0.0, lambda = 1.0;
 4870     FILE *fp = NULL;
 4871     int i, K = freq->numbins;
 4872     char withstr[32] = {0};
 4873     char label[80] = {0};
 4874     double plotmin = 0.0, plotmax = 0.0;
 4875     double barwidth;
 4876     const double *endpt;
 4877     int plottype, use_boxes = 1;
 4878     char **S = NULL;
 4879     int ns = 0, real_ns = 0;
 4880     int err = 0;
 4881 
 4882     if (K == 0) {
 4883     return E_DATA;
 4884     }
 4885 
 4886     if (K == 1) {
 4887     gretl_errmsg_sprintf(_("'%s' is a constant"), freq->varname);
 4888     return E_DATA;
 4889     }
 4890 
 4891     if (dist == D_NORMAL) {
 4892     plottype = PLOT_FREQ_NORMAL;
 4893     } else if (dist == D_GAMMA) {
 4894     plottype = PLOT_FREQ_GAMMA;
 4895     } else {
 4896     plottype = PLOT_FREQ_SIMPLE;
 4897     }
 4898 
 4899     fp = open_plot_input_file(plottype, 0, &err);
 4900     if (err) {
 4901     return err;
 4902     }
 4903 
 4904 #if GP_DEBUG
 4905     fprintf(stderr, "*** plot_freq called\n");
 4906 #endif
 4907 
 4908     if (freq->discrete) {
 4909     endpt = freq->midpt;
 4910     barwidth = discrete_minskip(freq);
 4911     use_boxes = 0;
 4912     } else {
 4913     /* equally sized bins, width to be determined */
 4914     endpt = freq->endpt;
 4915     barwidth = freq->endpt[K-1] - freq->endpt[K-2];
 4916     }
 4917 
 4918     S = literal_strings_from_opt(FREQ, &ns, &real_ns);
 4919 
 4920     gretl_push_c_numeric_locale();
 4921 
 4922     if (dist) {
 4923     int nlit = 2 + 2 * (!na(freq->test)) + real_ns;
 4924 
 4925     lambda = 1.0 / (freq->n * barwidth);
 4926 
 4927     if (dist == D_NORMAL) {
 4928         fprintf(fp, "# literal lines = %d\n", nlit);
 4929         fprintf(fp, "sigma = %g\n", freq->sdx);
 4930         fprintf(fp, "mu = %g\n", freq->xbar);
 4931 
 4932         plotmin = endpt[0] - barwidth;
 4933         if (plotmin > freq->xbar - 3.3 * freq->sdx) {
 4934         plotmin = freq->xbar - 3.3 * freq->sdx;
 4935         }
 4936 
 4937         plotmax = endpt[K-1] + barwidth;
 4938         if (plotmax < freq->xbar + 3.3 * freq->sdx) {
 4939         plotmax = freq->xbar + 3.3 * freq->sdx;
 4940         }
 4941 
 4942         if (!na(freq->test)) {
 4943         fprintf(fp, "set label \"%s:\" at graph .03, graph .97 front\n",
 4944             _("Test statistic for normality"));
 4945         print_freq_test_label(label, GRETL_STAT_NORMAL_CHISQ, freq->test,
 4946                       chisq_cdf_comp(2, freq->test));
 4947         fprintf(fp, "set label '%s' at graph .03, graph .93 front\n",
 4948             label);
 4949         }
 4950 
 4951         if (real_ns > 0) {
 4952         print_extra_literal_lines(S, ns, fp);
 4953         }
 4954     } else if (dist == D_GAMMA) {
 4955         double var = freq->sdx * freq->sdx;
 4956 
 4957         /* scale param = variance/mean */
 4958         beta = var / freq->xbar;
 4959         /* shape param = mean/scale */
 4960         alpha = freq->xbar / beta;
 4961 
 4962         fprintf(fp, "# literal lines = %d\n", nlit);
 4963         fprintf(fp, "beta = %g\n", beta);
 4964         fprintf(fp, "alpha = %g\n", alpha);
 4965         plotmin = 0.0;
 4966         plotmax = freq->xbar + 4.0 * freq->sdx;
 4967 
 4968         if (!na(freq->test)) {
 4969         fprintf(fp, "set label '%s:' at graph .03, graph .97 front\n",
 4970             _("Test statistic for gamma"));
 4971         print_freq_test_label(label, GRETL_STAT_Z, freq->test,
 4972                       normal_pvalue_2(freq->test));
 4973         fprintf(fp, "set label '%s' at graph .03, graph .93 front\n",
 4974             label);
 4975         }
 4976 
 4977         if (real_ns > 0) {
 4978         print_extra_literal_lines(S, ns, fp);
 4979         }
 4980     }
 4981 
 4982     /* adjust min, max if needed */
 4983     if (freq->midpt[0] < plotmin) {
 4984         plotmin = freq->midpt[0];
 4985     }
 4986     if (freq->midpt[K-1] > plotmax) {
 4987         plotmax = freq->midpt[K-1];
 4988     }
 4989 
 4990     fprintf(fp, "set xrange [%.10g:%.10g]\n", plotmin, plotmax);
 4991     fputs("set key right top\n", fp);
 4992     } else {
 4993     /* plain frequency plot (no theoretical distribution shown) */
 4994     lambda = 1.0 / freq->n;
 4995     plotmin = freq->midpt[0] - barwidth;
 4996     plotmax = freq->midpt[K-1] + barwidth;
 4997     fprintf(fp, "set xrange [%.10g:%.10g]\n", plotmin, plotmax);
 4998     maybe_set_yrange(freq, lambda, fp);
 4999     fputs("set nokey\n", fp);
 5000 
 5001     if (real_ns > 0) {
 5002         fprintf(fp, "# literal lines = %d\n", real_ns);
 5003         print_extra_literal_lines(S, ns, fp);
 5004     }
 5005     }
 5006 
 5007     if (isnan(lambda)) {
 5008     if (fp != NULL) {
 5009         fclose(fp);
 5010     }
 5011     return 1;
 5012     }
 5013 
 5014     fprintf(fp, "set xlabel '%s'\n", freq->varname);
 5015     if (dist) {
 5016     fprintf(fp, "set ylabel '%s'\n", _("Density"));
 5017     } else {
 5018     fprintf(fp, "set ylabel '%s'\n", _("Relative frequency"));
 5019     }
 5020 
 5021     if (freq->discrete > 1 && K < 10 && fabs(freq->midpt[K-1]) < 1000) {
 5022     /* few values, all integers: force integer tic marks */
 5023     fprintf(fp, "set xtics %.0f, 1, %.0f\n", freq->midpt[0],
 5024         freq->midpt[K-1]);
 5025     }
 5026 
 5027     /* plot instructions */
 5028     if (use_boxes) {
 5029     fputs("set style fill solid 0.6\n", fp);
 5030     strcpy(withstr, "w boxes");
 5031     } else {
 5032     strcpy(withstr, "w impulses linewidth 3");
 5033     }
 5034 
 5035     if (!dist) {
 5036     fprintf(fp, "plot '-' using 1:2 %s\n", withstr);
 5037     } else if (dist == D_NORMAL) {
 5038     print_freq_dist_label(label, dist, freq->xbar, freq->sdx);
 5039     fputs("plot \\\n", fp);
 5040     fprintf(fp, "'-' using 1:2 title \"%s\" %s, \\\n"
 5041         "1.0/(sqrt(2.0*pi)*sigma)*exp(-.5*((x-mu)/sigma)**2) "
 5042         "title \"%s\" w lines\n",
 5043         _("relative frequency"), withstr, label);
 5044     } else if (dist == D_GAMMA) {
 5045     print_freq_dist_label(label, dist, alpha, beta);
 5046     fputs("plot \\\n", fp);
 5047     fprintf(fp, "'-' using 1:2 title '%s' %s, \\\n"
 5048         "x**(alpha-1.0)*exp(-x/beta)/(exp(lgamma(alpha))*(beta**alpha)) "
 5049         "title \"%s\" w lines\n",
 5050         _("relative frequency"), withstr, label);
 5051     }
 5052 
 5053     for (i=0; i<K; i++) {
 5054     fprintf(fp, "%.10g %.10g\n", freq->midpt[i], lambda * freq->f[i]);
 5055     }
 5056 
 5057     fputs("e\n", fp);
 5058 
 5059     gretl_pop_c_numeric_locale();
 5060 
 5061     return finalize_plot_input_file(fp);
 5062 }
 5063 
 5064 /**
 5065  * plot_corrmat:
 5066  * @corr: pointer to correlation matrix struct.
 5067  * @opt: can use OPT_T for triangular representation.
 5068  *
 5069  * Produces a heatmap plot based on a correlation matrix.
 5070  *
 5071  * Returns: 0 on successful completion, error code on error.
 5072  */
 5073 
 5074 int plot_corrmat (VMatrix *corr, gretlopt opt)
 5075 {
 5076     FILE *fp;
 5077     double rcrit = 0.0;
 5078     int i, j, df, n, idx;
 5079     int allpos = 1;
 5080     int err = 0;
 5081 
 5082     fp = open_plot_input_file(PLOT_HEATMAP, 0, &err);
 5083     if (err) {
 5084     return err;
 5085     }
 5086 
 5087     n = corr->dim;
 5088 
 5089     /* are all the correlations non-negative? */
 5090     for (i=0; i<n; i++) {
 5091     for (j=i+1; j<n; j++) {
 5092         idx = ijton(i, j, n);
 5093         if (corr->vec[idx] < 0) {
 5094         allpos = 0;
 5095         break;
 5096         }
 5097     }
 5098     }
 5099 
 5100     df = corr->n - 2;
 5101     if (df > 1) {
 5102     /* determine 20% critical value */
 5103     double tc = student_critval(df, 0.10);
 5104     double t2 = tc * tc;
 5105 
 5106     rcrit = sqrt(t2 / (t2 + df));
 5107     }
 5108 
 5109     gretl_push_c_numeric_locale();
 5110 
 5111     fprintf(fp, "set title '%s'\n", _("Correlation matrix"));
 5112     fputs("set nokey\n", fp);
 5113     fputs("set tics nomirror\n", fp);
 5114 
 5115     if (allpos) {
 5116     fputs("set cbrange [0:1]\n", fp);
 5117     if (rcrit > 0) {
 5118         fprintf(fp, "set palette defined (0 'white', %.4f 'white', 1 'red')\n",
 5119             rcrit);
 5120     } else {
 5121         fputs("set palette defined (0 'white', 1 'red')\n", fp);
 5122     }
 5123     } else {
 5124     fputs("set cbrange [-1:1]\n", fp);
 5125     if (rcrit > 0) {
 5126         fprintf(fp, "set palette defined (-1 'blue', %.4f 'white', %.4f 'white', 1 'red')\n",
 5127             -rcrit, rcrit);
 5128     } else {
 5129         fputs("set palette defined (-1 'blue', 0 'white', 1 'red')\n", fp);
 5130     }
 5131     }
 5132 
 5133     /* for grid lines */
 5134     fputs("set x2tics 1 format '' scale 0,0.001\n", fp);
 5135     fputs("set y2tics 1 format '' scale 0,0.001\n", fp);
 5136     fputs("set mx2tics 2\n", fp);
 5137     fputs("set my2tics 2\n", fp);
 5138 
 5139     /* y-axis tics */