Alien-libhisto

 view release on metacpan or  search on metacpan

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

        if (max_col > bar_max) max_col = bar_max;

        bool in_bar_color = false;
        for (int k = 0; k < max_col && pos + 32 < (int)sizeof(row_buf); ++k) {
            if (k == kde_col && k == fit_col) {
                if (in_bar_color) { pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[0m"); in_bar_color = false; }
                pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[1;33m✦\033[0m");
            } else if (k == kde_col) {
                if (in_bar_color) { pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[0m"); in_bar_color = false; }
                pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[1;36mâ—†\033[0m");
            } else if (k == fit_col) {
                if (in_bar_color) { pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[0m"); in_bar_color = false; }
                pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[1;35m✖\033[0m");
            } else if (k < full_chars) {
                if (!in_bar_color) { pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "%s", color_ansi); in_bar_color = true; }
                pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "â–ˆ");
            } else if (k == full_chars && rem_eighths > 0) {
                if (!in_bar_color) { pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "%s", color_ansi); in_bar_color = true; }
                pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "%s", BLOCKS_UTF8[rem_eighths]);
            } else {
                if (in_bar_color) { pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[0m"); in_bar_color = false; }
                pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, " ");
            }
        }
        if (in_bar_color) {
            pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[0m");
        }

        /* Error whiskers if enabled */
        if (st->show_errors && total_w > 0.0) {
            double err = 0.0;
            histo_bin_error(h, i, &err);
            if (err > 0.0 && pos + 32 < (int)sizeof(row_buf)) {
                pos += snprintf(row_buf + pos, sizeof(row_buf) - pos, "\033[90m ╎±%.2g╎\033[0m", err);
            }
        }

        tui_render_row(f, row_buf, width, true);
        rows_drawn++;
    }

    if (kde) histo_kde_destroy(kde);
    if (fit_res) histo_fit_result_destroy(fit_res);

    while (rows_drawn < max_rows) {
        tui_render_row(f, "", width, true);
        rows_drawn++;
    }
}

static void render_help_viewport(tui_frame_t *f, const tui_state_t *st, int width, int max_rows) {
    if (st && st->view_mode == VIEW_2D_HEATMAP) {
        const char *help_lines_2d[] = {
            "── Interactive Help Cheatsheet (2D Heatmap Mode) ───────────────────────────",
            "",
            "DISPLAY & INTENSITY SCALING",
            "  l           Toggle Log-Z intensity color scale (LIN <-> LOG-Z)",
            "  p / P       Cycle color palettes (viridis, plasma, inferno, magma, turbo, cividis, gray, rainbow)",
            "  C           Toggle TrueColor / Monochrome ASCII density",
            "  g           Toggle Color Range Legend reference bar",
            "  [Space]     Freeze / Unfreeze live streaming display",
            "  c           Clear all accumulators and reset live count",
            "",
            "COMMANDS & PROMPT (:)",
            "  :palette <NAME> Set color palette (or shorthand :p <NAME>)",
            "  :clear      Clear accumulators",
            "  :quit / :q  Quit application cleanly",
            "  :help       Open this help modal",
            "",
            "Press [?], [Esc], or [Space] to dismiss this help window."
        };

        size_t n_lines = sizeof(help_lines_2d) / sizeof(help_lines_2d[0]);
        int rows_drawn = 0;

        for (size_t i = 0; i < n_lines && rows_drawn < max_rows; ++i) {
            tui_render_row(f, help_lines_2d[i], width, true);
            rows_drawn++;
        }
        while (rows_drawn < max_rows) {
            tui_render_row(f, "", width, true);
            rows_drawn++;
        }
        return;
    }

    const char *help_lines[] = {
        "── Interactive Help Cheatsheet ───────────────────────────────────────────",
        "",
        "NAVIGATION & VIEWPORT              DISPLAY & SCALING",
        "  + / - , Wheel Zoom in / out          l   Cycle Log scales (Y, X, Log-Log)",
        "  ←/→, h/l    Pan viewport left/right  p   Cycle color palettes",
        "  0           Reset to full range      a   Toggle Dynamic Auto-Range",
        "  r / R       Rebin coarser / finer    C   Toggle TrueColor / Monochrome",
        "  k           Toggle KDE curve         f   Toggle Curve Fit overlay",
        "  Tab / 1..9  Switch active column     s   Save snapshot to disk",
        "  w           Cycle rolling window     H   Toggle Compact header mode",
        "  B           Auto-fit bins to rows    Click  Inspect bin details",
        "COMMANDS & PROMPT (:)",
        "  :bins <N|auto>  Set bin count / auto-fit       :save [path]    Save binary / JSON snapshot",
        "  :col <N>        Select metric column (1..16)   :window <N>     Set rolling window size",
        "  :decay <lambda> Set exponential decay rate     :compact [on]   Toggle compact header",
        "  :range A B      Set range (e.g. :r 0 100)      [Space] Freeze / Unfreeze display",
        "  :autorange      Toggle dynamic autorange       c       Clear all accumulators",
        "  :help           Open full help modal           q       Quit cleanly",
        "",
        "Press [?], [Esc], or [Space] to dismiss this help window."
    };

    size_t n_lines = sizeof(help_lines) / sizeof(help_lines[0]);
    int rows_drawn = 0;

    for (size_t i = 0; i < n_lines && rows_drawn < max_rows; ++i) {
        tui_render_row(f, help_lines[i], width, true);
        rows_drawn++;
    }
    while (rows_drawn < max_rows) {
        tui_render_row(f, "", width, true);
        rows_drawn++;
    }
}

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

                st->scale_mode = SCALE_LOG_LOG;
                tui_engine_rebuild_1d_log(eng, 50, NULL);
            } else {
                st->scale_mode = SCALE_LINEAR;
                tui_engine_rebuild_1d(eng, 50, 0, 0, NULL);
            }
        }
    } else if (strncasecmp(cmd, "autorange", 9) == 0 || (strncasecmp(cmd, "a", 1) == 0 && (cmd[1] == ' ' || cmd[1] == '\0'))) {
        const char *arg = strchr(cmd, ' ');
        if (arg) {
            while (*arg == ' ') arg++;
            if (strcasecmp(arg, "on") == 0 || strcasecmp(arg, "1") == 0 || strcasecmp(arg, "true") == 0) {
                tui_engine_set_autorange(eng, true, eng->auto_range_threshold);
            } else if (strcasecmp(arg, "off") == 0 || strcasecmp(arg, "0") == 0 || strcasecmp(arg, "false") == 0) {
                tui_engine_set_autorange(eng, false, eng->auto_range_threshold);
            } else {
                double thresh = atof(arg);
                if (thresh > 0.0 && thresh < 1.0) {
                    tui_engine_set_autorange(eng, true, thresh);
                }
            }
        } else {
            tui_engine_set_autorange(eng, !eng->auto_range, eng->auto_range_threshold);
        }
    } else if (strncasecmp(cmd, "zoom ", 5) == 0 || strncasecmp(cmd, "z ", 2) == 0) {
        double factor = atof(cmd + (*cmd == 'z' ? 2 : 5));
        if (factor > 0.0) {
            if (eng->is_2d) {
                tui_engine_zoom_2d(eng, 1.0 / factor, st->paused ? &st->frozen_snapshot_2d : NULL);
            } else {
                tui_engine_zoom_1d(eng, 1.0 / factor, st->paused ? &st->frozen_snapshot : NULL);
            }
            snprintf(st->status_msg, sizeof(st->status_msg), "Zoomed %.2fx", factor);
            st->status_msg_time = cli_get_time_sec();
        }
    } else if (strncasecmp(cmd, "pan ", 4) == 0) {
        double frac_x = 0.0, frac_y = 0.0;
        int n_parsed = sscanf(cmd + 4, "%lf %lf", &frac_x, &frac_y);
        if (n_parsed >= 1) {
            if (eng->is_2d) {
                tui_engine_pan_2d(eng, frac_x, (n_parsed >= 2 ? frac_y : 0.0), st->paused ? &st->frozen_snapshot_2d : NULL);
            } else {
                tui_engine_pan_1d(eng, frac_x, st->paused ? &st->frozen_snapshot : NULL);
            }
            snprintf(st->status_msg, sizeof(st->status_msg), "Panned %.0f%%", frac_x * 100.0);
            st->status_msg_time = cli_get_time_sec();
        }
    } else if (strcasecmp(cmd, "clear") == 0 || strcasecmp(cmd, "reset") == 0) {
        tui_engine_clear(eng);
    } else if (strcasecmp(cmd, "help") == 0 || strcasecmp(cmd, "h") == 0) {
        st->modal = MODAL_HELP;
    } else if (strcasecmp(cmd, "quit") == 0 || strcasecmp(cmd, "q") == 0) {
        eng->running = false;
    }
}

static void print_top_usage(FILE *out) {
    if (!out) out = stdout;
    fprintf(out, "Usage: histo-top [OPTIONS] [FILE]\n");
    fprintf(out, "       histo top [OPTIONS] [FILE]\n\n");
    fprintf(out, "Real-time interactive terminal monitor for streaming 1D/2D distributions.\n\n");
    fprintf(out, "1D Geometry Options:\n");
    fprintf(out, "  -n, --bins=<N>           Initial number of bins (default: 50)\n");
    fprintf(out, "      --min=<X>            Initial lower boundary (default: 0.0)\n");
    fprintf(out, "      --max=<X>            Initial upper boundary (default: 100.0)\n");
    fprintf(out, "  -a, --auto-range         Enable dynamic quantile auto-ranging (default: ON)\n");
    fprintf(out, "      --no-auto-range      Disable dynamic auto-ranging\n\n");
    fprintf(out, "2D Geometry Options:\n");
    fprintf(out, "      --2d                 Enable 2D bivariate heatmap mode\n");
    fprintf(out, "      --xbins=<N>          Number of bins along X axis (default: 50)\n");
    fprintf(out, "      --xmin=<X>, --xmax=<X> Initial X axis bounds (default: [0, 100])\n");
    fprintf(out, "      --ybins=<N>          Number of bins along Y axis (default: 50)\n");
    fprintf(out, "      --ymin=<Y>, --ymax=<Y> Initial Y axis bounds (default: [0, 100])\n\n");
    fprintf(out, "Input Parsing & Columns:\n");
    fprintf(out, "  -w, --weights            Input stream contains weights: 'x weight' or 'x y weight'\n");
    fprintf(out, "      --value-col=<COL>    1-based column for sample coordinate in 1D mode (default: 1)\n");
    fprintf(out, "      --xcol=<COL>         1-based column for X coordinate in 2D mode (default: 1)\n");
    fprintf(out, "      --ycol=<COL>         1-based column for Y coordinate in 2D mode (default: 2)\n");
    fprintf(out, "      --weights-col=<COL>  1-based column for sample weight (default: 2 for 1D, 3 for 2D)\n");
    fprintf(out, "  -d, --delimiter=<CHAR>   Field delimiter character (default: whitespace/auto)\n\n");
    fprintf(out, "Display & Styling:\n");
    fprintf(out, "  -p, --palette=<NAME>     Color palette: viridis (default), plasma, inferno, magma,\n");
    fprintf(out, "                           turbo, cividis, grayscale, rainbow (alias: --colormap)\n");
    fprintf(out, "  -M, --mono               Monochrome mode (disable ANSI colors)\n");
    fprintf(out, "  -h, --help               Show this help message\n\n");
    fprintf(out, "Interactive Keyboard Controls:\n");
    fprintf(out, "  [Space]  Freeze / resume live ingestion snapshot\n");
    fprintf(out, "  [a]      Toggle dynamic auto-ranging\n");
    fprintf(out, "  [l]      Cycle count scale (Linear -> Log-Y -> Log-X -> Log-Log in 1D; Log-Z in 2D)\n");
    fprintf(out, "  [p]      Cycle color palette preset\n");
    fprintf(out, "  [k]      Toggle real-time Gaussian Kernel Density Estimation (KDE) overlay (1D)\n");
    fprintf(out, "  [f]      Toggle online Gaussian parametric curve fit overlay (1D)\n");
    fprintf(out, "  [e]      Toggle error bars (1D)\n");
    fprintf(out, "  [y]      Toggle vertical count axis scale ruler (1D)\n");
    fprintf(out, "  [g]      Toggle color reference legend bar (2D)\n");
    fprintf(out, "  [r / R]  Increase / decrease bin resolution\n");
    fprintf(out, "  [c]      Clear / reset accumulated histogram data\n");
    fprintf(out, "  [:]      Open interactive command prompt (:help, :autorange, :rebin, :save, :quit)\n");
    fprintf(out, "  [?]      Show full in-app help modal\n");
    fprintf(out, "  [q]      Quit monitor\n");
}

int cmd_top_main(int argc, char **argv) {
    bool is_2d = false;
    uint32_t nbins = 50;
    uint32_t xbins = 50, ybins = 50;
    double rmin = 0.0, rmax = 100.0;
    double xmin = 0.0, xmax = 100.0;
    double ymin = 0.0, ymax = 100.0;
    bool auto_range = true;
    bool monochrome = false;
    bool has_weights = false;
    char delim = ' ';
    int val_col = 1, x_col = 1, y_col = 2, w_col = 2;
    histo_palette_t palette = HISTO_PALETTE_VIRIDIS;
    uint32_t flags = HISTO_FLAG_TRACK_SUMW2 | HISTO_FLAG_EXACT_MOMENTS;
    const char *file_arg = NULL;

    for (int i = 1; i < argc; ++i) {
        const char *arg = argv[i];
        if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {

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

            } else {
                snprintf(badge, sizeof(badge), "[EOF | N=%lu]", (unsigned long)n_entries);
            }
        } else {
            double rate = eng.current_rate_ops;
            char rate_str[32];
            if (rate >= 1e6) snprintf(rate_str, sizeof(rate_str), "%.1fM/s", rate / 1e6);
            else if (rate >= 1e3) snprintf(rate_str, sizeof(rate_str), "%.1fk/s", rate / 1e3);
            else snprintf(rate_str, sizeof(rate_str), "%.0f/s", rate);

            if (eng.has_weights) {
                snprintf(badge, sizeof(badge), "[LIVE: %s | N=%lu | W=%.2f]", rate_str, (unsigned long)n_entries, tot_w);
            } else {
                snprintf(badge, sizeof(badge), "[LIVE: %s | N=%lu]", rate_str, (unsigned long)n_entries);
            }
        }
        render_top_border(&frame, eng.is_2d ? "libhisto top (2D)" : "libhisto top", badge, cols);

        if (st.compact_header) {
            char stats_buf[256];
            if (eng.is_2d) {
                if (snap_2d && n_entries > 0) {
                    histo2d_axis_t ax, ay;
                    histo2d_axis_x(snap_2d, &ax);
                    histo2d_axis_y(snap_2d, &ay);
                    snprintf(stats_buf, sizeof(stats_buf), "[2D] X:[%.1f,%.1f] Y:[%.1f,%.1f] │ N=%lu │ %s │ Pal:%s",
                             ax.min, ax.max, ay.min, ay.max, (unsigned long)n_entries,
                             st.log_z ? "LOG-Z" : "LIN", histo_palette_name(st.palette));
                } else {
                    snprintf(stats_buf, sizeof(stats_buf), "Waiting for 2D stream...");
                }
            } else {
                if (snap && n_entries > 0) {
                    double mean = 0, sdev = 0, med = 0, p95 = 0;
                    histo_mean(snap, &mean);
                    histo_std_dev(snap, &sdev);
                    histo_median(snap, &med);
                    histo_quantile(snap, 0.95, &p95);
                    snprintf(stats_buf, sizeof(stats_buf), "[Col %d] Mean: %.2f ± %.2f │ Med: %.2f │ P95: %.2f │ N=%lu │ Pal:%s",
                             eng.val_col, mean, sdev, med, p95, (unsigned long)n_entries, histo_palette_name(st.palette));
                } else {
                    snprintf(stats_buf, sizeof(stats_buf), "Waiting for stream...");
                }
            }
            tui_render_row(&frame, stats_buf, cols, true);
            render_divider(&frame, cols);
        } else {
            // Row 2: Stats summary
            char stats_buf[256];
            if (eng.is_2d) {
                if (snap_2d && n_entries > 0) {
                    histo2d_axis_t ax, ay;
                    histo2d_axis_x(snap_2d, &ax);
                    histo2d_axis_y(snap_2d, &ay);
                    snprintf(stats_buf, sizeof(stats_buf),
                             "X: [%.2f, %.2f] %ux │ Y: [%.2f, %.2f] %uy │ Entries: %lu │ Weight: %.2f",
                             ax.min, ax.max, histo2d_nbins_x(snap_2d),
                             ay.min, ay.max, histo2d_nbins_y(snap_2d),
                             (unsigned long)n_entries, tot_w);
                } else {
                    snprintf(stats_buf, sizeof(stats_buf), "Waiting for streaming 'x y' samples...");
                }
            } else {
                if (snap && n_entries > 0) {
                    double mean = 0, sdev = 0, med = 0, iqr = 0, p95 = 0, p99 = 0;
                    histo_mean(snap, &mean);
                    histo_std_dev(snap, &sdev);
                    histo_median(snap, &med);
                    histo_iqr(snap, &iqr);
                    histo_quantile(snap, 0.95, &p95);
                    histo_quantile(snap, 0.99, &p99);
                    snprintf(stats_buf, sizeof(stats_buf), "Mean: %.2f ± %.2f │ Med: %.2f │ IQR: %.2f │ P95: %.2f │ P99: %.2f",
                             mean, sdev, med, iqr, p95, p99);
                } else {
                    snprintf(stats_buf, sizeof(stats_buf), "Waiting for streaming samples...");
                }
            }
            tui_render_row(&frame, stats_buf, cols, true);

            // Row 3: Divider
            render_divider(&frame, cols);

            // Row 4: Subheader
            char subhdr[384];
            if (eng.is_2d) {
                if (snap_2d) {
                    histo2d_axis_t ax, ay;
                    histo2d_axis_x(snap_2d, &ax);
                    histo2d_axis_y(snap_2d, &ay);
                    snprintf(subhdr, sizeof(subhdr), "2D Heatmap │ X: %u bins [%.1f, %.1f] │ Y: %u bins [%.1f, %.1f] │ Pal: %s │ %s %s",
                             histo2d_nbins_x(snap_2d), ax.min, ax.max,
                             histo2d_nbins_y(snap_2d), ay.min, ay.max,
                             histo_palette_name(st.palette),
                             st.log_z ? "LOG-Z" : "LIN",
                             st.show_legend ? "│ Legend: ON" : "");
                } else {
                    snprintf(subhdr, sizeof(subhdr), "Initializing 2D...");
                }
            } else if (snap) {
                double r_min = 0, r_max = 0;
                histo_range(snap, &r_min, &r_max);
                uint32_t nb = histo_nbins(snap);
                const char *sc = (st.scale_mode == SCALE_LOG_Y) ? "LOG-Y" :
                                 (st.scale_mode == SCALE_LOG_X) ? "LOG-X" :
                                 (st.scale_mode == SCALE_LOG_LOG) ? "LOG-LOG" : "LIN";
                char kde_tag[64] = "";
                char fit_tag[64] = "";
                if (st.show_kde && n_entries >= 5) {
                    histo_kde_t *sub_kde = histo_kde_create_from_histo(snap, NULL);
                    if (sub_kde) {
                        double bw = histo_kde_get_bandwidth(sub_kde);
                        snprintf(kde_tag, sizeof(kde_tag), "│ KDE: Gauss(h=%.2g) ", bw);
                        histo_kde_destroy(sub_kde);
                    } else {
                        snprintf(kde_tag, sizeof(kde_tag), "│ KDE ");
                    }
                }
                if (st.show_fit && n_entries >= 5) {
                    histo_fit_result_t *sub_fit = NULL;
                    histo_fit_model(snap, HISTO_FIT_MODEL_GAUSSIAN, NULL, NULL, &sub_fit);
                    if (sub_fit && sub_fit->converged) {
                        snprintf(fit_tag, sizeof(fit_tag), "│ Fit: μ=%.2f σ=%.2f ", sub_fit->params[1], sub_fit->params[2]);
                        histo_fit_result_destroy(sub_fit);
                    } else {
                        if (sub_fit) histo_fit_result_destroy(sub_fit);
                        snprintf(fit_tag, sizeof(fit_tag), "│ Fit: Gauss ");
                    }
                }
                char err_tag[32] = "";
                if (st.show_errors) snprintf(err_tag, sizeof(err_tag), "│ Err: ON ");
                char yaxis_tag[32] = "";
                if (st.show_y_axis) snprintf(yaxis_tag, sizeof(yaxis_tag), "│ Axis: ON ");
                char col_tag[32] = "";
                if (eng.val_col > 1) snprintf(col_tag, sizeof(col_tag), "Col:%d │ ", eng.val_col);
                char win_tag[32] = "";



( run in 0.945 second using v1.01-cache-2.11-cpan-9789f410c06 )