Alien-libhisto

 view release on metacpan or  search on metacpan

bundled/tools/src/cmd_fill.c  view on Meta::CPAN

        }
    } else {
        /* TSV / Table */
        uint32_t nx = 0, ny = 0;
        histo2d_axis_t x_axis, y_axis;
        histo2d_axis_x(h2, &x_axis);
        histo2d_axis_y(h2, &y_axis);
        nx = x_axis.nbins;
        ny = y_axis.nbins;


        fprintf(out_fp, "Bin_X\tBin_Y\tCenter_X\tCenter_Y\tContent\tError\n");
        for (uint32_t ix = 0; ix < nx; ++ix) {
            for (uint32_t iy = 0; iy < ny; ++iy) {
                double cx = 0.0, cy = 0.0, content = 0.0, err = 0.0;
                histo2d_bin_center(h2, ix, iy, &cx, &cy);
                histo2d_bin_content(h2, ix, iy, &content);
                histo2d_bin_error(h2, ix, iy, &err);
                fprintf(out_fp, "%u\t%u\t%.4f\t%.4f\t%.4f\t%.4f\n", ix, iy, cx, cy, content, err);
            }
        }
        fflush(out_fp);
    }
    return status;
}

#include "cli_opt.h"

typedef struct {
    double *var_edges;
    size_t n_edges;
} fill_edges_ctx_t;

static int cb_parse_edges(const char *opt_name, const char *val, void *data, char *err_buf, size_t err_size) {
    (void)opt_name;
    fill_edges_ctx_t *ctx = (fill_edges_ctx_t *)data;
    if (!val || !*val) {
        snprintf(err_buf, err_size, "Option '--edges' requires comma-separated numbers");
        return 1;
    }
    const char *p = val;
    size_t edge_cap = 16;
    ctx->var_edges = (double *)malloc(edge_cap * sizeof(double));
    ctx->n_edges = 0;
    while (*p) {
        if (ctx->n_edges >= edge_cap) {
            edge_cap *= 2;
            ctx->var_edges = (double *)realloc(ctx->var_edges, edge_cap * sizeof(double));
        }
        char *endp = NULL;
        ctx->var_edges[ctx->n_edges++] = strtod(p, &endp);
        if (endp == p) break;
        if (*endp == ',') p = endp + 1;
        else p = endp;
    }
    return 0;
}

int histo_cli_fill(int argc, char **argv, FILE *out, FILE *err) {
    if (!out) out = stdout;
    if (!err) err = stderr;

    bool is_2d = false;
    uint32_t nbins = 50;
    double range_min = NAN, range_max = NAN;
    bool auto_range = false;
    const char *auto_bins_rule_str = NULL;
    int auto_bins_rule = -1;

    uint32_t xbins = 0, ybins = 0;
    double xmin = NAN, xmax = NAN, ymin = NAN, ymax = NAN;

    fill_edges_ctx_t edges_ctx = {NULL, 0};
    bool has_weights = false;
    int val_col = 1;
    int x_col = 1, y_col = 2;
    int w_col = 0;
    char delim = '\0';
    bool binary_input = false;
    bool merge_mode = false;
    bool flag_no_sumw2 = false;
    bool flag_exact_moments = false;
    uint32_t flags = HISTO_FLAG_TRACK_SUMW2;
    uint32_t rebin_factor = 1;
    bool do_cdf = false;
    double norm_area = 0.0;
    double scale_input = 1.0;
    const char *out_format = NULL;
    const char *out_file = NULL;
    uint64_t emit_every = 0;
    double emit_interval = 0.0;

    const cli_opt_spec_t specs[] = {
        {'n', "bins", NULL, CLI_OPT_TYPE_UINT32, &nbins, NULL, 0, "N",
         "Number of uniform bins (default: 50)", "50"},
        {'S', "scale-input", NULL, CLI_OPT_TYPE_DOUBLE, &scale_input, NULL, 0, "FACTOR",
         "Multiply incoming measurements by scale factor (e.g. 1e-3 for ns->us)", "1.0"},
        {0, "min", NULL, CLI_OPT_TYPE_DOUBLE, &range_min, NULL, 0, "X",
         "Lower boundary (required unless --auto-range)", NULL},
        {0, "max", NULL, CLI_OPT_TYPE_DOUBLE, &range_max, NULL, 0, "X",
         "Upper boundary (required unless --auto-range)", NULL},
        {0, "edges", NULL, CLI_OPT_TYPE_CALLBACK, &edges_ctx, cb_parse_edges, 0, "E0,E1,...",
         "Variable bin edges (comma-separated)", NULL},
        {0, "auto-range", NULL, CLI_OPT_TYPE_BOOL, &auto_range, NULL, CLI_OPT_FLAG_SET_TRUE, NULL,
         "Buffer input to determine min/max automatically", NULL},
        {'a', "auto-bins", NULL, CLI_OPT_TYPE_STRING, &auto_bins_rule_str, NULL, CLI_OPT_FLAG_OPTIONAL_ARG, "RULE",
         "Buffer input and estimate optimal bins (fd, scott, sturges, doane, knuth)", "auto"},
        {0, "2d", NULL, CLI_OPT_TYPE_BOOL, &is_2d, NULL, CLI_OPT_FLAG_SET_TRUE, NULL,
         "Enable 2D bivariate histogramming mode", NULL},
        {0, "xbins", NULL, CLI_OPT_TYPE_UINT32, &xbins, NULL, 0, "N",
         "Number of bins along X axis (default: 50)", "50"},
        {0, "ybins", NULL, CLI_OPT_TYPE_UINT32, &ybins, NULL, 0, "N",
         "Number of bins along Y axis (default: 50)", "50"},
        {0, "xmin", NULL, CLI_OPT_TYPE_DOUBLE, &xmin, NULL, 0, "X",
         "X axis lower bound", NULL},
        {0, "xmax", NULL, CLI_OPT_TYPE_DOUBLE, &xmax, NULL, 0, "X",
         "X axis upper bound", NULL},
        {0, "ymin", NULL, CLI_OPT_TYPE_DOUBLE, &ymin, NULL, 0, "Y",
         "Y axis lower bound", NULL},
        {0, "ymax", NULL, CLI_OPT_TYPE_DOUBLE, &ymax, NULL, 0, "Y",
         "Y axis upper bound", NULL},

bundled/tools/src/cmd_fill.c  view on Meta::CPAN


    bool has_min = !isnan(range_min);
    bool has_max = !isnan(range_max);
    bool has_xmin = !isnan(xmin);
    bool has_xmax = !isnan(xmax);
    bool has_ymin = !isnan(ymin);
    bool has_ymax = !isnan(ymax);

    if (has_xmin || has_xmax || has_ymin || has_ymax || xbins > 0 || ybins > 0) {
        is_2d = true;
    }
    if (xbins == 0) xbins = 50;
    if (ybins == 0) ybins = 50;

    if (w_col != 0) {
        has_weights = true;
    } else {
        w_col = is_2d ? 3 : 2;
    }

    if (flag_no_sumw2) flags &= ~HISTO_FLAG_TRACK_SUMW2;
    if (flag_exact_moments) flags |= HISTO_FLAG_EXACT_MOMENTS;

    double *var_edges = edges_ctx.var_edges;
    size_t n_edges = edges_ctx.n_edges;

    if (!out_format) {
        out_format = ((out == stdout) && cli_is_stdout_tty() && !out_file) ? "json" : "binary";
    }

    FILE *out_fp = out;
    if (out_file && strcmp(out_file, "-") != 0) {
        out_fp = fopen(out_file, "wb");
        if (!out_fp) {
            fprintf(err, "Error: Cannot open output file '%s'\n", out_file);
            if (var_edges) free(var_edges);
            cli_opt_free(&parser);
            return 1;
        }
    }

    const char *default_files[] = {"-"};
    const char **files = (parser.num_positionals > 0) ? parser.positionals : default_files;
    int nfiles = (parser.num_positionals > 0) ? parser.num_positionals : 1;

    if (is_2d) {
        /* -------------------------------------------------------------
         * 2D Histogram Ingestion Pipeline
         * ------------------------------------------------------------- */
        double *x_samples = NULL, *y_samples = NULL, *w_samples = NULL;
        size_t count_2d = 0, cap_2d = 0;
        bool auto_range_2d = (!has_xmin || !has_xmax || !has_ymin || !has_ymax || auto_range);
        uint64_t sample_count_2d = 0;
        double last_emit_time_2d = cli_get_time_sec();


        histo2d_t *h2 = NULL;
        if (!auto_range_2d) {
            h2 = histo2d_create_uniform(xbins, xmin, xmax, ybins, ymin, ymax, flags);
            if (!h2) {
                fprintf(stderr, "Error: Failed to initialize 2D histogram.\n");
                if (out_fp != stdout) fclose(out_fp);
                return 1;
            }
        }

        for (int f = 0; f < nfiles; ++f) {
            FILE *in_fp = strcmp(files[f], "-") == 0 ? stdin : fopen(files[f], "r");
            if (!in_fp) {
                fprintf(stderr, "Warning: Cannot open input file '%s'\n", files[f]);
                continue;
            }

            char line[4096];
            char detected_delim = delim;

            while (fgets(line, sizeof(line), in_fp)) {
                char *p = line;
                while (*p && isspace((unsigned char)*p)) p++;
                if (*p == '\0' || *p == '#') continue;

                if (detected_delim == '\0') {
                    detected_delim = auto_detect_delimiter(p);
                }

                double vx = 0.0, vy = 0.0, vw = 1.0;
                bool parsed_x = false, parsed_y = false;

                if (detected_delim != ' ') {
                    int col = 1;
                    char *tok = strtok(p, (char[]){detected_delim, '\0'});
                    while (tok) {
                        if (col == x_col) { vx = atof(tok); parsed_x = true; }
                        else if (col == y_col) { vy = atof(tok); parsed_y = true; }
                        else if (has_weights && col == w_col) { vw = atof(tok); }
                        tok = strtok(NULL, (char[]){detected_delim, '\0'});
                        col++;
                    }
                } else {
                    int col = 1;
                    char *tok = strtok(p, " \t\r\n");
                    while (tok) {
                        if (col == x_col) { vx = atof(tok); parsed_x = true; }
                        else if (col == y_col) { vy = atof(tok); parsed_y = true; }
                        else if (has_weights && col == w_col) { vw = atof(tok); }
                        tok = strtok(NULL, " \t\r\n");
                        col++;
                    }
                }

                if (!parsed_x || !parsed_y) continue;
                if (scale_input > 0.0 && scale_input != 1.0) {
                    vx *= scale_input;
                    vy *= scale_input;
                }

                if (auto_range_2d) {
                    if (count_2d >= cap_2d) {
                        cap_2d = cap_2d == 0 ? 1024 : cap_2d * 2;
                        x_samples = (double *)realloc(x_samples, cap_2d * sizeof(double));
                        y_samples = (double *)realloc(y_samples, cap_2d * sizeof(double));
                        if (has_weights) w_samples = (double *)realloc(w_samples, cap_2d * sizeof(double));
                    }
                    x_samples[count_2d] = vx;
                    y_samples[count_2d] = vy;
                    if (has_weights) w_samples[count_2d] = vw;
                    count_2d++;
                } else {
                    histo2d_fill_w(h2, vx, vy, vw);
                    sample_count_2d++;

bundled/tools/src/cmd_fill.c  view on Meta::CPAN

                for (size_t i = 1; i < count_2d; ++i) {
                    if (x_samples[i] < xmin) xmin = x_samples[i];
                    if (x_samples[i] > xmax) xmax = x_samples[i];
                    if (y_samples[i] < ymin) ymin = y_samples[i];
                    if (y_samples[i] > ymax) ymax = y_samples[i];
                }
                if (fabs(xmax - xmin) < 1e-12) { xmin -= 1.0; xmax += 1.0; }
                else { xmax += (xmax - xmin) * 1e-6; }
                if (fabs(ymax - ymin) < 1e-12) { ymin -= 1.0; ymax += 1.0; }
                else { ymax += (ymax - ymin) * 1e-6; }
            }

            h2 = histo2d_create_uniform(xbins, xmin, xmax, ybins, ymin, ymax, flags);
            if (h2) {
                if (has_weights && w_samples) {
                    histo2d_fill_n(h2, count_2d, x_samples, y_samples, w_samples);
                } else {
                    histo2d_fill_n(h2, count_2d, x_samples, y_samples, NULL);
                }
            }
            if (x_samples) free(x_samples);
            if (y_samples) free(y_samples);
            if (w_samples) free(w_samples);
        }

        if (h2) {
            emit_histo2d(h2, out_format, out_fp);
            histo2d_destroy(h2);
        }
        if (out_fp != out && out_fp != stdout) fclose(out_fp);
        cli_opt_free(&parser);
        return 0;
    }

    /* -------------------------------------------------------------
     * 1D Histogram Ingestion Pipeline
     * ------------------------------------------------------------- */
    double *auto_samples = NULL;
    double *auto_weights = NULL;
    size_t auto_count = 0;
    size_t auto_cap = 0;

    if (!has_min || !has_max) {
        if (!var_edges) {
            auto_range = true;
        }
    }

    histo_t *h = NULL;
    if (!auto_range) {
        if (var_edges && n_edges >= 2) {
            h = histo_create_variable((uint32_t)(n_edges - 1), var_edges, flags);
        } else {
            if (range_min >= range_max) {
                range_min = 0.0;
                range_max = 100.0;
            }
            h = histo_create_uniform(nbins, range_min, range_max, flags);
        }
        if (!h) {
            fprintf(stderr, "Error: Failed to initialize histogram.\n");
            if (var_edges) free(var_edges);
            if (out_fp != stdout) fclose(out_fp);
            cli_opt_free(&parser);
            return 1;
        }
    }

    uint64_t sample_count = 0;
    double last_emit_time = cli_get_time_sec();

    for (int f = 0; f < nfiles; ++f) {
        FILE *in_fp = NULL;
        if (strcmp(files[f], "-") == 0) {
            in_fp = stdin;
        } else {
            in_fp = fopen(files[f], binary_input || merge_mode ? "rb" : "r");
            if (!in_fp) {
                fprintf(stderr, "Warning: Cannot open input file '%s'\n", files[f]);
                continue;
            }
        }

        if (merge_mode) {
            histo_t *incoming = NULL;
            while (cli_read_histogram_from_stream(in_fp, &incoming) == HISTO_OK) {
                if (!h) {
                    h = incoming;
                } else {
                    histo_add(h, incoming);
                    histo_destroy(incoming);
                }
            }
        } else if (binary_input) {
            double val_buf[2];
            size_t needed = has_weights ? 2 : 1;
            while (fread(val_buf, sizeof(double), needed, in_fp) == needed) {
                double x = val_buf[0] * (scale_input > 0.0 ? scale_input : 1.0);
                double w = has_weights ? val_buf[1] : 1.0;
                if (auto_range) {
                    if (auto_count >= auto_cap) {
                        auto_cap = auto_cap == 0 ? 1024 : auto_cap * 2;
                        auto_samples = (double *)realloc(auto_samples, auto_cap * sizeof(double));
                        if (has_weights) auto_weights = (double *)realloc(auto_weights, auto_cap * sizeof(double));
                    }
                    auto_samples[auto_count] = x;
                    if (has_weights) auto_weights[auto_count] = w;
                    auto_count++;
                } else {
                    histo_fill_w(h, x, w);
                    sample_count++;
                    if ((emit_every > 0 && sample_count % emit_every == 0) ||
                        (emit_interval > 0.0 && (cli_get_time_sec() - last_emit_time) >= emit_interval)) {
                        emit_histogram(h, out_format, out_fp, rebin_factor, do_cdf, norm_area);
                        last_emit_time = cli_get_time_sec();
                    }
                }
            }
        } else {
            /* Check if input stream is a pre-formatted histogram (e.g. bpftrace table or JSON) */
            cli_input_format_t fmt = cli_detect_stream_format(in_fp);
            if (fmt == CLI_INPUT_BPFTRACE_HISTO || fmt == CLI_INPUT_JSON_HISTO || fmt == CLI_INPUT_BINARY_HISTO) {
                histo_t *incoming = NULL;
                while (cli_read_histogram_from_stream(in_fp, &incoming) == HISTO_OK) {
                    if (!h) {
                        h = incoming;
                    } else {
                        histo_add(h, incoming);
                        histo_destroy(incoming);
                    }
                }
                auto_range = false;
            } else {
                /* Text line parsing with delimiter auto-detection */
                char line[2048];
                char detected_delim = delim;

                while (fgets(line, sizeof(line), in_fp)) {
                    char *p = line;



( run in 0.883 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )