File: apop_output.c

package info (click to toggle)
apophenia 0.999b%2Bds3-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 2,892 kB
  • ctags: 1,840
  • sloc: ansic: 20,530; makefile: 346; sh: 230; awk: 135; sed: 26
file content (391 lines) | stat: -rw-r--r-- 16,063 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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
83
84
85
86
87
88
89
90
91
92
93
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
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
327
328
329
330
331
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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

/** \file 
  Some printing and output interface functions. */
/* Copyright (c) 2006--2007, 2009 by Ben Klemens.  Licensed under the modified GNU GPL v2; see COPYING and COPYING2.  */

//The reader will find a few function headers for this file in asst.h
#include "apop_internal.h"

/** \defgroup output		Printing to the screen or a text file

Most functions print only to the screen, but the 
\ref apop_print "matrix and vector printing functions" will let you print to a text file as
well. The presumption is that statistic estimates are for your own
consumption, while you are printing a matrix for import into another program.

*/
/** \defgroup apop_print 	Assorted printing functions		

The <tt>apop_*_print</tt> functions will print to screen, text file,
or database, depending on how you set \c .output_type.
The <tt>apop_*_show</tt> functions print only to screen, and are basically
just a convenience shell to the corresponding <tt>apop_*_print</tt>
function.

\ingroup output
*/
 
#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.
  \param output_type \c 'p' = pipe, \c 'f'= file, \c 'd' = database, \c 's' = stdout
  \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.

\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
*/
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 =='s') *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(" ");
}

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

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.

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_print to dump your data to a text file, and view the text file, or print subsets.

For more machine-readable printing, see \ref apop_data_print.

\ingroup output
*/
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 ++)
            apop_text_add(printout, i + hascolnames, 0, "%s", in->names->row[i]);
    for (size_t i=0; i < vsize; i ++) //vsize may be zero.
        apop_text_add(printout, i + hascolnames, hasrownames, "%g", gsl_vector_get(in->vector, i));
    for (size_t i=0; i < msize1; i ++) //msize1 may be zero.
        for (size_t j=0; j < msize2; j ++)
            apop_text_add(printout, i + hascolnames, hasrownames + (vsize >0)+ j, "%g", gsl_matrix_get(in->matrix, i, j));
    if (in->textsize[0])
        for (size_t i=0; i < in->textsize[0]; i ++)
            for (size_t j=0; j < in->textsize[1]; j ++)
                apop_text_add(printout, i + hascolnames, hasrownames + (vsize>0)+ msize2 + j, "%s", in->text[i][j]);
    if (hasweights)
        for (size_t i=0; i < in->weights->size; i ++)
            apop_text_add(printout, i + hascolnames, outsize_c-1, "%g", gsl_vector_get(in->weights, i));

//column names
    if (hascolnames){
        if (vsize && in->names->vector)
            apop_text_add(printout, 0 , hasrownames, "%s", in->names->vector);
        if (msize2 && in->names)
            for (size_t i=0; i < in->names->colct; i ++)
                apop_text_add(printout, 0 , hasrownames + (vsize>0) + i, "%s", in->names->col[i]);
        if (in->textsize[1] && in->names)
            for (size_t i=0; i < in->names->textct; i ++)
                apop_text_add(printout, 0 , hasrownames + (vsize>0) + msize2 + i, "%s", in->names->text[i]);
        if (hasweights)
            apop_text_add(printout, 0 , outsize_c-1, "Weights");
    }

//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);
}

/** Print a vector in float format.
You may want to set \ref apop_opts_type "apop_opts.output_delimiter"; the default is a tab, which puts the vector on one line, but a newline would print the vector vertically.

\li See \ref apop_prep_output for more on how printing settings are set.
\li See also the legible output section of the \ref outline for more details and examples.
\li This function uses the \ref designated syntax for inputs.
\ingroup apop_print */
#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);
 }

/** Dump a <tt>gsl_vector</tt> to the screen. 
    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.
\li See also the legible output section of the \ref outline for more details and examples.
\li This function uses the \ref designated syntax for inputs.
\ingroup apop_print */
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;
    if (data->names->title && strlen(data->names->title))
        fprintf(f, "\t%s\n\n", data->names->title);
    if (data->names->rowct)
        L   = get_max_strlen(data->names->row, data->names->rowct);
    if (data->names->rowct && (data->names->vector || data->names->colct || data->names->textct))
        fprintf(f, "%*s  ", L+2, " ");
    if (data->vector && data->names->vector){
        fprintf(f, "%s", data->names->vector);
    }
    if (data->matrix){
        if (data->vector && data->names->colct){
            fprintf(f, "%c ", data->names->vector ? ' ' : '\t' );
            a_pipe(f, displaytype);
        }
        for(i=0; i< data->names->colct; i++){
            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]);
        }
    }
    if (data->textsize[1] && data->names->textct){
        if ((data->vector && data->names->vector) || (data->matrix && data->names->colct))
            a_pipe(f, displaytype);
        for(i=0; i< data->names->textct; i++){
            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]);
        }
    }
    if(data->names->vector || data->names->colct || data->names->textct)
        fprintf(f, "\n");
    for(j=0; j< rowend; j++){
        if (data->names->rowct > j)
            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.
\li See also the legible output section of the \ref outline for more details and examples.
\li This function uses the \ref designated syntax for inputs.
\ingroup apop_print */
#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);
}

/** Print a matrix in float format.
    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.
\li See also the legible output section of the \ref outline for more details and examples.
\li This function uses the \ref designated syntax for inputs.
\ingroup apop_print */
#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;
    }
    apop_data *d = apop_data_alloc();
    d->matrix=(gsl_matrix *) data; //cheating on the const qualifier
    apop_data_print(d, Output_vars);
    d->matrix=NULL;
    apop_data_free(d);
}

/** Dump a <tt>gsl_matrix</tt> to the screen.
    You may want to set \ref apop_opts_type "apop_opts.output_delimiter".
\li This function uses the \ref designated syntax for inputs.
\ingroup apop_print */
void apop_matrix_show(const gsl_matrix *data){
    apop_data *dtmp = apop_matrix_to_data((gsl_matrix*) data);
    apop_data_print_core(dtmp,  stdout, 's');
    dtmp->matrix = NULL;
    apop_data_free(dtmp);
}