        -:    0:Source:LikeR.c
        -:    0:Graph:./Stats-LikeR-0.06/LikeR.gcno
        -:    0:Data:./Stats-LikeR-0.06/LikeR.gcda
        -:    0:Runs:1
        -:    0:Source is newer than graph
        -:    1:/*
        -:    2: * This file was generated automatically by ExtUtils::ParseXS version 3.57 from the
        -:    3: * contents of LikeR.xs. Do not edit this file, edit LikeR.xs instead.
        -:    4: *
        -:    5: *    ANY CHANGES MADE HERE WILL BE LOST!
        -:    6: *
        -:    7: */
        -:    8:
        -:    9:#line 1 "LikeR.xs"
        -:   10:#define _GNU_SOURCE
        -:   11:/* --- C HELPER SECTION --- */
        -:   12:#define PERL_NO_GET_CONTEXT
        -:   13:#include "EXTERN.h"
        -:   14:#include "perl.h"
        -:   15:#include "XSUB.h"
        -:   16:#include "ppport.h"
        -:   17:#include <math.h>
        -:   18:#include <ctype.h>
        -:   19:#include <stdlib.h>
        -:   20:#include <float.h>
        -:   21:#include <string.h>
        -:   22:#include <stdint.h>   /* uint64_t — harmless if perl.h already pulled it in */
        -:   23:
        -:   24:/* ── sample(): private splitmix64 PRNG ─────────────────────────────────────
        -:   25: *
        -:   26: * sample() gets its own PRNG state, completely separate from Drand01.
        -:   27: * That means generate_binomial(), ruif(), rbinom(), and every other caller
        -:   28: * of Drand01() are unaffected — their streams are never advanced or reseeded
        -:   29: * by anything sample() does.
        -:   30: *
        -:   31: * Seeding is lazy (first call) and reads from /dev/urandom; falls back to
        -:   32: * time()^PID on systems without it.  No aTHX needed: all calls are plain C.
        -:   33: * PERL_NO_GET_CONTEXT is therefore not a concern here.
        -:   34: */
        -:   35:static uint64_t sample__state  = 0;
        -:   36:static bool     sample__seeded = FALSE;
        -:   37:
        -:   38:PERL_STATIC_INLINE uint64_t
        -:   39:sample__mix64(void)
        -:   40:{
        -:   41:	uint64_t z = (sample__state += UINT64_C(0x9e3779b97f4a7c15));
        -:   42:	z = (z ^ (z >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
        -:   43:	z = (z ^ (z >> 27)) * UINT64_C(0x94d049bb133111eb);
        -:   44:	return z ^ (z >> 31);
        -:   45:}
        -:   46:
        -:   47:static void
        -:   48:sample__seed(void)
        -:   49:{
        -:   50:	uint64_t s = 0;
        -:   51:	size_t   got = 0;
        -:   52:	FILE    *restrict ur  = fopen("/dev/urandom", "rb");
        -:   53:	if (ur) { got = fread(&s, sizeof s, 1, ur); fclose(ur); }
        -:   54:	if (got != 1 || s == 0)
        -:   55:	  s = (uint64_t)time(NULL) ^ ((uint64_t)getpid() << 32);
        -:   56:	sample__state  = s;
        -:   57:	(void)sample__mix64();   /* discard first output to warm the state */
        -:   58:	sample__seeded = TRUE;
        -:   59:}
        -:   60:
        -:   61:/* Uniform integer in [0, upper) — rejection loop, no modulo bias */
        -:   62:PERL_STATIC_INLINE size_t
        -:   63:sample__rand(size_t upper) {
        -:   64:	const uint64_t u = (uint64_t)upper;
        -:   65:	const uint64_t t = (uint64_t)(-(uint64_t)u) % u;
        -:   66:	uint64_t r;
        -:   67:	do { r = sample__mix64(); } while (r < t);
        -:   68:	return (size_t)(r % u);
        -:   69:}
        -:   70:/* ── end sample() private PRNG ─────────────────────────────────────────── */
        -:   71:
        -:   72:/* Ensure Perl's PRNG is seeded, matching the lazy-evaluation of Perl's rand() */
        -:   73:#define AUTO_SEED_PRNG() \
        -:   74:    do { \
        -:   75:        if (!PL_srand_called) { \
        -:   76:            (void)seedDrand01((Rand_seed_t)Perl_seed(aTHX)); \
        -:   77:            PL_srand_called = TRUE; \
        -:   78:        } \
        -:   79:    } while (0)
        -:   80:
        -:   81:// ---------------------------------------
        -:   82://   Helpers for Random Number Generation
        -:   83:// ---------------------------------------
        -:   84:#ifndef M_PI
        -:   85:#define M_PI 3.14159265358979323846
        -:   86:#endif
        -:   87:// C helper for EXACT Non-central T-distribution CDF via Numerical Integration.
        -:   88:// This perfectly replicates R's pt(..., ncp) exactness without requiring complex Beta functions.
        -:   89:static double exact_pnt(double t, double df, double ncp) {
        -:   90:	if (df <= 0.0) return 0.0;
        -:   91:	unsigned short int n_steps = 30000;
        -:   92:	double step = 1.0 / n_steps;
        -:   93:	double integral = 0.0, half_df = df / 2.0;
        -:   94:
        -:   95:	double log_coef = log(2.0) + half_df * log(half_df) - lgamma(half_df);
        -:   96:	double root_half = 0.70710678118654752440; // 1 / sqrt(2)
        -:   97:
        -:   98:	for (unsigned short i = 1; i < n_steps; i++) {
        -:   99:		double u = i * step;
        -:  100:		double w = u / (1.0 - u);
        -:  101:		// Scaled Chi-distribution log-density
        -:  102:		double log_M = log_coef + (df - 1.0) * log(w) - half_df * w * w;
        -:  103:		double M = exp(log_M);
        -:  104:		// Exact Normal CDF using the C standard library's erfc function
        -:  105:		double z = t * w - ncp;
        -:  106:		double pnorm_val = 0.5 * erfc(-z * root_half);
        -:  107:		double weight = (i % 2 != 0) ? 4.0 : 2.0;
        -:  108:		integral += weight * (pnorm_val * M / ((1.0 - u) * (1.0 - u)));
        -:  109:	}
        -:  110:	return integral * (step / 3.0);
        -:  111:}
        -:  112:// --- Math Helpers for P-values and Confidence Intervals --- 
        -:  113:
        -:  114:// Ranking helper with tie adjustment (matches R's tie handling)
        -:  115:typedef struct { double val; size_t idx; double rank; } RankInfo;
        -:  116:static int compare_rank(const void *restrict a, const void *restrict b) {
        -:  117:	double diff = ((RankInfo*)a)->val - ((RankInfo*)b)->val;
        -:  118:	return (diff > 0) - (diff < 0);
        -:  119:}
        -:  120:
        -:  121:static int compare_index(const void *restrict a, const void *restrict b) {
        -:  122:	return ((RankInfo*)a)->idx - ((RankInfo*)b)->idx;
        -:  123:}
        -:  124:
        -:  125:static void compute_ranks(double *restrict data, double *restrict ranks, size_t n) {
        -:  126:	RankInfo *restrict items = safemalloc(n * sizeof(RankInfo));
        -:  127:	for (size_t i = 0; i < n; i++) {
        -:  128:		items[i].val = data[i];
        -:  129:		items[i].idx = i;
        -:  130:	}
        -:  131:	qsort(items, n, sizeof(RankInfo), compare_rank);
        -:  132:	// Handle ties by averaging ranks
        -:  133:	for (size_t i = 0; i < n; ) {
        -:  134:		size_t j = i + 1;
        -:  135:		while (j < n && items[j].val == items[i].val) j++;
        -:  136:		double avg_rank = (i + 1 + j) / 2.0;
        -:  137:		for (size_t k = i; k < j; k++) items[k].rank = avg_rank;
        -:  138:		i = j;
        -:  139:	}
        -:  140:	qsort(items, n, sizeof(RankInfo), compare_index);
        -:  141:	for (size_t i = 0; i < n; i++) ranks[i] = items[i].rank;
        -:  142:	Safefree(items);
        -:  143:}
        -:  144:// Generates a single binomial random variate. 
        -:  145://Uses the standard Bernoulli trial loop. Drand01() taps into Perl's PRNG.
        -:  146:static size_t generate_binomial(const size_t size, const double prob) {
        -:  147:	if (prob <= 0.0) return 0;
        -:  148:	if (prob >= 1.0) return size;
        -:  149:
        -:  150:	size_t successes = 0;
        -:  151:	for (size_t i = 0; i < size; i++) {
        -:  152:		if (Drand01() <= prob) successes++;
        -:  153:	}
        -:  154:	return successes;
        -:  155:}
        -:  156:// Helper: log combination
        -:  157:static double log_choose(size_t n, size_t k) {
        -:  158:	return lgamma((double)n + 1.0) - lgamma((double)k + 1.0) - lgamma((double)(n - k) + 1.0);
        -:  159:}
        -:  160:
        -:  161:// Log-space tails for non-central hypergeometric
        -:  162:static void calc_tails_logspace(size_t a, size_t min_x, size_t max_x, double omega, const double *logdc, double *restrict lower_tail, double *restrict upper_tail) {
        -:  163:	double max_d = -1e300, log_omega = log(omega);
        -:  164:
        -:  165:	for(size_t k = 0; k <= max_x - min_x; ++k) {
        -:  166:	  double d_val = logdc[k] + log_omega * (min_x + k);
        -:  167:	  if (d_val > max_d) max_d = d_val;
        -:  168:	}
        -:  169:
        -:  170:	double sum_d = 0.0;
        -:  171:	for(size_t k = 0; k <= max_x - min_x; ++k) {
        -:  172:	  sum_d += exp(logdc[k] + log_omega * (min_x + k) - max_d);
        -:  173:	}
        -:  174:
        -:  175:	*lower_tail = 0.0;
        -:  176:	*upper_tail = 0.0;
        -:  177:
        -:  178:	for(size_t k = 0; k <= max_x - min_x; ++k) {
        -:  179:	  double p_prob = exp(logdc[k] + log_omega * (min_x + k) - max_d) / sum_d;
        -:  180:	  if (min_x + k <= a) *lower_tail += p_prob;
        -:  181:	  if (min_x + k >= a) *upper_tail += p_prob;
        -:  182:	}
        -:  183:}
        -:  184:
        -:  185:// Exact stats using log-space
        -:  186:static void calculate_exact_stats(size_t a, size_t b, size_t c, size_t d, double conf_level, const char*restrict alt, double *restrict mle_or, double *restrict ci_low, double *restrict ci_high) {
        -:  187:    double alpha = 1.0 - conf_level;
        -:  188:    size_t r1 = a + b, r2 = c + d, c1 = a + c;
        -:  189:    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
        -:  190:    size_t max_x = (r1 < c1) ? r1 : c1;
        -:  191:    
        -:  192:    bool is_less = (strcmp(alt, "less") == 0);
        -:  193:    bool is_greater = (strcmp(alt, "greater") == 0);
        -:  194:
        -:  195:    double *restrict logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
        -:  196:    double denom = log_choose(r1 + r2, c1);
        -:  197:    for(size_t x = min_x; x <= max_x; ++x) {
        -:  198:        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
        -:  199:    }
        -:  200:
        -:  201:    // MLE
        -:  202:    if (a == min_x && a == max_x) *mle_or = 1.0;
        -:  203:    else if (a == min_x) *mle_or = 0.0;
        -:  204:    else if (a == max_x) *mle_or = INFINITY;
        -:  205:    else {
        -:  206:        double log_low = -100.0, log_high = 100.0;
        -:  207:        for (unsigned short int i = 0; i < 3000; i++) {
        -:  208:            double log_mid = 0.5 * (log_low + log_high);
        -:  209:            double max_d = -1e300;
        -:  210:            for(size_t k = 0; k <= max_x - min_x; ++k) {
        -:  211:                double d_val = logdc[k] + log_mid * (min_x + k);
        -:  212:                if (d_val > max_d) max_d = d_val;
        -:  213:            }
        -:  214:            double sum_d = 0.0, exp_val = 0.0;
        -:  215:            for(size_t k = 0; k <= max_x - min_x; ++k) {
        -:  216:                double p_prob = exp(logdc[k] + log_mid * (min_x + k) - max_d);
        -:  217:                sum_d += p_prob;
        -:  218:                exp_val += (min_x + k) * p_prob;
        -:  219:            }
        -:  220:            exp_val /= sum_d;
        -:  221:            
        -:  222:            if (exp_val > a) log_high = log_mid;
        -:  223:            else log_low = log_mid;
        -:  224:            if (log_high - log_low < 1e-15) break;
        -:  225:        }
        -:  226:        *mle_or = exp(0.5 * (log_low + log_high));
        -:  227:    }
        -:  228:
        -:  229:    *ci_low = 0.0;
        -:  230:    *ci_high = INFINITY;
        -:  231:
        -:  232:    // Lower CI
        -:  233:    if (!is_less) { 
        -:  234:        double target_alpha = is_greater ? alpha : alpha / 2.0;
        -:  235:        if (a != min_x) {
        -:  236:            double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
        -:  237:            for (unsigned short int i = 0; i < 1000; i++) {
        -:  238:                double log_mid = 0.5 * (log_low + log_high);
        -:  239:                double mid = exp(log_mid);
        -:  240:                calc_tails_logspace(a, min_x, max_x, mid, logdc, &lt, &ut);
        -:  241:                double err = fabs(ut - target_alpha);
        -:  242:                if (err < best_err) { best_err = err; best = mid; }
        -:  243:                if (ut > target_alpha) log_high = log_mid;
        -:  244:                else log_low = log_mid;
        -:  245:                if (log_high - log_low < 1e-15) break;
        -:  246:            }
        -:  247:            *ci_low = best;
        -:  248:        }
        -:  249:    }
        -:  250:
        -:  251:    // Upper CI
        -:  252:    if (!is_greater) { 
        -:  253:        double target_alpha = is_less ? alpha : alpha / 2.0;
        -:  254:        if (a != max_x) {
        -:  255:            double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
        -:  256:            for (unsigned short int i = 0; i < 1000; i++) {
        -:  257:                double log_mid = 0.5 * (log_low + log_high);
        -:  258:                double mid = exp(log_mid);
        -:  259:                calc_tails_logspace(a, min_x, max_x, mid, logdc, &lt, &ut);
        -:  260:                double err = fabs(lt - target_alpha);
        -:  261:                if (err < best_err) { best_err = err; best = mid; }
        -:  262:                if (lt > target_alpha) log_low = log_mid;
        -:  263:                else log_high = log_mid;
        -:  264:                if (log_high - log_low < 1e-15) break;
        -:  265:            }
        -:  266:            *ci_high = best;
        -:  267:        }
        -:  268:    }
        -:  269:    safefree(logdc);
        -:  270:}
        -:  271:
        -:  272:// Exact p-value using log-space
        -:  273:static double exact_p_value(size_t a, size_t b, size_t c, size_t d, const char* alt) {
        -:  274:    size_t r1 = a + b, r2 = c + d, c1 = a + c;
        -:  275:    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
        -:  276:    size_t max_x = (r1 < c1) ? r1 : c1;
        -:  277:
        -:  278:    double *logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
        -:  279:    double denom = log_choose(r1 + r2, c1);
        -:  280:    for(size_t x = min_x; x <= max_x; ++x) {
        -:  281:        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
        -:  282:    }
        -:  283:
        -:  284:    double p_val = 0.0;
        -:  285:    
        -:  286:    if (strcmp(alt, "less") == 0) {
        -:  287:        for(size_t x = min_x; x <= a; ++x) p_val += exp(logdc[x - min_x]);
        -:  288:    } else if (strcmp(alt, "greater") == 0) {
        -:  289:        for(size_t x = a; x <= max_x; ++x) p_val += exp(logdc[x - min_x]);
        -:  290:    } else {
        -:  291:        double p_obs = exp(logdc[a - min_x]);
        -:  292:        double relErr = 1.0 + 1e-7;
        -:  293:        for(size_t x = min_x; x <= max_x; ++x) {
        -:  294:            double p_cur = exp(logdc[x - min_x]);
        -:  295:            if (p_cur <= p_obs * relErr) p_val += p_cur;
        -:  296:        }
        -:  297:    }
        -:  298:    
        -:  299:    safefree(logdc);
        -:  300:    return (p_val > 1.0) ? 1.0 : p_val;
        -:  301:}
        -:  302:/* -----------------------------------------------------------------------
        -:  303: * Helpers for lm Linear Regression: OLS Matrix Math & Formula Parsing
        -:  304: * ----------------------------------------------------------------------- */
        -:  305:
        -:  306:/* Sweep operator for symmetric positive-definite matrices (e.g., XtX).
        -:  307: * This gracefully handles collinearity by bypassing aliased columns.
        -:  308: * Utilizes a relative tolerance check to prevent dropping micro-variance features.
        -:  309: */
        -:  310:static int sweep_matrix_ols(double *restrict A, size_t n, bool *restrict aliased) {
        -:  311:	int rank = 0;
        -:  312:	double *restrict orig_diag = (double*)safemalloc(n * sizeof(double));
        -:  313:
        -:  314:	// Save the original diagonal values to use as a baseline for relative variance
        -:  315:	for (size_t k = 0; k < n; k++) {
        -:  316:		aliased[k] = FALSE;
        -:  317:		orig_diag[k] = A[k * n + k];
        -:  318:	}
        -:  319:
        -:  320:	for (size_t k = 0; k < n; k++) {
        -:  321:		// Check pivot for collinearity using a RELATIVE tolerance
        -:  322:		// (Fallback to a tiny absolute tolerance of 1e-24 to catch literal zero vectors)
        -:  323:		if (fabs(A[k * n + k]) <= 1e-10 * orig_diag[k] || fabs(A[k * n + k]) < 1e-24) {
        -:  324:			aliased[k] = TRUE;
        -:  325:			// Isolate this column so it doesn't affect the rest of the matrix
        -:  326:			for (size_t i = 0; i < n; i++) { 
        -:  327:				A[k * n + i] = 0.0; 
        -:  328:				A[i * n + k] = 0.0; 
        -:  329:			}
        -:  330:			continue;
        -:  331:		}
        -:  332:		rank++;
        -:  333:		double pivot = 1.0 / A[k * n + k];
        -:  334:		A[k * n + k] = 1.0;
        -:  335:		for (size_t j = 0; j < n; j++) A[k * n + j] *= pivot;
        -:  336:		for (size_t i = 0; i < n; i++) {
        -:  337:			if (i != k && A[i * n + k] != 0.0) {
        -:  338:				  double factor = A[i * n + k];
        -:  339:				  A[i * n + k] = 0.0;
        -:  340:				  for (size_t j = 0; j < n; j++) {
        -:  341:				       A[i * n + j] -= factor * A[k * n + j];
        -:  342:				  }
        -:  343:			}
        -:  344:		}
        -:  345:	}
        -:  346:	Safefree(orig_diag);
        -:  347:	return rank;
        -:  348:}
        -:  349:
        -:  350:// Internal extractor resolving single data values. Returns NAN on missing or non-numeric.
        -:  351:static double get_data_value(HV *restrict data_hoa, HV **restrict row_hashes, unsigned int i, const char *restrict var) {
        -:  352:    SV **restrict val = NULL;
        -:  353:    if (row_hashes) {
        -:  354:        val = hv_fetch(row_hashes[i], var, strlen(var), 0);
        -:  355:        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
        -:  356:            AV*restrict av = (AV*)SvRV(*val);
        -:  357:            val = av_fetch(av, 0, 0);
        -:  358:        }
        -:  359:    } else if (data_hoa) {
        -:  360:        SV**restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
        -:  361:        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
        -:  362:            AV*restrict av = (AV*)SvRV(*col);
        -:  363:            val = av_fetch(av, i, 0);
        -:  364:        }
        -:  365:    }
        -:  366:    if (val && SvOK(*val)) {
        -:  367:        if (looks_like_number(*val)) return SvNV(*val);
        -:  368:        return NAN; // Catch strings like "blue"
        -:  369:    }
        -:  370:    return NAN; // Catch undef/missing keys
        -:  371:}
        -:  372:
        -:  373:// Helper: Get all available columns for the '.' operator expansion
        -:  374:static AV* get_all_columns(HV *restrict data_hoa, HV **restrict row_hashes, size_t n) {
        -:  375:    AV *cols = newAV();
        -:  376:    if (data_hoa) {
        -:  377:        hv_iterinit(data_hoa);
        -:  378:        HE *entry;
        -:  379:        while ((entry = hv_iternext(data_hoa))) {
        -:  380:            av_push(cols, newSVsv(hv_iterkeysv(entry)));
        -:  381:        }
        -:  382:    } else if (row_hashes && n > 0 && row_hashes[0]) {
        -:  383:        hv_iterinit(row_hashes[0]);
        -:  384:        HE *entry;
        -:  385:        while ((entry = hv_iternext(row_hashes[0]))) {
        -:  386:            av_push(cols, newSVsv(hv_iterkeysv(entry)));
        -:  387:        }
        -:  388:    }
        -:  389:    return cols;
        -:  390:}
        -:  391:
        -:  392:// Recursive formula resolver with tightened NaN and Null handling
        -:  393:static double evaluate_term(HV *restrict data_hoa, HV **restrict row_hashes, unsigned int i, const char *restrict term) {
        -:  394:    if (!term || term[0] == '\0') return NAN;
        -:  395:
        -:  396:    char *restrict term_cpy = savepv(term); 
        -:  397:    char *restrict colon = strchr(term_cpy, ':');
        -:  398:    if (colon) {
        -:  399:        *colon = '\0';
        -:  400:        double left = evaluate_term(data_hoa, row_hashes, i, term_cpy);
        -:  401:        double right = evaluate_term(data_hoa, row_hashes, i, colon + 1);
        -:  402:        Safefree(term_cpy); 
        -:  403:        
        -:  404:        if (isnan(left) || isnan(right)) return NAN;
        -:  405:        return left * right;
        -:  406:    }
        -:  407:    if (strncmp(term_cpy, "I(", 2) == 0) {
        -:  408:        char *restrict end = strrchr(term_cpy, ')');
        -:  409:        if (end) *end = '\0';
        -:  410:        char *restrict inner = term_cpy + 2;
        -:  411:        char *restrict caret = strchr(inner, '^');
        -:  412:        int power = 1;
        -:  413:        if (caret) {
        -:  414:            *caret = '\0';
        -:  415:            power = atoi(caret + 1);
        -:  416:        }
        -:  417:        double v = get_data_value(data_hoa, row_hashes, i, inner);
        -:  418:        Safefree(term_cpy); 
        -:  419:        
        -:  420:        if (isnan(v)) return NAN;
        -:  421:        return power == 1 ? v : pow(v, power);
        -:  422:    }
        -:  423:    double result = get_data_value(data_hoa, row_hashes, i, term_cpy);
        -:  424:    Safefree(term_cpy); 
        -:  425:    return result;
        -:  426:}
        -:  427:
        -:  428:// Helper to infer column type from its first valid element
        -:  429:static bool is_column_categorical(HV *restrict data_hoa, HV **restrict row_hashes, size_t n, const char *restrict var) {
        -:  430:	for (size_t i = 0; i < n; i++) {
        -:  431:		SV **restrict val = NULL;
        -:  432:		if (row_hashes) {
        -:  433:			val = hv_fetch(row_hashes[i], var, strlen(var), 0);
        -:  434:			if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
        -:  435:				 AV*restrict av = (AV*)SvRV(*val);
        -:  436:				 val = av_fetch(av, 0, 0);
        -:  437:			}
        -:  438:		} else if (data_hoa) {
        -:  439:			SV **restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
        -:  440:			if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
        -:  441:				 AV*restrict av = (AV*)SvRV(*col);
        -:  442:				 val = av_fetch(av, i, 0);
        -:  443:			}
        -:  444:		}
        -:  445:		if (val && SvOK(*val)) {
        -:  446:			if (looks_like_number(*val)) return FALSE; // First valid is number -> Numeric Column
        -:  447:			return TRUE; // First valid is string -> Categorical Column
        -:  448:		}
        -:  449:	}
        -:  450:	return FALSE;
        -:  451:}
        -:  452:
        -:  453:/* Internal extractor resolving single data string values using dynamic allocation. */
        -:  454:static char* get_data_string_alloc(HV *restrict data_hoa, HV **restrict row_hashes, size_t i, const char *restrict var) {
        -:  455:	SV **restrict val = NULL;
        -:  456:	if (row_hashes) {
        -:  457:		val = hv_fetch(row_hashes[i], var, strlen(var), 0);
        -:  458:		if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
        -:  459:			AV*restrict av = (AV*)SvRV(*val);
        -:  460:			val = av_fetch(av, 0, 0);
        -:  461:		}
        -:  462:	} else if (data_hoa) {
        -:  463:		SV **restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
        -:  464:		if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
        -:  465:			AV*restrict av = (AV*)SvRV(*col);
        -:  466:			val = av_fetch(av, i, 0);
        -:  467:		}
        -:  468:	}
        -:  469:	if (val && SvOK(*val)) {
        -:  470:	  return savepv(SvPV_nolen(*val)); /* Allocates and returns string */
        -:  471:	}
        -:  472:	return NULL;
        -:  473:}
        -:  474:
        -:  475:// Struct for sorting p-values while remembering their original index
        -:  476:typedef struct {
        -:  477:	double p;
        -:  478:	size_t orig_idx;
        -:  479:} PVal;
        -:  480:
        -:  481:// Comparator for qsort
        -:  482:static int cmp_pval(const void *restrict a, const void *restrict b) {
        -:  483:	double diff = ((PVal*)a)->p - ((PVal*)b)->p;
        -:  484:	if (diff < 0) return -1;
        -:  485:	if (diff > 0) return 1;
        -:  486:	/* Stabilize sort by falling back to original index */
        -:  487:	return ((PVal*)a)->orig_idx - ((PVal*)b)->orig_idx; 
        -:  488:}
        -:  489:/* -----------------------------------------------------------------------
        -:  490: * Helpers for cor(): ranking (Spearman), Pearson r, Kendall tau-b
        -:  491: * ----------------------------------------------------------------------- */
        -:  492:/* Item used to sort values while remembering their original index,
        -:  493: * needed for average-rank tie-breaking in Spearman correlation.        */
        -:  494:typedef struct {
        -:  495:	double val;
        -:  496:	size_t idx;
        -:  497:} RankItem;
        -:  498:
        -:  499:static int cmp_rank_item(const void *restrict a, const void *restrict b) {
        -:  500:	double diff = ((RankItem*)a)->val - ((RankItem*)b)->val;
        -:  501:	if (diff < 0) return -1;
        -:  502:	if (diff > 0) return  1;
        -:  503:	return 0;
        -:  504:}
        -:  505:
        -:  506:/* Compute 1-based average ranks with tie-breaking into out[].
        -:  507: * in[] is not modified.                                                 */
        -:  508:static void rank_data(const double *restrict in, double *restrict out, size_t n) {
        -:  509:	RankItem *restrict ri;
        -:  510:	Newx(ri, n, RankItem);
        -:  511:	for (size_t i = 0; i < n; i++) { ri[i].val = in[i]; ri[i].idx = i; }
        -:  512:	qsort(ri, n, sizeof(RankItem), cmp_rank_item);
        -:  513:
        -:  514:	size_t i = 0;
        -:  515:	while (i < n) {
        -:  516:		size_t j = i;
        -:  517:		/* Find the full extent of this tie group */
        -:  518:		while (j + 1 < n && ri[j + 1].val == ri[j].val) j++;
        -:  519:		/* All members get the average of ranks i+1 … j+1 (1-based) */
        -:  520:		double avg = (double)(i + j) / 2.0 + 1.0;
        -:  521:		for (size_t k = i; k <= j; k++) out[ri[k].idx] = avg;
        -:  522:		i = j + 1;
        -:  523:	}
        -:  524:	Safefree(ri);
        -:  525:}
        -:  526:
        -:  527:/* Pearson product-moment r between two n-element arrays.
        -:  528: * Returns NAN when either variable has zero variance (matches R).       */
        -:  529:static double pearson_corr(const double *restrict x, const double *restrict y, size_t n) {
        -:  530:	double sx = 0, sy = 0, sxy = 0, sx2 = 0, sy2 = 0;
        -:  531:	for (size_t i = 0; i < n; i++) {
        -:  532:	  sx  += x[i];     sy  += y[i];
        -:  533:	  sxy += x[i]*y[i]; sx2 += x[i]*x[i]; sy2 += y[i]*y[i];
        -:  534:	}
        -:  535:	double num = (double)n * sxy - sx * sy;
        -:  536:	double den = sqrt(((double)n * sx2 - sx*sx) * ((double)n * sy2 - sy*sy));
        -:  537:	if (den == 0.0) return NAN;
        -:  538:	return num / den;
        -:  539:}
        -:  540:
        -:  541:/* Kendall's tau-b between two n-element arrays.
        -:  542: *
        -:  543: *   tau-b = (C − D) / sqrt((C + D + T_x)(C + D + T_y))
        -:  544: *
        -:  545: * where C = concordant pairs, D = discordant, T_x = pairs tied only on
        -:  546: * x, T_y = pairs tied only on y.  Joint ties (both zero) are excluded
        -:  547: * from numerator and denominator, matching R's cor(method="kendall").
        -:  548: * Returns NAN when the denominator is zero.                             */
        -:  549:static double kendall_tau_b(const double *restrict x, const double *restrict y, unsigned int n) {
        -:  550:	size_t C = 0, D = 0, tie_x = 0, tie_y = 0;
        -:  551:	for (size_t i = 0; i < n - 1; i++) {
        -:  552:	  for (size_t j = i + 1; j < n; j++) {
        -:  553:		   int sx = (x[i] > x[j]) - (x[i] < x[j]);   /* sign of x[i]-x[j] */
        -:  554:		   int sy = (y[i] > y[j]) - (y[i] < y[j]);
        -:  555:		   if      (sx == 0 && sy == 0) { /* joint tie — not counted */ }
        -:  556:		   else if (sx == 0)            tie_x++;
        -:  557:		   else if (sy == 0)            tie_y++;
        -:  558:		   else if (sx == sy)           C++;
        -:  559:		   else                         D++;
        -:  560:	  }
        -:  561:	}
        -:  562:	double denom = sqrt((double)(C + D + tie_x) * (double)(C + D + tie_y));
        -:  563:	if (denom == 0.0) return NAN;
        -:  564:	return (double)(C - D) / denom;
        -:  565:}
        -:  566:
        -:  567:/* Single dispatch: compute correlation according to method string.
        -:  568: * Allocates and frees temporary rank arrays internally for Spearman.   */
        -:  569:static double compute_cor(const double *restrict x, const double *restrict y,
        -:  570:                           size_t n, const char *restrict method) {
        -:  571:	if (strcmp(method, "spearman") == 0) {
        -:  572:	  double *restrict rx, *restrict ry;
        -:  573:	  Newx(rx, n, double); Newx(ry, n, double);
        -:  574:	  rank_data(x, rx, n);
        -:  575:	  rank_data(y, ry, n);
        -:  576:	  double r = pearson_corr(rx, ry, n);
        -:  577:	  Safefree(rx); Safefree(ry);
        -:  578:	  return r;
        -:  579:	}
        -:  580:	if (strcmp(method, "kendall") == 0)
        -:  581:	  return kendall_tau_b(x, y, n);
        -:  582:	/* default: pearson */
        -:  583:	return pearson_corr(x, y, n);
        -:  584:}
        -:  585:
        -:  586:// Math macros
        -:  587:#define MAX_ITER 500
        -:  588:#define EPS 3.0e-15
        -:  589:#define FPMIN 1.0e-30
        -:  590:
        -:  591:static double _incbeta_cf(double a, double b, double x) {
        -:  592:	int m;
        -:  593:	double aa, c, d, del, h, qab, qam, qap;
        -:  594:	qab = a + b; qap = a + 1.0; qam = a - 1.0;
        -:  595:	c = 1.0; d = 1.0 - qab * x / qap;
        -:  596:	if (fabs(d) < FPMIN) d = FPMIN;
        -:  597:	d = 1.0 / d; h = d;
        -:  598:	for (m = 1; m <= MAX_ITER; m++) {
        -:  599:	  int m2 = 2 * m;
        -:  600:	  aa = m * (b - m) * x / ((qam + m2) * (a + m2));
        -:  601:	  d = 1.0 + aa * d;
        -:  602:	  if (fabs(d) < FPMIN) d = FPMIN;
        -:  603:	  c = 1.0 + aa / c;
        -:  604:	  if (fabs(c) < FPMIN) c = FPMIN;
        -:  605:	  d = 1.0 / d; h *= d * c;
        -:  606:	  aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
        -:  607:	  d = 1.0 + aa * d;
        -:  608:	  if (fabs(d) < FPMIN) d = FPMIN;
        -:  609:	  c = 1.0 + aa / c;
        -:  610:	  if (fabs(c) < FPMIN) c = FPMIN;
        -:  611:	  d = 1.0 / d; del = d * c; h *= del;
        -:  612:	  if (fabs(del - 1.0) < EPS) break;
        -:  613:	}
        -:  614:	return h;
        -:  615:}
        -:  616:
        -:  617:static double incbeta(double a, double b, double x) {
        -:  618:	if (x <= 0.0) return 0.0;
        -:  619:	if (x >= 1.0) return 1.0;
        -:  620:	double bt = exp(lgamma(a + b) - lgamma(a) - lgamma(b) + a * log(x) + b * log(1.0 - x));
        -:  621:	if (x < (a + 1.0) / (a + b + 2.0)) return bt * _incbeta_cf(a, b, x) / a;
        -:  622:	return 1.0 - bt * _incbeta_cf(b, a, 1.0 - x) / b;
        -:  623:}
        -:  624:
        -:  625:static double get_t_pvalue(double t, double df, const char*restrict alt) {
        -:  626:	double x = df / (df + t * t);
        -:  627:	double prob_2tail = incbeta(df / 2.0, 0.5, x);
        -:  628:	if (strcmp(alt, "less") == 0) return (t < 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
        -:  629:	if (strcmp(alt, "greater") == 0) return (t > 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
        -:  630:	return prob_2tail;
        -:  631:}
        -:  632:
        -:  633:// Bisection algorithm to find the inverse t-distribution (Critical t-value)
        -:  634:static double qt_tail(double df, double p_tail) {
        -:  635:	double low = 0.0, high = 1.0;
        -:  636:	// Find upper bound
        -:  637:	while (get_t_pvalue(high, df, "greater") > p_tail) {
        -:  638:	  low = high;
        -:  639:	  high *= 2.0;
        -:  640:	  if (high > 1000000.0) break; /* Fallback limit */
        -:  641:	}
        -:  642:	// Bisect to find the root
        -:  643:	for (unsigned short int i = 0; i < 100; i++) {
        -:  644:	  double mid = (low + high) / 2.0;
        -:  645:	  double p_mid = get_t_pvalue(mid, df, "greater");
        -:  646:	  if (p_mid > p_tail) {
        -:  647:		   low = mid;
        -:  648:	  } else {
        -:  649:		   high = mid;
        -:  650:	  }
        -:  651:	  if (high - low < 1e-8) break;
        -:  652:	}
        -:  653:	return (low + high) / 2.0;
        -:  654:}
        -:  655:
        -:  656:int compare_doubles(const void *restrict a, const void *restrict b) {
        -:  657:	double da = *(const double*restrict)a;
        -:  658:	double db = *(const double*restrict)b;
        -:  659:	return (da > db) - (da < db);
        -:  660:}
        -:  661:/* Helper to calculate the number of bins using Sturges' formula: log2(n) + 1 */
        -:  662:static size_t calculate_sturges_bins(size_t n) {
        -:  663:	if (n == 0) return 1;
        -:  664:	return (size_t)(log((double)n) / log(2.0) + 1.0);
        -:  665:}
        -:  666:
        -:  667:// Logic for distributing data into bins (Optimized to O(N))
        -:  668:static void compute_hist_logic(double *restrict x, size_t n, double *restrict breaks, size_t n_bins, 
        -:  669:                               size_t *restrict counts, double *restrict mids, double *restrict density) {
        -:  670:	double total_n = (double)n;
        -:  671:	double min_val = breaks[0];
        -:  672:	double step = (n_bins > 0) ? (breaks[1] - breaks[0]) : 0.0;
        -:  673:	// Initialize counts and compute midpoints
        -:  674:	for (size_t i = 0; i < n_bins; i++) {
        -:  675:	  counts[i] = 0;
        -:  676:	  mids[i] = (breaks[i] + breaks[i+1]) / 2.0;
        -:  677:	}
        -:  678:	// Single O(N) pass to assign elements to bins
        -:  679:	if (step > 0.0) {
        -:  680:	  for (size_t j = 0; j < n; j++) {
        -:  681:		   double val = x[j];
        -:  682:		   // Ignore out-of-bounds or invalid values
        -:  683:		   if (isnan(val) || isinf(val) || val < min_val) continue;
        -:  684:		   // Calculate initial bin index mathematically
        -:  685:		   size_t idx = (size_t)((val - min_val) / step);
        -:  686:		   // Clamp to valid array bounds first to prevent overflow */
        -:  687:		   if (idx >= n_bins) {
        -:  688:		       idx = n_bins - 1;
        -:  689:		   }
        -:  690:		   /* Adjust for exact boundaries (R's right-inclusive default: (a, b]) */
        -:  691:		   /* If value is exactly on or slightly below the lower boundary of the assigned bin, 
        -:  692:		      it belongs in the previous bin. (First bin [a, b] is inclusive on both ends) */
        -:  693:		   while (idx > 0 && val <= breaks[idx]) {
        -:  694:		       idx--;
        -:  695:		   }
        -:  696:		   // Conversely, if floating-point truncation placed it too low, push it up
        -:  697:		   while (idx < n_bins - 1 && val > breaks[idx + 1]) {
        -:  698:		       idx++;
        -:  699:		   }
        -:  700:		   counts[idx]++;
        -:  701:	  }
        -:  702:	} else if (n_bins > 0) {
        -:  703:	  // Edge case: All data points have the exact same value (step == 0)
        -:  704:	  counts[0] = n;
        -:  705:	}
        -:  706:	// Compute densities
        -:  707:	for (size_t i = 0; i < n_bins; i++) {
        -:  708:	  double bin_width = breaks[i+1] - breaks[i];
        -:  709:	  if (bin_width > 0) {
        -:  710:		   density[i] = (double)counts[i] / (total_n * bin_width);
        -:  711:	  } else {
        -:  712:		   density[i] = (n_bins == 1) ? 1.0 : 0.0;
        -:  713:	  }
        -:  714:	}
        -:  715:}
        -:  716:
        -:  717:// Standard Normal CDF approximation
        -:  718:double approx_pnorm(double x) {
        -:  719:	return 0.5 * erfc(-x * 0.70710678118654752440); // 0.707... = 1/sqrt(2)
        -:  720:}
        -:  721:#ifndef M_SQRT1_2
        -:  722:#define M_SQRT1_2 0.70710678118654752440
        -:  723:#endif
        -:  724:
        -:  725:/* Macro for exact Wilcoxon 3D array indexing */
        -:  726:#define DP_INDEX(i, j, k, n2, max_u) ((i) * ((n2) + 1) * ((max_u) + 1) + (j) * ((max_u) + 1) + (k))
        -:  727:static double inverse_normal_cdf(double p) {
        -:  728:	double a[4] = {2.50662823884, -18.61500062529, 41.39119773534, -25.44106049637};
        -:  729:	double b[4] = {-8.47351093090, 23.08336743743, -21.06224101826, 3.13082909833};
        -:  730:	double c[9] = {0.3374754822726147, 0.9761690190917186, 0.1607979714918209,
        -:  731:		          0.0276438810333863, 0.0038405729373609, 0.0003951896511919,
        -:  732:		          0.0000321767881768, 0.0000002888167364, 0.0000003960315187};
        -:  733:	double x, r, y;
        -:  734:	y = p - 0.5;
        -:  735:	if (fabs(y) < 0.42) {
        -:  736:	  r = y * y;
        -:  737:	  x = y * (((a[3]*r + a[2])*r + a[1])*r + a[0]) /
        -:  738:		       ((((b[3]*r + b[2])*r + b[1])*r + b[0])*r + 1.0);
        -:  739:	} else {
        -:  740:	  r = p;
        -:  741:	  if (y > 0) r = 1.0 - p;
        -:  742:	  r = log(-log(r));
        -:  743:	  x = c[0] + r * (c[1] + r * (c[2] + r * (c[3] + r * (c[4] +
        -:  744:		   r * (c[5] + r * (c[6] + r * (c[7] + r * c[8])))))));
        -:  745:	  if (y < 0) x = -x;
        -:  746:	}
        -:  747:	return x;
        -:  748:}
        -:  749:/* -----------------------------------------------------------------------
        -:  750: * Exact Spearman p-value via exhaustive permutation enumeration.
        -:  751: *
        -:  752: * Under H0, all n! orderings of ranks are equally probable.  We visit
        -:  753: * every permutation of {1..n} with Heap's algorithm (O(n!), no allocs
        -:  754: * inside the loop) and count how many yield S ≤ s_obs ("lower tail",
        -:  755: * i.e. rho ≥ rho_obs) and how many yield S ≥ s_obs ("upper tail").
        -:  756: *
        -:  757: * Mirrors R's default: exact = (n < 10) with no ties.
        -:  758: * Valid up to n = 9 (362 880 iterations — negligible cost).
        -:  759: * ----------------------------------------------------------------------- */
        -:  760:static double spearman_exact_pvalue(double s_obs, size_t n, const char *restrict alt) {
        -:  761:	int *restrict perm = (int*)safemalloc(n * sizeof(int));
        -:  762:	int *restrict c    = (int*)safemalloc(n * sizeof(int));
        -:  763:	for (size_t i = 0; i < n; i++) { perm[i] = i + 1; c[i] = 0; }
        -:  764:
        -:  765:	long count_le = 0, count_ge = 0, total = 0;
        -:  766:
        -:  767:	#define TALLY_PERM() do {                                    \
        -:  768:	  double s_ = 0.0;                                     \
        -:  769:	  for (int ii = 0; ii < n; ii++) {                    \
        -:  770:		   double d_ = (double)(ii + 1) - (double)perm[ii];\
        -:  771:		   s_ += d_ * d_;                                   \
        -:  772:	  }                                                    \
        -:  773:	  if (s_ <= s_obs + 1e-9) count_le++;                 \
        -:  774:	  if (s_ >= s_obs - 1e-9) count_ge++;                 \
        -:  775:	  total++;                                             \
        -:  776:	} while (0)
        -:  777:
        -:  778:	TALLY_PERM();   /* initial permutation [1, 2, ..., n] */
        -:  779:
        -:  780:	unsigned int k = 1;
        -:  781:	while (k < n) {
        -:  782:	  if (c[k] < k) {
        -:  783:		   int tmp;
        -:  784:		   if (k % 2 == 0) {
        -:  785:		       tmp = perm[0]; perm[0] = perm[k]; perm[k] = tmp;
        -:  786:		   } else {
        -:  787:		       tmp = perm[c[k]]; perm[c[k]] = perm[k]; perm[k] = tmp;
        -:  788:		   }
        -:  789:		   TALLY_PERM();
        -:  790:		   c[k]++;
        -:  791:		   k = 1;
        -:  792:	  } else {
        -:  793:		   c[k] = 0;
        -:  794:		   k++;
        -:  795:	  }
        -:  796:	}
        -:  797:	#undef TALLY_PERM
        -:  798:
        -:  799:	Safefree(perm); Safefree(c);
        -:  800:	/* p_le = P(S ≤ s_obs) ≡ P(rho ≥ rho_obs)  — upper rho tail
        -:  801:	* p_ge = P(S ≥ s_obs) ≡ P(rho ≤ rho_obs)  — lower rho tail  */
        -:  802:	double p_le = (double)count_le / (double)total;
        -:  803:	double p_ge = (double)count_ge / (double)total;
        -:  804:
        -:  805:	if (strcmp(alt, "greater") == 0) return p_le;
        -:  806:	if (strcmp(alt, "less")    == 0) return p_ge;
        -:  807:	/* two.sided: 2 × the smaller tail, clamped to 1 */
        -:  808:	double p = 2.0 * (p_le < p_ge ? p_le : p_ge);
        -:  809:	return (p > 1.0) ? 1.0 : p;
        -:  810:}
        -:  811:/* -----------------------------------------------------------------------
        -:  812: * Exact Kendall p-value via Mahonian Numbers (Inversions distribution)
        -:  813: * Matches R's behavior for N < 50 without ties.
        -:  814: * ----------------------------------------------------------------------- */
        -:  815:static double kendall_exact_pvalue(size_t n, double s_obs, const char *restrict alt) {
        -:  816:	long max_inv = (long)n * (n - 1) / 2;
        -:  817:	double *restrict dp = (double*)safemalloc((max_inv + 1) * sizeof(double));
        -:  818:	for (long i = 0; i <= max_inv; i++) dp[i] = 0.0;
        -:  819:	dp[0] = 1.0;
        -:  820:	/* Build the distribution of inversions via DP */
        -:  821:	for (size_t i = 2; i <= n; i++) {
        -:  822:	  double *restrict next_dp = (double*)safemalloc((max_inv + 1) * sizeof(double));
        -:  823:	  for (long k = 0; k <= max_inv; k++) next_dp[k] = 0.0;
        -:  824:	  int current_max_inv = i * (i - 1) / 2;
        -:  825:	  for (int k = 0; k <= current_max_inv; k++) {
        -:  826:		   double sum = 0;
        -:  827:		   for (int j = 0; j <= i - 1 && k - j >= 0; j++) {
        -:  828:		       sum += dp[k - j];
        -:  829:		   }
        -:  830:		   // Divide by 'i' directly to keep array as pure probabilities and prevent overflow
        -:  831:		   next_dp[k] = sum / (double)i;
        -:  832:	  }
        -:  833:	  Safefree(dp);
        -:  834:	  dp = next_dp;
        -:  835:	}
        -:  836:	// Convert S statistic to target number of inversions
        -:  837:	long i_obs = (long)round((max_inv - s_obs) / 2.0);
        -:  838:	if (i_obs < 0) i_obs = 0;
        -:  839:	if (i_obs > max_inv) i_obs = max_inv;
        -:  840:	double p_le = 0.0; /* P(S <= S_obs) */
        -:  841:	for (long k = i_obs; k <= max_inv; k++) p_le += dp[k];
        -:  842:	double p_ge = 0.0; /* P(S >= S_obs) */
        -:  843:	for (long k = 0; k <= i_obs; k++) p_ge += dp[k];
        -:  844:	Safefree(dp);
        -:  845:	if (strcmp(alt, "greater") == 0) return p_ge;
        -:  846:	if (strcmp(alt, "less") == 0) return p_le;
        -:  847:	// two.sided
        -:  848:	double p = 2.0 * (p_ge < p_le ? p_ge : p_le);
        -:  849:	return p > 1.0 ? 1.0 : p;
        -:  850:}
        -:  851:// F-distribution Cumulative Distribution Function P(F <= f)
        -:  852:static double pf(double f, double df1, double df2) {
        -:  853:	if (f <= 0.0) return 0.0;
        -:  854:	double x = (df1 * f) / (df1 * f + df2);
        -:  855:	return incbeta(df1 / 2.0, df2 / 2.0, x);
        -:  856:}
        -:  857:
        -:  858:/* Householder QR Decomposition for Sequential Sums of Squares */
        -:  859:/* Householder QR Decomposition for Sequential Sums of Squares */
        -:  860:static void apply_householder_aov(double** restrict X, double* restrict y, size_t n, size_t p, bool* restrict aliased, size_t* restrict rank_map) {
        -:  861:	size_t r = 0; // Rank/Row tracker
        -:  862:	for (size_t k = 0; k < p; k++) {
        -:  863:		aliased[k] = FALSE;
        -:  864:		if (r >= n) {
        -:  865:			aliased[k] = TRUE;
        -:  866:			continue;
        -:  867:		}
        -:  868:
        -:  869:		double max_val = 0;
        -:  870:		for (size_t i = r; i < n; i++) {
        -:  871:			if (fabs(X[i][k]) > max_val) max_val = fabs(X[i][k]);
        -:  872:		}
        -:  873:		if (max_val < 1e-10) { 
        -:  874:			aliased[k] = TRUE; 
        -:  875:			continue; 
        -:  876:		} // Collinear or zero column
        -:  877:
        -:  878:		double norm = 0;
        -:  879:		for (size_t i = r; i < n; i++) {
        -:  880:			X[i][k] /= max_val;
        -:  881:			norm += X[i][k] * X[i][k];
        -:  882:		}
        -:  883:		norm = sqrt(norm);
        -:  884:		double s = (X[r][k] > 0) ? -norm : norm;
        -:  885:		double u1 = X[r][k] - s;
        -:  886:		X[r][k] = s * max_val;
        -:  887:
        -:  888:		for (size_t j = k + 1; j < p; j++) {
        -:  889:			double dot = u1 * X[r][j];
        -:  890:			for (size_t i = r + 1; i < n; i++) dot += X[i][j] * X[i][k];
        -:  891:			double tau = dot / (s * u1);
        -:  892:			X[r][j] += tau * u1;
        -:  893:			for (size_t i = r + 1; i < n; i++) X[i][j] += tau * X[i][k];
        -:  894:		}
        -:  895:
        -:  896:		// Transform the response vector y
        -:  897:		double dot_y = u1 * y[r];
        -:  898:		for (size_t i = r + 1; i < n; i++) dot_y += y[i] * X[i][k];
        -:  899:		double tau_y = dot_y / (s * u1);
        -:  900:		y[r] += tau_y * u1;
        -:  901:		for (size_t i = r + 1; i < n; i++) y[i] += tau_y * X[i][k];
        -:  902:
        -:  903:		rank_map[k] = r; // Map original column index to orthogonal row index
        -:  904:		r++;
        -:  905:	}
        -:  906:}
        -:  907:
        -:  908:// --- write_table Helpers ---
        -:  909:
        -:  910:// Sorts string arrays alphabetically
        -:  911:static int cmp_string_wt(const void *a, const void *b) {
        -:  912:	return strcmp(*(const char**)a, *(const char**)b);
        -:  913:}
        -:  914:
        -:  915:// Emulates Perl's /\D/ check
        -:  916:static bool contains_nondigit(SV *restrict sv) {
        -:  917:	if (!sv || !SvOK(sv)) return 0;
        -:  918:	STRLEN len;
        -:  919:	const char *restrict s = SvPVbyte(sv, len);
        -:  920:	for (size_t i = 0; i < len; i++) {
        -:  921:	  if (!isdigit(s[i])) return 1;
        -:  922:	}
        -:  923:	return 0;
        -:  924:}
        -:  925:
        -:  926:// Writes a properly quoted string dynamically
        -:  927:static void print_str_quoted(PerlIO *fh, const char *str, const char *sep) {
        -:  928:	if (!str) str = "";
        -:  929:	bool needs_quotes = 0;
        -:  930:	if (strstr(str, sep) != NULL || strchr(str, '"') != NULL || strchr(str, '\r') != NULL || strchr(str, '\n') != NULL) {
        -:  931:	  needs_quotes = 1;
        -:  932:	}
        -:  933:
        -:  934:	if (needs_quotes) {
        -:  935:	  PerlIO_putc(fh, '"');
        -:  936:	  for (const char *restrict p = str; *p; p++) {
        -:  937:		   if (*p == '"') {
        -:  938:		       PerlIO_putc(fh, '"');
        -:  939:		       PerlIO_putc(fh, '"');
        -:  940:		   } else {
        -:  941:		       PerlIO_putc(fh, *p);
        -:  942:		   }
        -:  943:	  }
        -:  944:	  PerlIO_putc(fh, '"');
        -:  945:	} else {
        -:  946:	  PerlIO_puts(fh, str);
        -:  947:	}
        -:  948:}
        -:  949:
        -:  950:// Writes an array of strings joined by sep
        -:  951:static void print_string_row(PerlIO *fh, const char **row, size_t len, const char *sep) {
        -:  952:	size_t sep_len = strlen(sep);
        -:  953:	for (size_t i = 0; i < len; i++) {
        -:  954:	  if (i > 0) PerlIO_write(fh, sep, sep_len);
        -:  955:	  if (row[i]) {
        -:  956:		   print_str_quoted(fh, row[i], sep);
        -:  957:	  } else {
        -:  958:		   print_str_quoted(fh, "", sep);
        -:  959:	  }
        -:  960:	}
        -:  961:	PerlIO_putc(fh, '\n');
        -:  962:}
        -:  963:// Calculates the Regularized Upper Incomplete Gamma Function Q(a, x)
        -:  964:// This perfectly replicates R's pchisq(..., lower.tail=FALSE)
        -:  965:double igamc(double a, double x) {
        -:  966:	if (x < 0.0 || a <= 0.0) return 1.0;
        -:  967:	if (x == 0.0) return 1.0;
        -:  968:
        -:  969:	// Series expansion for x < a + 1
        -:  970:	if (x < a + 1.0) {
        -:  971:		double sum = 1.0 / a;
        -:  972:		double term = 1.0 / a;
        -:  973:		double n = 1.0;
        -:  974:		while (fabs(term) > 1e-15) {
        -:  975:			term *= x / (a + n);
        -:  976:			sum += term;
        -:  977:			n += 1.0;
        -:  978:		}
        -:  979:		return 1.0 - (sum * exp(-x + a * log(x) - lgamma(a)));
        -:  980:	}
        -:  981:
        -:  982:	// Continued fraction for x >= a + 1
        -:  983:	double b = x + 1.0 - a;
        -:  984:	double c = 1.0 / 1e-30;
        -:  985:	double d = 1.0 / b;
        -:  986:	double h = d, i = 1.0;
        -:  987:	while (i < 10000) { // Safety bound
        -:  988:		double an = -i * (i - a);
        -:  989:		b += 2.0;
        -:  990:		d = an * d + b;
        -:  991:		if (fabs(d) < 1e-30) d = 1e-30;
        -:  992:		c = b + an / c;
        -:  993:		if (fabs(c) < 1e-30) c = 1e-30;
        -:  994:		d = 1.0 / d;
        -:  995:		double del = d * c;
        -:  996:		h *= del;
        -:  997:		if (fabs(del - 1.0) < 1e-15) break;
        -:  998:		i += 1.0;
        -:  999:	}
        -: 1000:	return h * exp(-x + a * log(x) - lgamma(a));
        -: 1001:}
        -: 1002:
        -: 1003:// Chi-Squared p-value is simply the Incomplete Gamma of (df/2, stat/2)
        -: 1004:double get_p_value(double stat, int df) {
        -: 1005:	if (df <= 0) return 1.0;
        -: 1006:	if (stat <= 0.0) return 1.0;
        -: 1007:	return igamc((double)df / 2.0, stat / 2.0);
        -: 1008:}
        -: 1009:
        -: 1010:/* --- C HELPER SECTION --- */
        -: 1011:#ifndef M_SQRT1_2
        -: 1012:#define M_SQRT1_2 0.70710678118654752440
        -: 1013:#endif
        -: 1014:
        -: 1015:/* Robust Binomial Coefficient using long double */
        -: 1016:static long double choose_comb(int n, int k) {
        -: 1017:	if (k < 0 || k > n) return 0.0L;
        -: 1018:	if (k > n / 2) k = n - k;
        -: 1019:	long double res = 1.0L;
        -: 1020:	for (int i = 1; i <= k; i++) {
        -: 1021:	  res = res * (long double)(n - i + 1) / (long double)i;
        -: 1022:	}
        -: 1023:	return res;
        -: 1024:}
        -: 1025:
        -: 1026:/* Exact CDF for Mann-Whitney U: P(U <= q) 
        -: 1027:   Mathematically identical to R's cwilcox generating function */
        -: 1028:static double exact_pwilcox(double q, int m, int n) {
        -: 1029:    int k = (int)floor(q + 1e-7); // R uses 1e-7 fuzz
        -: 1030:    int max_u = m * n;
        -: 1031:    if (k < 0) return 0.0;
        -: 1032:    if (k >= max_u) return 1.0;
        -: 1033:
        -: 1034:    long double *restrict w = (long double *)safecalloc(max_u + 1, sizeof(long double));
        -: 1035:    w[0] = 1.0L;
        -: 1036:
        -: 1037:    for (int j = 1; j <= n; j++) {
        -: 1038:        for (int i = j; i <= max_u; i++) w[i] += w[i - j];
        -: 1039:        for (int i = max_u; i >= j + m; i--) w[i] -= w[i - j - m];
        -: 1040:    }
        -: 1041:
        -: 1042:    long double cum_p = 0.0L;
        -: 1043:    for (int i = 0; i <= k; i++) cum_p += w[i];
        -: 1044:    
        -: 1045:    long double total = choose_comb(m + n, n);
        -: 1046:    double result = (double)(cum_p / total);
        -: 1047:    
        -: 1048:    Safefree(w);
        -: 1049:    return result;
        -: 1050:}
        -: 1051:
        -: 1052:/* Exact CDF for Wilcoxon Signed Rank: P(V <= q) 
        -: 1053:   Mathematically identical to R's csignrank subset-sum DP */
        -: 1054:static double exact_psignrank(double q, int n) {
        -: 1055:	int k = (int)floor(q + 1e-7);
        -: 1056:	int max_v = n * (n + 1) / 2;
        -: 1057:	if (k < 0) return 0.0;
        -: 1058:	if (k >= max_v) return 1.0;
        -: 1059:
        -: 1060:	long double *restrict w = (long double *)safecalloc(max_v + 1, sizeof(long double));
        -: 1061:	w[0] = 1.0L;
        -: 1062:
        -: 1063:	for (int i = 1; i <= n; i++) {
        -: 1064:	  for (int j = max_v; j >= i; j--) w[j] += w[j - i];
        -: 1065:	}
        -: 1066:
        -: 1067:	long double cum_p = 0.0L;
        -: 1068:	for (int i = 0; i <= k; i++) cum_p += w[i];
        -: 1069:
        -: 1070:	long double total = powl(2.0L, (long double)n);
        -: 1071:	double result = (double)(cum_p / total);
        -: 1072:
        -: 1073:	Safefree(w);
        -: 1074:	return result;
        -: 1075:}
        -: 1076:
        -: 1077:static int cmp_rank_info(const void *a, const void *b) {
        -: 1078:	double da = ((const RankInfo*)a)->val;
        -: 1079:	double db = ((const RankInfo*)b)->val;
        -: 1080:	return (da > db) - (da < db);
        -: 1081:}
        -: 1082:
        -: 1083:static double rank_and_count_ties(RankInfo *restrict ri, size_t n, bool *restrict has_ties) {
        -: 1084:	if (n == 0) return 0.0;
        -: 1085:	qsort(ri, n, sizeof(RankInfo), cmp_rank_info);
        -: 1086:	size_t i = 0;
        -: 1087:	double tie_adj = 0.0;
        -: 1088:	*has_ties = 0;
        -: 1089:	while (i < n) {
        -: 1090:		size_t j = i + 1;
        -: 1091:		while (j < n && ri[j].val == ri[i].val) j++;
        -: 1092:		double r = (double)(i + 1 + j) / 2.0; 
        -: 1093:		for (size_t k = i; k < j; k++) ri[k].rank = r;
        -: 1094:		size_t t = j - i;
        -: 1095:		if (t > 1) { *has_ties = 1; tie_adj += ((double)t * t * t - t); }
        -: 1096:		i = j;
        -: 1097:	}
        -: 1098:	return tie_adj;
        -: 1099:}
        -: 1100:/* --- KS-TEST C HELPER SECTION --- */
        -: 1101:#ifndef M_PI_2
        -: 1102:#define M_PI_2 1.57079632679489661923
        -: 1103:#endif
        -: 1104:#ifndef M_PI_4
        -: 1105:#define M_PI_4 0.78539816339744830962
        -: 1106:#endif
        -: 1107:#ifndef M_1_SQRT_2PI
        -: 1108:#define M_1_SQRT_2PI 0.39894228040143267794
        -: 1109:#endif
        -: 1110:
        -: 1111:// Scalar integer power used by K2x
        -: 1112:static double r_pow_di(double x, int n) {
        -: 1113:	if (n == 0) return 1.0;
        -: 1114:	if (n < 0) return 1.0 / r_pow_di(x, -n);
        -: 1115:	double val = 1.0;
        -: 1116:	for (int i = 0; i < n; i++) val *= x;
        -: 1117:	return val;
        -: 1118:}
        -: 1119:
        -: 1120:// Two-sample two-sided asymptotic distribution
        -: 1121:static double K2l(double x, int lower, double tol) {
        -: 1122:	double s, z, p;
        -: 1123:	int k;
        -: 1124:	if(x <= 0.) {
        -: 1125:	  if(lower) p = 0.;
        -: 1126:	  else p = 1.;
        -: 1127:	} else if(x < 1.) {
        -: 1128:	  int k_max = (int) sqrt(2.0 - log(tol));
        -: 1129:	  double w = log(x);
        -: 1130:	  z = - (M_PI_2 * M_PI_4) / (x * x);
        -: 1131:	  s = 0;
        -: 1132:	  for(k = 1; k < k_max; k += 2) {
        -: 1133:		   s += exp(k * k * z - w);
        -: 1134:	  }
        -: 1135:	  p = s / M_1_SQRT_2PI;
        -: 1136:	  if(!lower) p = 1.0 - p;
        -: 1137:	} else {
        -: 1138:	  double new_val, old_val;
        -: 1139:	  z = -2.0 * x * x;
        -: 1140:	  s = -1.0;
        -: 1141:	  if(lower) {
        -: 1142:		   k = 1; old_val = 0.0; new_val = 1.0;
        -: 1143:	  } else {
        -: 1144:		   k = 2; old_val = 0.0; new_val = 2.0 * exp(z);
        -: 1145:	  }
        -: 1146:	  while(fabs(old_val - new_val) > tol) {
        -: 1147:		   old_val = new_val;
        -: 1148:		   new_val += 2.0 * s * exp(z * k * k);
        -: 1149:		   s *= -1.0;
        -: 1150:		   k++;
        -: 1151:	  }
        -: 1152:	  p = new_val;
        -: 1153:	}
        -: 1154:	return p;
        -: 1155:}
        -: 1156:
        -: 1157:// Auxiliary routines used by K2x() for matrix operations
        -: 1158:static void m_multiply(double *A, double *B, double *C, unsigned int m) {
        -: 1159:	for(unsigned int i = 0; i < m; i++) {
        -: 1160:	  for(unsigned int j = 0; j < m; j++) {
        -: 1161:		   double s = 0.;
        -: 1162:		   for(unsigned int k = 0; k < m; k++) s += A[i * m + k] * B[k * m + j];
        -: 1163:		   C[i * m + j] = s;
        -: 1164:	  }
        -: 1165:	}
        -: 1166:}
        -: 1167:
        -: 1168:static void m_power(double *A, int eA, double *V, int *eV, int m, int n) {
        -: 1169:    if(n == 1) {
        -: 1170:        for(int i = 0; i < m * m; i++) V[i] = A[i];
        -: 1171:        *eV = eA;
        -: 1172:        return;
        -: 1173:    }
        -: 1174:    m_power(A, eA, V, eV, m, n / 2);
        -: 1175:    double *restrict B = (double*) safecalloc(m * m, sizeof(double));
        -: 1176:    m_multiply(V, V, B, m);
        -: 1177:    int eB = 2 * (*eV);
        -: 1178:    if((n % 2) == 0) {
        -: 1179:        for(int i = 0; i < m * m; i++) V[i] = B[i];
        -: 1180:        *eV = eB;
        -: 1181:    } else {
        -: 1182:        m_multiply(A, B, V, m);
        -: 1183:        *eV = eA + eB;
        -: 1184:    }
        -: 1185:    if(V[(m / 2) * m + (m / 2)] > 1e140) {
        -: 1186:        for(int i = 0; i < m * m; i++) V[i] = V[i] * 1e-140;
        -: 1187:        *eV += 140;
        -: 1188:    }
        -: 1189:    Safefree(B);
        -: 1190:}
        -: 1191:
        -: 1192:// One-sample two-sided exact distribution
        -: 1193:static double K2x(int n, double d) {
        -: 1194:	int k = (int) (n * d) + 1;
        -: 1195:	int m = 2 * k - 1;
        -: 1196:	double h = k - n * d;
        -: 1197:	double *restrict H = (double*) safecalloc(m * m, sizeof(double));
        -: 1198:	double *restrict Q = (double*) safecalloc(m * m, sizeof(double));
        -: 1199:
        -: 1200:	for(int i = 0; i < m; i++) {
        -: 1201:	  for(int j = 0; j < m; j++) {
        -: 1202:		   if(i - j + 1 < 0) H[i * m + j] = 0;
        -: 1203:		   else H[i * m + j] = 1;
        -: 1204:	  }
        -: 1205:	}
        -: 1206:	for(int i = 0; i < m; i++) {
        -: 1207:	  H[i * m] -= r_pow_di(h, i + 1);
        -: 1208:	  H[(m - 1) * m + i] -= r_pow_di(h, (m - i));
        -: 1209:	}
        -: 1210:	H[(m - 1) * m] += ((2 * h - 1 > 0) ? r_pow_di(2 * h - 1, m) : 0);
        -: 1211:
        -: 1212:	for(int i = 0; i < m; i++) {
        -: 1213:	  for(int j = 0; j < m; j++) {
        -: 1214:		   if(i - j + 1 > 0) {
        -: 1215:		       for(int g = 1; g <= i - j + 1; g++) H[i * m + j] /= g;
        -: 1216:		   }
        -: 1217:	  }
        -: 1218:	}
        -: 1219:
        -: 1220:	int eH = 0, eQ;
        -: 1221:	m_power(H, eH, Q, &eQ, m, n);
        -: 1222:	double s = Q[(k - 1) * m + k - 1];
        -: 1223:
        -: 1224:	for(int i = 1; i <= n; i++) {
        -: 1225:	  s = s * (double)i / (double)n;
        -: 1226:	  if(s < 1e-140) {
        -: 1227:		   s *= 1e140;
        -: 1228:		   eQ -= 140;
        -: 1229:	  }
        -: 1230:	}
        -: 1231:	s *= pow(10.0, eQ);
        -: 1232:	Safefree(H);
        -: 1233:	Safefree(Q);
        -: 1234:	return s;
        -: 1235:}
        -: 1236:
        -: 1237:// Calculate D (two-sided), D+ (greater), and D- (less) simultaneously
        -: 1238:static void calc_2sample_stats(double *x, size_t nx, double *y, size_t ny,
        -: 1239:                               double *d, double *d_plus, double *d_minus) {
        -: 1240:	qsort(x, nx, sizeof(double), compare_doubles);
        -: 1241:	qsort(y, ny, sizeof(double), compare_doubles);
        -: 1242:	double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
        -: 1243:	size_t i = 0, j = 0;
        -: 1244:
        -: 1245:	while(i < nx || j < ny) {
        -: 1246:	  double val;
        -: 1247:	  if (i < nx && j < ny) val = (x[i] < y[j]) ? x[i] : y[j];
        -: 1248:	  else if (i < nx) val = x[i];
        -: 1249:	  else val = y[j];
        -: 1250:	  
        -: 1251:	  while(i < nx && x[i] <= val) i++;
        -: 1252:	  while(j < ny && y[j] <= val) j++;
        -: 1253:	  
        -: 1254:	  double cdf1 = (double)i / nx;
        -: 1255:	  double cdf2 = (double)j / ny;
        -: 1256:	  double diff = cdf1 - cdf2;
        -: 1257:	  
        -: 1258:	  if (diff > max_d_plus) max_d_plus = diff;
        -: 1259:	  if (-diff > max_d_minus) max_d_minus = -diff;
        -: 1260:	  if (fabs(diff) > max_d) max_d = fabs(diff);
        -: 1261:	}
        -: 1262:	*d = max_d;
        -: 1263:	*d_plus = max_d_plus;
        -: 1264:	*d_minus = max_d_minus;
        -: 1265:}
        -: 1266:
        -: 1267:// Branch the DP boundary check based on the 'alternative'
        -: 1268:static int psmirnov_exact_test(double q, double r, double s, int two_sided) {
        -: 1269:	if (two_sided) return (fabs(r - s) >= q);
        -: 1270:	return ((r - s) >= q); // Used for both D+ and D- via symmetry
        -: 1271:}
        -: 1272:
        -: 1273:// Evaluate the exact 2-sample probability
        -: 1274:static double psmirnov_exact_uniq_upper(double q, int m, int n, int two_sided) {
        -: 1275:	double md = (double) m, nd = (double) n;
        -: 1276:	double *restrict u = (double *) safecalloc(n + 1, sizeof(double));
        -: 1277:	u[0] = 0.;
        -: 1278:
        -: 1279:	for(unsigned int j = 1; j <= n; j++) {
        -: 1280:	  if(psmirnov_exact_test(q, 0., j / nd, two_sided)) u[j] = 1.;
        -: 1281:	  else u[j] = u[j - 1];
        -: 1282:	}
        -: 1283:	for(unsigned int i = 1; i <= m; i++) {
        -: 1284:	  if(psmirnov_exact_test(q, i / md, 0., two_sided)) u[0] = 1.;
        -: 1285:	  for(int j = 1; j <= n; j++) {
        -: 1286:		   if(psmirnov_exact_test(q, i / md, j / nd, two_sided)) u[j] = 1.;
        -: 1287:		   else {
        -: 1288:		       double v = (double)(i) / (double)(i + j);
        -: 1289:		       double w = (double)(j) / (double)(i + j);
        -: 1290:		       u[j] = v * u[j] + w * u[j - 1];
        -: 1291:		   }
        -: 1292:	  }
        -: 1293:	}
        -: 1294:	double res = u[n];
        -: 1295:	Safefree(u);
        -: 1296:	return res;
        -: 1297:}
        -: 1298:
        -: 1299:static double p_body(double n, double delta, double sd, double sig_level, int tsample, int tside, bool strict) {
        -: 1300:	double nu = (n - 1.0) * (double)tsample;
        -: 1301:	if (nu < 1e-7) nu = 1e-7; 
        -: 1302:
        -: 1303:	// Ensure sig_level/tside is not truncated
        -: 1304:	double p_tail = sig_level / (double)tside;
        -: 1305:	double qu = qt_tail(nu, p_tail); // qt(p, df, lower.tail=FALSE)
        -: 1306:
        -: 1307:	double ncp = sqrt(n / (double)tsample) * (delta / sd);
        -: 1308:
        -: 1309:	if (strict && tside == 2) {
        -: 1310:	  // Use R-style tail calls: 1 - P(T < qu) + P(T < -qu)
        -: 1311:	  return (1.0 - exact_pnt(qu, nu, ncp)) + exact_pnt(-qu, nu, ncp);
        -: 1312:	} else {
        -: 1313:	  // Default: 1 - P(T < qu)
        -: 1314:	  // Ensure exact_pnt is using a convergence tolerance of at least 1e-15
        -: 1315:	  return 1.0 - exact_pnt(qu, nu, ncp);
        -: 1316:	}
        -: 1317:}
        -: 1318:
        -: 1319:// Bisection algorithm to find the inverse F-distribution (Quantile function)
        -: 1320:// Equivalent to R's qf(p, df1, df2)
        -: 1321:static double qf_bisection(double p, double df1, double df2) {
        -: 1322:	if (p <= 0.0) return 0.0;
        -: 1323:	if (p >= 1.0) return INFINITY;
        -: 1324:	double low = 0.0, high = 1.0;
        -: 1325:	// Find upper bound
        -: 1326:	while (pf(high, df1, df2) < p) {
        -: 1327:	  low = high;
        -: 1328:	  high *= 2.0;
        -: 1329:	  if (high > 1e100) break; /* Fallback limit */
        -: 1330:	}
        -: 1331:
        -: 1332:	// Bisect to find the root
        -: 1333:	for (unsigned short int i = 0; i < 150; i++) {
        -: 1334:		double mid = low + (high - low) / 2.0;
        -: 1335:		double p_mid = pf(mid, df1, df2);
        -: 1336:
        -: 1337:		if (p_mid < p) {
        -: 1338:			low = mid;
        -: 1339:		} else {
        -: 1340:			high = mid;
        -: 1341:		}
        -: 1342:		if (high - low < 1e-12) break;
        -: 1343:	}
        -: 1344:	return (low + high) / 2.0;
        -: 1345:}
        -: 1346:// --- XS SECTION ---
        -: 1347:#line 1348 "LikeR.c"
        -: 1348:#ifndef PERL_UNUSED_VAR
        -: 1349:#  define PERL_UNUSED_VAR(var) if (0) var = var
        -: 1350:#endif
        -: 1351:
        -: 1352:#ifndef dVAR
        -: 1353:#  define dVAR		dNOOP
        -: 1354:#endif
        -: 1355:
        -: 1356:
        -: 1357:/* This stuff is not part of the API! You have been warned. */
        -: 1358:#ifndef PERL_VERSION_DECIMAL
        -: 1359:#  define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
        -: 1360:#endif
        -: 1361:#ifndef PERL_DECIMAL_VERSION
        -: 1362:#  define PERL_DECIMAL_VERSION \
        -: 1363:	  PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
        -: 1364:#endif
        -: 1365:#ifndef PERL_VERSION_GE
        -: 1366:#  define PERL_VERSION_GE(r,v,s) \
        -: 1367:	  (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
        -: 1368:#endif
        -: 1369:#ifndef PERL_VERSION_LE
        -: 1370:#  define PERL_VERSION_LE(r,v,s) \
        -: 1371:	  (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
        -: 1372:#endif
        -: 1373:
        -: 1374:/* XS_INTERNAL is the explicit static-linkage variant of the default
        -: 1375: * XS macro.
        -: 1376: *
        -: 1377: * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
        -: 1378: * "STATIC", ie. it exports XSUB symbols. You probably don't want that
        -: 1379: * for anything but the BOOT XSUB.
        -: 1380: *
        -: 1381: * See XSUB.h in core!
        -: 1382: */
        -: 1383:
        -: 1384:
        -: 1385:/* TODO: This might be compatible further back than 5.10.0. */
        -: 1386:#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
        -: 1387:#  undef XS_EXTERNAL
        -: 1388:#  undef XS_INTERNAL
        -: 1389:#  if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
        -: 1390:#    define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
        -: 1391:#    define XS_INTERNAL(name) STATIC XSPROTO(name)
        -: 1392:#  endif
        -: 1393:#  if defined(__SYMBIAN32__)
        -: 1394:#    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
        -: 1395:#    define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
        -: 1396:#  endif
        -: 1397:#  ifndef XS_EXTERNAL
        -: 1398:#    if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
        -: 1399:#      define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
        -: 1400:#      define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
        -: 1401:#    else
        -: 1402:#      ifdef __cplusplus
        -: 1403:#        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
        -: 1404:#        define XS_INTERNAL(name) static XSPROTO(name)
        -: 1405:#      else
        -: 1406:#        define XS_EXTERNAL(name) XSPROTO(name)
        -: 1407:#        define XS_INTERNAL(name) STATIC XSPROTO(name)
        -: 1408:#      endif
        -: 1409:#    endif
        -: 1410:#  endif
        -: 1411:#endif
        -: 1412:
        -: 1413:/* perl >= 5.10.0 && perl <= 5.15.1 */
        -: 1414:
        -: 1415:
        -: 1416:/* The XS_EXTERNAL macro is used for functions that must not be static
        -: 1417: * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
        -: 1418: * macro defined, the best we can do is assume XS is the same.
        -: 1419: * Dito for XS_INTERNAL.
        -: 1420: */
        -: 1421:#ifndef XS_EXTERNAL
        -: 1422:#  define XS_EXTERNAL(name) XS(name)
        -: 1423:#endif
        -: 1424:#ifndef XS_INTERNAL
        -: 1425:#  define XS_INTERNAL(name) XS(name)
        -: 1426:#endif
        -: 1427:
        -: 1428:/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
        -: 1429: * internal macro that we're free to redefine for varying linkage due
        -: 1430: * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
        -: 1431: * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
        -: 1432: */
        -: 1433:
        -: 1434:#undef XS_EUPXS
        -: 1435:#if defined(PERL_EUPXS_ALWAYS_EXPORT)
        -: 1436:#  define XS_EUPXS(name) XS_EXTERNAL(name)
        -: 1437:#else
        -: 1438:   /* default to internal */
        -: 1439:#  define XS_EUPXS(name) XS_INTERNAL(name)
        -: 1440:#endif
        -: 1441:
        -: 1442:#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
        -: 1443:#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
        -: 1444:
        -: 1445:/* prototype to pass -Wmissing-prototypes */
        -: 1446:STATIC void
        -: 1447:S_croak_xs_usage(const CV *const cv, const char *const params);
        -: 1448:
        -: 1449:STATIC void
        -: 1450:S_croak_xs_usage(const CV *const cv, const char *const params)
        -: 1451:{
        -: 1452:    const GV *const gv = CvGV(cv);
        -: 1453:
        -: 1454:    PERL_ARGS_ASSERT_CROAK_XS_USAGE;
        -: 1455:
function XS_Stats__LikeR_ks_test called 4 returned 100% blocks executed 77%
        4: 1456:    if (gv) {
        -: 1457:        const char *const gvname = GvNAME(gv);
        4: 1458:        const HV *const stash = GvSTASH(gv);
        4: 1458-block  0
call    0 returned 4
branch  1 taken 4 (fallthrough)
branch  2 taken 0
        -: 1459:        const char *const hvname = stash ? HvNAME(stash) : NULL;
        -: 1460:
        -: 1461:        if (hvname)
        -: 1462:	    Perl_croak_nocontext("Usage: %s::%s(%s)", hvname, gvname, params);
        -: 1463:        else
        -: 1464:	    Perl_croak_nocontext("Usage: %s(%s)", gvname, params);
        -: 1465:    } else {
        -: 1466:        /* Pants. I don't think that it should be possible to get here. */
        -: 1467:	Perl_croak_nocontext("Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
        -: 1468:    }
        -: 1469:}
        -: 1470:#undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE
        -: 1471:
        -: 1472:#define croak_xs_usage        S_croak_xs_usage
        -: 1473:
        -: 1474:#endif
        -: 1475:
        -: 1476:/* NOTE: the prototype of newXSproto() is different in versions of perls,
        -: 1477: * so we define a portable version of newXSproto()
        -: 1478: */
        -: 1479:#ifdef newXS_flags
        -: 1480:#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
        -: 1481:#else
        -: 1482:#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
        -: 1483:#endif /* !defined(newXS_flags) */
        -: 1484:
        -: 1485:#if PERL_VERSION_LE(5, 21, 5)
        -: 1486:#  define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
        -: 1487:#else
        -: 1488:#  define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
        -: 1489:#endif
        -: 1490:
        -: 1491:/* simple backcompat versions of the TARGx() macros with no optimisation */
        -: 1492:#ifndef TARGi
        -: 1493:#  define TARGi(iv, do_taint) sv_setiv_mg(TARG, iv)
        -: 1494:#  define TARGu(uv, do_taint) sv_setuv_mg(TARG, uv)
        -: 1495:#  define TARGn(nv, do_taint) sv_setnv_mg(TARG, nv)
        -: 1496:#endif
        -: 1497:
        -: 1498:#line 1499 "LikeR.c"
        -: 1499:
        -: 1500:XS_EUPXS(XS_Stats__LikeR_ks_test); /* prototype to pass -Wmissing-prototypes */
        -: 1501:XS_EUPXS(XS_Stats__LikeR_ks_test)
        -: 1502:{
        -: 1503:    dVAR; dXSARGS;
        -: 1504:    PERL_UNUSED_VAR(cv); /* -W */
        -: 1505:    PERL_UNUSED_VAR(items); /* -W */
        -: 1506:    {
        -: 1507:	SV *	RETVAL;
        -: 1508:#line 1342 "LikeR.xs"
        -: 1509:{
        -: 1510:	SV *restrict x_sv = NULL, *restrict y_sv = NULL;
        -: 1511:	short int exact = -1;
        -: 1512:	const char *restrict alternative = "two.sided";
        -: 1513:	int arg_idx = 0;
        -: 1514:
        -: 1515:	// Shift arrays if provided positionally
        -: 1516:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 1517:		x_sv = ST(arg_idx);
        -: 1518:		arg_idx++;
        -: 1519:	}
        -: 1520:	// Check if second argument is an array (2-sample) or a string representing a CDF (1-sample)
        -: 1521:	if (arg_idx < items) {
        -: 1522:		if (SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 1523:			y_sv = ST(arg_idx);
        -: 1524:			arg_idx++;
        -: 1525:		} else if (SvPOK(ST(arg_idx))) {
        -: 1526:			y_sv = ST(arg_idx); // Save string (e.g., "pnorm") for 1-sample test logic
        -: 1527:			arg_idx++;
        -: 1528:		}
        -: 1529:	}
        -: 1530:
        -: 1531:	// Parse named arguments
        -: 1532:	for (; arg_idx < items; arg_idx += 2) {
        -: 1533:	  const char *restrict key = SvPV_nolen(ST(arg_idx));
        -: 1534:	  SV *restrict val = ST(arg_idx + 1);
        -: 1535:	  if      (strEQ(key, "x"))           x_sv = val;
        -: 1536:	  else if (strEQ(key, "y"))           y_sv = val;
        -: 1537:	  else if (strEQ(key, "exact"))       {
        -: 1538:		   if (!SvOK(val)) exact = -1;
        -: 1539:		   else exact = SvTRUE(val) ? 1 : 0;
        -: 1540:	  }
        -: 1541:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 1542:	  else croak("ks_test: unknown argument '%s'", key);
        -: 1543:	}
        -: 1544:
        -: 1545:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV) {
        -: 1546:	  croak("ks_test: 'x' is a required argument and must be an ARRAY reference");
        -: 1547:	}
        -: 1548:
        -: 1549:	bool is_two_sided = strEQ(alternative, "two.sided") ? 1 : 0;
        -: 1550:	bool is_greater   = strEQ(alternative, "greater") ? 1 : 0;
        -: 1551:	bool is_less      = strEQ(alternative, "less") ? 1 : 0;
        -: 1552:
        -: 1553:	if (!is_two_sided && !is_greater && !is_less) {
        -: 1554:	  croak("ks_test: alternative must be 'two.sided', 'less', or 'greater'");
        -: 1555:	}
        -: 1556:
        -: 1557:	AV *restrict x_av = (AV*)SvRV(x_sv);
        -: 1558:	size_t nx = av_len(x_av) + 1;
        -: 1559:	if (nx == 0) croak("Not enough 'x' observations");
        -: 1560:
        -: 1561:	// Extract 'x' array to C-array
        -: 1562:	double *restrict x_data = (double *)safemalloc(nx * sizeof(double));
        -: 1563:	size_t valid_nx = 0;
        -: 1564:	for (size_t i = 0; i < nx; i++) {
        -: 1565:	  SV**restrict el = av_fetch(x_av, i, 0);
        -: 1566:	  if (el && SvOK(*el) && looks_like_number(*el)) {
        -: 1567:		   x_data[valid_nx++] = SvNV(*el);
        -: 1568:	  }
        -: 1569:	}
        -: 1570:
        -: 1571:	double statistic = 0.0, p_value = 0.0;
        -: 1572:	const char *restrict method_desc = "";
        -: 1573:
        -: 1574:	// --- TWO SAMPLE ---
        -: 1575:	if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
        -: 1576:	  AV *restrict y_av = (AV*)SvRV(y_sv);
        -: 1577:	  size_t ny = av_len(y_av) + 1;
        -: 1578:
        -: 1579:	  double *restrict y_data = (double *)safemalloc(ny * sizeof(double));
        -: 1580:	  size_t valid_ny = 0;
        -: 1581:	  for (size_t i = 0; i < ny; i++) {
        -: 1582:		   SV**restrict el = av_fetch(y_av, i, 0);
        -: 1583:		   if (el && SvOK(*el) && looks_like_number(*el)) {
        -: 1584:		       y_data[valid_ny++] = SvNV(*el);
        -: 1585:		   }
        -: 1586:	  }
        -: 1587:
        -: 1588:	  if (valid_nx < 1 || valid_ny < 1) {
        -: 1589:		   Safefree(x_data); Safefree(y_data);
        -: 1590:		   croak("Not enough non-missing observations for KS test");
        -: 1591:	  }
        -: 1592:
        -: 1593:	  double d, d_plus, d_minus;
        -: 1594:	  calc_2sample_stats(x_data, valid_nx, y_data, valid_ny, &d, &d_plus, &d_minus);
        -: 1595:
        -: 1596:	  // Map alternative to the correct statistic
        -: 1597:	  if (is_greater) statistic = d_plus;
        -: 1598:	  else if (is_less) statistic = d_minus;
        -: 1599:	  else statistic = d;
        -: 1600:
        -: 1601:	  // Determine if exact or asymptotic
        -: 1602:	  bool use_exact = FALSE;
        -: 1603:	  if (exact == 1) use_exact = TRUE;
        -: 1604:	  else if (exact == 0) use_exact = FALSE;
        -: 1605:	  else use_exact = (valid_nx * valid_ny < 10000); 
        -: 1606:
        -: 1607:	  // Check for ties in combined set
        -: 1608:	  size_t total_n = valid_nx + valid_ny;
        -: 1609:	  double *restrict comb = (double *)safemalloc(total_n * sizeof(double));
        -: 1610:	  for(size_t i=0; i<valid_nx; i++) comb[i] = x_data[i];
        -: 1611:	  for(size_t i=0; i<valid_ny; i++) comb[valid_nx+i] = y_data[i];
        -: 1612:	  qsort(comb, total_n, sizeof(double), compare_doubles);
        -: 1613:
        -: 1614:	  bool has_ties = FALSE;
        -: 1615:	  for(size_t i = 1; i < total_n; i++) {
        -: 1616:		   if(comb[i] == comb[i-1]) { has_ties = TRUE; break; }
        -: 1617:	  }
        -: 1618:	  Safefree(comb);
        -: 1619:	  if (use_exact && has_ties) {
        -: 1620:		   warn("cannot compute exact p-value with ties; falling back to asymptotic");
        -: 1621:		   use_exact = FALSE;
        -: 1622:	  }
        -: 1623:	  if (use_exact) {
        -: 1624:		   method_desc = "Two-sample Kolmogorov-Smirnov exact test";
        -: 1625:		   double q = (0.5 + floor(statistic * valid_nx * valid_ny - 1e-7)) / ((double)valid_nx * valid_ny);
        -: 1626:		   p_value = psmirnov_exact_uniq_upper(q, valid_nx, valid_ny, is_two_sided);
        -: 1627:	  } else {
        -: 1628:		   method_desc = "Two-sample Kolmogorov-Smirnov test (asymptotic)";
        -: 1629:		   double z = statistic * sqrt((double)(valid_nx * valid_ny) / (valid_nx + valid_ny));
        -: 1630:		   if (is_two_sided) {
        -: 1631:		       p_value = K2l(z, 0, 1e-9); 
        -: 1632:		   } else {
        -: 1633:		       p_value = exp(-2.0 * z * z); // One-sided limit distribution
        -: 1634:		   }
        -: 1635:	  }
        -: 1636:	  Safefree(y_data);
        -: 1637:	} else if (y_sv && SvPOK(y_sv)) {// --- ONE SAMPLE (e.g. against pnorm) ---
        -: 1638:	  const char *restrict dist = SvPV_nolen(y_sv);
        -: 1639:	  if (strEQ(dist, "pnorm")) {
        -: 1640:		   qsort(x_data, valid_nx, sizeof(double), compare_doubles);
        -: 1641:		   double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
        -: 1642:		   for(size_t i = 0; i < valid_nx; i++) {
        -: 1643:		       double cdf_obs_low  = (double)i / valid_nx;
        -: 1644:		       double cdf_obs_high = (double)(i + 1) / valid_nx;
        -: 1645:		       double cdf_theor    = approx_pnorm(x_data[i]);
        -: 1646:
        -: 1647:		       double diff1 = cdf_obs_low - cdf_theor;
        -: 1648:		       double diff2 = cdf_obs_high - cdf_theor;
        -: 1649:
        -: 1650:		       if (diff1 > max_d_plus) max_d_plus = diff1;
        -: 1651:		       if (diff2 > max_d_plus) max_d_plus = diff2;
        -: 1652:		       if (-diff1 > max_d_minus) max_d_minus = -diff1;
        4: 1653:		       if (-diff2 > max_d_minus) max_d_minus = -diff2;
call    0 returned 4
        4: 1654:
        -: 1655:		       if (fabs(diff1) > max_d) max_d = fabs(diff1);
        4: 1656:		       if (fabs(diff2) > max_d) max_d = fabs(diff2);
        -: 1657:		   }
        -: 1658:		   if (is_greater) statistic = max_d_plus;
        -: 1659:		   else if (is_less) statistic = max_d_minus;
        -: 1660:		   else statistic = max_d;
function XS_Stats__LikeR_wilcox_test called 10 returned 80% blocks executed 72%
       10: 1661:		   bool use_exact = (exact == -1) ? (valid_nx < 100) : (exact == 1);
        -: 1662:		   if (use_exact) {
       10: 1663:		       method_desc = "One-sample Kolmogorov-Smirnov exact test";
       10: 1663-block  0
call    0 returned 10
branch  1 taken 10 (fallthrough)
branch  2 taken 0
        -: 1664:		       if (is_two_sided) {
        -: 1665:		           p_value = 1.0 - K2x(valid_nx, statistic);
        -: 1666:		       } else {
        -: 1667:		           warn("exact 1-sample 1-sided KS test not implemented; using asymptotic");
        -: 1668:		           double z = statistic * sqrt((double)valid_nx);
        -: 1669:		           p_value = exp(-2.0 * z * z);
        -: 1670:		       }
        -: 1671:		   } else {
        -: 1672:		       method_desc = "One-sample Kolmogorov-Smirnov test (asymptotic)";
        -: 1673:		       double z = statistic * sqrt((double)valid_nx);
        -: 1674:		       if (is_two_sided) p_value = K2l(z, 0, 1e-6); 
        -: 1675:		       else p_value = exp(-2.0 * z * z);
        -: 1676:		   }
        -: 1677:	  } else {
        -: 1678:		    Safefree(x_data);
        -: 1679:		    croak("ks_test: Unsupported 1-sample distribution '%s'. Use arrays for 2-sample.", dist);
        -: 1680:	  }
        -: 1681:	} else {
        -: 1682:	  Safefree(x_data);
        -: 1683:	  croak("ks_test: Invalid arguments for 'y'.");
        -: 1684:	}
        -: 1685:	Safefree(x_data);
        -: 1686:	if (p_value > 1.0) p_value = 1.0;
        -: 1687:	if (p_value < 0.0) p_value = 0.0;
        -: 1688:	HV *restrict res = newHV();
        -: 1689:	hv_stores(res, "statistic", newSVnv(statistic));
        -: 1690:	hv_stores(res, "p_value", newSVnv(p_value));
        -: 1691:	hv_stores(res, "method", newSVpv(method_desc, 0));
        -: 1692:	hv_stores(res, "alternative", newSVpv(alternative, 0));
        -: 1693:	RETVAL = newRV_noinc((SV*)res);
        -: 1694:}
        -: 1695:#line 1696 "LikeR.c"
        -: 1696:	RETVAL = sv_2mortal(RETVAL);
        -: 1697:	ST(0) = RETVAL;
        -: 1698:    }
        -: 1699:    XSRETURN(1);
        -: 1700:}
        -: 1701:
        -: 1702:
        -: 1703:XS_EUPXS(XS_Stats__LikeR_wilcox_test); /* prototype to pass -Wmissing-prototypes */
        -: 1704:XS_EUPXS(XS_Stats__LikeR_wilcox_test)
        -: 1705:{
        -: 1706:    dVAR; dXSARGS;
        -: 1707:    PERL_UNUSED_VAR(cv); /* -W */
        -: 1708:    PERL_UNUSED_VAR(items); /* -W */
        -: 1709:    {
        -: 1710:	SV *	RETVAL;
        -: 1711:#line 1533 "LikeR.xs"
        -: 1712:{
        -: 1713:	SV *restrict x_sv = NULL, *restrict y_sv = NULL;
        -: 1714:	bool paired = FALSE, correct = TRUE;
        -: 1715:	double mu = 0.0;
        -: 1716:	short int exact = -1;
        -: 1717:	const char *restrict alternative = "two.sided";
        -: 1718:	int arg_idx = 0;
        -: 1719:	// 1. Shift first positional argument as 'x' if it's an array reference
        -: 1720:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 1721:		x_sv = ST(arg_idx);
        -: 1722:		arg_idx++;
        -: 1723:	}
        -: 1724:	// 2. Shift second positional argument as 'y' if it's an array reference
        -: 1725:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 1726:		y_sv = ST(arg_idx);
        -: 1727:		arg_idx++;
        -: 1728:	}
        -: 1729:	// Ensure the remaining arguments form complete key-value pairs
        -: 1730:	if ((items - arg_idx) % 2 != 0) {
        -: 1731:		croak("Usage: wilcox_test(\\@x, [\\@y], key => value, ...)");
        -: 1732:	}
        -: 1733:	// --- Parse named arguments from the remaining flat stack ---
        -: 1734:	for (; arg_idx < items; arg_idx += 2) {
        -: 1735:		const char *restrict key = SvPV_nolen(ST(arg_idx));
        -: 1736:		SV *restrict val = ST(arg_idx + 1);
        -: 1737:		if      (strEQ(key, "x"))           x_sv = val;
        -: 1738:		else if (strEQ(key, "y"))           y_sv = val;
        -: 1739:		else if (strEQ(key, "paired"))      paired = SvTRUE(val);
        -: 1740:		else if (strEQ(key, "correct"))     correct = SvTRUE(val);
        -: 1741:		else if (strEQ(key, "mu"))          mu = SvNV(val);
        -: 1742:		else if (strEQ(key, "exact"))       {
        -: 1743:			if (!SvOK(val)) exact = -1;
        -: 1744:			else exact = SvTRUE(val) ? 1 : 0;
        -: 1745:		}
        -: 1746:		else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 1747:		else croak("wilcox_test: unknown argument '%s'", key);
        -: 1748:	}
        -: 1749:	// --- Validate required / types ---
        -: 1750:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 1751:		croak("wilcox_test: 'x' is a required argument and must be an ARRAY reference");
        -: 1752:	AV *restrict x_av = (AV*)SvRV(x_sv);
        -: 1753:	size_t nx = av_len(x_av) + 1;
        -: 1754:	if (nx == 0) croak("Not enough 'x' observations");
        -: 1755:
        -: 1756:	AV *restrict y_av = NULL;
        -: 1757:	size_t ny = 0;
        -: 1758:	if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
        -: 1759:		y_av = (AV*)SvRV(y_sv);
        -: 1760:		ny = av_len(y_av) + 1;
        -: 1761:	}
        -: 1762:	double p_value = 0.0, statistic = 0.0;
        -: 1763:	const char *restrict method_desc = "";
        -: 1764:	bool use_exact = FALSE;
        -: 1765:	// --- TWO SAMPLE (Mann-Whitney) ---
        -: 1766:	if (ny > 0 && !paired) {
        -: 1767:		RankInfo *restrict ri = (RankInfo *)safemalloc((nx + ny) * sizeof(RankInfo));
        -: 1768:		size_t valid_nx = 0, valid_ny = 0;
        -: 1769:		for (size_t i = 0; i < nx; i++) {
        -: 1770:			SV**restrict el = av_fetch(x_av, i, 0);
        -: 1771:			if (el && SvOK(*el) && looks_like_number(*el)) {
        -: 1772:				ri[valid_nx].val = SvNV(*el) - mu; // R subtracts mu from x
        -: 1773:				ri[valid_nx].idx = 1;
        -: 1774:				valid_nx++;
        -: 1775:			}
        -: 1776:		}
        -: 1777:		for (size_t i = 0; i < ny; i++) {
        -: 1778:			SV**restrict el = av_fetch(y_av, i, 0);
        -: 1779:			if (el && SvOK(*el) && looks_like_number(*el)) {
        -: 1780:				ri[valid_nx + valid_ny].val = SvNV(*el);
        -: 1781:				ri[valid_nx + valid_ny].idx = 2;
        -: 1782:				valid_ny++;
        -: 1783:			}
        -: 1784:		}
        -: 1785:		if (valid_nx == 0) { Safefree(ri); croak("not enough (non-missing) 'x' observations"); }
        -: 1786:		if (valid_ny == 0) { Safefree(ri); croak("not enough 'y' observations"); }
        -: 1787:		size_t total_n = valid_nx + valid_ny;
        -: 1788:		bool has_ties = 0;
        -: 1789:		double tie_adj = rank_and_count_ties(ri, total_n, &has_ties);
        -: 1790:		double w_rank_sum = 0.0;
        -: 1791:		for (size_t i = 0; i < total_n; i++) if (ri[i].idx == 1) w_rank_sum += ri[i].rank;
        -: 1792:		statistic = w_rank_sum - (double)valid_nx * (valid_nx + 1.0) / 2.0;
        -: 1793:
        -: 1794:		if (exact == 1) use_exact = TRUE;
        -: 1795:		else if (exact == 0) use_exact = FALSE;
        -: 1796:		else use_exact = (valid_nx < 50 && valid_ny < 50 && !has_ties);
        -: 1797:
        -: 1798:		if (use_exact && has_ties) {
        -: 1799:			warn("cannot compute exact p-value with ties; falling back to approximation");
        -: 1800:			use_exact = FALSE;
        -: 1801:		}
        -: 1802:		if (use_exact) {
        -: 1803:			method_desc = "Wilcoxon rank sum exact test";
        -: 1804:			double p_less = exact_pwilcox(statistic, valid_nx, valid_ny);
        -: 1805:			double p_greater = 1.0 - exact_pwilcox(statistic - 1.0, valid_nx, valid_ny);
        -: 1806:
        -: 1807:			if (strcmp(alternative, "less") == 0) p_value = p_less;
        -: 1808:			else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
        -: 1809:			else {
        -: 1810:				double p = (p_less < p_greater) ? p_less : p_greater;
        -: 1811:				p_value = 2.0 * p;
        -: 1812:			}
        -: 1813:		} else {
        -: 1814:			method_desc = correct ? "Wilcoxon rank sum test with continuity correction" : "Wilcoxon rank sum test";
        -: 1815:			double exp = (double)valid_nx * valid_ny / 2.0;
        -: 1816:			double var = ((double)valid_nx * valid_ny / 12.0) * ((total_n + 1.0) - tie_adj / (total_n * (total_n - 1.0)));
        -: 1817:			double z = statistic - exp;
        -: 1818:
        -: 1819:			double CORRECTION = 0.0;
        -: 1820:			if (correct) {
        -: 1821:				if (strcmp(alternative, "two.sided") == 0) CORRECTION = (z > 0 ? 0.5 : -0.5);
        -: 1822:				else if (strcmp(alternative, "greater") == 0) CORRECTION = 0.5;
        -: 1823:				else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
        -: 1824:			}
        -: 1825:			z = (z - CORRECTION) / sqrt(var);
        -: 1826:
        -: 1827:			if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
        -: 1828:			else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
        -: 1829:			else p_value = 2.0 * approx_pnorm(-fabs(z));
        -: 1830:		}
        -: 1831:		Safefree(ri);
        -: 1832:	} else { // --- ONE SAMPLE / PAIRED ---
        -: 1833:		if (paired && (!y_av || nx != ny)) croak("'x' and 'y' must have the same length for paired test");
        -: 1834:		double *restrict diffs = (double *)safemalloc(nx * sizeof(double));
        -: 1835:		size_t n_nz = 0;
        -: 1836:		bool has_zeroes = FALSE;
        -: 1837:		for (size_t i = 0; i < nx; i++) {
        -: 1838:			SV**restrict x_el = av_fetch(x_av, i, 0);
        -: 1839:			if (!x_el || !SvOK(*x_el) || !looks_like_number(*x_el)) continue;
        -: 1840:			double dx = SvNV(*x_el);
        -: 1841:
        -: 1842:			if (paired) {
        -: 1843:				SV**restrict y_el = av_fetch(y_av, i, 0);
        -: 1844:				if (!y_el || !SvOK(*y_el) || !looks_like_number(*y_el)) continue;
        -: 1845:				double dy = SvNV(*y_el);
        -: 1846:				double d = dx - dy - mu;
        -: 1847:				if (d == 0.0) has_zeroes = TRUE; // Drop exact zeroes
        -: 1848:				else diffs[n_nz++] = d;
        -: 1849:			} else {
        -: 1850:				double d = dx - mu;
        -: 1851:				if (d == 0.0) has_zeroes = TRUE;
        -: 1852:				else diffs[n_nz++] = d;
        -: 1853:			}
        -: 1854:		}
        -: 1855:		if (n_nz == 0) {
        -: 1856:			Safefree(diffs);
        -: 1857:			croak("not enough (non-missing) observations");
        -: 1858:		}
        -: 1859:		RankInfo *ri = (RankInfo *)safemalloc(n_nz * sizeof(RankInfo));
        -: 1860:		for (size_t i = 0; i < n_nz; i++) { 
        -: 1861:			ri[i].val = fabs(diffs[i]); 
        -: 1862:			ri[i].idx = (diffs[i] > 0);
        -: 1863:		}
        -: 1864:		bool has_ties = 0;
        -: 1865:		double tie_adj = rank_and_count_ties(ri, n_nz, &has_ties);
        -: 1866:		statistic = 0.0;
        -: 1867:		for (size_t i = 0; i < n_nz; i++) {
        -: 1868:			if (ri[i].idx) statistic += ri[i].rank;
        -: 1869:		}
        -: 1870:		if (exact == 1) use_exact = TRUE;
        -: 1871:		else if (exact == 0) use_exact = FALSE;
        -: 1872:		else use_exact = (n_nz < 50 && !has_ties);
        -: 1873:		if (use_exact && has_ties) {
        -: 1874:			warn("cannot compute exact p-value with ties; falling back to approximation");
        -: 1875:			use_exact = FALSE;
        -: 1876:		}
        8: 1877:		if (use_exact && has_zeroes) {
call    0 returned 8
        8: 1878:			warn("cannot compute exact p-value with zeroes; falling back to approximation");
        -: 1879:			use_exact = FALSE;
        8: 1880:		}
        -: 1881:		if (use_exact) {
        -: 1882:			method_desc = paired ? "Wilcoxon exact signed rank test" : "Wilcoxon exact signed rank test";
        -: 1883:			double p_less = exact_psignrank(statistic, n_nz);
        -: 1884:			double p_greater = 1.0 - exact_psignrank(statistic - 1.0, n_nz);
function XS_Stats__LikeR__chisq_c called 3 returned 100% blocks executed 94%
        3: 1885:
        -: 1886:			if (strcmp(alternative, "less") == 0) p_value = p_less;
        3: 1887:			else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
        3: 1887-block  0
call    0 returned 3
        3: 1888:			else {
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 1889:				double p = (p_less < p_greater) ? p_less : p_greater;
    %%%%%: 1889-block  0
call    0 never executed
        -: 1890:				p_value = 2.0 * p;
        3: 1891:			}
        3: 1891-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
        -: 1892:		} else {
        -: 1893:			method_desc = correct ? "Wilcoxon signed rank test with continuity correction" : "Wilcoxon signed rank test";
        -: 1894:			double exp = (double)n_nz * (n_nz + 1.0) / 4.0;
        -: 1895:			double var = (n_nz * (n_nz + 1.0) * (2.0 * n_nz + 1.0) / 24.0) - (tie_adj / 48.0);
        -: 1896:			double z = statistic - exp;
        -: 1897:			double CORRECTION = 0.0;
        -: 1898:			if (correct) {
        -: 1899:				if (strcmp(alternative, "two.sided") == 0) CORRECTION = (z > 0 ? 0.5 : -0.5);
        -: 1900:				else if (strcmp(alternative, "greater") == 0) CORRECTION = 0.5;
        -: 1901:				else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
        -: 1902:			}
        -: 1903:			z = (z - CORRECTION) / sqrt(var);
        -: 1904:
        -: 1905:			if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
        -: 1906:			else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
        -: 1907:			else p_value = 2.0 * approx_pnorm(-fabs(z));
        -: 1908:		}
        -: 1909:		Safefree(ri); Safefree(diffs);
        -: 1910:	}
        -: 1911:	if (p_value > 1.0) p_value = 1.0;
        -: 1912:	HV *restrict res = newHV();
        -: 1913:	hv_stores(res, "statistic", newSVnv(statistic));
        -: 1914:	hv_stores(res, "p_value", newSVnv(p_value));
        -: 1915:	hv_stores(res, "method", newSVpv(method_desc, 0));
        -: 1916:	hv_stores(res, "alternative", newSVpv(alternative, 0));
        -: 1917:	RETVAL = newRV_noinc((SV*)res);
        -: 1918:}
        -: 1919:#line 1920 "LikeR.c"
        -: 1920:	RETVAL = sv_2mortal(RETVAL);
        -: 1921:	ST(0) = RETVAL;
        -: 1922:    }
        -: 1923:    XSRETURN(1);
        -: 1924:}
        -: 1925:
        -: 1926:
        -: 1927:XS_EUPXS(XS_Stats__LikeR__chisq_c); /* prototype to pass -Wmissing-prototypes */
        -: 1928:XS_EUPXS(XS_Stats__LikeR__chisq_c)
        -: 1929:{
        -: 1930:    dVAR; dXSARGS;
        -: 1931:    if (items != 1)
        -: 1932:       croak_xs_usage(cv,  "data_ref");
        -: 1933:    {
        -: 1934:	SV*	data_ref = ST(0)
        -: 1935:;
        -: 1936:	SV *	RETVAL;
        -: 1937:#line 1746 "LikeR.xs"
        -: 1938:{
        -: 1939:	AV*restrict obs_av = (AV*)SvRV(data_ref);
        -: 1940:	int r = av_top_index(obs_av) + 1, c = 0;
        -: 1941:	bool is_2d = 0;
        -: 1942:	SV**restrict first_elem = av_fetch(obs_av, 0, 0);
        -: 1943:	if (first_elem && SvROK(*first_elem) && SvTYPE(SvRV(*first_elem)) == SVt_PVAV) {
        -: 1944:		is_2d = 1;
        -: 1945:		AV*restrict first_row = (AV*)SvRV(*first_elem);
        -: 1946:		c = av_top_index(first_row) + 1;
        -: 1947:	} else {
        -: 1948:		c = r;
        -: 1949:		r = 1;
        -: 1950:	}
        -: 1951:
        -: 1952:	double stat = 0.0, grand_total = 0.0;
        -: 1953:	int df = 0;
        -: 1954:	int yates = (is_2d && r == 2 && c == 2) ? 1 : 0;
        -: 1955:
        -: 1956:	AV*restrict expected_av = newAV();
        -: 1957:	if (is_2d) {
        -: 1958:		double *restrict row_sum = (double*)safemalloc(r * sizeof(double));
        -: 1959:		double *restrict col_sum = (double*)safemalloc(c * sizeof(double));
        -: 1960:		for(unsigned int i=0; i<r; i++) row_sum[i] = 0.0;
        -: 1961:		for(unsigned int j=0; j<c; j++) col_sum[j] = 0.0;
        -: 1962:		for (unsigned int i = 0; i < r; i++) {
        -: 1963:			SV**restrict row_sv = av_fetch(obs_av, i, 0);
        -: 1964:			AV*restrict row = (AV*)SvRV(*row_sv);
        -: 1965:			for (unsigned int j = 0; j < c; j++) {
        -: 1966:				 SV**restrict val_sv = av_fetch(row, j, 0);
        -: 1967:				 double val = SvNV(*val_sv);
        -: 1968:				 row_sum[i] += val;
        -: 1969:				 col_sum[j] += val;
        -: 1970:				 grand_total += val;
        -: 1971:			}
        -: 1972:		}
        -: 1973:		for (unsigned int i = 0; i < r; i++) {
        -: 1974:			AV*restrict exp_row = newAV();
        -: 1975:			SV**restrict row_sv = av_fetch(obs_av, i, 0);
        -: 1976:			AV*restrict row = (AV*)SvRV(*row_sv);
        -: 1977:			for (unsigned int j = 0; j < c; j++) {
        -: 1978:				double E = (row_sum[i] * col_sum[j]) / grand_total;
        -: 1979:				SV**restrict val_sv = av_fetch(row, j, 0);
        -: 1980:				double O = SvNV(*val_sv);
        -: 1981:				av_push(exp_row, newSVnv(E));
        -: 1982:				if (yates) {
        -: 1983:				  // Exact R logic: min(0.5, abs(O - E))
        -: 1984:				  double abs_diff = fabs(O - E);
        3: 1985:				  double y_corr = (abs_diff > 0.5) ? 0.5 : abs_diff;
call    0 returned 3
        3: 1986:				  double diff = abs_diff - y_corr;
        -: 1987:				  stat += (diff * diff) / E;
        3: 1988:				} else {
        -: 1989:				  stat += ((O - E) * (O - E)) / E;
        -: 1990:				}
        -: 1991:			}
        -: 1992:			av_push(expected_av, newRV_noinc((SV*)exp_row));
function XS_Stats__LikeR_write_table called 13 returned 85% blocks executed 78%
       13: 1993:		}
        -: 1994:		safefree(row_sum); safefree(col_sum);
       13: 1995:		df = (r - 1) * (c - 1);
       13: 1995-block  0
call    0 returned 13
        -: 1996:	} else {
        -: 1997:	  for (unsigned int j = 0; j < c; j++) {
        -: 1998:		   SV**restrict val_sv = av_fetch(obs_av, j, 0);
       13: 1999:		   grand_total += SvNV(*val_sv);
branch  0 taken 13 (fallthrough)
branch  1 taken 0
        -: 2000:	  }
        -: 2001:	  double E = grand_total / (double)c;
        -: 2002:	  for (unsigned int j = 0; j < c; j++) {
        -: 2003:		   SV**restrict val_sv = av_fetch(obs_av, j, 0);
        -: 2004:		   double O = SvNV(*val_sv);
        -: 2005:		   av_push(expected_av, newSVnv(E));
        -: 2006:		   stat += ((O - E) * (O - E)) / E;
        -: 2007:	  }
        -: 2008:	  df = c - 1;
        -: 2009:	}
        -: 2010:	double p_val = get_p_value(stat, df);
        -: 2011:	HV*restrict results = newHV();
        -: 2012:	hv_store(results, "statistic", 9, newSVnv(stat), 0);
        -: 2013:	hv_store(results, "df", 2, newSViv(df), 0);
        -: 2014:	hv_store(results, "p_value", 7, newSVnv(p_val), 0);
        -: 2015:	hv_store(results, "expected", 8, newRV_noinc((SV*)expected_av), 0);
        -: 2016:	if (is_2d) {
        -: 2017:		if (yates) {
        -: 2018:			hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test with Yates' continuity correction", 0), 0);
        -: 2019:		} else {
        -: 2020:			hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test", 0), 0);
        -: 2021:		}
        -: 2022:	} else {
        -: 2023:	  hv_store(results, "method", 6, newSVpv("Chi-squared test for given probabilities", 0), 0);
        -: 2024:	}
        -: 2025:	RETVAL = newRV_noinc((SV*)results);
        -: 2026:}
        -: 2027:#line 2028 "LikeR.c"
        -: 2028:	RETVAL = sv_2mortal(RETVAL);
        -: 2029:	ST(0) = RETVAL;
        -: 2030:    }
        -: 2031:    XSRETURN(1);
        -: 2032:}
        -: 2033:
        -: 2034:
        -: 2035:XS_EUPXS(XS_Stats__LikeR_write_table); /* prototype to pass -Wmissing-prototypes */
        -: 2036:XS_EUPXS(XS_Stats__LikeR_write_table)
        -: 2037:{
        -: 2038:    dVAR; dXSARGS;
        -: 2039:    PERL_UNUSED_VAR(cv); /* -W */
        -: 2040:    PERL_UNUSED_VAR(items); /* -W */
        -: 2041:    PERL_UNUSED_VAR(ax); /* -Wall */
        -: 2042:    SP -= items;
        -: 2043:    {
        -: 2044:#line 1842 "LikeR.xs"
        -: 2045:{
        -: 2046:	SV *restrict data_sv = NULL;
        -: 2047:	SV *restrict file_sv = NULL;
        -: 2048:	unsigned int arg_idx = 0;
        -: 2049:
        -: 2050:	// Mimic the Perl shift logic
        -: 2051:	if (arg_idx < items && SvROK(ST(arg_idx))) {
        -: 2052:	int type = SvTYPE(SvRV(ST(arg_idx)));
        -: 2053:	if (type == SVt_PVHV || type == SVt_PVAV) {
        -: 2054:		  data_sv = ST(arg_idx);
        -: 2055:		  arg_idx++;
        -: 2056:	}
        -: 2057:	}
        -: 2058:	if (arg_idx < items) {
        -: 2059:	file_sv = ST(arg_idx);
        -: 2060:	arg_idx++;
        -: 2061:	}
        -: 2062:
        -: 2063:	const char *restrict sep = ",";
        -: 2064:	const char *restrict undef_val = "NA";
        -: 2065:	SV *restrict row_names_sv = sv_2mortal(newSViv(1));
        -: 2066:	SV *restrict col_names_sv = NULL;
        -: 2067:
        -: 2068:	// Read the remaining Hash-style arguments
        -: 2069:	for (; arg_idx < items; arg_idx += 2) {
        -: 2070:	if (arg_idx + 1 >= items) croak("write_table: Odd number of arguments passed");
        -: 2071:	const char *restrict key = SvPV_nolen(ST(arg_idx));
        -: 2072:	SV *restrict val = ST(arg_idx + 1);
        -: 2073:	if (strEQ(key, "data")) data_sv = val;
        -: 2074:	else if (strEQ(key, "col.names")) col_names_sv = val;
        -: 2075:	else if (strEQ(key, "file")) file_sv = val;
        -: 2076:	else if (strEQ(key, "row.names")) row_names_sv = val;
        -: 2077:	else if (strEQ(key, "sep")) sep = SvPV_nolen(val);
        -: 2078:	else if (strEQ(key, "undef.val")) undef_val = SvPV_nolen(val);
        -: 2079:	else croak("write_table: Unknown arguments passed: %s", key);
        -: 2080:	}
        -: 2081:
        -: 2082:	if (!data_sv || !SvROK(data_sv)) {
        -: 2083:	croak("write_table: 'data' must be a HASH or ARRAY reference\n");
        -: 2084:	}
        -: 2085:	SV *restrict data_ref = SvRV(data_sv);
        -: 2086:	if (SvTYPE(data_ref) != SVt_PVHV && SvTYPE(data_ref) != SVt_PVAV) {
        -: 2087:	croak("write_table: 'data' must be a HASH or ARRAY reference\n");
        -: 2088:	}
        -: 2089:
        -: 2090:	if (!file_sv || !SvOK(file_sv)) croak("write_table: file name missing\n");
        -: 2091:	const char *restrict file = SvPV_nolen(file_sv);
        -: 2092:
        -: 2093:	if (col_names_sv && SvOK(col_names_sv)) {
        -: 2094:	if (!SvROK(col_names_sv) || SvTYPE(SvRV(col_names_sv)) != SVt_PVAV) {
        -: 2095:		  croak("write_table: 'col.names' must be an ARRAY reference\n");
        -: 2096:	}
        -: 2097:	}
        -: 2098:
        -: 2099:	bool is_hoh = 0, is_hoa = 0, is_aoh = 0;
        -: 2100:	AV *restrict rows_av = NULL;
        -: 2101:
        -: 2102:	// Validate Input Structures & Homogeneity 
        -: 2103:	if (SvTYPE(data_ref) == SVt_PVHV) {
        -: 2104:	  HV *restrict hv = (HV*)data_ref;
        -: 2105:	  if (hv_iterinit(hv) == 0) XSRETURN_EMPTY;
        -: 2106:
        -: 2107:	  HE *restrict entry = hv_iternext(hv);
        -: 2108:	  SV *restrict first_val = hv_iterval(hv, entry);
        -: 2109:	  if (!first_val || !SvROK(first_val)) {
        -: 2110:		   croak("write_table: Data values must be either all HASHes or all ARRAYs\n");
        -: 2111:	  }
        -: 2112:	  int first_type = SvTYPE(SvRV(first_val));
        -: 2113:	  if (first_type != SVt_PVHV && first_type != SVt_PVAV) {
        -: 2114:		   croak("write_table: Data values must be either all HASHes or all ARRAYs\n");
        -: 2115:	  }
        -: 2116:	  is_hoh = (first_type == SVt_PVHV);
        -: 2117:	  is_hoa = (first_type == SVt_PVAV);
        -: 2118:	  hv_iterinit(hv);
        -: 2119:	  while ((entry = hv_iternext(hv))) {
        -: 2120:		   SV *restrict val = hv_iterval(hv, entry);
        -: 2121:		   if (!val || !SvROK(val) || SvTYPE(SvRV(val)) != first_type) {
        -: 2122:		        croak("write_table: Mixed data types detected. Ensure all values are %s references.\n", is_hoh ? "HASH" : "ARRAY");
        -: 2123:		   }
        -: 2124:	  }
        -: 2125:	  if (is_hoh) {
        -: 2126:		   rows_av = newAV();
        -: 2127:		   hv_iterinit(hv);
        -: 2128:		   while ((entry = hv_iternext(hv))) {
        -: 2129:		        av_push(rows_av, newSVsv(hv_iterkeysv(entry)));
        -: 2130:		   }
        -: 2131:	  }
        -: 2132:	} else {
        -: 2133:	  AV *restrict av = (AV*)data_ref;
        -: 2134:	  if (av_len(av) < 0) XSRETURN_EMPTY;
        -: 2135:	  SV **restrict first_ptr = av_fetch(av, 0, 0);
        -: 2136:	  if (!first_ptr || !*first_ptr || !SvROK(*first_ptr) || SvTYPE(SvRV(*first_ptr)) != SVt_PVHV) {
        -: 2137:		   croak("write_table: For ARRAY data, all elements must be HASH references (Array of Hashes)\n");
        -: 2138:	  }
        -: 2139:
        -: 2140:	  for (size_t i = 0; i <= av_len(av); i++) {
        -: 2141:		   SV **restrict ptr = av_fetch(av, i, 0);
        -: 2142:		   if (!ptr || !*ptr || !SvROK(*ptr) || SvTYPE(SvRV(*ptr)) != SVt_PVHV) {
        -: 2143:		        croak("write_table: Mixed data types detected in Array of Hashes. All elements must be HASH references.\n");
        -: 2144:		   }
        -: 2145:	  }
        -: 2146:	  is_aoh = 1;
        -: 2147:	}
        -: 2148:
        -: 2149:	PerlIO *restrict fh = PerlIO_open(file, "w");
        -: 2150:	if (!fh) croak("write_table: Could not open '%s' for writing", file);
        -: 2151:
        -: 2152:	AV *restrict headers_av = newAV();
        -: 2153:	bool inc_rownames = (row_names_sv && SvTRUE(row_names_sv)) ? 1 : 0;
        -: 2154:	const char *restrict rownames_col = NULL;
        -: 2155:
        -: 2156:	// ----- Hash of Hashes -----
        -: 2157:	if (is_hoh) {
        -: 2158:		if (col_names_sv && SvOK(col_names_sv)) {
        -: 2159:			AV *restrict c_av = (AV*)SvRV(col_names_sv);
        -: 2160:			for(size_t i=0; i<=av_len(c_av); i++) {
        -: 2161:				SV **restrict c = av_fetch(c_av, i, 0);
        -: 2162:				if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
        -: 2163:			}
        -: 2164:		} else {
        -: 2165:			HV *restrict col_map = newHV();
        -: 2166:			hv_iterinit((HV*)data_ref);
        -: 2167:			HE *restrict entry;
        -: 2168:			while((entry = hv_iternext((HV*)data_ref))) {
        -: 2169:				 HV *restrict inner = (HV*)SvRV(hv_iterval((HV*)data_ref, entry));
        -: 2170:				 hv_iterinit(inner);
        -: 2171:				 HE *restrict inner_entry;
        -: 2172:				 while((inner_entry = hv_iternext(inner))) {
        -: 2173:				     hv_store_ent(col_map, hv_iterkeysv(inner_entry), newSViv(1), 0);
        -: 2174:				 }
        -: 2175:			}
        -: 2176:			unsigned num_cols = hv_iterinit(col_map);
        -: 2177:			const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
        -: 2178:			for(unsigned i=0; i<num_cols; i++) {
        -: 2179:				 HE *restrict ce = hv_iternext(col_map);
        -: 2180:				 col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
        -: 2181:			}
        -: 2182:			qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
        -: 2183:			for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
        -: 2184:			safefree(col_array);
        -: 2185:			SvREFCNT_dec(col_map);
        -: 2186:	}
        -: 2187:	size_t num_headers = av_len(headers_av) + 1;
        -: 2188:	const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
        -: 2189:
        -: 2190:	size_t h_idx = 0;
        -: 2191:	if (inc_rownames) header_row[h_idx++] = "";
        -: 2192:	for(unsigned short int i=0; i<num_headers; i++) {
        -: 2193:		  SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        -: 2194:		  header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        -: 2195:	}
        -: 2196:	print_string_row(fh, header_row, h_idx, sep);
        -: 2197:	safefree(header_row);
        -: 2198:
        -: 2199:	size_t num_rows = av_len(rows_av) + 1;
        -: 2200:	const char **restrict row_array = safemalloc(num_rows * sizeof(char*));
        -: 2201:	for(size_t i=0; i<num_rows; i++) {
        -: 2202:		 row_array[i] = SvPV_nolen(*av_fetch(rows_av, i, 0));
        -: 2203:	}
        -: 2204:	qsort(row_array, num_rows, sizeof(char*), cmp_string_wt);
        -: 2205:
        -: 2206:	HV *restrict data_hv = (HV*)data_ref;
        -: 2207:	const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
        -: 2208:
        -: 2209:	for(size_t i=0; i<num_rows; i++) {
        -: 2210:		  size_t d_idx = 0;
        -: 2211:		  if (inc_rownames) row_data[d_idx++] = row_array[i];
        -: 2212:
        -: 2213:		  SV **restrict inner_hv_ptr = hv_fetch(data_hv, row_array[i], strlen(row_array[i]), 0);
        -: 2214:		  HV *restrict inner_hv = inner_hv_ptr ? (HV*)SvRV(*inner_hv_ptr) : NULL;
        -: 2215:
        -: 2216:		  for(size_t j=0; j<num_headers; j++) {
        -: 2217:		      SV**restrict h_ptr = av_fetch(headers_av, j, 0);
        -: 2218:		      const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        -: 2219:		      SV **restrict cell_ptr = inner_hv ? hv_fetch(inner_hv, col_name, strlen(col_name), 0) : NULL;
        -: 2220:		      if (cell_ptr && SvOK(*cell_ptr)) {
        -: 2221:		          if (SvROK(*cell_ptr)) {
        -: 2222:		              PerlIO_close(fh);
        -: 2223:		              safefree(row_array);
        -: 2224:		            safefree(row_data);
        -: 2225:		              if (headers_av) SvREFCNT_dec(headers_av);
        -: 2226:		              if (rows_av) SvREFCNT_dec(rows_av);
        -: 2227:		              croak("write_table: Cannot write nested reference types to table\n");
        -: 2228:		          }
        -: 2229:		          row_data[d_idx++] = SvPV_nolen(*cell_ptr);
        -: 2230:		      } else {
        -: 2231:		          row_data[d_idx++] = undef_val;
        -: 2232:		      }
        -: 2233:		  }
        -: 2234:		  print_string_row(fh, row_data, d_idx, sep);
        -: 2235:	}
        -: 2236:	safefree(row_array); safefree(row_data);
        -: 2237:
        -: 2238:	} else if (is_hoa) { // ----- Hash of Arrays -----
        -: 2239:	  HV *restrict data_hv = (HV*)data_ref;
        -: 2240:	  size_t max_rows = 0;
        -: 2241:	  hv_iterinit(data_hv);
        -: 2242:	  HE *restrict entry;
        -: 2243:	  while((entry = hv_iternext(data_hv))) {
        -: 2244:		   AV *restrict arr = (AV*)SvRV(hv_iterval(data_hv, entry));
        -: 2245:		   size_t len = av_len(arr) + 1;
        -: 2246:		   if (len > max_rows) max_rows = len;
        -: 2247:	  }
        -: 2248:
        -: 2249:	  if (col_names_sv && SvOK(col_names_sv)) {
        -: 2250:		   AV *restrict c_av = (AV*)SvRV(col_names_sv);
        -: 2251:		   for(size_t i=0; i<=av_len(c_av); i++) {
        -: 2252:		        SV **restrict c = av_fetch(c_av, i, 0);
        -: 2253:		        if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
        -: 2254:		   }
        -: 2255:	  } else {
        -: 2256:		   unsigned int num_cols = hv_iterinit(data_hv);
        -: 2257:		   const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
        -: 2258:		   for(unsigned int i=0; i<num_cols; i++) {
        -: 2259:		        HE *restrict ce = hv_iternext(data_hv);
        -: 2260:		        col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
        -: 2261:		   }
        -: 2262:		   qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
        -: 2263:		   for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
        -: 2264:		   safefree(col_array);
        -: 2265:	  }
        -: 2266:	  if (av_len(headers_av) < 0) croak("Could not get headers in write_table");
        -: 2267:	  if (inc_rownames && contains_nondigit(row_names_sv)) {
        -: 2268:		   rownames_col = SvPV_nolen(row_names_sv);
        -: 2269:		   AV *restrict filtered_headers = (AV*)sv_2mortal((SV*)newAV());
        -: 2270:
        -: 2271:		   for(size_t i=0; i<=av_len(headers_av); i++) {
        -: 2272:		        SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        -: 2273:		        if (!h_ptr || !*h_ptr) continue;
        -: 2274:		        SV *restrict h_sv = *h_ptr;
        -: 2275:		        if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
        -: 2276:		            av_push(filtered_headers, newSVsv(h_sv));
        -: 2277:		        }
        -: 2278:		   }
        -: 2279:		   SvREFCNT_dec(headers_av);
        -: 2280:		   headers_av = filtered_headers;
        -: 2281:	  }
        -: 2282:	  size_t num_headers = av_len(headers_av) + 1;
        -: 2283:	  const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
        -: 2284:	  size_t h_idx = 0;
        -: 2285:	  if (inc_rownames) header_row[h_idx++] = "";
        -: 2286:	  for(size_t i=0; i<num_headers; i++) {
        -: 2287:		   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        -: 2288:		   header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        -: 2289:	  }
        -: 2290:	  print_string_row(fh, header_row, h_idx, sep);
        -: 2291:	  safefree(header_row);
        -: 2292:	  const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
        -: 2293:	  for(size_t i=0; i<max_rows; i++) {
        -: 2294:		   size_t d_idx = 0;
        -: 2295:		   if (inc_rownames) {
        -: 2296:		        if (rownames_col) {
        -: 2297:		            SV **restrict rn_arr_ptr = hv_fetch(data_hv, rownames_col, strlen(rownames_col), 0);
        -: 2298:		            if (rn_arr_ptr && SvROK(*rn_arr_ptr)) {
        -: 2299:		                AV *restrict rn_arr = (AV*)SvRV(*rn_arr_ptr);
        -: 2300:		                SV **restrict rn_val_ptr = av_fetch(rn_arr, i, 0);
        -: 2301:		                if (rn_val_ptr && SvOK(*rn_val_ptr)) {
        -: 2302:		                    if (SvROK(*rn_val_ptr)) {
        -: 2303:		                        PerlIO_close(fh);
        -: 2304:		                        safefree(row_data);
        -: 2305:		                        if (headers_av) SvREFCNT_dec(headers_av);
        -: 2306:		                        croak("write_table: Cannot write nested reference types to table\n");
        -: 2307:		                    }
        -: 2308:		                    row_data[d_idx++] = SvPV_nolen(*rn_val_ptr);
        -: 2309:		                } else {
        -: 2310:		                    row_data[d_idx++] = undef_val;
        -: 2311:		                }
        -: 2312:		            } else {
        -: 2313:		                row_data[d_idx++] = undef_val;
        -: 2314:		            }
        -: 2315:		        } else {
        -: 2316:		            char buf[32];
        -: 2317:		            snprintf(buf, sizeof(buf), "%ld", (long)(i + 1));
        -: 2318:		            row_data[d_idx++] = savepv(buf);
        -: 2319:		        }
        -: 2320:		   }
        -: 2321:		   for(size_t j=0; j<num_headers; j++) {
        -: 2322:		        SV**restrict h_ptr = av_fetch(headers_av, j, 0);
        -: 2323:		        const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        -: 2324:		        SV **restrict arr_ptr = hv_fetch(data_hv, col_name, strlen(col_name), 0);
        -: 2325:		        if (arr_ptr && SvROK(*arr_ptr)) {
        -: 2326:		            AV *restrict arr = (AV*)SvRV(*arr_ptr);
        -: 2327:		            SV **restrict cell_ptr = av_fetch(arr, i, 0);
        -: 2328:		            if (cell_ptr && SvOK(*cell_ptr)) {
        -: 2329:		                if (SvROK(*cell_ptr)) {
        -: 2330:		                    PerlIO_close(fh);
        -: 2331:		                    safefree(row_data);
        -: 2332:		                    if (headers_av) SvREFCNT_dec(headers_av);
        -: 2333:		                    croak("write_table: Cannot write nested reference types to table\n");
        -: 2334:		                }
        -: 2335:		                row_data[d_idx++] = SvPV_nolen(*cell_ptr);
        -: 2336:		            } else {
        -: 2337:		                row_data[d_idx++] = undef_val;
        -: 2338:		            }
        -: 2339:		        } else {
        -: 2340:		            row_data[d_idx++] = undef_val;
        -: 2341:		        }
        -: 2342:		   }
        -: 2343:		   print_string_row(fh, row_data, d_idx, sep);
        -: 2344:		   if (inc_rownames && !rownames_col) safefree((char*)row_data[0]);
        -: 2345:	  }
        -: 2346:	  safefree(row_data);
        -: 2347:	} else if (is_aoh) {// ----- Array of Hashes -----
        -: 2348:	  AV *restrict data_av = (AV*)data_ref;
        -: 2349:	  size_t num_rows = av_len(data_av) + 1;
        -: 2350:	  if (col_names_sv && SvOK(col_names_sv)) {
        -: 2351:		   AV *restrict c_av = (AV*)SvRV(col_names_sv);
        -: 2352:		   for(size_t i=0; i<=av_len(c_av); i++) {
        -: 2353:		        SV **restrict c = av_fetch(c_av, i, 0);
        -: 2354:		        if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
        -: 2355:		   }
        -: 2356:	  } else {
        -: 2357:		   HV *restrict col_map = newHV();
        -: 2358:		   for(size_t i=0; i<num_rows; i++) {
        -: 2359:		        SV **restrict row_ptr = av_fetch(data_av, i, 0);
        -: 2360:		        if (row_ptr && SvROK(*row_ptr)) {
        -: 2361:		            HV *restrict row_hv = (HV*)SvRV(*row_ptr);
        -: 2362:		            hv_iterinit(row_hv);
        -: 2363:		            HE *restrict entry;
        -: 2364:		            while((entry = hv_iternext(row_hv))) {
        -: 2365:		                hv_store_ent(col_map, hv_iterkeysv(entry), newSViv(1), 0);
        -: 2366:		            }
        -: 2367:		        }
        -: 2368:		   }
        -: 2369:		   unsigned num_cols = hv_iterinit(col_map);
        -: 2370:		   const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
        -: 2371:		   for(unsigned int i=0; i<num_cols; i++) {
        -: 2372:		        HE *restrict ce = hv_iternext(col_map);
        -: 2373:		        col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
        -: 2374:		   }
        -: 2375:		   qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
        -: 2376:		   for(unsigned int i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
        -: 2377:		   safefree(col_array);
        -: 2378:		   SvREFCNT_dec(col_map);
        -: 2379:	  }
        -: 2380:	  if (inc_rownames && contains_nondigit(row_names_sv)) {
        -: 2381:		   rownames_col = SvPV_nolen(row_names_sv);
        -: 2382:		   AV *restrict filtered_headers = newAV();
        -: 2383:		   for(size_t i=0; i<=av_len(headers_av); i++) {
        -: 2384:		        SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        -: 2385:		        if (!h_ptr || !*h_ptr) continue;
        -: 2386:		        SV *restrict h_sv = *h_ptr;
        -: 2387:		        if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
        -: 2388:		            av_push(filtered_headers, newSVsv(h_sv));
        -: 2389:		        }
        -: 2390:		   }
        -: 2391:		   SvREFCNT_dec(headers_av);
        -: 2392:		   headers_av = filtered_headers;
        -: 2393:	  }
        -: 2394:	  size_t num_headers = av_len(headers_av) + 1;
        -: 2395:	  const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
        -: 2396:	  size_t h_idx = 0;
        -: 2397:	  if (inc_rownames) header_row[h_idx++] = "";
        -: 2398:	  for(size_t i=0; i<num_headers; i++) {
        -: 2399:		   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        -: 2400:		   header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        -: 2401:	  }
        -: 2402:	  print_string_row(fh, header_row, h_idx, sep);
        -: 2403:	  safefree(header_row);
        -: 2404:	  const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
        -: 2405:	  for(size_t i=0; i<num_rows; i++) {
        -: 2406:		   size_t d_idx = 0;
        -: 2407:		   SV **restrict row_ptr = av_fetch(data_av, i, 0);
        -: 2408:		   HV *restrict row_hv = (row_ptr && SvROK(*row_ptr)) ? (HV*)SvRV(*row_ptr) : NULL;
        -: 2409:		   if (inc_rownames) {
        -: 2410:		       if (rownames_col) {
        -: 2411:		         SV **restrict rn_val_ptr = row_hv ? hv_fetch(row_hv, rownames_col, strlen(rownames_col), 0) : NULL;
        -: 2412:		         if (rn_val_ptr && SvOK(*rn_val_ptr)) {
        -: 2413:		               if (SvROK(*rn_val_ptr)) {
        -: 2414:		                    PerlIO_close(fh);
        -: 2415:		                         safefree(row_data);
        -: 2416:		                         if (headers_av) SvREFCNT_dec(headers_av);
        -: 2417:		                    croak("write_table: Cannot write nested reference types to table\n");
        -: 2418:		               }
function XS_Stats__LikeR__parse_csv_file called 13 returned 100% blocks executed 86%
       13: 2419:		               row_data[d_idx++] = SvPV_nolen(*rn_val_ptr);
        -: 2420:		         } else {
       13: 2421:		               row_data[d_idx++] = undef_val;
       13: 2421-block  0
call    0 returned 13
       13: 2422:		         }
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2422-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 13
    #####: 2423:		       } else {
    %%%%%: 2423-block  0
call    0 never executed
        -: 2424:		         char buf[32];
        -: 2425:		         snprintf(buf, sizeof(buf), "%ld", (long)(i + 1));
       13: 2426:		         row_data[d_idx++] = savepv(buf);
       13: 2426-block  0
call    0 returned 13
        -: 2427:		       }
       13: 2428:		   }
call    0 returned 13
        -: 2429:
       13: 2430:		   for(size_t j=0; j<num_headers; j++) {
call    0 returned 13
        -: 2431:		        SV**restrict h_ptr = av_fetch(headers_av, j, 0);
        -: 2432:		        const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        -: 2433:		        SV **restrict cell_ptr = row_hv ? hv_fetch(row_hv, col_name, strlen(col_name), 0) : NULL;
       13: 2434:		        if (cell_ptr && SvOK(*cell_ptr)) {
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    #####: 2435:		            if (SvROK(*cell_ptr)) {
    %%%%%: 2435-block  0
        -: 2436:		                PerlIO_close(fh);
       13: 2437:		                safefree(row_data);
       13: 2437-block  0
        -: 2438:		                if (headers_av) SvREFCNT_dec(headers_av);
        -: 2439:		                croak("write_table: Cannot write nested reference types to table\n");
        -: 2440:		            }
        -: 2441:		            row_data[d_idx++] = SvPV_nolen(*cell_ptr);
        -: 2442:		        } else {
        -: 2443:		            row_data[d_idx++] = undef_val;
        -: 2444:		        }
        -: 2445:		   }
        -: 2446:		   print_string_row(fh, row_data, d_idx, sep);
        -: 2447:		   if (inc_rownames && !rownames_col) safefree((char*)row_data[0]);
        -: 2448:	  }
        -: 2449:	  safefree(row_data);
        -: 2450:	}
        -: 2451:	if (headers_av) SvREFCNT_dec(headers_av);
        -: 2452:	if (rows_av) SvREFCNT_dec(rows_av);
        -: 2453:	PerlIO_close(fh);
        -: 2454:	XSRETURN_EMPTY;
        -: 2455:}
        -: 2456:#line 2457 "LikeR.c"
        -: 2457:	PUTBACK;
        -: 2458:	return;
        -: 2459:    }
        -: 2460:}
        -: 2461:
        -: 2462:
        -: 2463:XS_EUPXS(XS_Stats__LikeR__parse_csv_file); /* prototype to pass -Wmissing-prototypes */
        -: 2464:XS_EUPXS(XS_Stats__LikeR__parse_csv_file)
        -: 2465:{
        -: 2466:    dVAR; dXSARGS;
        -: 2467:    if (items < 3 || items > 4)
        -: 2468:       croak_xs_usage(cv,  "file, sep_str, comment_str, callback= &PL_sv_undef");
        -: 2469:    {
        -: 2470:	SV *	RETVAL;
        -: 2471:	char*	file = (char *)SvPV_nolen(ST(0))
        -: 2472:;
        -: 2473:	const char*	sep_str = (const char *)SvPV_nolen(ST(1))
        -: 2474:;
        -: 2475:	const char*	comment_str = (const char *)SvPV_nolen(ST(2))
        -: 2476:;
        -: 2477:	SV*	callback;
        -: 2478:
        -: 2479:	if (items < 4)
        -: 2480:	    callback = &PL_sv_undef;
        -: 2481:	else {
        -: 2482:	    callback = ST(3)
        -: 2483:;
        -: 2484:	}
        -: 2485:#line 2257 "LikeR.xs"
        -: 2486:	PerlIO *restrict fp;
        -: 2487:	AV *restrict data = NULL;
        -: 2488:	AV *restrict current_row = newAV();
        -: 2489:	SV *restrict field = newSVpvs("");
        -: 2490:	bool in_quotes = 0, post_quote = 0;
        -: 2491:	size_t sep_len, comment_len;
        -: 2492:	SV *restrict line_sv;
        -: 2493:	bool use_cb = 0;
        -: 2494:#line 2495 "LikeR.c"
        -: 2495:#line 2266 "LikeR.xs"
        -: 2496:	if (SvOK(callback) && SvROK(callback) && SvTYPE(SvRV(callback)) == SVt_PVCV) {
        -: 2497:		use_cb = 1;
        -: 2498:	} else {
        -: 2499:		data = newAV();
        -: 2500:	}
        -: 2501:	sep_len = sep_str ? strlen(sep_str) : 0;
        -: 2502:	comment_len = comment_str ? strlen(comment_str) : 0;
        -: 2503:
        -: 2504:	fp = PerlIO_open(file, "r");
        -: 2505:	if (!fp) {
        -: 2506:		croak("Could not open file '%s'", file);
        -: 2507:	}
        -: 2508:	line_sv = newSV_type(SVt_PV);
        -: 2509:	// Read line by line using PerlIO
        -: 2510:	while (sv_gets(line_sv, fp, 0) != NULL) {
        -: 2511:		char *restrict line = SvPV_nolen(line_sv);
        -: 2512:		size_t len = SvCUR(line_sv);
        -: 2513:		// chomp \r\n (Handles Windows invisible \r natively)
        -: 2514:		if (len > 0 && line[len-1] == '\n') {
        -: 2515:			len--;
        -: 2516:			if (len > 0 && line[len-1] == '\r') {
        -: 2517:				len--;
        -: 2518:			}
        -: 2519:		}
        -: 2520:		if (!in_quotes) {
        -: 2521:			// Skip completely empty lines (\h*[\r\n]+$ equivalent)
        -: 2522:			bool is_empty = 1;
        -: 2523:			for (size_t i = 0; i < len; i++) {
        -: 2524:				if (line[i] != ' ' && line[i] != '\t') { is_empty = 0; break; }
        -: 2525:			}
        -: 2526:			if (is_empty) continue;
        -: 2527:
        -: 2528:			// Skip comments
        -: 2529:			if (comment_len > 0 && len >= comment_len && strncmp(line, comment_str, comment_len) == 0) {
        -: 2530:				continue;
        -: 2531:			}
        -: 2532:		}
        -: 2533:		// --- CORE PARSING MACHINE ---
        -: 2534:		for (size_t i = 0; i < len; i++) {
        -: 2535:			const char ch = line[i];
        -: 2536:			if (ch == '\r') continue;
        -: 2537:			if (ch == '"') {
        -: 2538:				if (in_quotes && (i + 1 < len) && line[i+1] == '"') {
        -: 2539:					sv_catpvn(field, "\"", 1);
        -: 2540:					i++; // Skip the escaped second quote
        -: 2541:				} else if (in_quotes) {
        -: 2542:					in_quotes = 0;  // Close quotes
        -: 2543:					post_quote = 1;
        -: 2544:				} else if (!post_quote) {
        -: 2545:					in_quotes = 1; // Open quotes (only when not in post-quote state)
        -: 2546:				}
        -: 2547:			} else if (!in_quotes && sep_len > 0 && (len - i) >= sep_len && strncmp(line + i, sep_str, sep_len) == 0) {
        -: 2548:				av_push(current_row, newSVsv(field));
        -: 2549:				sv_setpvs(field, ""); // Reset for next field
        -: 2550:				i += sep_len - 1;     // Advance past multi-char separators
        -: 2551:				post_quote = 0;
        -: 2552:			} else {
        -: 2553:				sv_catpvn(field, &ch, 1);
        -: 2554:			}
        -: 2555:		}
        -: 2556:		if (in_quotes) {
        -: 2557:			// Line ended but quotes are still open! Append newline and fetch next
        -: 2558:			sv_catpvn(field, "\n", 1);
        -: 2559:		} else {
        -: 2560:			post_quote = 0; // Reset post-quote state at row boundary
        -: 2561:			// Push the final field of the record
        -: 2562:			av_push(current_row, newSVsv(field));
        -: 2563:			sv_setpvs(field, "");
        -: 2564:			// If a callback is provided, invoke it in a streaming fashion
        -: 2565:			if (use_cb) {
       13: 2566:				dSP;
       13: 2566-block  0
call    0 returned 13
       13: 2567:				ENTER;
        -: 2568:				SAVETMPS;
       13: 2569:				PUSHMARK(SP);
        -: 2570:				XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
        -: 2571:				PUTBACK;
        -: 2572:				call_sv(callback, G_DISCARD);
        -: 2573:				FREETMPS;
function XS_Stats__LikeR_cov called 4 returned 100% blocks executed 86%
        4: 2574:				LEAVE;
        -: 2575:				SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
        4: 2576:			} else {
        4: 2576-block  0
call    0 returned 4
        4: 2577:				av_push(data, newRV_noinc((SV*)current_row));
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 2577-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 4
    #####: 2578:			}
    %%%%%: 2578-block  0
call    0 never executed
        -: 2579:			current_row = newAV();
        -: 2580:		}
        4: 2581:	}
        -: 2582:	PerlIO_close(fp);
        4: 2583:	SvREFCNT_dec(line_sv);
        -: 2584:
        -: 2585:	if (in_quotes) {
        -: 2586:		av_push(current_row, newSVsv(field));
        4: 2587:		if (use_cb) {
        4: 2587-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1: 2588:			dSP;
        1: 2588-block  0
        -: 2589:			ENTER;
        3: 2590:			SAVETMPS;
        3: 2590-block  0
call    0 returned 3
        -: 2591:			PUSHMARK(SP);
        -: 2592:			XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
        -: 2593:			PUTBACK;
        -: 2594:			call_sv(callback, G_DISCARD);
        -: 2595:			FREETMPS;
        -: 2596:			LEAVE;
        -: 2597:			SvREFCNT_dec(current_row);
        -: 2598:		} else {
        -: 2599:			av_push(data, newRV_noinc((SV*)current_row));
        -: 2600:		}
        -: 2601:		current_row = newAV();
        -: 2602:	}
        -: 2603:	SvREFCNT_dec(field);
        -: 2604:	SvREFCNT_dec(current_row);
        -: 2605:	if (use_cb) {
        -: 2606:		RETVAL = &PL_sv_undef; // Memory was fully handled by callback stream
        -: 2607:	} else {
        -: 2608:		RETVAL = newRV_noinc((SV*)data);
        -: 2609:	}
        -: 2610:#line 2611 "LikeR.c"
        -: 2611:	RETVAL = sv_2mortal(RETVAL);
        -: 2612:	ST(0) = RETVAL;
        -: 2613:    }
        -: 2614:    XSRETURN(1);
        -: 2615:}
        -: 2616:
        -: 2617:
        -: 2618:XS_EUPXS(XS_Stats__LikeR_cov); /* prototype to pass -Wmissing-prototypes */
        -: 2619:XS_EUPXS(XS_Stats__LikeR_cov)
        -: 2620:{
        -: 2621:    dVAR; dXSARGS;
        -: 2622:    if (items < 2 || items > 3)
        -: 2623:       croak_xs_usage(cv,  "x_sv, y_sv, method= \"pearson\"");
        -: 2624:    {
        -: 2625:	SV *	RETVAL;
        -: 2626:	SV *	x_sv = ST(0)
        -: 2627:;
        -: 2628:	SV *	y_sv = ST(1)
        -: 2629:;
        -: 2630:	const char *	method;
        -: 2631:
        -: 2632:	if (items < 3)
        -: 2633:	    method = "pearson";
        -: 2634:	else {
        -: 2635:	    method = (const char *)SvPV_nolen(ST(2))
        -: 2636:;
        -: 2637:	}
        -: 2638:#line 2385 "LikeR.xs"
        -: 2639:	{
        -: 2640:		// 1. Validate inputs are Array References
        -: 2641:		if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV) {
        -: 2642:			croak("cov: first argument 'x' must be an ARRAY reference");
        -: 2643:		}
        -: 2644:		if (!SvROK(y_sv) || SvTYPE(SvRV(y_sv)) != SVt_PVAV) {
        -: 2645:			croak("cov: second argument 'y' must be an ARRAY reference");
        -: 2646:		}
        -: 2647:
        -: 2648:		// 2. Validate method argument
        -: 2649:		if (strcmp(method, "pearson") != 0 && 
        -: 2650:			strcmp(method, "spearman") != 0 && 
        -: 2651:			strcmp(method, "kendall") != 0) {
        -: 2652:			croak("cov: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')", method);
        -: 2653:		}
        -: 2654:
        -: 2655:		AV *restrict x_av = (AV*)SvRV(x_sv);
        -: 2656:		AV *restrict y_av = (AV*)SvRV(y_sv);
        -: 2657:		size_t nx = av_len(x_av) + 1;
        -: 2658:		size_t ny = av_len(y_av) + 1;
        -: 2659:
        -: 2660:		if (nx != ny) {
        -: 2661:			croak("cov: incompatible dimensions (x has %lu, y has %lu)", 
        -: 2662:				   (unsigned long)nx, (unsigned long)ny);
        -: 2663:		}
        -: 2664:
        -: 2665:		// 3. Extract Valid Pairwise Data
        -: 2666:		// Allocate temporary C arrays for numeric processing
        -: 2667:		double *restrict x_val = (double*)safemalloc(nx * sizeof(double));
        -: 2668:		double *restrict y_val = (double*)safemalloc(nx * sizeof(double));
        -: 2669:		size_t n = 0;
        -: 2670:
        -: 2671:		for (size_t i = 0; i < nx; i++) {
        -: 2672:			SV **restrict x_tv = av_fetch(x_av, i, 0);
        -: 2673:			SV **restrict y_tv = av_fetch(y_av, i, 0);
        -: 2674:
        -: 2675:			// Extract numeric values, defaulting to NAN for missing/invalid data
        -: 2676:			double xv = (x_tv && SvOK(*x_tv) && looks_like_number(*x_tv)) ? SvNV(*x_tv) : NAN;
        -: 2677:			double yv = (y_tv && SvOK(*y_tv) && looks_like_number(*y_tv)) ? SvNV(*y_tv) : NAN;
        -: 2678:
        -: 2679:			// Pairwise complete observations (skips NAs seamlessly like R)
        -: 2680:			if (!isnan(xv) && !isnan(yv)) {
        -: 2681:				 x_val[n] = xv;
        -: 2682:				 y_val[n] = yv;
        -: 2683:				 n++;
        -: 2684:			}
        -: 2685:		}
        -: 2686:
        -: 2687:		// 4. Handle edge cases where data is too sparse
        -: 2688:		if (n < 2) {
        -: 2689:			Safefree(x_val);	Safefree(y_val);
        -: 2690:			RETVAL = newSVnv(NAN);
        -: 2691:		} else {
        -: 2692:			double ans = 0.0;			
        -: 2693:			// 5. Algorithm routing
        -: 2694:			if (strcmp(method, "kendall") == 0) {
        -: 2695:				 // R's default cov(..., method="kendall") iterates the full n x n space
        -: 2696:				 for (size_t i = 0; i < n; i++) {
        -: 2697:				     for (size_t j = 0; j < n; j++) {
        -: 2698:				         int sx = (x_val[i] > x_val[j]) - (x_val[i] < x_val[j]);
        -: 2699:				         int sy = (y_val[i] > y_val[j]) - (y_val[i] < y_val[j]);
        4: 2700:				         ans += (double)(sx * sy);
        4: 2700-block  0
call    0 returned 4
        4: 2701:				     }
        -: 2702:				 }
        4: 2703:			} else {
        -: 2704:				 double mean_x = 0.0, mean_y = 0.0, cov_sum = 0.0;
        -: 2705:				 if (strcmp(method, "spearman") == 0) {
        -: 2706:				     // Spearman: Rank the data first, then run standard covariance
        -: 2707:				     double *restrict rx = (double*)safemalloc(n * sizeof(double));
function XS_Stats__LikeR_glm called 7 returned 100% blocks executed 79%
        7: 2708:				     double *restrict ry = (double*)safemalloc(n * sizeof(double));
        -: 2709:				     // Uses your existing rank_data() helper from LikeR.xs
        7: 2710:				     rank_data(x_val, rx, n);
        7: 2710-block  0
call    0 returned 7
branch  1 taken 0 (fallthrough)
branch  2 taken 7
        -: 2711:				     rank_data(y_val, ry, n);
        -: 2712:				     for (size_t i = 0; i < n; i++) {
        -: 2713:				         double dx = rx[i] - mean_x;
        -: 2714:				         mean_x += dx / (i + 1);
        -: 2715:				         double dy = ry[i] - mean_y;
        -: 2716:				         mean_y += dy / (i + 1);
        -: 2717:				         cov_sum += dx * (ry[i] - mean_y);
        -: 2718:				     }
        -: 2719:				     Safefree(rx); Safefree(ry);
        -: 2720:				 } else { 
        -: 2721:				     // Pearson: Welford's Single-Pass Covariance Algorithm
        -: 2722:				     for (size_t i = 0; i < n; i++) {
        -: 2723:				         double dx = x_val[i] - mean_x;
        -: 2724:				         mean_x += dx / (i + 1);
        -: 2725:				         double dy = y_val[i] - mean_y;
        -: 2726:				         mean_y += dy / (i + 1);
        -: 2727:				         cov_sum += dx * (y_val[i] - mean_y);
        -: 2728:				     }
        -: 2729:				 }
        -: 2730:
        -: 2731:				 // Unbiased Sample Covariance (N - 1) for Pearson & Spearman
        -: 2732:				 ans = cov_sum / (n - 1);
        -: 2733:			}
        -: 2734:			Safefree(x_val); Safefree(y_val);
        -: 2735:			RETVAL = newSVnv(ans);
        -: 2736:		}
        -: 2737:	}
        -: 2738:#line 2739 "LikeR.c"
        -: 2739:	RETVAL = sv_2mortal(RETVAL);
        -: 2740:	ST(0) = RETVAL;
        -: 2741:    }
        -: 2742:    XSRETURN(1);
        -: 2743:}
        -: 2744:
        -: 2745:
        -: 2746:XS_EUPXS(XS_Stats__LikeR_glm); /* prototype to pass -Wmissing-prototypes */
        -: 2747:XS_EUPXS(XS_Stats__LikeR_glm)
        -: 2748:{
        -: 2749:    dVAR; dXSARGS;
        -: 2750:    PERL_UNUSED_VAR(cv); /* -W */
        -: 2751:    PERL_UNUSED_VAR(items); /* -W */
        -: 2752:    {
        -: 2753:	SV *	RETVAL;
        -: 2754:#line 2489 "LikeR.xs"
        -: 2755:{
        -: 2756:	const char *restrict formula  = NULL;
        -: 2757:	SV *restrict data_sv = NULL;
        -: 2758:	const char *restrict family_str = "gaussian";
        -: 2759:	char f_cpy[512];
        -: 2760:	char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
        -: 2761:
        -: 2762:	// Dynamic Term Arrays
        -: 2763:	char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
        -: 2764:	bool *restrict is_dummy = NULL;
        -: 2765:	char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
        -: 2766:	unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
        -: 2767:	size_t n = 0, valid_n = 0, i;
        -: 2768:	bool has_intercept = TRUE, converged = FALSE, boundary = FALSE;
        -: 2769:	unsigned int iter = 0, max_iter = 25, final_rank = 0, df_res = 0;
        -: 2770:	double deviance_old = 0.0, deviance_new = 0.0, null_dev = 0.0, aic = 0.0;
        -: 2771:	double dispersion = 0.0, epsilon = 1e-8;
        -: 2772:
        -: 2773:	char **restrict row_names = NULL;
        -: 2774:	char **restrict valid_row_names = NULL;
        -: 2775:	HV **restrict row_hashes = NULL;
        -: 2776:	HV *restrict data_hoa = NULL;
        -: 2777:	SV *restrict ref = NULL;
        -: 2778:
        -: 2779:	double *restrict X = NULL, *restrict Y = NULL, *restrict mu = NULL, *restrict eta = NULL;
        -: 2780:	double *restrict W = NULL, *restrict Z = NULL, *restrict beta = NULL, *restrict beta_old = NULL;
        -: 2781:	bool *restrict aliased = NULL;
        -: 2782:	double *restrict XtWX = NULL, *restrict XtWZ = NULL;
        -: 2783:
        -: 2784:	HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
        -: 2785:	AV *restrict terms_av;
        -: 2786:	HE *restrict entry;
        -: 2787:
        -: 2788:	if (items % 2 != 0) croak("Usage: glm(formula => 'am ~ wt + hp', data => \\%mtcars)");
        -: 2789:
        -: 2790:	for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
        -: 2791:	  const char *restrict key = SvPV_nolen(ST(i_arg));
        -: 2792:	  SV *restrict val = ST(i_arg + 1);
        -: 2793:	  if      (strEQ(key, "formula")) formula = SvPV_nolen(val);
        -: 2794:	  else if (strEQ(key, "data"))    data_sv = val;
        -: 2795:	  else if (strEQ(key, "family"))  family_str = SvPV_nolen(val);
        -: 2796:	  else croak("glm: unknown argument '%s'", key);
        -: 2797:	}        
        -: 2798:	if (!formula) croak("glm: formula is required");
        -: 2799:	if (!data_sv || !SvROK(data_sv)) croak("glm: data is required and must be a reference");
        -: 2800:
        -: 2801:	bool is_binomial = (strcmp(family_str, "binomial") == 0);
        -: 2802:	bool is_gaussian = (strcmp(family_str, "gaussian") == 0);
        -: 2803:	if (!is_binomial && !is_gaussian) croak("glm: unsupported family '%s'", family_str);
        -: 2804:
        -: 2805:	// --- Formula Parsing & Expansion ---
        -: 2806:	Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
        -: 2807:	Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
        -: 2808:	Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
        -: 2809:
        -: 2810:	src = (char*)formula; dst = f_cpy;
        -: 2811:	while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
        -: 2812:	*dst = '\0';
        -: 2813:
        -: 2814:	tilde = strchr(f_cpy, '~');
        -: 2815:	if (!tilde) croak("glm: invalid formula, missing '~'");
        -: 2816:	*tilde = '\0';
        -: 2817:	lhs = f_cpy; rhs = tilde + 1;
        -: 2818:
        -: 2819:	if (strstr(rhs, "-1")) has_intercept = FALSE;
        -: 2820:	if (has_intercept) terms[num_terms++] = savepv("Intercept");
        -: 2821:
        -: 2822:	chunk = strtok(rhs, "+");
        -: 2823:	while (chunk != NULL) {
        -: 2824:	  if (num_terms >= term_cap - 3) {
        -: 2825:		   term_cap *= 2;
        -: 2826:		   Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
        -: 2827:	  }
        -: 2828:	  if (strcmp(chunk, "1") == 0 || strcmp(chunk, "-1") == 0) {
        -: 2829:		   chunk = strtok(NULL, "+");
        -: 2830:		   continue;
        -: 2831:	  }
        -: 2832:	  char *restrict star = strchr(chunk, '*');
        -: 2833:	  if (star) {
        -: 2834:		   *star = '\0';
        -: 2835:		   char *restrict left = chunk; char *restrict right = star + 1;
        -: 2836:		   char *restrict c_l = strchr(left, '^'); if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
        -: 2837:		   char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
        -: 2838:
        -: 2839:		   terms[num_terms++] = savepv(left);
        -: 2840:		   terms[num_terms++] = savepv(right);
        -: 2841:		   size_t inter_len = strlen(left) + strlen(right) + 2;
        -: 2842:		   terms[num_terms] = (char*)safemalloc(inter_len);
        -: 2843:		   snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
        -: 2844:	  } else {
        -: 2845:		   char *restrict c_chunk = strchr(chunk, '^'); 
        -: 2846:		   if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
        -: 2847:		   terms[num_terms++] = savepv(chunk);
        -: 2848:	  }
        -: 2849:	  chunk = strtok(NULL, "+");
        -: 2850:	}
        -: 2851:
        -: 2852:	for (i = 0; i < num_terms; i++) {
        -: 2853:	  bool found = FALSE;
        -: 2854:	  for (size_t j = 0; j < num_uniq; j++) {
        -: 2855:		   if (strcmp(terms[i], uniq_terms[j]) == 0) { found = TRUE; break; }
        -: 2856:	  }
        -: 2857:	  if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
        -: 2858:	}
        -: 2859:	p = num_uniq;
        -: 2860:
        -: 2861:	// --- Data Extraction ---
        -: 2862:	ref = SvRV(data_sv);
        -: 2863:	if (SvTYPE(ref) == SVt_PVHV) {
        -: 2864:		HV*restrict hv = (HV*)ref;
        -: 2865:		if (hv_iterinit(hv) == 0) croak("glm: Data hash is empty");
        -: 2866:		entry = hv_iternext(hv);
        -: 2867:		if (entry) {
        -: 2868:			SV*restrict val = hv_iterval(hv, entry);
        -: 2869:			if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
        -: 2870:				 data_hoa = hv;
        -: 2871:				 n = av_len((AV*)SvRV(val)) + 1;
        -: 2872:				 Newx(row_names, n, char*);
        -: 2873:				 for(i = 0; i < n; i++) {
        -: 2874:				     char buf[32]; snprintf(buf, sizeof(buf), "%lu", i+1);
        -: 2875:				     row_names[i] = savepv(buf);
        -: 2876:				 }
        -: 2877:			} else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
        -: 2878:				 n = hv_iterinit(hv);
        -: 2879:				 Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
        -: 2880:				 i = 0;
        -: 2881:				 while ((entry = hv_iternext(hv))) {
        -: 2882:				     I32 len;
        -: 2883:				     row_names[i] = savepv(hv_iterkey(entry, &len));
        -: 2884:				     row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
        -: 2885:				     i++;
        -: 2886:				 }
        -: 2887:			} else croak("glm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
        -: 2888:		}
        -: 2889:	} else if (SvTYPE(ref) == SVt_PVAV) {
        -: 2890:	  AV*restrict av = (AV*)ref;
        -: 2891:	  n = av_len(av) + 1;
        -: 2892:	  Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
        -: 2893:	  for (i = 0; i < n; i++) {
        -: 2894:		   SV**restrict val = av_fetch(av, i, 0);
        -: 2895:		   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
        -: 2896:		       row_hashes[i] = (HV*)SvRV(*val);
        -: 2897:		       char buf[32]; snprintf(buf, sizeof(buf), "%lu", i + 1);
        -: 2898:		       row_names[i] = savepv(buf);
        -: 2899:		   } else {
        -: 2900:		       for (size_t k = 0; k < i; k++) Safefree(row_names[k]);
        -: 2901:		       Safefree(row_names); Safefree(row_hashes);
        -: 2902:		       croak("glm: Array values must be HashRefs (AoH)");
        -: 2903:		   }
        -: 2904:	  }
        -: 2905:	} else croak("glm: Data must be an Array or Hash reference");
        -: 2906:
        -: 2907:	// --- Categorical Expansion ---
        -: 2908:	for (size_t j = 0; j < p; j++) {
        -: 2909:	  if (p_exp + 32 >= exp_cap) {
        -: 2910:		   exp_cap *= 2;
        -: 2911:		   Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
        -: 2912:		   Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 2913:	  }
        -: 2914:	  if (strcmp(uniq_terms[j], "Intercept") == 0) {
        -: 2915:		   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = FALSE; p_exp++; continue;
        -: 2916:	  }
        -: 2917:	  if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
        -: 2918:		   char **restrict levels = NULL; size_t num_levels = 0, levels_cap = 8;
        -: 2919:		   Newx(levels, levels_cap, char*);
        -: 2920:		   for (i = 0; i < n; i++) {
        -: 2921:		       char*restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
        -: 2922:		       if (str_val) {
        -: 2923:		           bool found = FALSE;
        -: 2924:		           for (size_t l = 0; l < num_levels; l++) {
        -: 2925:		               if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; }
        -: 2926:		           }
        -: 2927:		           if (!found) {
        -: 2928:		               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
        -: 2929:		               levels[num_levels++] = savepv(str_val);
        -: 2930:		           }
        -: 2931:		           Safefree(str_val);
        -: 2932:		       }
        -: 2933:		   }
        -: 2934:		   if (num_levels > 0) {
        -: 2935:		       for (size_t l1 = 0; l1 < num_levels - 1; l1++) {
        -: 2936:		           for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
        -: 2937:		               if (strcmp(levels[l1], levels[l2]) > 0) {
        -: 2938:		                   char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp;
        -: 2939:		               }
        -: 2940:		           }
        -: 2941:		       }
        -: 2942:		       for (size_t l = 1; l < num_levels; l++) {
        -: 2943:		           if (p_exp >= exp_cap) {
        -: 2944:		               exp_cap *= 2;
        -: 2945:		               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
        -: 2946:		               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 2947:		           }
        -: 2948:		           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
        -: 2949:		           exp_terms[p_exp] = (char*)safemalloc(t_len);
        -: 2950:		           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
        -: 2951:		           is_dummy[p_exp] = TRUE; dummy_base[p_exp] = savepv(uniq_terms[j]); dummy_level[p_exp] = savepv(levels[l]);
        -: 2952:		           p_exp++;
        -: 2953:		       }
        -: 2954:		       for (size_t l = 0; l < num_levels; l++) Safefree(levels[l]);
        -: 2955:		       Safefree(levels);
        -: 2956:		   } else {
        -: 2957:		       Safefree(levels); exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
        -: 2958:		   }
        -: 2959:	  } else {
        -: 2960:		   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
        -: 2961:	  }
        -: 2962:	}
        -: 2963:	p = p_exp;
        -: 2964:
        -: 2965:	Newx(X, n * p, double); Newx(Y, n, double);
        -: 2966:	Newx(valid_row_names, n, char*);
        -: 2967:
        -: 2968:	// --- Listwise Deletion ---
        -: 2969:	for (size_t i = 0; i < n; i++) {
        -: 2970:		double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
        -: 2971:		if (isnan(y_val)) { Safefree(row_names[i]); continue; }
        -: 2972:
        -: 2973:		bool row_ok = TRUE;
        -: 2974:		double *restrict row_x = (double*)safemalloc(p * sizeof(double));
        -: 2975:		for (size_t j = 0; j < p; j++) {
        -: 2976:			if (strcmp(exp_terms[j], "Intercept") == 0) {
        -: 2977:				 row_x[j] = 1.0;
        -: 2978:			} else if (is_dummy[j]) {
        -: 2979:				 char* str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
        -: 2980:				 if (str_val) {
        -: 2981:				     row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
        -: 2982:				     Safefree(str_val);
        -: 2983:				 } else { row_ok = FALSE; break; }
        -: 2984:			} else {
        -: 2985:				 row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
        -: 2986:				 if (isnan(row_x[j])) { row_ok = FALSE; break; }
        -: 2987:			}
        -: 2988:		}
        -: 2989:		if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
        -: 2990:		Y[valid_n] = y_val;
        -: 2991:		for (size_t j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
        -: 2992:		valid_row_names[valid_n] = row_names[i];
        -: 2993:		valid_n++;
        -: 2994:		Safefree(row_x);
        -: 2995:	}
        -: 2996:	Safefree(row_names); 
        -: 2997:	if (valid_n <= p) {
        -: 2998:	  Safefree(X); Safefree(Y); Safefree(valid_row_names); if (row_hashes) Safefree(row_hashes);
        -: 2999:	  croak("glm: 0 degrees of freedom (too many NAs or parameters > observations)");
        -: 3000:	}
        -: 3001:	// --- R glm.fit IRLS Implementation ---
        -: 3002:	mu = (double*)safemalloc(valid_n * sizeof(double)); eta = (double*)safemalloc(valid_n * sizeof(double));
        -: 3003:	W = (double*)safemalloc(valid_n * sizeof(double)); Z = (double*)safemalloc(valid_n * sizeof(double));
        -: 3004:	beta = (double*)safemalloc(p * sizeof(double)); beta_old = (double*)safemalloc(p * sizeof(double));
        -: 3005:	aliased = (bool*)safemalloc(p * sizeof(bool));
        -: 3006:	XtWX = (double*)safemalloc(p * p * sizeof(double)); XtWZ = (double*)safemalloc(p * sizeof(double));
        -: 3007:	for (i = 0; i < p; i++) { beta[i] = 0.0; beta_old[i] = 0.0; }
        -: 3008:	// Initialize (mustart / etastart equivalent)
        -: 3009:	double sum_y = 0.0;
        -: 3010:	for (i = 0; i < valid_n; i++) sum_y += Y[i];
        -: 3011:	double mean_y = sum_y / valid_n;
        -: 3012:	for (i = 0; i < valid_n; i++) {
        -: 3013:	  if (is_binomial) {
        -: 3014:		   if (Y[i] < 0.0 || Y[i] > 1.0) croak("glm: binomial family requires response between 0 and 1");
        -: 3015:		   mu[i] = (Y[i] + 0.5) / 2.0; 
        -: 3016:		   eta[i] = log(mu[i] / (1.0 - mu[i]));
        -: 3017:		   double dev = 0.0;
        -: 3018:		   if (Y[i] == 0.0)      dev = -2.0 * log(1.0 - mu[i]);
        -: 3019:		   else if (Y[i] == 1.0) dev = -2.0 * log(mu[i]);
        -: 3020:		   else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
        -: 3021:		   deviance_old += dev;
        -: 3022:	  } else { 
        -: 3023:		   mu[i] = mean_y; // R gaussian init
        -: 3024:		   eta[i] = mu[i]; 
        -: 3025:	  }
        -: 3026:	}
        -: 3027:	// IRLS Loop
        -: 3028:	for (iter = 1; iter <= max_iter; iter++) {
        -: 3029:		for (i = 0; i < valid_n; i++) {
        -: 3030:			if (is_binomial) {
        -: 3031:				 double varmu = mu[i] * (1.0 - mu[i]);
        -: 3032:				 double mu_eta = varmu; // Link derivative for logit
        -: 3033:				 if (varmu < 1e-10) varmu = 1e-10;
        -: 3034:				 Z[i] = eta[i] + (Y[i] - mu[i]) / mu_eta;
        -: 3035:				 W[i] = (mu_eta * mu_eta) / varmu; 
        -: 3036:			} else { 
        -: 3037:				 W[i] = 1.0; 
        -: 3038:				 Z[i] = Y[i]; 
        -: 3039:			}
        -: 3040:		}
        -: 3041:		// Formulate XtWX and XtWZ
        -: 3042:		for (i = 0; i < p; i++) { XtWZ[i] = 0.0; for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
        -: 3043:		for (size_t k = 0; k < valid_n; k++) {
        -: 3044:			double w = W[k], z = Z[k];
        -: 3045:			for (i = 0; i < p; i++) {
        -: 3046:				 XtWZ[i] += X[k * p + i] * w * z;
        -: 3047:				 double xw = X[k * p + i] * w;
        -: 3048:				 for (size_t j = 0; j < p; j++) XtWX[i * p + j] += xw * X[k * p + j];
        -: 3049:			}
        -: 3050:		}
        -: 3051:		final_rank = sweep_matrix_ols(XtWX, p, aliased);
        -: 3052:		for (i = 0; i < p; i++) {
        -: 3053:			if (aliased[i]) { beta[i] = NAN; } else {
        -: 3054:				 double sum = 0.0;
        -: 3055:				 for (size_t j = 0; j < p; j++) if (!aliased[j]) sum += XtWX[i * p + j] * XtWZ[j];
        -: 3056:				 beta[i] = sum;
        -: 3057:			}
        -: 3058:		}
        -: 3059:		// Calculate updated ETA, MU, and Deviance (with Step-Halving)
        -: 3060:		boundary = FALSE;
        -: 3061:		for (unsigned short int half = 0; half < 10; half++) {
        -: 3062:			deviance_new = 0.0;
        -: 3063:			for (i = 0; i < valid_n; i++) {
        -: 3064:				 double linear_pred = 0.0;
        -: 3065:				 for (size_t j = 0; j < p; j++) if (!aliased[j]) linear_pred += X[i * p + j] * beta[j];
        -: 3066:				 eta[i] = linear_pred;
        -: 3067:				 if (is_binomial) {
        -: 3068:				     mu[i] = 1.0 / (1.0 + exp(-eta[i]));
        -: 3069:				     // Boundary enforcement
        -: 3070:				     if (mu[i] < 10 * DBL_EPSILON) mu[i] = 10 * DBL_EPSILON;
        -: 3071:				     if (mu[i] > 1.0 - 10 * DBL_EPSILON) mu[i] = 1.0 - 10 * DBL_EPSILON;
        -: 3072:
        -: 3073:				     double dev = 0.0;
        -: 3074:				     if (Y[i] == 0.0)      dev = -2.0 * log(1.0 - mu[i]);
        -: 3075:				     else if (Y[i] == 1.0) dev = -2.0 * log(mu[i]);
        -: 3076:				     else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
        -: 3077:				     deviance_new += dev;
        -: 3078:				 } else {
        -: 3079:				     mu[i] = eta[i];
        -: 3080:				     double res = Y[i] - mu[i];
        -: 3081:				     deviance_new += res * res;
        -: 3082:				 }
        -: 3083:			}
        -: 3084:			// Step halving divergence check
        -: 3085:			if (!is_binomial || deviance_new <= deviance_old + 1e-7 || !isfinite(deviance_new)) {
        -: 3086:				 continue; 
        -: 3087:			}
        -: 3088:
        -: 3089:			boundary = TRUE;
        -: 3090:			for (size_t j = 0; j < p; j++) beta[j] = (beta[j] + beta_old[j]) / 2.0;
        -: 3091:		}
        -: 3092:		// Convergence Check
        -: 3093:		if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) { 
        -: 3094:			converged = TRUE; break; 
        -: 3095:		}
        -: 3096:		deviance_old = deviance_new;
        -: 3097:		for (size_t j = 0; j < p; j++) beta_old[j] = beta[j];
        -: 3098:	}
        -: 3099:	// Final accurate calculation of W for standard errors
        -: 3100:	for (i = 0; i < p; i++) { for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
        -: 3101:	for (size_t k = 0; k < valid_n; k++) {
        -: 3102:	  double w = is_binomial ? (mu[k] * (1.0 - mu[k])) : 1.0;
        -: 3103:	  if (w < 1e-10) w = 1e-10;
        -: 3104:	  for (i = 0; i < p; i++) {
        -: 3105:		   double xw = X[k * p + i] * w;
        -: 3106:		   for (size_t j = 0; j < p; j++) XtWX[i * p + j] += xw * X[k * p + j];
        -: 3107:	  }
        -: 3108:	}
        -: 3109:	final_rank = sweep_matrix_ols(XtWX, p, aliased);
        -: 3110:	// --- Null Deviance Calculation ---
        -: 3111:	double wtdmu = mean_y; // Since weights are 1.0 initially
        -: 3112:	for (i = 0; i < valid_n; i++) {
        -: 3113:	  if (is_binomial) {
        -: 3114:		   if (Y[i] == 0.0)      null_dev += -2.0 * log(1.0 - wtdmu);
        -: 3115:		   else if (Y[i] == 1.0) null_dev += -2.0 * log(wtdmu);
        -: 3116:		   else null_dev += 2.0 * (Y[i] * log(Y[i] / wtdmu) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - wtdmu)));
        -: 3117:	  } else {
        -: 3118:		   double diff = Y[i] - wtdmu;
        -: 3119:		   null_dev += diff * diff;
        -: 3120:	  }
        -: 3121:	}
        -: 3122:	// --- AIC Calculation ---
        -: 3123:	if (is_gaussian) {
        -: 3124:	  double n_f = (double)valid_n;
        -: 3125:	  aic = n_f * (log(2.0 * M_PI) + 1.0 + log(deviance_new / n_f)) + 2.0 * (final_rank + 1.0);
        -: 3126:	} else if (is_binomial) { 
        -: 3127:	  aic = deviance_new + 2.0 * final_rank; 
        -: 3128:	}
        -: 3129:	// --- Return Structures ---
        -: 3130:	res_hv = newHV(); coef_hv = newHV(); fitted_hv = newHV(); resid_hv = newHV();
        -: 3131:	df_res = valid_n - final_rank;
        -: 3132:	dispersion = is_binomial ? 1.0 : ((df_res > 0) ? (deviance_new / df_res) : NAN);
        -: 3133:	for (size_t i = 0; i < valid_n; i++) {
        -: 3134:		double res = Y[i] - mu[i];
        -: 3135:		if (is_binomial) {
        -: 3136:			// Deviance residuals for binomial
        -: 3137:			double d_res = 0.0;
        -: 3138:			if (Y[i] == 0.0)      d_res = sqrt(-2.0 * log(1.0 - mu[i]));
        -: 3139:			else if (Y[i] == 1.0) d_res = sqrt(-2.0 * log(mu[i]));
        -: 3140:			else d_res = sqrt(2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i]))));
        -: 3141:			res = (Y[i] > mu[i]) ? d_res : -d_res;
        -: 3142:		}
        -: 3143:		hv_store(fitted_hv, valid_row_names[i], strlen(valid_row_names[i]), newSVnv(mu[i]), 0);
        -: 3144:		hv_store(resid_hv,  valid_row_names[i], strlen(valid_row_names[i]), newSVnv(res), 0);
        -: 3145:		Safefree(valid_row_names[i]);
        -: 3146:	}
        -: 3147:	Safefree(valid_row_names);
        -: 3148:
        -: 3149:	summary_hv = newHV(); terms_av = newAV();
        -: 3150:	for (size_t j = 0; j < p; j++) {
        -: 3151:		hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
        -: 3152:		av_push(terms_av, newSVpv(exp_terms[j], 0));
        -: 3153:
        -: 3154:		HV *restrict row_hv = newHV();
        -: 3155:		if (aliased[j]) {
        -: 3156:			hv_store(row_hv, "Estimate",   8, newSVpv("NaN", 0), 0);
        -: 3157:			hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
        -: 3158:			hv_store(row_hv, is_binomial ? "z value" : "t value", 7, newSVpv("NaN", 0), 0);
        -: 3159:			hv_store(row_hv, is_binomial ? "Pr(>|z|)" : "Pr(>|t|)", 8, newSVpv("NaN", 0), 0);
        -: 3160:		} else {
        -: 3161:			double se = sqrt(dispersion * XtWX[j * p + j]);
        -: 3162:			double val_stat = beta[j] / se;
        -: 3163:			double p_val = is_binomial ? 2.0 * (1.0 - approx_pnorm(fabs(val_stat))) : get_t_pvalue(val_stat, df_res, "two.sided");
        -: 3164:
        -: 3165:			hv_store(row_hv, "Estimate",   8, newSVnv(beta[j]), 0);
        -: 3166:			hv_store(row_hv, "Std. Error", 10, newSVnv(se), 0);
        -: 3167:			hv_store(row_hv, is_binomial ? "z value" : "t value", 7, newSVnv(val_stat), 0);
        -: 3168:			hv_store(row_hv, is_binomial ? "Pr(>|z|)" : "Pr(>|t|)", 8, newSVnv(p_val), 0);
        7: 3169:		}
call    0 returned 7
        7: 3170:		hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
        -: 3171:	}
        7: 3172:
        -: 3173:	hv_store(res_hv, "aic",            3, newSVnv(aic), 0);
        -: 3174:	hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv), 0);
        -: 3175:	hv_store(res_hv, "converged",      9, newSVuv(converged ? 1 : 0), 0);
        -: 3176:	hv_store(res_hv, "boundary",       8, newSVuv(boundary ? 1 : 0), 0);
function XS_Stats__LikeR_cor_test called 7 returned 100% blocks executed 76%
        7: 3177:	hv_store(res_hv, "deviance",       8, newSVnv(deviance_new), 0);
        -: 3178:	hv_store(res_hv, "deviance.resid", 14, newRV_noinc((SV*)resid_hv), 0);
        7: 3179:	hv_store(res_hv, "df.null",        7, newSVuv(valid_n - has_intercept), 0);
        7: 3179-block  0
call    0 returned 7
branch  1 taken 7 (fallthrough)
branch  2 taken 0
        -: 3180:	hv_store(res_hv, "df.residual",   11, newSVuv(df_res), 0);
        -: 3181:	hv_store(res_hv, "family",         6, newSVpv(family_str, 0), 0);
        -: 3182:	hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
        -: 3183:	hv_store(res_hv, "iter",           4, newSVuv(iter > max_iter ? max_iter : iter), 0);
        -: 3184:	hv_store(res_hv, "null.deviance", 13, newSVnv(null_dev), 0);
        -: 3185:	hv_store(res_hv, "rank",           4, newSVuv(final_rank), 0);
        -: 3186:	hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv), 0);
        -: 3187:	hv_store(res_hv, "terms",          5, newRV_noinc((SV*)terms_av), 0);
        -: 3188:
        -: 3189:	// --- Cleanup ---
        -: 3190:	for (i = 0; i < num_terms; i++) Safefree(terms[i]);
        -: 3191:	Safefree(terms);
        -: 3192:	for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]);
        -: 3193:	Safefree(uniq_terms);
        -: 3194:	for (size_t j = 0; j < p_exp; j++) {
        -: 3195:		Safefree(exp_terms[j]);
        -: 3196:		if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
        -: 3197:	}
        -: 3198:	Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
        -: 3199:
        -: 3200:	Safefree(mu); Safefree(eta); Safefree(Z); Safefree(W);
        -: 3201:	Safefree(beta); Safefree(beta_old); Safefree(aliased);
        -: 3202:	Safefree(XtWX); Safefree(XtWZ); Safefree(X); Safefree(Y);
        -: 3203:	if (row_hashes) Safefree(row_hashes);
        -: 3204:
        -: 3205:	RETVAL = newRV_noinc((SV*)res_hv);
        -: 3206:}
        -: 3207:#line 3208 "LikeR.c"
        -: 3208:	RETVAL = sv_2mortal(RETVAL);
        -: 3209:	ST(0) = RETVAL;
        -: 3210:    }
        -: 3211:    XSRETURN(1);
        -: 3212:}
        -: 3213:
        -: 3214:
        -: 3215:XS_EUPXS(XS_Stats__LikeR_cor_test); /* prototype to pass -Wmissing-prototypes */
        -: 3216:XS_EUPXS(XS_Stats__LikeR_cor_test)
        -: 3217:{
        -: 3218:    dVAR; dXSARGS;
        -: 3219:    PERL_UNUSED_VAR(cv); /* -W */
        -: 3220:    PERL_UNUSED_VAR(items); /* -W */
        -: 3221:    {
        -: 3222:	SV *	RETVAL;
        -: 3223:#line 2946 "LikeR.xs"
        -: 3224:{
        -: 3225:	if (items < 2 || items % 2 != 0)
        -: 3226:		croak("Usage: cor_test(\\@x, \\@y, method => 'pearson', ...)");
        -: 3227:
        -: 3228:	SV *restrict x_ref = ST(0), *restrict y_ref = ST(1);
        -: 3229:
        -: 3230:	const char *restrict alternative = "two.sided";
        -: 3231:	const char *restrict method = "pearson";
        -: 3232:	SV *restrict exact_sv = NULL;
        -: 3233:	double conf_level = 0.95;
        -: 3234:	bool continuity = 0;
        -: 3235:
        -: 3236:	/* Parse named arguments from the flat stack starting at index 2 */
        -: 3237:	for (unsigned short int i = 2; i < items; i += 2) {
        -: 3238:	  const char *restrict key = SvPV_nolen(ST(i));
        -: 3239:	  SV *restrict val = ST(i + 1);
        -: 3240:
        -: 3241:	  if      (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 3242:	  else if (strEQ(key, "method"))      method = SvPV_nolen(val);
        -: 3243:	  else if (strEQ(key, "exact"))       exact_sv = val;
        -: 3244:	  else if (strEQ(key, "conf.level") || strEQ(key, "conf_level")) conf_level = SvNV(val);
        -: 3245:	  else if (strEQ(key, "continuity"))  continuity = SvTRUE(val);
        -: 3246:	  else croak("cor_test: unknown argument '%s'", key);
        -: 3247:	}
        -: 3248:
        -: 3249:	AV *restrict x_av, *restrict y_av;
        -: 3250:	double *restrict x, *restrict y;
        -: 3251:	double estimate = 0, p_value = 0, statistic = 0, df = 0, ci_lower = 0, ci_upper = 0;
        -: 3252:
        -: 3253:	bool is_pearson  = (strcmp(method, "pearson") == 0);
        -: 3254:	bool is_kendall  = (strcmp(method, "kendall") == 0);
        -: 3255:	bool is_spearman = (strcmp(method, "spearman") == 0);
        -: 3256:	HV *restrict rhv;
        -: 3257:
        -: 3258:	if (!SvOK(x_ref) || !SvROK(x_ref) || SvTYPE(SvRV(x_ref)) != SVt_PVAV ||
        -: 3259:	    !SvOK(y_ref) || !SvROK(y_ref) || SvTYPE(SvRV(y_ref)) != SVt_PVAV) {
        -: 3260:	  croak("cor_test: x and y must be array references");
        -: 3261:	}
        -: 3262:
        -: 3263:	x_av = (AV*)SvRV(x_ref);
        -: 3264:	y_av = (AV*)SvRV(y_ref);
        -: 3265:
        -: 3266:	size_t n_raw = av_len(x_av) + 1;
        -: 3267:	if (n_raw != av_len(y_av) + 1) croak("incompatible dimensions");
        -: 3268:
        -: 3269:	x = safemalloc(n_raw * sizeof(double));
        -: 3270:	y = safemalloc(n_raw * sizeof(double));
        -: 3271:
        -: 3272:	size_t n = 0; /* Final count of pairwise complete observations */
        -: 3273:	for (size_t i = 0; i < n_raw; i++) {
        -: 3274:	  SV **restrict x_val = av_fetch(x_av, i, 0);
        -: 3275:	  SV **restrict y_val = av_fetch(y_av, i, 0);
        -: 3276:
        -: 3277:	  double xv = (x_val && SvOK(*x_val) && looks_like_number(*x_val)) ? SvNV(*x_val) : NAN;
        -: 3278:	  double yv = (y_val && SvOK(*y_val) && looks_like_number(*y_val)) ? SvNV(*y_val) : NAN;
        -: 3279:
        -: 3280:	  /* Pairwise complete observations (skips NAs seamlessly like R) */
        -: 3281:	  if (!isnan(xv) && !isnan(yv)) {
        -: 3282:	      x[n] = xv;
        -: 3283:	      y[n] = yv;
        -: 3284:	      n++;
        -: 3285:	  }
        -: 3286:	}
        -: 3287:
        -: 3288:	if (n < 3) {
        -: 3289:	  Safefree(x); 
        -: 3290:	  Safefree(y);
        -: 3291:	  croak("not enough finite observations");
        -: 3292:	}
        -: 3293:
        -: 3294:	if (is_pearson) {
        -: 3295:	  // Welford's Method for Pearson Correlation
        -: 3296:	  double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
        -: 3297:	  for (size_t i = 0; i < n; i++) {
        -: 3298:		   double dx = x[i] - mean_x;
        -: 3299:		   mean_x += dx / (i + 1);
        -: 3300:		   double dy = y[i] - mean_y;
        -: 3301:		   mean_y += dy / (i + 1);
        -: 3302:		   M2_x += dx * (x[i] - mean_x);
        -: 3303:		   M2_y += dy * (y[i] - mean_y);
        -: 3304:		   cov  += dx * (y[i] - mean_y);
        -: 3305:	  }
        -: 3306:	  estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
        -: 3307:	  df = n - 2;
        -: 3308:	  statistic = estimate * sqrt(df / (1.0 - estimate * estimate));
        -: 3309:
        -: 3310:	  // Confidence interval using Fisher's Z transform
        -: 3311:	  double z = 0.5 * log((1.0 + estimate) / (1.0 - estimate));
        -: 3312:	  double se = 1.0 / sqrt(n - 3);
        -: 3313:	  double alpha = 1.0 - conf_level;
        -: 3314:	  double q = inverse_normal_cdf(1.0 - alpha/2.0);
        -: 3315:	  ci_lower = tanh(z - q * se);
        -: 3316:	  ci_upper = tanh(z + q * se);
        -: 3317:
        -: 3318:	  // HIGH-PRECISION P-VALUE USING INCOMPLETE BETA
        -: 3319:	  p_value = get_t_pvalue(statistic, df, alternative);
        -: 3320:	} else if (is_kendall) {
        -: 3321:	  int c = 0, d = 0, tie_x = 0, tie_y = 0;
        -: 3322:	  for (size_t i = 0; i < n - 1; i++) {
        -: 3323:		   for (size_t j = i + 1; j < n; j++) {
        -: 3324:		       double sign_x = (x[i] - x[j] > 0) - (x[i] - x[j] < 0);
        -: 3325:		       double sign_y = (y[i] - y[j] > 0) - (y[i] - y[j] < 0);
        -: 3326:
        -: 3327:		       if (sign_x == 0 && sign_y == 0) { /* Joint tie, ignore */ }
        -: 3328:		       else if (sign_x == 0) tie_x++;
        -: 3329:		       else if (sign_y == 0) tie_y++;
        -: 3330:		       else if (sign_x * sign_y > 0) c++;
        -: 3331:		       else d++;
        -: 3332:		   }
        -: 3333:	  }
        -: 3334:	  double denom = sqrt((double)(c + d + tie_x) * (double)(c + d + tie_y));
        -: 3335:	  estimate = (denom == 0.0) ? (0.0/0.0) : (double)(c - d) / denom;
        -: 3336:
        -: 3337:	  bool has_ties = (tie_x > 0 || tie_y > 0);
        -: 3338:	  bool do_exact;
        -: 3339:
        -: 3340:	  /* Mirror R: exact defaults to TRUE if N < 50 and NO ties */
        -: 3341:	  if (!exact_sv || !SvOK(exact_sv)) {
        -: 3342:		   do_exact = (n < 50) && !has_ties;
        -: 3343:	  } else {
        -: 3344:		   do_exact = SvTRUE(exact_sv) ? 1 : 0;
        -: 3345:	  }
        -: 3346:	  // If forced exact but ties exist, R overrides and falls back to approximation anyway
        -: 3347:	  if (do_exact && has_ties) do_exact = 0;
        -: 3348:
        -: 3349:	  if (do_exact) {
        -: 3350:		   double S_stat = c - d;
        -: 3351:		   statistic = c;
        -: 3352:		   p_value = kendall_exact_pvalue(n, S_stat, alternative);
        -: 3353:	  } else {
        -: 3354:		   // Normal approximation for large N or ties
        -: 3355:		   double var_S = n * (n - 1) * (2.0 * n + 5.0) / 18.0;
        -: 3356:		   double S = c - d;
        -: 3357:		   if (continuity) S -= (S > 0 ? 1 : -1);
        -: 3358:		   statistic = S / sqrt(var_S);
        -: 3359:
        -: 3360:		   if (strcmp(alternative, "two.sided") == 0) {
        -: 3361:		       p_value = 2.0 * (1.0 - approx_pnorm(fabs(statistic)));
        -: 3362:		   } else if (strcmp(alternative, "less") == 0) {
        -: 3363:		       p_value = approx_pnorm(statistic);
        -: 3364:		   } else {
        -: 3365:		       p_value = 1.0 - approx_pnorm(statistic);
        -: 3366:		   }
        -: 3367:	  }
        -: 3368:	} else if (is_spearman) {
        -: 3369:	  double *restrict rank_x = safemalloc(n * sizeof(double));
        -: 3370:	  double *restrict rank_y = safemalloc(n * sizeof(double));
        -: 3371:	  compute_ranks(x, rank_x, n);
        -: 3372:	  compute_ranks(y, rank_y, n);
        -: 3373:
        -: 3374:	  // Spearman rho = Pearson r of the ranks (Welford's Method)
        -: 3375:	  double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
        -: 3376:	  for (size_t i = 0; i < n; i++) {
        -: 3377:		   double dx = rank_x[i] - mean_x;
        -: 3378:		   mean_x += dx / (i + 1);
        -: 3379:		   double dy = rank_y[i] - mean_y;
        -: 3380:		   mean_y += dy / (i + 1);
        -: 3381:		   M2_x += dx * (rank_x[i] - mean_x);
        -: 3382:		   M2_y += dy * (rank_y[i] - mean_y);
        -: 3383:		   cov  += dx * (rank_y[i] - mean_y);
        -: 3384:	  }
        -: 3385:	  estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
        -: 3386:
        -: 3387:	  // S = sum of squared rank differences (R's reported statistic)
        -: 3388:	  double S_stat = 0.0;
        -: 3389:	  for (size_t i = 0; i < n; i++) {
        -: 3390:		   double diff = rank_x[i] - rank_y[i];
        -: 3391:		   S_stat += diff * diff;
        -: 3392:	  }
        -: 3393:
        -: 3394:	  // Ties produce fractional (averaged) ranks — detect them
        -: 3395:	  bool has_ties = 0, do_exact;
        -: 3396:	  for (size_t i = 0; i < n; i++) {
        -: 3397:		   if (rank_x[i] != floor(rank_x[i]) || rank_y[i] != floor(rank_y[i])) {
        -: 3398:		       has_ties = 1;
        -: 3399:		       break;
        -: 3400:		   }
        -: 3401:	  }
        7: 3402:	  if (!exact_sv || !SvOK(exact_sv)) {
call    0 returned 7
        7: 3403:		   do_exact = (n < 10) && !has_ties;
        -: 3404:	  } else {
        7: 3405:		   do_exact = SvTRUE(exact_sv) ? 1 : 0;
        -: 3406:	  }
        -: 3407:
        -: 3408:	  if (do_exact) {
        -: 3409:		   statistic = S_stat;
function XS_Stats__LikeR_shapiro_test called 2 returned 100% blocks executed 81%
        2: 3410:		   p_value   = spearman_exact_pvalue(S_stat, n, alternative);
        -: 3411:	  } else {
        2: 3412:		   double r = estimate;
        2: 3412-block  0
call    0 returned 2
        2: 3413:		   if (continuity)
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 3414:		       r *= (1.0 - 1.0 / (2.0 * (n - 1)));
    %%%%%: 3414-block  0
call    0 never executed
        -: 3415:		   statistic = r * sqrt((n - 2.0) / (1.0 - r * r));
        2: 3416:		   p_value = get_t_pvalue(statistic, (double)(n - 2), alternative);
        -: 3417:	  }
        2: 3418:	  Safefree(rank_x); Safefree(rank_y);
        2: 3418-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        -: 3419:	} else {
        -: 3420:	  Safefree(x); Safefree(y);
        -: 3421:	  croak("Unknown method");
        -: 3422:	}
        -: 3423:	Safefree(x); Safefree(y);
        -: 3424:	rhv = newHV();
        -: 3425:	hv_stores(rhv, "estimate", newSVnv(estimate));
        -: 3426:	hv_stores(rhv, "p.value", newSVnv(p_value));
        -: 3427:	hv_stores(rhv, "statistic", newSVnv(statistic));
        -: 3428:	hv_stores(rhv, "method", newSVpv(method, 0));
        -: 3429:	hv_stores(rhv, "alternative", newSVpv(alternative, 0));
        -: 3430:	if (is_pearson) {
        -: 3431:	  hv_stores(rhv, "parameter", newSVnv(df));
        -: 3432:	  AV *restrict ci_av = newAV();
        -: 3433:	  av_push(ci_av, newSVnv(ci_lower));
        -: 3434:	  av_push(ci_av, newSVnv(ci_upper));
        -: 3435:	  hv_stores(rhv, "conf.int", newRV_noinc((SV*)ci_av));
        -: 3436:	}
        -: 3437:
        -: 3438:	RETVAL = newRV_noinc((SV*)rhv);
        -: 3439:}
        -: 3440:#line 3441 "LikeR.c"
        -: 3441:	RETVAL = sv_2mortal(RETVAL);
        -: 3442:	ST(0) = RETVAL;
        -: 3443:    }
        -: 3444:    XSRETURN(1);
        -: 3445:}
        -: 3446:
        -: 3447:
        -: 3448:XS_EUPXS(XS_Stats__LikeR_shapiro_test); /* prototype to pass -Wmissing-prototypes */
        -: 3449:XS_EUPXS(XS_Stats__LikeR_shapiro_test)
        -: 3450:{
        -: 3451:    dVAR; dXSARGS;
        -: 3452:    if (items != 1)
        -: 3453:       croak_xs_usage(cv,  "data");
        -: 3454:    PERL_UNUSED_VAR(ax); /* -Wall */
        -: 3455:    SP -= items;
        -: 3456:    {
        -: 3457:	SV *	data = ST(0)
        -: 3458:;
        -: 3459:#line 3168 "LikeR.xs"
        -: 3460:	AV *restrict av;
        -: 3461:	HV *restrict ret_hash;
        -: 3462:	size_t n_raw, n = 0;
        -: 3463:	double *restrict x, w = 0.0, p_val = 0.0, mean = 0.0, ssq = 0.0;
        -: 3464:#line 3465 "LikeR.c"
        -: 3465:#line 3173 "LikeR.xs"
        -: 3466:	if (!SvROK(data) || SvTYPE(SvRV(data)) != SVt_PVAV) {
        -: 3467:	  croak("Expected an array reference");
        -: 3468:	}
        -: 3469:
        -: 3470:	av = (AV *)SvRV(data);
        -: 3471:	n_raw = av_len(av) + 1;
        -: 3472:
        -: 3473:	Newx(x, n_raw, double);
        -: 3474:
        -: 3475:	// Extract variables and calculate mean (skipping undefined/NaN values)
        -: 3476:	for (size_t i = 0; i < n_raw; i++) {
        -: 3477:	  SV **restrict elem = av_fetch(av, i, 0);
        -: 3478:	  if (elem && SvOK(*elem)) {
        -: 3479:		   double val = SvNV(*elem);
        -: 3480:		   if (!isnan(val)) {
        -: 3481:		       x[n] = val;
        -: 3482:		       mean += val;
        -: 3483:		       n++;
        -: 3484:		   }
        -: 3485:	  }
        -: 3486:	}
        -: 3487:
        -: 3488:	if (n < 3 || n > 5000) {
        -: 3489:	  Safefree(x);
        -: 3490:	  croak("Sample size must be between 3 and 5000 (R's limit)");
        -: 3491:	}
        -: 3492:
        -: 3493:	mean /= n;
        -: 3494:	// Calculate Sum of Squares */
        -: 3495:	for (size_t i = 0; i < n; i++) {
        -: 3496:	  ssq += (x[i] - mean) * (x[i] - mean);
        -: 3497:	}
        -: 3498:	if (ssq == 0.0) {
        -: 3499:	  Safefree(x);
        -: 3500:	  croak("Data is perfectly constant; cannot compute Shapiro-Wilk test");
        -: 3501:	}
        -: 3502:	qsort(x, n, sizeof(double), compare_doubles);
        -: 3503:
        -: 3504:	// --- Core AS R94 Algorithm: Weights and Statistic W ---
        -: 3505:	if (n == 3) {
        -: 3506:	  double a_val = 0.7071067811865475; /* sqrt(1/2) */
        -: 3507:	  double b_val = a_val * (x[2] - x[0]);
        -: 3508:	  w = (b_val * b_val) / ssq;
        -: 3509:	  if (w < 0.75) w = 0.75; 
        -: 3510:	  // Exact P-value for n=3
        -: 3511:	  p_val = 1.90985931710274 * (asin(sqrt(w)) - 1.04719755119660);
        -: 3512:	} else {
        -: 3513:	  double *restrict m, *restrict a;
        -: 3514:	  double sum_m2 = 0.0, b_val = 0.0;
        -: 3515:	  Newx(m, n, double);
        -: 3516:	  Newx(a, n, double);
        -: 3517:	  for (size_t i = 0; i < n; i++) {
        -: 3518:		   m[i] = inverse_normal_cdf((i + 1.0 - 0.375) / (n + 0.25));
        -: 3519:		   sum_m2 += m[i] * m[i];
        -: 3520:	  }
        -: 3521:	  double u = 1.0 / sqrt((double)n);
        -: 3522:	  double a_n = -2.706056*pow(u,5) + 4.434685*pow(u,4) - 2.071190*pow(u,3) - 0.147981*pow(u,2) + 0.221157*u + m[n-1]/sqrt(sum_m2);
        -: 3523:	  a[n-1] = a_n;
        -: 3524:	  a[0]   = -a_n;
        -: 3525:	  if (n == 4 || n == 5) {
        -: 3526:		   double eps = (sum_m2 - 2.0 * m[n-1]*m[n-1]) / (1.0 - 2.0 * a_n*a_n);
        -: 3527:		   for (unsigned int i = 1; i < n-1; i++) {
        -: 3528:		       a[i] = m[i] / sqrt(eps);
        -: 3529:		   }
        -: 3530:	  } else {
        -: 3531:		   double a_n1 = -3.582633*pow(u,5) + 5.682633*pow(u,4) - 1.752461*pow(u,3) - 0.293762*pow(u,2) + 0.042981*u + m[n-2]/sqrt(sum_m2);
        -: 3532:		   a[n-2] = a_n1;
        -: 3533:		   a[1]   = -a_n1;
        -: 3534:		   double eps = (sum_m2 - 2.0 * m[n-1]*m[n-1] - 2.0 * m[n-2]*m[n-2]) / (1.0 - 2.0 * a_n*a_n - 2.0 * a_n1*a_n1);
        -: 3535:		   for (unsigned int i = 2; i < n-2; i++) {
        -: 3536:		       a[i] = m[i] / sqrt(eps);
        -: 3537:		   }
        -: 3538:	  }
        -: 3539:	  for (size_t i = 0; i < n; i++) {
        -: 3540:		   b_val += a[i] * x[i];
        -: 3541:	  }
        -: 3542:	  w = (b_val * b_val) / ssq;
        -: 3543:	// --- AS R94 P-Value Calculation: High Precision Refinement ---
        -: 3544:	  /* NOTE: p_val is declared in PREINIT above;
        -: 3545:		* do NOT shadow it with a local 'double p_val' here or the result will never reach the caller.
        -: 3546:		*/
        -: 3547:	  double y = log(1.0 - w);
        -: 3548:	  double z;
        -: 3549:	  if (n <= 11) {
        -: 3550:		   // Royston's branch for 4 <= n <= 11 (AS R94, small-sample path).
        -: 3551:		   // gamma is the upper bound on y = log(1-W);
        -: 3552:		   // if y reaches gamma the p-value is essentially zero
        2: 3553:		   double nn = (double)n;
        2: 3554:		   double gamma = 0.459 * nn - 2.273;
        -: 3555:		   if (y >= gamma) {
        -: 3556:		       p_val = 1e-19;
        -: 3557:		   } else {
        -: 3558:		       // Horner-form polynomials in n for mu and log(sigma)
        -: 3559:		       double mu     = 0.544  + nn * (-0.39978  + nn * ( 0.025054  - nn * 0.0006714));
function XS_Stats__LikeR_min called 12 returned 75% blocks executed 95%
       12: 3560:		       double sig_val= 1.3822 + nn * (-0.77857  + nn * ( 0.062767  - nn * 0.0020322));
        -: 3561:		       double sigma  = exp(sig_val);
       12: 3562:		       z = (-log(gamma - y) - mu) / sigma;
       12: 3562-block  0
call    0 returned 12
        -: 3563:		       /* Upper-tail probability P(Z > z): small W → large z → small p-value.
        -: 3564:		       */
        -: 3565:		       p_val = 0.5 * erfc(z * M_SQRT1_2);
        -: 3566:		   }
      12*: 3567:	  } else {
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3567-block  0
    %%%%%: 3567-block  1
call    2 never executed
       12: 3567-block  2
        -: 3568:		   // Royston's branch for n >= 12 (AS R94, large-sample path)
        -: 3569:		   double ln_n   = log((double)n);
        -: 3570:		   // Horner-form polynomials in log(n) for mu and log(sigma). */
        -: 3571:		   double mu     = -1.5861 + ln_n * (-0.31082 + ln_n * (-0.083751 + ln_n * 0.0038915));
        -: 3572:		   double sig_val= -0.4803 + ln_n * (-0.082676 + ln_n * 0.0030302);
        -: 3573:		   double sigma  = exp(sig_val);
        -: 3574:		   z = (y - mu) / sigma;
        -: 3575:		   p_val = 0.5 * erfc(z * M_SQRT1_2);
        -: 3576:	  }
        -: 3577:	  // Clamp the p-value
        -: 3578:	  if (p_val > 1.0) p_val = 1.0;
        -: 3579:	  if (p_val < 0.0) p_val = 0.0;
        -: 3580:
        -: 3581:	  Safefree(m); m = NULL;  Safefree(a); a = NULL;
        -: 3582:	}
        -: 3583:	Safefree(x); x = NULL;
        -: 3584:	ret_hash = newHV();
        -: 3585:	hv_stores(ret_hash, "statistic", newSVnv(w));
        -: 3586:	hv_stores(ret_hash, "W",         newSVnv(w));
        -: 3587:	hv_stores(ret_hash, "p_value",   newSVnv(p_val));
        -: 3588:	hv_stores(ret_hash, "p.value",   newSVnv(p_val));
        -: 3589:	EXTEND(SP, 1);
        -: 3590:	PUSHs(sv_2mortal(newRV_noinc((SV *)ret_hash)));
        -: 3591:#line 3592 "LikeR.c"
        -: 3592:	PUTBACK;
        -: 3593:	return;
        -: 3594:    }
        -: 3595:}
        -: 3596:
        -: 3597:
        -: 3598:XS_EUPXS(XS_Stats__LikeR_min); /* prototype to pass -Wmissing-prototypes */
        -: 3599:XS_EUPXS(XS_Stats__LikeR_min)
        -: 3600:{
        -: 3601:    dVAR; dXSARGS;
        -: 3602:    PERL_UNUSED_VAR(cv); /* -W */
        -: 3603:    PERL_UNUSED_VAR(items); /* -W */
        -: 3604:    {
        -: 3605:	double	RETVAL;
       9*: 3606:	dXSTARG;
        9: 3606-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 3606-block  1
        9: 3606-block  2
call    2 returned 9
        9: 3607:#line 3302 "LikeR.xs"
        -: 3608:		double min_val = 0.0;
        9: 3609:		size_t count = 0;
        9: 3609-block  0
        -: 3610:		bool first = TRUE;
        -: 3611:#line 3612 "LikeR.c"
        -: 3612:#line 3306 "LikeR.xs"
        -: 3613:		for (unsigned short int i = 0; i < items; i++) {
function XS_Stats__LikeR_max called 13 returned 77% blocks executed 95%
       13: 3614:			SV* restrict arg = ST(i);
        -: 3615:			if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
       13: 3616:				AV* restrict av = (AV*)SvRV(arg);
       13: 3616-block  0
call    0 returned 13
        -: 3617:				size_t len = av_len(av) + 1;
        -: 3618:				for (size_t j = 0; j < len; j++) {
        -: 3619:				     SV** restrict tv = av_fetch(av, j, 0);
        -: 3620:				     if (tv && SvOK(*tv)) {
      13*: 3621:				         double val = SvNV(*tv);
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 3621-block  0
    %%%%%: 3621-block  1
call    2 never executed
       13: 3621-block  2
        -: 3622:				         if (first || val < min_val) {
        -: 3623:				             min_val = val;
        -: 3624:				             first = FALSE;
        -: 3625:				         }
        -: 3626:				         count++;
        -: 3627:				     } else {
        -: 3628:				         croak("min: undefined value at array ref index %zu (argument %d)", j, (int)i);
        -: 3629:				     }
        -: 3630:				 }
        -: 3631:			} else if (SvOK(arg)) {
        -: 3632:				 double val = SvNV(arg);
        -: 3633:				 if (first || val < min_val) {
        -: 3634:				     min_val = val;
        -: 3635:				     first = FALSE;
        -: 3636:				 }
        -: 3637:				 count++;
        -: 3638:			} else {
        -: 3639:				 croak("min: undefined value at argument index %d", (int)i);
        -: 3640:			}
        -: 3641:		}
        -: 3642:		if (count == 0) croak("min needs >= 1 numeric element");
        -: 3643:		RETVAL = min_val;
        -: 3644:#line 3645 "LikeR.c"
        -: 3645:	TARGn((double)RETVAL, 1);
        -: 3646:	ST(0) = TARG;
        -: 3647:    }
        -: 3648:    XSRETURN(1);
        -: 3649:}
        -: 3650:
        -: 3651:
        -: 3652:XS_EUPXS(XS_Stats__LikeR_max); /* prototype to pass -Wmissing-prototypes */
        -: 3653:XS_EUPXS(XS_Stats__LikeR_max)
        -: 3654:{
        -: 3655:    dVAR; dXSARGS;
        -: 3656:    PERL_UNUSED_VAR(cv); /* -W */
        -: 3657:    PERL_UNUSED_VAR(items); /* -W */
        -: 3658:    {
        -: 3659:	double	RETVAL;
      10*: 3660:	dXSTARG;
       10: 3660-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 10
    %%%%%: 3660-block  1
       10: 3660-block  2
call    2 returned 10
       10: 3661:#line 3343 "LikeR.xs"
        -: 3662:		double max_val = 0.0;
       10: 3663:		size_t count = 0;
       10: 3663-block  0
        -: 3664:		bool first = TRUE;
        -: 3665:#line 3666 "LikeR.c"
        -: 3666:#line 3347 "LikeR.xs"
        -: 3667:		for (size_t i = 0; i < items; i++) {
function XS_Stats__LikeR_runif called 4 returned 100% blocks executed 86%
        4: 3668:		   SV* restrict arg = ST(i);
        -: 3669:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        4: 3670:		       AV* restrict av = (AV*)SvRV(arg);
        4: 3670-block  0
call    0 returned 4
branch  1 taken 0 (fallthrough)
branch  2 taken 4
        -: 3671:		       size_t len = av_len(av) + 1;
        -: 3672:		       for (size_t j = 0; j < len; j++) {
        -: 3673:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 3674:		           if (tv && SvOK(*tv)) {
        -: 3675:		               double val = SvNV(*tv);
        -: 3676:		               if (first || val > max_val) {
        -: 3677:		                   max_val = val;
        -: 3678:		                   first = FALSE;
        -: 3679:		               }
        -: 3680:		               count++;
        -: 3681:		           } else {
        -: 3682:		               croak("max: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 3683:		           }
        -: 3684:		       }
        -: 3685:		   } else if (SvOK(arg)) {
        -: 3686:		       double val = SvNV(arg);
        -: 3687:		       if (first || val > max_val) {
        -: 3688:		           max_val = val;
        -: 3689:		           first = FALSE;
        -: 3690:		       }
        -: 3691:		       count++;
        -: 3692:		   } else {
        -: 3693:		       croak("max: undefined value at argument index %zu", i);
        -: 3694:		   }
        -: 3695:	  }
        -: 3696:	  if (count == 0) croak("max needs >= 1 numeric element");
        -: 3697:	  RETVAL = max_val;
        -: 3698:#line 3699 "LikeR.c"
        -: 3699:	TARGn((double)RETVAL, 1);
        -: 3700:	ST(0) = TARG;
        -: 3701:    }
        -: 3702:    XSRETURN(1);
        -: 3703:}
        -: 3704:
        -: 3705:
        -: 3706:XS_EUPXS(XS_Stats__LikeR_runif); /* prototype to pass -Wmissing-prototypes */
        -: 3707:XS_EUPXS(XS_Stats__LikeR_runif)
        -: 3708:{
        -: 3709:    dVAR; dXSARGS;
        -: 3710:    PERL_UNUSED_VAR(cv); /* -W */
        -: 3711:    PERL_UNUSED_VAR(items); /* -W */
        -: 3712:    {
        -: 3713:	SV *	RETVAL;
        -: 3714:#line 3383 "LikeR.xs"
        -: 3715:{
        -: 3716:	size_t n = 0;
        -: 3717:	double min = 0.0, max = 1.0;
        -: 3718:
        -: 3719:	// Flags to track what has been assigned
        -: 3720:	bool n_set = 0, min_set = 0, max_set = 0;
        -: 3721:
        -: 3722:	unsigned int i = 0;
        -: 3723:
        -: 3724:	if (items == 0) {
        -: 3725:	  croak("Usage: runif(n, [min=0], [max=1]) or runif(n => $n, ...)");
        -: 3726:	}
        -: 3727:
        -: 3728:	while (i < items) {
        -: 3729:		// 1. Check if the current argument is a string key for a named parameter
        -: 3730:		if (i + 1 < items && SvPOK(ST(i))) {
        -: 3731:			char *restrict key = SvPV_nolen(ST(i));
        -: 3732:			if (strEQ(key, "n")) {
        -: 3733:				n = (size_t)SvUV(ST(i+1));
        -: 3734:				n_set = 1;
        -: 3735:				i += 2;
        -: 3736:				continue;
        -: 3737:			} else if (strEQ(key, "min")) {
        -: 3738:				min = SvNV(ST(i+1));
        -: 3739:				min_set = 1;
        -: 3740:				i += 2;
        -: 3741:				continue;
        -: 3742:			} else if (strEQ(key, "max")) {
        -: 3743:				max = SvNV(ST(i+1));
        -: 3744:				max_set = 1;
        -: 3745:				i += 2;
        -: 3746:				continue;
        -: 3747:			}
        4: 3748:		}
call    0 returned 4
        4: 3749:
        -: 3750:		// 2. Fallback to positional parsing if it's not a recognized key
        4: 3751:		if (!n_set) {
        -: 3752:			n = (size_t)SvUV(ST(i));
        -: 3753:			n_set = 1;
        -: 3754:		} else if (!min_set) {
        -: 3755:			min = SvNV(ST(i));
function XS_Stats__LikeR_rbinom called 12 returned 58% blocks executed 90%
       12: 3756:			min_set = 1;
        -: 3757:		} else if (!max_set) {
       12: 3758:			max = SvNV(ST(i));
       12: 3758-block  0
call    0 returned 12
branch  1 taken 0 (fallthrough)
branch  2 taken 12
        -: 3759:			max_set = 1;
        -: 3760:		} else {
        -: 3761:			croak("Too many arguments or unrecognized parameter passed to runif()");
        -: 3762:		}
        -: 3763:		i++;
        -: 3764:	}
        -: 3765:	if (!n_set) {
        -: 3766:		croak("runif() requires at least the 'n' parameter");
        -: 3767:	}
        -: 3768:	// Ensure PRNG is seeded
        -: 3769:	AUTO_SEED_PRNG();
        -: 3770:	AV *restrict results = newAV();
        -: 3771:	if (n > 0) {
        -: 3772:		av_extend(results, n - 1);
        -: 3773:	}
        -: 3774:	const double range = max - min;
        -: 3775:	for (size_t j = 0; j < n; j++) {
        -: 3776:		double r;
        -: 3777:		if (max < min) {
        -: 3778:			r = NAN; // R behavior for inverted ranges
        -: 3779:		} else {
        -: 3780:			r = min + range * Drand01();
        -: 3781:		}
        -: 3782:		av_push(results, newSVnv(r));
        -: 3783:	}
        -: 3784:	RETVAL = newRV_noinc((SV*)results);
        -: 3785:}
        -: 3786:#line 3787 "LikeR.c"
        -: 3787:	RETVAL = sv_2mortal(RETVAL);
        -: 3788:	ST(0) = RETVAL;
        -: 3789:    }
        -: 3790:    XSRETURN(1);
        -: 3791:}
        -: 3792:
        -: 3793:
        -: 3794:XS_EUPXS(XS_Stats__LikeR_rbinom); /* prototype to pass -Wmissing-prototypes */
        -: 3795:XS_EUPXS(XS_Stats__LikeR_rbinom)
        -: 3796:{
        -: 3797:    dVAR; dXSARGS;
        -: 3798:    PERL_UNUSED_VAR(cv); /* -W */
        -: 3799:    PERL_UNUSED_VAR(items); /* -W */
        7: 3800:    {
call    0 returned 7
        7: 3801:	SV *	RETVAL;
        -: 3802:#line 3459 "LikeR.xs"
        7: 3803:	{
        -: 3804:	  // Auto-seed the PRNG if the Perl script hasn't done so yet
        -: 3805:	  AUTO_SEED_PRNG();
        -: 3806:	  if (items % 2 != 0)
        -: 3807:		   croak("Usage: rbinom(n => 10, size => 100, prob => 0.5)");
function XS_Stats__LikeR_hist called 9 returned 56% blocks executed 83%
        9: 3808:	  //Parse named arguments
        -: 3809:	  size_t n = 0, size = 0;
        9: 3810:	  double prob = 0.5;
        9: 3810-block  0
call    0 returned 9
        9: 3811:
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    #####: 3812:	  bool size_set = FALSE, prob_set = FALSE;
    %%%%%: 3812-block  0
call    0 never executed
        -: 3813:
        -: 3814:	  for (unsigned short i = 0; i < items; i += 2) {
        9: 3815:		   const char* restrict key = SvPV_nolen(ST(i));
        9: 3815-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 1
        -: 3816:		   SV* restrict val = ST(i + 1);
        -: 3817:
        -: 3818:		   if      (strEQ(key, "n"))      n    = (unsigned int)SvUV(val);
        -: 3819:		   else if (strEQ(key, "size")) { size = (unsigned int)SvUV(val); size_set = TRUE; }
        -: 3820:		   else if (strEQ(key, "prob")) { prob = SvNV(val); prob_set = TRUE; }
        -: 3821:		   else croak("rbinom: unknown argument '%s'", key);
        -: 3822:	  }
        -: 3823:
        -: 3824:	  // R requires size and prob to be explicitly passed in rbinom
        -: 3825:	  if (!size_set || !prob_set) croak("rbinom: 'size' and 'prob' are required arguments");
        -: 3826:	  if (prob < 0.0 || prob > 1.0) croak("rbinom: prob must be between 0 and 1");
        -: 3827:
        -: 3828:	  AV *restrict result_av = newAV();
        -: 3829:	  if (n > 0) {
        -: 3830:		   av_extend(result_av, n - 1);
        -: 3831:		   for (unsigned int i = 0; i < n; i++) {
        -: 3832:		       av_store(result_av, i, newSVuv(generate_binomial(size, prob)));
        -: 3833:		   }
        -: 3834:	  }
        -: 3835:
        -: 3836:	  RETVAL = newRV_noinc((SV*)result_av);
        -: 3837:	}
        -: 3838:#line 3839 "LikeR.c"
        -: 3839:	RETVAL = sv_2mortal(RETVAL);
        -: 3840:	ST(0) = RETVAL;
        -: 3841:    }
        -: 3842:    XSRETURN(1);
        -: 3843:}
        -: 3844:
        -: 3845:
        -: 3846:XS_EUPXS(XS_Stats__LikeR_hist); /* prototype to pass -Wmissing-prototypes */
        -: 3847:XS_EUPXS(XS_Stats__LikeR_hist)
        -: 3848:{
        -: 3849:    dVAR; dXSARGS;
        -: 3850:    if (items < 1)
        -: 3851:       croak_xs_usage(cv,  "x_sv, ...");
        -: 3852:    {
        -: 3853:	SV *	RETVAL;
        -: 3854:	SV*	x_sv = ST(0)
        -: 3855:;
        -: 3856:#line 3500 "LikeR.xs"
        -: 3857:	{
        -: 3858:		// 1. Validate Input
        -: 3859:		if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 3860:			croak("hist: first argument must be an array reference");
        -: 3861:
        -: 3862:		AV*restrict x_av = (AV*)SvRV(x_sv);
        -: 3863:		size_t n_raw = av_len(x_av) + 1;
        -: 3864:		if (n_raw == 0) croak("hist: input array is empty");
        -: 3865:
        -: 3866:		// 2. Extract Data & Find Range
        -: 3867:		double *restrict x;
        -: 3868:		Newx(x, n_raw, double);
        -: 3869:		size_t n = 0;
        -: 3870:		double min_val = DBL_MAX, max_val = -DBL_MAX;
        -: 3871:
        -: 3872:		for (size_t i = 0; i < n_raw; i++) {
        -: 3873:			SV**restrict tv = av_fetch(x_av, i, 0);
        -: 3874:			if (tv && SvOK(*tv)) {
        -: 3875:				 double val = SvNV(*tv);
        -: 3876:				 x[n++] = val;
        -: 3877:				 if (val < min_val) min_val = val;
        -: 3878:				 if (val > max_val) max_val = val;
        -: 3879:			}
        -: 3880:		}
        -: 3881:		if (n == 0) {
        -: 3882:			Safefree(x);
        -: 3883:			croak("hist: input contains no valid numeric data");
        -: 3884:		}
        -: 3885:		// 3. Determine Bin Count (Sturges default or user-provided)
        -: 3886:		size_t n_bins = 0;
        -: 3887:
        -: 3888:		if (items == 2) {
        -: 3889:			// Support pure positional argument: hist($data, 22)
        -: 3890:			n_bins = (size_t)SvIV(ST(1));
        -: 3891:		} else if (items > 2) {
        -: 3892:			/* Support named parameters even if mixed with positional arguments */
        -: 3893:			for (unsigned short i = 1; i < items - 1; i++) {
        -: 3894:				 /* Make sure the SV holds a string before doing string comparison */
        -: 3895:				 if (SvPOK(ST(i)) && strEQ(SvPV_nolen(ST(i)), "breaks")) {
        -: 3896:				     n_bins = (size_t)SvIV(ST(i+1));
        -: 3897:				     break;
        -: 3898:				 }
        -: 3899:			}
        -: 3900:			/* Fallback: if 'breaks' wasn't found but a positional number was given first */
        -: 3901:			if (n_bins == 0 && looks_like_number(ST(1))) {
        -: 3902:				 n_bins = (size_t)SvIV(ST(1));
        -: 3903:			}
        -: 3904:		}
        -: 3905:		if (n_bins == 0) n_bins = calculate_sturges_bins(n);
        -: 3906:		// 4. Allocate Result Arrays
        -: 3907:		double *restrict breaks, *restrict mids, *restrict density;
        -: 3908:		size_t *restrict counts;
        -: 3909:		Newx(breaks,  n_bins + 1, double);
        5: 3910:		Newx(mids,    n_bins,     double);
call    0 returned 5
        5: 3911:		Newx(density, n_bins,     double);
        -: 3912:		Newx(counts,  n_bins,     size_t);
        5: 3913:
        -: 3914:		// Generate simple linear breaks
        -: 3915:		double step = (max_val - min_val) / (double)n_bins;
        -: 3916:		for (size_t i = 0; i <= n_bins; i++) {
        -: 3917:			breaks[i] = min_val + (double)i * step;
function XS_Stats__LikeR_quantile called 4 returned 100% blocks executed 81%
        4: 3918:		}
        -: 3919:
        4: 3920:		// 5. Compute Statistics
        4: 3920-block  0
call    0 returned 4
branch  1 taken 4 (fallthrough)
branch  2 taken 0
        -: 3921:		compute_hist_logic(x, n, breaks, n_bins, counts, mids, density);
        -: 3922:
        -: 3923:		// 6. Build Return HashRef
        -: 3924:		HV*restrict res_hv = newHV();
        -: 3925:		AV*restrict av_breaks  = newAV();
        -: 3926:		AV*restrict av_counts  = newAV();
        -: 3927:		AV*restrict av_mids    = newAV();
        -: 3928:		AV*restrict av_density = newAV();
        -: 3929:		for (size_t i = 0; i <= n_bins; i++) {
        -: 3930:			av_push(av_breaks, newSVnv(breaks[i]));
        -: 3931:			if (i < n_bins) {
        -: 3932:				 av_push(av_counts,  newSViv(counts[i]));
        -: 3933:				 av_push(av_mids,    newSVnv(mids[i]));
        -: 3934:				 av_push(av_density, newSVnv(density[i]));
        -: 3935:			}
        -: 3936:		}
        -: 3937:		hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
        -: 3938:		hv_stores(res_hv, "counts",  newRV_noinc((SV*)av_counts));
        -: 3939:		hv_stores(res_hv, "mids",    newRV_noinc((SV*)av_mids));
        -: 3940:		hv_stores(res_hv, "density", newRV_noinc((SV*)av_density));
        -: 3941:
        -: 3942:		// Clean
        -: 3943:		Safefree(x); Safefree(breaks); Safefree(mids);
        -: 3944:		Safefree(density); Safefree(counts);
        -: 3945:
        -: 3946:		RETVAL = newRV_noinc((SV*)res_hv);
        -: 3947:	}
        -: 3948:#line 3949 "LikeR.c"
        -: 3949:	RETVAL = sv_2mortal(RETVAL);
        -: 3950:	ST(0) = RETVAL;
        -: 3951:    }
        -: 3952:    XSRETURN(1);
        -: 3953:}
        -: 3954:
        -: 3955:
        -: 3956:XS_EUPXS(XS_Stats__LikeR_quantile); /* prototype to pass -Wmissing-prototypes */
        -: 3957:XS_EUPXS(XS_Stats__LikeR_quantile)
        -: 3958:{
        -: 3959:    dVAR; dXSARGS;
        -: 3960:    PERL_UNUSED_VAR(cv); /* -W */
        -: 3961:    PERL_UNUSED_VAR(items); /* -W */
        -: 3962:    {
        -: 3963:	SV *	RETVAL;
        -: 3964:#line 3596 "LikeR.xs"
        -: 3965:	{
        -: 3966:		SV *restrict x_sv = NULL;
        -: 3967:		SV *restrict probs_sv = NULL;
        -: 3968:		int arg_idx = 0;
        -: 3969:
        -: 3970:		/* --- 1. Consume first positional arg as 'x' if it's an array ref --- */
        -: 3971:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 3972:			 x_sv = ST(arg_idx);
        -: 3973:			 arg_idx++;
        -: 3974:		}
        -: 3975:
        -: 3976:		/* --- 2. Remaining args must be key-value pairs --- */
        -: 3977:		if ((items - arg_idx) % 2 != 0)
        -: 3978:			 croak("Usage: quantile(\\@data, probs => \\@probs)  OR  quantile(x => \\@data, probs => \\@probs)");
        -: 3979:
        -: 3980:		for (; arg_idx < items; arg_idx += 2) {
        -: 3981:			 const char *restrict key = SvPV_nolen(ST(arg_idx));
        -: 3982:			 SV *restrict val = ST(arg_idx + 1);
        -: 3983:
        -: 3984:			 if      (strEQ(key, "x"))     x_sv     = val;
        -: 3985:			 else if (strEQ(key, "probs")) probs_sv = val;
        -: 3986:			 else croak("quantile: unknown argument '%s'", key);
        -: 3987:		}
        -: 3988:		if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 3989:			croak("quantile: 'x' must be an array reference");
        -: 3990:		AV *restrict x_av = (AV*)SvRV(x_sv);
        -: 3991:		size_t n_raw = av_len(x_av) + 1;
        -: 3992:		if (n_raw == 0) croak("quantile: 'x' is empty");
        -: 3993:
        -: 3994:		/* --- Extract valid numeric data & drop NAs --- */
        -: 3995:		double *restrict x;
        -: 3996:		Newx(x, n_raw, double);
        -: 3997:		size_t n = 0;
        -: 3998:		for (size_t i = 0; i < n_raw; i++) {
        -: 3999:			SV **restrict tv = av_fetch(x_av, i, 0);
        -: 4000:			if (tv && SvOK(*tv)) {
        -: 4001:				 x[n++] = SvNV(*tv);
        -: 4002:			}
        -: 4003:		}
        -: 4004:		if (n == 0) {
        -: 4005:			Safefree(x);
        -: 4006:			croak("quantile: 'x' contains no valid numbers");
        -: 4007:		}
        -: 4008:		// --- Sort Data for Quantile Math ---
        -: 4009:		qsort(x, n, sizeof(double), compare_doubles);
        -: 4010:		// --- Parse Probabilities (Default matches R's c(0, .25, .5, .75, 1)) ---
        -: 4011:		double default_probs[] = {0.0, 0.25, 0.50, 0.75, 1.0};
        -: 4012:		unsigned int n_probs = 5;
        -: 4013:		double *restrict probs;
        -: 4014:
        -: 4015:		if (probs_sv && SvROK(probs_sv) && SvTYPE(SvRV(probs_sv)) == SVt_PVAV) {
        -: 4016:			AV *restrict p_av = (AV*)SvRV(probs_sv);
        -: 4017:			n_probs = av_len(p_av) + 1;
        -: 4018:			Newx(probs, n_probs, double);
        -: 4019:			for (unsigned int i = 0; i < n_probs; i++) {
        -: 4020:				 SV **tv = av_fetch(p_av, i, 0);
        -: 4021:				 probs[i] = (tv && SvOK(*tv)) ? SvNV(*tv) : 0.0;
        -: 4022:				 if (probs[i] < 0.0 || probs[i] > 1.0) {
        -: 4023:				     Safefree(x); Safefree(probs);
        -: 4024:				     croak("quantile: probabilities must be between 0 and 1");
        -: 4025:				 }
        -: 4026:			}
        -: 4027:		} else {
        -: 4028:			Newx(probs, n_probs, double);
        -: 4029:			for (unsigned int i = 0; i < n_probs; i++) probs[i] = default_probs[i];
        -: 4030:		}
        -: 4031:
        4: 4032:		/* --- Calculate Quantiles (R Type 7 Algorithm) --- */
call    0 returned 4
        4: 4033:		HV *restrict res_hv = newHV();
        -: 4034:
        4: 4035:		for (size_t i = 0; i < n_probs; i++) {
        -: 4036:			double p = probs[i], q = 0.0;
        -: 4037:
        -: 4038:			if (n == 1) {
        -: 4039:				 q = x[0];
function XS_Stats__LikeR_mean called 41 returned 93% blocks executed 97%
       41: 4040:			} else if (p == 1.0) {
        -: 4041:				 q = x[n - 1]; /* Prevent out-of-bounds mapping */
       41: 4042:			} else if (p == 0.0) {
       41: 4042-block  0
call    0 returned 41
        -: 4043:				 q = x[0];
        -: 4044:			} else {
        -: 4045:				 /* Continuous sample quantile interpolation (Type 7) */
        -: 4046:				 double h = (n - 1) * p;
      41*: 4047:				 unsigned int j = (unsigned int)h; /* floor via cast */
branch  0 taken 41 (fallthrough)
branch  1 taken 0
       41: 4047-block  0
    %%%%%: 4047-block  1
call    2 never executed
       41: 4047-block  2
        -: 4048:				 double gamma = h - j;
        -: 4049:				 q = (1.0 - gamma) * x[j] + gamma * x[j + 1];
        -: 4050:			}
        -: 4051:
        -: 4052:			/* Format hash key to exactly match R's naming convention ("25%", "33.3%") */
        -: 4053:			char key[32];
        -: 4054:			double pct = p * 100.0;
        -: 4055:
        -: 4056:			if (pct == (unsigned int)pct) {
        -: 4057:				 snprintf(key, sizeof(key), "%.0f%%", pct);
        -: 4058:			} else {
        -: 4059:				 snprintf(key, sizeof(key), "%.1f%%", pct);
        -: 4060:			}
        -: 4061:
        -: 4062:			hv_store(res_hv, key, strlen(key), newSVnv(q), 0);
        -: 4063:		}
        -: 4064:
        -: 4065:		Safefree(x);
        -: 4066:		Safefree(probs);
        -: 4067:
        -: 4068:		RETVAL = newRV_noinc((SV*)res_hv);
        -: 4069:	}
        -: 4070:#line 4071 "LikeR.c"
        -: 4071:	RETVAL = sv_2mortal(RETVAL);
        -: 4072:	ST(0) = RETVAL;
        -: 4073:    }
        -: 4074:    XSRETURN(1);
        -: 4075:}
        -: 4076:
       38: 4077:
       38: 4077-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 8
       30: 4077-block  1
        8: 4077-block  2
call    2 returned 8
       38: 4078:XS_EUPXS(XS_Stats__LikeR_mean); /* prototype to pass -Wmissing-prototypes */
        -: 4079:XS_EUPXS(XS_Stats__LikeR_mean)
       38: 4080:{
       38: 4080-block  0
        -: 4081:    dVAR; dXSARGS;
        -: 4082:    PERL_UNUSED_VAR(cv); /* -W */
        -: 4083:    PERL_UNUSED_VAR(items); /* -W */
        -: 4084:    {
function XS_Stats__LikeR_sum called 5 returned 60% blocks executed 93%
        5: 4085:	double	RETVAL;
        -: 4086:	dXSTARG;
        5: 4087:#line 3708 "LikeR.xs"
        5: 4087-block  0
call    0 returned 5
        -: 4088:	  double total = 0;
        -: 4089:	  size_t count = 0;
        -: 4090:#line 4091 "LikeR.c"
        -: 4091:#line 3711 "LikeR.xs"
       5*: 4092:		for (size_t i = 0; i < items; i++) {
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4092-block  0
    %%%%%: 4092-block  1
call    2 never executed
        5: 4092-block  2
        -: 4093:			SV* restrict arg = ST(i);
        -: 4094:			if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 4095:				AV* restrict av = (AV*)SvRV(arg);
        -: 4096:				size_t len = av_len(av) + 1;
        -: 4097:				for (size_t j = 0; j < len; j++) {
        -: 4098:				     SV** restrict tv = av_fetch(av, j, 0);
        -: 4099:				     if (tv && SvOK(*tv)) {
        -: 4100:				         total += SvNV(*tv);
        -: 4101:				         count++;
        -: 4102:				     } else {
        -: 4103:				         croak("mean: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 4104:				     }
        -: 4105:				}
        -: 4106:			} else if (SvOK(arg)) {
        -: 4107:				 total += SvNV(arg);
        -: 4108:				 count++;
        -: 4109:			} else {
        -: 4110:				 croak("mean: undefined value at argument index %zu", i);
        -: 4111:			}
        -: 4112:		}
        -: 4113:		if (count == 0) croak("mean needs >= 1 element");
        -: 4114:		RETVAL = total / count;
        -: 4115:#line 4116 "LikeR.c"
        -: 4116:	TARGn((double)RETVAL, 1);
        -: 4117:	ST(0) = TARG;
        -: 4118:    }
        -: 4119:    XSRETURN(1);
        -: 4120:}
        -: 4121:
        3: 4122:
        3: 4122-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 4122-block  1
        2: 4122-block  2
call    2 returned 2
        3: 4123:XS_EUPXS(XS_Stats__LikeR_sum); /* prototype to pass -Wmissing-prototypes */
        -: 4124:XS_EUPXS(XS_Stats__LikeR_sum)
        3: 4125:{
        3: 4125-block  0
        -: 4126:    dVAR; dXSARGS;
        -: 4127:    PERL_UNUSED_VAR(cv); /* -W */
        -: 4128:    PERL_UNUSED_VAR(items); /* -W */
        -: 4129:    {
function XS_Stats__LikeR_sd called 7 returned 57% blocks executed 94%
        7: 4130:	double	RETVAL;
        -: 4131:	dXSTARG;
        7: 4132:#line 3740 "LikeR.xs"
        7: 4132-block  0
call    0 returned 7
        -: 4133:	  double total = 0;
        -: 4134:	  size_t count = 0;
        -: 4135:#line 4136 "LikeR.c"
        -: 4136:#line 3743 "LikeR.xs"
       7*: 4137:	  for (size_t i = 0; i < items; i++) {
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 4137-block  0
    %%%%%: 4137-block  1
call    2 never executed
        7: 4137-block  2
        -: 4138:		   SV* restrict arg = ST(i);
        -: 4139:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 4140:		       AV* restrict av = (AV*)SvRV(arg);
        -: 4141:		       size_t len = av_len(av) + 1;
        -: 4142:		       for (size_t j = 0; j < len; j++) {
        -: 4143:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 4144:		           if (tv && SvOK(*tv)) {
        -: 4145:		               total += SvNV(*tv);
        -: 4146:		               count++;
        -: 4147:		           } else {
        -: 4148:		               croak("sum: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 4149:		           }
        -: 4150:		       }
        -: 4151:		   } else if (SvOK(arg)) {
        -: 4152:		       total += SvNV(arg);
        -: 4153:		       count++;
        -: 4154:		   } else {
        -: 4155:		       croak("sum: undefined value at argument index %zu", i);
        -: 4156:		   }
        -: 4157:	  }
        -: 4158:	  if (count == 0) croak("sum needs >= 1 element");
        -: 4159:	  RETVAL = total;
        -: 4160:#line 4161 "LikeR.c"
        -: 4161:	TARGn((double)RETVAL, 1);
        -: 4162:	ST(0) = TARG;
        -: 4163:    }
        -: 4164:    XSRETURN(1);
        -: 4165:}
        -: 4166:
        -: 4167:
        -: 4168:XS_EUPXS(XS_Stats__LikeR_sd); /* prototype to pass -Wmissing-prototypes */
        -: 4169:XS_EUPXS(XS_Stats__LikeR_sd)
        -: 4170:{
        -: 4171:    dVAR; dXSARGS;
        -: 4172:    PERL_UNUSED_VAR(cv); /* -W */
        -: 4173:    PERL_UNUSED_VAR(items); /* -W */
       4*: 4174:    {
        4: 4174-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 4174-block  1
        4: 4174-block  2
call    2 returned 4
        4: 4175:	double	RETVAL;
        -: 4176:	dXSTARG;
        4: 4177:#line 3772 "LikeR.xs"
        4: 4177-block  0
        -: 4178:        double mean = 0.0, M2 = 0.0;
        -: 4179:        size_t count = 0;
        -: 4180:#line 4181 "LikeR.c"
        -: 4181:#line 3775 "LikeR.xs"
function XS_Stats__LikeR_var called 8 returned 62% blocks executed 97%
        8: 4182:        /* Single Pass Standard Deviation via Welford's Algorithm */
        -: 4183:        for (size_t i = 0; i < items; i++) {
        8: 4184:            SV* restrict arg = ST(i);
        8: 4184-block  0
call    0 returned 8
        -: 4185:            if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 4186:                AV* restrict av = (AV*)SvRV(arg);
        -: 4187:                size_t len = av_len(av) + 1;
        -: 4188:                for (size_t j = 0; j < len; j++) {
       8*: 4189:                    SV** restrict tv = av_fetch(av, j, 0);
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 4189-block  0
    %%%%%: 4189-block  1
call    2 never executed
        8: 4189-block  2
        -: 4190:                    if (tv && SvOK(*tv)) {
        -: 4191:                        count++;
        -: 4192:                        double val = SvNV(*tv);
        -: 4193:                        double delta = val - mean;
        -: 4194:                        mean += delta / count;
        -: 4195:                        M2 += delta * (val - mean);
        -: 4196:                    } else {
        -: 4197:                        croak("sd: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 4198:                    }
        -: 4199:                }
        -: 4200:            } else if (SvOK(arg)) {
        -: 4201:                count++;
        -: 4202:                double val = SvNV(arg);
        -: 4203:                double delta = val - mean;
        -: 4204:                mean += delta / count;
        -: 4205:                M2 += delta * (val - mean);
        -: 4206:            } else {
        -: 4207:                croak("sd: undefined value at argument index %zu", i);
        -: 4208:            }
        -: 4209:        }
        -: 4210:        if (count < 2) croak("sd needs >= 2 elements");
        -: 4211:        RETVAL = sqrt(M2 / (count - 1));
        -: 4212:#line 4213 "LikeR.c"
        -: 4213:	TARGn((double)RETVAL, 1);
        -: 4214:	ST(0) = TARG;
        -: 4215:    }
        -: 4216:    XSRETURN(1);
        -: 4217:}
        -: 4218:
        -: 4219:
        -: 4220:XS_EUPXS(XS_Stats__LikeR_var); /* prototype to pass -Wmissing-prototypes */
        -: 4221:XS_EUPXS(XS_Stats__LikeR_var)
        -: 4222:{
        -: 4223:    dVAR; dXSARGS;
        -: 4224:    PERL_UNUSED_VAR(cv); /* -W */
        -: 4225:    PERL_UNUSED_VAR(items); /* -W */
        5: 4226:    {
        5: 4226-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        1: 4226-block  1
        4: 4226-block  2
call    2 returned 4
        5: 4227:	double	RETVAL;
        -: 4228:	dXSTARG;
        5: 4229:#line 3812 "LikeR.xs"
        5: 4229-block  0
        -: 4230:	  double mean = 0.0, M2 = 0.0;
        -: 4231:	  size_t count = 0;
        -: 4232:#line 4233 "LikeR.c"
        -: 4233:#line 3815 "LikeR.xs"
function XS_Stats__LikeR_t_test called 47 returned 89% blocks executed 91%
       47: 4234:	  /* Single Pass Variance via Welford's Algorithm */
        -: 4235:	  for (size_t i = 0; i < items; i++) {
       47: 4236:		   SV* restrict arg = ST(i);
       47: 4236-block  0
call    0 returned 47
branch  1 taken 47 (fallthrough)
branch  2 taken 0
        -: 4237:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 4238:		       AV* restrict av = (AV*)SvRV(arg);
        -: 4239:		       size_t len = av_len(av) + 1;
        -: 4240:		       for (size_t j = 0; j < len; j++) {
        -: 4241:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 4242:		           if (tv && SvOK(*tv)) {
        -: 4243:		               count++;
        -: 4244:		               double val = SvNV(*tv);
        -: 4245:		               double delta = val - mean;
        -: 4246:		               mean += delta / count;
        -: 4247:		               M2 += delta * (val - mean);
        -: 4248:		           } else {
        -: 4249:		               croak("var: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 4250:		           }
        -: 4251:		       }
        -: 4252:		   } else if (SvOK(arg)) {
        -: 4253:		       count++;
        -: 4254:		       double val = SvNV(arg);
        -: 4255:		       double delta = val - mean;
        -: 4256:		       mean += delta / count;
        -: 4257:		       M2 += delta * (val - mean);
        -: 4258:		   } else {
        -: 4259:		       croak("var: undefined value at argument index %zu", i);
        -: 4260:		   }
        -: 4261:	  }
        -: 4262:	  if (count < 2) croak("var needs >= 2 elements");
        -: 4263:	  RETVAL = M2 / (count - 1);
        -: 4264:#line 4265 "LikeR.c"
        -: 4265:	TARGn((double)RETVAL, 1);
        -: 4266:	ST(0) = TARG;
        -: 4267:    }
        -: 4268:    XSRETURN(1);
        -: 4269:}
        -: 4270:
        -: 4271:
        -: 4272:XS_EUPXS(XS_Stats__LikeR_t_test); /* prototype to pass -Wmissing-prototypes */
        -: 4273:XS_EUPXS(XS_Stats__LikeR_t_test)
        -: 4274:{
        -: 4275:    dVAR; dXSARGS;
        -: 4276:    PERL_UNUSED_VAR(cv); /* -W */
        -: 4277:    PERL_UNUSED_VAR(items); /* -W */
        -: 4278:    {
        -: 4279:	SV *	RETVAL;
        -: 4280:#line 3850 "LikeR.xs"
        -: 4281:	{
        -: 4282:		SV*restrict x_sv = NULL;
        -: 4283:		SV*restrict y_sv = NULL;
        -: 4284:		double mu = 0.0, conf_level = 0.95;
        -: 4285:		bool paired = FALSE, var_equal = FALSE;
        -: 4286:		const char*restrict alternative = "two.sided";
        -: 4287:
        -: 4288:		int arg_idx = 0;
        -: 4289:
        -: 4290:		// 1. Shift first positional argument as 'x' if it's an array reference
        -: 4291:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 4292:		  x_sv = ST(arg_idx);
        -: 4293:		  arg_idx++;
        -: 4294:		}
        -: 4295:
        -: 4296:		// 2. Shift second positional argument as 'y' if it's an array reference
        -: 4297:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 4298:		  y_sv = ST(arg_idx);
        -: 4299:		  arg_idx++;
        -: 4300:		}
        -: 4301:
        -: 4302:		// Ensure the remaining arguments form complete key-value pairs
        -: 4303:		if ((items - arg_idx) % 2 != 0) {
        -: 4304:		  croak("Usage: t_test(\\@x, [\\@y], key => value, ...)");
        -: 4305:		}
        -: 4306:
        -: 4307:		// --- Parse named arguments from the remaining flat stack ---
        -: 4308:		for (; arg_idx < items; arg_idx += 2) {
        -: 4309:			const char*restrict key = SvPV_nolen(ST(arg_idx));
        -: 4310:			SV*restrict val = ST(arg_idx + 1);
        -: 4311:
        -: 4312:			if      (strEQ(key, "x"))           x_sv        = val;
        -: 4313:			else if (strEQ(key, "y"))           y_sv        = val;
        -: 4314:			else if (strEQ(key, "mu"))          mu          = SvNV(val);
        -: 4315:			else if (strEQ(key, "paired"))      paired      = SvTRUE(val);
        -: 4316:			else if (strEQ(key, "var_equal"))   var_equal   = SvTRUE(val);
        -: 4317:			else if (strEQ(key, "conf_level"))  conf_level  = SvNV(val);
        -: 4318:			else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 4319:			else croak("t_test: unknown argument '%s'", key);
        -: 4320:		}
        -: 4321:
        -: 4322:		// --- Validate required / types ---
        -: 4323:		if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 4324:			croak("t_test: 'x' is a required argument and must be an ARRAY reference");
        -: 4325:		AV*restrict x_av = (AV*)SvRV(x_sv);
        -: 4326:		size_t nx = av_len(x_av) + 1;
        -: 4327:		if (nx < 2) croak("t_test: 'x' needs at least 2 elements");
        -: 4328:		AV*restrict y_av = NULL;
        -: 4329:		if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV)
        -: 4330:			y_av = (AV*)SvRV(y_sv);
        -: 4331:
        -: 4332:		if (conf_level <= 0.0 || conf_level >= 1.0)
        -: 4333:			croak("t_test: 'conf_level' must be between 0 and 1");
        -: 4334:		// --- Computation via Welford's Algorithm --- */
        -: 4335:		double mean_x = 0.0, M2_x = 0.0, var_x, t_stat, df, p_val, std_err, cint_est;
        -: 4336:		HV*restrict results = newHV();
        -: 4337:		for (size_t i = 0; i < nx; i++) {
        -: 4338:			SV**restrict tv = av_fetch(x_av, i, 0);
        -: 4339:			double val = (tv && SvOK(*tv)) ? SvNV(*tv) : 0;
        -: 4340:			double delta = val - mean_x;
        -: 4341:			mean_x += delta / (i + 1);
        -: 4342:			M2_x += delta * (val - mean_x);
        -: 4343:		}
        -: 4344:		var_x = M2_x / (nx - 1);
        -: 4345:		if (var_x == 0.0 && !y_av) croak("t_test: data are essentially constant");
        -: 4346:
        -: 4347:		if (paired || y_av) {
        -: 4348:			if (!y_av) croak("t_test: 'y' must be provided for paired or two-sample tests");
        -: 4349:			size_t ny = av_len(y_av) + 1;
        -: 4350:			if (paired && ny != nx) croak("t_test: Paired arrays must be same length");
        -: 4351:			double mean_y = 0.0, M2_y = 0.0, var_y;
        -: 4352:			for (size_t i = 0; i < ny; i++) {
        -: 4353:				 SV**restrict tv = av_fetch(y_av, i, 0);
        -: 4354:				 double val = (tv && SvOK(*tv)) ? SvNV(*tv) : 0;
        -: 4355:				 double delta = val - mean_y;
        -: 4356:				 mean_y += delta / (i + 1);
        -: 4357:				 M2_y += delta * (val - mean_y);
        -: 4358:			}
        -: 4359:			var_y = M2_y / (ny - 1);
        -: 4360:			if (paired) {
        -: 4361:				 double mean_d = 0.0, M2_d = 0.0;
        -: 4362:				 for (size_t i = 0; i < nx; i++) {
        -: 4363:					  SV**restrict dx_ptr = av_fetch(x_av, i, 0);
        -: 4364:					  SV**restrict dy_ptr = av_fetch(y_av, i, 0);
        -: 4365:				     double dx = (dx_ptr && SvOK(*dx_ptr)) ? SvNV(*dx_ptr) : 0.0;
        -: 4366:				     double dy = (dy_ptr && SvOK(*dy_ptr)) ? SvNV(*dy_ptr) : 0.0;
        -: 4367:				     double val = dx - dy;
        -: 4368:				     double delta = val - mean_d;
        -: 4369:				     mean_d += delta / (i + 1);
        -: 4370:				     M2_d += delta * (val - mean_d);
        -: 4371:				 }
        -: 4372:				 double var_d = M2_d / (nx - 1);
        -: 4373:				 if (var_d == 0.0) croak("t_test: data are essentially constant");
        -: 4374:				 cint_est = mean_d;
        -: 4375:				 std_err  = sqrt(var_d / nx);
        -: 4376:				 t_stat   = (cint_est - mu) / std_err;
        -: 4377:				 df       = nx - 1;
        -: 4378:				 hv_store(results, "estimate", 8, newSVnv(mean_d), 0);
        -: 4379:			} else if (var_equal) {
        -: 4380:				 if (var_x == 0.0 && var_y == 0.0) croak("t_test: data are essentially constant");
        -: 4381:				 double pooled_var = ((nx - 1) * var_x + (ny - 1) * var_y) / (nx + ny - 2);
        -: 4382:				 cint_est = mean_x - mean_y;
        -: 4383:				 std_err  = sqrt(pooled_var * (1.0 / nx + 1.0 / ny));
        -: 4384:				 t_stat   = (cint_est - mu) / std_err;
        -: 4385:				 df       = nx + ny - 2;
        -: 4386:				 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
        -: 4387:				 hv_store(results, "estimate_y", 10, newSVnv(mean_y), 0);
        -: 4388:			} else {
        -: 4389:				 if (var_x == 0.0 && var_y == 0.0) croak("t_test: data are essentially constant");
        -: 4390:				 cint_est         = mean_x - mean_y;
        -: 4391:				 double stderr_x2 = var_x / nx;
        -: 4392:				 double stderr_y2 = var_y / ny;
       42: 4393:				 std_err          = sqrt(stderr_x2 + stderr_y2);
call    0 returned 42
       42: 4394:				 t_stat           = (cint_est - mu) / std_err;
        -: 4395:				 df = pow(stderr_x2 + stderr_y2, 2) /
       42: 4396:				      (pow(stderr_x2, 2) / (nx - 1) + pow(stderr_y2, 2) / (ny - 1));
        -: 4397:				 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
        -: 4398:				 hv_store(results, "estimate_y", 10, newSVnv(mean_y), 0);
        -: 4399:			}
        -: 4400:		} else {
function XS_Stats__LikeR_p_adjust called 15 returned 87% blocks executed 90%
       15: 4401:			cint_est = mean_x;
        -: 4402:			std_err  = sqrt(var_x / nx);
       15: 4403:			t_stat   = (cint_est - mu) / std_err;
       15: 4403-block  0
call    0 returned 15
       15: 4404:			df       = nx - 1;
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4404-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 15
    #####: 4405:			hv_store(results, "estimate", 8, newSVnv(mean_x), 0);
    %%%%%: 4405-block  0
call    0 never executed
        -: 4406:		}
       15: 4407:		p_val = get_t_pvalue(t_stat, df, alternative);
        -: 4408:		double alpha = 1.0 - conf_level, t_crit, ci_lower, ci_upper;
       15: 4409:		if (strcmp(alternative, "less") == 0) {
        -: 4410:			t_crit   = qt_tail(df, alpha);
        -: 4411:			ci_lower = -INFINITY;
        -: 4412:			ci_upper = cint_est + t_crit * std_err;
       15: 4413:		} else if (strcmp(alternative, "greater") == 0) {
       15: 4413-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 13
        2: 4414:			t_crit   = qt_tail(df, alpha);
        2: 4414-block  0
        -: 4415:			ci_lower = cint_est - t_crit * std_err;
       13: 4416:			ci_upper = INFINITY;
       13: 4416-block  0
call    0 returned 13
        -: 4417:		} else {
        -: 4418:			t_crit   = qt_tail(df, alpha / 2.0);
        -: 4419:			ci_lower = cint_est - t_crit * std_err;
        -: 4420:			ci_upper = cint_est + t_crit * std_err;
        -: 4421:		}
        -: 4422:		AV*restrict conf_int = newAV();
        -: 4423:		av_push(conf_int, newSVnv(ci_lower));
        -: 4424:		av_push(conf_int, newSVnv(ci_upper));
        -: 4425:		hv_store(results, "statistic", 9, newSVnv(t_stat), 0);
        -: 4426:		hv_store(results, "df",        2, newSVnv(df),     0);
        -: 4427:		hv_store(results, "p_value",   7, newSVnv(p_val),  0);
        -: 4428:		hv_store(results, "conf_int",  8, newRV_noinc((SV*)conf_int), 0);
        -: 4429:		RETVAL = newRV_noinc((SV*)results);
        -: 4430:	}
        -: 4431:#line 4432 "LikeR.c"
        -: 4432:	RETVAL = sv_2mortal(RETVAL);
        -: 4433:	ST(0) = RETVAL;
        -: 4434:    }
        -: 4435:    XSRETURN(1);
        -: 4436:}
        -: 4437:
        -: 4438:
        -: 4439:XS_EUPXS(XS_Stats__LikeR_p_adjust); /* prototype to pass -Wmissing-prototypes */
        -: 4440:XS_EUPXS(XS_Stats__LikeR_p_adjust)
        -: 4441:{
        -: 4442:    dVAR; dXSARGS;
        -: 4443:    if (items < 1 || items > 2)
        -: 4444:       croak_xs_usage(cv,  "p_sv, method= \"holm\"");
        -: 4445:    PERL_UNUSED_VAR(ax); /* -Wall */
        -: 4446:    SP -= items;
        -: 4447:    {
        -: 4448:	SV *	p_sv = ST(0)
        -: 4449:;
        -: 4450:	const char *	method;
        -: 4451:
        -: 4452:	if (items < 2)
        -: 4453:	    method = "holm";
        -: 4454:	else {
        -: 4455:	    method = (const char *)SvPV_nolen(ST(1))
        -: 4456:;
        -: 4457:	}
        -: 4458:#line 4005 "LikeR.xs"
        -: 4459:		if (!SvROK(p_sv) || SvTYPE(SvRV(p_sv)) != SVt_PVAV) {
        -: 4460:			croak("p_adjust: first argument must be an ARRAY reference of p-values");
        -: 4461:		}
        -: 4462:		AV *restrict p_av = (AV*)SvRV(p_sv);
        -: 4463:		size_t n = av_len(p_av) + 1;
        -: 4464:		// Handle empty input
        -: 4465:		if (n == 0) {
        -: 4466:			XSRETURN_EMPTY;
        -: 4467:		}
        -: 4468:		// Normalize method string
        -: 4469:		char meth[64];
        -: 4470:		strncpy(meth, method, 63); meth[63] = '\0';
        -: 4471:		for(unsigned short int i = 0; meth[i]; i++) meth[i] = tolower(meth[i]);
        -: 4472:		// Resolve aliases
        -: 4473:		if (strstr(meth, "benjamini") && strstr(meth, "hochberg")) strcpy(meth, "bh");
        -: 4474:		if (strstr(meth, "benjamini") && strstr(meth, "yekutieli")) strcpy(meth, "by");
        -: 4475:		if (strcmp(meth, "fdr") == 0) strcpy(meth, "bh");
        -: 4476:		// Allocate C memory
        -: 4477:		PVal *restrict arr;
        -: 4478:		double *restrict adj;
        -: 4479:		Newx(arr, n, PVal);
        -: 4480:		Newx(adj, n, double);
        -: 4481:
        -: 4482:		for (size_t i = 0; i < n; i++) {
        -: 4483:			SV**restrict tv = av_fetch(p_av, i, 0);
        -: 4484:			arr[i].p = (tv && SvOK(*tv)) ? SvNV(*tv) : 1.0;
        -: 4485:			arr[i].orig_idx = i;
        -: 4486:		}
        -: 4487:		// Sort ascending (Stable sort using original index)
        -: 4488:		qsort(arr, n, sizeof(PVal), cmp_pval);
        -: 4489:#line 4490 "LikeR.c"
        -: 4490:#line 4036 "LikeR.xs"
        -: 4491:		if (strcmp(meth, "bonferroni") == 0) {
        -: 4492:			for (size_t i = 0; i < n; i++) {
        -: 4493:				double v = arr[i].p * n;
        -: 4494:				adj[arr[i].orig_idx] = (v < 1.0) ? v : 1.0;
        -: 4495:			}
        -: 4496:		} else if (strcmp(meth, "holm") == 0) {
        -: 4497:			double cummax = 0.0;
        -: 4498:			for (size_t i = 0; i < n; i++) {
        -: 4499:				 double v = arr[i].p * (n - i);
        -: 4500:				 if (v > cummax) cummax = v;
        -: 4501:				 adj[arr[i].orig_idx] = (cummax < 1.0) ? cummax : 1.0;
        -: 4502:			}
        -: 4503:		} else if (strcmp(meth, "hochberg") == 0) {
        -: 4504:			double cummin = 1.0;
        -: 4505:			for (ssize_t i = n - 1; i >= 0; i--) {
        -: 4506:				 double v = arr[i].p * (n - i);
        -: 4507:				 if (v < cummin) cummin = v;
        -: 4508:				 adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
        -: 4509:			}
        -: 4510:		} else if (strcmp(meth, "bh") == 0) {
        -: 4511:			double cummin = 1.0;
        -: 4512:			for (ssize_t i = n - 1; i >= 0; i--) {
        -: 4513:				double v = arr[i].p * n / (i + 1.0);
        -: 4514:				if (v < cummin) cummin = v;
        -: 4515:				adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
        -: 4516:			}
        -: 4517:		} else if (strcmp(meth, "by") == 0) {
        -: 4518:			double q = 0.0;
        -: 4519:			for (size_t i = 1; i <= n; i++) q += 1.0 / i;
        -: 4520:			double cummin = 1.0;
        -: 4521:			for (ssize_t i = n - 1; i >= 0; i--) {
        -: 4522:				double v = arr[i].p * n / (i + 1.0) * q;
        -: 4523:				if (v < cummin) cummin = v;
        -: 4524:				adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
        -: 4525:			}
        -: 4526:		} else if (strcmp(meth, "hommel") == 0) {
        -: 4527:			double *restrict pa, *restrict q_arr;
        -: 4528:			Newx(pa, n, double);
        -: 4529:			Newx(q_arr, n, double);
        -: 4530:			// Initial: min(n * p[i] / (i + 1))
        -: 4531:			double min_val = n * arr[0].p;
        -: 4532:			for (size_t i = 1; i < n; i++) {
        -: 4533:				double temp = (n * arr[i].p) / (i + 1.0);
        -: 4534:				if (temp < min_val) {
        -: 4535:				   min_val = temp;
        -: 4536:				}
        -: 4537:			}
        -: 4538:			// pa <- q <- rep(min, n)
        -: 4539:			for (size_t i = 0; i < n; i++) {
        -: 4540:				 pa[i] = min_val;
        -: 4541:				 q_arr[i] = min_val;
        -: 4542:			}
        -: 4543:			for (size_t j = n - 1; j >= 2; j--) {
        -: 4544:				 ssize_t n_mj = n - j;       // Max index for 'ij'. Length is n_mj + 1
        -: 4545:				 ssize_t i2_len = j - 1;     // Length of 'i2
        -: 4546:				 // Calculate q1 = min(j * p[i2] / (2:j))
        -: 4547:				 double q1 = (j * arr[n_mj + 1].p) / 2.0;
        -: 4548:				 for (size_t k = 1; k < i2_len; k++) {
        -: 4549:				     double temp_q1 = (j * arr[n_mj + 1 + k].p) / (2.0 + k);
        -: 4550:				     if (temp_q1 < q1) {
        -: 4551:				         q1 = temp_q1;
        -: 4552:				     }
        -: 4553:				 }
       12: 4554:				 // q[ij] <- pmin(j * p[ij], q1)
       12: 4555:				 for (size_t i = 0; i <= n_mj; i++) {
        -: 4556:				     double v = j * arr[i].p;
        -: 4557:				     q_arr[i] = (v < q1) ? v : q1;
        -: 4558:				 }
        -: 4559:				 // q[i2] <- q[n - j]
        -: 4560:				 for (size_t i = 0; i < i2_len; i++) {
function XS_Stats__LikeR_median called 8 returned 62% blocks executed 90%
        8: 4561:				     q_arr[n_mj + 1 + i] = q_arr[n_mj];
        -: 4562:				}
        8: 4563:				 // pa <- pmax(pa, q)
        8: 4563-block  0
call    0 returned 8
        -: 4564:				for (size_t i = 0; i < n; i++) {
        -: 4565:				    if (pa[i] < q_arr[i]) {
        -: 4566:				       pa[i] = q_arr[i];
        -: 4567:				    }
       8*: 4568:				}
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 4568-block  0
    %%%%%: 4568-block  1
call    2 never executed
        8: 4568-block  2
        -: 4569:			}
        -: 4570:			// pmin(1, pmax(pa, p))[ro] — map sorted results back to original indices
        -: 4571:			for (size_t i = 0; i < n; i++) {
        -: 4572:				double v = (pa[i] > arr[i].p) ? pa[i] : arr[i].p;
        -: 4573:				if (v > 1.0) v = 1.0;
        -: 4574:				adj[arr[i].orig_idx] = v;
        -: 4575:			}
        -: 4576:			Safefree(pa);  Safefree(q_arr);
        -: 4577:		} else if (strcmp(meth, "none") == 0) {
        -: 4578:			for (size_t i = 0; i < n; i++) {
        -: 4579:				adj[arr[i].orig_idx] = arr[i].p;
        -: 4580:			}
        -: 4581:		} else {
        -: 4582:			Safefree(arr); Safefree(adj);
        -: 4583:			croak("Unknown p-value adjustment method: %s", method);
        -: 4584:		}
        -: 4585:		// Push values onto the Perl stack as a flat list
        -: 4586:		EXTEND(SP, n);
        -: 4587:		for (size_t i = 0; i < n; i++) {
        -: 4588:			PUSHs(sv_2mortal(newSVnv(adj[i])));
        -: 4589:		}
        -: 4590:		Safefree(arr); arr = NULL;
        -: 4591:		Safefree(adj); adj = NULL;
        -: 4592:#line 4593 "LikeR.c"
        -: 4593:	PUTBACK;
        -: 4594:	return;
        -: 4595:    }
        -: 4596:}
        -: 4597:
        -: 4598:
        -: 4599:XS_EUPXS(XS_Stats__LikeR_median); /* prototype to pass -Wmissing-prototypes */
        -: 4600:XS_EUPXS(XS_Stats__LikeR_median)
        -: 4601:{
        -: 4602:    dVAR; dXSARGS;
        -: 4603:    PERL_UNUSED_VAR(cv); /* -W */
        -: 4604:    PERL_UNUSED_VAR(items); /* -W */
        -: 4605:    {
        -: 4606:	double	RETVAL;
        -: 4607:	dXSTARG;
        -: 4608:#line 4141 "LikeR.xs"
        -: 4609:	  size_t total_count = 0, k = 0;
        -: 4610:	  double* restrict nums;
        -: 4611:	  double median_val = 0.0;
        -: 4612:#line 4613 "LikeR.c"
        -: 4613:#line 4145 "LikeR.xs"
        -: 4614:	  /* Pass 1: Count valid elements — die immediately on any undef */
        -: 4615:	  for (size_t i = 0; i < items; i++) {
        -: 4616:		   SV* restrict arg = ST(i);
        -: 4617:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 4618:		       AV* restrict av = (AV*)SvRV(arg);
        -: 4619:		       size_t len = av_len(av) + 1;
        -: 4620:		       for (size_t j = 0; j < len; j++) {
        -: 4621:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 4622:		           if (tv && SvOK(*tv)) {
        -: 4623:		               total_count++;
        -: 4624:		           } else {
        -: 4625:		               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 4626:		           }
        -: 4627:		       }
        -: 4628:		   } else if (SvOK(arg)) {
        -: 4629:		       total_count++;
        -: 4630:		   } else {
        -: 4631:		       croak("median: undefined value at argument index %zu", i);
        -: 4632:		   }
        -: 4633:	  }
        5: 4634:	  if (total_count == 0) croak("median needs >= 1 element");
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 4634-block  0
        3: 4634-block  1
call    2 returned 3
        5: 4635:
        -: 4636:	  /* Allocate C array now that we know the exact size */
        5: 4637:	  Newx(nums, total_count, double);
        5: 4637-block  0
        -: 4638:
        -: 4639:	  /* Pass 2: Populate the C array — Safefree before any croak */
        -: 4640:	  for (size_t i = 0; i < items; i++) {
        -: 4641:		   SV* restrict arg = ST(i);
function XS_Stats__LikeR_cor called 6 returned 67% blocks executed 75%
        6: 4642:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 4643:		       AV* restrict av = (AV*)SvRV(arg);
        6: 4644:		       size_t len = av_len(av) + 1;
        6: 4644-block  0
call    0 returned 6
        6: 4645:		       for (size_t j = 0; j < len; j++) {
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4645-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    #####: 4646:		           SV** restrict tv = av_fetch(av, j, 0);
    %%%%%: 4646-block  0
call    0 never executed
        -: 4647:		           if (tv && SvOK(*tv)) {
        -: 4648:		               nums[k++] = SvNV(*tv);
        6: 4649:		           } else {
        -: 4650:		               Safefree(nums);
        -: 4651:		               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 4652:		           }
        -: 4653:		       }
        6: 4654:		   } else if (SvOK(arg)) {
        6: 4654-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 4655:		       nums[k++] = SvNV(arg);
    %%%%%: 4655-block  0
        -: 4656:		   } else {
        6: 4657:		       Safefree(nums);
        6: 4657-block  0
        -: 4658:		       croak("median: undefined value at argument index %zu", i);
        -: 4659:		   }
        -: 4660:	  }
        6: 4661:
        6: 4661-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1: 4662:	  /* Sort and calculate median */
        1: 4662-block  0
        -: 4663:	  qsort(nums, total_count, sizeof(double), compare_doubles);
        5: 4664:	  if (total_count % 2 == 0) {
        5: 4664-block  0
call    0 returned 5
        -: 4665:		   median_val = (nums[total_count / 2 - 1] + nums[total_count / 2]) / 2.0;
        -: 4666:	  } else {
        -: 4667:		   median_val = nums[total_count / 2];
        -: 4668:	  }
        -: 4669:	  Safefree(nums);
        -: 4670:	  nums = NULL;
        -: 4671:	  RETVAL = median_val;
        -: 4672:#line 4673 "LikeR.c"
        -: 4673:	TARGn((double)RETVAL, 1);
        -: 4674:	ST(0) = TARG;
        -: 4675:    }
        -: 4676:    XSRETURN(1);
        -: 4677:}
        -: 4678:
        -: 4679:
        -: 4680:XS_EUPXS(XS_Stats__LikeR_cor); /* prototype to pass -Wmissing-prototypes */
        -: 4681:XS_EUPXS(XS_Stats__LikeR_cor)
        -: 4682:{
        -: 4683:    dVAR; dXSARGS;
        -: 4684:    if (items < 1 || items > 3)
        -: 4685:       croak_xs_usage(cv,  "x_sv, y_sv= &PL_sv_undef, method= \"pearson\"");
        -: 4686:    {
        -: 4687:	SV *	RETVAL;
        -: 4688:	SV *	x_sv = ST(0)
        -: 4689:;
        -: 4690:	SV *	y_sv;
        -: 4691:	const char *	method;
        -: 4692:
        -: 4693:	if (items < 2)
        -: 4694:	    y_sv = &PL_sv_undef;
        -: 4695:	else {
        -: 4696:	    y_sv = ST(1)
        -: 4697:;
        -: 4698:	}
        -: 4699:
        -: 4700:	if (items < 3)
        -: 4701:	    method = "pearson";
        -: 4702:	else {
        -: 4703:	    method = (const char *)SvPV_nolen(ST(2))
        -: 4704:;
        -: 4705:	}
        -: 4706:#line 4208 "LikeR.xs"
        -: 4707:	// --- validate method -------------------------------------------
        -: 4708:	if (strcmp(method, "pearson")  != 0 &&
        -: 4709:		strcmp(method, "spearman") != 0 &&
        -: 4710:		strcmp(method, "kendall")  != 0)
        -: 4711:		  croak("cor: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')",
        -: 4712:		        method);
        -: 4713:
        -: 4714:	// --- validate x ------------------------------------------------
        -: 4715:	if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 4716:		  croak("cor: x must be an ARRAY reference");
        -: 4717:
        -: 4718:	AV*restrict x_av = (AV*)SvRV(x_sv);
        -: 4719:	size_t nx   = av_len(x_av) + 1;
        -: 4720:	if (nx == 0) croak("cor: x is empty");
        -: 4721:
        -: 4722:	// --- detect whether x is a flat vector or a matrix (AoA) -------
        -: 4723:	bool x_is_matrix = 0;
        -: 4724:	{
        -: 4725:		  SV**restrict fp = av_fetch(x_av, 0, 0);
        -: 4726:		  if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
        -: 4727:		      x_is_matrix = 1;
        -: 4728:	}
        -: 4729:
        -: 4730:	// --- detect y ----------------------------
        -: 4731:	bool has_y = (SvOK(y_sv) && SvROK(y_sv) &&
        -: 4732:		           SvTYPE(SvRV(y_sv)) == SVt_PVAV);
        -: 4733:
        -: 4734:	AV*restrict y_av = has_y ? (AV*)SvRV(y_sv) : NULL;
        -: 4735:	size_t ny = has_y ? av_len(y_av) + 1 : 0;
        -: 4736:
        -: 4737:	bool y_is_matrix = 0;
        -: 4738:	if (has_y && ny > 0) {
        -: 4739:		SV**restrict fp = av_fetch(y_av, 0, 0);
        -: 4740:		if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
        -: 4741:			y_is_matrix = 1;
        -: 4742:	}
        -: 4743:
        -: 4744:#line 4745 "LikeR.c"
        -: 4745:#line 4246 "LikeR.xs"
        -: 4746:	// Branch 1: both inputs are flat vectors  →  scalar result
        -: 4747:	if (!x_is_matrix && !y_is_matrix) {
        -: 4748:		  if (!has_y) {
        -: 4749:		      /* cor(vector) == 1 by definition */
        -: 4750:		      RETVAL = newSVnv(1.0);
        -: 4751:		  } else {
        -: 4752:		      if (nx != ny)
        -: 4753:		          croak("cor: x and y must have the same length (%lu vs %lu)",
        -: 4754:		                nx, ny);
        -: 4755:
        -: 4756:		      if (nx < 2)
        -: 4757:		          croak("cor: need at least 2 observations");
        -: 4758:
        -: 4759:		      double *restrict xd, *restrict yd;
        -: 4760:		      Newx(xd, nx, double);
        -: 4761:		      Newx(yd, ny, double);
        -: 4762:
        -: 4763:		      bool x_sd0 = 1, y_sd0 = 1;
        -: 4764:		      double x_first = NAN, y_first = NAN;
        -: 4765:
        -: 4766:		      for (size_t i = 0; i < nx; i++) {
        -: 4767:		          SV**restrict tv = av_fetch(x_av, i, 0);
        -: 4768:		          double val = (tv && SvOK(*tv) && looks_like_number(*tv)) ? SvNV(*tv) : NAN;
        -: 4769:		          xd[i] = val;
        -: 4770:		          if (!isnan(val)) {
        -: 4771:		              if (isnan(x_first)) x_first = val;
        -: 4772:		              else if (val != x_first) x_sd0 = 0;
        -: 4773:		          }
        -: 4774:		      }
        -: 4775:		      for (size_t i = 0; i < ny; i++) {
        -: 4776:		          SV**restrict tv = av_fetch(y_av, i, 0);
        -: 4777:		          double val = (tv && SvOK(*tv) && looks_like_number(*tv)) ? SvNV(*tv) : NAN;
        -: 4778:		          yd[i] = val;
        -: 4779:		          if (!isnan(val)) {
        -: 4780:		              if (isnan(y_first)) y_first = val;
        -: 4781:		              else if (val != y_first) y_sd0 = 0;
        -: 4782:		          }
        -: 4783:		      }
        -: 4784:
        -: 4785:		      if (x_sd0 || y_sd0) {
        -: 4786:		          Safefree(xd); Safefree(yd);
        -: 4787:		          if (x_sd0) croak("cor: standard deviation of x is 0");
        -: 4788:		          croak("cor: standard deviation of y is 0");
        -: 4789:		      }
        -: 4790:
        -: 4791:		      double r = compute_cor(xd, yd, nx, method);
        -: 4792:		      Safefree(xd); Safefree(yd);
        -: 4793:		      RETVAL = newSVnv(r);
        -: 4794:		  }
        -: 4795:	} else {//Branch 2: x is a matrix (or y is a matrix)  →  AoA result
        -: 4796:		  // -- resolve x matrix dimensions
        -: 4797:		  if (!x_is_matrix)
        -: 4798:		      croak("cor: x must be a matrix (array ref of array refs) "
        -: 4799:		            "when y is a matrix");
        -: 4800:
        -: 4801:		  SV**restrict xr0 = av_fetch(x_av, 0, 0);
        -: 4802:		  if (!xr0 || !SvROK(*xr0) || SvTYPE(SvRV(*xr0)) != SVt_PVAV)
        -: 4803:		      croak("cor: each row of x must be an ARRAY reference");
        -: 4804:
        -: 4805:		  size_t ncols_x = av_len((AV*)SvRV(*xr0)) + 1;
        -: 4806:		  if (ncols_x == 0) croak("cor: x matrix has zero columns");
        -: 4807:
        -: 4808:		  size_t nrows   = nx;    /* observations */
        -: 4809:
        -: 4810:		  // PRE-VALIDATION PASS: Ensure all rows are arrays to prevent memory leaks on croak
        -: 4811:		  for (size_t i = 0; i < nrows; i++) {
        -: 4812:		      SV**restrict rv = av_fetch(x_av, i, 0);
        -: 4813:		      if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
        -: 4814:		          croak("cor: x row %lu is not an array ref", i);
        -: 4815:		  }
        -: 4816:
        -: 4817:		  if (has_y && y_is_matrix) {
        -: 4818:		      if (ny != nrows) croak("cor: x and y must have the same number of rows (%lu vs %lu)", nrows, ny);
        -: 4819:		      for (size_t i = 0; i < nrows; i++) {
        -: 4820:		          SV**restrict rv = av_fetch(y_av, i, 0);
        -: 4821:		          if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
        -: 4822:		              croak("cor: y row %lu is not an array ref", i);
        -: 4823:		      }
        -: 4824:		  }
        -: 4825:
        -: 4826:		  // -- extract x columns
        -: 4827:		  double **restrict col_x;
        -: 4828:		  Newx(col_x, ncols_x, double*);
        -: 4829:
        -: 4830:		  for (size_t j = 0; j < ncols_x; j++) {
        -: 4831:		      Newx(col_x[j], nrows, double);
        -: 4832:		      bool sd0 = 1;
        -: 4833:		      double first = NAN;
        -: 4834:		      for (size_t i = 0; i < nrows; i++) {
        -: 4835:		          SV**restrict rv = av_fetch(x_av, i, 0);
        -: 4836:		          AV*restrict  row = (AV*)SvRV(*rv);
        -: 4837:		          SV**restrict cv  = av_fetch(row, j, 0);
        -: 4838:		          double val = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
        -: 4839:		          col_x[j][i] = val;
        -: 4840:		          if (!isnan(val)) {
        -: 4841:		              if (isnan(first)) first = val;
        -: 4842:		              else if (val != first) sd0 = 0;
        -: 4843:		          }
        -: 4844:		      }
        -: 4845:		      if (sd0) {
        -: 4846:		          for (size_t k = 0; k <= j; k++) Safefree(col_x[k]);
        -: 4847:		          Safefree(col_x);
        -: 4848:		          croak("cor: standard deviation is 0 in x column %lu", j);
        -: 4849:		      }
        -: 4850:		  }
        -: 4851:
        -: 4852:		  // -- resolve y: separate matrix or re-use x (symmetric)
        -: 4853:		  size_t ncols_y;
        -: 4854:		  double **restrict col_y   = NULL;
        -: 4855:		  bool symmetric = 0;
        -: 4856:
        -: 4857:		  // 1 = cor(X) — result is symmetric
        -: 4858:		  if (has_y && y_is_matrix) {
        -: 4859:		      // cross-correlation: X (nrows × p) vs Y (nrows × q)
        -: 4860:		      SV**restrict yr0 = av_fetch(y_av, 0, 0);
        -: 4861:		      ncols_y = av_len((AV*)SvRV(*yr0)) + 1;
        -: 4862:		      if (ncols_y == 0) croak("cor: y matrix has zero columns");
        4: 4863:
        4: 4863-block  0
call    0 returned 4
        4: 4864:		      Newx(col_y, ncols_y, double*);
        -: 4865:		      for (size_t j = 0; j < ncols_y; j++) {
        4: 4866:		          Newx(col_y[j], nrows, double);
        -: 4867:		          bool sd0 = 1;
        -: 4868:		          double first = NAN;
        -: 4869:		          for (size_t i = 0; i < nrows; i++) {
        -: 4870:		              SV**restrict  rv = av_fetch(y_av, i, 0);
function XS_Stats__LikeR_scale called 5 returned 80% blocks executed 70%
        5: 4871:		              AV*restrict  row = (AV*)SvRV(*rv);
        -: 4872:		              SV**restrict cv  = av_fetch(row, j, 0);
        5: 4873:		              double val = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
        5: 4873-block  0
call    0 returned 5
        -: 4874:		              col_y[j][i] = val;
        -: 4875:		              if (!isnan(val)) {
        -: 4876:		                  if (isnan(first)) first = val;
        5: 4877:		                  else if (val != first) sd0 = 0;
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        -: 4878:		              }
        -: 4879:		          }
        -: 4880:		          if (sd0) {
        -: 4881:		              for (size_t k = 0; k < ncols_x; k++) Safefree(col_x[k]);
        -: 4882:		              Safefree(col_x);
        -: 4883:		              for (size_t k = 0; k <= j; k++) Safefree(col_y[k]);
        -: 4884:		              Safefree(col_y);
        -: 4885:		              croak("cor: standard deviation is 0 in y column %lu", j);
        -: 4886:		          }
        -: 4887:		      }
        -: 4888:		  } else { // cor(X) — symmetric p×p result; share column arrays
        -: 4889:		      ncols_y  = ncols_x;
        -: 4890:		      col_y    = col_x;
        -: 4891:		      symmetric = 1;
        -: 4892:		  }
        -: 4893:		  if (nrows < 2)
        -: 4894:		      croak("cor: need at least 2 observations (got %lu)", nrows);
        -: 4895:		  // -- build cache for symmetric case: compute upper triangle, store results, mirror to lower triangle
        -: 4896:		  AV*restrict result_av = newAV();
        -: 4897:		  av_extend(result_av, ncols_x - 1);
        -: 4898:		  // Allocate per-row AVs up front so we can fill them in order
        -: 4899:		  AV **restrict rows_out;
        -: 4900:		  Newx(rows_out, ncols_x, AV*);
        -: 4901:		  for (size_t i = 0; i < ncols_x; i++) {
        -: 4902:		      rows_out[i] = newAV();
        -: 4903:		      av_extend(rows_out[i], ncols_y - 1);
        -: 4904:		  }
        -: 4905:		  if (symmetric) {
        -: 4906:/* Upper triangle + diagonal, then mirror. r_cache[i][j] (j >= i) holds the computed value. */
        -: 4907:		      double **restrict r_cache;
        -: 4908:		      Newx(r_cache, ncols_x, double*);
        -: 4909:		      for (size_t i = 0; i < ncols_x; i++)
        -: 4910:		          Newx(r_cache[i], ncols_x, double);
        -: 4911:
        -: 4912:		      for (size_t i = 0; i < ncols_x; i++) {
        -: 4913:		          r_cache[i][i] = 1.0; // diagonal
        -: 4914:		          for (size_t j = i + 1; j < ncols_x; j++) {
        -: 4915:		              double r = compute_cor(col_x[i], col_x[j], nrows, method);
        -: 4916:		              r_cache[i][j] = r;
        -: 4917:		              r_cache[j][i] = r; // symmetry
        -: 4918:		          }
        -: 4919:		      }
        -: 4920:		      // fill output AoA from cache
        -: 4921:		      for (size_t i = 0; i < ncols_x; i++)
        -: 4922:		          for (size_t j = 0; j < ncols_x; j++)
        -: 4923:		              av_store(rows_out[i], j, newSVnv(r_cache[i][j]));
        -: 4924:
        -: 4925:		      for (size_t i = 0; i < ncols_x; i++) Safefree(r_cache[i]);
        -: 4926:		      Safefree(r_cache); r_cache = NULL;
        -: 4927:		  } else {
        -: 4928:		      // cross-correlation: every (i,j) pair is independent
        -: 4929:		      for (size_t i = 0; i < ncols_x; i++)
        -: 4930:		          for (size_t j = 0; j < ncols_y; j++)
        -: 4931:		              av_store(rows_out[i], j, newSVnv(compute_cor(col_x[i], col_y[j], nrows, method)));
        -: 4932:		  }
        -: 4933:		  // push row AVs into result
        -: 4934:		  for (size_t i = 0; i < ncols_x; i++)
        -: 4935:		      av_store(result_av, i, newRV_noinc((SV*)rows_out[i]));
        -: 4936:		  Safefree(rows_out); rows_out = NULL;
        -: 4937:		  // -- free column arrays -------------------------------------
        -: 4938:		  for (size_t j = 0; j < ncols_x; j++) Safefree(col_x[j]);
        -: 4939:		  Safefree(col_x); col_x = NULL;
        -: 4940:		  if (!symmetric) {
        -: 4941:		      for (size_t j = 0; j < ncols_y; j++) Safefree(col_y[j]);
        -: 4942:		      Safefree(col_y);
        -: 4943:		  }
        -: 4944:		  RETVAL = newRV_noinc((SV*)result_av);
        -: 4945:	}
        -: 4946:#line 4947 "LikeR.c"
        -: 4947:	RETVAL = sv_2mortal(RETVAL);
        -: 4948:	ST(0) = RETVAL;
        -: 4949:    }
        -: 4950:    XSRETURN(1);
        -: 4951:}
        -: 4952:
        -: 4953:
        -: 4954:XS_EUPXS(XS_Stats__LikeR_scale); /* prototype to pass -Wmissing-prototypes */
        -: 4955:XS_EUPXS(XS_Stats__LikeR_scale)
        -: 4956:{
        -: 4957:    dVAR; dXSARGS;
        -: 4958:    PERL_UNUSED_VAR(cv); /* -W */
        -: 4959:    PERL_UNUSED_VAR(items); /* -W */
        -: 4960:    PERL_UNUSED_VAR(ax); /* -Wall */
        -: 4961:    SP -= items;
        -: 4962:    {
        -: 4963:#line 4452 "LikeR.xs"
        -: 4964:	{
        -: 4965:		bool do_center_mean = TRUE, do_scale_sd = TRUE;
        -: 4966:		double center_val = 0.0, scale_val = 1.0;
        -: 4967:		size_t data_items = items;
        -: 4968:		// 1. Parse Options Hash (if it exists as the last argument)
        -: 4969:		if (items > 0) {
        -: 4970:			SV*restrict last_arg = ST(items - 1);
        -: 4971:			if (SvROK(last_arg) && SvTYPE(SvRV(last_arg)) == SVt_PVHV) {
        -: 4972:				 data_items = items - 1; // Exclude hash from data processing
        -: 4973:				 HV*restrict opt_hv = (HV*)SvRV(last_arg);
        -: 4974:				 // --- Parse 'center'
        -: 4975:				 SV**restrict center_sv = hv_fetch(opt_hv, "center", 6, 0);
        -: 4976:				 if (center_sv) {
        -: 4977:				     SV*restrict val_sv = *center_sv;
        -: 4978:				     if (!SvOK(val_sv)) {
        -: 4979:				         do_center_mean = FALSE; center_val = 0.0;
        -: 4980:				     } else {
        -: 4981:				         char *restrict str = SvPV_nolen(val_sv);
        -: 4982:				         /* Trap booleans and empty strings before numeric checks */
        -: 4983:				         if (strcasecmp(str, "mean") == 0 || strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) {
        -: 4984:				             do_center_mean = TRUE;
        -: 4985:				         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
        -: 4986:				             do_center_mean = FALSE; center_val = 0.0;
        -: 4987:				         } else if (looks_like_number(val_sv)) {
        -: 4988:				             do_center_mean = FALSE; center_val = SvNV(val_sv);
        -: 4989:				         } else if (SvTRUE(val_sv)) {
        -: 4990:				             do_center_mean = TRUE;
        -: 4991:				         } else {
        -: 4992:				             do_center_mean = FALSE; center_val = 0.0;
        -: 4993:				         }
        -: 4994:				     }
        -: 4995:				 }
        -: 4996:				 // --- Parse 'scale' ---
        -: 4997:				 SV**restrict scale_sv = hv_fetch(opt_hv, "scale", 5, 0);
        -: 4998:				 if (scale_sv) {
        -: 4999:				     SV*restrict val_sv = *scale_sv;
        -: 5000:				     if (!SvOK(val_sv)) {
        -: 5001:				         do_scale_sd = FALSE; scale_val = 1.0;
        -: 5002:				     } else {
        -: 5003:				         char *restrict str = SvPV_nolen(val_sv);
        -: 5004:				         if (strcasecmp(str, "sd") == 0 || strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) {
        -: 5005:				             do_scale_sd = TRUE;
        -: 5006:				         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
        -: 5007:				             do_scale_sd = FALSE; scale_val = 1.0;
        -: 5008:				         } else if (looks_like_number(val_sv)) {
        -: 5009:				             do_scale_sd = FALSE; scale_val = SvNV(val_sv);
        -: 5010:				             if (scale_val == 0.0) scale_val = 1.0; /* Prevent Division By Zero */
        -: 5011:				         } else if (SvTRUE(val_sv)) {
        -: 5012:				             do_scale_sd = TRUE;
        -: 5013:				         } else {
        -: 5014:				             do_scale_sd = FALSE; scale_val = 1.0;
        -: 5015:				         }
        -: 5016:				     }
        -: 5017:				 }
        -: 5018:			}
        -: 5019:		}
        -: 5020:		// 2. Detect if the input is a Matrix (Array of Arrays)
        -: 5021:		bool is_matrix = FALSE;
        -: 5022:		if (data_items == 1) {
        -: 5023:			SV*restrict first_arg = ST(0);
        -: 5024:			if (SvROK(first_arg) && SvTYPE(SvRV(first_arg)) == SVt_PVAV) {
        -: 5025:				 AV*restrict av = (AV*)SvRV(first_arg);
        -: 5026:				 if (av_len(av) >= 0) {
        -: 5027:				     SV**restrict first_elem = av_fetch(av, 0, 0);
        -: 5028:				     if (first_elem && SvROK(*first_elem) && SvTYPE(SvRV(*first_elem)) == SVt_PVAV) {
        -: 5029:				         is_matrix = TRUE;
        -: 5030:				     }
        -: 5031:				 }
        -: 5032:			}
        -: 5033:		}
        -: 5034:		if (is_matrix) {
        -: 5035:			//=========================================================
        -: 5036:			// MATRIX MODE: Scale columns independently (Just like R)
        -: 5037:			//=========================================================
        -: 5038:			AV*restrict mat_av = (AV*)SvRV(ST(0));
        -: 5039:			size_t nrow = av_len(mat_av) + 1, ncol = 0;
        -: 5040:
        -: 5041:			SV**restrict first_row = av_fetch(mat_av, 0, 0);
        -: 5042:			ncol = av_len((AV*)SvRV(*first_row)) + 1;
        -: 5043:
        -: 5044:			if (nrow == 0 || ncol == 0) croak("scale requires non-empty matrix");
        -: 5045:
        -: 5046:			// Create a new matrix for the scaled output
        -: 5047:			AV*restrict result_av = newAV();
        -: 5048:			av_extend(result_av, nrow - 1);
        -: 5049:			AV**restrict row_ptrs = (AV**)safemalloc(nrow * sizeof(AV*));
        -: 5050:			for (size_t r = 0; r < nrow; r++) {
        -: 5051:				 row_ptrs[r] = newAV();
        -: 5052:				 av_extend(row_ptrs[r], ncol - 1);
        -: 5053:				 av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
        -: 5054:			}
        -: 5055:			// Calculate and apply scale per column
        -: 5056:			for (size_t c = 0; c < ncol; c++) {
        -: 5057:				 double col_sum = 0.0;
        -: 5058:				 double *restrict col_data;
        -: 5059:				 Newx(col_data, nrow, double);
        -: 5060:				 // Extract the column data
        -: 5061:				 for (size_t r = 0; r < nrow; r++) {
        -: 5062:				     SV**restrict row_sv = av_fetch(mat_av, r, 0);
        -: 5063:				     if (row_sv && SvROK(*row_sv)) {
        -: 5064:				         AV*restrict row_av = (AV*)SvRV(*row_sv);
        -: 5065:				         SV**restrict cell_sv = av_fetch(row_av, c, 0);
        -: 5066:				         col_data[r] = (cell_sv && SvOK(*cell_sv)) ? SvNV(*cell_sv) : 0.0;
        -: 5067:				     } else {
        -: 5068:				         col_data[r] = 0.0;
        -: 5069:				     }
        -: 5070:				     col_sum += col_data[r];
        -: 5071:				 }
        -: 5072:
        -: 5073:				 double col_center = do_center_mean ? (col_sum / nrow) : center_val;
        -: 5074:				 double col_scale = scale_val;
        -: 5075:				 // Calculate Standard Deviation for this specific column if needed
        -: 5076:				 if (do_scale_sd) {
        -: 5077:				     if (nrow <= 1) {
        -: 5078:				         Safefree(col_data);
        4: 5079:				         safefree(row_ptrs);
        4: 5080:				         croak("scale needs >= 2 rows to calculate standard deviation for a matrix column");
        4: 5080-block  0
        -: 5081:				     }
        -: 5082:				     double sum_sq = 0.0;
        -: 5083:				     for (size_t r = 0; r < nrow; r++) {
        -: 5084:				         double diff = col_data[r] - col_center;
        -: 5085:				         sum_sq += diff * diff;
function XS_Stats__LikeR_matrix called 4 returned 25% blocks executed 82%
        4: 5086:				     }
        -: 5087:				     col_scale = sqrt(sum_sq / (nrow - 1));
        4: 5088:				 }
        4: 5088-block  0
call    0 returned 4
branch  1 taken 0 (fallthrough)
branch  2 taken 4
        -: 5089:				 // Store scaled values back into the new matrix rows
        -: 5090:				 for (size_t r = 0; r < nrow; r++) {
        -: 5091:				     double centered = col_data[r] - col_center;
        -: 5092:				     double final_val = (col_scale == 0.0) ? (0.0 / 0.0) : (centered / col_scale);
        -: 5093:				     av_store(row_ptrs[r], c, newSVnv(final_val));
        -: 5094:				 }
        -: 5095:				 Safefree(col_data);
        -: 5096:			}
        -: 5097:			safefree(row_ptrs);
        -: 5098:			// Push the resulting matrix as a single Reference onto the Perl stack
        -: 5099:			EXTEND(SP, 1);
        -: 5100:			PUSHs(sv_2mortal(newRV_noinc((SV*)result_av)));
        -: 5101:		} else {
        -: 5102:			// ======================================
        -: 5103:			// FLAT LIST MODE: Original functionality
        -: 5104:			// ======================================
        -: 5105:			size_t total_count = 0, k = 0;
        -: 5106:			double *restrict nums;
        -: 5107:			double sum = 0.0;
        -: 5108:			for (size_t i = 0; i < data_items; i++) {
        -: 5109:				SV*restrict arg = ST(i);
        -: 5110:				if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 5111:					AV*restrict av = (AV*)SvRV(arg);
        -: 5112:					size_t len = av_len(av) + 1;
        -: 5113:					for (unsigned int j = 0; j < len; j++) {
        -: 5114:						SV**restrict tv = av_fetch(av, j, 0);
        -: 5115:						if (tv && SvOK(*tv)) { total_count++; }
        -: 5116:					}
        -: 5117:				} else if (SvOK(arg)) {
        -: 5118:					total_count++;
        -: 5119:				}
        -: 5120:			}
        -: 5121:			if (total_count == 0) croak("scale requires at least 1 numeric element");
        -: 5122:			Newx(nums, total_count, double);
        -: 5123:			for (size_t i = 0; i < data_items; i++) {
        -: 5124:				 SV*restrict arg = ST(i);
        -: 5125:				 if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 5126:				     AV*restrict av = (AV*)SvRV(arg);
        -: 5127:				     size_t len = av_len(av) + 1;
        -: 5128:				     for (size_t j = 0; j < len; j++) {
        -: 5129:				         SV**restrict tv = av_fetch(av, j, 0);
        -: 5130:				         if (tv && SvOK(*tv)) { 
        -: 5131:				             double val = SvNV(*tv);
        -: 5132:				             nums[k++] = val; sum += val;
        -: 5133:				         }
        -: 5134:				     }
        -: 5135:				 } else if (SvOK(arg)) {
        -: 5136:				     double val = SvNV(arg);
        -: 5137:				     nums[k++] = val; sum += val;
        -: 5138:				 }
        -: 5139:			}
        -: 5140:			if (do_center_mean) center_val = sum / total_count;
        -: 5141:			if (do_scale_sd) {
        -: 5142:				 if (total_count <= 1) {
        -: 5143:				     Safefree(nums);
        -: 5144:				     croak("scale needs >= 2 elements to calculate SD");
        -: 5145:				 }
        -: 5146:				 double sum_sq = 0.0;
        -: 5147:				 for (size_t i = 0; i < total_count; i++) {
        -: 5148:				     double diff = nums[i] - center_val;
        -: 5149:				     sum_sq += diff * diff;
        -: 5150:				 }
        -: 5151:				 scale_val = sqrt(sum_sq / (total_count - 1));
        -: 5152:			}
        -: 5153:			EXTEND(SP, total_count);
        -: 5154:			for (size_t i = 0; i < total_count; i++) {
        -: 5155:				double centered = nums[i] - center_val;
        -: 5156:				double final_val = (scale_val == 0.0) ? (0.0 / 0.0) : (centered / scale_val);
        -: 5157:				PUSHs(sv_2mortal(newSVnv(final_val)));
        -: 5158:			}
        -: 5159:			Safefree(nums); nums = NULL;
        -: 5160:		}
        -: 5161:	}
        -: 5162:#line 5163 "LikeR.c"
        -: 5163:	PUTBACK;
        -: 5164:	return;
        -: 5165:    }
        -: 5166:}
        -: 5167:
        -: 5168:
        1: 5169:XS_EUPXS(XS_Stats__LikeR_matrix); /* prototype to pass -Wmissing-prototypes */
call    0 returned 1
        1: 5170:XS_EUPXS(XS_Stats__LikeR_matrix)
        -: 5171:{
        1: 5172:    dVAR; dXSARGS;
        -: 5173:    PERL_UNUSED_VAR(cv); /* -W */
        -: 5174:    PERL_UNUSED_VAR(items); /* -W */
        -: 5175:    {
        -: 5176:	SV *	RETVAL;
function XS_Stats__LikeR_lm called 22 returned 73% blocks executed 80%
       22: 5177:#line 4653 "LikeR.xs"
        -: 5178:	// Basic check: must have an even number of arguments for key => value
       22: 5179:	if (items % 2 != 0) {
       22: 5179-block  0
call    0 returned 22
branch  1 taken 0 (fallthrough)
branch  2 taken 22
        -: 5180:	  croak("Usage: matrix(data => [...], nrow => $n, ncol => $m, byrow => $bool)");
        -: 5181:	}
        -: 5182:	SV*restrict data_sv = NULL;
        -: 5183:	size_t nrow = 0, ncol = 0;
        -: 5184:	bool byrow = FALSE, nrow_set = FALSE, ncol_set = FALSE;
        -: 5185:	// Parse named arguments
        -: 5186:	for (size_t i = 0; i < items; i += 2) {
        -: 5187:	  char*restrict key = SvPV_nolen(ST(i));
        -: 5188:	  SV*restrict val   = ST(i + 1);
        -: 5189:	  if (strEQ(key, "data")) {
        -: 5190:		   data_sv = val;
        -: 5191:	  } else if (strEQ(key, "nrow")) {
        -: 5192:		   nrow = (size_t)SvUV(val);
        -: 5193:		   nrow_set = TRUE;
        -: 5194:	  } else if (strEQ(key, "ncol")) {
        -: 5195:		   ncol = (size_t)SvUV(val);
        -: 5196:		   ncol_set = TRUE;
        -: 5197:	  } else if (strEQ(key, "byrow")) {
        -: 5198:		   byrow = SvTRUE(val);
        -: 5199:	  } else {
        -: 5200:		   croak("Unknown option: %s", key);
        -: 5201:	  }
        -: 5202:	}
        -: 5203:	// Validate data input
        -: 5204:	if (!data_sv || !SvROK(data_sv) || SvTYPE(SvRV(data_sv)) != SVt_PVAV) {
        -: 5205:	  croak("The 'data' option must be an array reference (e.g. data => [1..6])");
        -: 5206:	}
        -: 5207:	AV*restrict data_av = (AV*)SvRV(data_sv);
        -: 5208:	size_t data_len = (UV)(av_top_index(data_av) + 1);
        -: 5209:	if (data_len == 0) {
        -: 5210:	  croak("Data array cannot be empty");
        -: 5211:	}
        -: 5212:	// R-style dimension inference
        -: 5213:	if (!nrow_set && !ncol_set) {
        -: 5214:	  nrow = data_len;
        -: 5215:	  ncol = 1;
        -: 5216:	} else if (nrow_set && !ncol_set) {
        -: 5217:	  ncol = (data_len + nrow - 1) / nrow;
        -: 5218:	} else if (!nrow_set && ncol_set) {
        -: 5219:	  nrow = (data_len + ncol - 1) / ncol;
        -: 5220:	}
        -: 5221:	// Final safety check for dimensions
        -: 5222:	if (nrow == 0 || ncol == 0) {
        -: 5223:	  croak("Dimensions must be greater than 0");
        -: 5224:	}
        -: 5225:	// Create the matrix (Array of Arrays)
        -: 5226:	AV*restrict result_av = newAV();
        -: 5227:	av_extend(result_av, nrow - 1);
        -: 5228:	size_t r, c;// Use unsigned types for counters to prevent negative indexing
        -: 5229:	AV**restrict row_ptrs = (AV**restrict)safemalloc(nrow * sizeof(AV*)); /* Pre-allocate row pointers */
        -: 5230:	for (r = 0; r < nrow; r++) {
        -: 5231:	  row_ptrs[r] = newAV();
        -: 5232:	  av_extend(row_ptrs[r], ncol - 1);
        -: 5233:	  av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
        -: 5234:	}
        -: 5235:	// Fill the matrix
        -: 5236:	size_t total_cells = nrow * ncol;
        -: 5237:	for (size_t i = 0; i < total_cells; i++) {
        -: 5238:	  // Vector recycling logic
        -: 5239:	  SV**restrict fetched = av_fetch(data_av, i % data_len, 0);
        -: 5240:	  SV*restrict val = fetched ? newSVsv(*fetched) : newSV(0);
        -: 5241:	  if (byrow) {
        -: 5242:		   r = i / ncol;
        -: 5243:		   c = i % ncol;
        -: 5244:	  } else {
        -: 5245:		   r = i % nrow;
        -: 5246:		   c = i / nrow;
        -: 5247:	  }
        -: 5248:	  av_store(row_ptrs[r], c, val);
        -: 5249:	}
        -: 5250:	safefree(row_ptrs);
        -: 5251:	RETVAL = newRV_noinc((SV*)result_av);
        -: 5252:#line 5253 "LikeR.c"
        -: 5253:	RETVAL = sv_2mortal(RETVAL);
        -: 5254:	ST(0) = RETVAL;
        -: 5255:    }
        -: 5256:    XSRETURN(1);
        -: 5257:}
        -: 5258:
        -: 5259:
        -: 5260:XS_EUPXS(XS_Stats__LikeR_lm); /* prototype to pass -Wmissing-prototypes */
        -: 5261:XS_EUPXS(XS_Stats__LikeR_lm)
        -: 5262:{
        -: 5263:    dVAR; dXSARGS;
        -: 5264:    PERL_UNUSED_VAR(cv); /* -W */
        -: 5265:    PERL_UNUSED_VAR(items); /* -W */
        -: 5266:    {
        -: 5267:	SV *	RETVAL;
        -: 5268:#line 4732 "LikeR.xs"
        -: 5269:{
        -: 5270:	const char *restrict formula  = NULL;
        -: 5271:	SV *restrict data_sv = NULL;
        -: 5272:	char f_cpy[512];
        -: 5273:	char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
        -: 5274:
        -: 5275:	char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
        -: 5276:	bool *restrict is_dummy = NULL;
        -: 5277:	char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
        -: 5278:	unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
        -: 5279:	size_t n = 0, valid_n = 0, i, j, k, l, l1, l2;
        -: 5280:	bool has_intercept = TRUE;
        -: 5281:
        -: 5282:	char **restrict row_names = NULL, **restrict valid_row_names = NULL;
        -: 5283:	HV **restrict row_hashes = NULL;
        -: 5284:	HV *restrict data_hoa = NULL;
        -: 5285:	SV *restrict ref = NULL;
        -: 5286:
        -: 5287:	double *restrict X = NULL, *restrict Y = NULL, *restrict XtX = NULL, *restrict XtY = NULL;
        -: 5288:	bool *restrict aliased = NULL;
        -: 5289:	double *restrict beta = NULL;
        -: 5290:	int final_rank = 0, df_res = 0;
        -: 5291:	HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
        -: 5292:	AV *restrict terms_av;
        -: 5293:	double rss = 0.0, rse_sq = 0.0;
        -: 5294:	HE *restrict entry;
        -: 5295:
        -: 5296:	if (items % 2 != 0) croak("Usage: lm(formula => 'mpg ~ wt * hp', data => \\%%mtcars)");
        -: 5297:
        -: 5298:	for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
        -: 5299:	  const char *restrict key = SvPV_nolen(ST(i_arg));
        -: 5300:	  SV *restrict val = ST(i_arg + 1);
        -: 5301:	  if      (strEQ(key, "formula")) formula = SvPV_nolen(val);
        -: 5302:	  else if (strEQ(key, "data"))    data_sv = val;
        -: 5303:	  else croak("lm: unknown argument '%s'", key);
        -: 5304:	}
        -: 5305:	if (!formula) croak("lm: formula is required");
        -: 5306:	if (!data_sv || !SvROK(data_sv)) croak("lm: data is required and must be a reference");
        -: 5307:
        -: 5308:	// ========================================================================
        -: 5309:	// PHASE 1: Data Extraction
        -: 5310:	// ========================================================================
        -: 5311:	ref = SvRV(data_sv);
        -: 5312:	if (SvTYPE(ref) == SVt_PVHV) {
        -: 5313:	  HV *restrict hv = (HV*)ref;
        -: 5314:	  if (hv_iterinit(hv) == 0) croak("lm: Data hash is empty");
        -: 5315:	  entry = hv_iternext(hv);
        -: 5316:	  if (entry) {
        -: 5317:		   SV *restrict val = hv_iterval(hv, entry);
        -: 5318:		   if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
        -: 5319:		       data_hoa = hv;
        -: 5320:		       n = av_len((AV*)SvRV(val)) + 1;
        -: 5321:		       Newx(row_names, n, char*);
        -: 5322:		       for (i = 0; i < n; i++) {
        -: 5323:		           char buf[32];
        -: 5324:		           snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
        -: 5325:		           row_names[i] = savepv(buf);
        -: 5326:		       }
        -: 5327:		   } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
        -: 5328:		       n = hv_iterinit(hv);
        -: 5329:		       Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
        -: 5330:		       i = 0;
        -: 5331:		       while ((entry = hv_iternext(hv))) {
        -: 5332:		           I32 len;
        -: 5333:		           row_names[i] = savepv(hv_iterkey(entry, &len));
        -: 5334:		           row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
        -: 5335:		           i++;
        -: 5336:		       }
        -: 5337:		   } else croak("lm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
        -: 5338:	  }
        -: 5339:	} else if (SvTYPE(ref) == SVt_PVAV) {
        -: 5340:	  AV *restrict av = (AV*)ref; n = av_len(av) + 1;
        -: 5341:	  Newx(row_names, n, char*);
        -: 5342:	  Newx(row_hashes, n, HV*);
        -: 5343:	  for (i = 0; i < n; i++) {
        -: 5344:		   SV **restrict val = av_fetch(av, i, 0);
        -: 5345:		   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
        -: 5346:		       row_hashes[i] = (HV*)SvRV(*val);
        -: 5347:		       char buf[32]; snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
        -: 5348:		       row_names[i] = savepv(buf);
        -: 5349:		   } else {
        -: 5350:		       for (k = 0; k < i; k++) Safefree(row_names[k]);
        -: 5351:		       Safefree(row_names); Safefree(row_hashes);
        -: 5352:		       croak("lm: Array values must be HashRefs (AoH)");
        -: 5353:		   }
        -: 5354:	  }
        -: 5355:	} else croak("lm: Data must be an Array or Hash reference");
        -: 5356:
        -: 5357:	// ========================================================================
        -: 5358:	// PHASE 2: Formula Parsing & `.` Expansion
        -: 5359:	// ========================================================================
        -: 5360:	src = (char*)formula; dst = f_cpy;
        -: 5361:	while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
        -: 5362:	*dst = '\0';
        -: 5363:
        -: 5364:	tilde = strchr(f_cpy, '~');
        -: 5365:	if (!tilde) {
        -: 5366:	  for (i = 0; i < n; i++) Safefree(row_names[i]);
        -: 5367:	  Safefree(row_names); if (row_hashes) Safefree(row_hashes);
        -: 5368:	  croak("lm: invalid formula, missing '~'");
        -: 5369:	}
        -: 5370:	*tilde = '\0';
        -: 5371:	lhs = f_cpy;
        -: 5372:	rhs = tilde + 1;
        -: 5373:
        -: 5374:	// Remove intercept-suppression markers from RHS.
        -: 5375:	// IMPORTANT: skip tokens that appear inside I(...) wrappers so that
        -: 5376:	// expressions like I(x^-1) are never mistakenly treated as "-1".
        -: 5377:	{
        -: 5378:	  char *restrict p_idx = rhs;
        -: 5379:	  while (*p_idx) {
        -: 5380:		   // Skip over I(...) sub-expressions entirely
        -: 5381:		   if (p_idx[0] == 'I' && p_idx[1] == '(') {
        -: 5382:		       int depth = 0;
        -: 5383:		       while (*p_idx) { if (*p_idx == '(') depth++; else if (*p_idx == ')') { depth--; if (depth == 0) { p_idx++; break; } } p_idx++; }
        -: 5384:		       continue;
        -: 5385:		   }
        -: 5386:		   // Match bare -1
        -: 5387:		   if (p_idx[0] == '-' && p_idx[1] == '1' &&
        -: 5388:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
        -: 5389:		       has_intercept = FALSE;
        -: 5390:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
        -: 5391:		       continue; // re-examine same position
        -: 5392:		   }
        -: 5393:		   // Match +0
        -: 5394:		   if (p_idx[0] == '+' && p_idx[1] == '0' &&
        -: 5395:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
        -: 5396:		       has_intercept = FALSE;
        -: 5397:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
        -: 5398:		       continue;
        -: 5399:		   }
        -: 5400:		   // Match leading 0+
        -: 5401:		   if (p_idx == rhs && p_idx[0] == '0' && p_idx[1] == '+') {
        -: 5402:		       has_intercept = FALSE;
        -: 5403:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
        -: 5404:		       continue;
        -: 5405:		   }
        -: 5406:		   // Match bare 0 (entire rhs)
        -: 5407:		   if (p_idx == rhs && p_idx[0] == '0' && p_idx[1] == '\0') {
        -: 5408:		       has_intercept = FALSE; p_idx[0] = '\0'; break;
        -: 5409:		   }
        -: 5410:		   // Strip redundant +1 (keep intercept, just remove marker)
        -: 5411:		   if (p_idx[0] == '+' && p_idx[1] == '1' &&
        -: 5412:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
        -: 5413:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
        -: 5414:		       continue;
        -: 5415:		   }
        -: 5416:		   // Strip leading bare 1 or 1+
        -: 5417:		   if (p_idx == rhs) {
        -: 5418:		       if (p_idx[0] == '1' && p_idx[1] == '\0') { p_idx[0] = '\0'; break; }
        -: 5419:		       if (p_idx[0] == '1' && p_idx[1] == '+') { memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); continue; }
        -: 5420:		   }
        -: 5421:		   p_idx++;
        -: 5422:	  }
        -: 5423:	}
        -: 5424:
        -: 5425:	// Clean up stray `++`, leading `+`, trailing `+`
        -: 5426:	{
        -: 5427:	  char *restrict p_idx;
        -: 5428:	  while ((p_idx = strstr(rhs, "++")) != NULL)
        -: 5429:		   memmove(p_idx, p_idx + 1, strlen(p_idx + 1) + 1);
        -: 5430:	  if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
        -: 5431:	  size_t len_rhs = strlen(rhs);
        -: 5432:	  if (len_rhs > 0 && rhs[len_rhs - 1] == '+') rhs[len_rhs - 1] = '\0';
        -: 5433:	}
        -: 5434:
        -: 5435:	// Expand `.` Operator
        -: 5436:	char rhs_expanded[2048] = "";
        -: 5437:	size_t rhs_len = 0;
        -: 5438:	chunk = strtok(rhs, "+");
        -: 5439:	while (chunk != NULL) {
        -: 5440:	  if (strcmp(chunk, ".") == 0) {
        -: 5441:		   AV *cols = get_all_columns(data_hoa, row_hashes, n);
        -: 5442:		   for (size_t c = 0; c <= (size_t)av_len(cols); c++) {
        -: 5443:		       SV **col_sv = av_fetch(cols, c, 0);
        -: 5444:		       if (col_sv && SvOK(*col_sv)) {
        -: 5445:		           const char *col_name = SvPV_nolen(*col_sv);
        -: 5446:		           if (strcmp(col_name, lhs) != 0) {
        -: 5447:		               size_t slen = strlen(col_name);
        -: 5448:		               if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
        -: 5449:		                   if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        -: 5450:		                   strcat(rhs_expanded, col_name);
        -: 5451:		                   rhs_len += slen;
        -: 5452:		               }
        -: 5453:		           }
        -: 5454:		       }
        -: 5455:		   }
        -: 5456:		   SvREFCNT_dec(cols);
        -: 5457:	  } else {
        -: 5458:		   size_t slen = strlen(chunk);
        -: 5459:		   if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
        -: 5460:		       if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        -: 5461:		       strcat(rhs_expanded, chunk);
        -: 5462:		       rhs_len += slen;
        -: 5463:		   }
        -: 5464:	  }
        -: 5465:	  chunk = strtok(NULL, "+");
        -: 5466:	}
        -: 5467:
        -: 5468:	Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
        -: 5469:	Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
        -: 5470:	Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
        -: 5471:
        -: 5472:	if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
        -: 5473:
        -: 5474:	if (strlen(rhs_expanded) > 0) {
        -: 5475:	  chunk = strtok(rhs_expanded, "+");
        -: 5476:	  while (chunk != NULL) {
        -: 5477:		   if (num_terms >= term_cap - 3) {
        -: 5478:		       term_cap *= 2;
        -: 5479:		       Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
        -: 5480:		   }
        -: 5481:		   char *restrict star = strchr(chunk, '*');
        -: 5482:		   if (star) {
        -: 5483:		       *star = '\0';
        -: 5484:		       char *restrict left = chunk;
        -: 5485:		       char *restrict right = star + 1;
        -: 5486:		       char *restrict c_l = strchr(left, '^');
        -: 5487:		       if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
        -: 5488:		       char *restrict c_r = strchr(right, '^');
        -: 5489:		       if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
        -: 5490:		       terms[num_terms++] = savepv(left);
        -: 5491:		       terms[num_terms++] = savepv(right);
        -: 5492:		       size_t inter_len = strlen(left) + strlen(right) + 2;
        -: 5493:		       terms[num_terms] = (char*)safemalloc(inter_len);
        -: 5494:		       snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
        -: 5495:		   } else {
        -: 5496:		       char *restrict c_chunk = strchr(chunk, '^');
        -: 5497:		       if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
        -: 5498:		       terms[num_terms++] = savepv(chunk);
        -: 5499:		   }
        -: 5500:		   chunk = strtok(NULL, "+");
        -: 5501:	  }
        -: 5502:	}
        -: 5503:
        -: 5504:	for (i = 0; i < num_terms; i++) {
        -: 5505:	  bool found = FALSE;
        -: 5506:	  for (j = 0; j < num_uniq; j++) { if (strcmp(terms[i], uniq_terms[j]) == 0) { found = TRUE; break; } }
        -: 5507:	  if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
        -: 5508:	}
        -: 5509:	p = num_uniq;
        -: 5510:
        -: 5511:	// ========================================================================
        -: 5512:	// PHASE 3: Categorical Expansion
        -: 5513:	// ========================================================================
        -: 5514:	for (j = 0; j < p; j++) {
        -: 5515:	  if (p_exp + 32 >= exp_cap) {
        -: 5516:		   exp_cap *= 2;
        -: 5517:		   Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
        -: 5518:		   Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 5519:	  }
        -: 5520:	  if (strcmp(uniq_terms[j], "Intercept") == 0) {
        -: 5521:		   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = FALSE; p_exp++; continue;
        -: 5522:	  }
        -: 5523:	  if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
        -: 5524:		   char **restrict levels = NULL;
        -: 5525:		   unsigned int num_levels = 0, levels_cap = 8;
        -: 5526:		   Newx(levels, levels_cap, char*);
        -: 5527:		   for (i = 0; i < n; i++) {
        -: 5528:		       char *str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
        -: 5529:		       if (str_val) {
        -: 5530:		           bool found = FALSE;
        -: 5531:		           for (l = 0; l < num_levels; l++) { if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; } }
        -: 5532:		           if (!found) {
        -: 5533:		               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
        -: 5534:		               levels[num_levels++] = savepv(str_val);
        -: 5535:		           }
        -: 5536:		           Safefree(str_val);
        -: 5537:		       }
        -: 5538:		   }
        -: 5539:		   if (num_levels > 0) {
        -: 5540:		       for (l1 = 0; l1 < num_levels - 1; l1++)
        -: 5541:		           for (l2 = l1 + 1; l2 < num_levels; l2++)
        -: 5542:		               if (strcmp(levels[l1], levels[l2]) > 0) { char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp; }
        -: 5543:		       for (l = 1; l < num_levels; l++) {
        -: 5544:		           if (p_exp >= exp_cap) {
        -: 5545:		               exp_cap *= 2;
        -: 5546:		               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
        -: 5547:		               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 5548:		           }
        -: 5549:		           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
        -: 5550:		           exp_terms[p_exp] = (char*)safemalloc(t_len);
        -: 5551:		           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
        -: 5552:		           is_dummy[p_exp] = TRUE;
        -: 5553:		           dummy_base[p_exp]  = savepv(uniq_terms[j]);
        -: 5554:		           dummy_level[p_exp] = savepv(levels[l]);
        -: 5555:		           p_exp++;
        -: 5556:		       }
        -: 5557:		       for (l = 0; l < num_levels; l++) Safefree(levels[l]);
        -: 5558:		       Safefree(levels);
        -: 5559:		   } else {
        -: 5560:		       Safefree(levels);
        -: 5561:		       exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
        -: 5562:		   }
        -: 5563:	  } else {
        -: 5564:		   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
        -: 5565:	  }
        -: 5566:	}
        -: 5567:	p = p_exp;
        -: 5568:	Newx(X, n * p, double); Newx(Y, n, double);
        -: 5569:	Newx(valid_row_names, n, char*);
        -: 5570:
        -: 5571:	// ========================================================================
        -: 5572:	// PHASE 4: Matrix Construction & Listwise Deletion
        -: 5573:	// ========================================================================
        -: 5574:	for (i = 0; i < n; i++) {
        -: 5575:	  double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
        -: 5576:	  if (isnan(y_val)) { Safefree(row_names[i]); continue; }
        -: 5577:
        -: 5578:	  bool row_ok = TRUE;
        -: 5579:	  double *restrict row_x = (double*)safemalloc(p * sizeof(double));
        -: 5580:	  for (j = 0; j < p; j++) {
        -: 5581:		   if (strcmp(exp_terms[j], "Intercept") == 0) {
        -: 5582:		       row_x[j] = 1.0;
        -: 5583:		   } else if (is_dummy[j]) {
        -: 5584:		       char *restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
        -: 5585:		       if (str_val) {
        -: 5586:		           row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
        -: 5587:		           Safefree(str_val);
        -: 5588:		       } else { row_ok = FALSE; break; }
        -: 5589:		   } else {
        -: 5590:		       row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
        -: 5591:		       if (isnan(row_x[j])) { row_ok = FALSE; break; }
        -: 5592:		   }
        -: 5593:	  }
        -: 5594:	  if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
        -: 5595:
        -: 5596:	  Y[valid_n] = y_val;
        -: 5597:	  for (j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
        -: 5598:	  valid_row_names[valid_n] = row_names[i];
        -: 5599:	  valid_n++;
        -: 5600:	  Safefree(row_x);
        -: 5601:	}
        -: 5602:	Safefree(row_names);
        -: 5603:
        -: 5604:	if (valid_n <= p) {
        -: 5605:	  for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
        -: 5606:	  for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
        -: 5607:	  for (j = 0; j < p_exp; j++) {
        -: 5608:		   Safefree(exp_terms[j]);
        -: 5609:		   if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
        -: 5610:	  }
        -: 5611:	  Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
        -: 5612:	  Safefree(X); Safefree(Y); Safefree(valid_row_names);
        -: 5613:	  if (row_hashes) Safefree(row_hashes);
        -: 5614:	  croak("lm: 0 degrees of freedom (too many NAs or parameters > observations)");
        -: 5615:	}
        -: 5616:
        -: 5617:	// ========================================================================
        -: 5618:	// PHASE 5: OLS Math
        -: 5619:	// ========================================================================
        -: 5620:	Newxz(XtX, p * p, double);
        -: 5621:	for (i = 0; i < p; i++)
        -: 5622:	  for (j = 0; j < p; j++) {
        -: 5623:		   double sum = 0.0;
        -: 5624:		   for (k = 0; k < valid_n; k++) sum += X[k * p + i] * X[k * p + j];
        -: 5625:		   XtX[i * p + j] = sum;
        -: 5626:	  }
        -: 5627:	Newxz(XtY, p, double);
        -: 5628:	for (i = 0; i < p; i++) {
        -: 5629:	  double sum = 0.0;
        -: 5630:	  for (k = 0; k < valid_n; k++) sum += X[k * p + i] * Y[k];
        -: 5631:	  XtY[i] = sum;
        -: 5632:	}
        -: 5633:	Newx(aliased, p, bool);
        -: 5634:	final_rank = sweep_matrix_ols(XtX, p, aliased);
        -: 5635:	Newxz(beta, p, double);
        -: 5636:	for (i = 0; i < p; i++) {
        -: 5637:	  if (aliased[i]) { beta[i] = NAN; }
        -: 5638:	  else {
        -: 5639:		   double sum = 0.0;
        -: 5640:		   for (j = 0; j < p; j++) if (!aliased[j]) sum += XtX[i * p + j] * XtY[j];
        -: 5641:		   beta[i] = sum;
        -: 5642:	  }
        -: 5643:	}
        -: 5644:
        -: 5645:	// ========================================================================
        -: 5646:	// PHASE 6: Metrics & Cleanup
        -: 5647:	// ========================================================================
        -: 5648:	res_hv = newHV(); coef_hv = newHV(); fitted_hv = newHV(); resid_hv = newHV();
        -: 5649:	summary_hv = newHV(); terms_av = newAV();
        -: 5650:
        -: 5651:	df_res = (int)valid_n - final_rank;
        -: 5652:
        -: 5653:	// rss / mss accumulated here — rse_sq computed AFTER this loop (not before)
        -: 5654:	double sum_y = 0.0, mss = 0.0;
        -: 5655:	for (i = 0; i < valid_n; i++) sum_y += Y[i];
        -: 5656:	double mean_y = sum_y / (double)valid_n;
        -: 5657:
        -: 5658:	for (i = 0; i < valid_n; i++) {
        -: 5659:	  double y_hat = 0.0;
        -: 5660:	  for (j = 0; j < p; j++) if (!aliased[j]) y_hat += X[i * p + j] * beta[j];
        -: 5661:	  double res   = Y[i] - y_hat;
        -: 5662:	  rss          += res * res;
       16: 5663:	  double diff_m = has_intercept ? (y_hat - mean_y) : y_hat;
call    0 returned 16
       16: 5664:	  mss          += diff_m * diff_m;
        -: 5665:	  hv_store(fitted_hv, valid_row_names[i], strlen(valid_row_names[i]), newSVnv(y_hat), 0);
       16: 5666:	  hv_store(resid_hv,  valid_row_names[i], strlen(valid_row_names[i]), newSVnv(res),   0);
        -: 5667:	  Safefree(valid_row_names[i]);
        -: 5668:	}
        -: 5669:	Safefree(valid_row_names);
        -: 5670:
function XS_Stats__LikeR_seq called 6 returned 100% blocks executed 67%
        6: 5671:	// Single, authoritative rse_sq calculation
        -: 5672:	rse_sq = (df_res > 0) ? (rss / (double)df_res) : NAN;
        6: 5673:
        6: 5673-block  0
call    0 returned 6
        6: 5674:	int df_int = has_intercept ? 1 : 0;
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5674-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    #####: 5675:	double r_squared = 0.0, adj_r_squared = 0.0, f_stat = NAN, f_pvalue = NAN;
    %%%%%: 5675-block  0
call    0 never executed
        -: 5676:	int numdf = final_rank - df_int;
        6: 5677:
        -: 5678:	if (final_rank != df_int && (mss + rss) > 0.0) {
        6: 5679:	  r_squared     = mss / (mss + rss);
        6: 5679-block  0
call    0 returned 6
        -: 5680:	  adj_r_squared = 1.0 - (1.0 - r_squared) * ((valid_n - df_int) / (double)df_res);
        6: 5681:	  if (rse_sq > 0.0 && numdf > 0) {
call    0 returned 6
        -: 5682:		   f_stat   = (mss / (double)numdf) / rse_sq;
        -: 5683:		   f_pvalue = 1.0 - pf(f_stat, (double)numdf, (double)df_res);
        -: 5684:	  } else if (rse_sq == 0.0) {
        6: 5685:		   f_stat   = INFINITY;
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1: 5686:		   f_pvalue = 0.0;
        1: 5686-block  0
        -: 5687:	  }
        5: 5688:	} else if (final_rank == df_int) {
        5: 5688-block  0
call    0 returned 5
        -: 5689:	  r_squared = 0.0; adj_r_squared = 0.0;
        -: 5690:	}
        -: 5691:
        -: 5692:	for (j = 0; j < p; j++) {
        -: 5693:	  hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
        -: 5694:	  av_push(terms_av, newSVpv(exp_terms[j], 0));
        -: 5695:	  HV *restrict row_hv = newHV();
        -: 5696:	  if (aliased[j]) {
        -: 5697:		   hv_store(row_hv, "Estimate",   8,  newSVpv("NaN", 0), 0);
        -: 5698:		   hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
        -: 5699:		   hv_store(row_hv, "t value",    7,  newSVpv("NaN", 0), 0);
        -: 5700:		   hv_store(row_hv, "Pr(>|t|)",   8,  newSVpv("NaN", 0), 0);
        -: 5701:	  } else {
        -: 5702:		   double se    = sqrt(rse_sq * XtX[j * p + j]);
        -: 5703:		   double t_val = (se > 0.0) ? (beta[j] / se) : (INFINITY * (beta[j] >= 0.0 ? 1.0 : -1.0));
        -: 5704:		   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
        -: 5705:		   hv_store(row_hv, "Estimate",   8,  newSVnv(beta[j]), 0);
        -: 5706:		   hv_store(row_hv, "Std. Error", 10, newSVnv(se),      0);
        -: 5707:		   hv_store(row_hv, "t value",    7,  newSVnv(t_val),   0);
        -: 5708:		   hv_store(row_hv, "Pr(>|t|)",   8,  newSVnv(p_val),   0);
        -: 5709:	  }
        -: 5710:	  hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
        -: 5711:	}
        -: 5712:
        -: 5713:	hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv),   0);
        -: 5714:	hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
        -: 5715:	hv_store(res_hv, "residuals",      9, newRV_noinc((SV*)resid_hv),  0);
        -: 5716:	hv_store(res_hv, "df.residual",   11, newSVuv(df_res),             0);
        -: 5717:	hv_store(res_hv, "rank",           4, newSVuv(final_rank),         0);
        -: 5718:	hv_store(res_hv, "rss",            3, newSVnv(rss),                0);
        -: 5719:	hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv),0);
        -: 5720:	hv_store(res_hv, "terms",          5, newRV_noinc((SV*)terms_av),  0);
        -: 5721:	hv_store(res_hv, "r.squared",      9, newSVnv(r_squared),          0);
        -: 5722:	hv_store(res_hv, "adj.r.squared", 13, newSVnv(adj_r_squared),      0);
        -: 5723:	if (!isnan(f_stat)) {
        -: 5724:	  AV *fstat_av = newAV();
        -: 5725:	  av_push(fstat_av, newSVnv(f_stat));
        -: 5726:	  av_push(fstat_av, newSViv(numdf));
        -: 5727:	  av_push(fstat_av, newSViv(df_res));
        -: 5728:	  hv_store(res_hv, "fstatistic", 10, newRV_noinc((SV*)fstat_av), 0);
function XS_Stats__LikeR_rnorm called 2 returned 50% blocks executed 87%
        2: 5729:	  hv_store(res_hv, "f.pvalue",    8, newSVnv(f_pvalue),          0);
        -: 5730:	}
        2: 5731:
        2: 5731-block  0
call    0 returned 2
branch  1 taken 1 (fallthrough)
branch  2 taken 1
        -: 5732:	// Deep Cleanup
        -: 5733:	for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
        -: 5734:	for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
        -: 5735:	for (j = 0; j < p_exp; j++) {
        -: 5736:	  Safefree(exp_terms[j]);
        -: 5737:	  if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
        -: 5738:	}
        -: 5739:	Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
        -: 5740:	Safefree(X); Safefree(Y); Safefree(XtX); Safefree(XtY);
        -: 5741:	Safefree(beta); Safefree(aliased);
        -: 5742:	if (row_hashes) Safefree(row_hashes);
        -: 5743:
        -: 5744:	RETVAL = newRV_noinc((SV*)res_hv);
        -: 5745:}
        -: 5746:#line 5747 "LikeR.c"
        -: 5747:	RETVAL = sv_2mortal(RETVAL);
        -: 5748:	ST(0) = RETVAL;
        -: 5749:    }
        -: 5750:    XSRETURN(1);
        -: 5751:}
        -: 5752:
        -: 5753:
        -: 5754:XS_EUPXS(XS_Stats__LikeR_seq); /* prototype to pass -Wmissing-prototypes */
        -: 5755:XS_EUPXS(XS_Stats__LikeR_seq)
        -: 5756:{
        -: 5757:    dVAR; dXSARGS;
        -: 5758:    if (items < 2 || items > 3)
        -: 5759:       croak_xs_usage(cv,  "from, to, by = 1.0");
        -: 5760:    PERL_UNUSED_VAR(ax); /* -Wall */
        -: 5761:    SP -= items;
        -: 5762:    {
        -: 5763:	double	from = (double)SvNV(ST(0))
        -: 5764:;
        -: 5765:	double	to = (double)SvNV(ST(1))
        -: 5766:;
        -: 5767:	double	by;
        -: 5768:
        -: 5769:	if (items < 3)
        -: 5770:	    by = 1.0;
        -: 5771:	else {
        -: 5772:	    by = (double)SvNV(ST(2))
        -: 5773:;
        -: 5774:	}
        -: 5775:#line 5217 "LikeR.xs"
        -: 5776:	{
        -: 5777:		//Handle the zero 'by' case
        -: 5778:		if (by == 0.0) {
        -: 5779:			if (from == to) {
        -: 5780:				 EXTEND(SP, 1);
        -: 5781:				 mPUSHn(from);
        -: 5782:				 XSRETURN(1);
        -: 5783:			} else {
        -: 5784:				 croak("invalid 'by' argument: cannot be zero when from != to");
        -: 5785:			}
        -: 5786:		}
        -: 5787:		// Check for wrong direction / infinite loop
        -: 5788:		if ((from < to && by < 0.0) || (from > to && by > 0.0)) {
        -: 5789:			croak("wrong sign in 'by' argument");
        -: 5790:		}
        -: 5791:		/* * Calculate number of elements. 
        1: 5792:		* R uses a small epsilon (like 1e-10) to avoid dropping the last 
call    0 returned 1
        1: 5793:		* element due to floating point inaccuracies.
        -: 5794:		*/
        1: 5795:		double n_elements_d = (to - from) / by;
        -: 5796:		if (n_elements_d < 0.0) n_elements_d = 0.0;
        -: 5797:		size_t n_elements = (n_elements_d + 1e-10) + 1;
        -: 5798:		// Pre-extend the stack to avoid reallocating inside the loop
        -: 5799:		EXTEND(SP, n_elements);
function XS_Stats__LikeR_aov called 9 returned 67% blocks executed 79%
        9: 5800:		for (size_t i = 0; i < n_elements; i++) {
        -: 5801:			mPUSHn(from + i * by);
        9: 5802:		}
        9: 5802-block  0
call    0 returned 9
        9: 5803:		XSRETURN(n_elements);
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    #####: 5804:	}
    %%%%%: 5804-block  0
call    0 never executed
        -: 5805:#line 5806 "LikeR.c"
        9: 5806:	PUTBACK;
        -: 5807:	return;
        9: 5808:    }
        9: 5808-block  0
call    0 returned 9
        -: 5809:}
        -: 5810:
        -: 5811:
        -: 5812:XS_EUPXS(XS_Stats__LikeR_rnorm); /* prototype to pass -Wmissing-prototypes */
        -: 5813:XS_EUPXS(XS_Stats__LikeR_rnorm)
        -: 5814:{
        -: 5815:    dVAR; dXSARGS;
        -: 5816:    PERL_UNUSED_VAR(cv); /* -W */
        -: 5817:    PERL_UNUSED_VAR(items); /* -W */
        -: 5818:    {
        -: 5819:	SV *	RETVAL;
        -: 5820:#line 5249 "LikeR.xs"
        -: 5821:	{
        -: 5822:	  // Auto-seed the PRNG if the Perl script hasn't done so yet
        -: 5823:	  AUTO_SEED_PRNG();
        -: 5824:
        -: 5825:	  size_t n = 0;
        -: 5826:	  double mean = 0.0, sd = 1.0;
        -: 5827:	  int arg_start = 0;
        -: 5828:
        -: 5829:	  // Check if the first argument is a simple integer (rnorm(33))
        -: 5830:	  if (items > 0 && SvIOK(ST(0)) && (items == 1 || items % 2 != 0)) {
        -: 5831:		   n = (unsigned int)SvUV(ST(0));
        -: 5832:		   arg_start = 1; // Start parsing named arguments from the second element
        -: 5833:	  }
        -: 5834:
        -: 5835:	  // --- Parse remaining named arguments from the flat stack ---
        -: 5836:	  if ((items - arg_start) % 2 != 0) {
        -: 5837:		   croak("Usage: rnorm(n), rnorm(n => 10, mean => 0, sd => 1), or rnorm(33, mean => 0)");
        -: 5838:	  }
        -: 5839:
        -: 5840:	  for (int i = arg_start; i < items; i += 2) {
        -: 5841:		   const char* restrict key = SvPV_nolen(ST(i));
        -: 5842:		   SV* restrict val = ST(i + 1);
        -: 5843:
        -: 5844:		   if      (strEQ(key, "n"))    n    = (unsigned int)SvUV(val);
        -: 5845:		   else if (strEQ(key, "mean")) mean = SvNV(val);
        -: 5846:		   else if (strEQ(key, "sd"))   sd   = SvNV(val);
        -: 5847:		   else croak("rnorm: unknown argument '%s'", key);
        -: 5848:	  }
        -: 5849:
        -: 5850:	  if (sd < 0.0) croak("rnorm: standard deviation must be non-negative");
        -: 5851:
        -: 5852:	  AV *restrict result_av = newAV();
        -: 5853:	  if (n > 0) {
        -: 5854:		   av_extend(result_av, n - 1);
        -: 5855:		   // Generate random normals using the Box-Muller transform
        -: 5856:		   for (size_t i = 0; i < n; ) {
        -: 5857:		        double u, v, s;
        -: 5858:		        do {
        -: 5859:		            // Drand01() hooks into Perl's internal PRNG, respecting Perl's srand()
        -: 5860:		            u = 2.0 * Drand01() - 1.0;
        -: 5861:		            v = 2.0 * Drand01() - 1.0;
        -: 5862:		            s = u * u + v * v;
        -: 5863:		        } while (s >= 1.0 || s == 0.0);
        -: 5864:
        -: 5865:		        double mul = sqrt(-2.0 * log(s) / s);
        -: 5866:		        // Box-Muller generates two independent values per iteration
        -: 5867:		        av_store(result_av, i++, newSVnv(mean + sd * u * mul));
        -: 5868:		        if (i < n) {
        -: 5869:		            av_store(result_av, i++, newSVnv(mean + sd * v * mul));
        -: 5870:		        }
        -: 5871:		   }
        -: 5872:	  }
        -: 5873:	  RETVAL = newRV_noinc((SV*)result_av);
        -: 5874:	}
        -: 5875:#line 5876 "LikeR.c"
        -: 5876:	RETVAL = sv_2mortal(RETVAL);
        -: 5877:	ST(0) = RETVAL;
        -: 5878:    }
        -: 5879:    XSRETURN(1);
        -: 5880:}
        -: 5881:
        -: 5882:
        -: 5883:XS_EUPXS(XS_Stats__LikeR_aov); /* prototype to pass -Wmissing-prototypes */
        -: 5884:XS_EUPXS(XS_Stats__LikeR_aov)
        -: 5885:{
        -: 5886:    dVAR; dXSARGS;
        -: 5887:    if (items != 2)
        -: 5888:       croak_xs_usage(cv,  "data_sv, formula_sv");
        -: 5889:    {
        -: 5890:	SV*	data_sv = ST(0)
        -: 5891:;
        -: 5892:	SV*	formula_sv = ST(1)
        -: 5893:;
        -: 5894:	SV *	RETVAL;
        -: 5895:#line 5310 "LikeR.xs"
        -: 5896:	{
        -: 5897:	  const char *restrict formula = SvPV_nolen(formula_sv);
        -: 5898:	  char f_cpy[512];
        -: 5899:	  char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
        -: 5900:
        -: 5901:	  char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL, **restrict parent_term = NULL;
        -: 5902:	  bool *restrict is_dummy = NULL, *is_interact = NULL;
        -: 5903:	  char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
        -: 5904:	  int *restrict term_map = NULL, *restrict left_idx = NULL, *restrict right_idx = NULL;
        -: 5905:	  unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
        -: 5906:	  size_t n = 0, valid_n = 0, i, j;
        -: 5907:	  bool has_intercept = TRUE;
        -: 5908:
        -: 5909:	  char **restrict row_names = NULL;
        -: 5910:	  HV **restrict row_hashes = NULL;
        -: 5911:	  HV *restrict data_hoa = NULL;
        -: 5912:	  SV *restrict ref = NULL;
        -: 5913:	  HE *restrict entry;
        -: 5914:	  double **restrict X_mat = NULL;
        -: 5915:	  double *restrict Y = NULL;
        -: 5916:
        -: 5917:	  char **restrict term_base_level = NULL;  /* reference level for each uniq_term (NULL if not categorical) */
        -: 5918:	  if (!SvROK(data_sv)) croak("aov: data is required and must be a reference");
        -: 5919:	  // ========================================================================
        -: 5920:	  // PHASE 1: Data Extraction
        -: 5921:	  // ========================================================================
        -: 5922:	  ref = SvRV(data_sv);
        -: 5923:	  if (SvTYPE(ref) == SVt_PVHV) {
        -: 5924:		   HV*restrict hv = (HV*)ref;
        -: 5925:		   if (hv_iterinit(hv) == 0) croak("aov: Data hash is empty");
        -: 5926:		   entry = hv_iternext(hv);
        -: 5927:		   if (entry) {
        -: 5928:		        SV*restrict val = hv_iterval(hv, entry);
        -: 5929:		        if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
        -: 5930:		            data_hoa = hv;
        -: 5931:		            n = av_len((AV*)SvRV(val)) + 1;
        -: 5932:		            Newx(row_names, n, char*);
        -: 5933:		            for(i = 0; i < n; i++) { 
        -: 5934:		                char buf[32]; snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i+1));
        -: 5935:		                row_names[i] = savepv(buf); 
        -: 5936:		            }
        -: 5937:		        } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
        -: 5938:		            n = hv_iterinit(hv);
        -: 5939:		            Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
        -: 5940:		            i = 0;
        -: 5941:		            while ((entry = hv_iternext(hv))) {
        -: 5942:		                I32 len;
        -: 5943:		                row_names[i] = savepv(hv_iterkey(entry, &len));
        -: 5944:		                row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
        -: 5945:		                i++;
        -: 5946:		            }
        -: 5947:		        } else croak("aov: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
        -: 5948:		   }
        -: 5949:	  } else if (SvTYPE(ref) == SVt_PVAV) {
        -: 5950:		   AV*restrict av = (AV*)ref;
        -: 5951:		   n = av_len(av) + 1;
        -: 5952:		   Newx(row_names, n, char*);
        -: 5953:		   Newx(row_hashes, n, HV*);
        -: 5954:		   for (i = 0; i < n; i++) {
        -: 5955:		        SV**restrict val = av_fetch(av, i, 0);
        -: 5956:		        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
        -: 5957:		            row_hashes[i] = (HV*)SvRV(*val);
        -: 5958:		            char buf[32];
        -: 5959:		            snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
        -: 5960:		            row_names[i] = savepv(buf);
        -: 5961:		        } else {
        -: 5962:		            for (size_t k = 0; k < i; k++) Safefree(row_names[k]);
        -: 5963:		            Safefree(row_names); Safefree(row_hashes);
        -: 5964:		            croak("aov: Array values must be HashRefs (AoH)");
        -: 5965:		        }
        -: 5966:		   }
        -: 5967:	  } else croak("aov: Data must be an Array or Hash reference");
        -: 5968:
        -: 5969:	  // ========================================================================
        -: 5970:	  // PHASE 2: Formula Parsing & `.` Expansion
        -: 5971:	  // ========================================================================
        -: 5972:	  src = (char*)formula; dst = f_cpy;
        -: 5973:	  while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
        -: 5974:	  *dst = '\0';
        -: 5975:
        -: 5976:	  tilde = strchr(f_cpy, '~');
        -: 5977:	  if (!tilde) {
        -: 5978:		   for (i = 0; i < n; i++) Safefree(row_names[i]);
        -: 5979:		   Safefree(row_names); if (row_hashes) Safefree(row_hashes);
        -: 5980:		   croak("aov: invalid formula, missing '~'");
        -: 5981:	  }
        -: 5982:	  *tilde = '\0';
        -: 5983:	  lhs = f_cpy;
        -: 5984:	  rhs = tilde + 1;
        -: 5985:
        -: 5986:	  char *restrict p_idx;
        -: 5987:	  while ((p_idx = strstr(rhs, "-1")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        -: 5988:	  while ((p_idx = strstr(rhs, "+0")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        -: 5989:	  while ((p_idx = strstr(rhs, "0+")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        -: 5990:	  if (rhs[0] == '0' && rhs[1] == '\0')        { has_intercept = FALSE; rhs[0] = '\0'; }
        -: 5991:	  while ((p_idx = strstr(rhs, "+1")) != NULL) { memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        -: 5992:	  if (rhs[0] == '1' && rhs[1] == '\0')        { rhs[0] = '\0'; } 
        -: 5993:	  else if (rhs[0] == '1' && rhs[1] == '+')    { memmove(rhs, rhs + 2, strlen(rhs + 2) + 1); }
        -: 5994:
        -: 5995:	  while ((p_idx = strstr(rhs, "++")) != NULL) memmove(p_idx, p_idx + 1, strlen(p_idx + 1) + 1);
        -: 5996:	  if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
        -: 5997:	  size_t len_rhs = strlen(rhs);
        -: 5998:	  if (len_rhs > 0 && rhs[len_rhs - 1] == '+') rhs[len_rhs - 1] = '\0';
        -: 5999:
        -: 6000:	  char rhs_expanded[2048] = "";
        -: 6001:	  size_t rhs_len = 0;
        -: 6002:	  chunk = strtok(rhs, "+");
        -: 6003:	  while (chunk != NULL) {
        -: 6004:		   if (strcmp(chunk, ".") == 0) {
        -: 6005:		        AV *restrict cols = get_all_columns(data_hoa, row_hashes, n);
        -: 6006:		        for (size_t c = 0; c <= av_len(cols); c++) {
        -: 6007:		            SV **restrict col_sv = av_fetch(cols, c, 0);
        -: 6008:		            if (col_sv && SvOK(*col_sv)) {
        -: 6009:		                const char *restrict col_name = SvPV_nolen(*col_sv);
        -: 6010:		                if (strcmp(col_name, lhs) != 0) {
        -: 6011:		                    size_t slen = strlen(col_name);
        -: 6012:		                    if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
        -: 6013:		                        if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        -: 6014:		                        strcat(rhs_expanded, col_name);
        -: 6015:		                        rhs_len += slen;
        -: 6016:		                    }
        -: 6017:		                }
        -: 6018:		            }
        -: 6019:		        }
        -: 6020:		        SvREFCNT_dec(cols);
        -: 6021:		   } else {
        -: 6022:		        size_t slen = strlen(chunk);
        -: 6023:		        if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
        -: 6024:		            if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        -: 6025:		            strcat(rhs_expanded, chunk);
        -: 6026:		            rhs_len += slen;
        -: 6027:		        }
        -: 6028:		   }
        -: 6029:		   chunk = strtok(NULL, "+");
        -: 6030:	  }
        -: 6031:
        -: 6032:	  // Setup arrays safely
        -: 6033:	  Newx(terms, term_cap, char*);
        -: 6034:	  Newx(uniq_terms, term_cap, char*);
        -: 6035:	  Newx(exp_terms, exp_cap, char*); Newx(parent_term, exp_cap, char*);
        -: 6036:	  Newx(is_dummy, exp_cap, bool); Newx(is_interact, exp_cap, bool);
        -: 6037:	  Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
        -: 6038:	  Newx(term_map, exp_cap, int); Newx(left_idx, exp_cap, int); Newx(right_idx, exp_cap, int);
        -: 6039:
        -: 6040:	  if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
        -: 6041:
        -: 6042:	  if (strlen(rhs_expanded) > 0) {
        -: 6043:		   chunk = strtok(rhs_expanded, "+");
        -: 6044:		   while (chunk != NULL) {
        -: 6045:		        if (num_terms >= term_cap - 3) {
        -: 6046:		            term_cap *= 2;
        -: 6047:		            Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
        -: 6048:		        }
        -: 6049:		        char *restrict star = strchr(chunk, '*');
        -: 6050:		        if (star) {
        -: 6051:		            *star = '\0';
        -: 6052:		            char *restrict left = chunk;
        -: 6053:		            char *right = star + 1;
        -: 6054:		            char *restrict c_l = strchr(left, '^');
        -: 6055:		            if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
        -: 6056:		            char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
        -: 6057:		            terms[num_terms++] = savepv(left);
        -: 6058:		            terms[num_terms++] = savepv(right);
        -: 6059:		            size_t inter_len = strlen(left) + strlen(right) + 2;
        -: 6060:		            terms[num_terms] = (char*)safemalloc(inter_len);
        -: 6061:		            snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
        -: 6062:		        } else {
        -: 6063:		            char *restrict c_chunk = strchr(chunk, '^'); 
        -: 6064:		            if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
        -: 6065:		            terms[num_terms++] = savepv(chunk);
        -: 6066:		        }
        -: 6067:		        chunk = strtok(NULL, "+");
        -: 6068:		   }
        -: 6069:	  }
        -: 6070:
        -: 6071:	  for (i = 0; i < num_terms; i++) {
        -: 6072:		   bool found = FALSE;
        -: 6073:		   for (size_t k = 0; k < num_uniq; k++) {
        -: 6074:		         if (strcmp(terms[i], uniq_terms[k]) == 0) { found = TRUE; break; }
        -: 6075:		   }
        -: 6076:		   if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
        -: 6077:	  }
        -: 6078:	  p = num_uniq;
        -: 6079:
        -: 6080:	  /* ---- NEW: allocate one slot per unique term, zero-initialised so
        -: 6081:		           non-categorical terms stay NULL                          ---- */
        -: 6082:	  Newxz(term_base_level, num_uniq, char*);
        -: 6083:	  /* ----------------------------------------------------------------------- */
        -: 6084:
        -: 6085:	  // ========================================================================
        -: 6086:	  // PHASE 3: Categorical & Interaction Expansion
        -: 6087:	  // ========================================================================
        -: 6088:	  for (j = 0; j < p; j++) {
        -: 6089:		   if (p_exp + 64 >= exp_cap) {
        -: 6090:		         exp_cap *= 2;
        -: 6091:		         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
        -: 6092:		         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
        -: 6093:		         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 6094:		         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
        -: 6095:		   }
        -: 6096:
        -: 6097:		   if (strcmp(uniq_terms[j], "Intercept") == 0) {
        -: 6098:		         exp_terms[p_exp] = savepv("Intercept");
        -: 6099:		         parent_term[p_exp] = savepv("Intercept");
        -: 6100:		         is_dummy[p_exp] = FALSE; is_interact[p_exp] = FALSE;
        -: 6101:		         term_map[p_exp] = j;
        -: 6102:		         p_exp++;
        -: 6103:		         continue;
        -: 6104:		   }
        -: 6105:
        -: 6106:		   char *restrict colon = strchr(uniq_terms[j], ':');
        -: 6107:		   if (colon) {
        -: 6108:		         char left[256], right[256];
        -: 6109:		         strncpy(left, uniq_terms[j], colon - uniq_terms[j]);
        -: 6110:		         left[colon - uniq_terms[j]] = '\0';
        -: 6111:		         strcpy(right, colon + 1);
        -: 6112:
        -: 6113:		         int *restrict l_indices = (int*)safemalloc(p_exp * sizeof(int)); int l_count = 0;
        -: 6114:		         int *restrict r_indices = (int*)safemalloc(p_exp * sizeof(int)); int r_count = 0;
        -: 6115:		         for (size_t e = 0; e < p_exp; e++) {
        -: 6116:		             if (strcmp(parent_term[e], left) == 0) l_indices[l_count++] = e;
        -: 6117:		             if (strcmp(parent_term[e], right) == 0) r_indices[r_count++] = e;
        -: 6118:		         }
        -: 6119:
        -: 6120:		         if (l_count == 0 || r_count == 0) {
        -: 6121:		             Safefree(l_indices); Safefree(r_indices);
        -: 6122:		             croak("aov: Interaction term '%s' requires its main effects to be explicitly included in the formula", uniq_terms[j]);
        -: 6123:		         } else {
        -: 6124:		             for (int li = 0; li < l_count; li++) {
        -: 6125:		                 for (int ri = 0; ri < r_count; ri++) {
        -: 6126:		                     if (p_exp >= exp_cap) {
        -: 6127:		                         exp_cap *= 2;
        -: 6128:		                         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
        -: 6129:		                         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
        -: 6130:		                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 6131:		                         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
        -: 6132:		                     }
        -: 6133:		                     size_t t_len = strlen(exp_terms[l_indices[li]]) + strlen(exp_terms[r_indices[ri]]) + 2;
        -: 6134:		                     exp_terms[p_exp] = (char*)safemalloc(t_len);
        -: 6135:		                     snprintf(exp_terms[p_exp], t_len, "%s:%s", exp_terms[l_indices[li]], exp_terms[r_indices[ri]]);
        -: 6136:		                     parent_term[p_exp] = savepv(uniq_terms[j]);
        -: 6137:		                     is_dummy[p_exp] = FALSE; is_interact[p_exp] = TRUE;
        -: 6138:		                     left_idx[p_exp] = l_indices[li];
        -: 6139:		                     right_idx[p_exp] = r_indices[ri];
        -: 6140:		                     term_map[p_exp] = j;
        -: 6141:		                     p_exp++;
        -: 6142:		                 }
        -: 6143:		             }
        -: 6144:		         }
        -: 6145:		         Safefree(l_indices); Safefree(r_indices);
        -: 6146:		   } else {
        -: 6147:		         if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
        -: 6148:		             char **restrict levels = NULL;
        -: 6149:		             unsigned int num_levels = 0, levels_cap = 8;
        -: 6150:		             Newx(levels, levels_cap, char*);
        -: 6151:		             for (i = 0; i < n; i++) {
        -: 6152:		                 char* str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
        -: 6153:		                 if (str_val) {
        -: 6154:		                     bool found = FALSE;
        -: 6155:		                     for (size_t l = 0; l < num_levels; l++) {
        -: 6156:		                         if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; }
        -: 6157:		                     }
        -: 6158:		                     if (!found) {
        -: 6159:		                         if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
        -: 6160:		                         levels[num_levels++] = savepv(str_val);
        -: 6161:		                     }
        -: 6162:		                     Safefree(str_val);
        -: 6163:		                 }
        -: 6164:		             }
        -: 6165:
        -: 6166:		             if (num_levels > 0) {
        -: 6167:		                 for (size_t l1 = 0; l1 < num_levels - 1; l1++) {
        -: 6168:		                     for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
        -: 6169:		                         if (strcmp(levels[l1], levels[l2]) > 0) {
        -: 6170:		                             char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp;
        -: 6171:		                         }
        -: 6172:		                     }
        -: 6173:		                 }
        -: 6174:
        -: 6175:		                 /* ---- NEW: record the reference (base) level for this term ---- */
        -: 6176:		                 term_base_level[j] = savepv(levels[0]);
        -: 6177:		                 /* --------------------------------------------------------------- */
        -: 6178:
        -: 6179:		                 for (size_t l = 1; l < num_levels; l++) {
        -: 6180:		                     if (p_exp >= exp_cap) {
        -: 6181:		                         exp_cap *= 2;
        -: 6182:		                         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
        -: 6183:		                         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
        -: 6184:		                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 6185:		                         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
        -: 6186:		                     }
        -: 6187:		                     size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
        -: 6188:		                     exp_terms[p_exp] = (char*)safemalloc(t_len);
        -: 6189:		                     snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
        -: 6190:		                     parent_term[p_exp] = savepv(uniq_terms[j]);
        -: 6191:		                     is_dummy[p_exp] = TRUE; is_interact[p_exp] = FALSE;
        -: 6192:		                     dummy_base[p_exp] = savepv(uniq_terms[j]);
        -: 6193:		                     dummy_level[p_exp] = savepv(levels[l]);
        -: 6194:		                     term_map[p_exp] = j;
        -: 6195:		                     p_exp++;
        -: 6196:		                 }
        -: 6197:		                 for (size_t l = 0; l < num_levels; l++) Safefree(levels[l]);
        -: 6198:		                 Safefree(levels);
        -: 6199:		             } else {
        -: 6200:		                 Safefree(levels);
        -: 6201:		                 exp_terms[p_exp] = savepv(uniq_terms[j]);
        -: 6202:		                 parent_term[p_exp] = savepv(uniq_terms[j]);
        -: 6203:		                 is_dummy[p_exp] = FALSE; is_interact[p_exp] = FALSE;
        -: 6204:		                 term_map[p_exp] = j;
        -: 6205:		                 p_exp++;
        -: 6206:		             }
        -: 6207:		         } else {
        -: 6208:		             exp_terms[p_exp] = savepv(uniq_terms[j]);
        -: 6209:		             parent_term[p_exp] = savepv(uniq_terms[j]);
        -: 6210:		             is_dummy[p_exp] = FALSE; is_interact[p_exp] = FALSE;
        -: 6211:		             term_map[p_exp] = j;
        -: 6212:		             p_exp++;
        -: 6213:		         }
        -: 6214:		   }
        -: 6215:	  }
        -: 6216:	  X_mat = (double**)safemalloc(n * sizeof(double*));
        -: 6217:	  for(i = 0; i < n; i++) X_mat[i] = (double*)safemalloc(p_exp * sizeof(double));
        -: 6218:	  Newx(Y, n, double);
        -: 6219:	  // ========================================================================
        -: 6220:	  // PHASE 4: Matrix Construction & Listwise Deletion
        -: 6221:	  // ========================================================================
        -: 6222:	  for (i = 0; i < n; i++) {
        -: 6223:		   double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
        -: 6224:		   if (isnan(y_val)) { Safefree(row_names[i]); continue; }
        -: 6225:		   bool row_ok = TRUE;
        -: 6226:		   double *restrict row_x = (double*)safemalloc(p_exp * sizeof(double));
        -: 6227:		   for (j = 0; j < p_exp; j++) {
        -: 6228:		         if (strcmp(exp_terms[j], "Intercept") == 0) {
        -: 6229:		             row_x[j] = 1.0;
        -: 6230:		         } else if (is_interact[j]) {
        -: 6231:		             row_x[j] = row_x[left_idx[j]] * row_x[right_idx[j]];
        -: 6232:		         } else if (is_dummy[j]) {
        -: 6233:		             char*restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
        -: 6234:		             if (str_val) {
        -: 6235:		                 row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
        -: 6236:		                 Safefree(str_val);
        -: 6237:		             } else { row_ok = FALSE; break; }
        -: 6238:		         } else {
        -: 6239:		             row_x[j] = evaluate_term(data_hoa, row_hashes, i, parent_term[j]);
        -: 6240:		             if (isnan(row_x[j])) { row_ok = FALSE; break; }
        -: 6241:		         }
        -: 6242:		   }
        -: 6243:		   if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
        -: 6244:
        -: 6245:		   Y[valid_n] = y_val;
        -: 6246:		   for (j = 0; j < p_exp; j++) X_mat[valid_n][j] = row_x[j];
        -: 6247:		   valid_n++;
        -: 6248:		   Safefree(row_x);
        -: 6249:		   Safefree(row_names[i]);
        -: 6250:	  }
        -: 6251:	  Safefree(row_names);
        -: 6252:	  if (valid_n <= p_exp) {
        -: 6253:		   // Full Clean Up 
        -: 6254:		   for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
        -: 6255:		   for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
        -: 6256:		   for (j = 0; j < p_exp; j++) {
        -: 6257:		        Safefree(exp_terms[j]); Safefree(parent_term[j]);
        -: 6258:		        if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
        -: 6259:		   }
        -: 6260:		   Safefree(exp_terms); Safefree(parent_term); 
        -: 6261:		   Safefree(is_dummy); Safefree(is_interact); 
        -: 6262:		   Safefree(dummy_base); Safefree(dummy_level);
        -: 6263:		   Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
        -: 6264:		   for(i = 0; i < n; i++) Safefree(X_mat[i]);
        -: 6265:		   Safefree(X_mat); Safefree(Y);
        -: 6266:		   if (row_hashes) Safefree(row_hashes);
        -: 6267:		   /* ---- NEW ---- */
        -: 6268:		   for (i = 0; i < num_uniq; i++) { if (term_base_level[i]) Safefree(term_base_level[i]); }
        -: 6269:		   Safefree(term_base_level);
        -: 6270:		   /* ------------ */
        -: 6271:		   croak("aov: 0 degrees of freedom (too many NAs or parameters > observations)");
        -: 6272:	  }
        -: 6273:	// ========================================================================
        -: 6274:	  // PHASE 5: Math & Output Formatting
        -: 6275:	  // ========================================================================
        -: 6276:	  bool *restrict aliased_qr = (bool*)safemalloc(p_exp * sizeof(bool));
        -: 6277:	  size_t *restrict rank_map = (size_t*)safemalloc(p_exp * sizeof(size_t));
        -: 6278:	  apply_householder_aov(X_mat, Y, valid_n, p_exp, aliased_qr, rank_map);
        -: 6279:	  double *restrict term_ss;
        -: 6280:	  int *restrict term_df;
        -: 6281:	  Newxz(term_ss, num_uniq, double);
        -: 6282:	  Newxz(term_df, num_uniq, int);
        -: 6283:
        -: 6284:	  for (i = 0; i < p_exp; i++) {
        -: 6285:		   if (strcmp(exp_terms[i], "Intercept") == 0) continue; 
        -: 6286:		   if (aliased_qr[i]) continue;
        -: 6287:		   int t_idx = term_map[i];
        -: 6288:		   size_t r_k = rank_map[i];
        -: 6289:		   term_ss[t_idx] += Y[r_k] * Y[r_k];
        -: 6290:		   term_df[t_idx] += 1;
        -: 6291:	  }
        -: 6292:	  int rank = 0;
        6: 6293:	  for (i = 0; i < p_exp; i++) {
call    0 returned 6
        6: 6294:		   if (!aliased_qr[i]) rank++;
        -: 6295:	  }
        6: 6296:	  double rss_prev = 0.0;
        -: 6297:	  for (i = rank; i < valid_n; i++) {
        -: 6298:		   rss_prev += Y[i] * Y[i];
        -: 6299:	  }
        -: 6300:	  int res_df = valid_n - rank;
function XS_Stats__LikeR_fisher_test called 6 returned 83% blocks executed 87%
        6: 6301:	  double ms_res = (res_df > 0) ? rss_prev / res_df : 0.0;
        -: 6302:
        6: 6303:	  HV*restrict ret_hash = newHV();
        6: 6303-block  0
call    0 returned 6
branch  1 taken 1 (fallthrough)
branch  2 taken 5
        -: 6304:	  for (j = 0; j < num_uniq; j++) {
        -: 6305:		   if (strcmp(uniq_terms[j], "Intercept") == 0) continue;
        -: 6306:		   HV*restrict term_stats = newHV();
        -: 6307:		   double ss = term_ss[j];
        -: 6308:		   int df = term_df[j];
        -: 6309:		   double ms = (df > 0) ? ss / df : 0.0;
        -: 6310:
        -: 6311:		   hv_stores(term_stats, "Df", newSViv(df));
        -: 6312:		   hv_stores(term_stats, "Sum Sq", newSVnv(ss));
        -: 6313:		   hv_stores(term_stats, "Mean Sq", newSVnv(ms));
        -: 6314:		   if (ms_res > 0.0 && df > 0) {
        -: 6315:		         double f_val = ms / ms_res;
        -: 6316:		         hv_stores(term_stats, "F value", newSVnv(f_val));
        -: 6317:		         hv_stores(term_stats, "Pr(>F)", newSVnv(1.0 - pf(f_val, (double)df, (double)res_df)));
        -: 6318:		   } else {
        -: 6319:		         hv_stores(term_stats, "F value", newSVnv(NAN));
        -: 6320:		         hv_stores(term_stats, "Pr(>F)", newSVnv(NAN));
        -: 6321:		   }
        -: 6322:		   hv_store(ret_hash, uniq_terms[j], strlen(uniq_terms[j]), newRV_noinc((SV*)term_stats), 0);
        -: 6323:	  }
        -: 6324:
        -: 6325:	  HV*restrict res_stats = newHV();
        -: 6326:	  hv_stores(res_stats, "Df", newSViv(res_df));
        -: 6327:	  hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
        -: 6328:	  hv_stores(res_stats, "Mean Sq", newSVnv(ms_res));
        -: 6329:	  hv_stores(ret_hash, "Residuals", newRV_noinc((SV*)res_stats));
        -: 6330:
        -: 6331:	  /* ---- NEW: group_stats => { mean => {...}, size => {...} } ---- */
        -: 6332:	  {
        -: 6333:		   AV *restrict all_cols = get_all_columns(data_hoa, row_hashes, n);
        -: 6334:		   HV *restrict mean_hv  = newHV();
        -: 6335:		   HV *restrict size_hv  = newHV();
        -: 6336:		   for (size_t c = 0; c <= (size_t)av_len(all_cols); c++) {
        -: 6337:		       SV **restrict col_sv = av_fetch(all_cols, c, 0);
        -: 6338:		       if (!col_sv || !SvOK(*col_sv)) continue;
        -: 6339:		       const char *restrict col_name = SvPV_nolen(*col_sv);
        -: 6340:		       double col_sum = 0.0;
        -: 6341:		       IV     col_count = 0;
        -: 6342:		       for (i = 0; i < n; i++) {
        -: 6343:		           double val = evaluate_term(data_hoa, row_hashes, i, col_name);
        -: 6344:		           if (!isnan(val)) { col_sum += val; col_count++; }
        -: 6345:		       }
        -: 6346:		       double col_mean = (col_count > 0) ? col_sum / col_count : NAN;
        -: 6347:		       hv_store(mean_hv, col_name, strlen(col_name), newSVnv(col_mean), 0);
        -: 6348:		       hv_store(size_hv, col_name, strlen(col_name), newSViv(col_count), 0);
        -: 6349:		   }
        -: 6350:		   SvREFCNT_dec(all_cols);
        -: 6351:		   HV *restrict gs_hv = newHV();
        -: 6352:		   hv_stores(gs_hv, "mean", newRV_noinc((SV*)mean_hv));
        -: 6353:		   hv_stores(gs_hv, "size", newRV_noinc((SV*)size_hv));
        -: 6354:		   hv_stores(ret_hash, "group_stats", newRV_noinc((SV*)gs_hv));
        -: 6355:	  }
        -: 6356:	  /* -------------------------------------------------------------- */
        -: 6357:
        -: 6358:	  // Deep Cleanup
        -: 6359:	  for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
        -: 6360:	  for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
        -: 6361:	  for (j = 0; j < p_exp; j++) {
        -: 6362:		   Safefree(exp_terms[j]); Safefree(parent_term[j]);
        -: 6363:		   if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
        -: 6364:	  }
        -: 6365:	  Safefree(exp_terms); Safefree(parent_term); 
        -: 6366:	  Safefree(is_dummy); Safefree(is_interact); 
        -: 6367:	  Safefree(dummy_base); Safefree(dummy_level);
        -: 6368:	  Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
        -: 6369:	  Safefree(term_ss); Safefree(term_df);
        -: 6370:	  for (i = 0; i < n; i++) Safefree(X_mat[i]);
        -: 6371:	  Safefree(X_mat); Safefree(Y);
        -: 6372:	  Safefree(aliased_qr); Safefree(rank_map);
        -: 6373:	  if (row_hashes) Safefree(row_hashes);
        -: 6374:	  RETVAL = newRV_noinc((SV*)ret_hash);
        -: 6375:	}
        -: 6376:#line 6377 "LikeR.c"
        -: 6377:	RETVAL = sv_2mortal(RETVAL);
        -: 6378:	ST(0) = RETVAL;
        -: 6379:    }
        -: 6380:    XSRETURN(1);
        -: 6381:}
        -: 6382:
        -: 6383:
        -: 6384:XS_EUPXS(XS_Stats__LikeR_fisher_test); /* prototype to pass -Wmissing-prototypes */
        -: 6385:XS_EUPXS(XS_Stats__LikeR_fisher_test)
        -: 6386:{
        -: 6387:    dVAR; dXSARGS;
        -: 6388:    PERL_UNUSED_VAR(cv); /* -W */
        -: 6389:    PERL_UNUSED_VAR(items); /* -W */
        -: 6390:    {
        -: 6391:	SV *	RETVAL;
        -: 6392:#line 5797 "LikeR.xs"
        -: 6393:{
        -: 6394:	if (items < 1) croak("fisher_test requires at least a data reference");
        -: 6395:
        -: 6396:	SV*restrict data_ref = ST(0);
        -: 6397:	double conf_level = 0.95;
        -: 6398:	const char*restrict alternative = "two.sided";
        -: 6399:
        -: 6400:	// Parse named arguments
        -: 6401:	for (unsigned short int i = 1; i < items; i += 2) {
        -: 6402:		if (i + 1 >= items) croak("fisher_test: odd number of arguments");
        -: 6403:		const char*restrict key = SvPV_nolen(ST(i));
        -: 6404:		SV*restrict val = ST(i + 1);
        -: 6405:		if (strEQ(key, "conf_level") || strEQ(key, "conf.level")) {
        -: 6406:			conf_level = SvNV(val);
        -: 6407:		} else if (strEQ(key, "alternative")) {
        -: 6408:			alternative = SvPV_nolen(val);
        -: 6409:		}
        -: 6410:	}
        -: 6411:
        5: 6412:	if (!SvROK(data_ref)) croak("fisher_test requires a reference to an Array or Hash");
call    0 returned 5
        5: 6413:	SV*restrict deref = SvRV(data_ref);
        -: 6414:	size_t a = 0, b = 0, c = 0, d = 0;
        5: 6415:	// Extract Data
        -: 6416:	if (SvTYPE(deref) == SVt_PVAV) {
        -: 6417:	  AV*restrict outer = (AV*)deref;
        -: 6418:	  if (av_len(outer) != 1) croak("Outer array must have exactly 2 rows");
        -: 6419:	  SV**restrict row1_ptr = av_fetch(outer, 0, 0);
function XS_Stats__LikeR_power_t_test called 7 returned 100% blocks executed 71%
        7: 6420:	  SV**restrict row2_ptr = av_fetch(outer, 1, 0);
        -: 6421:	  if (row1_ptr && row2_ptr && SvROK(*row1_ptr) && SvROK(*row2_ptr)) {
        7: 6422:		   AV*restrict row1 = (AV*)SvRV(*row1_ptr);
        7: 6422-block  0
call    0 returned 7
branch  1 taken 0 (fallthrough)
branch  2 taken 7
        -: 6423:		   AV*restrict row2 = (AV*)SvRV(*row2_ptr);
        -: 6424:		   SV**restrict a_ptr = av_fetch(row1, 0, 0);
        -: 6425:		   SV**restrict b_ptr = av_fetch(row1, 1, 0);
        -: 6426:		   SV**restrict c_ptr = av_fetch(row2, 0, 0);
        -: 6427:		   SV**restrict d_ptr = av_fetch(row2, 1, 0);
        -: 6428:		   a = (a_ptr && SvOK(*a_ptr)) ? SvIV(*a_ptr) : 0;
        -: 6429:		   b = (b_ptr && SvOK(*b_ptr)) ? SvIV(*b_ptr) : 0;
        -: 6430:		   c = (c_ptr && SvOK(*c_ptr)) ? SvIV(*c_ptr) : 0;
        -: 6431:		   d = (d_ptr && SvOK(*d_ptr)) ? SvIV(*d_ptr) : 0;
        -: 6432:	  } else {
        -: 6433:		  croak("Invalid 2D Array structure");
        -: 6434:	  }
        -: 6435:	} else if (SvTYPE(deref) == SVt_PVHV) {
        -: 6436:		// Fixed 2D Hash Logic: Sort keys lexically to enforce structured rows/columns
        -: 6437:		HV*restrict outer = (HV*)deref;
        -: 6438:		if (hv_iterinit(outer) != 2) croak("Outer hash must have exactly 2 keys");
        -: 6439:		HE*restrict he1 = hv_iternext(outer);
        -: 6440:		HE*restrict he2 = hv_iternext(outer);
        -: 6441:		if (!he1 || !he2) croak("Invalid outer hash");
        -: 6442:		const char*restrict k1 = SvPV_nolen(hv_iterkeysv(he1));
        -: 6443:		const char*restrict k2 = SvPV_nolen(hv_iterkeysv(he2));
        -: 6444:		HE*restrict row1_he = (strcmp(k1, k2) < 0) ? he1 : he2;
        -: 6445:		HE*restrict row2_he = (strcmp(k1, k2) < 0) ? he2 : he1;
        -: 6446:		SV*restrict row1_sv = hv_iterval(outer, row1_he);
        -: 6447:		SV*restrict row2_sv = hv_iterval(outer, row2_he);
        -: 6448:		if (!SvROK(row1_sv) || SvTYPE(SvRV(row1_sv)) != SVt_PVHV ||
        -: 6449:			!SvROK(row2_sv) || SvTYPE(SvRV(row2_sv)) != SVt_PVHV) {
        -: 6450:			croak("Inner elements must be hashes");
        -: 6451:		}
        -: 6452:		HV*restrict in1 = (HV*)SvRV(row1_sv);
        -: 6453:		HV*restrict in2 = (HV*)SvRV(row2_sv);
        -: 6454:		if (hv_iterinit(in1) != 2 || hv_iterinit(in2) != 2) croak("Inner hashes must have exactly 2 keys");
        -: 6455:		HE*restrict in1_he1 = hv_iternext(in1);
        -: 6456:		HE*restrict in1_he2 = hv_iternext(in1);
        -: 6457:		const char*restrict in1_k1 = SvPV_nolen(hv_iterkeysv(in1_he1));
        -: 6458:		const char*restrict in1_k2 = SvPV_nolen(hv_iterkeysv(in1_he2));
        -: 6459:		HE*restrict in1_c1 = (strcmp(in1_k1, in1_k2) < 0) ? in1_he1 : in1_he2;
        -: 6460:		HE*restrict in1_c2 = (strcmp(in1_k1, in1_k2) < 0) ? in1_he2 : in1_he1;
        -: 6461:		HE*restrict in2_he1 = hv_iternext(in2);
        -: 6462:		HE*restrict in2_he2 = hv_iternext(in2);
        -: 6463:		const char*restrict in2_k1 = SvPV_nolen(hv_iterkeysv(in2_he1));
        -: 6464:		const char*restrict in2_k2 = SvPV_nolen(hv_iterkeysv(in2_he2));
        -: 6465:		HE*restrict in2_c1 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he1 : in2_he2;
        -: 6466:		HE*restrict in2_c2 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he2 : in2_he1;
        -: 6467:		a = (hv_iterval(in1, in1_c1) && SvOK(hv_iterval(in1, in1_c1))) ? SvIV(hv_iterval(in1, in1_c1)) : 0;
        -: 6468:		b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
        -: 6469:		c = (hv_iterval(in2, in2_c1) && SvOK(hv_iterval(in2, in2_c1))) ? SvIV(hv_iterval(in2, in2_c1)) : 0;
        -: 6470:		d = (hv_iterval(in2, in2_c2) && SvOK(hv_iterval(in2, in2_c2))) ? SvIV(hv_iterval(in2, in2_c2)) : 0;
        -: 6471:	} else {
        -: 6472:	  croak("Input must be a 2D Array or 2D Hash");
        -: 6473:	}
        -: 6474:
        -: 6475:	// Perform Calculations via Helpers
        -: 6476:	double p_val = exact_p_value(a, b, c, d, alternative);
        -: 6477:	double mle_or, ci_low, ci_high;
        -: 6478:	calculate_exact_stats(a, b, c, d, conf_level, alternative, &mle_or, &ci_low, &ci_high);
        -: 6479:
        -: 6480:	// Construct the Return HashRef purely in C
        -: 6481:	HV*restrict ret_hash = newHV();
        -: 6482:	hv_stores(ret_hash, "method", newSVpv("Fisher's Exact Test for Count Data", 0));
        -: 6483:	hv_stores(ret_hash, "alternative", newSVpv(alternative, 0));
        -: 6484:	AV*restrict ci_array = newAV();
        -: 6485:	av_push(ci_array, newSVnv(ci_low));
        -: 6486:	av_push(ci_array, newSVnv(ci_high));
        -: 6487:	hv_stores(ret_hash, "conf_int", newRV_noinc((SV*)ci_array));
        -: 6488:	HV*restrict est_hash = newHV();
        -: 6489:	hv_stores(ret_hash, "estimate", newRV_noinc((SV*)est_hash));
        -: 6490:	hv_stores(est_hash, "odds ratio", newSVnv(mle_or));
        -: 6491:	hv_stores(ret_hash, "p_value", newSVnv(p_val));
        -: 6492:	// Return the HashRef
        -: 6493:	RETVAL = newRV_noinc((SV*)ret_hash);
        -: 6494:}
        -: 6495:#line 6496 "LikeR.c"
        -: 6496:	RETVAL = sv_2mortal(RETVAL);
        -: 6497:	ST(0) = RETVAL;
        -: 6498:    }
        -: 6499:    XSRETURN(1);
        -: 6500:}
        -: 6501:
        -: 6502:
        -: 6503:XS_EUPXS(XS_Stats__LikeR_power_t_test); /* prototype to pass -Wmissing-prototypes */
        -: 6504:XS_EUPXS(XS_Stats__LikeR_power_t_test)
        -: 6505:{
        -: 6506:    dVAR; dXSARGS;
        -: 6507:    PERL_UNUSED_VAR(cv); /* -W */
        -: 6508:    PERL_UNUSED_VAR(items); /* -W */
        -: 6509:    {
        -: 6510:	SV *	RETVAL;
        -: 6511:#line 5904 "LikeR.xs"
        -: 6512:{
        -: 6513:	SV*restrict sv_n = NULL;
        -: 6514:	SV*restrict sv_delta = NULL;
        -: 6515:	SV*restrict sv_sd = NULL;
        -: 6516:	SV*restrict sv_sig_level = NULL;
        -: 6517:	SV*restrict sv_power = NULL;
        -: 6518:
        -: 6519:	const char* restrict type = "two.sample";
        -: 6520:	const char* restrict alternative = "two.sided";
        -: 6521:	bool strict = FALSE;
        -: 6522:	double tol = pow(2.2204460492503131e-16, 0.25); 
        -: 6523:
        -: 6524:	if (items % 2 != 0) croak("Usage: power_t_test(n => 30, delta => 0.5, sd => 1.0, ...)");
        -: 6525:	for (unsigned short int i = 0; i < items; i += 2) {
        -: 6526:	  const char* restrict key = SvPV_nolen(ST(i));
        -: 6527:	  SV* restrict val = ST(i+1);
        -: 6528:
        -: 6529:	  if      (strEQ(key, "n"))           sv_n = val;
        -: 6530:	  else if (strEQ(key, "delta"))       sv_delta = val;
        -: 6531:	  else if (strEQ(key, "sd"))          sv_sd = val;
        -: 6532:	  else if (strEQ(key, "sig.level") || strEQ(key, "sig_level")) sv_sig_level = val;
        7: 6533:	  else if (strEQ(key, "power"))       sv_power = val;
call    0 returned 7
        7: 6534:	  else if (strEQ(key, "type"))        type = SvPV_nolen(val);
        -: 6535:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        7: 6536:	  else if (strEQ(key, "strict"))      strict = SvTRUE(val);
        -: 6537:	  else if (strEQ(key, "tol"))         tol = SvNV(val);
        -: 6538:	  else croak("power_t_test: unknown argument '%s'", key);
        -: 6539:	}
        -: 6540:
function XS_Stats__LikeR_kruskal_test called 3 returned 100% blocks executed 85%
        3: 6541:	bool is_null_n = (!sv_n || !SvOK(sv_n));
        -: 6542:	bool is_null_delta = (!sv_delta || !SvOK(sv_delta));
        3: 6543:	bool is_null_power = (!sv_power || !SvOK(sv_power));
        3: 6543-block  0
call    0 returned 3
branch  1 taken 3 (fallthrough)
branch  2 taken 0
        -: 6544:	bool is_null_sd = (sv_sd && !SvOK(sv_sd)); 
        -: 6545:	bool is_null_sig_level = (sv_sig_level && !SvOK(sv_sig_level));
        -: 6546:
        -: 6547:	unsigned int missing_count = 0;
        -: 6548:	if (is_null_n) missing_count++;
        -: 6549:	if (is_null_delta) missing_count++;
        -: 6550:	if (is_null_power) missing_count++;
        -: 6551:	if (is_null_sd) missing_count++;
        -: 6552:	if (is_null_sig_level) missing_count++;
        -: 6553:
        -: 6554:	if (missing_count != 1) {
        -: 6555:	  croak("power_t_test: exactly one of 'n', 'delta', 'sd', 'power', and 'sig_level' must be undef/NULL");
        -: 6556:	}
        -: 6557:
        -: 6558:	double n = is_null_n ? 0.0 : SvNV(sv_n);
        -: 6559:	double delta = is_null_delta ? 0.0 : SvNV(sv_delta);
        -: 6560:	double sd = (!sv_sd || is_null_sd) ? 1.0 : SvNV(sv_sd);
        -: 6561:	double sig_level = (!sv_sig_level || is_null_sig_level) ? 0.05 : SvNV(sv_sig_level);
        -: 6562:	double power = is_null_power ? 0.0 : SvNV(sv_power);
        -: 6563:	short int tsample = (strEQ(type, "one.sample") || strEQ(type, "paired")) ? 1 : 2;
        -: 6564:	short int tside = (strEQ(alternative, "one.sided") || strEQ(alternative, "greater") || strEQ(alternative, "less")) ? 1 : 2;
        -: 6565:	if (tside == 2 && !is_null_delta) delta = fabs(delta);
        -: 6566:	if (is_null_power) {
        -: 6567:	  power = p_body(n, delta, sd, sig_level, tsample, tside, strict);
        -: 6568:	} else if (is_null_n) {
        -: 6569:	  double low = 2.0, high = 1e7;
        -: 6570:	  while (p_body(high, delta, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
        -: 6571:	  while (high - low > tol) {
        -: 6572:		   double mid = low + (high - low) / 2.0;
        -: 6573:		   if (p_body(mid, delta, sd, sig_level, tsample, tside, strict) < power) low = mid;
        -: 6574:		   else high = mid;
        -: 6575:	  }
        -: 6576:	  n = low + (high - low) / 2.0;
        -: 6577:	} else if (is_null_sd) {
        -: 6578:	  double low = delta * 1e-7, high = delta * 1e7;
        -: 6579:	  while (high - low > tol) {
        -: 6580:		   double mid = low + (high - low) / 2.0;
        -: 6581:		   if (p_body(n, delta, mid, sig_level, tsample, tside, strict) > power) low = mid;
        -: 6582:		   else high = mid;
        -: 6583:	  }
        -: 6584:	  sd = low + (high - low) / 2.0;
        -: 6585:	} else if (is_null_delta) {
        -: 6586:	  double low = sd * 1e-7, high = sd * 1e7;
        -: 6587:	  while (p_body(n, high, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
        -: 6588:	  while (high - low > tol) {
        -: 6589:		   double mid = low + (high - low) / 2.0;
        -: 6590:		   if (p_body(n, mid, sd, sig_level, tsample, tside, strict) < power) low = mid;
        -: 6591:		   else high = mid;
        -: 6592:	  }
        -: 6593:	  delta = low + (high - low) / 2.0;
        -: 6594:	} else if (is_null_sig_level) {
        -: 6595:	  double low = 1e-10, high = 1.0 - 1e-10;
        -: 6596:	  while (high - low > tol) {
        -: 6597:		   double mid = low + (high - low) / 2.0;
        -: 6598:		   if (p_body(n, delta, sd, mid, tsample, tside, strict) < power) low = mid;
        -: 6599:		   else high = mid;
        -: 6600:	  }
        -: 6601:	  sig_level = low + (high - low) / 2.0;
        -: 6602:	}
        -: 6603:	HV*restrict ret = newHV();
        -: 6604:	hv_stores(ret, "n", newSVnv(n));
        -: 6605:	hv_stores(ret, "delta", newSVnv(delta));
        -: 6606:	hv_stores(ret, "sd", newSVnv(sd));
        -: 6607:	hv_stores(ret, "sig.level", newSVnv(sig_level));
        -: 6608:	hv_stores(ret, "power", newSVnv(power));
        -: 6609:	hv_stores(ret, "alternative", newSVpv(alternative, 0));
        -: 6610:	const char*restrict m_str = (tsample == 1) ? (strEQ(type, "paired") ? "Paired t test power calculation" : "One-sample t test power calculation") : "Two-sample t test power calculation";
        -: 6611:	hv_stores(ret, "method", newSVpv(m_str, 0));
        -: 6612:	const char*restrict n_str = (tsample == 2) ? "n is number in *each* group" : (strEQ(type, "paired") ? "n is number of *pairs*, sd is std.dev. of *differences* within pairs" : "");
        -: 6613:	if (n_str[0] != '\0') hv_stores(ret, "note", newSVpv(n_str, 0));
        -: 6614:	RETVAL = newRV_noinc((SV*)ret);
        -: 6615:}
        -: 6616:#line 6617 "LikeR.c"
        -: 6617:	RETVAL = sv_2mortal(RETVAL);
        -: 6618:	ST(0) = RETVAL;
        -: 6619:    }
        -: 6620:    XSRETURN(1);
        -: 6621:}
        -: 6622:
        -: 6623:
        -: 6624:XS_EUPXS(XS_Stats__LikeR_kruskal_test); /* prototype to pass -Wmissing-prototypes */
        -: 6625:XS_EUPXS(XS_Stats__LikeR_kruskal_test)
        -: 6626:{
        -: 6627:    dVAR; dXSARGS;
        -: 6628:    PERL_UNUSED_VAR(cv); /* -W */
        -: 6629:    PERL_UNUSED_VAR(items); /* -W */
        -: 6630:    {
        -: 6631:	SV *	RETVAL;
        -: 6632:#line 6013 "LikeR.xs"
        -: 6633:{
        -: 6634:	SV *restrict x_sv = NULL, *restrict g_sv = NULL, *restrict h_sv = NULL;
        -: 6635:	unsigned int arg_idx = 0;
        -: 6636:
        -: 6637:	// 1. Shift positional arguments
        -: 6638:	//    Accept either: (arrayref, arrayref) or (hashref)
        -: 6639:	if (arg_idx < items && SvROK(ST(arg_idx))) {
        -: 6640:	  svtype t = SvTYPE(SvRV(ST(arg_idx)));
        -: 6641:	  if (t == SVt_PVAV) {
        -: 6642:		   x_sv = ST(arg_idx++);
        -: 6643:	  } else if (t == SVt_PVHV) {
        -: 6644:		   h_sv = ST(arg_idx++);          /* hash-of-arrays shortcut */
        -: 6645:	  }
        -: 6646:	}
        -: 6647:	if (!h_sv && arg_idx < items
        -: 6648:		     && SvROK(ST(arg_idx))
        -: 6649:		     && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 6650:	  g_sv = ST(arg_idx++);
        -: 6651:	}
        -: 6652:	// 2. Parse named arguments (fallback)
        -: 6653:	for (; arg_idx < items; arg_idx += 2) {
        -: 6654:	  const char *restrict key = SvPV_nolen(ST(arg_idx));
        -: 6655:	  SV         *restrict val = ST(arg_idx + 1);
        -: 6656:	  if      (strEQ(key, "x")) x_sv = val;
        -: 6657:	  else if (strEQ(key, "g")) g_sv = val;
        -: 6658:	  else if (strEQ(key, "h")) h_sv = val;
        -: 6659:	  else croak("kruskal_test: unknown argument '%s'", key);
        -: 6660:	}
        -: 6661:	// 3. Mutual-exclusion guard
        -: 6662:	if (h_sv && (x_sv || g_sv))
        -: 6663:	  croak("kruskal_test: cannot mix 'h' (hash-of-arrays) with 'x'/'g' inputs");
        -: 6664:
        -: 6665:	/* ------------------------------------------------------------------ */
        -: 6666:	/* Shared state filled by whichever input branch runs                 */
        -: 6667:	/* ------------------------------------------------------------------ */
        -: 6668:	RankInfo *restrict ri = NULL;
        -: 6669:	char **restrict group_names = NULL; /* Track names to build group_stats */
        -: 6670:	size_t valid_n = 0;
        -: 6671:	size_t k       = 0;
        -: 6672:
        -: 6673:	/* ------------------------------------------------------------------ */
        -: 6674:	/* 4a. Hash-of-arrays input path                                      */
        -: 6675:	/*     my %x = ( group1 => [...], group2 => [...], ... )              */
        -: 6676:	/* ------------------------------------------------------------------ */
        -: 6677:	if (h_sv) {
        -: 6678:	  if (!SvROK(h_sv) || SvTYPE(SvRV(h_sv)) != SVt_PVHV)
        -: 6679:		   croak("kruskal_test: 'h' must be a HASH reference");
        -: 6680:	  HV *restrict h_hv = (HV*)SvRV(h_sv);
        -: 6681:	  // First pass – validate values and tally total elements
        -: 6682:	  size_t total = 0;
        -: 6683:	  hv_iterinit(h_hv);
        -: 6684:	  HE *restrict he;
        -: 6685:	  while ((he = hv_iternext(h_hv))) {
        -: 6686:		   SV *restrict val = HeVAL(he);
        -: 6687:		   if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVAV)
        -: 6688:		       croak("kruskal_test: every value in 'h' must be an ARRAY reference");
        -: 6689:		   total += (size_t)(av_len((AV*)SvRV(val)) + 1);
        -: 6690:	  }
        -: 6691:	  if (total < 2) croak("not enough observations");
        -: 6692:
        -: 6693:	  ri = (RankInfo *)safemalloc(total * sizeof(RankInfo));
        -: 6694:	  size_t num_keys = HvKEYS(h_hv);
        -: 6695:	  group_names = (char **)safecalloc(num_keys, sizeof(char*));
        -: 6696:	  /* Second pass – fill ri[], assigning one group_id per hash key */
        -: 6697:	  size_t group_id = 0;
        -: 6698:	  hv_iterinit(h_hv);
        -: 6699:	  while ((he = hv_iternext(h_hv))) {
        -: 6700:		   STRLEN klen;
        -: 6701:		   const char *restrict key_str = HePV(he, klen);
        -: 6702:		   group_names[group_id] = savepvn(key_str, klen); // Save string key
        -: 6703:
        -: 6704:		   AV *restrict av  = (AV*)SvRV(HeVAL(he));
        -: 6705:		   size_t       n_g = (size_t)(av_len(av) + 1);
        -: 6706:		   for (size_t i = 0; i < n_g; i++) {
        -: 6707:		       SV **restrict el = av_fetch(av, i, 0);
        -: 6708:		       if (el && SvOK(*el) && looks_like_number(*el)) {
        -: 6709:		           ri[valid_n].val = SvNV(*el);
        -: 6710:		           ri[valid_n].idx = group_id;   /* group identity */
        -: 6711:		           valid_n++;
        -: 6712:		       }
        -: 6713:		   }
        -: 6714:		   group_id++;
        -: 6715:	  }
        -: 6716:	  k = group_id;   /* number of unique groups = number of hash keys */
        -: 6717:
        -: 6718:	/* ------------------------------------------------------------------ */
        -: 6719:	/* 4b. Original x / g array-pair input path                           */
        -: 6720:	/* ------------------------------------------------------------------ */
        -: 6721:	} else {
        -: 6722:	  if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 6723:		   croak("kruskal_test: 'x' is a required argument and must be an ARRAY reference");
        -: 6724:	  if (!g_sv || !SvROK(g_sv) || SvTYPE(SvRV(g_sv)) != SVt_PVAV)
        -: 6725:		   croak("kruskal_test: 'g' is a required argument and must be an ARRAY reference");
        -: 6726:
        -: 6727:	  AV *restrict x_av = (AV*)SvRV(x_sv);
        -: 6728:	  AV *restrict g_av = (AV*)SvRV(g_sv);
        -: 6729:	  size_t nx = (size_t)(av_len(x_av) + 1);
        -: 6730:	  size_t ng = (size_t)(av_len(g_av) + 1);
        -: 6731:	  if (nx != ng) croak("kruskal_test: 'x' and 'g' must have the same length");
        -: 6732:	  if (nx < 2)   croak("not enough observations");
        -: 6733:
        -: 6734:	  ri = (RankInfo *)safemalloc(nx * sizeof(RankInfo));
        -: 6735:	  group_names = (char **)safecalloc(nx, sizeof(char*)); // Upper bound
        -: 6736:
        -: 6737:	  // Map string group names → contiguous integer IDs
        -: 6738:	  HV *restrict group_map    = newHV();
        -: 6739:	  size_t          next_group_id = 0;
        -: 6740:
        -: 6741:	  for (size_t i = 0; i < nx; i++) {
        -: 6742:		   SV **restrict x_el = av_fetch(x_av, i, 0);
        -: 6743:		   SV **restrict g_el = av_fetch(g_av, i, 0);
        -: 6744:		   if (x_el && SvOK(*x_el) && looks_like_number(*x_el)
        -: 6745:		            && g_el && SvOK(*g_el)) {
        -: 6746:		       const char *restrict g_str = SvPV_nolen(*g_el);
        -: 6747:		       STRLEN               glen  = strlen(g_str);
        -: 6748:		       SV   **restrict id_sv = hv_fetch(group_map, g_str, glen, 0);
        -: 6749:		       size_t group_id;
        -: 6750:		       if (id_sv) {
        -: 6751:		           group_id = SvUV(*id_sv);
        -: 6752:		       } else {
        -: 6753:		           group_id = next_group_id++;
        -: 6754:		           hv_store(group_map, g_str, glen, newSVuv(group_id), 0);
        -: 6755:		           group_names[group_id] = savepvn(g_str, glen); // Save string key
        -: 6756:		       }
        -: 6757:		       ri[valid_n].val = SvNV(*x_el);
        -: 6758:		       ri[valid_n].idx = group_id;
        -: 6759:		       valid_n++;
        -: 6760:		   }
        -: 6761:	  }
        -: 6762:	  k = next_group_id;
        -: 6763:	  SvREFCNT_dec(group_map);
        -: 6764:	}
        -: 6765:
        -: 6766:	/* ------------------------------------------------------------------ */
        -: 6767:	/* 5. Shared post-extraction validation                               */
        -: 6768:	/* ------------------------------------------------------------------ */
        -: 6769:	if (valid_n < 2 || k < 2) { 
        3: 6770:	  Safefree(ri); 
call    0 returned 3
        3: 6771:	  if (group_names) {
        -: 6772:		   for (size_t i = 0; i < k; i++) { if (group_names[i]) Safefree(group_names[i]); }
        3: 6773:		   Safefree(group_names);
        -: 6774:	  }
        -: 6775:	  if (valid_n < 2) croak("not enough observations");
        -: 6776:	  croak("all observations are in the same group");
        -: 6777:	}
function XS_Stats__LikeR_var_test called 6 returned 50% blocks executed 86%
        6: 6778:
        -: 6779:	// 6. Ranking and Tie Accumulation (Reusing LikeR Helper)
        6: 6780:	bool   has_ties = 0;
        6: 6780-block  0
call    0 returned 6
branch  1 taken 6 (fallthrough)
branch  2 taken 0
        -: 6781:	double tie_adj  = rank_and_count_ties(ri, valid_n, &has_ties);
        -: 6782:
        -: 6783:	// 7. Aggregate Sum of Ranks AND Actual Values by Group
        -: 6784:	double *restrict group_rank_sums = (double *)safecalloc(k, sizeof(double));
        -: 6785:	double *restrict group_val_sums  = (double *)safecalloc(k, sizeof(double)); // For Mean
        -: 6786:	size_t *restrict group_counts    = (size_t *)safecalloc(k, sizeof(size_t));
        -: 6787:
        -: 6788:	for (size_t i = 0; i < valid_n; i++) {
        -: 6789:	  size_t g_id = ri[i].idx;
        -: 6790:	  group_rank_sums[g_id] += ri[i].rank;
        -: 6791:	  group_val_sums[g_id]  += ri[i].val;
        -: 6792:	  group_counts[g_id]++;
        -: 6793:	}
        -: 6794:
        -: 6795:	// 8. Calculate STATISTIC
        -: 6796:	double stat_base = 0.0;
        -: 6797:	for (size_t i = 0; i < k; i++) {
        -: 6798:	  if (group_counts[i] > 0)
        -: 6799:		   stat_base += (group_rank_sums[i] * group_rank_sums[i])
        -: 6800:		                / (double)group_counts[i];
        -: 6801:	}
        -: 6802:
        -: 6803:	double n_d  = (double)valid_n;
        -: 6804:	double stat = (12.0 * stat_base / (n_d * (n_d + 1.0))) - 3.0 * (n_d + 1.0);
        -: 6805:	if (tie_adj > 0.0) {
        -: 6806:	  double tie_denom = 1.0 - (tie_adj / (n_d * n_d * n_d - n_d));
        -: 6807:	  stat /= tie_denom;
        -: 6808:	}
        -: 6809:	int    df    = (int)k - 1;
        -: 6810:	double p_val = get_p_value(stat, df);
        -: 6811:
        -: 6812:	// 9. Return structured data exactly like R's htest
        -: 6813:	HV *restrict res = newHV();
        -: 6814:	hv_stores(res, "statistic", newSVnv(stat));
        -: 6815:	hv_stores(res, "parameter", newSViv(df));
        -: 6816:	hv_stores(res, "p_value",   newSVnv(p_val));
        -: 6817:	hv_stores(res, "p.value",   newSVnv(p_val));
        -: 6818:	hv_stores(res, "method",    newSVpv("Kruskal-Wallis rank sum test", 0));
        -: 6819:
        -: 6820:	// 10. Build the group_stats hash
        -: 6821:	HV *restrict group_stats = newHV();
        -: 6822:	HV *restrict stats_mean  = newHV();
        -: 6823:	HV *restrict stats_size  = newHV();
        -: 6824:
        -: 6825:	for (size_t i = 0; i < k; i++) {
        -: 6826:	  if (group_counts[i] > 0 && group_names[i]) {
        -: 6827:		   double mean = group_val_sums[i] / (double)group_counts[i];
        -: 6828:		   size_t nlen = strlen(group_names[i]);
        -: 6829:
        -: 6830:		   hv_store(stats_mean, group_names[i], nlen, newSVnv(mean), 0);
        -: 6831:		   hv_store(stats_size, group_names[i], nlen, newSVuv(group_counts[i]), 0);
        -: 6832:	  }
        -: 6833:	  if (group_names[i]) Safefree(group_names[i]); // Clean up name copy
        -: 6834:	}
        -: 6835:
        -: 6836:	// Embed the nested hashes
        -: 6837:	hv_stores(group_stats, "mean", newRV_noinc((SV*)stats_mean));
        -: 6838:	hv_stores(group_stats, "size", newRV_noinc((SV*)stats_size));
        -: 6839:	hv_stores(res, "group_stats",  newRV_noinc((SV*)group_stats));
        -: 6840:
        -: 6841:	// Memory Cleanup
        -: 6842:	Safefree(group_names);    Safefree(group_rank_sums); 
        -: 6843:	Safefree(group_val_sums); Safefree(group_counts); 
        -: 6844:	Safefree(ri);
        -: 6845:
        -: 6846:	RETVAL = newRV_noinc((SV*)res);
        -: 6847:}
        -: 6848:#line 6849 "LikeR.c"
        -: 6849:	RETVAL = sv_2mortal(RETVAL);
        -: 6850:	ST(0) = RETVAL;
        -: 6851:    }
        -: 6852:    XSRETURN(1);
        -: 6853:}
        -: 6854:
        -: 6855:
        -: 6856:XS_EUPXS(XS_Stats__LikeR_var_test); /* prototype to pass -Wmissing-prototypes */
        -: 6857:XS_EUPXS(XS_Stats__LikeR_var_test)
        -: 6858:{
        -: 6859:    dVAR; dXSARGS;
        -: 6860:    PERL_UNUSED_VAR(cv); /* -W */
        -: 6861:    PERL_UNUSED_VAR(items); /* -W */
        -: 6862:    {
        -: 6863:	SV *	RETVAL;
        -: 6864:#line 6233 "LikeR.xs"
        -: 6865:{
        -: 6866:	SV* restrict x_sv = NULL;
        -: 6867:	SV* restrict y_sv = NULL;
        -: 6868:	double ratio = 1.0, conf_level = 0.95;
        -: 6869:	const char* restrict alternative = "two.sided";
        -: 6870:	unsigned int arg_idx = 0;
        -: 6871:
        -: 6872:	// 1. Shift positional argument 'x' if it's an array reference
        -: 6873:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 6874:	  x_sv = ST(arg_idx);
        -: 6875:	  arg_idx++;
        -: 6876:	}
        -: 6877:
        -: 6878:	// 2. Shift positional argument 'y' if it's an array reference
        -: 6879:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 6880:	  y_sv = ST(arg_idx);
        -: 6881:	  arg_idx++;
        -: 6882:	}
        -: 6883:
        -: 6884:	// Ensure the remaining arguments form complete key-value pairs
        -: 6885:	if ((items - arg_idx) % 2 != 0) {
        -: 6886:	  croak("Usage: var_test(\\@x, \\@y, key => value, ...)");
        -: 6887:	}
        -: 6888:
        -: 6889:	// --- Parse named arguments from the remaining flat stack ---
        -: 6890:	for (; arg_idx < items; arg_idx += 2) {
        -: 6891:	  const char* restrict key = SvPV_nolen(ST(arg_idx));
        -: 6892:	  SV* restrict val = ST(arg_idx + 1);
        -: 6893:
        -: 6894:	  if      (strEQ(key, "x"))           x_sv        = val;
        -: 6895:	  else if (strEQ(key, "y"))           y_sv        = val;
        -: 6896:	  else if (strEQ(key, "ratio"))       ratio       = SvNV(val);
        -: 6897:	  else if (strEQ(key, "conf_level") || strEQ(key, "conf.level")) conf_level = SvNV(val);
        -: 6898:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 6899:	  else croak("var_test: unknown argument '%s'", key);
        -: 6900:	}
        -: 6901:
        -: 6902:	// --- Validate required inputs / types ---
        -: 6903:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 6904:	  croak("var_test: 'x' is a required argument and must be an ARRAY reference");
        -: 6905:	if (!y_sv || !SvROK(y_sv) || SvTYPE(SvRV(y_sv)) != SVt_PVAV)
        -: 6906:	  croak("var_test: 'y' is a required argument and must be an ARRAY reference");
        -: 6907:
        -: 6908:	if (ratio <= 0.0 || !isfinite(ratio)) 
        -: 6909:	  croak("var_test: 'ratio' must be a single positive number");
        -: 6910:	if (conf_level <= 0.0 || conf_level >= 1.0 || !isfinite(conf_level))
        -: 6911:	  croak("var_test: 'conf.level' must be a single number between 0 and 1");
        -: 6912:
        -: 6913:	AV* restrict x_av = (AV*)SvRV(x_sv);
        -: 6914:	AV* restrict y_av = (AV*)SvRV(y_sv);
        -: 6915:	size_t nx_raw = av_len(x_av) + 1;
        -: 6916:	size_t ny_raw = av_len(y_av) + 1;
        -: 6917:
        -: 6918:	// --- Computation via Welford's Algorithm (ignoring NaNs) ---
        -: 6919:	double mean_x = 0.0, M2_x = 0.0;
        -: 6920:	size_t nx = 0;
        -: 6921:	for (size_t i = 0; i < nx_raw; i++) {
        -: 6922:		SV** restrict tv = av_fetch(x_av, i, 0);
        -: 6923:		if (tv && SvOK(*tv) && looks_like_number(*tv)) {
        -: 6924:			double val = SvNV(*tv);
        -: 6925:			if (!isnan(val) && isfinite(val)) {
        -: 6926:				nx++;
        3: 6927:				double delta = val - mean_x;
call    0 returned 3
        3: 6928:				mean_x += delta / nx;
        -: 6929:				M2_x += delta * (val - mean_x);
        3: 6930:			}
        -: 6931:		}
        -: 6932:	}
        -: 6933:
        -: 6934:	double mean_y = 0.0, M2_y = 0.0;
        -: 6935:	size_t ny = 0;
        -: 6936:	for (size_t i = 0; i < ny_raw; i++) {
function boot_Stats__LikeR called 1 returned 100% blocks executed 100%
        1: 6937:	  SV** restrict tv = av_fetch(y_av, i, 0);
        -: 6938:	  if (tv && SvOK(*tv) && looks_like_number(*tv)) {
        -: 6939:		   double val = SvNV(*tv);
        -: 6940:		   if (!isnan(val) && isfinite(val)) {
        -: 6941:		       ny++;
        1: 6942:		       double delta = val - mean_y;
        1: 6942-block  0
call    0 returned 1
        -: 6943:		       mean_y += delta / ny;
        -: 6944:		       M2_y += delta * (val - mean_y);
        -: 6945:		   }
        -: 6946:	  }
        1: 6947:	}
        -: 6948:
        -: 6949:	if (nx < 2) croak("not enough 'x' observations");
        -: 6950:	if (ny < 2) croak("not enough 'y' observations");
        -: 6951:
        -: 6952:	double df_x = (double)(nx - 1);
        -: 6953:	double df_y = (double)(ny - 1);
        -: 6954:	double var_x = M2_x / df_x;
        -: 6955:	double var_y = M2_y / df_y;
        -: 6956:
        -: 6957:	if (var_y == 0.0) croak("var_test: variance of 'y' is zero (cannot divide by zero)");
        -: 6958:
        -: 6959:	// --- Statistics Math ---
        -: 6960:	double estimate = var_x / var_y;
        1: 6961:	double statistic = estimate / ratio;
call    0 returned 1
        1: 6962:
call    0 returned 1
        1: 6963:	double p_val = pf(statistic, df_x, df_y);
call    0 returned 1
        1: 6964:	double ci_lower = 0.0, ci_upper = INFINITY;
call    0 returned 1
        1: 6965:
call    0 returned 1
        1: 6966:	if (strcmp(alternative, "less") == 0) {
call    0 returned 1
        1: 6967:	  ci_upper = estimate / qf_bisection(1.0 - conf_level, df_x, df_y);
call    0 returned 1
        1: 6968:	} else if (strcmp(alternative, "greater") == 0) {
call    0 returned 1
        1: 6969:	  p_val = 1.0 - p_val;
call    0 returned 1
        1: 6970:	  ci_lower = estimate / qf_bisection(conf_level, df_x, df_y);
call    0 returned 1
        1: 6971:	} else {
call    0 returned 1
        1: 6972:	  // two.sided
call    0 returned 1
        1: 6973:	  double p1 = p_val;
call    0 returned 1
        1: 6974:	  double p2 = 1.0 - p_val;
call    0 returned 1
        1: 6975:	  p_val = 2.0 * (p1 < p2 ? p1 : p2);
call    0 returned 1
        1: 6976:
call    0 returned 1
        1: 6977:	  double beta = (1.0 - conf_level) / 2.0;
call    0 returned 1
        1: 6978:	  ci_lower = estimate / qf_bisection(1.0 - beta, df_x, df_y);
call    0 returned 1
        1: 6979:	  ci_upper = estimate / qf_bisection(beta, df_x, df_y);
call    0 returned 1
        1: 6980:	}
call    0 returned 1
        1: 6981:
call    0 returned 1
        1: 6982:	// --- Pack Results ---
call    0 returned 1
        1: 6983:	HV* restrict results = newHV();
call    0 returned 1
        1: 6984:	hv_store(results, "statistic", 9, newSVnv(statistic), 0);
call    0 returned 1
        1: 6985:
call    0 returned 1
        1: 6986:	AV* restrict param_av = newAV();
call    0 returned 1
        1: 6987:	av_push(param_av, newSVnv(df_x));
call    0 returned 1
        1: 6988:	av_push(param_av, newSVnv(df_y));
call    0 returned 1
        1: 6989:	hv_store(results, "parameter", 9, newRV_noinc((SV*)param_av), 0);
call    0 returned 1
        1: 6990:
call    0 returned 1
        1: 6991:	hv_store(results, "p_value", 7, newSVnv(p_val), 0);
call    0 returned 1
        1: 6992:
call    0 returned 1
        1: 6993:	AV* restrict conf_int = newAV();
call    0 returned 1
        -: 6994:	av_push(conf_int, newSVnv(ci_lower));
        -: 6995:	av_push(conf_int, newSVnv(ci_upper));
        -: 6996:	hv_store(results, "conf_int", 8, newRV_noinc((SV*)conf_int), 0);
        -: 6997:
        -: 6998:	hv_store(results, "estimate", 8, newSVnv(estimate), 0);
        -: 6999:	hv_store(results, "null_value", 10, newSVnv(ratio), 0);
        -: 7000:	hv_store(results, "alternative", 11, newSVpv(alternative, 0), 0);
        1: 7001:	hv_store(results, "method", 6, newSVpv("F test to compare two variances", 0), 0);
call    0 returned 1
        -: 7002:
        1: 7003:	RETVAL = newRV_noinc((SV*)results);
        -: 7004:}
        -: 7005:#line 7006 "LikeR.c"
        -: 7006:	RETVAL = sv_2mortal(RETVAL);
        -: 7007:	ST(0) = RETVAL;
        -: 7008:    }
        -: 7009:    XSRETURN(1);
        -: 7010:}
        -: 7011:
        -: 7012:
        -: 7013:XS_EUPXS(XS_Stats__LikeR_sample); /* prototype to pass -Wmissing-prototypes */
        -: 7014:XS_EUPXS(XS_Stats__LikeR_sample)
        -: 7015:{
        -: 7016:    dVAR; dXSARGS;
        -: 7017:    if (items < 1 || items > 2)
        -: 7018:       croak_xs_usage(cv,  "ref, n = 1");
        -: 7019:    {
        -: 7020:	SV *	ref = ST(0)
        -: 7021:;
        -: 7022:	IV	n;
        -: 7023:#line 6380 "LikeR.xs"
        -: 7024:    SV *restrict ret = &PL_sv_undef;
        -: 7025:#line 7026 "LikeR.c"
        -: 7026:	SV *	RETVAL;
        -: 7027:
        -: 7028:	if (items < 2)
        -: 7029:	    n = 1;
        -: 7030:	else {
        -: 7031:	    n = (IV)SvIV(ST(1))
        -: 7032:;
        -: 7033:	}
        -: 7034:#line 6382 "LikeR.xs"
        -: 7035:	if (!PL_srand_called) {
        -: 7036:	  (void)seedDrand01((Rand_seed_t)Perl_seed(aTHX));
        -: 7037:	  PL_srand_called = TRUE;
        -: 7038:	}
        -: 7039:	if (n < 0) n = 0;
        -: 7040:	if (SvROK(ref)) {
        -: 7041:		SV *restrict rv = SvRV(ref);
        -: 7042:		/* --- HASH REFERENCE --- */
        -: 7043:		if (SvTYPE(rv) == SVt_PVHV) {
        -: 7044:			HV *restrict hv    = (HV *)rv;
        -: 7045:			I32 count = hv_iterinit(hv);
        -: 7046:			I32 limit = (n < (IV)count) ? (I32)n : count;
        -: 7047:			HV *restrict ret_hv = newHV();
        -: 7048:
        -: 7049:			if (count > 0 && limit > 0) {
        -: 7050:				 HE **restrict entries;
        -: 7051:				 HE  *restrict entry;
        -: 7052:				 unsigned i;
        -: 7053:
        -: 7054:				 Newx(entries, count, HE *);
        -: 7055:
        -: 7056:				 /* Collect all HE pointers in one pass */
        -: 7057:				 i = 0;
        -: 7058:				 while ((entry = hv_iternext(hv)))
        -: 7059:				     entries[i++] = entry;
        -: 7060:
        -: 7061:				 /* Partial Fisher-Yates (only 'limit' passes) */
        -: 7062:				 for (i = 0; i < limit; i++) {
        -: 7063:				     I32 j    = i + (I32)(Drand01() * (count - i));
        -: 7064:				     HE *restrict tmp  = entries[i];
        -: 7065:				     entries[i] = entries[j];
        -: 7066:				     entries[j] = tmp;
        -: 7067:				 }
        -: 7068:
        -: 7069:				 /* Pre-size result hash to avoid rehashing during population */
        -: 7070:				 hv_ksplit(ret_hv, limit);
        -: 7071:
        -: 7072:				 for (i = 0; i < limit; i++) {
        -: 7073:				     HEK *restrict hek = HeKEY_hek(entries[i]);
        -: 7074:				     /*
        -: 7075:				      * hv_store() with a precomputed hash skips the hash
        -: 7076:				      * computation entirely.  Negative klen signals UTF-8.
        -: 7077:				      */
        -: 7078:				     (void)hv_store(
        -: 7079:				         ret_hv,
        -: 7080:				         HEK_KEY(hek),
        -: 7081:				         HEK_UTF8(hek) ? -(I32)HEK_LEN(hek) : (I32)HEK_LEN(hek),
        -: 7082:				         SvREFCNT_inc(HeVAL(entries[i])),  /* HeVAL: direct macro, no call */
        -: 7083:				         HeHASH(entries[i])                /* reuse precomputed hash */
        -: 7084:				     );
        -: 7085:				 }
        -: 7086:				 Safefree(entries);
        -: 7087:			}
        -: 7088:			ret = newRV_noinc((SV *)ret_hv);
        -: 7089:		} else if (SvTYPE(rv) == SVt_PVAV) {/* --- ARRAY REFERENCE --- */
        -: 7090:			AV    *restrict av    = (AV *)rv;
        -: 7091:			size_t count = av_top_index(av) + 1;  /* signed; 0 for empty AV */
        -: 7092:			size_t limit = (n < count) ? (size_t)n : count;
        -: 7093:			AV    *restrict ret_av = newAV();
        -: 7094:
        -: 7095:			/* Pre-allocate the result array to avoid incremental reallocs */
        -: 7096:			if (n > 0)
        -: 7097:				 av_extend(ret_av, (size_t)n - 1);
        -: 7098:
        -: 7099:			if (count > 0) {
        -: 7100:				 SV    **restrict src = AvARRAY(av);   /* direct pointer into AV's C array */
        -: 7101:				 size_t *restrict idx;
        -: 7102:				 size_t  i;
        -: 7103:
        -: 7104:				 /* Shuffle indices rather than SV** to keep the original AV intact */
        -: 7105:				 Newx(idx, count, size_t);
        -: 7106:				 for (i = 0; i < count; i++)
        -: 7107:				     idx[i] = i;
        -: 7108:
        -: 7109:				 /* Partial Fisher-Yates on the index array */
        -: 7110:				 for (i = 0; i < limit; i++) {
        -: 7111:				     size_t j   = i + (size_t)(Drand01() * (count - i));
        -: 7112:				     size_t tmp = idx[i];
        -: 7113:				     idx[i]  = idx[j];
        -: 7114:				     idx[j]  = tmp;
        -: 7115:				 }
        -: 7116:
        -: 7117:				 for (i = 0; i < (size_t)n; i++) {
        -: 7118:				     if (i < limit) {
        -: 7119:				         SV *restrict sv = src[idx[i]];   /* AvARRAY direct access — no av_fetch call */
        -: 7120:				         SV *push_sv;
        -: 7121:							if (sv && sv != &PL_sv_undef)
        -: 7122:								 push_sv = SvREFCNT_inc(sv);
        -: 7123:							else
        -: 7124:								 push_sv = newSV(0);
        -: 7125:							av_push(ret_av, push_sv);
        -: 7126:				     } else {
        -: 7127:				         av_push(ret_av, newSV(0));
        -: 7128:				     }
        -: 7129:				 }
        -: 7130:				 Safefree(idx);
        -: 7131:			} else {
        -: 7132:				for (size_t i = 0; i < (size_t)n; i++)
        -: 7133:				    av_push(ret_av, newSV(0));
        -: 7134:			}
        -: 7135:			ret = newRV_noinc((SV *)ret_av);
        -: 7136:		}
        -: 7137:	}
        -: 7138:	RETVAL = ret;
        -: 7139:#line 7140 "LikeR.c"
        -: 7140:	RETVAL = sv_2mortal(RETVAL);
        -: 7141:	ST(0) = RETVAL;
        -: 7142:    }
        -: 7143:    XSRETURN(1);
        -: 7144:}
        -: 7145:
        -: 7146:#ifdef __cplusplus
        -: 7147:extern "C" {
        -: 7148:#endif
        -: 7149:XS_EXTERNAL(boot_Stats__LikeR); /* prototype to pass -Wmissing-prototypes */
        -: 7150:XS_EXTERNAL(boot_Stats__LikeR)
        -: 7151:{
        -: 7152:#if PERL_VERSION_LE(5, 21, 5)
        -: 7153:    dVAR; dXSARGS;
        -: 7154:#else
        -: 7155:    dVAR; dXSBOOTARGSXSAPIVERCHK;
        -: 7156:#endif
        -: 7157:#if PERL_VERSION_LE(5, 8, 999) /* PERL_VERSION_LT is 5.33+ */
        -: 7158:    char* file = __FILE__;
        -: 7159:#else
        -: 7160:    const char* file = __FILE__;
        -: 7161:#endif
        -: 7162:
        -: 7163:    PERL_UNUSED_VAR(file);
        -: 7164:
        -: 7165:    PERL_UNUSED_VAR(cv); /* -W */
        -: 7166:    PERL_UNUSED_VAR(items); /* -W */
        -: 7167:#if PERL_VERSION_LE(5, 21, 5)
        -: 7168:    XS_VERSION_BOOTCHECK;
        -: 7169:#  ifdef XS_APIVERSION_BOOTCHECK
        -: 7170:    XS_APIVERSION_BOOTCHECK;
        -: 7171:#  endif
        -: 7172:#endif
        -: 7173:
        -: 7174:        newXS_deffile("Stats::LikeR::ks_test", XS_Stats__LikeR_ks_test);
        -: 7175:        newXS_deffile("Stats::LikeR::wilcox_test", XS_Stats__LikeR_wilcox_test);
        -: 7176:        newXS_deffile("Stats::LikeR::_chisq_c", XS_Stats__LikeR__chisq_c);
        -: 7177:        (void)newXSproto_portable("Stats::LikeR::write_table", XS_Stats__LikeR_write_table, file, ";@");
        -: 7178:        (void)newXSproto_portable("Stats::LikeR::_parse_csv_file", XS_Stats__LikeR__parse_csv_file, file, "$$$;$");
        -: 7179:        (void)newXSproto_portable("Stats::LikeR::cov", XS_Stats__LikeR_cov, file, "$$;$");
        -: 7180:        (void)newXSproto_portable("Stats::LikeR::glm", XS_Stats__LikeR_glm, file, ";@");
        -: 7181:        (void)newXSproto_portable("Stats::LikeR::cor_test", XS_Stats__LikeR_cor_test, file, ";@");
        -: 7182:        (void)newXSproto_portable("Stats::LikeR::shapiro_test", XS_Stats__LikeR_shapiro_test, file, "$");
        -: 7183:        (void)newXSproto_portable("Stats::LikeR::min", XS_Stats__LikeR_min, file, "@");
        -: 7184:        (void)newXSproto_portable("Stats::LikeR::max", XS_Stats__LikeR_max, file, "@");
        -: 7185:        (void)newXSproto_portable("Stats::LikeR::runif", XS_Stats__LikeR_runif, file, ";@");
        -: 7186:        (void)newXSproto_portable("Stats::LikeR::rbinom", XS_Stats__LikeR_rbinom, file, ";@");
        -: 7187:        (void)newXSproto_portable("Stats::LikeR::hist", XS_Stats__LikeR_hist, file, "$;@");
        -: 7188:        (void)newXSproto_portable("Stats::LikeR::quantile", XS_Stats__LikeR_quantile, file, ";@");
        -: 7189:        (void)newXSproto_portable("Stats::LikeR::mean", XS_Stats__LikeR_mean, file, "@");
        -: 7190:        (void)newXSproto_portable("Stats::LikeR::sum", XS_Stats__LikeR_sum, file, "@");
        -: 7191:        (void)newXSproto_portable("Stats::LikeR::sd", XS_Stats__LikeR_sd, file, "@");
        -: 7192:        (void)newXSproto_portable("Stats::LikeR::var", XS_Stats__LikeR_var, file, "@");
        -: 7193:        (void)newXSproto_portable("Stats::LikeR::t_test", XS_Stats__LikeR_t_test, file, ";@");
        -: 7194:        (void)newXSproto_portable("Stats::LikeR::p_adjust", XS_Stats__LikeR_p_adjust, file, "$;$");
        -: 7195:        (void)newXSproto_portable("Stats::LikeR::median", XS_Stats__LikeR_median, file, "@");
        -: 7196:        (void)newXSproto_portable("Stats::LikeR::cor", XS_Stats__LikeR_cor, file, "$;$$");
        -: 7197:        (void)newXSproto_portable("Stats::LikeR::scale", XS_Stats__LikeR_scale, file, "@");
        -: 7198:        (void)newXSproto_portable("Stats::LikeR::matrix", XS_Stats__LikeR_matrix, file, ";@");
        -: 7199:        (void)newXSproto_portable("Stats::LikeR::lm", XS_Stats__LikeR_lm, file, ";@");
        -: 7200:        (void)newXSproto_portable("Stats::LikeR::seq", XS_Stats__LikeR_seq, file, "$$;$");
        -: 7201:        (void)newXSproto_portable("Stats::LikeR::rnorm", XS_Stats__LikeR_rnorm, file, ";@");
        -: 7202:        (void)newXSproto_portable("Stats::LikeR::aov", XS_Stats__LikeR_aov, file, "$$");
        -: 7203:        newXS_deffile("Stats::LikeR::fisher_test", XS_Stats__LikeR_fisher_test);
        -: 7204:        newXS_deffile("Stats::LikeR::power_t_test", XS_Stats__LikeR_power_t_test);
        -: 7205:        newXS_deffile("Stats::LikeR::kruskal_test", XS_Stats__LikeR_kruskal_test);
        -: 7206:        newXS_deffile("Stats::LikeR::var_test", XS_Stats__LikeR_var_test);
        -: 7207:        newXS_deffile("Stats::LikeR::sample", XS_Stats__LikeR_sample);
        -: 7208:#if PERL_VERSION_LE(5, 21, 5)
        -: 7209:#  if PERL_VERSION_GE(5, 9, 0)
        -: 7210:    if (PL_unitcheckav)
        -: 7211:        call_list(PL_scopestack_ix, PL_unitcheckav);
        -: 7212:#  endif
        -: 7213:    XSRETURN_YES;
        -: 7214:#else
        -: 7215:    Perl_xs_boot_epilog(aTHX_ ax);
        -: 7216:#endif
        -: 7217:}
        -: 7218:
        -: 7219:#ifdef __cplusplus
        -: 7220:}
        -: 7221:#endif
