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