apop_output.c 15.3 KB
Newer Older
1 2 3

/** \file 
  Some printing and output interface functions. */
4
/* Copyright (c) 2006--2007, 2009 by Ben Klemens.  Licensed under the GPLv2; see COPYING.  */
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

//The reader will find a few function headers for this file in asst.h
#include "apop_internal.h"
 
#define Output_vars output_name, output_pipe, output_type, output_append

#define Output_declares char const * output_name, FILE * output_pipe, char output_type, char output_append

/** If you're reading this, it is probably because you were referred by another function
  that uses this internally. You should never call this function directly, but do read
  this documentation.

  There are four settings that affect how output happens, which can be set when you call the
  function that sent you to this documentation, e.g:

  \code
  apop_data_print(your_data, .output_type ='f', .output_append = 'w');
  \endcode

  \param output_name The name of the output file, if any. For a database, the table to write.
  \param output_pipe If you have already opened a file and have a \c FILE* on hand, use
  this instead of giving the file name.
27
  \param output_type \c 'p' = pipe, \c 'f'= file, \c 'd' = database
28 29 30 31 32
  \param output_append \c 'a' = append (default), \c 'w' = write over.

At the end, \c output_name, \c output_pipe, and \c output_type are all set.
Notably, the local \c output_pipe will have the correct location for the calling function to \c fprintf to.

33 34 35 36 37 38 39 40 41
\li See \ref legi for more discussion.

\li The default is output to stdout. For example,
\code
apop_data_print(your_data);
//is equivalent to
apop_data_print(your_data, .output_type='p', .output_pipe=stdout);
\endcode

42 43 44 45 46 47 48
\li Tip: if writing to the database, you can get a major speed boost by wrapping the call in a begin/commit wrapper:

\code
apop_query("begin;");
apop_data_print(your_data, .output_name="dbtab", .output_type='d');
apop_query("commit;");
\endcode
49
\ingroup all_public
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
*/
int apop_prep_output(char const *output_name, FILE ** output_pipe, char *output_type, char *output_append){
    *output_append = *output_append ? *output_append : 'w';

    if (!output_name && !*output_pipe && !*output_type)     *output_type = 's';              
    else if (output_name && !*output_pipe && !*output_type) *output_type = 'f'; 
    else if (!output_name && *output_pipe && !*output_type) *output_type = 'p';     

    if (*output_type =='p')      *output_pipe = *output_pipe ? *output_pipe: stdout;      
    else if (*output_type =='d') *output_pipe = stdout;  //won't be used.
    else *output_pipe = output_name
                        ? fopen(output_name, *output_append == 'a' ? "a" : "w")
                        : stdout;
    Apop_stopif(!output_pipe && output_name, return -1, 0, "Trouble opening file %s.", output_name);
    return 0;
}

#define Dispatch_output                        \
    char const *apop_varad_var(output_name, NULL);  \
    FILE * apop_varad_var(output_pipe, NULL);  \
    char apop_varad_var(output_type, 0);       \
    char apop_varad_var(output_append, 0);     \
    Apop_stopif(apop_prep_output(output_name, &output_pipe, &output_type, &output_append), \
            return, 0, "Trouble preparing to write output.");


/////The printing functions.

static void white_pad(int ct){
    for(size_t i=0; i < ct; i ++)
        printf(" ");
}

83 84 85
/* This function prettyprints the \c apop_data set to a screen.

It is currently not in the documentation. It'd be nice to merge this w/apop_data_print.
86 87 88

This takes a lot of machinery. I write every last element to a text array, then measure column widths, then print to screen with padding to guarantee that everything lines up.  There's no way to have the first element of a column line up with the last unless you interrogate the width of every element in the column, so printing columns really can't be a one-pass process.

89 90 91 92 93
So, I produce an \ref apop_data set with no numeric elements and a text element to be
filled with the input data set, and then print that. That means that I'll be using
(more than) twice the memory to print this. If this is a problem, you can use \ref
apop_data_print to dump your data to a text file, and view the text file, or print
subsets.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

For more machine-readable printing, see \ref apop_data_print.
*/
void apop_data_show(const apop_data *in){
    if (!in) {printf("NULL\n"); return;}
    Get_vmsizes(in) //vsize, msize1, msize2, tsize
//Take inventory and get sizes
    size_t hasrownames = (in->names && in->names->rowct) ? 1 : 0;
    size_t hascolnames = in->names && 
                    (in->names->vector || in->names->colct || in->names->textct);
    size_t hasweights = (in->weights != NULL);

    size_t outsize_r = GSL_MAX(in->matrix ? in->matrix->size1 : 0, in->vector ? in->vector->size: 0);
    outsize_r = GSL_MAX(outsize_r, in->textsize[0]);
    outsize_r = GSL_MAX(outsize_r, wsize);
    if (in->names) outsize_r = GSL_MAX(outsize_r, in->names->rowct);
    outsize_r += hascolnames;

    size_t outsize_c = msize2;
    outsize_c += in->textsize[1];
    outsize_c += (vsize>0);
    outsize_c += (wsize>0);
    outsize_c += hasrownames + hasweights;

//Write to the printout data set.
    apop_data *printout = apop_text_alloc(NULL , outsize_r, outsize_c);
    if (hasrownames)
        for (size_t i=0; i < in->names->rowct; i ++)
122
            apop_text_set(printout, i + hascolnames, 0, "%s", in->names->row[i]);
123
    for (size_t i=0; i < vsize; i ++) //vsize may be zero.
124
        apop_text_set(printout, i + hascolnames, hasrownames, "%g", gsl_vector_get(in->vector, i));
125 126
    for (size_t i=0; i < msize1; i ++) //msize1 may be zero.
        for (size_t j=0; j < msize2; j ++)
127
            apop_text_set(printout, i + hascolnames, hasrownames + (vsize >0)+ j, "%g", gsl_matrix_get(in->matrix, i, j));
128 129 130
    if (in->textsize[0])
        for (size_t i=0; i < in->textsize[0]; i ++)
            for (size_t j=0; j < in->textsize[1]; j ++)
131
                apop_text_set(printout, i + hascolnames, hasrownames + (vsize>0)+ msize2 + j, "%s", in->text[i][j]);
132 133
    if (hasweights)
        for (size_t i=0; i < in->weights->size; i ++)
134
            apop_text_set(printout, i + hascolnames, outsize_c-1, "%g", gsl_vector_get(in->weights, i));
135 136 137 138

//column names
    if (hascolnames){
        if (vsize && in->names->vector)
139
            apop_text_set(printout, 0 , hasrownames, "%s", in->names->vector);
140 141
        if (msize2 && in->names)
            for (size_t i=0; i < in->names->colct; i ++)
142
                apop_text_set(printout, 0 , hasrownames + (vsize>0) + i, "%s", in->names->col[i]);
143 144
        if (in->textsize[1] && in->names)
            for (size_t i=0; i < in->names->textct; i ++)
145
                apop_text_set(printout, 0 , hasrownames + (vsize>0) + msize2 + i, "%s", in->names->text[i]);
146
        if (hasweights)
147
            apop_text_set(printout, 0 , outsize_c-1, "Weights");
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    }

//get column sizes
    int colsizes[outsize_c];
    for (size_t i=0; i < outsize_c; i ++){
        colsizes[i] = strlen(printout->text[0][i]);
        for (size_t j=1; j < outsize_r; j ++)
            colsizes[i] = GSL_MAX(colsizes[i], strlen(printout->text[j][i]));
    }

//Finally, print
    if (in->names && in->names->title && strlen(in->names->title))
        printf("\t%s\n\n", in->names->title);
    for (size_t j=0; j < outsize_r; j ++){
        for (size_t i=0; i < outsize_c; i ++){
            white_pad(colsizes[i] - strlen(printout->text[j][i]) + 1);//one spare space.
            printf("%s", printout->text[j][i]);
            if (i > 0 && i< outsize_c-1) 
                printf(" %s ", apop_opts.output_delimiter);
        }
        printf("\n");
    }

    if (in->more) {
        printf("\n");
        apop_data_show(in->more);
    }
    apop_data_free(printout);
}

void p_fn(FILE * f, double data){
    if (data == (int) data) fprintf(f, "% 5i", (int) data); 
    else                    fprintf(f, "% 5f", data);
}

static void print_core_v(const gsl_vector *data, char *separator, Output_declares){
    FILE *f = output_pipe;
    if (!data) fprintf(f, "NULL\n");
    else {
	    for (size_t i=0; i<data->size; i++){
		    p_fn(f, gsl_vector_get(data, i));
		    if (i< data->size -1) fprintf(f, "%s", separator);
	    }
	    fprintf(f,"\n");
    }
	if (output_name) fclose(f);
}

196
/** Print a vector to the screen, a file, a pipe, or the database.
197

198 199 200 201 202 203 204 205
  \li See \ref apop_prep_output for more on how printing settings are set.
  \li For example, the default for \ref apop_opts_type "apop_opts.output_delimiter"
is a tab, which puts the vector on one line, but <tt>apop_opts.output_type="\n"</tt>
would print the vector vertically.
  \li See also \ref Legi for more details and examples.
  \li This function uses the \ref designated syntax for inputs.
\ingroup all_public
*/
206 207 208 209 210 211 212 213 214 215 216 217 218 219
#ifdef APOP_NO_VARIADIC
void apop_vector_print(gsl_vector *data, Output_declares){
#else
apop_varad_head(void, apop_vector_print){
    gsl_vector *apop_varad_var(data, NULL);
    Dispatch_output
     apop_vector_print_base(data, Output_vars);
}

 void apop_vector_print_base(gsl_vector *data, Output_declares){
#endif
	print_core_v(data, apop_opts.output_delimiter, Output_vars);
 }

220 221
/* currently removed from the documentation.
 Dump a <tt>gsl_vector</tt> to the screen. 
222 223 224
    You may want to set \ref apop_opts_type "apop_opts.output_delimiter".

\li See \ref apop_prep_output for more on how printing settings are set.
225
\li See also \ref Legi for more details and examples.
226
\li This function uses the \ref designated syntax for inputs.
227
*/
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
void apop_vector_show(const gsl_vector *data){
	print_core_v(data, apop_opts.output_delimiter, NULL, stdout, 's', 0); 
}

static int get_max_strlen(char **names, size_t len){
    int max  = 0;
    for (int i=0; i< len; i++)
        max = GSL_MAX(max, strlen(names[i]));
    return max;
}

//On screen, display a pipe, else use the usual output delimiter.
static void a_pipe(FILE *f, char displaytype){
    if (displaytype == 's') fprintf(f, " | ");
    else                    fprintf(f, "%s", apop_opts.output_delimiter);
}

static void apop_data_print_core(const apop_data *data, FILE *f, char displaytype){
    if (!data){
        fprintf(f, "NULL\n");
        return;
    }
    int i, j, L = 0, 
        start   = (data->vector)? -1 : 0,
        end     = (data->matrix)? data->matrix->size2 : 0,
        rowend  = (data->matrix)? data->matrix->size1 : (data->vector) ? data->vector->size : data->text ? data->textsize[0] : -1;
254
    if (data->names && data->names->title && strlen(data->names->title))
255
        fprintf(f, "\t%s\n\n", data->names->title);
256
    if (data->names && data->names->rowct)
257
        L   = get_max_strlen(data->names->row, data->names->rowct);
258 259 260 261 262 263 264
    if (data->names && data->names->rowct && (data->names->vector || data->names->colct || data->names->textct)){
        if ((apop_opts.db_name_column || *apop_opts.db_name_column=='\0') || 
                !strcmp(apop_opts.db_name_column, "row_names"))
            fprintf(f, "%*s  ", L+2, " ");
        else { fprintf(f, "%s", apop_opts.db_name_column); a_pipe(f, displaytype); }
    }
    if (data->vector && data->names && data->names->vector){
265 266 267
        fprintf(f, "%s", data->names->vector);
    }
    if (data->matrix){
268
        if (data->vector && data->names && data->names->colct){
269 270 271
            fprintf(f, "%c ", data->names->vector ? ' ' : '\t' );
            a_pipe(f, displaytype);
        }
272 273
        if (data->names) 
          for(i=0; i< data->names->colct; i++){
274 275 276 277 278 279
            if (i < data->names->colct -1)
                fprintf(f, "%s%s", data->names->col[i], apop_opts.output_delimiter);
            else
                fprintf(f, "%s", data->names->col[i]);
        }
    }
280 281
    if (data->textsize[1] && data->names && data->names->textct){
        if ((data->vector && data->names && data->names->vector) || (data->matrix && data->names->colct))
282
            a_pipe(f, displaytype);
283 284
        if (data->names)
          for(i=0; i< data->names->textct; i++){
285 286 287 288 289 290
            if (i < data->names->textct -1)
                fprintf(f, "%s%s", data->names->text[i], apop_opts.output_delimiter);
            else
                fprintf(f, "%s", data->names->text[i]);
        }
    }
291
    if(data->names && (data->names->vector || data->names->colct || data->names->textct))
292 293
        fprintf(f, "\n");
    for(j=0; j< rowend; j++){
294
        if (data->names && data->names->rowct > j)
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
            fprintf(f, "%*s%s", L+2, data->names->row[j], apop_opts.output_delimiter);
        for(i=start; i< end; i++){
            if ((i < 0 && j < data->vector->size) || (i>= 0 && j < data->matrix->size1 && i < data->matrix->size2))
                p_fn(f,  apop_data_get(data, j, i));
            else
                fprintf(f, " ");
            if (i==-1 && data->matrix) 
                a_pipe(f, displaytype);
            if (i < end-1)
                fprintf(f, "%s", apop_opts.output_delimiter);
        }
        if (data->text){
            if (data->vector || data->matrix)
                a_pipe(f, displaytype);
            if (j < data->textsize[0])
                for(i=0; i< data->textsize[1]; i++){
                    fprintf(f, "%s", data->text[j][i]);
                    if (i < data->textsize[1]-1) fprintf(f, "%s", apop_opts.output_delimiter);
                }
        }
        if (data->weights && j < data->weights->size){
            a_pipe(f, displaytype);
            p_fn(f, data->weights->data[j]);
        }
        fprintf(f, "\n");
    }
}

/** Print an \ref apop_data set to a file, the database, or the screen,
  as determined by the \c .output_type.

\li See \ref apop_prep_output for more on how printing settings are set.
327 328
\li See \ref Legi for more details and examples.
\li See \ref sqlsec for notes on writing an \ref apop_data set to the database.
329
\li This function uses the \ref designated syntax for inputs.
330 331
\ingroup all_public
*/
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
#ifdef APOP_NO_VARIADIC
void apop_data_print(const apop_data *data, Output_declares){
#else
apop_varad_head(void, apop_data_print){
    const apop_data * apop_varad_var(data, NULL);
    Dispatch_output
     apop_data_print_base(data, Output_vars);
}

 void apop_data_print_base(const apop_data *data, Output_declares){
#endif 
    if (output_type  == 'd'){
        if (output_append == 'w') apop_table_exists(output_name, 'd');
        apop_data_to_db(data, output_name, output_append);
        return;
    }
    apop_data_print_core(data, output_pipe, output_type);
    if (data && data->more) {
        output_append='a';
        apop_data_print(data->more, Output_vars);
    }
    if (output_name)
        fclose(output_pipe);
}

357
/** Print a \c gsl_matrix to the screen, a file, a pipe, or a database table.
358 359

\li See \ref apop_prep_output for more on how printing settings are set.
360
\li See also \ref Legi for more details and examples.
361
\li This function uses the \ref designated syntax for inputs.
362 363
\ingroup all_public
*/
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
#ifdef APOP_NO_VARIADIC
void apop_matrix_print(const gsl_matrix *data, Output_declares){
#else
apop_varad_head(void, apop_matrix_print){
    const gsl_matrix *apop_varad_var(data, NULL);
    Dispatch_output
     apop_matrix_print_base(data, Output_vars);
}

 void apop_matrix_print_base(const gsl_matrix *data, Output_declares){
#endif
    if (output_type == 'd'){
        Apop_assert_c(data, , 1, "You sent me a NULL matrix. No database table will be created.");
    } else if (!data){
        fprintf(output_pipe, "NULL\n");
        return;
    }
381
    apop_data_print(&(apop_data){.matrix=(gsl_matrix*)data}, Output_vars); //cheating on the const qualifier
382 383
}

384
//leaving this undocumented for now.
385
void apop_matrix_show(const gsl_matrix *data){
386
    apop_data_print_core(&(apop_data){.matrix=(gsl_matrix*)data},  stdout, 's');
387
}