        -:    0:Source:LikeR.xs
        -:    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:#define _GNU_SOURCE
        -:    2:/* --- C HELPER SECTION --- */
        -:    3:#define PERL_NO_GET_CONTEXT
        -:    4:#include "EXTERN.h"
        -:    5:#include "perl.h"
        -:    6:#include "XSUB.h"
        -:    7:#include "ppport.h"
        -:    8:#include <math.h>
        -:    9:#include <ctype.h>
        -:   10:#include <stdlib.h>
        -:   11:#include <float.h>
        -:   12:#include <string.h>
        -:   13:#include <stdint.h>   /* uint64_t — harmless if perl.h already pulled it in */
        -:   14:
        -:   15:/* ── sample(): private splitmix64 PRNG ─────────────────────────────────────
        -:   16: *
        -:   17: * sample() gets its own PRNG state, completely separate from Drand01.
        -:   18: * That means generate_binomial(), ruif(), rbinom(), and every other caller
        -:   19: * of Drand01() are unaffected — their streams are never advanced or reseeded
        -:   20: * by anything sample() does.
        -:   21: *
        -:   22: * Seeding is lazy (first call) and reads from /dev/urandom; falls back to
        -:   23: * time()^PID on systems without it.  No aTHX needed: all calls are plain C.
        -:   24: * PERL_NO_GET_CONTEXT is therefore not a concern here.
        -:   25: */
        -:   26:static uint64_t sample__state  = 0;
        -:   27:static bool     sample__seeded = FALSE;
        -:   28:
function exact_pnt called 229 returned 100% blocks executed 90%
      229:   29:PERL_STATIC_INLINE uint64_t
     229*:   30:sample__mix64(void)
      229:   30-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 229
    %%%%%:   30-block  1
        -:   31:{
      229:   32:	uint64_t z = (sample__state += UINT64_C(0x9e3779b97f4a7c15));
      229:   33:	z = (z ^ (z >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
      229:   34:	z = (z ^ (z >> 27)) * UINT64_C(0x94d049bb133111eb);
      229:   35:	return z ^ (z >> 31);
        -:   36:}
      229:   37:
      229:   38:static void
        -:   39:sample__seed(void)
  6870000:   40:{
      229:   40-block  0
  6870000:   40-block  1
branch  0 taken 6869771
branch  1 taken 229 (fallthrough)
  6869771:   41:	uint64_t s = 0;
  6869771:   42:	size_t   got = 0;
        -:   43:	FILE    *restrict ur  = fopen("/dev/urandom", "rb");
        -:   44:	if (ur) { got = fread(&s, sizeof s, 1, ur); fclose(ur); }
  6869771:   45:	if (got != 1 || s == 0)
  6869771:   46:	  s = (uint64_t)time(NULL) ^ ((uint64_t)getpid() << 32);
        -:   47:	sample__state  = s;
        -:   48:	(void)sample__mix64();   /* discard first output to warm the state */
  6869771:   49:	sample__seeded = TRUE;
  6869771:   50:}
        -:   51:
  6869771:   52:/* Uniform integer in [0, upper) — rejection loop, no modulo bias */
  6869771:   52-block  0
branch  0 taken 3435000 (fallthrough)
branch  1 taken 3434771
  3435000:   52-block  1
  3434771:   52-block  2
  6869771:   53:PERL_STATIC_INLINE size_t
  6869771:   53-block  0
        -:   54:sample__rand(size_t upper) {
        -:   55:	const uint64_t u = (uint64_t)upper;
      229:   56:	const uint64_t t = (uint64_t)(-(uint64_t)u) % u;
      229:   56-block  0
        -:   57:	uint64_t r;
        -:   58:	do { r = sample__mix64(); } while (r < t);
        -:   59:	return (size_t)(r % u);
        -:   60:}
        -:   61:/* ── end sample() private PRNG ─────────────────────────────────────────── */
function compare_rank called 11 returned 100% blocks executed 100%
       11:   62:
       11:   63:/* Ensure Perl's PRNG is seeded, matching the lazy-evaluation of Perl's rand() */
       11:   64:#define AUTO_SEED_PRNG() \
       11:   64-block  0
        -:   65:    do { \
        -:   66:        if (!PL_srand_called) { \
function compare_index called 11 returned 100% blocks executed 100%
       11:   67:            (void)seedDrand01((Rand_seed_t)Perl_seed(aTHX)); \
       11:   68:            PL_srand_called = TRUE; \
       11:   68-block  0
        -:   69:        } \
        -:   70:    } while (0)
function compute_ranks called 2 returned 100% blocks executed 95%
        2:   71:
        2:   72:// ---------------------------------------
        2:   72-block  0
call    0 returned 2
       12:   73://   Helpers for Random Number Generation
       12:   73-block  0
branch  0 taken 10
branch  1 taken 2 (fallthrough)
       10:   74:// ---------------------------------------
       10:   75:#ifndef M_PI
       10:   75-block  0
        -:   76:#define M_PI 3.14159265358979323846
        2:   77:#endif
        2:   77-block  0
call    0 returned 2
        -:   78:// C helper for EXACT Non-central T-distribution CDF via Numerical Integration.
       12:   79:// This perfectly replicates R's pt(..., ncp) exactness without requiring complex Beta functions.
       12:   79-block  0
branch  0 taken 10
branch  1 taken 2 (fallthrough)
       10:   80:static double exact_pnt(double t, double df, double ncp) {
      10*:   81:	if (df <= 0.0) return 0.0;
       10:   81-block  0
    %%%%%:   81-block  1
       10:   81-block  2
branch  0 taken 8 (fallthrough)
branch  1 taken 2
        8:   81-block  3
branch  2 taken 0
branch  3 taken 8 (fallthrough)
       10:   82:	unsigned short int n_steps = 30000;
       20:   83:	double step = 1.0 / n_steps;
       10:   83-block  0
       10:   83-block  1
       20:   83-block  2
branch  0 taken 10
branch  1 taken 10 (fallthrough)
       10:   84:	double integral = 0.0, half_df = df / 2.0;
       10:   84-block  0
        -:   85:
        2:   86:	double log_coef = log(2.0) + half_df * log(half_df) - lgamma(half_df);
        2:   86-block  0
call    0 returned 2
       12:   87:	double root_half = 0.70710678118654752440; // 1 / sqrt(2)
       10:   87-block  0
       12:   87-block  1
branch  0 taken 10
branch  1 taken 2 (fallthrough)
        2:   88:
        2:   88-block  0
call    0 returned 2
        2:   89:	for (unsigned short i = 1; i < n_steps; i++) {
        -:   90:		double u = i * step;
        -:   91:		double w = u / (1.0 - u);
function generate_binomial called 20499 returned 100% blocks executed 100%
    20499:   92:		// Scaled Chi-distribution log-density
    20499:   93:		double log_M = log_coef + (df - 1.0) * log(w) - half_df * w * w;
    20499:   93-block  0
branch  0 taken 100 (fallthrough)
branch  1 taken 20399
      100:   93-block  1
    20399:   94:		double M = exp(log_M);
    20399:   94-block  0
branch  0 taken 100 (fallthrough)
branch  1 taken 20299
      100:   94-block  1
        -:   95:		// Exact Normal CDF using the C standard library's erfc function
    20299:   96:		double z = t * w - ncp;
   312290:   97:		double pnorm_val = 0.5 * erfc(-z * root_half);
    20299:   97-block  0
   291991:   97-block  1
   312290:   97-block  2
branch  0 taken 291991
branch  1 taken 20299 (fallthrough)
   291991:   98:		double weight = (i % 2 != 0) ? 4.0 : 2.0;
   291991:   98-block  0
call    0 returned 291991
branch  1 taken 105698 (fallthrough)
branch  2 taken 186293
   105698:   98-block  1
        -:   99:		integral += weight * (pnorm_val * M / ((1.0 - u) * (1.0 - u)));
    20299:  100:	}
    20299:  100-block  0
        -:  101:	return integral * (step / 3.0);
        -:  102:}
function log_choose called 142 returned 100% blocks executed 100%
      142:  103:// --- Math Helpers for P-values and Confidence Intervals --- 
      142:  104:
      142:  104-block  0
        -:  105:// Ranking helper with tie adjustment (matches R's tie handling)
        -:  106:typedef struct { double val; size_t idx; double rank; } RankInfo;
        -:  107:static int compare_rank(const void *restrict a, const void *restrict b) {
function calc_tails_logspace called 404 returned 100% blocks executed 100%
      404:  108:	double diff = ((RankInfo*)a)->val - ((RankInfo*)b)->val;
      404:  109:	return (diff > 0) - (diff < 0);
        -:  110:}
     3352:  111:
      404:  111-block  0
     2948:  111-block  1
     3352:  111-block  2
branch  0 taken 2948
branch  1 taken 404 (fallthrough)
     2948:  112:static int compare_index(const void *restrict a, const void *restrict b) {
     2948:  113:	return ((RankInfo*)a)->idx - ((RankInfo*)b)->idx;
     2948:  113-block  0
branch  0 taken 2209 (fallthrough)
branch  1 taken 739
     2209:  113-block  1
        -:  114:}
        -:  115:
      404:  116:static void compute_ranks(double *restrict data, double *restrict ranks, size_t n) {
     3352:  117:	RankInfo *restrict items = safemalloc(n * sizeof(RankInfo));
      404:  117-block  0
     3352:  117-block  1
branch  0 taken 2948
branch  1 taken 404 (fallthrough)
     2948:  118:	for (size_t i = 0; i < n; i++) {
     2948:  118-block  0
        -:  119:		items[i].val = data[i];
        -:  120:		items[i].idx = i;
      404:  121:	}
      404:  122:	qsort(items, n, sizeof(RankInfo), compare_rank);
        -:  123:	// Handle ties by averaging ranks
     3352:  124:	for (size_t i = 0; i < n; ) {
      404:  124-block  0
     2948:  124-block  1
     3352:  124-block  2
branch  0 taken 2948
branch  1 taken 404 (fallthrough)
     2948:  125:		size_t j = i + 1;
     2948:  126:		while (j < n && items[j].val == items[i].val) j++;
     2948:  126-block  0
branch  0 taken 2486 (fallthrough)
branch  1 taken 462
     2486:  126-block  1
     2948:  127:		double avg_rank = (i + 1 + j) / 2.0;
     2948:  127-block  0
branch  0 taken 866 (fallthrough)
branch  1 taken 2082
      866:  127-block  1
        -:  128:		for (size_t k = i; k < j; k++) items[k].rank = avg_rank;
      404:  129:		i = j;
        -:  130:	}
        -:  131:	qsort(items, n, sizeof(RankInfo), compare_index);
function calculate_exact_stats called 5 returned 100% blocks executed 96%
        5:  132:	for (size_t i = 0; i < n; i++) ranks[i] = items[i].rank;
        5:  133:	Safefree(items);
        5:  134:}
        5:  135:// Generates a single binomial random variate. 
        5:  135-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4:  135-block  1
        1:  135-block  2
        5:  136://Uses the standard Bernoulli trial loop. Drand01() taps into Perl's PRNG.
        -:  137:static size_t generate_binomial(const size_t size, const double prob) {
        5:  138:	if (prob <= 0.0) return 0;
        5:  139:	if (prob >= 1.0) return size;
        -:  140:
        5:  141:	size_t successes = 0;
        5:  141-block  0
call    0 returned 5
        5:  142:	for (size_t i = 0; i < size; i++) {
call    0 returned 5
       38:  143:		if (Drand01() <= prob) successes++;
       38:  143-block  0
branch  0 taken 33
branch  1 taken 5 (fallthrough)
       33:  144:	}
       33:  144-block  0
call    0 returned 33
call    1 returned 33
        -:  145:	return successes;
        -:  146:}
        -:  147:// Helper: log combination
       5*:  148:static double log_choose(size_t n, size_t k) {
        5:  148-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%:  148-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%:  148-block  2
       5*:  149:	return lgamma((double)n + 1.0) - lgamma((double)k + 1.0) - lgamma((double)(n - k) + 1.0);
        5:  149-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%:  149-block  1
        5:  150:}
        5:  150-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        1:  150-block  1
        -:  151:
        4:  152:// Log-space tails for non-central hypergeometric
      232:  153: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) {
        4:  153-block  0
      228:  153-block  1
      232:  153-block  2
branch  0 taken 232
branch  1 taken 0 (fallthrough)
      232:  154:	double max_d = -1e300, log_omega = log(omega);
      232:  155:
     1856:  156:	for(size_t k = 0; k <= max_x - min_x; ++k) {
      232:  156-block  0
     1624:  156-block  1
     1856:  156-block  2
branch  0 taken 1624
branch  1 taken 232 (fallthrough)
     1624:  157:	  double d_val = logdc[k] + log_omega * (min_x + k);
     1624:  158:	  if (d_val > max_d) max_d = d_val;
     1624:  158-block  0
branch  0 taken 1346 (fallthrough)
branch  1 taken 278
     1346:  158-block  1
        -:  159:	}
      232:  160:
     1856:  161:	double sum_d = 0.0;
      232:  161-block  0
     1856:  161-block  1
branch  0 taken 1624
branch  1 taken 232 (fallthrough)
     1624:  162:	for(size_t k = 0; k <= max_x - min_x; ++k) {
     1624:  163:	  sum_d += exp(logdc[k] + log_omega * (min_x + k) - max_d);
     1624:  164:	}
     1624:  164-block  0
        -:  165:
      232:  166:	*lower_tail = 0.0;
        -:  167:	*upper_tail = 0.0;
      232:  168:
      232:  168-block  0
branch  0 taken 140 (fallthrough)
branch  1 taken 92
      140:  168-block  1
       92:  169:	for(size_t k = 0; k <= max_x - min_x; ++k) {
       92:  169-block  0
      232:  170:	  double p_prob = exp(logdc[k] + log_omega * (min_x + k) - max_d) / sum_d;
      232:  170-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 228
        4:  170-block  1
        -:  171:	  if (min_x + k <= a) *lower_tail += p_prob;
        4:  172:	  if (min_x + k >= a) *upper_tail += p_prob;
        4:  172-block  0
        -:  173:	}
        -:  174:}
        5:  175:
        5:  176:// Exact stats using log-space
        -:  177: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) {
        -:  178:    double alpha = 1.0 - conf_level;
        5:  179:    size_t r1 = a + b, r2 = c + d, c1 = a + c;
        5:  179-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4:  180:    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
        4:  180-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1:  180-block  1
        3:  180-block  2
        4:  181:    size_t max_x = (r1 < c1) ? r1 : c1;
        4:  181-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4:  182:    
      232:  183:    bool is_less = (strcmp(alt, "less") == 0);
        4:  183-block  0
      228:  183-block  1
      232:  183-block  2
branch  0 taken 232
branch  1 taken 0 (fallthrough)
      232:  184:    bool is_greater = (strcmp(alt, "greater") == 0);
      232:  185:
      232:  186:    double *restrict logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
      232:  186-block  0
call    0 returned 232
      232:  187:    double denom = log_choose(r1 + r2, c1);
      232:  188:    for(size_t x = min_x; x <= max_x; ++x) {
branch  0 taken 103 (fallthrough)
branch  1 taken 129
      103:  188-block  0
      232:  189:        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
      232:  189-block  0
branch  0 taken 126 (fallthrough)
branch  1 taken 106
      126:  189-block  1
      106:  190:    }
      106:  190-block  0
      232:  191:
      232:  191-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 228
        4:  191-block  1
        -:  192:    // MLE
        4:  193:    if (a == min_x && a == max_x) *mle_or = 1.0;
        4:  193-block  0
        -:  194:    else if (a == min_x) *mle_or = 0.0;
        -:  195:    else if (a == max_x) *mle_or = INFINITY;
        -:  196:    else {
        -:  197:        double log_low = -100.0, log_high = 100.0;
        5:  198:        for (unsigned short int i = 0; i < 3000; i++) {
        5:  198-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4:  199:            double log_mid = 0.5 * (log_low + log_high);
        4:  199-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1:  199-block  1
        3:  199-block  2
        4:  200:            double max_d = -1e300;
        4:  200-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 1
        3:  201:            for(size_t k = 0; k <= max_x - min_x; ++k) {
      172:  202:                double d_val = logdc[k] + log_mid * (min_x + k);
        3:  202-block  0
      169:  202-block  1
      172:  202-block  2
branch  0 taken 172
branch  1 taken 0 (fallthrough)
      172:  203:                if (d_val > max_d) max_d = d_val;
      172:  204:            }
      172:  205:            double sum_d = 0.0, exp_val = 0.0;
      172:  205-block  0
call    0 returned 172
      172:  206:            for(size_t k = 0; k <= max_x - min_x; ++k) {
      172:  207:                double p_prob = exp(logdc[k] + log_mid * (min_x + k) - max_d);
branch  0 taken 84 (fallthrough)
branch  1 taken 88
       84:  207-block  0
      172:  208:                sum_d += p_prob;
      172:  208-block  0
branch  0 taken 84 (fallthrough)
branch  1 taken 88
       84:  208-block  1
       88:  209:                exp_val += (min_x + k) * p_prob;
       88:  209-block  0
      172:  210:            }
      172:  210-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 169
        3:  210-block  1
        -:  211:            exp_val /= sum_d;
        3:  212:            
        3:  212-block  0
        -:  213:            if (exp_val > a) log_high = log_mid;
        -:  214:            else log_low = log_mid;
        5:  215:            if (log_high - log_low < 1e-15) break;
        5:  215-block  0
call    0 returned 5
        5:  216:        }
        -:  217:        *mle_or = exp(0.5 * (log_low + log_high));
        -:  218:    }
function exact_p_value called 5 returned 100% blocks executed 97%
        5:  219:
        5:  220:    *ci_low = 0.0;
        5:  221:    *ci_high = INFINITY;
        5:  221-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4:  221-block  1
        1:  221-block  2
        5:  222:
        -:  223:    // Lower CI
        5:  224:    if (!is_less) { 
        5:  224-block  0
call    0 returned 5
        5:  225:        double target_alpha = is_greater ? alpha : alpha / 2.0;
call    0 returned 5
       38:  226:        if (a != min_x) {
       38:  226-block  0
branch  0 taken 33
branch  1 taken 5 (fallthrough)
       33:  227:            double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
       33:  227-block  0
call    0 returned 33
call    1 returned 33
        -:  228:            for (unsigned short int i = 0; i < 1000; i++) {
        -:  229:                double log_mid = 0.5 * (log_low + log_high);
        5:  230:                double mid = exp(log_mid);
        -:  231:                calc_tails_logspace(a, min_x, max_x, mid, logdc, &lt, &ut);
        5:  232:                double err = fabs(ut - target_alpha);
        5:  232-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        5:  233:                if (err < best_err) { best_err = err; best = mid; }
        1:  233-block  0
        4:  233-block  1
        5:  233-block  2
branch  0 taken 4
branch  1 taken 1
        4:  234:                if (ut > target_alpha) log_high = log_mid;
        4:  234-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        3:  235:                else log_low = log_mid;
        1:  235-block  0
        2:  235-block  1
        3:  235-block  2
branch  0 taken 2
branch  1 taken 1
        -:  236:                if (log_high - log_low < 1e-15) break;
        3:  237:            }
        3:  238:            *ci_low = best;
       26:  239:        }
        3:  239-block  0
       23:  239-block  1
       26:  239-block  2
branch  0 taken 23
branch  1 taken 3 (fallthrough)
       23:  240:    }
       23:  241:
       23:  241-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 13
       10:  241-block  1
        -:  242:    // Upper CI
        -:  243:    if (!is_greater) { 
        -:  244:        double target_alpha = is_less ? alpha : alpha / 2.0;
        5:  245:        if (a != max_x) {
        5:  245-block  0
call    0 returned 5
       5*:  246:            double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%:  246-block  0
        5:  246-block  1
        5:  246-block  2
        -:  247:            for (unsigned short int i = 0; i < 1000; i++) {
        -:  248:                double log_mid = 0.5 * (log_low + log_high);
        -:  249:                double mid = exp(log_mid);
        -:  250:                calc_tails_logspace(a, min_x, max_x, mid, logdc, &lt, &ut);
        -:  251:                double err = fabs(lt - target_alpha);
        -:  252:                if (err < best_err) { best_err = err; best = mid; }
        -:  253:                if (lt > target_alpha) log_low = log_mid;
        -:  254:                else log_high = log_mid;
        -:  255:                if (log_high - log_low < 1e-15) break;
function sweep_matrix_ols called 44 returned 100% blocks executed 100%
       44:  256:            }
       44:  257:            *ci_high = best;
       44:  258:        }
       44:  258-block  0
call    0 returned 44
        -:  259:    }
        -:  260:    safefree(logdc);
      170:  261:}
      170:  261-block  0
branch  0 taken 126
branch  1 taken 44 (fallthrough)
      126:  262:
      126:  263:// Exact p-value using log-space
      126:  263-block  0
        -:  264:static double exact_p_value(size_t a, size_t b, size_t c, size_t d, const char* alt) {
        -:  265:    size_t r1 = a + b, r2 = c + d, c1 = a + c;
      170:  266:    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
       44:  266-block  0
      126:  266-block  1
      170:  266-block  2
branch  0 taken 126
branch  1 taken 44 (fallthrough)
        -:  267:    size_t max_x = (r1 < c1) ? r1 : c1;
        -:  268:
      126:  269:    double *logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
      126:  269-block  0
branch  0 taken 125 (fallthrough)
branch  1 taken 1
      125:  269-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 125
        1:  270:    double denom = log_choose(r1 + r2, c1);
        -:  271:    for(size_t x = min_x; x <= max_x; ++x) {
        4:  272:        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
        1:  272-block  0
        4:  272-block  1
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3:  273:    }
        3:  274:
        3:  274-block  0
        -:  275:    double p_val = 0.0;
        1:  276:    
        1:  276-block  0
        -:  277:    if (strcmp(alt, "less") == 0) {
      125:  278:        for(size_t x = min_x; x <= a; ++x) p_val += exp(logdc[x - min_x]);
      125:  279:    } else if (strcmp(alt, "greater") == 0) {
      125:  280:        for(size_t x = a; x <= max_x; ++x) p_val += exp(logdc[x - min_x]);
      492:  281:    } else {
      125:  281-block  0
      367:  281-block  1
      492:  281-block  2
branch  0 taken 367
branch  1 taken 125 (fallthrough)
      492:  282:        double p_obs = exp(logdc[a - min_x]);
      125:  282-block  0
      367:  282-block  1
      492:  282-block  2
branch  0 taken 367
branch  1 taken 125 (fallthrough)
      367:  283:        double relErr = 1.0 + 1e-7;
      367:  283-block  0
branch  0 taken 242 (fallthrough)
branch  1 taken 125
      242:  283-block  1
branch  2 taken 236 (fallthrough)
branch  3 taken 6
      236:  284:        for(size_t x = min_x; x <= max_x; ++x) {
      236:  285:            double p_cur = exp(logdc[x - min_x]);
      946:  286:            if (p_cur <= p_obs * relErr) p_val += p_cur;
      236:  286-block  0
      946:  286-block  1
branch  0 taken 710
branch  1 taken 236 (fallthrough)
      710:  287:        }
      710:  287-block  0
        -:  288:    }
        -:  289:    
        -:  290:    safefree(logdc);
        -:  291:    return (p_val > 1.0) ? 1.0 : p_val;
       44:  292:}
       44:  292-block  0
call    0 returned 44
       44:  293:/* -----------------------------------------------------------------------
        -:  294: * Helpers for lm Linear Regression: OLS Matrix Math & Formula Parsing
        -:  295: * ----------------------------------------------------------------------- */
        -:  296:
function get_data_value called 1759 returned 100% blocks executed 100%
     1759:  297:/* Sweep operator for symmetric positive-definite matrices (e.g., XtX).
     1759:  298: * This gracefully handles collinearity by bypassing aliased columns.
     1759:  299: * Utilizes a relative tolerance check to prevent dropping micro-variance features.
     1759:  299-block  0
branch  0 taken 1184 (fallthrough)
branch  1 taken 575
     1184:  300: */
     1184:  300-block  0
call    0 returned 1184
     1184:  301:static int sweep_matrix_ols(double *restrict A, size_t n, bool *restrict aliased) {
branch  0 taken 1184 (fallthrough)
branch  1 taken 0
     1184:  301-block  0
branch  2 taken 1184 (fallthrough)
branch  3 taken 0
     1184:  301-block  1
branch  4 taken 1184 (fallthrough)
branch  5 taken 0
     1184:  302:	int rank = 0;
     1184:  303:	double *restrict orig_diag = (double*)safemalloc(n * sizeof(double));
     1184:  303-block  0
call    0 returned 1184
        -:  304:
      575:  305:	// Save the original diagonal values to use as a baseline for relative variance
      575:  305-block  0
branch  0 taken 575 (fallthrough)
branch  1 taken 0
      575:  306:	for (size_t k = 0; k < n; k++) {
      575:  306-block  0
call    0 returned 575
      575:  307:		aliased[k] = FALSE;
branch  0 taken 575 (fallthrough)
branch  1 taken 0
      575:  307-block  0
branch  2 taken 575 (fallthrough)
branch  3 taken 0
      575:  307-block  1
branch  4 taken 575 (fallthrough)
branch  5 taken 0
      575:  308:		orig_diag[k] = A[k * n + k];
      575:  309:	}
      575:  309-block  0
call    0 returned 575
        -:  310:
        -:  311:	for (size_t k = 0; k < n; k++) {
     1759:  312:		// Check pivot for collinearity using a RELATIVE tolerance
     1759:  312-block  0
branch  0 taken 1759 (fallthrough)
branch  1 taken 0
     1759:  312-block  1
branch  2 taken 1756 (fallthrough)
branch  3 taken 3
     1756:  313:		// (Fallback to a tiny absolute tolerance of 1e-24 to catch literal zero vectors)
     1756:  313-block  0
call    0 returned 1756
branch  1 taken 1707 (fallthrough)
branch  2 taken 49
     1707:  313-block  1
call    3 returned 1707
       49:  314:		if (fabs(A[k * n + k]) <= 1e-10 * orig_diag[k] || fabs(A[k * n + k]) < 1e-24) {
       49:  314-block  0
        -:  315:			aliased[k] = TRUE;
        3:  316:			// Isolate this column so it doesn't affect the rest of the matrix
        3:  316-block  0
        -:  317:			for (size_t i = 0; i < n; i++) { 
        -:  318:				A[k * n + i] = 0.0; 
        -:  319:				A[i * n + k] = 0.0; 
function get_all_columns called 8 returned 100% blocks executed 52%
        8:  320:			}
        8:  321:			continue;
        8:  321-block  0
call    0 returned 8
        8:  322:		}
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8:  323:		rank++;
        8:  323-block  0
call    0 returned 8
        -:  324:		double pivot = 1.0 / A[k * n + k];
       30:  325:		A[k * n + k] = 1.0;
       30:  325-block  0
call    0 returned 30
branch  1 taken 22
branch  2 taken 8
       22:  326:		for (size_t j = 0; j < n; j++) A[k * n + j] *= pivot;
       22:  326-block  0
call    0 returned 22
call    1 returned 22
call    2 returned 22
        -:  327:		for (size_t i = 0; i < n; i++) {
    #####:  328:			if (i != k && A[i * n + k] != 0.0) {
    %%%%%:  328-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  328-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%:  328-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####:  329:				  double factor = A[i * n + k];
    %%%%%:  329-block  0
call    0 never executed
        -:  330:				  A[i * n + k] = 0.0;
    #####:  331:				  for (size_t j = 0; j < n; j++) {
    %%%%%:  331-block  0
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
    #####:  332:				       A[i * n + j] -= factor * A[k * n + j];
    %%%%%:  332-block  0
call    0 never executed
call    1 never executed
call    2 never executed
        -:  333:				  }
        -:  334:			}
        8:  335:		}
        8:  335-block  0
        -:  336:	}
        -:  337:	Safefree(orig_diag);
        -:  338:	return rank;
function evaluate_term called 1791 returned 100% blocks executed 52%
     1791:  339:}
    1791*:  340:
     1791:  340-block  0
branch  0 taken 1791 (fallthrough)
branch  1 taken 0
     1791:  340-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 1791
    %%%%%:  340-block  2
        -:  341:// Internal extractor resolving single data values. Returns NAN on missing or non-numeric.
     1791:  342:static double get_data_value(HV *restrict data_hoa, HV **restrict row_hashes, unsigned int i, const char *restrict var) {
     1791:  342-block  0
call    0 returned 1791
     1791:  343:    SV **restrict val = NULL;
     1791:  344:    if (row_hashes) {
branch  0 taken 32 (fallthrough)
branch  1 taken 1759
       32:  345:        val = hv_fetch(row_hashes[i], var, strlen(var), 0);
       32:  346:        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
       32:  346-block  0
call    0 returned 32
       32:  347:            AV*restrict av = (AV*)SvRV(*val);
call    0 returned 32
       32:  348:            val = av_fetch(av, 0, 0);
call    0 returned 32
        -:  349:        }
      32*:  350:    } else if (data_hoa) {
branch  0 taken 32 (fallthrough)
branch  1 taken 0
       32:  350-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 32
    %%%%%:  350-block  1
       32:  351:        SV**restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
       32:  351-block  0
        -:  352:        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
     1759:  353:            AV*restrict av = (AV*)SvRV(*col);
     1759:  353-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1759
    #####:  354:            val = av_fetch(av, i, 0);
    #####:  355:        }
    %%%%%:  355-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  355-block  1
    #####:  356:    }
    #####:  357:    if (val && SvOK(*val)) {
    #####:  358:        if (looks_like_number(*val)) return SvNV(*val);
    #####:  359:        return NAN; // Catch strings like "blue"
    %%%%%:  359-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####:  360:    }
    #####:  361:    return NAN; // Catch undef/missing keys
    %%%%%:  361-block  0
        -:  362:}
    #####:  363:
    %%%%%:  363-block  0
call    0 never executed
    #####:  364:// Helper: Get all available columns for the '.' operator expansion
call    0 never executed
        -:  365:static AV* get_all_columns(HV *restrict data_hoa, HV **restrict row_hashes, size_t n) {
    #####:  366:    AV *cols = newAV();
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  366-block  0
    #####:  367:    if (data_hoa) {
    %%%%%:  367-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  367-block  1
    %%%%%:  367-block  2
    %%%%%:  367-block  3
        -:  368:        hv_iterinit(data_hoa);
     1759:  369:        HE *entry;
     1759:  369-block  0
call    0 returned 1759
     1759:  370:        while ((entry = hv_iternext(data_hoa))) {
call    0 returned 1759
     1759:  371:            av_push(cols, newSVsv(hv_iterkeysv(entry)));
        -:  372:        }
        -:  373:    } else if (row_hashes && n > 0 && row_hashes[0]) {
        -:  374:        hv_iterinit(row_hashes[0]);
function is_column_categorical called 54 returned 100% blocks executed 100%
       54:  375:        HE *entry;
       86:  376:        while ((entry = hv_iternext(row_hashes[0]))) {
       54:  376-block  0
       32:  376-block  1
       86:  376-block  2
branch  0 taken 85
branch  1 taken 1 (fallthrough)
       85:  377:            av_push(cols, newSVsv(hv_iterkeysv(entry)));
       85:  378:        }
       85:  378-block  0
branch  0 taken 55 (fallthrough)
branch  1 taken 30
       55:  379:    }
       55:  379-block  0
call    0 returned 55
       55:  380:    return cols;
branch  0 taken 23 (fallthrough)
branch  1 taken 32
       23:  380-block  0
branch  2 taken 23 (fallthrough)
branch  3 taken 0
       23:  380-block  1
branch  4 taken 23 (fallthrough)
branch  5 taken 0
       23:  381:}
       23:  382:
       23:  382-block  0
call    0 returned 23
        -:  383:// Recursive formula resolver with tightened NaN and Null handling
       30:  384:static double evaluate_term(HV *restrict data_hoa, HV **restrict row_hashes, unsigned int i, const char *restrict term) {
       30:  384-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 0
       30:  385:    if (!term || term[0] == '\0') return NAN;
       30:  385-block  0
call    0 returned 30
       30:  386:
branch  0 taken 30 (fallthrough)
branch  1 taken 0
       30:  386-block  0
branch  2 taken 30 (fallthrough)
branch  3 taken 0
       30:  386-block  1
branch  4 taken 30 (fallthrough)
branch  5 taken 0
       30:  387:    char *restrict term_cpy = savepv(term); 
       30:  388:    char *restrict colon = strchr(term_cpy, ':');
       30:  388-block  0
call    0 returned 30
        -:  389:    if (colon) {
        -:  390:        *colon = '\0';
       85:  391:        double left = evaluate_term(data_hoa, row_hashes, i, term_cpy);
       85:  391-block  0
branch  0 taken 53 (fallthrough)
branch  1 taken 32
       53:  391-block  1
branch  2 taken 53 (fallthrough)
branch  3 taken 0
       53:  392:        double right = evaluate_term(data_hoa, row_hashes, i, colon + 1);
       53:  392-block  0
call    0 returned 53
branch  1 taken 44 (fallthrough)
branch  2 taken 9
       44:  392-block  1
        9:  393:        Safefree(term_cpy); 
        9:  393-block  0
        -:  394:        
        -:  395:        if (isnan(left) || isnan(right)) return NAN;
        1:  396:        return left * right;
        1:  396-block  0
        -:  397:    }
        -:  398:    if (strncmp(term_cpy, "I(", 2) == 0) {
        -:  399:        char *restrict end = strrchr(term_cpy, ')');
function get_data_string_alloc called 347 returned 100% blocks executed 68%
      347:  400:        if (end) *end = '\0';
      347:  401:        char *restrict inner = term_cpy + 2;
      347:  402:        char *restrict caret = strchr(inner, '^');
      347:  402-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 347
    #####:  403:        int power = 1;
    %%%%%:  403-block  0
call    0 never executed
    #####:  404:        if (caret) {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  404-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%:  404-block  1
branch  4 never executed (fallthrough)
branch  5 never executed
    #####:  405:            *caret = '\0';
    #####:  406:            power = atoi(caret + 1);
    %%%%%:  406-block  0
call    0 never executed
        -:  407:        }
      347:  408:        double v = get_data_value(data_hoa, row_hashes, i, inner);
      347:  408-block  0
branch  0 taken 347 (fallthrough)
branch  1 taken 0
      347:  409:        Safefree(term_cpy); 
      347:  409-block  0
call    0 returned 347
      347:  410:        
branch  0 taken 347 (fallthrough)
branch  1 taken 0
      347:  410-block  0
branch  2 taken 347 (fallthrough)
branch  3 taken 0
      347:  410-block  1
branch  4 taken 347 (fallthrough)
branch  5 taken 0
      347:  411:        if (isnan(v)) return NAN;
      347:  412:        return power == 1 ? v : pow(v, power);
      347:  412-block  0
call    0 returned 347
        -:  413:    }
        -:  414:    double result = get_data_value(data_hoa, row_hashes, i, term_cpy);
      347:  415:    Safefree(term_cpy); 
      347:  415-block  0
branch  0 taken 347 (fallthrough)
branch  1 taken 0
      347:  415-block  1
branch  2 taken 347 (fallthrough)
branch  3 taken 0
      347:  416:    return result;
      347:  416-block  0
call    0 returned 347
call    1 returned 347
        -:  417:}
    #####:  418:
    %%%%%:  418-block  0
        -:  419:// Helper to infer column type from its first valid element
        -:  420:static bool is_column_categorical(HV *restrict data_hoa, HV **restrict row_hashes, size_t n, const char *restrict var) {
        -:  421:	for (size_t i = 0; i < n; i++) {
        -:  422:		SV **restrict val = NULL;
        -:  423:		if (row_hashes) {
        -:  424:			val = hv_fetch(row_hashes[i], var, strlen(var), 0);
        -:  425:			if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
        -:  426:				 AV*restrict av = (AV*)SvRV(*val);
        -:  427:				 val = av_fetch(av, 0, 0);
function cmp_pval called 1519 returned 100% blocks executed 83%
     1519:  428:			}
     1519:  429:		} else if (data_hoa) {
     1519:  430:			SV **restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
     1519:  430-block  0
branch  0 taken 707 (fallthrough)
branch  1 taken 812
      707:  430-block  1
      812:  431:			if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
      812:  431-block  0
branch  0 taken 812 (fallthrough)
branch  1 taken 0
      812:  431-block  1
        -:  432:				 AV*restrict av = (AV*)SvRV(*col);
    #####:  433:				 val = av_fetch(av, i, 0);
    %%%%%:  433-block  0
        -:  434:			}
        -:  435:		}
        -:  436:		if (val && SvOK(*val)) {
        -:  437:			if (looks_like_number(*val)) return FALSE; // First valid is number -> Numeric Column
        -:  438:			return TRUE; // First valid is string -> Categorical Column
        -:  439:		}
        -:  440:	}
        -:  441:	return FALSE;
        -:  442:}
        -:  443:
        -:  444:/* Internal extractor resolving single data string values using dynamic allocation. */
function cmp_rank_item called 37 returned 100% blocks executed 100%
       37:  445:static char* get_data_string_alloc(HV *restrict data_hoa, HV **restrict row_hashes, size_t i, const char *restrict var) {
       37:  446:	SV **restrict val = NULL;
       37:  447:	if (row_hashes) {
       37:  447-block  0
branch  0 taken 33 (fallthrough)
branch  1 taken 4
       33:  447-block  1
        4:  448:		val = hv_fetch(row_hashes[i], var, strlen(var), 0);
        4:  448-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 1
        3:  448-block  1
        1:  449:		if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
        1:  449-block  0
        -:  450:			AV*restrict av = (AV*)SvRV(*val);
        -:  451:			val = av_fetch(av, 0, 0);
        -:  452:		}
        -:  453:	} else if (data_hoa) {
function rank_data called 4 returned 100% blocks executed 95%
        4:  454:		SV **restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
        -:  455:		if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
       4*:  456:			AV*restrict av = (AV*)SvRV(*col);
        4:  456-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%:  456-block  1
call    2 never executed
        4:  456-block  2
call    3 returned 4
       32:  457:			val = av_fetch(av, i, 0);
       28:  457-block  0
       32:  457-block  1
branch  0 taken 28
branch  1 taken 4 (fallthrough)
        4:  458:		}
        4:  458-block  0
call    0 returned 4
        -:  459:	}
        4:  460:	if (val && SvOK(*val)) {
       31:  461:	  return savepv(SvPV_nolen(*val)); /* Allocates and returns string */
       31:  461-block  0
branch  0 taken 27
branch  1 taken 4 (fallthrough)
       27:  462:	}
        -:  463:	return NULL;
       28:  464:}
       27:  464-block  0
        1:  464-block  1
       28:  464-block  2
branch  0 taken 24 (fallthrough)
branch  1 taken 4
       24:  464-block  3
branch  2 taken 1
branch  3 taken 23 (fallthrough)
        -:  465:
       27:  466:// Struct for sorting p-values while remembering their original index
       55:  467:typedef struct {
       27:  467-block  0
       28:  467-block  1
       55:  467-block  2
branch  0 taken 28
branch  1 taken 27 (fallthrough)
       27:  468:	double p;
       27:  468-block  0
        -:  469:	size_t orig_idx;
        4:  470:} PVal;
        4:  470-block  0
call    0 returned 4
        4:  471:
        -:  472:// Comparator for qsort
        -:  473:static int cmp_pval(const void *restrict a, const void *restrict b) {
        -:  474:	double diff = ((PVal*)a)->p - ((PVal*)b)->p;
function pearson_corr called 6 returned 100% blocks executed 86%
        6:  475:	if (diff < 0) return -1;
        6:  476:	if (diff > 0) return 1;
       36:  477:	/* Stabilize sort by falling back to original index */
        6:  477-block  0
       36:  477-block  1
branch  0 taken 30
branch  1 taken 6 (fallthrough)
       30:  478:	return ((PVal*)a)->orig_idx - ((PVal*)b)->orig_idx; 
       30:  479:}
       30:  479-block  0
        -:  480:/* -----------------------------------------------------------------------
        6:  481: * Helpers for cor(): ranking (Spearman), Pearson r, Kendall tau-b
        6:  482: * ----------------------------------------------------------------------- */
       6*:  483:/* Item used to sort values while remembering their original index,
        6:  483-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  483-block  1
        6:  484: * needed for average-rank tie-breaking in Spearman correlation.        */
        6:  484-block  0
        -:  485:typedef struct {
        -:  486:	double val;
        -:  487:	size_t idx;
        -:  488:} RankItem;
        -:  489:
        -:  490:static int cmp_rank_item(const void *restrict a, const void *restrict b) {
        -:  491:	double diff = ((RankItem*)a)->val - ((RankItem*)b)->val;
        -:  492:	if (diff < 0) return -1;
        -:  493:	if (diff > 0) return  1;
        -:  494:	return 0;
function kendall_tau_b called 1 returned 100% blocks executed 80%
        1:  495:}
        1:  496:
        9:  497:/* Compute 1-based average ranks with tie-breaking into out[].
        1:  497-block  0
        8:  497-block  1
        9:  497-block  2
branch  0 taken 8
branch  1 taken 1 (fallthrough)
       44:  498: * in[] is not modified.                                                 */
        8:  498-block  0
       36:  498-block  1
       44:  498-block  2
branch  0 taken 36
branch  1 taken 8 (fallthrough)
       36:  499:static void rank_data(const double *restrict in, double *restrict out, size_t n) {
       36:  500:	RankItem *restrict ri;
      36*:  501:	Newx(ri, n, RankItem);
       36:  501-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 35
        1:  501-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 1
    %%%%%:  501-block  2
       36:  502:	for (size_t i = 0; i < n; i++) { ri[i].val = in[i]; ri[i].idx = i; }
       36:  502-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 35
        1:  502-block  1
      35*:  503:	qsort(ri, n, sizeof(RankItem), cmp_rank_item);
       35:  503-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 35
    %%%%%:  503-block  1
       35:  504:
       35:  504-block  0
branch  0 taken 35 (fallthrough)
branch  1 taken 0
       35:  504-block  1
    #####:  505:	size_t i = 0;
    %%%%%:  505-block  0
        -:  506:	while (i < n) {
        -:  507:		size_t j = i;
        1:  508:		/* Find the full extent of this tie group */
       1*:  509:		while (j + 1 < n && ri[j + 1].val == ri[j].val) j++;
        1:  509-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%:  509-block  1
        1:  510:		/* All members get the average of ranks i+1 … j+1 (1-based) */
        1:  510-block  0
        -:  511:		double avg = (double)(i + j) / 2.0 + 1.0;
        -:  512:		for (size_t k = i; k <= j; k++) out[ri[k].idx] = avg;
        -:  513:		i = j + 1;
        -:  514:	}
function compute_cor called 7 returned 100% blocks executed 89%
        7:  515:	Safefree(ri);
        -:  516:}
        7:  517:
        7:  517-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        -:  518:/* Pearson product-moment r between two n-element arrays.
       1*:  519: * Returns NAN when either variable has zero variance (matches R).       */
        1:  519-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%:  519-block  1
call    2 never executed
        1:  519-block  2
call    3 returned 1
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    %%%%%:  519-block  3
call    6 never executed
        1:  519-block  4
call    7 returned 1
        1:  520:static double pearson_corr(const double *restrict x, const double *restrict y, size_t n) {
call    0 returned 1
        1:  521:	double sx = 0, sy = 0, sxy = 0, sx2 = 0, sy2 = 0;
call    0 returned 1
        1:  522:	for (size_t i = 0; i < n; i++) {
call    0 returned 1
        1:  523:	  sx  += x[i];     sy  += y[i];
call    0 returned 1
call    1 returned 1
        1:  524:	  sxy += x[i]*y[i]; sx2 += x[i]*x[i]; sy2 += y[i]*y[i];
        -:  525:	}
        6:  526:	double num = (double)n * sxy - sx * sy;
        6:  526-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1:  527:	double den = sqrt(((double)n * sx2 - sx*sx) * ((double)n * sy2 - sy*sy));
        1:  527-block  0
call    0 returned 1
        -:  528:	if (den == 0.0) return NAN;
        5:  529:	return num / den;
        5:  529-block  0
call    0 returned 5
        -:  530:}
        -:  531:
        -:  532:/* Kendall's tau-b between two n-element arrays.
        -:  533: *
        -:  534: *   tau-b = (C − D) / sqrt((C + D + T_x)(C + D + T_y))
        -:  535: *
        -:  536: * where C = concordant pairs, D = discordant, T_x = pairs tied only on
function _incbeta_cf called 8427 returned 100% blocks executed 71%
     8427:  537: * x, T_y = pairs tied only on y.  Joint ties (both zero) are excluded
        -:  538: * from numerator and denominator, matching R's cor(method="kendall").
        -:  539: * Returns NAN when the denominator is zero.                             */
     8427:  540:static double kendall_tau_b(const double *restrict x, const double *restrict y, unsigned int n) {
     8427:  541:	size_t C = 0, D = 0, tie_x = 0, tie_y = 0;
    8427*:  542:	for (size_t i = 0; i < n - 1; i++) {
     8427:  542-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8427
    %%%%%:  542-block  1
     8427:  543:	  for (size_t j = i + 1; j < n; j++) {
   181198:  544:		   int sx = (x[i] > x[j]) - (x[i] < x[j]);   /* sign of x[i]-x[j] */
     8427:  544-block  0
   172771:  544-block  1
   181198:  544-block  2
branch  0 taken 181198
branch  1 taken 0 (fallthrough)
   181198:  545:		   int sy = (y[i] > y[j]) - (y[i] < y[j]);
   181198:  546:		   if      (sx == 0 && sy == 0) { /* joint tie — not counted */ }
   181198:  547:		   else if (sx == 0)            tie_x++;
  181198*:  548:		   else if (sy == 0)            tie_y++;
   181198:  548-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 181198
    %%%%%:  548-block  1
   181198:  549:		   else if (sx == sy)           C++;
  181198*:  550:		   else                         D++;
   181198:  550-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 181198
    %%%%%:  550-block  1
   181198:  551:	  }
   181198:  552:	}
   181198:  553:	double denom = sqrt((double)(C + D + tie_x) * (double)(C + D + tie_y));
  181198*:  554:	if (denom == 0.0) return NAN;
   181198:  554-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 181198
    %%%%%:  554-block  1
   181198:  555:	return (double)(C - D) / denom;
  181198*:  556:}
   181198:  556-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 181198
    %%%%%:  556-block  1
   181198:  557:
   181198:  558:/* Single dispatch: compute correlation according to method string.
   181198:  558-block  0
branch  0 taken 8427 (fallthrough)
branch  1 taken 172771
     8427:  558-block  1
        -:  559: * Allocates and frees temporary rank arrays internally for Spearman.   */
     8427:  560:static double compute_cor(const double *restrict x, const double *restrict y,
     8427:  560-block  0
        -:  561:                           size_t n, const char *restrict method) {
        -:  562:	if (strcmp(method, "spearman") == 0) {
function incbeta called 8468 returned 100% blocks executed 90%
     8468:  563:	  double *restrict rx, *restrict ry;
    8468*:  564:	  Newx(rx, n, double); Newx(ry, n, double);
     8468:  564-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8468
    %%%%%:  564-block  1
     8468:  565:	  rank_data(x, rx, n);
     8468:  565-block  0
branch  0 taken 41 (fallthrough)
branch  1 taken 8427
       41:  565-block  1
     8427:  566:	  rank_data(y, ry, n);
     8427:  567:	  double r = pearson_corr(rx, ry, n);
     8427:  567-block  0
branch  0 taken 6847 (fallthrough)
branch  1 taken 1580
     6847:  567-block  1
call    2 returned 6847
     1580:  568:	  Safefree(rx); Safefree(ry);
     1580:  568-block  0
call    0 returned 1580
        -:  569:	  return r;
        -:  570:	}
function get_t_pvalue called 8168 returned 100% blocks executed 92%
     8168:  571:	if (strcmp(method, "kendall") == 0)
     8168:  572:	  return kendall_tau_b(x, y, n);
     8168:  573:	/* default: pearson */
     8168:  573-block  0
call    0 returned 8168
     8168:  574:	return pearson_corr(x, y, n);
branch  0 taken 2 (fallthrough)
branch  1 taken 8166
        2:  574-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 1
        1:  574-block  1
        1:  574-block  2
        2:  574-block  3
    8166*:  575:}
     8166:  575-block  0
branch  0 taken 8062 (fallthrough)
branch  1 taken 104
     8062:  575-block  1
branch  2 taken 8062 (fallthrough)
branch  3 taken 0
     8062:  575-block  2
    %%%%%:  575-block  3
     8062:  575-block  4
      104:  576:
      104:  576-block  0
        -:  577:// Math macros
        -:  578:#define MAX_ITER 500
        -:  579:#define EPS 3.0e-15
function qt_tail called 271 returned 100% blocks executed 94%
      271:  580:#define FPMIN 1.0e-30
      271:  581:
        -:  582:static double _incbeta_cf(double a, double b, double x) {
      643:  583:	int m;
      271:  583-block  0
      643:  583-block  1
call    0 returned 643
branch  1 taken 372
branch  2 taken 271 (fallthrough)
      372:  584:	double aa, c, d, del, h, qab, qam, qap;
      372:  585:	qab = a + b; qap = a + 1.0; qam = a - 1.0;
     372*:  586:	c = 1.0; d = 1.0 - qab * x / qap;
      372:  586-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 372
    %%%%%:  586-block  1
        -:  587:	if (fabs(d) < FPMIN) d = FPMIN;
        -:  588:	d = 1.0 / d; h = d;
     7418:  589:	for (m = 1; m <= MAX_ITER; m++) {
      271:  589-block  0
     7147:  589-block  1
     7418:  589-block  2
branch  0 taken 7418
branch  1 taken 0 (fallthrough)
     7418:  590:	  int m2 = 2 * m;
     7418:  591:	  aa = m * (b - m) * x / ((qam + m2) * (a + m2));
     7418:  591-block  0
call    0 returned 7418
     7418:  592:	  d = 1.0 + aa * d;
branch  0 taken 3623 (fallthrough)
branch  1 taken 3795
     3623:  593:	  if (fabs(d) < FPMIN) d = FPMIN;
     3623:  593-block  0
        -:  594:	  c = 1.0 + aa / c;
     3795:  595:	  if (fabs(c) < FPMIN) c = FPMIN;
     3795:  595-block  0
        -:  596:	  d = 1.0 / d; h *= d * c;
     7418:  597:	  aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
     7418:  597-block  0
branch  0 taken 271 (fallthrough)
branch  1 taken 7147
      271:  597-block  1
        -:  598:	  d = 1.0 + aa * d;
      271:  599:	  if (fabs(d) < FPMIN) d = FPMIN;
      271:  599-block  0
        -:  600:	  c = 1.0 + aa / c;
        -:  601:	  if (fabs(c) < FPMIN) c = FPMIN;
function compare_doubles called 2835 returned 100% blocks executed 100%
     2835:  602:	  d = 1.0 / d; del = d * c; h *= del;
     2835:  603:	  if (fabs(del - 1.0) < EPS) break;
     2835:  604:	}
     2835:  605:	return h;
     2835:  605-block  0
        -:  606:}
        -:  607:
function calculate_sturges_bins called 0 returned 0% blocks executed 0%
    #####:  608:static double incbeta(double a, double b, double x) {
    #####:  609:	if (x <= 0.0) return 0.0;
    %%%%%:  609-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  609-block  1
    #####:  610:	if (x >= 1.0) return 1.0;
    %%%%%:  610-block  0
        -:  611:	double bt = exp(lgamma(a + b) - lgamma(a) - lgamma(b) + a * log(x) + b * log(1.0 - x));
        -:  612:	if (x < (a + 1.0) / (a + b + 2.0)) return bt * _incbeta_cf(a, b, x) / a;
        -:  613:	return 1.0 - bt * _incbeta_cf(b, a, 1.0 - x) / b;
function compute_hist_logic called 5 returned 100% blocks executed 89%
        5:  614:}
        -:  615:
        5:  616:static double get_t_pvalue(double t, double df, const char*restrict alt) {
        5:  617:	double x = df / (df + t * t);
       5*:  618:	double prob_2tail = incbeta(df / 2.0, 0.5, x);
        5:  618-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5:  618-block  1
    %%%%%:  618-block  2
        -:  619:	if (strcmp(alt, "less") == 0) return (t < 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
       23:  620:	if (strcmp(alt, "greater") == 0) return (t > 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
        5:  620-block  0
       23:  620-block  1
branch  0 taken 18
branch  1 taken 5 (fallthrough)
       18:  621:	return prob_2tail;
       18:  622:}
       18:  622-block  0
        -:  623:
        -:  624:// Bisection algorithm to find the inverse t-distribution (Critical t-value)
        5:  625:static double qt_tail(double df, double p_tail) {
        5:  625-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 2
     2017:  626:	double low = 0.0, high = 1.0;
        3:  626-block  0
     2014:  626-block  1
     2017:  626-block  2
branch  0 taken 2014
branch  1 taken 3
     2014:  627:	// Find upper bound
        -:  628:	while (get_t_pvalue(high, df, "greater") > p_tail) {
    2014*:  629:	  low = high;
     2014:  629-block  0
branch  0 taken 2014 (fallthrough)
branch  1 taken 0
     2014:  629-block  1
branch  2 taken 2014 (fallthrough)
branch  3 taken 0
     2014:  629-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 2014
    %%%%%:  629-block  3
        -:  630:	  high *= 2.0;
     2014:  631:	  if (high > 1000000.0) break; /* Fallback limit */
        -:  632:	}
     2014:  633:	// Bisect to find the root
     2014:  633-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 2011
        3:  634:	for (unsigned short int i = 0; i < 100; i++) {
        3:  634-block  0
        -:  635:	  double mid = (low + high) / 2.0;
        -:  636:	  double p_mid = get_t_pvalue(mid, df, "greater");
        -:  637:	  if (p_mid > p_tail) {
        -:  638:		   low = mid;
     2023:  639:	  } else {
     2014:  639-block  0
     2023:  639-block  1
branch  0 taken 1816 (fallthrough)
branch  1 taken 207
     1816:  639-block  2
branch  2 taken 9
branch  3 taken 1807 (fallthrough)
        9:  640:		   high = mid;
        9:  640-block  0
        -:  641:	  }
        -:  642:	  if (high - low < 1e-8) break;
     2014:  643:	}
     2014:  643-block  0
     2014:  643-block  1
branch  0 taken 1811 (fallthrough)
branch  1 taken 203
     1811:  643-block  2
branch  2 taken 0
branch  3 taken 1811 (fallthrough)
    #####:  644:	return (low + high) / 2.0;
    %%%%%:  644-block  0
        -:  645:}
     2014:  646:
     2014:  646-block  0
        -:  647:int compare_doubles(const void *restrict a, const void *restrict b) {
        2:  648:	double da = *(const double*restrict)a;
        2:  648-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        -:  649:	double db = *(const double*restrict)b;
        2:  650:	return (da > db) - (da < db);
        2:  650-block  0
        -:  651:}
        -:  652:/* Helper to calculate the number of bins using Sturges' formula: log2(n) + 1 */
       23:  653:static size_t calculate_sturges_bins(size_t n) {
        5:  653-block  0
       18:  653-block  1
       23:  653-block  2
branch  0 taken 18
branch  1 taken 5 (fallthrough)
       18:  654:	if (n == 0) return 1;
       18:  655:	return (size_t)(log((double)n) / log(2.0) + 1.0);
       18:  655-block  0
branch  0 taken 16 (fallthrough)
branch  1 taken 2
       16:  656:}
       16:  656-block  0
        -:  657:
       2*:  658:// Logic for distributing data into bins (Optimized to O(N))
        2:  658-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2:  658-block  1
    %%%%%:  658-block  2
        2:  658-block  3
        -:  659:static void compute_hist_logic(double *restrict x, size_t n, double *restrict breaks, size_t n_bins, 
        -:  660:                               size_t *restrict counts, double *restrict mids, double *restrict density) {
        5:  661:	double total_n = (double)n;
        -:  662:	double min_val = breaks[0];
        -:  663:	double step = (n_bins > 0) ? (breaks[1] - breaks[0]) : 0.0;
function approx_pnorm called 56 returned 100% blocks executed 100%
       56:  664:	// Initialize counts and compute midpoints
       56:  665:	for (size_t i = 0; i < n_bins; i++) {
       56:  665-block  0
        -:  666:	  counts[i] = 0;
        -:  667:	  mids[i] = (breaks[i] + breaks[i+1]) / 2.0;
        -:  668:	}
        -:  669:	// Single O(N) pass to assign elements to bins
        -:  670:	if (step > 0.0) {
        -:  671:	  for (size_t j = 0; j < n; j++) {
        -:  672:		   double val = x[j];
function inverse_normal_cdf called 28 returned 100% blocks executed 100%
       28:  673:		   // Ignore out-of-bounds or invalid values
       28:  674:		   if (isnan(val) || isinf(val) || val < min_val) continue;
       28:  675:		   // Calculate initial bin index mathematically
       28:  676:		   size_t idx = (size_t)((val - min_val) / step);
        -:  677:		   // Clamp to valid array bounds first to prevent overflow */
        -:  678:		   if (idx >= n_bins) {
        -:  679:		       idx = n_bins - 1;
       28:  680:		   }
       28:  681:		   /* Adjust for exact boundaries (R's right-inclusive default: (a, b]) */
       28:  681-block  0
branch  0 taken 22 (fallthrough)
branch  1 taken 6
       22:  682:		   /* If value is exactly on or slightly below the lower boundary of the assigned bin, 
       22:  683:		      it belongs in the previous bin. (First bin [a, b] is inclusive on both ends) */
       22:  684:		   while (idx > 0 && val <= breaks[idx]) {
       22:  684-block  0
        -:  685:		       idx--;
        6:  686:		   }
        6:  687:		   // Conversely, if floating-point truncation placed it too low, push it up
        6:  687-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 1
        5:  687-block  1
        6:  688:		   while (idx < n_bins - 1 && val > breaks[idx + 1]) {
        6:  689:		       idx++;
        6:  690:		   }
        6:  691:		   counts[idx]++;
        6:  691-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1:  691-block  1
        -:  692:	  }
       28:  693:	} else if (n_bins > 0) {
       28:  693-block  0
        -:  694:	  // Edge case: All data points have the exact same value (step == 0)
        -:  695:	  counts[0] = n;
        -:  696:	}
        -:  697:	// Compute densities
        -:  698:	for (size_t i = 0; i < n_bins; i++) {
        -:  699:	  double bin_width = breaks[i+1] - breaks[i];
        -:  700:	  if (bin_width > 0) {
        -:  701:		   density[i] = (double)counts[i] / (total_n * bin_width);
        -:  702:	  } else {
        -:  703:		   density[i] = (n_bins == 1) ? 1.0 : 0.0;
        -:  704:	  }
        -:  705:	}
function spearman_exact_pvalue called 1 returned 100% blocks executed 88%
        1:  706:}
        1:  707:
        1:  707-block  0
call    0 returned 1
        1:  708:// Standard Normal CDF approximation
call    0 returned 1
        6:  709:double approx_pnorm(double x) {
        5:  709-block  0
        6:  709-block  1
branch  0 taken 5
branch  1 taken 1 (fallthrough)
        -:  710:	return 0.5 * erfc(-x * 0.70710678118654752440); // 0.707... = 1/sqrt(2)
        1:  711:}
        -:  712:#ifndef M_SQRT1_2
        -:  713:#define M_SQRT1_2 0.70710678118654752440
        -:  714:#endif
        -:  715:
        -:  716:/* Macro for exact Wilcoxon 3D array indexing */
        -:  717:#define DP_INDEX(i, j, k, n2, max_u) ((i) * ((n2) + 1) * ((max_u) + 1) + (j) * ((max_u) + 1) + (k))
        -:  718:static double inverse_normal_cdf(double p) {
        -:  719:	double a[4] = {2.50662823884, -18.61500062529, 41.39119773534, -25.44106049637};
        -:  720:	double b[4] = {-8.47351093090, 23.08336743743, -21.06224101826, 3.13082909833};
        -:  721:	double c[9] = {0.3374754822726147, 0.9761690190917186, 0.1607979714918209,
        -:  722:		          0.0276438810333863, 0.0038405729373609, 0.0003951896511919,
        -:  723:		          0.0000321767881768, 0.0000002888167364, 0.0000003960315187};
       6*:  724:	double x, r, y;
        1:  724-block  0
        5:  724-block  1
        6:  724-block  2
branch  0 taken 5
branch  1 taken 1 (fallthrough)
        1:  724-block  3
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1:  724-block  4
        1:  724-block  5
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    %%%%%:  724-block  6
        -:  725:	y = p - 0.5;
        1:  726:	if (fabs(y) < 0.42) {
      206:  727:	  r = y * y;
        1:  727-block  0
      206:  727-block  1
branch  0 taken 205
branch  1 taken 1 (fallthrough)
      205:  728:	  x = y * (((a[3]*r + a[2])*r + a[1])*r + a[0]) /
      205:  728-block  0
branch  0 taken 119 (fallthrough)
branch  1 taken 86
        -:  729:		       ((((b[3]*r + b[2])*r + b[1])*r + b[0])*r + 1.0);
      119:  730:	} else {
      119:  730-block  0
branch  0 taken 44 (fallthrough)
branch  1 taken 75
       44:  731:	  r = p;
       44:  731-block  0
        -:  732:	  if (y > 0) r = 1.0 - p;
       75:  733:	  r = log(-log(r));
       75:  733-block  0
        -:  734:	  x = c[0] + r * (c[1] + r * (c[2] + r * (c[3] + r * (c[4] +
      714:  735:		   r * (c[5] + r * (c[6] + r * (c[7] + r * c[8])))))));
      119:  735-block  0
      595:  735-block  1
      714:  735-block  2
branch  0 taken 595
branch  1 taken 119 (fallthrough)
      119:  735-block  3
branch  2 taken 7 (fallthrough)
branch  3 taken 112
        7:  735-block  4
      119:  735-block  5
branch  4 taken 115 (fallthrough)
branch  5 taken 4
      115:  735-block  6
      119:  736:	  if (y < 0) x = -x;
      119:  737:	}
      119:  737-block  0
        -:  738:	return x;
       86:  739:}
       86:  740:/* -----------------------------------------------------------------------
       86:  740-block  0
        -:  741: * Exact Spearman p-value via exhaustive permutation enumeration.
        -:  742: *
        -:  743: * Under H0, all n! orderings of ranks are equally probable.  We visit
        -:  744: * every permutation of {1..n} with Heap's algorithm (O(n!), no allocs
        1:  745: * inside the loop) and count how many yield S ≤ s_obs ("lower tail",
        1:  745-block  0
call    0 returned 1
call    1 returned 1
        -:  746: * i.e. rho ≥ rho_obs) and how many yield S ≥ s_obs ("upper tail").
        -:  747: *
        1:  748: * Mirrors R's default: exact = (n < 10) with no ties.
        1:  749: * Valid up to n = 9 (362 880 iterations — negligible cost).
        -:  750: * ----------------------------------------------------------------------- */
       1*:  751:static double spearman_exact_pvalue(double s_obs, size_t n, const char *restrict alt) {
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%:  751-block  0
       1*:  752:	int *restrict perm = (int*)safemalloc(n * sizeof(int));
        1:  752-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%:  752-block  1
        -:  753:	int *restrict c    = (int*)safemalloc(n * sizeof(int));
       1*:  754:	for (size_t i = 0; i < n; i++) { perm[i] = i + 1; c[i] = 0; }
        1:  754-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1:  754-block  1
    %%%%%:  754-block  2
       1*:  755:
        1:  755-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%:  755-block  1
        1:  755-block  2
        1:  755-block  3
        -:  756:	long count_le = 0, count_ge = 0, total = 0;
        -:  757:
        -:  758:	#define TALLY_PERM() do {                                    \
        -:  759:	  double s_ = 0.0;                                     \
        -:  760:	  for (int ii = 0; ii < n; ii++) {                    \
function kendall_exact_pvalue called 2 returned 100% blocks executed 88%
        2:  761:		   double d_ = (double)(ii + 1) - (double)perm[ii];\
        2:  762:		   s_ += d_ * d_;                                   \
        2:  763:	  }                                                    \
        2:  763-block  0
call    0 returned 2
       24:  764:	  if (s_ <= s_obs + 1e-9) count_le++;                 \
       22:  764-block  0
       24:  764-block  1
branch  0 taken 22
branch  1 taken 2 (fallthrough)
        2:  765:	  if (s_ >= s_obs - 1e-9) count_ge++;                 \
        -:  766:	  total++;                                             \
       10:  767:	} while (0)
        2:  767-block  0
       10:  767-block  1
branch  0 taken 8
branch  1 taken 2 (fallthrough)
        8:  768:
        8:  768-block  0
call    0 returned 8
       96:  769:	TALLY_PERM();   /* initial permutation [1, 2, ..., n] */
       88:  769-block  0
       96:  769-block  1
branch  0 taken 88
branch  1 taken 8 (fallthrough)
        8:  770:
       56:  771:	unsigned int k = 1;
        8:  771-block  0
       56:  771-block  1
branch  0 taken 48
branch  1 taken 8 (fallthrough)
       48:  772:	while (k < n) {
      206:  773:	  if (c[k] < k) {
       48:  773-block  0
      206:  773-block  1
branch  0 taken 178 (fallthrough)
branch  1 taken 28
      178:  773-block  2
branch  2 taken 158
branch  3 taken 20 (fallthrough)
      158:  774:		   int tmp;
      158:  774-block  0
        -:  775:		   if (k % 2 == 0) {
        -:  776:		       tmp = perm[0]; perm[0] = perm[k]; perm[k] = tmp;
       48:  777:		   } else {
       48:  777-block  0
        -:  778:		       tmp = perm[c[k]]; perm[c[k]] = perm[k]; perm[k] = tmp;
        8:  779:		   }
        8:  779-block  0
call    0 returned 8
        8:  780:		   TALLY_PERM();
        -:  781:		   c[k]++;
        -:  782:		   k = 1;
        2:  783:	  } else {
       2*:  784:		   c[k] = 0;
        2:  784-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%:  784-block  1
       2*:  785:		   k++;
        2:  785-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%:  785-block  1
        2:  786:	  }
       20:  787:	}
        2:  787-block  0
       18:  787-block  1
       20:  787-block  2
branch  0 taken 18
branch  1 taken 2 (fallthrough)
        2:  788:	#undef TALLY_PERM
        8:  789:
        2:  789-block  0
        6:  789-block  1
        8:  789-block  2
branch  0 taken 6
branch  1 taken 2 (fallthrough)
        2:  790:	Safefree(perm); Safefree(c);
        2:  790-block  0
call    0 returned 2
       2*:  791:	/* p_le = P(S ≤ s_obs) ≡ P(rho ≥ rho_obs)  — upper rho tail
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%:  791-block  0
        2:  792:	* p_ge = P(S ≥ s_obs) ≡ P(rho ≤ rho_obs)  — lower rho tail  */
        2:  792-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1:  792-block  1
        -:  793:	double p_le = (double)count_le / (double)total;
       1*:  794:	double p_ge = (double)count_ge / (double)total;
        1:  794-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1:  794-block  1
    %%%%%:  794-block  2
       1*:  795:
        1:  795-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%:  795-block  1
        1:  795-block  2
        1:  795-block  3
        -:  796:	if (strcmp(alt, "greater") == 0) return p_le;
        -:  797:	if (strcmp(alt, "less")    == 0) return p_ge;
function pf called 300 returned 100% blocks executed 75%
      300:  798:	/* two.sided: 2 × the smaller tail, clamped to 1 */
     300*:  799:	double p = 2.0 * (p_le < p_ge ? p_le : p_ge);
      300:  799-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 300
    %%%%%:  799-block  1
      300:  800:	return (p > 1.0) ? 1.0 : p;
      300:  801:}
      300:  801-block  0
call    0 returned 300
        -:  802:/* -----------------------------------------------------------------------
        -:  803: * Exact Kendall p-value via Mahonian Numbers (Inversions distribution)
        -:  804: * Matches R's behavior for N < 50 without ties.
        -:  805: * ----------------------------------------------------------------------- */
function apply_householder_aov called 6 returned 100% blocks executed 97%
        6:  806:static double kendall_exact_pvalue(size_t n, double s_obs, const char *restrict alt) {
        6:  807:	long max_inv = (long)n * (n - 1) / 2;
       24:  808:	double *restrict dp = (double*)safemalloc((max_inv + 1) * sizeof(double));
        6:  808-block  0
       18:  808-block  1
       24:  808-block  2
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18:  809:	for (long i = 0; i <= max_inv; i++) dp[i] = 0.0;
      18*:  810:	dp[0] = 1.0;
       18:  810-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    #####:  811:	/* Build the distribution of inversions via DP */
    #####:  812:	for (size_t i = 2; i <= n; i++) {
    %%%%%:  812-block  0
        -:  813:	  double *restrict next_dp = (double*)safemalloc((max_inv + 1) * sizeof(double));
        -:  814:	  for (long k = 0; k <= max_inv; k++) next_dp[k] = 0.0;
       18:  815:	  int current_max_inv = i * (i - 1) / 2;
      163:  816:	  for (int k = 0; k <= current_max_inv; k++) {
       18:  816-block  0
      145:  816-block  1
      163:  816-block  2
branch  0 taken 145
branch  1 taken 18 (fallthrough)
      145:  817:		   double sum = 0;
      145:  817-block  0
branch  0 taken 38 (fallthrough)
branch  1 taken 107
       38:  817-block  1
        -:  818:		   for (int j = 0; j <= i - 1 && k - j >= 0; j++) {
       18:  819:		       sum += dp[k - j];
       18:  819-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 17
        1:  820:		   }
        1:  821:		   // Divide by 'i' directly to keep array as pure probabilities and prevent overflow
        1:  821-block  0
        -:  822:		   next_dp[k] = sum / (double)i;
        -:  823:	  }
       17:  824:	  Safefree(dp);
      159:  825:	  dp = next_dp;
       17:  825-block  0
      159:  825-block  1
branch  0 taken 142
branch  1 taken 17 (fallthrough)
      142:  826:	}
      142:  827:	// Convert S statistic to target number of inversions
      142:  827-block  0
        -:  828:	long i_obs = (long)round((max_inv - s_obs) / 2.0);
       17:  829:	if (i_obs < 0) i_obs = 0;
       17:  830:	if (i_obs > max_inv) i_obs = max_inv;
       17:  830-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 7
       10:  830-block  1
        7:  830-block  2
       17:  831:	double p_le = 0.0; /* P(S <= S_obs) */
       17:  832:	for (long k = i_obs; k <= max_inv; k++) p_le += dp[k];
        -:  833:	double p_ge = 0.0; /* P(S >= S_obs) */
       36:  834:	for (long k = 0; k <= i_obs; k++) p_ge += dp[k];
       17:  834-block  0
       19:  834-block  1
       36:  834-block  2
branch  0 taken 19
branch  1 taken 17 (fallthrough)
       19:  835:	Safefree(dp);
      190:  836:	if (strcmp(alt, "greater") == 0) return p_ge;
       19:  836-block  0
      171:  836-block  1
      190:  836-block  2
branch  0 taken 171
branch  1 taken 19 (fallthrough)
       19:  837:	if (strcmp(alt, "less") == 0) return p_le;
       19:  838:	// two.sided
      190:  839:	double p = 2.0 * (p_ge < p_le ? p_ge : p_le);
       19:  839-block  0
      171:  839-block  1
      190:  839-block  2
branch  0 taken 171
branch  1 taken 19 (fallthrough)
        -:  840:	return p > 1.0 ? 1.0 : p;
        -:  841:}
        -:  842:// F-distribution Cumulative Distribution Function P(F <= f)
       17:  843:static double pf(double f, double df1, double df2) {
      142:  844:	if (f <= 0.0) return 0.0;
       17:  844-block  0
      125:  844-block  1
      142:  844-block  2
branch  0 taken 125
branch  1 taken 17 (fallthrough)
       17:  845:	double x = (df1 * f) / (df1 * f + df2);
       17:  846:	return incbeta(df1 / 2.0, df2 / 2.0, x);
      142:  847:}
       17:  847-block  0
      125:  847-block  1
      142:  847-block  2
branch  0 taken 125
branch  1 taken 17 (fallthrough)
        -:  848:
       17:  849:/* Householder QR Decomposition for Sequential Sums of Squares */
       17:  850:/* Householder QR Decomposition for Sequential Sums of Squares */
       17:  850-block  0
        -:  851: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) {
        6:  852:	size_t r = 0; // Rank/Row tracker
        -:  853:	for (size_t k = 0; k < p; k++) {
        -:  854:		aliased[k] = FALSE;
        -:  855:		if (r >= n) {
        -:  856:			aliased[k] = TRUE;
function cmp_string_wt called 22 returned 100% blocks executed 100%
       22:  857:			continue;
       22:  858:		}
       22:  858-block  0
        -:  859:
        -:  860:		double max_val = 0;
        -:  861:		for (size_t i = r; i < n; i++) {
function contains_nondigit called 5 returned 100% blocks executed 83%
        5:  862:			if (fabs(X[i][k]) > max_val) max_val = fabs(X[i][k]);
       5*:  863:		}
        5:  863-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5:  863-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 5
    %%%%%:  863-block  2
        -:  864:		if (max_val < 1e-10) { 
        5:  865:			aliased[k] = TRUE; 
        5:  865-block  0
call    0 returned 5
       10:  866:			continue; 
        5:  866-block  0
       10:  866-block  1
branch  0 taken 5
branch  1 taken 5 (fallthrough)
       5*:  867:		} // Collinear or zero column
        5:  867-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%:  867-block  1
        -:  868:
        5:  869:		double norm = 0;
        5:  869-block  0
        -:  870:		for (size_t i = r; i < n; i++) {
        -:  871:			X[i][k] /= max_val;
        -:  872:			norm += X[i][k] * X[i][k];
function print_str_quoted called 198 returned 100% blocks executed 95%
      198:  873:		}
     198*:  874:		norm = sqrt(norm);
      198:  874-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 198
    %%%%%:  874-block  1
      198:  875:		double s = (X[r][k] > 0) ? -norm : norm;
      198:  876:		double u1 = X[r][k] - s;
      198:  876-block  0
branch  0 taken 193 (fallthrough)
branch  1 taken 5
      193:  876-block  1
branch  2 taken 187 (fallthrough)
branch  3 taken 6
      187:  876-block  2
branch  4 taken 187 (fallthrough)
branch  5 taken 0
      187:  876-block  3
branch  6 taken 0 (fallthrough)
branch  7 taken 187
       11:  877:		X[r][k] = s * max_val;
       11:  877-block  0
        -:  878:
        -:  879:		for (size_t j = k + 1; j < p; j++) {
      198:  880:			double dot = u1 * X[r][j];
      198:  880-block  0
branch  0 taken 11 (fallthrough)
branch  1 taken 187
       11:  881:			for (size_t i = r + 1; i < n; i++) dot += X[i][j] * X[i][k];
       11:  881-block  0
call    0 returned 11
      118:  882:			double tau = dot / (s * u1);
      107:  882-block  0
      118:  882-block  1
branch  0 taken 107
branch  1 taken 11 (fallthrough)
      107:  883:			X[r][j] += tau * u1;
      107:  883-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 100
        7:  884:			for (size_t i = r + 1; i < n; i++) X[i][j] += tau * X[i][k];
        7:  884-block  0
call    0 returned 7
        7:  885:		}
call    0 returned 7
        -:  886:
      100:  887:		// Transform the response vector y
      100:  887-block  0
call    0 returned 100
        -:  888:		double dot_y = u1 * y[r];
        -:  889:		for (size_t i = r + 1; i < n; i++) dot_y += y[i] * X[i][k];
       11:  890:		double tau_y = dot_y / (s * u1);
       11:  890-block  0
call    0 returned 11
        -:  891:		y[r] += tau_y * u1;
      187:  892:		for (size_t i = r + 1; i < n; i++) y[i] += tau_y * X[i][k];
      187:  892-block  0
call    0 returned 187
        -:  893:
      198:  894:		rank_map[k] = r; // Map original column index to orthogonal row index
        -:  895:		r++;
        -:  896:	}
function print_string_row called 53 returned 100% blocks executed 90%
       53:  897:}
       53:  898:
      251:  899:// --- write_table Helpers ---
       53:  899-block  0
      198:  899-block  1
      251:  899-block  2
branch  0 taken 198
branch  1 taken 53 (fallthrough)
      198:  900:
      198:  900-block  0
branch  0 taken 145 (fallthrough)
branch  1 taken 53
      145:  900-block  1
call    2 returned 145
      198:  901:// Sorts string arrays alphabetically
      198:  901-block  0
branch  0 taken 198 (fallthrough)
branch  1 taken 0
      198:  902:static int cmp_string_wt(const void *a, const void *b) {
      198:  902-block  0
call    0 returned 198
        -:  903:	return strcmp(*(const char**)a, *(const char**)b);
    #####:  904:}
    %%%%%:  904-block  0
call    0 never executed
        -:  905:
        -:  906:// Emulates Perl's /\D/ check
       53:  907:static bool contains_nondigit(SV *restrict sv) {
       53:  907-block  0
call    0 returned 53
       53:  908:	if (!sv || !SvOK(sv)) return 0;
        -:  909:	STRLEN len;
        -:  910:	const char *restrict s = SvPVbyte(sv, len);
function igamc called 6 returned 100% blocks executed 81%
        6:  911:	for (size_t i = 0; i < len; i++) {
       6*:  912:	  if (!isdigit(s[i])) return 1;
        6:  912-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6:  912-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    %%%%%:  912-block  2
       6*:  913:	}
        6:  913-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  913-block  1
        -:  914:	return 0;
        -:  915:}
        6:  916:
        6:  916-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3:  917:// Writes a properly quoted string dynamically
        3:  918:static void print_str_quoted(PerlIO *fh, const char *str, const char *sep) {
        3:  919:	if (!str) str = "";
       42:  920:	bool needs_quotes = 0;
        3:  920-block  0
       42:  920-block  1
branch  0 taken 39
branch  1 taken 3 (fallthrough)
       39:  921:	if (strstr(str, sep) != NULL || strchr(str, '"') != NULL || strchr(str, '\r') != NULL || strchr(str, '\n') != NULL) {
       39:  922:	  needs_quotes = 1;
       39:  923:	}
       39:  923-block  0
        -:  924:
        3:  925:	if (needs_quotes) {
        3:  925-block  0
        -:  926:	  PerlIO_putc(fh, '"');
        -:  927:	  for (const char *restrict p = str; *p; p++) {
        -:  928:		   if (*p == '"') {
        3:  929:		       PerlIO_putc(fh, '"');
        3:  930:		       PerlIO_putc(fh, '"');
        3:  931:		   } else {
        3:  932:		       PerlIO_putc(fh, *p);
       47:  933:		   }
        3:  933-block  0
       47:  933-block  1
branch  0 taken 47
branch  1 taken 0 (fallthrough)
       47:  934:	  }
       47:  935:	  PerlIO_putc(fh, '"');
       47:  936:	} else {
      47*:  937:	  PerlIO_puts(fh, str);
       47:  937-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 47
    %%%%%:  937-block  1
       47:  938:	}
      47*:  939:}
       47:  939-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 47
    %%%%%:  939-block  1
       47:  940:
       47:  941:// Writes an array of strings joined by sep
       47:  942:static void print_string_row(PerlIO *fh, const char **row, size_t len, const char *sep) {
       47:  943:	size_t sep_len = strlen(sep);
       47:  943-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 44
        3:  943-block  1
       44:  944:	for (size_t i = 0; i < len; i++) {
       44:  944-block  0
        -:  945:	  if (i > 0) PerlIO_write(fh, sep, sep_len);
        3:  946:	  if (row[i]) {
        3:  946-block  0
        -:  947:		   print_str_quoted(fh, row[i], sep);
        -:  948:	  } else {
        -:  949:		   print_str_quoted(fh, "", sep);
function get_p_value called 6 returned 100% blocks executed 67%
        6:  950:	  }
       6*:  951:	}
        6:  951-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  951-block  1
       6*:  952:	PerlIO_putc(fh, '\n');
        6:  952-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  952-block  1
        6:  953:}
        6:  953-block  0
call    0 returned 6
        -:  954:// Calculates the Regularized Upper Incomplete Gamma Function Q(a, x)
        -:  955:// This perfectly replicates R's pchisq(..., lower.tail=FALSE)
        -:  956:double igamc(double a, double x) {
        -:  957:	if (x < 0.0 || a <= 0.0) return 1.0;
        -:  958:	if (x == 0.0) return 1.0;
        -:  959:
        -:  960:	// Series expansion for x < a + 1
        -:  961:	if (x < a + 1.0) {
function choose_comb called 2 returned 100% blocks executed 80%
        2:  962:		double sum = 1.0 / a;
       2*:  963:		double term = 1.0 / a;
        2:  963-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2:  963-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    %%%%%:  963-block  2
       2*:  964:		double n = 1.0;
        2:  964-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%:  964-block  1
        2:  965:		while (fabs(term) > 1e-15) {
        8:  966:			term *= x / (a + n);
        2:  966-block  0
        8:  966-block  1
branch  0 taken 6
branch  1 taken 2 (fallthrough)
        6:  967:			sum += term;
        6:  967-block  0
        -:  968:			n += 1.0;
        2:  969:		}
        2:  969-block  0
        -:  970:		return 1.0 - (sum * exp(-x + a * log(x) - lgamma(a)));
        -:  971:	}
        -:  972:
        -:  973:	// Continued fraction for x >= a + 1
function exact_pwilcox called 4 returned 100% blocks executed 95%
        4:  974:	double b = x + 1.0 - a;
        4:  975:	double c = 1.0 / 1e-30;
        4:  976:	double d = 1.0 / b;
        4:  977:	double h = d, i = 1.0;
        4:  977-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 2
        2:  977-block  1
       2*:  978:	while (i < 10000) { // Safety bound
        2:  978-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%:  978-block  1
        -:  979:		double an = -i * (i - a);
        2:  980:		b += 2.0;
        2:  980-block  0
call    0 returned 2
        2:  981:		d = an * d + b;
        -:  982:		if (fabs(d) < 1e-30) d = 1e-30;
        8:  983:		c = b + an / c;
        6:  983-block  0
        8:  983-block  1
branch  0 taken 6
branch  1 taken 2 (fallthrough)
       54:  984:		if (fabs(c) < 1e-30) c = 1e-30;
        6:  984-block  0
       48:  984-block  1
       54:  984-block  2
branch  0 taken 48
branch  1 taken 6 (fallthrough)
       36:  985:		d = 1.0 / d;
        6:  985-block  0
       30:  985-block  1
       36:  985-block  2
branch  0 taken 30
branch  1 taken 6 (fallthrough)
        -:  986:		double del = d * c;
        -:  987:		h *= del;
        2:  988:		if (fabs(del - 1.0) < 1e-15) break;
        4:  989:		i += 1.0;
        2:  989-block  0
        2:  989-block  1
        4:  989-block  2
branch  0 taken 2
branch  1 taken 2 (fallthrough)
        -:  990:	}
        2:  991:	return h * exp(-x + a * log(x) - lgamma(a));
        2:  991-block  0
call    0 returned 2
        2:  992:}
        -:  993:
        2:  994:// Chi-Squared p-value is simply the Incomplete Gamma of (df/2, stat/2)
call    0 returned 2
        2:  995:double get_p_value(double stat, int df) {
        -:  996:	if (df <= 0) return 1.0;
        -:  997:	if (stat <= 0.0) return 1.0;
        -:  998:	return igamc((double)df / 2.0, stat / 2.0);
        -:  999:}
function exact_psignrank called 6 returned 100% blocks executed 94%
        6: 1000:
        6: 1001:/* --- C HELPER SECTION --- */
        6: 1002:#ifndef M_SQRT1_2
       6*: 1003:#define M_SQRT1_2 0.70710678118654752440
        6: 1003-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 1003-block  1
        6: 1004:#endif
        6: 1004-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1: 1004-block  1
        -: 1005:
        5: 1006:/* Robust Binomial Coefficient using long double */
        5: 1006-block  0
call    0 returned 5
        5: 1007:static long double choose_comb(int n, int k) {
        -: 1008:	if (k < 0 || k > n) return 0.0L;
       46: 1009:	if (k > n / 2) k = n - k;
       41: 1009-block  0
       46: 1009-block  1
branch  0 taken 41
branch  1 taken 5 (fallthrough)
     1582: 1010:	long double res = 1.0L;
       41: 1010-block  0
     1541: 1010-block  1
     1582: 1010-block  2
branch  0 taken 1541
branch  1 taken 41 (fallthrough)
        -: 1011:	for (int i = 1; i <= k; i++) {
        -: 1012:	  res = res * (long double)(n - i + 1) / (long double)i;
        5: 1013:	}
      182: 1014:	return res;
        5: 1014-block  0
      177: 1014-block  1
      182: 1014-block  2
branch  0 taken 177
branch  1 taken 5 (fallthrough)
        -: 1015:}
        5: 1016:
        5: 1017:/* Exact CDF for Mann-Whitney U: P(U <= q) 
        -: 1018:   Mathematically identical to R's cwilcox generating function */
        5: 1019:static double exact_pwilcox(double q, int m, int n) {
        5: 1019-block  0
call    0 returned 5
        5: 1020:    int k = (int)floor(q + 1e-7); // R uses 1e-7 fuzz
        -: 1021:    int max_u = m * n;
        -: 1022:    if (k < 0) return 0.0;
function cmp_rank_info called 298 returned 100% blocks executed 100%
      298: 1023:    if (k >= max_u) return 1.0;
      298: 1024:
      298: 1025:    long double *restrict w = (long double *)safecalloc(max_u + 1, sizeof(long double));
      298: 1026:    w[0] = 1.0L;
      298: 1026-block  0
        -: 1027:
        -: 1028:    for (int j = 1; j <= n; j++) {
function rank_and_count_ties called 11 returned 100% blocks executed 94%
       11: 1029:        for (int i = j; i <= max_u; i++) w[i] += w[i - j];
      11*: 1030:        for (int i = max_u; i >= j + m; i--) w[i] -= w[i - j - m];
       11: 1030-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 11
    %%%%%: 1030-block  1
       11: 1031:    }
       11: 1031-block  0
call    0 returned 11
       11: 1032:
       11: 1033:    long double cum_p = 0.0L;
       11: 1034:    for (int i = 0; i <= k; i++) cum_p += w[i];
      124: 1035:    
      124: 1035-block  0
branch  0 taken 113
branch  1 taken 11 (fallthrough)
      113: 1036:    long double total = choose_comb(m + n, n);
      121: 1037:    double result = (double)(cum_p / total);
      113: 1037-block  0
        8: 1037-block  1
      121: 1037-block  2
branch  0 taken 110 (fallthrough)
branch  1 taken 11
      110: 1037-block  3
branch  2 taken 8
branch  3 taken 102 (fallthrough)
      113: 1038:    
      234: 1039:    Safefree(w);
      113: 1039-block  0
      121: 1039-block  1
      234: 1039-block  2
branch  0 taken 121
branch  1 taken 113 (fallthrough)
      113: 1040:    return result;
      113: 1041:}
      113: 1041-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 107
        6: 1041-block  1
      113: 1042:
      113: 1042-block  0
        -: 1043:/* Exact CDF for Wilcoxon Signed Rank: P(V <= q) 
       11: 1044:   Mathematically identical to R's csignrank subset-sum DP */
       11: 1044-block  0
        -: 1045:static double exact_psignrank(double q, int n) {
        -: 1046:	int k = (int)floor(q + 1e-7);
        -: 1047:	int max_v = n * (n + 1) / 2;
        -: 1048:	if (k < 0) return 0.0;
        -: 1049:	if (k >= max_v) return 1.0;
        -: 1050:
        -: 1051:	long double *restrict w = (long double *)safecalloc(max_v + 1, sizeof(long double));
        -: 1052:	w[0] = 1.0L;
        -: 1053:
        -: 1054:	for (int i = 1; i <= n; i++) {
        -: 1055:	  for (int j = max_v; j >= i; j--) w[j] += w[j - i];
        -: 1056:	}
        -: 1057:
function r_pow_di called 39 returned 100% blocks executed 70%
       39: 1058:	long double cum_p = 0.0L;
      39*: 1059:	for (int i = 0; i <= k; i++) cum_p += w[i];
       39: 1059-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    %%%%%: 1059-block  1
      39*: 1060:
       39: 1060-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    %%%%%: 1060-block  1
call    2 never executed
       39: 1061:	long double total = powl(2.0L, (long double)n);
      438: 1062:	double result = (double)(cum_p / total);
       39: 1062-block  0
      399: 1062-block  1
      438: 1062-block  2
branch  0 taken 399
branch  1 taken 39 (fallthrough)
       39: 1063:
       39: 1063-block  0
        -: 1064:	Safefree(w);
        -: 1065:	return result;
        -: 1066:}
function K2l called 0 returned 0% blocks executed 0%
    #####: 1067:
        -: 1068:static int cmp_rank_info(const void *a, const void *b) {
        -: 1069:	double da = ((const RankInfo*)a)->val;
    #####: 1070:	double db = ((const RankInfo*)b)->val;
    %%%%%: 1070-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1071:	return (da > db) - (da < db);
    %%%%%: 1071-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1071-block  1
    #####: 1072:}
    %%%%%: 1072-block  0
    #####: 1073:
    %%%%%: 1073-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1074:static double rank_and_count_ties(RankInfo *restrict ri, size_t n, bool *restrict has_ties) {
    #####: 1075:	if (n == 0) return 0.0;
    #####: 1076:	qsort(ri, n, sizeof(RankInfo), cmp_rank_info);
    #####: 1077:	size_t i = 0;
    #####: 1078:	double tie_adj = 0.0;
    %%%%%: 1078-block  0
    %%%%%: 1078-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 1079:	*has_ties = 0;
    %%%%%: 1079-block  0
        -: 1080:	while (i < n) {
    #####: 1081:		size_t j = i + 1;
    #####: 1082:		while (j < n && ri[j].val == ri[i].val) j++;
    %%%%%: 1082-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1082-block  1
        -: 1083:		double r = (double)(i + 1 + j) / 2.0; 
        -: 1084:		for (size_t k = i; k < j; k++) ri[k].rank = r;
    #####: 1085:		size_t t = j - i;
    #####: 1086:		if (t > 1) { *has_ties = 1; tie_adj += ((double)t * t * t - t); }
    #####: 1087:		i = j;
    %%%%%: 1087-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1088:	}
    %%%%%: 1088-block  0
        -: 1089:	return tie_adj;
    #####: 1090:}
    %%%%%: 1090-block  0
        -: 1091:/* --- KS-TEST C HELPER SECTION --- */
    #####: 1092:#ifndef M_PI_2
    %%%%%: 1092-block  0
    %%%%%: 1092-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 1093:#define M_PI_2 1.57079632679489661923
    #####: 1094:#endif
    #####: 1095:#ifndef M_PI_4
    #####: 1096:#define M_PI_4 0.78539816339744830962
    %%%%%: 1096-block  0
        -: 1097:#endif
    #####: 1098:#ifndef M_1_SQRT_2PI
    %%%%%: 1098-block  0
        -: 1099:#define M_1_SQRT_2PI 0.39894228040143267794
    #####: 1100:#endif
    %%%%%: 1100-block  0
        -: 1101:
        -: 1102:// Scalar integer power used by K2x
        -: 1103:static double r_pow_di(double x, int n) {
function m_multiply called 7 returned 100% blocks executed 100%
        7: 1104:	if (n == 0) return 1.0;
      140: 1105:	if (n < 0) return 1.0 / r_pow_di(x, -n);
        7: 1105-block  0
      133: 1105-block  1
      140: 1105-block  2
branch  0 taken 133
branch  1 taken 7 (fallthrough)
     2660: 1106:	double val = 1.0;
      133: 1106-block  0
     2660: 1106-block  1
branch  0 taken 2527
branch  1 taken 133 (fallthrough)
     2527: 1107:	for (int i = 0; i < n; i++) val *= x;
    50540: 1108:	return val;
     2527: 1108-block  0
    48013: 1108-block  1
    50540: 1108-block  2
branch  0 taken 48013
branch  1 taken 2527 (fallthrough)
     2527: 1109:}
     2527: 1109-block  0
        -: 1110:
        -: 1111:// Two-sample two-sided asymptotic distribution
        7: 1112:static double K2l(double x, int lower, double tol) {
        -: 1113:	double s, z, p;
function m_power called 6 returned 100% blocks executed 82%
        6: 1114:	int k;
        6: 1115:	if(x <= 0.) {
        6: 1115-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
      362: 1116:	  if(lower) p = 0.;
        1: 1116-block  0
      361: 1116-block  1
      362: 1116-block  2
branch  0 taken 361
branch  1 taken 1 (fallthrough)
        1: 1117:	  else p = 1.;
        1: 1118:	} else if(x < 1.) {
        1: 1118-block  0
        -: 1119:	  int k_max = (int) sqrt(2.0 - log(tol));
        5: 1120:	  double w = log(x);
        5: 1120-block  0
call    0 returned 5
        5: 1121:	  z = - (M_PI_2 * M_PI_4) / (x * x);
call    0 returned 5
        5: 1122:	  s = 0;
call    0 returned 5
        5: 1123:	  for(k = 1; k < k_max; k += 2) {
        5: 1124:		   s += exp(k * k * z - w);
branch  0 taken 3 (fallthrough)
branch  1 taken 2
     1086: 1125:	  }
        3: 1125-block  0
     1083: 1125-block  1
     1086: 1125-block  2
branch  0 taken 1083
branch  1 taken 3 (fallthrough)
        3: 1126:	  p = s / M_1_SQRT_2PI;
        3: 1126-block  0
        -: 1127:	  if(!lower) p = 1.0 - p;
        2: 1128:	} else {
        2: 1128-block  0
call    0 returned 2
        2: 1129:	  double new_val, old_val;
        -: 1130:	  z = -2.0 * x * x;
        5: 1131:	  s = -1.0;
        5: 1131-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    #####: 1132:	  if(lower) {
    %%%%%: 1132-block  0
    %%%%%: 1132-block  1
    %%%%%: 1132-block  2
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 1133:		   k = 1; old_val = 0.0; new_val = 1.0;
    %%%%%: 1133-block  0
        -: 1134:	  } else {
        5: 1135:		   k = 2; old_val = 0.0; new_val = 2.0 * exp(z);
        5: 1135-block  0
call    0 returned 5
        -: 1136:	  }
        -: 1137:	  while(fabs(old_val - new_val) > tol) {
        -: 1138:		   old_val = new_val;
function K2x called 1 returned 100% blocks executed 95%
        1: 1139:		   new_val += 2.0 * s * exp(z * k * k);
        1: 1140:		   s *= -1.0;
        1: 1141:		   k++;
        1: 1142:	  }
        1: 1143:	  p = new_val;
        1: 1143-block  0
call    0 returned 1
        1: 1144:	}
call    0 returned 1
        -: 1145:	return p;
       20: 1146:}
       19: 1146-block  0
       20: 1146-block  1
branch  0 taken 19
branch  1 taken 1 (fallthrough)
      380: 1147:
       19: 1147-block  0
      361: 1147-block  1
      380: 1147-block  2
branch  0 taken 361
branch  1 taken 19 (fallthrough)
      361: 1148:// Auxiliary routines used by K2x() for matrix operations
      361: 1148-block  0
branch  0 taken 153 (fallthrough)
branch  1 taken 208
      153: 1148-block  1
      208: 1149:static void m_multiply(double *A, double *B, double *C, unsigned int m) {
      208: 1149-block  0
        -: 1150:	for(unsigned int i = 0; i < m; i++) {
        -: 1151:	  for(unsigned int j = 0; j < m; j++) {
       20: 1152:		   double s = 0.;
        1: 1152-block  0
       20: 1152-block  1
branch  0 taken 19
branch  1 taken 1 (fallthrough)
       19: 1153:		   for(unsigned int k = 0; k < m; k++) s += A[i * m + k] * B[k * m + j];
       19: 1153-block  0
call    0 returned 19
       19: 1154:		   C[i * m + j] = s;
call    0 returned 19
        -: 1155:	  }
       1*: 1156:	}
        1: 1156-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1156-block  1
call    2 returned 1
    %%%%%: 1156-block  2
        -: 1157:}
       20: 1158:
        1: 1158-block  0
       19: 1158-block  1
       20: 1158-block  2
branch  0 taken 19
branch  1 taken 1 (fallthrough)
      380: 1159:static void m_power(double *A, int eA, double *V, int *eV, int m, int n) {
       19: 1159-block  0
      361: 1159-block  1
      380: 1159-block  2
branch  0 taken 361
branch  1 taken 19 (fallthrough)
      361: 1160:    if(n == 1) {
      361: 1160-block  0
branch  0 taken 190 (fallthrough)
branch  1 taken 171
     1520: 1161:        for(int i = 0; i < m * m; i++) V[i] = A[i];
      190: 1161-block  0
     1330: 1161-block  1
     1520: 1161-block  2
branch  0 taken 1330
branch  1 taken 190 (fallthrough)
        -: 1162:        *eV = eA;
        -: 1163:        return;
        -: 1164:    }
        -: 1165:    m_power(A, eA, V, eV, m, n / 2);
        1: 1166:    double *restrict B = (double*) safecalloc(m * m, sizeof(double));
        1: 1167:    m_multiply(V, V, B, m);
        1: 1167-block  0
call    0 returned 1
        1: 1168:    int eB = 2 * (*eV);
        -: 1169:    if((n % 2) == 0) {
       51: 1170:        for(int i = 0; i < m * m; i++) V[i] = B[i];
       50: 1170-block  0
       51: 1170-block  1
branch  0 taken 50
branch  1 taken 1 (fallthrough)
       50: 1171:        *eV = eB;
       50: 1172:    } else {
       50: 1172-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 50
    #####: 1173:        m_multiply(A, B, V, m);
    #####: 1174:        *eV = eA + eB;
    %%%%%: 1174-block  0
        -: 1175:    }
        -: 1176:    if(V[(m / 2) * m + (m / 2)] > 1e140) {
        1: 1177:        for(int i = 0; i < m * m; i++) V[i] = V[i] * 1e-140;
        1: 1178:        *eV += 140;
        1: 1178-block  0
call    0 returned 1
        1: 1179:    }
call    0 returned 1
        1: 1180:    Safefree(B);
        -: 1181:}
        -: 1182:
        -: 1183:// One-sample two-sided exact distribution
function calc_2sample_stats called 3 returned 100% blocks executed 97%
        3: 1184:static double K2x(int n, double d) {
        -: 1185:	int k = (int) (n * d) + 1;
        3: 1186:	int m = 2 * k - 1;
        3: 1186-block  0
call    0 returned 3
        3: 1187:	double h = k - n * d;
call    0 returned 3
        3: 1188:	double *restrict H = (double*) safecalloc(m * m, sizeof(double));
        3: 1189:	double *restrict Q = (double*) safecalloc(m * m, sizeof(double));
        -: 1190:
      243: 1191:	for(int i = 0; i < m; i++) {
      243: 1191-block  0
branch  0 taken 240
branch  1 taken 3 (fallthrough)
        3: 1191-block  1
branch  2 taken 0
branch  3 taken 3 (fallthrough)
        -: 1192:	  for(int j = 0; j < m; j++) {
      240: 1193:		   if(i - j + 1 < 0) H[i * m + j] = 0;
      240: 1193-block  0
branch  0 taken 240 (fallthrough)
branch  1 taken 0
      240: 1193-block  1
branch  2 taken 201 (fallthrough)
branch  3 taken 39
      201: 1193-block  2
branch  4 taken 111 (fallthrough)
branch  5 taken 90
      111: 1193-block  3
       90: 1193-block  4
      201: 1193-block  5
       39: 1194:		   else H[i * m + j] = 1;
       39: 1194-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 1194-block  1
    #####: 1195:	  }
    %%%%%: 1195-block  0
        -: 1196:	}
      390: 1197:	for(int i = 0; i < m; i++) {
      240: 1197-block  0
      150: 1197-block  1
      390: 1197-block  2
branch  0 taken 387 (fallthrough)
branch  1 taken 3
      387: 1197-block  3
branch  2 taken 150
branch  3 taken 237 (fallthrough)
      330: 1198:	  H[i * m] -= r_pow_di(h, i + 1);
      240: 1198-block  0
       90: 1198-block  1
      330: 1198-block  2
branch  0 taken 288 (fallthrough)
branch  1 taken 42
      288: 1198-block  3
branch  2 taken 90
branch  3 taken 198 (fallthrough)
        -: 1199:	  H[(m - 1) * m + i] -= r_pow_di(h, (m - i));
      240: 1200:	}
      240: 1201:	H[(m - 1) * m] += ((2 * h - 1 > 0) ? r_pow_di(2 * h - 1, m) : 0);
      240: 1202:
        -: 1203:	for(int i = 0; i < m; i++) {
      240: 1204:	  for(int j = 0; j < m; j++) {
      240: 1204-block  0
branch  0 taken 63 (fallthrough)
branch  1 taken 177
       63: 1204-block  1
      240: 1205:		   if(i - j + 1 > 0) {
      240: 1205-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 210
       30: 1205-block  1
      240: 1206:		       for(int g = 1; g <= i - j + 1; g++) H[i * m + j] /= g;
      240: 1206-block  0
branch  0 taken 63 (fallthrough)
branch  1 taken 177
       63: 1206-block  1
        -: 1207:		   }
        3: 1208:	  }
        3: 1209:	}
        3: 1210:
        3: 1211:	int eH = 0, eQ;
        -: 1212:	m_power(H, eH, Q, &eQ, m, n);
        -: 1213:	double s = Q[(k - 1) * m + k - 1];
function psmirnov_exact_test called 4740 returned 100% blocks executed 100%
     4740: 1214:
     4740: 1215:	for(int i = 1; i <= n; i++) {
     4740: 1215-block  0
branch  0 taken 1580 (fallthrough)
branch  1 taken 3160
     1580: 1215-block  1
     3160: 1216:	  s = s * (double)i / (double)n;
     3160: 1216-block  0
        -: 1217:	  if(s < 1e-140) {
        -: 1218:		   s *= 1e140;
        -: 1219:		   eQ -= 140;
function psmirnov_exact_uniq_upper called 3 returned 100% blocks executed 100%
        3: 1220:	  }
        3: 1221:	}
        3: 1222:	s *= pow(10.0, eQ);
        3: 1222-block  0
call    0 returned 3
        3: 1223:	Safefree(H);
        -: 1224:	Safefree(Q);
       93: 1225:	return s;
       90: 1225-block  0
       93: 1225-block  1
branch  0 taken 90
branch  1 taken 3 (fallthrough)
       90: 1226:}
       90: 1226-block  0
call    0 returned 90
branch  1 taken 18 (fallthrough)
branch  2 taken 72
       18: 1226-block  1
       72: 1227:
       72: 1227-block  0
        -: 1228:// Calculate D (two-sided), D+ (greater), and D- (less) simultaneously
      153: 1229:static void calc_2sample_stats(double *x, size_t nx, double *y, size_t ny,
        3: 1229-block  0
      150: 1229-block  1
      153: 1229-block  2
branch  0 taken 150
branch  1 taken 3 (fallthrough)
      150: 1230:                               double *d, double *d_plus, double *d_minus) {
      150: 1230-block  0
call    0 returned 150
branch  1 taken 98 (fallthrough)
branch  2 taken 52
       98: 1230-block  1
     4650: 1231:	qsort(x, nx, sizeof(double), compare_doubles);
      150: 1231-block  0
     4500: 1231-block  1
     4650: 1231-block  2
branch  0 taken 4500
branch  1 taken 150 (fallthrough)
     4500: 1232:	qsort(y, ny, sizeof(double), compare_doubles);
     4500: 1232-block  0
call    0 returned 4500
branch  1 taken 1166 (fallthrough)
branch  2 taken 3334
     1166: 1232-block  1
        -: 1233:	double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
     3334: 1234:	size_t i = 0, j = 0;
     3334: 1235:
     3334: 1236:	while(i < nx || j < ny) {
     3334: 1236-block  0
        -: 1237:	  double val;
        -: 1238:	  if (i < nx && j < ny) val = (x[i] < y[j]) ? x[i] : y[j];
        -: 1239:	  else if (i < nx) val = x[i];
        3: 1240:	  else val = y[j];
        3: 1241:	  
        3: 1241-block  0
call    0 returned 3
        3: 1242:	  while(i < nx && x[i] <= val) i++;
        -: 1243:	  while(j < ny && y[j] <= val) j++;
        -: 1244:	  
function p_body called 229 returned 100% blocks executed 55%
      229: 1245:	  double cdf1 = (double)i / nx;
      229: 1246:	  double cdf2 = (double)j / ny;
     229*: 1247:	  double diff = cdf1 - cdf2;
      229: 1247-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 229
    %%%%%: 1247-block  1
        -: 1248:	  
        -: 1249:	  if (diff > max_d_plus) max_d_plus = diff;
      229: 1250:	  if (-diff > max_d_minus) max_d_minus = -diff;
      229: 1251:	  if (fabs(diff) > max_d) max_d = fabs(diff);
      229: 1251-block  0
call    0 returned 229
        -: 1252:	}
      229: 1253:	*d = max_d;
        -: 1254:	*d_plus = max_d_plus;
     229*: 1255:	*d_minus = max_d_minus;
branch  0 taken 0 (fallthrough)
branch  1 taken 229
    %%%%%: 1255-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
        -: 1256:}
    #####: 1257:
    %%%%%: 1257-block  0
call    0 never executed
call    1 never executed
        -: 1258:// Branch the DP boundary check based on the 'alternative'
        -: 1259:static int psmirnov_exact_test(double q, double r, double s, int two_sided) {
        -: 1260:	if (two_sided) return (fabs(r - s) >= q);
      229: 1261:	return ((r - s) >= q); // Used for both D+ and D- via symmetry
      229: 1261-block  0
call    0 returned 229
        -: 1262:}
        -: 1263:
        -: 1264:// Evaluate the exact 2-sample probability
        -: 1265:static double psmirnov_exact_uniq_upper(double q, int m, int n, int two_sided) {
        -: 1266:	double md = (double) m, nd = (double) n;
function qf_bisection called 6 returned 100% blocks executed 86%
        6: 1267:	double *restrict u = (double *) safecalloc(n + 1, sizeof(double));
       6*: 1268:	u[0] = 0.;
        6: 1268-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 1268-block  1
       6*: 1269:
        6: 1269-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 1269-block  1
        6: 1270:	for(unsigned int j = 1; j <= n; j++) {
        -: 1271:	  if(psmirnov_exact_test(q, 0., j / nd, two_sided)) u[j] = 1.;
       20: 1272:	  else u[j] = u[j - 1];
        6: 1272-block  0
       20: 1272-block  1
call    0 returned 20
branch  1 taken 14
branch  2 taken 6 (fallthrough)
       14: 1273:	}
       14: 1274:	for(unsigned int i = 1; i <= m; i++) {
      14*: 1275:	  if(psmirnov_exact_test(q, i / md, 0., two_sided)) u[0] = 1.;
       14: 1275-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 14
    %%%%%: 1275-block  1
        -: 1276:	  for(int j = 1; j <= n; j++) {
        -: 1277:		   if(psmirnov_exact_test(q, i / md, j / nd, two_sided)) u[j] = 1.;
        -: 1278:		   else {
      251: 1279:		       double v = (double)(i) / (double)(i + j);
        6: 1279-block  0
      245: 1279-block  1
      251: 1279-block  2
branch  0 taken 251
branch  1 taken 0 (fallthrough)
      251: 1280:		       double w = (double)(j) / (double)(i + j);
      251: 1281:		       u[j] = v * u[j] + w * u[j - 1];
      251: 1281-block  0
call    0 returned 251
        -: 1282:		   }
      251: 1283:	  }
branch  0 taken 122 (fallthrough)
branch  1 taken 129
      122: 1284:	}
      122: 1284-block  0
        -: 1285:	double res = u[n];
      129: 1286:	Safefree(u);
      129: 1286-block  0
        -: 1287:	return res;
      251: 1288:}
      251: 1288-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 245
        6: 1288-block  1
        -: 1289:
        6: 1290:static double p_body(double n, double delta, double sd, double sig_level, int tsample, int tside, bool strict) {
        6: 1290-block  0
        -: 1291:	double nu = (n - 1.0) * (double)tsample;
        -: 1292:	if (nu < 1e-7) nu = 1e-7; 
        -: 1293:
        -: 1294:	// Ensure sig_level/tside is not truncated
        -: 1295:	double p_tail = sig_level / (double)tside;
        -: 1296:	double qu = qt_tail(nu, p_tail); // qt(p, df, lower.tail=FALSE)
        -: 1297:
        4: 1298:	double ncp = sqrt(n / (double)tsample) * (delta / sd);
        4: 1299:
        4: 1300:	if (strict && tside == 2) {
        4: 1301:	  // Use R-style tail calls: 1 - P(T < qu) + P(T < -qu)
        -: 1302:	  return (1.0 - exact_pnt(qu, nu, ncp)) + exact_pnt(-qu, nu, ncp);
        -: 1303:	} else {
        4: 1304:	  // Default: 1 - P(T < qu)
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 1304-block  0
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        4: 1304-block  1
branch  4 taken 4 (fallthrough)
branch  5 taken 0
        4: 1305:	  // Ensure exact_pnt is using a convergence tolerance of at least 1e-15
        4: 1306:	  return 1.0 - exact_pnt(qu, nu, ncp);
        4: 1306-block  0
        -: 1307:	}
        -: 1308:}
        4: 1309:
        4: 1309-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 1310:// Bisection algorithm to find the inverse F-distribution (Quantile function)
        4: 1310-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 1
        3: 1310-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1311:// Equivalent to R's qf(p, df1, df2)
        3: 1312:static double qf_bisection(double p, double df1, double df2) {
        3: 1312-block  0
        1: 1313:	if (p <= 0.0) return 0.0;
        1: 1313-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1314:	if (p >= 1.0) return INFINITY;
        1: 1315:	double low = 0.0, high = 1.0;
        1: 1315-block  0
        -: 1316:	// Find upper bound
        -: 1317:	while (pf(high, df1, df2) < p) {
        -: 1318:	  low = high;
        -: 1319:	  high *= 2.0;
        6: 1320:	  if (high > 1e100) break; /* Fallback limit */
        4: 1320-block  0
        2: 1320-block  1
        6: 1320-block  2
branch  0 taken 2
branch  1 taken 4 (fallthrough)
        2: 1321:	}
        2: 1321-block  0
call    0 returned 2
        2: 1322:
       2*: 1323:	// Bisect to find the root
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 1323-block  0
       2*: 1324:	for (unsigned short int i = 0; i < 150; i++) {
        2: 1324-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 1324-block  1
        2: 1325:		double mid = low + (high - low) / 2.0;
        2: 1325-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 1326:		double p_mid = pf(mid, df1, df2);
    %%%%%: 1326-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1326-block  1
    #####: 1327:
    %%%%%: 1327-block  0
call    0 never executed
        -: 1328:		if (p_mid < p) {
        2: 1329:			low = mid;
        2: 1329-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 1329-block  1
call    2 returned 2
    #####: 1330:		} else {
    %%%%%: 1330-block  0
call    0 never executed
        -: 1331:			high = mid;
        -: 1332:		}
        4: 1333:		if (high - low < 1e-12) break;
        4: 1333-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 1333-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        4: 1333-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 4
    #####: 1334:	}
    %%%%%: 1334-block  0
call    0 never executed
        -: 1335:	return (low + high) / 2.0;
        -: 1336:}
        4: 1337:// --- XS SECTION ---
        4: 1338:MODULE = Stats::LikeR  PACKAGE = Stats::LikeR
        4: 1339:
        -: 1340:SV* ks_test(...)
        4: 1341:CODE:
        4: 1341-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 2
        2: 1341-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 1
        1: 1341-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    #####: 1342:{
    %%%%%: 1342-block  0
call    0 never executed
        -: 1343:	SV *restrict x_sv = NULL, *restrict y_sv = NULL;
        -: 1344:	short int exact = -1;
        4: 1345:	const char *restrict alternative = "two.sided";
        4: 1346:	int arg_idx = 0;
        4: 1346-block  0
call    0 returned 4
       4*: 1347:
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 1347-block  0
call    2 never executed
        -: 1348:	// Shift arrays if provided positionally
        -: 1349:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        4: 1350:		x_sv = ST(arg_idx);
        4: 1350-block  0
call    0 returned 4
        4: 1351:		arg_idx++;
      204: 1352:	}
      200: 1352-block  0
      204: 1352-block  1
branch  0 taken 200
branch  1 taken 4 (fallthrough)
      200: 1353:	// Check if second argument is an array (2-sample) or a string representing a CDF (1-sample)
      200: 1353-block  0
call    0 returned 200
      200: 1354:	if (arg_idx < items) {
branch  0 taken 200 (fallthrough)
branch  1 taken 0
      200: 1354-block  0
branch  2 taken 200 (fallthrough)
branch  3 taken 0
      200: 1354-block  1
call    4 returned 200
branch  5 taken 200 (fallthrough)
branch  6 taken 0
      200: 1355:		if (SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
      200: 1355-block  0
call    0 returned 200
        -: 1356:			y_sv = ST(arg_idx);
        -: 1357:			arg_idx++;
        -: 1358:		} else if (SvPOK(ST(arg_idx))) {
        4: 1359:			y_sv = ST(arg_idx); // Save string (e.g., "pnorm") for 1-sample test logic
        4: 1360:			arg_idx++;
        -: 1361:		}
        -: 1362:	}
        7: 1363:
        4: 1363-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 1363-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 1
        3: 1363-block  2
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 1364:	// Parse named arguments
        3: 1365:	for (; arg_idx < items; arg_idx += 2) {
        3: 1365-block  0
call    0 returned 3
        -: 1366:	  const char *restrict key = SvPV_nolen(ST(arg_idx));
        3: 1367:	  SV *restrict val = ST(arg_idx + 1);
call    0 returned 3
        3: 1368:	  if      (strEQ(key, "x"))           x_sv = val;
       93: 1369:	  else if (strEQ(key, "y"))           y_sv = val;
       90: 1369-block  0
       93: 1369-block  1
branch  0 taken 90
branch  1 taken 3 (fallthrough)
       90: 1370:	  else if (strEQ(key, "exact"))       {
       90: 1370-block  0
call    0 returned 90
       90: 1371:		   if (!SvOK(val)) exact = -1;
branch  0 taken 90 (fallthrough)
branch  1 taken 0
       90: 1371-block  0
branch  2 taken 90 (fallthrough)
branch  3 taken 0
       90: 1371-block  1
call    4 returned 90
branch  5 taken 90 (fallthrough)
branch  6 taken 0
       90: 1372:		   else exact = SvTRUE(val) ? 1 : 0;
       90: 1372-block  0
call    0 returned 90
        -: 1373:	  }
        -: 1374:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 1375:	  else croak("ks_test: unknown argument '%s'", key);
        3: 1376:	}
        3: 1376-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1376-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 1377:
    %%%%%: 1377-block  0
call    0 never executed
call    1 never executed
    #####: 1378:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV) {
call    0 never executed
        -: 1379:	  croak("ks_test: 'x' is a required argument and must be an ARRAY reference");
        -: 1380:	}
        -: 1381:
        3: 1382:	bool is_two_sided = strEQ(alternative, "two.sided") ? 1 : 0;
        3: 1382-block  0
call    0 returned 3
        -: 1383:	bool is_greater   = strEQ(alternative, "greater") ? 1 : 0;
        -: 1384:	bool is_less      = strEQ(alternative, "less") ? 1 : 0;
        3: 1385:
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 1385-block  0
        2: 1386:	if (!is_two_sided && !is_greater && !is_less) {
        2: 1386-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 1386-block  1
        1: 1387:	  croak("ks_test: alternative must be 'two.sided', 'less', or 'greater'");
        1: 1387-block  0
        -: 1388:	}
        -: 1389:
        3: 1390:	AV *restrict x_av = (AV*)SvRV(x_sv);
       3*: 1391:	size_t nx = av_len(x_av) + 1;
        3: 1391-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1391-block  1
       3*: 1392:	if (nx == 0) croak("Not enough 'x' observations");
        3: 1392-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1392-block  1
        3: 1393:
        3: 1393-block  0
        -: 1394:	// Extract 'x' array to C-array
        -: 1395:	double *restrict x_data = (double *)safemalloc(nx * sizeof(double));
        3: 1396:	size_t valid_nx = 0;
        3: 1397:	for (size_t i = 0; i < nx; i++) {
        3: 1397-block  0
call    0 returned 3
      153: 1398:	  SV**restrict el = av_fetch(x_av, i, 0);
      150: 1398-block  0
      153: 1398-block  1
branch  0 taken 150
branch  1 taken 3 (fallthrough)
       93: 1399:	  if (el && SvOK(*el) && looks_like_number(*el)) {
        3: 1399-block  0
       90: 1399-block  1
       93: 1399-block  2
branch  0 taken 90
branch  1 taken 3 (fallthrough)
        3: 1400:		   x_data[valid_nx++] = SvNV(*el);
        3: 1400-block  0
call    0 returned 3
        -: 1401:	  }
        3: 1402:	}
      240: 1403:
      237: 1403-block  0
      240: 1403-block  1
branch  0 taken 237
branch  1 taken 3 (fallthrough)
     237*: 1404:	double statistic = 0.0, p_value = 0.0;
      237: 1404-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 237
    %%%%%: 1404-block  1
        -: 1405:	const char *restrict method_desc = "";
        3: 1406:
        3: 1406-block  0
call    0 returned 3
        3: 1407:	// --- TWO SAMPLE ---
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1407-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 1408:	if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
    %%%%%: 1408-block  0
call    0 never executed
    #####: 1409:	  AV *restrict y_av = (AV*)SvRV(y_sv);
        -: 1410:	  size_t ny = av_len(y_av) + 1;
        3: 1411:	  
        3: 1411-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1412:	  double *restrict y_data = (double *)safemalloc(ny * sizeof(double));
        3: 1413:	  size_t valid_ny = 0;
        3: 1414:	  for (size_t i = 0; i < ny; i++) {
        3: 1414-block  0
call    0 returned 3
        -: 1415:		   SV**restrict el = av_fetch(y_av, i, 0);
    #####: 1416:		   if (el && SvOK(*el) && looks_like_number(*el)) {
    #####: 1417:		       y_data[valid_ny++] = SvNV(*el);
    #####: 1418:		   }
    %%%%%: 1418-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1419:	  }
    %%%%%: 1419-block  0
call    0 never executed
        -: 1420:
    #####: 1421:	  if (valid_nx < 1 || valid_ny < 1) {
    %%%%%: 1421-block  0
        -: 1422:		   Safefree(x_data); Safefree(y_data);
        -: 1423:		   croak("Not enough non-missing observations for KS test");
        3: 1424:	  }
        3: 1424-block  0
call    0 returned 3
        -: 1425:
        -: 1426:	  double d, d_plus, d_minus;
        2: 1427:	  calc_2sample_stats(x_data, valid_nx, y_data, valid_ny, &d, &d_plus, &d_minus);
        1: 1427-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1427-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 1427-block  2
        1: 1428:
        1: 1428-block  0
call    0 returned 1
        1: 1429:	  // Map alternative to the correct statistic
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1430:	  if (is_greater) statistic = d_plus;
        1: 1430-block  0
call    0 returned 1
        1: 1431:	  else if (is_less) statistic = d_minus;
       51: 1432:	  else statistic = d;
       50: 1432-block  0
       51: 1432-block  1
branch  0 taken 50
branch  1 taken 1 (fallthrough)
       50: 1433:
       50: 1434:	  // Determine if exact or asymptotic
       50: 1435:	  bool use_exact = FALSE;
       50: 1435-block  0
call    0 returned 50
        -: 1436:	  if (exact == 1) use_exact = TRUE;
       50: 1437:	  else if (exact == 0) use_exact = FALSE;
       50: 1438:	  else use_exact = (valid_nx * valid_ny < 10000); 
        -: 1439:
      50*: 1440:	  // Check for ties in combined set
branch  0 taken 0 (fallthrough)
branch  1 taken 50
    %%%%%: 1440-block  0
       50: 1441:	  size_t total_n = valid_nx + valid_ny;
       50: 1441-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 49
        1: 1441-block  1
       50: 1442:	  double *restrict comb = (double *)safemalloc(total_n * sizeof(double));
       50: 1442-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 42
        8: 1442-block  1
      50*: 1443:	  for(size_t i=0; i<valid_nx; i++) comb[i] = x_data[i];
       50: 1443-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 50
    %%%%%: 1443-block  1
        -: 1444:	  for(size_t i=0; i<valid_ny; i++) comb[valid_nx+i] = y_data[i];
       50: 1445:	  qsort(comb, total_n, sizeof(double), compare_doubles);
       50: 1445-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 42
        8: 1445-block  1
      50*: 1446:
       50: 1446-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 50
    %%%%%: 1446-block  1
        -: 1447:	  bool has_ties = FALSE;
       1*: 1448:	  for(size_t i = 1; i < total_n; i++) {
        1: 1448-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 1448-block  1
       1*: 1449:		   if(comb[i] == comb[i-1]) { has_ties = TRUE; break; }
        1: 1449-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 1449-block  1
        1: 1450:	  }
        1: 1450-block  0
       1*: 1451:	  Safefree(comb);
        1: 1451-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1451-block  1
    %%%%%: 1451-block  2
        1: 1452:	  if (use_exact && has_ties) {
        1: 1452-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1453:		   warn("cannot compute exact p-value with ties; falling back to asymptotic");
        1: 1454:		   use_exact = FALSE;
        1: 1454-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1455:	  }
        1: 1455-block  0
call    0 returned 1
        -: 1456:	  if (use_exact) {
    #####: 1457:		   method_desc = "Two-sample Kolmogorov-Smirnov exact test";
    %%%%%: 1457-block  0
call    0 never executed
    #####: 1458:		   double q = (0.5 + floor(statistic * valid_nx * valid_ny - 1e-7)) / ((double)valid_nx * valid_ny);
    #####: 1459:		   p_value = psmirnov_exact_uniq_upper(q, valid_nx, valid_ny, is_two_sided);
        -: 1460:	  } else {
        -: 1461:		   method_desc = "Two-sample Kolmogorov-Smirnov test (asymptotic)";
    #####: 1462:		   double z = statistic * sqrt((double)(valid_nx * valid_ny) / (valid_nx + valid_ny));
    #####: 1463:		   if (is_two_sided) {
    #####: 1464:		       p_value = K2l(z, 0, 1e-9); 
    %%%%%: 1464-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1464-block  1
call    2 never executed
    #####: 1465:		   } else {
    %%%%%: 1465-block  0
        -: 1466:		       p_value = exp(-2.0 * z * z); // One-sided limit distribution
        -: 1467:		   }
    #####: 1468:	  }
    %%%%%: 1468-block  0
call    0 never executed
    #####: 1469:	  Safefree(y_data);
call    0 never executed
        -: 1470:	} else if (y_sv && SvPOK(y_sv)) {// --- ONE SAMPLE (e.g. against pnorm) ---
        -: 1471:	  const char *restrict dist = SvPV_nolen(y_sv);
    #####: 1472:	  if (strEQ(dist, "pnorm")) {
    %%%%%: 1472-block  0
call    0 never executed
    #####: 1473:		   qsort(x_data, valid_nx, sizeof(double), compare_doubles);
call    0 never executed
        -: 1474:		   double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
        4: 1475:		   for(size_t i = 0; i < valid_nx; i++) {
        4: 1475-block  0
call    0 returned 4
       4*: 1476:		       double cdf_obs_low  = (double)i / valid_nx;
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 1476-block  0
       4*: 1477:		       double cdf_obs_high = (double)(i + 1) / valid_nx;
        4: 1477-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 1477-block  1
        4: 1478:		       double cdf_theor    = approx_pnorm(x_data[i]);
        4: 1478-block  0
call    0 returned 4
        4: 1479:		       
call    0 returned 4
call    1 returned 4
        4: 1480:		       double diff1 = cdf_obs_low - cdf_theor;
call    0 returned 4
call    1 returned 4
        4: 1481:		       double diff2 = cdf_obs_high - cdf_theor;
call    0 returned 4
call    1 returned 4
        4: 1482:		       
call    0 returned 4
call    1 returned 4
        4: 1483:		       if (diff1 > max_d_plus) max_d_plus = diff1;
call    0 returned 4
        -: 1484:		       if (diff2 > max_d_plus) max_d_plus = diff2;
        -: 1485:		       if (-diff1 > max_d_minus) max_d_minus = -diff1;
        -: 1486:		       if (-diff2 > max_d_minus) max_d_minus = -diff2;
        -: 1487:		       
        -: 1488:		       if (fabs(diff1) > max_d) max_d = fabs(diff1);
        -: 1489:		       if (fabs(diff2) > max_d) max_d = fabs(diff2);
        -: 1490:		   }
       10: 1491:		   if (is_greater) statistic = max_d_plus;
       10: 1492:		   else if (is_less) statistic = max_d_minus;
       10: 1493:		   else statistic = max_d;
       10: 1494:		   bool use_exact = (exact == -1) ? (valid_nx < 100) : (exact == 1);
       10: 1495:		   if (use_exact) {
       10: 1496:		       method_desc = "One-sample Kolmogorov-Smirnov exact test";
        -: 1497:		       if (is_two_sided) {
       10: 1498:		           p_value = 1.0 - K2x(valid_nx, statistic);
branch  0 taken 10 (fallthrough)
branch  1 taken 0
       10: 1498-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 8
        2: 1498-block  1
branch  4 taken 2 (fallthrough)
branch  5 taken 0
        2: 1499:		       } else {
        2: 1500:		           warn("exact 1-sample 1-sided KS test not implemented; using asymptotic");
        2: 1500-block  0
        -: 1501:		           double z = statistic * sqrt((double)valid_nx);
        -: 1502:		           p_value = exp(-2.0 * z * z);
       10: 1503:		       }
       10: 1503-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 0
       10: 1503-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 8
        2: 1503-block  2
branch  4 taken 2 (fallthrough)
branch  5 taken 0
        2: 1504:		   } else {
        2: 1505:		       method_desc = "One-sample Kolmogorov-Smirnov test (asymptotic)";
        2: 1505-block  0
        -: 1506:		       double z = statistic * sqrt((double)valid_nx);
        -: 1507:		       if (is_two_sided) p_value = K2l(z, 0, 1e-6); 
       10: 1508:		       else p_value = exp(-2.0 * z * z);
       10: 1508-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 10
    #####: 1509:		   }
    %%%%%: 1509-block  0
call    0 never executed
        -: 1510:	  } else {
        -: 1511:		    Safefree(x_data);
       30: 1512:		    croak("ks_test: Unsupported 1-sample distribution '%s'. Use arrays for 2-sample.", dist);
       10: 1512-block  0
       20: 1512-block  1
       30: 1512-block  2
branch  0 taken 20
branch  1 taken 10 (fallthrough)
       20: 1513:	  }
       20: 1513-block  0
call    0 returned 20
       20: 1514:	} else {
       20: 1515:	  Safefree(x_data);
branch  0 taken 7 (fallthrough)
branch  1 taken 13
        7: 1515-block  0
       13: 1516:	  croak("ks_test: Invalid arguments for 'y'.");
       13: 1516-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 6
        7: 1516-block  1
        6: 1517:	}
        6: 1517-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 1517-block  1
call    2 returned 3
       3*: 1518:	Safefree(x_data);
        3: 1518-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1518-block  1
call    2 never executed
        3: 1519:	if (p_value > 1.0) p_value = 1.0;
        3: 1519-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 1519-block  1
call    2 returned 1
        2: 1520:	if (p_value < 0.0) p_value = 0.0;
        2: 1520-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 1521:	HV *restrict res = newHV();
    %%%%%: 1521-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1521-block  1
    #####: 1522:	hv_stores(res, "statistic", newSVnv(statistic));
    %%%%%: 1522-block  0
call    0 never executed
        -: 1523:	hv_stores(res, "p_value", newSVnv(p_value));
        2: 1524:	hv_stores(res, "method", newSVpv(method_desc, 0));
        2: 1524-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 1524-block  1
call    2 returned 2
    #####: 1525:	hv_stores(res, "alternative", newSVpv(alternative, 0));
    %%%%%: 1525-block  0
call    0 never executed
        -: 1526:	RETVAL = newRV_noinc((SV*)res);
        -: 1527:}
       10: 1528:OUTPUT:
       10: 1528-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 1
        9: 1528-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 1528-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 9
        1: 1529:    RETVAL
        1: 1529-block  0
call    0 returned 0
        9: 1530:
        9: 1531:SV* wilcox_test(...)
        9: 1531-block  0
call    0 returned 9
       9*: 1532:CODE:
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1532-block  0
call    2 never executed
        -: 1533:{
        9: 1534:	SV *restrict x_sv = NULL, *restrict y_sv = NULL;
        9: 1535:	bool paired = FALSE, correct = TRUE;
        9: 1536:	double mu = 0.0;
        9: 1536-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 1
        8: 1536-block  1
branch  2 taken 8 (fallthrough)
branch  3 taken 0
        8: 1536-block  2
branch  4 taken 8 (fallthrough)
branch  5 taken 0
        8: 1537:	short int exact = -1;
        8: 1538:	const char *restrict alternative = "two.sided";
        8: 1538-block  0
call    0 returned 8
        -: 1539:	int arg_idx = 0;
        9: 1540:	// 1. Shift first positional argument as 'x' if it's an array reference
        9: 1541:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        9: 1542:		x_sv = ST(arg_idx);
        -: 1543:		arg_idx++;
       14: 1544:	}
        9: 1544-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 1
        8: 1544-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 3
        5: 1545:	// 2. Shift second positional argument as 'y' if it's an array reference
        5: 1545-block  0
call    0 returned 5
        5: 1546:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
       33: 1547:		y_sv = ST(arg_idx);
       28: 1547-block  0
       33: 1547-block  1
branch  0 taken 28
branch  1 taken 5 (fallthrough)
       28: 1548:		arg_idx++;
       28: 1548-block  0
call    0 returned 28
       28: 1549:	}
branch  0 taken 28 (fallthrough)
branch  1 taken 0
       28: 1549-block  0
branch  2 taken 28 (fallthrough)
branch  3 taken 0
       28: 1549-block  1
call    4 returned 28
branch  5 taken 28 (fallthrough)
branch  6 taken 0
       28: 1550:	// Ensure the remaining arguments form complete key-value pairs
       28: 1550-block  0
call    0 returned 28
       28: 1551:	if ((items - arg_idx) % 2 != 0) {
       28: 1552:		croak("Usage: wilcox_test(\\@x, [\\@y], key => value, ...)");
        -: 1553:	}
        -: 1554:	// --- Parse named arguments from the remaining flat stack ---
       33: 1555:	for (; arg_idx < items; arg_idx += 2) {
        5: 1555-block  0
       28: 1555-block  1
       33: 1555-block  2
branch  0 taken 28
branch  1 taken 5 (fallthrough)
       28: 1556:		const char *restrict key = SvPV_nolen(ST(arg_idx));
       28: 1556-block  0
call    0 returned 28
       28: 1557:		SV *restrict val = ST(arg_idx + 1);
branch  0 taken 28 (fallthrough)
branch  1 taken 0
       28: 1557-block  0
branch  2 taken 28 (fallthrough)
branch  3 taken 0
       28: 1557-block  1
call    4 returned 28
branch  5 taken 28 (fallthrough)
branch  6 taken 0
       28: 1558:		if      (strEQ(key, "x"))           x_sv = val;
       28: 1558-block  0
call    0 returned 28
       28: 1559:		else if (strEQ(key, "y"))           y_sv = val;
       28: 1560:		else if (strEQ(key, "paired"))      paired = SvTRUE(val);
        -: 1561:		else if (strEQ(key, "correct"))     correct = SvTRUE(val);
        -: 1562:		else if (strEQ(key, "mu"))          mu = SvNV(val);
       5*: 1563:		else if (strEQ(key, "exact"))       {
        5: 1563-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 1563-block  1
call    2 never executed
call    3 never executed
       5*: 1564:			if (!SvOK(val)) exact = -1;
        5: 1564-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 1564-block  1
call    2 never executed
call    3 never executed
        5: 1565:			else exact = SvTRUE(val) ? 1 : 0;
        5: 1566:		}
        5: 1567:		else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        5: 1567-block  0
call    0 returned 5
        5: 1568:		else croak("wilcox_test: unknown argument '%s'", key);
       61: 1569:	}
       56: 1569-block  0
branch  0 taken 28 (fallthrough)
branch  1 taken 28
       28: 1569-block  1
       56: 1569-block  2
       61: 1569-block  3
branch  2 taken 56
branch  3 taken 5 (fallthrough)
        5: 1570:	// --- Validate required / types ---
        -: 1571:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
       5*: 1572:		croak("wilcox_test: 'x' is a required argument and must be an ARRAY reference");
        5: 1572-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 1572-block  1
       5*: 1573:	AV *restrict x_av = (AV*)SvRV(x_sv);
        5: 1573-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 1573-block  1
        5: 1574:	size_t nx = av_len(x_av) + 1;
        5: 1574-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 1574-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 1574-block  2
branch  4 taken 2 (fallthrough)
branch  5 taken 3
        2: 1574-block  3
        3: 1574-block  4
        5: 1574-block  5
        -: 1575:	if (nx == 0) croak("Not enough 'x' observations");
        5: 1576:
        5: 1576-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 1576-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    #####: 1577:	AV *restrict y_av = NULL;
    %%%%%: 1577-block  0
call    0 never executed
    #####: 1578:	size_t ny = 0;
        -: 1579:	if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
        5: 1580:		y_av = (AV*)SvRV(y_sv);
        5: 1580-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 1581:		ny = av_len(y_av) + 1;
        2: 1582:	}
        2: 1582-block  0
call    0 returned 2
        2: 1583:	double p_value = 0.0, statistic = 0.0;
call    0 returned 2
        -: 1584:	const char *restrict method_desc = "";
        2: 1585:	bool use_exact = FALSE;
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 1585-block  0
        1: 1586:	// --- TWO SAMPLE (Mann-Whitney) ---
        1: 1586-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1586-block  1
        -: 1587:	if (ny > 0 && !paired) {
    #####: 1588:		RankInfo *restrict ri = (RankInfo *)safemalloc((nx + ny) * sizeof(RankInfo));
    %%%%%: 1588-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1588-block  1
    %%%%%: 1588-block  2
    #####: 1589:		size_t valid_nx = 0, valid_ny = 0;
    %%%%%: 1589-block  0
        -: 1590:		for (size_t i = 0; i < nx; i++) {
        -: 1591:			SV**restrict el = av_fetch(x_av, i, 0);
       3*: 1592:			if (el && SvOK(*el) && looks_like_number(*el)) {
        3: 1592-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1592-block  1
    %%%%%: 1592-block  2
        3: 1593:				ri[valid_nx].val = SvNV(*el) - mu; // R subtracts mu from x
        3: 1594:				ri[valid_nx].idx = 1;
        3: 1595:				valid_nx++;
        -: 1596:			}
        3: 1597:		}
        3: 1598:		for (size_t i = 0; i < ny; i++) {
        3: 1598-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1599:			SV**restrict el = av_fetch(y_av, i, 0);
        3: 1599-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1599-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 1
        2: 1599-block  2
        1: 1599-block  3
        3: 1599-block  4
    #####: 1600:			if (el && SvOK(*el) && looks_like_number(*el)) {
    %%%%%: 1600-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1600-block  1
    #####: 1601:				ri[valid_nx + valid_ny].val = SvNV(*el);
    %%%%%: 1601-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1601-block  1
        -: 1602:				ri[valid_nx + valid_ny].idx = 2;
        3: 1603:				valid_ny++;
        -: 1604:			}
       3*: 1605:		}
        3: 1605-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1605-block  1
call    2 never executed
       3*: 1606:		if (valid_nx == 0) { Safefree(ri); croak("not enough (non-missing) 'x' observations"); }
        3: 1606-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1606-block  1
call    2 never executed
        3: 1607:		if (valid_ny == 0) { Safefree(ri); croak("not enough 'y' observations"); }
        3: 1607-block  0
call    0 returned 3
        -: 1608:		size_t total_n = valid_nx + valid_ny;
        5: 1609:		bool has_ties = 0;
        5: 1609-block  0
call    0 returned 5
        -: 1610:		double tie_adj = rank_and_count_ties(ri, total_n, &has_ties);
        4: 1611:		double w_rank_sum = 0.0;
        4: 1611-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 1
        3: 1611-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1611-block  2
branch  4 taken 1 (fallthrough)
branch  5 taken 2
        1: 1611-block  3
call    6 returned 0
        3: 1612:		for (size_t i = 0; i < total_n; i++) if (ri[i].idx == 1) w_rank_sum += ri[i].rank;
        3: 1612-block  0
call    0 returned 3
        3: 1613:		statistic = w_rank_sum - (double)valid_nx * (valid_nx + 1.0) / 2.0;
        3: 1614:		
       26: 1615:		if (exact == 1) use_exact = TRUE;
       23: 1615-block  0
       26: 1615-block  1
branch  0 taken 23
branch  1 taken 3 (fallthrough)
       23: 1616:		else if (exact == 0) use_exact = FALSE;
       23: 1616-block  0
call    0 returned 23
      23*: 1617:		else use_exact = (valid_nx < 50 && valid_ny < 50 && !has_ties);
branch  0 taken 23 (fallthrough)
branch  1 taken 0
       23: 1617-block  0
branch  2 taken 23 (fallthrough)
branch  3 taken 0
       23: 1617-block  1
call    4 returned 23
branch  5 taken 0 (fallthrough)
branch  6 taken 23
    %%%%%: 1617-block  2
       23: 1618:		
       23: 1618-block  0
call    0 returned 23
        -: 1619:		if (use_exact && has_ties) {
       23: 1620:			warn("cannot compute exact p-value with ties; falling back to approximation");
branch  0 taken 18 (fallthrough)
branch  1 taken 5
       18: 1621:			use_exact = FALSE;
       18: 1621-block  0
call    0 returned 18
      18*: 1622:		}
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 1622-block  0
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 1622-block  1
call    4 returned 18
branch  5 taken 0 (fallthrough)
branch  6 taken 18
    %%%%%: 1622-block  2
       18: 1623:		if (use_exact) {
       18: 1623-block  0
call    0 returned 18
       18: 1624:			method_desc = "Wilcoxon rank sum exact test";
      18*: 1625:			double p_less = exact_pwilcox(statistic, valid_nx, valid_ny);
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 1625-block  0
       18: 1626:			double p_greater = 1.0 - exact_pwilcox(statistic - 1.0, valid_nx, valid_ny);
       18: 1626-block  0
        -: 1627:
        5: 1628:			if (strcmp(alternative, "less") == 0) p_value = p_less;
       5*: 1629:			else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
        5: 1629-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 1629-block  1
        5: 1630:			else {
        5: 1630-block  0
        -: 1631:				double p = (p_less < p_greater) ? p_less : p_greater;
        -: 1632:				p_value = 2.0 * p;
        3: 1633:			}
        3: 1633-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 1634:		} else {
    %%%%%: 1634-block  0
call    0 never executed
    #####: 1635:			method_desc = correct ? "Wilcoxon rank sum test with continuity correction" : "Wilcoxon rank sum test";
call    0 never executed
        -: 1636:			double exp = (double)valid_nx * valid_ny / 2.0;
        3: 1637:			double var = ((double)valid_nx * valid_ny / 12.0) * ((total_n + 1.0) - tie_adj / (total_n * (total_n - 1.0)));
        3: 1637-block  0
call    0 returned 3
       26: 1638:			double z = statistic - exp;
       26: 1638-block  0
branch  0 taken 23
branch  1 taken 3 (fallthrough)
       23: 1639:			
       23: 1640:			double CORRECTION = 0.0;
       23: 1640-block  0
        -: 1641:			if (correct) {
        3: 1642:				if (strcmp(alternative, "two.sided") == 0) CORRECTION = (z > 0 ? 0.5 : -0.5);
        3: 1643:				else if (strcmp(alternative, "greater") == 0) CORRECTION = 0.5;
        3: 1643-block  0
call    0 returned 3
        3: 1644:				else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
       26: 1645:			}
       23: 1645-block  0
       26: 1645-block  1
branch  0 taken 23
branch  1 taken 3 (fallthrough)
       23: 1646:			z = (z - CORRECTION) / sqrt(var);
       23: 1646-block  0
branch  0 taken 19 (fallthrough)
branch  1 taken 4
       19: 1646-block  1
        -: 1647:
       3*: 1648:			if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
        3: 1648-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1648-block  1
       3*: 1649:			else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
        3: 1649-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1649-block  1
       3*: 1650:			else p_value = 2.0 * approx_pnorm(-fabs(z));
        3: 1650-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1650-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1650-block  2
    %%%%%: 1650-block  3
        3: 1650-block  4
        3: 1651:		}
        3: 1651-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1651-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 1652:		Safefree(ri);
    %%%%%: 1652-block  0
call    0 never executed
    #####: 1653:	} else { // --- ONE SAMPLE / PAIRED ---
        -: 1654:		if (paired && (!y_av || nx != ny)) croak("'x' and 'y' must have the same length for paired test");
        3: 1655:		double *restrict diffs = (double *)safemalloc(nx * sizeof(double));
        3: 1655-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1655-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 1656:		size_t n_nz = 0;
    %%%%%: 1656-block  0
call    0 never executed
    #####: 1657:		bool has_zeroes = FALSE;
        -: 1658:		for (size_t i = 0; i < nx; i++) {
        3: 1659:			SV**restrict x_el = av_fetch(x_av, i, 0);
        3: 1659-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1660:			if (!x_el || !SvOK(*x_el) || !looks_like_number(*x_el)) continue;
        3: 1661:			double dx = SvNV(*x_el);
        3: 1661-block  0
call    0 returned 3
        3: 1662:
call    0 returned 3
        -: 1663:			if (paired) {
       3*: 1664:				SV**restrict y_el = av_fetch(y_av, i, 0);
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1664-block  0
       3*: 1665:				if (!y_el || !SvOK(*y_el) || !looks_like_number(*y_el)) continue;
        3: 1665-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1665-block  1
        -: 1666:				double dy = SvNV(*y_el);
       3*: 1667:				double d = dx - dy - mu;
        3: 1667-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1667-block  1
        3: 1667-block  2
        3: 1668:				if (d == 0.0) has_zeroes = TRUE; // Drop exact zeroes
        3: 1668-block  0
        -: 1669:				else diffs[n_nz++] = d;
        -: 1670:			} else {
    #####: 1671:				double d = dx - mu;
    %%%%%: 1671-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1671-block  1
    %%%%%: 1671-block  2
    #####: 1672:				if (d == 0.0) has_zeroes = TRUE;
    #####: 1673:				else diffs[n_nz++] = d;
    #####: 1674:			}
    #####: 1675:		}
    #####: 1676:		if (n_nz == 0) {
    %%%%%: 1676-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1677:			Safefree(diffs);
    %%%%%: 1677-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1677-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 1677-block  2
    %%%%%: 1677-block  3
    %%%%%: 1677-block  4
    #####: 1678:			croak("not enough (non-missing) observations");
    %%%%%: 1678-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1678-block  1
    #####: 1679:		}
    %%%%%: 1679-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1679-block  1
        -: 1680:		RankInfo *ri = (RankInfo *)safemalloc(n_nz * sizeof(RankInfo));
    #####: 1681:		for (size_t i = 0; i < n_nz; i++) { 
        -: 1682:			ri[i].val = fabs(diffs[i]); 
    #####: 1683:			ri[i].idx = (diffs[i] > 0);
    %%%%%: 1683-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1683-block  1
call    2 never executed
    #####: 1684:		}
    %%%%%: 1684-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1684-block  1
call    2 never executed
    #####: 1685:		bool has_ties = 0;
    %%%%%: 1685-block  0
call    0 never executed
        -: 1686:		double tie_adj = rank_and_count_ties(ri, n_nz, &has_ties);
        3: 1687:		statistic = 0.0;
        3: 1687-block  0
call    0 returned 3
call    1 returned 3
        -: 1688:		for (size_t i = 0; i < n_nz; i++) {
       8*: 1689:			if (ri[i].idx) statistic += ri[i].rank;
        8: 1689-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    %%%%%: 1689-block  1
        8: 1690:		}
        8: 1690-block  0
call    0 returned 8
        8: 1691:		if (exact == 1) use_exact = TRUE;
call    0 returned 8
call    1 returned 8
        8: 1692:		else if (exact == 0) use_exact = FALSE;
call    0 returned 8
call    1 returned 8
        8: 1693:		else use_exact = (n_nz < 50 && !has_ties);
call    0 returned 8
call    1 returned 8
        8: 1694:		if (use_exact && has_ties) {
call    0 returned 8
call    1 returned 8
        8: 1695:			warn("cannot compute exact p-value with ties; falling back to approximation");
call    0 returned 8
        -: 1696:			use_exact = FALSE;
        -: 1697:		}
        -: 1698:		if (use_exact && has_zeroes) {
        -: 1699:			warn("cannot compute exact p-value with zeroes; falling back to approximation");
        -: 1700:			use_exact = FALSE;
        -: 1701:		}
        -: 1702:		if (use_exact) {
        -: 1703:			method_desc = paired ? "Wilcoxon exact signed rank test" : "Wilcoxon exact signed rank test";
        3: 1704:			double p_less = exact_psignrank(statistic, n_nz);
       3*: 1705:			double p_greater = 1.0 - exact_psignrank(statistic - 1.0, n_nz);
        3: 1705-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1705-block  1
call    2 never executed
        3: 1705-block  2
        3: 1706:
        3: 1707:			if (strcmp(alternative, "less") == 0) p_value = p_less;
        3: 1707-block  0
call    0 returned 3
        3: 1708:			else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1708-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 1
        2: 1708-block  1
branch  4 taken 2 (fallthrough)
branch  5 taken 0
        2: 1709:			else {
        2: 1710:				double p = (p_less < p_greater) ? p_less : p_greater;
       2*: 1711:				p_value = 2.0 * p;
        2: 1711-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 1711-block  1
call    2 never executed
        2: 1711-block  2
        2: 1711-block  3
        -: 1712:			}
        1: 1713:		} else {
        1: 1714:			method_desc = correct ? "Wilcoxon signed rank test with continuity correction" : "Wilcoxon signed rank test";
        1: 1714-block  0
        -: 1715:			double exp = (double)n_nz * (n_nz + 1.0) / 4.0;
        -: 1716:			double var = (n_nz * (n_nz + 1.0) * (2.0 * n_nz + 1.0) / 24.0) - (tie_adj / 48.0);
        3: 1717:			double z = statistic - exp;
        3: 1718:			double CORRECTION = 0.0;
        3: 1719:			if (correct) {
        3: 1719-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 1719-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 1719-block  2
branch  4 taken 1 (fallthrough)
branch  5 taken 1
        1: 1719-block  3
        2: 1719-block  4
        -: 1720:				if (strcmp(alternative, "two.sided") == 0) CORRECTION = (z > 0 ? 0.5 : -0.5);
        3: 1721:				else if (strcmp(alternative, "greater") == 0) CORRECTION = 0.5;
        3: 1721-block  0
call    0 returned 3
        3: 1722:				else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 1723:			}
        2: 1723-block  0
call    0 returned 2
        2: 1724:			z = (z - CORRECTION) / sqrt(var);
call    0 returned 2
        6: 1725:
        4: 1725-block  0
        6: 1725-block  1
branch  0 taken 4
branch  1 taken 2 (fallthrough)
        7: 1726:			if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
        2: 1726-block  0
        5: 1726-block  1
        7: 1726-block  2
branch  0 taken 5
branch  1 taken 2 (fallthrough)
        6: 1727:			else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
        2: 1727-block  0
        4: 1727-block  1
        6: 1727-block  2
branch  0 taken 4
branch  1 taken 2 (fallthrough)
        4: 1728:			else p_value = 2.0 * approx_pnorm(-fabs(z));
        4: 1728-block  0
call    0 returned 4
        4: 1729:		}
       14: 1730:		Safefree(ri); Safefree(diffs);
       14: 1730-block  0
branch  0 taken 10
branch  1 taken 4 (fallthrough)
       10: 1731:	}
       10: 1731-block  0
call    0 returned 10
       10: 1732:	if (p_value > 1.0) p_value = 1.0;
call    0 returned 10
       10: 1733:	HV *restrict res = newHV();
       10: 1734:	hv_stores(res, "statistic", newSVnv(statistic));
       10: 1735:	hv_stores(res, "p_value", newSVnv(p_value));
        -: 1736:	hv_stores(res, "method", newSVpv(method_desc, 0));
        -: 1737:	hv_stores(res, "alternative", newSVpv(alternative, 0));
        6: 1738:	RETVAL = newRV_noinc((SV*)res);
        2: 1738-block  0
        6: 1738-block  1
branch  0 taken 4
branch  1 taken 2 (fallthrough)
        4: 1739:}
        4: 1739-block  0
call    0 returned 4
        4: 1740:OUTPUT:
call    0 returned 4
        4: 1741:	RETVAL
       14: 1742:
       10: 1742-block  0
       14: 1742-block  1
branch  0 taken 10
branch  1 taken 4 (fallthrough)
       10: 1743:SV* _chisq_c(data_ref)
       10: 1744:    SV* data_ref;
       10: 1744-block  0
call    0 returned 10
       10: 1745:CODE:
call    0 returned 10
       10: 1746:{
call    0 returned 10
call    1 returned 10
       10: 1747:	AV*restrict obs_av = (AV*)SvRV(data_ref);
branch  0 taken 4 (fallthrough)
branch  1 taken 6
        -: 1748:	int r = av_top_index(obs_av) + 1, c = 0;
        4: 1749:	bool is_2d = 0;
       4*: 1750:	SV**restrict first_elem = av_fetch(obs_av, 0, 0);
        4: 1750-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 1750-block  1
    %%%%%: 1750-block  2
        4: 1751:	if (first_elem && SvROK(*first_elem) && SvTYPE(SvRV(*first_elem)) == SVt_PVAV) {
        4: 1752:		is_2d = 1;
        4: 1752-block  0
        -: 1753:		AV*restrict first_row = (AV*)SvRV(*first_elem);
        6: 1754:		c = av_top_index(first_row) + 1;
        6: 1754-block  0
        -: 1755:	} else {
        -: 1756:		c = r;
        4: 1757:		r = 1;
        4: 1757-block  0
call    0 returned 4
call    1 returned 4
        -: 1758:	}
        2: 1759:
        2: 1759-block  0
call    0 returned 2
call    1 returned 2
        2: 1760:	double stat = 0.0, grand_total = 0.0;
        -: 1761:	int df = 0;
        4: 1762:	int yates = (is_2d && r == 2 && c == 2) ? 1 : 0;
        1: 1762-block  0
        4: 1762-block  1
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3: 1763:
        3: 1763-block  0
call    0 returned 3
        3: 1764:	AV*restrict expected_av = newAV();
call    0 returned 3
        -: 1765:	if (is_2d) {
        1: 1766:		double *restrict row_sum = (double*)safemalloc(r * sizeof(double));
        4: 1767:		double *restrict col_sum = (double*)safemalloc(c * sizeof(double));
        1: 1767-block  0
        4: 1767-block  1
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3: 1768:		for(unsigned int i=0; i<r; i++) row_sum[i] = 0.0;
        3: 1768-block  0
call    0 returned 3
        3: 1769:		for(unsigned int j=0; j<c; j++) col_sum[j] = 0.0;
call    0 returned 3
        3: 1770:		for (unsigned int i = 0; i < r; i++) {
call    0 returned 3
call    1 returned 3
        3: 1771:			SV**restrict row_sv = av_fetch(obs_av, i, 0);
        -: 1772:			AV*restrict row = (AV*)SvRV(*row_sv);
        1: 1773:			for (unsigned int j = 0; j < c; j++) {
        1: 1773-block  0
        -: 1774:				 SV**restrict val_sv = av_fetch(row, j, 0);
        3: 1775:				 double val = SvNV(*val_sv);
        3: 1775-block  0
call    0 returned 3
        3: 1776:				 row_sum[i] += val;
call    0 returned 3
        3: 1777:				 col_sum[j] += val;
call    0 returned 3
call    1 returned 3
        3: 1778:				 grand_total += val;
call    0 returned 3
call    1 returned 3
        3: 1779:			}
call    0 returned 3
call    1 returned 3
        3: 1780:		}
call    0 returned 3
call    1 returned 3
        3: 1781:		for (unsigned int i = 0; i < r; i++) {
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 1782:			AV*restrict exp_row = newAV();
        2: 1782-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 1783:			SV**restrict row_sv = av_fetch(obs_av, i, 0);
        1: 1783-block  0
call    0 returned 1
call    1 returned 1
        -: 1784:			AV*restrict row = (AV*)SvRV(*row_sv);
        1: 1785:			for (unsigned int j = 0; j < c; j++) {
        1: 1785-block  0
call    0 returned 1
call    1 returned 1
        -: 1786:				double E = (row_sum[i] * col_sum[j]) / grand_total;
        -: 1787:				SV**restrict val_sv = av_fetch(row, j, 0);
        1: 1788:				double O = SvNV(*val_sv);
        1: 1788-block  0
call    0 returned 1
call    1 returned 1
        -: 1789:				av_push(exp_row, newSVnv(E));
        3: 1790:				if (yates) {
        3: 1790-block  0
call    0 returned 3
        -: 1791:				  // Exact R logic: min(0.5, abs(O - E))
        -: 1792:				  double abs_diff = fabs(O - E);
        -: 1793:				  double y_corr = (abs_diff > 0.5) ? 0.5 : abs_diff;
        -: 1794:				  double diff = abs_diff - y_corr;
        -: 1795:				  stat += (diff * diff) / E;
        -: 1796:				} else {
        -: 1797:				  stat += ((O - E) * (O - E)) / E;
        -: 1798:				}
        -: 1799:			}
       13: 1800:			av_push(expected_av, newRV_noinc((SV*)exp_row));
       13: 1801:		}
       13: 1802:		safefree(row_sum); safefree(col_sum);
        -: 1803:		df = (r - 1) * (c - 1);
        -: 1804:	} else {
       13: 1805:	  for (unsigned int j = 0; j < c; j++) {
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 1805-block  0
branch  2 taken 13 (fallthrough)
branch  3 taken 0
       13: 1806:		   SV**restrict val_sv = av_fetch(obs_av, j, 0);
       13: 1807:		   grand_total += SvNV(*val_sv);
       13: 1807-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 10
        3: 1807-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
       13: 1808:	  }
       13: 1809:	  double E = grand_total / (double)c;
       13: 1809-block  0
        -: 1810:	  for (unsigned int j = 0; j < c; j++) {
        -: 1811:		   SV**restrict val_sv = av_fetch(obs_av, j, 0);
       13: 1812:		   double O = SvNV(*val_sv);
       13: 1812-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 1813:		   av_push(expected_av, newSVnv(E));
       13: 1814:		   stat += ((O - E) * (O - E)) / E;
       13: 1814-block  0
        -: 1815:	  }
        -: 1816:	  df = c - 1;
       13: 1817:	}
       13: 1818:	double p_val = get_p_value(stat, df);
       13: 1818-block  0
call    0 returned 13
call    1 returned 13
       13: 1819:	HV*restrict results = newHV();
        -: 1820:	hv_store(results, "statistic", 9, newSVnv(stat), 0);
        -: 1821:	hv_store(results, "df", 2, newSViv(df), 0);
       37: 1822:	hv_store(results, "p_value", 7, newSVnv(p_val), 0);
       24: 1822-block  0
       37: 1822-block  1
branch  0 taken 24
branch  1 taken 13 (fallthrough)
      24*: 1823:	hv_store(results, "expected", 8, newRV_noinc((SV*)expected_av), 0);
       24: 1823-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24
    %%%%%: 1823-block  1
call    2 never executed
       24: 1824:	if (is_2d) {
       24: 1824-block  0
call    0 returned 24
       24: 1825:		if (yates) {
      24*: 1826:			hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test with Yates' continuity correction", 0), 0);
branch  0 taken 0 (fallthrough)
branch  1 taken 24
    %%%%%: 1826-block  0
       24: 1827:		} else {
       24: 1827-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 19
        5: 1827-block  1
      19*: 1828:			hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test", 0), 0);
       19: 1828-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 19
    %%%%%: 1828-block  1
       19: 1829:		}
       19: 1829-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 11
        8: 1829-block  1
       11: 1830:	} else {
       11: 1830-block  0
branch  0 taken 11 (fallthrough)
branch  1 taken 0
       11: 1830-block  1
call    2 returned 11
    #####: 1831:	  hv_store(results, "method", 6, newSVpv("Chi-squared test for given probabilities", 0), 0);
    %%%%%: 1831-block  0
call    0 never executed
        -: 1832:	}
        -: 1833:	RETVAL = newRV_noinc((SV*)results);
       13: 1834:}
       13: 1834-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 1834-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 13
    #####: 1835:OUTPUT:
    %%%%%: 1835-block  0
call    0 never executed
        -: 1836:	RETVAL
       13: 1837:
       13: 1838:PROTOTYPES: ENABLE
       13: 1838-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 10
        3: 1838-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 1839:
    %%%%%: 1839-block  0
call    0 never executed
        -: 1840:void write_table(...)
        -: 1841:PPCODE:
      13*: 1842:{
       13: 1842-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 1842-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 13
    %%%%%: 1842-block  2
call    4 never executed
       13: 1843:	SV *restrict data_sv = NULL;
       13: 1843-block  0
call    0 returned 13
        -: 1844:	SV *restrict file_sv = NULL;
       13: 1845:	unsigned int arg_idx = 0;
branch  0 taken 5 (fallthrough)
branch  1 taken 8
        5: 1845-block  0
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 1846:
        5: 1846-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4: 1846-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 4
        1: 1847:	// Mimic the Perl shift logic
        1: 1847-block  0
call    0 returned 0
        -: 1848:	if (arg_idx < items && SvROK(ST(arg_idx))) {
        -: 1849:	int type = SvTYPE(SvRV(ST(arg_idx)));
        -: 1850:	if (type == SVt_PVHV || type == SVt_PVAV) {
       12: 1851:		  data_sv = ST(arg_idx);
       12: 1852:		  arg_idx++;
        -: 1853:	}
        -: 1854:	}
       12: 1855:	if (arg_idx < items) {
       12: 1855-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9: 1856:	file_sv = ST(arg_idx);
       9*: 1857:	arg_idx++;
        9: 1857-block  0
call    0 returned 9
branch  1 taken 0 (fallthrough)
branch  2 taken 9
    %%%%%: 1857-block  1
        -: 1858:	}
        9: 1859:
        9: 1859-block  0
call    0 returned 9
        9: 1860:	const char *restrict sep = ",";
call    0 returned 9
        9: 1861:	const char *restrict undef_val = "NA";
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1861-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 1862:	SV *restrict row_names_sv = sv_2mortal(newSViv(1));
    %%%%%: 1862-block  0
call    0 never executed
        -: 1863:	SV *restrict col_names_sv = NULL;
        9: 1864:
        9: 1865:	// Read the remaining Hash-style arguments
        9: 1865-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 4
        5: 1865-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 5
    #####: 1866:	for (; arg_idx < items; arg_idx += 2) {
    %%%%%: 1866-block  0
call    0 never executed
        -: 1867:	if (arg_idx + 1 >= items) croak("write_table: Odd number of arguments passed");
        9: 1868:	const char *restrict key = SvPV_nolen(ST(arg_idx));
        9: 1869:	SV *restrict val = ST(arg_idx + 1);
        9: 1870:	if (strEQ(key, "data")) data_sv = val;
        9: 1870-block  0
call    0 returned 9
       32: 1871:	else if (strEQ(key, "col.names")) col_names_sv = val;
       32: 1871-block  0
call    0 returned 32
branch  1 taken 23
branch  2 taken 9 (fallthrough)
       23: 1872:	else if (strEQ(key, "file")) file_sv = val;
       23: 1872-block  0
call    0 returned 23
       23: 1873:	else if (strEQ(key, "row.names")) row_names_sv = val;
branch  0 taken 23 (fallthrough)
branch  1 taken 0
       23: 1873-block  0
branch  2 taken 23 (fallthrough)
branch  3 taken 0
       23: 1873-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 23
    #####: 1874:	else if (strEQ(key, "sep")) sep = SvPV_nolen(val);
    %%%%%: 1874-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1874-block  1
    %%%%%: 1874-block  2
    %%%%%: 1874-block  3
call    2 never executed
        -: 1875:	else if (strEQ(key, "undef.val")) undef_val = SvPV_nolen(val);
        -: 1876:	else croak("write_table: Unknown arguments passed: %s", key);
        9: 1877:	}
        9: 1877-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 5
        4: 1878:
        4: 1878-block  0
call    0 returned 4
        4: 1879:	if (!data_sv || !SvROK(data_sv)) {
call    0 returned 4
       12: 1880:	croak("write_table: 'data' must be a HASH or ARRAY reference\n");
       12: 1880-block  0
call    0 returned 12
branch  1 taken 8
branch  2 taken 4
        8: 1881:	}
        8: 1881-block  0
call    0 returned 8
call    1 returned 8
call    2 returned 8
        -: 1882:	SV *restrict data_ref = SvRV(data_sv);
        -: 1883:	if (SvTYPE(data_ref) != SVt_PVHV && SvTYPE(data_ref) != SVt_PVAV) {
        -: 1884:	croak("write_table: 'data' must be a HASH or ARRAY reference\n");
        3: 1885:	}
       3*: 1886:
        3: 1886-block  0
call    0 returned 3
branch  1 taken 0 (fallthrough)
branch  2 taken 3
    %%%%%: 1886-block  1
        3: 1887:	if (!file_sv || !SvOK(file_sv)) croak("write_table: file name missing\n");
        3: 1887-block  0
call    0 returned 3
        3: 1888:	const char *restrict file = SvPV_nolen(file_sv);
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1888-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1888-block  1
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 1888-block  2
branch  6 taken 0 (fallthrough)
branch  7 taken 3
    #####: 1889:
    %%%%%: 1889-block  0
call    0 never executed
        -: 1890:	if (col_names_sv && SvOK(col_names_sv)) {
        -: 1891:	if (!SvROK(col_names_sv) || SvTYPE(SvRV(col_names_sv)) != SVt_PVAV) {
       10: 1892:		  croak("write_table: 'col.names' must be an ARRAY reference\n");
        3: 1892-block  0
        7: 1892-block  1
       10: 1892-block  2
call    0 returned 10
branch  1 taken 7
branch  2 taken 3 (fallthrough)
        7: 1893:	}
        7: 1893-block  0
call    0 returned 7
        7: 1894:	}
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 1894-block  0
branch  2 taken 7 (fallthrough)
branch  3 taken 0
        7: 1894-block  1
branch  4 taken 7 (fallthrough)
branch  5 taken 0
        7: 1894-block  2
branch  6 taken 0 (fallthrough)
branch  7 taken 7
    #####: 1895:
    %%%%%: 1895-block  0
call    0 never executed
        -: 1896:	bool is_hoh = 0, is_hoa = 0, is_aoh = 0;
        -: 1897:	AV *restrict rows_av = NULL;
        3: 1898:
        3: 1898-block  0
        -: 1899:	// Validate Input Structures & Homogeneity 
        -: 1900:	if (SvTYPE(data_ref) == SVt_PVHV) {
       12: 1901:	  HV *restrict hv = (HV*)data_ref;
       12: 1901-block  0
call    0 returned 12
      12*: 1902:	  if (hv_iterinit(hv) == 0) XSRETURN_EMPTY;
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 1902-block  0
call    2 never executed
        -: 1903:
       12: 1904:	  HE *restrict entry = hv_iternext(hv);
       12: 1904-block  0
call    0 returned 12
       12: 1905:	  SV *restrict first_val = hv_iterval(hv, entry);
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1905-block  0
call    2 returned 12
branch  3 taken 9 (fallthrough)
branch  4 taken 3
        9: 1905-block  1
        3: 1905-block  2
       12: 1906:	  if (!first_val || !SvROK(first_val)) {
        -: 1907:		   croak("write_table: Data values must be either all HASHes or all ARRAYs\n");
        -: 1908:	  }
       12: 1909:	  int first_type = SvTYPE(SvRV(first_val));
       12: 1909-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 8
        5: 1910:	  if (first_type != SVt_PVHV && first_type != SVt_PVAV) {
        4: 1910-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1: 1910-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 1910-block  2
        1: 1911:		   croak("write_table: Data values must be either all HASHes or all ARRAYs\n");
        4: 1912:	  }
        1: 1912-block  0
        3: 1912-block  1
        4: 1912-block  2
call    0 returned 4
branch  1 taken 3
branch  2 taken 1 (fallthrough)
        3: 1913:	  is_hoh = (first_type == SVt_PVHV);
        3: 1913-block  0
call    0 returned 3
        3: 1914:	  is_hoa = (first_type == SVt_PVAV);
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1914-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1914-block  1
call    4 returned 3
call    5 returned 3
        -: 1915:	  hv_iterinit(hv);
        -: 1916:	  while ((entry = hv_iternext(hv))) {
        3: 1917:		   SV *restrict val = hv_iterval(hv, entry);
        3: 1917-block  0
call    0 returned 3
        3: 1918:		   if (!val || !SvROK(val) || SvTYPE(SvRV(val)) != first_type) {
call    0 returned 3
        -: 1919:		        croak("write_table: Mixed data types detected. Ensure all values are %s references.\n", is_hoh ? "HASH" : "ARRAY");
        9: 1920:		   }
        9: 1920-block  0
call    0 returned 9
branch  1 taken 6
branch  2 taken 3 (fallthrough)
        6: 1921:	  }
        6: 1921-block  0
call    0 returned 6
        6: 1922:	  if (is_hoh) {
call    0 returned 6
        -: 1923:		   rows_av = newAV();
       18: 1924:		   hv_iterinit(hv);
       18: 1924-block  0
call    0 returned 18
branch  1 taken 12
branch  2 taken 6 (fallthrough)
       12: 1925:		   while ((entry = hv_iternext(hv))) {
       12: 1925-block  0
call    0 returned 12
call    1 returned 12
call    2 returned 12
        -: 1926:		        av_push(rows_av, newSVsv(hv_iterkeysv(entry)));
        -: 1927:		   }
        3: 1928:	  }
        3: 1928-block  0
call    0 returned 3
        3: 1929:	} else {
call    0 returned 3
       12: 1930:	  AV *restrict av = (AV*)data_ref;
       12: 1930-block  0
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 1931:	  if (av_len(av) < 0) XSRETURN_EMPTY;
        9: 1931-block  0
call    0 returned 9
        9: 1932:	  SV **restrict first_ptr = av_fetch(av, 0, 0);
call    0 returned 9
call    1 returned 9
        -: 1933:	  if (!first_ptr || !*first_ptr || !SvROK(*first_ptr) || SvTYPE(SvRV(*first_ptr)) != SVt_PVHV) {
        3: 1934:		   croak("write_table: For ARRAY data, all elements must be HASH references (Array of Hashes)\n");
        3: 1934-block  0
call    0 returned 3
       12: 1935:	  }
        9: 1935-block  0
call    0 returned 9
call    1 returned 9
       12: 1935-block  1
branch  2 taken 9
branch  3 taken 3 (fallthrough)
        3: 1936:
        3: 1936-block  0
call    0 returned 3
        3: 1937:	  for (size_t i = 0; i <= av_len(av); i++) {
call    0 returned 3
        -: 1938:		   SV **restrict ptr = av_fetch(av, i, 0);
        4: 1939:		   if (!ptr || !*ptr || !SvROK(*ptr) || SvTYPE(SvRV(*ptr)) != SVt_PVHV) {
        4: 1939-block  0
call    0 returned 4
        4: 1940:		        croak("write_table: Mixed data types detected in Array of Hashes. All elements must be HASH references.\n");
call    0 returned 4
        -: 1941:		   }
        4: 1942:	  }
        4: 1943:	  is_aoh = 1;
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 1943-block  0
       16: 1944:	}
        4: 1944-block  0
       16: 1944-block  1
branch  0 taken 12
branch  1 taken 4 (fallthrough)
       12: 1945:
       12: 1945-block  0
call    0 returned 12
      12*: 1946:	PerlIO *restrict fh = PerlIO_open(file, "w");
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1946-block  0
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 1946-block  1
call    4 returned 12
    %%%%%: 1946-block  2
       12: 1946-block  3
        -: 1947:	if (!fh) croak("write_table: Could not open '%s' for writing", file);
        4: 1948:
        4: 1948-block  0
call    0 returned 4
        4: 1949:	AV *restrict headers_av = newAV();
call    0 returned 4
        -: 1950:	bool inc_rownames = (row_names_sv && SvTRUE(row_names_sv)) ? 1 : 0;
        4: 1951:	const char *restrict rownames_col = NULL;
call    0 returned 4
        4: 1952:
call    0 returned 4
       12: 1953:	// ----- Hash of Hashes -----
       12: 1953-block  0
branch  0 taken 8
branch  1 taken 4 (fallthrough)
        8: 1954:	if (is_hoh) {
        8: 1954-block  0
call    0 returned 8
call    1 returned 8
        -: 1955:		if (col_names_sv && SvOK(col_names_sv)) {
        4: 1956:			AV *restrict c_av = (AV*)SvRV(col_names_sv);
        4: 1956-block  0
call    0 returned 4
        -: 1957:			for(size_t i=0; i<=av_len(c_av); i++) {
        4: 1958:				SV **restrict c = av_fetch(c_av, i, 0);
        4: 1959:				if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
call    0 returned 4
        -: 1960:			}
       11: 1961:		} else {
       11: 1961-block  0
branch  0 taken 8
branch  1 taken 3 (fallthrough)
        8: 1962:			HV *restrict col_map = newHV();
        8: 1963:			hv_iterinit((HV*)data_ref);
        8: 1963-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 1963-block  1
        -: 1964:			HE *restrict entry;
        8: 1965:			while((entry = hv_iternext((HV*)data_ref))) {
        8: 1965-block  0
call    0 returned 8
       8*: 1966:				 HV *restrict inner = (HV*)SvRV(hv_iterval((HV*)data_ref, entry));
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 1966-block  0
    %%%%%: 1966-block  1
        -: 1967:				 hv_iterinit(inner);
       33: 1968:				 HE *restrict inner_entry;
        8: 1968-block  0
       25: 1968-block  1
       33: 1968-block  2
branch  0 taken 26
branch  1 taken 7 (fallthrough)
       26: 1969:				 while((inner_entry = hv_iternext(inner))) {
       26: 1969-block  0
call    0 returned 26
      26*: 1970:				     hv_store_ent(col_map, hv_iterkeysv(inner_entry), newSViv(1), 0);
branch  0 taken 26 (fallthrough)
branch  1 taken 0
       26: 1970-block  0
branch  2 taken 26 (fallthrough)
branch  3 taken 0
       26: 1970-block  1
call    4 returned 26
    %%%%%: 1970-block  2
      26*: 1971:				 }
       26: 1971-block  0
branch  0 taken 26 (fallthrough)
branch  1 taken 0
       26: 1971-block  1
call    2 returned 26
    %%%%%: 1971-block  2
       26: 1972:			}
       26: 1972-block  0
branch  0 taken 16 (fallthrough)
branch  1 taken 10
       16: 1972-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 1
       15: 1973:			unsigned num_cols = hv_iterinit(col_map);
       15: 1973-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 14
        1: 1974:			const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
        1: 1974-block  0
call    0 returned 1
        1: 1975:			for(unsigned i=0; i<num_cols; i++) {
call    0 returned 1
        1: 1976:				 HE *restrict ce = hv_iternext(col_map);
call    0 returned 1
        1: 1977:				 col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1977-block  0
call    2 returned 1
        1: 1978:			}
        1: 1978-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 1978-block  1
call    2 returned 1
        1: 1979:			qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
        1: 1979-block  0
call    0 returned 0
        -: 1980:			for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
       14: 1981:			safefree(col_array);
       14: 1981-block  0
call    0 returned 14
        -: 1982:			SvREFCNT_dec(col_map);
       11: 1983:	}
       11: 1983-block  0
        -: 1984:	size_t num_headers = av_len(headers_av) + 1;
        -: 1985:	const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
        7: 1986:
        7: 1986-block  0
call    0 returned 7
        -: 1987:	size_t h_idx = 0;
        3: 1988:	if (inc_rownames) header_row[h_idx++] = "";
        3: 1988-block  0
call    0 returned 3
call    1 returned 3
        -: 1989:	for(unsigned short int i=0; i<num_headers; i++) {
        8: 1990:		  SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        8: 1990-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 3
        5: 1991:		  header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        5: 1992:	}
        5: 1993:	print_string_row(fh, header_row, h_idx, sep);
        5: 1993-block  0
call    0 returned 5
        -: 1994:	safefree(header_row);
       20: 1995:
       20: 1995-block  0
call    0 returned 20
branch  1 taken 15
branch  2 taken 5 (fallthrough)
       15: 1996:	size_t num_rows = av_len(rows_av) + 1;
       15: 1996-block  0
call    0 returned 15
       15: 1997:	const char **restrict row_array = safemalloc(num_rows * sizeof(char*));
call    0 returned 15
       15: 1998:	for(size_t i=0; i<num_rows; i++) {
branch  0 taken 7 (fallthrough)
branch  1 taken 8
        7: 1998-block  0
        -: 1999:		 row_array[i] = SvPV_nolen(*av_fetch(rows_av, i, 0));
        -: 2000:	}
        7: 2001:	qsort(row_array, num_rows, sizeof(char*), cmp_string_wt);
        5: 2001-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 2001-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 2001-block  2
        2: 2002:
        8: 2003:	HV *restrict data_hv = (HV*)data_ref;
        2: 2003-block  0
        6: 2003-block  1
        8: 2003-block  2
call    0 returned 8
branch  1 taken 6
branch  2 taken 2 (fallthrough)
        6: 2004:	const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
        6: 2004-block  0
call    0 returned 6
        6: 2005:
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 2005-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 2005-block  1
call    4 returned 6
call    5 returned 6
        -: 2006:	for(size_t i=0; i<num_rows; i++) {
        -: 2007:		  size_t d_idx = 0;
        3: 2008:		  if (inc_rownames) row_data[d_idx++] = row_array[i];
        3: 2008-block  0
call    0 returned 3
        3: 2009:
call    0 returned 3
       12: 2010:		  SV **restrict inner_hv_ptr = hv_fetch(data_hv, row_array[i], strlen(row_array[i]), 0);
       12: 2010-block  0
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 2011:		  HV *restrict inner_hv = inner_hv_ptr ? (HV*)SvRV(*inner_hv_ptr) : NULL;
        9: 2011-block  0
call    0 returned 9
        9: 2012:
call    0 returned 9
call    1 returned 9
        -: 2013:		  for(size_t j=0; j<num_headers; j++) {
        3: 2014:		      SV**restrict h_ptr = av_fetch(headers_av, j, 0);
        3: 2014-block  0
call    0 returned 3
       12: 2015:		      const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        9: 2015-block  0
call    0 returned 9
call    1 returned 9
       12: 2015-block  1
branch  2 taken 9
branch  3 taken 3 (fallthrough)
        3: 2016:		      SV **restrict cell_ptr = inner_hv ? hv_fetch(inner_hv, col_name, strlen(col_name), 0) : NULL;
        3: 2016-block  0
call    0 returned 3
        -: 2017:		      if (cell_ptr && SvOK(*cell_ptr)) {
       5*: 2018:		          if (SvROK(*cell_ptr)) {
        5: 2018-block  0
call    0 returned 5
branch  1 taken 0 (fallthrough)
branch  2 taken 5
    %%%%%: 2018-block  1
call    3 never executed
        5: 2019:		              PerlIO_close(fh);
        5: 2019-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4: 2019-block  1
call    2 returned 4
branch  3 taken 0 (fallthrough)
branch  4 taken 4
    #####: 2020:		              safefree(row_array);
    %%%%%: 2020-block  0
call    0 never executed
    #####: 2021:		            safefree(row_data);
call    0 never executed
call    1 never executed
        -: 2022:		              if (headers_av) SvREFCNT_dec(headers_av);
    #####: 2023:		              if (rows_av) SvREFCNT_dec(rows_av);
    %%%%%: 2023-block  0
    %%%%%: 2023-block  1
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 2024:		              croak("write_table: Cannot write nested reference types to table\n");
    %%%%%: 2024-block  0
call    0 never executed
    #####: 2025:		          }
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2025-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2025-block  1
    #####: 2026:		          row_data[d_idx++] = SvPV_nolen(*cell_ptr);
    #####: 2027:		      } else {
    %%%%%: 2027-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 2028:		          row_data[d_idx++] = undef_val;
    %%%%%: 2028-block  0
call    0 never executed
call    1 never executed
        -: 2029:		      }
        -: 2030:		  }
    #####: 2031:		  print_string_row(fh, row_data, d_idx, sep);
    %%%%%: 2031-block  0
call    0 never executed
    #####: 2032:	}
        -: 2033:	safefree(row_array); safefree(row_data);
        5: 2034:
        5: 2034-block  0
call    0 returned 5
        5: 2035:	} else if (is_hoa) { // ----- Hash of Arrays -----
call    0 returned 5
        5: 2036:	  HV *restrict data_hv = (HV*)data_ref;
        5: 2037:	  size_t max_rows = 0;
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4: 2037-block  0
       20: 2038:	  hv_iterinit(data_hv);
        5: 2038-block  0
       20: 2038-block  1
branch  0 taken 15
branch  1 taken 5 (fallthrough)
       15: 2039:	  HE *restrict entry;
       15: 2039-block  0
call    0 returned 15
      15*: 2040:	  while((entry = hv_iternext(data_hv))) {
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 2040-block  0
branch  2 taken 15 (fallthrough)
branch  3 taken 0
       15: 2040-block  1
call    4 returned 15
    %%%%%: 2040-block  2
       15: 2040-block  3
        -: 2041:		   AV *restrict arr = (AV*)SvRV(hv_iterval(data_hv, entry));
        5: 2042:		   size_t len = av_len(arr) + 1;
        5: 2042-block  0
call    0 returned 5
        5: 2043:		   if (len > max_rows) max_rows = len;
call    0 returned 5
        5: 2044:	  }
call    0 returned 5
       32: 2045:
       27: 2045-block  0
       32: 2045-block  1
branch  0 taken 27
branch  1 taken 5 (fallthrough)
       27: 2046:	  if (col_names_sv && SvOK(col_names_sv)) {
       27: 2047:		   AV *restrict c_av = (AV*)SvRV(col_names_sv);
       27: 2047-block  0
branch  0 taken 23 (fallthrough)
branch  1 taken 4
       23: 2048:		   for(size_t i=0; i<=av_len(c_av); i++) {
       23: 2048-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 23
    #####: 2049:		        SV **restrict c = av_fetch(c_av, i, 0);
    %%%%%: 2049-block  0
call    0 never executed
    #####: 2050:		        if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2050-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2050-block  1
    #####: 2051:		   }
    #####: 2052:	  } else {
    %%%%%: 2052-block  0
call    0 never executed
    #####: 2053:		   unsigned int num_cols = hv_iterinit(data_hv);
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2053-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 2054:		   const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
    %%%%%: 2054-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 2055:		   for(unsigned int i=0; i<num_cols; i++) {
    %%%%%: 2055-block  0
call    0 never executed
    #####: 2056:		        HE *restrict ce = hv_iternext(data_hv);
call    0 never executed
    #####: 2057:		        col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2057-block  0
call    2 never executed
    #####: 2058:		   }
    %%%%%: 2058-block  0
call    0 never executed
        -: 2059:		   qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
    #####: 2060:		   for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
    %%%%%: 2060-block  0
call    0 never executed
        -: 2061:		   safefree(col_array);
    #####: 2062:	  }
    %%%%%: 2062-block  0
        -: 2063:	  if (av_len(headers_av) < 0) croak("Could not get headers in write_table");
        -: 2064:	  if (inc_rownames && contains_nondigit(row_names_sv)) {
    #####: 2065:		   rownames_col = SvPV_nolen(row_names_sv);
    %%%%%: 2065-block  0
        -: 2066:		   AV *restrict filtered_headers = (AV*)sv_2mortal((SV*)newAV());
        -: 2067:
        -: 2068:		   for(size_t i=0; i<=av_len(headers_av); i++) {
       23: 2069:		        SV**restrict h_ptr = av_fetch(headers_av, i, 0);
       23: 2070:		        if (!h_ptr || !*h_ptr) continue;
       23: 2070-block  0
call    0 returned 23
        -: 2071:		        SV *restrict h_sv = *h_ptr;
        -: 2072:		        if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
      108: 2073:		            av_push(filtered_headers, newSVsv(h_sv));
       27: 2073-block  0
       81: 2073-block  1
      108: 2073-block  2
branch  0 taken 81
branch  1 taken 27 (fallthrough)
       81: 2074:		        }
       81: 2074-block  0
call    0 returned 81
      81*: 2075:		   }
branch  0 taken 81 (fallthrough)
branch  1 taken 0
       81: 2075-block  0
branch  2 taken 81 (fallthrough)
branch  3 taken 0
       81: 2075-block  1
call    4 returned 81
    %%%%%: 2075-block  2
       81: 2076:		   SvREFCNT_dec(headers_av);
       81: 2076-block  0
call    0 returned 81
      162: 2077:		   headers_av = filtered_headers;
branch  0 taken 81 (fallthrough)
branch  1 taken 0
       81: 2077-block  0
branch  2 taken 81 (fallthrough)
branch  3 taken 0
       81: 2077-block  1
       81: 2078:	  }
       81: 2079:	  size_t num_headers = av_len(headers_av) + 1;
       81: 2079-block  0
call    0 returned 81
       81: 2080:	  const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
branch  0 taken 67 (fallthrough)
branch  1 taken 14
       67: 2080-block  0
branch  2 taken 46 (fallthrough)
branch  3 taken 21
       46: 2081:	  size_t h_idx = 0;
       46: 2081-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 46
    #####: 2082:	  if (inc_rownames) header_row[h_idx++] = "";
    %%%%%: 2082-block  0
call    0 never executed
    #####: 2083:	  for(size_t i=0; i<num_headers; i++) {
call    0 never executed
    #####: 2084:		   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2084-block  0
call    2 never executed
    #####: 2085:		   header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
    %%%%%: 2085-block  0
call    0 never executed
        -: 2086:	  }
       46: 2087:	  print_string_row(fh, header_row, h_idx, sep);
       46: 2087-block  0
call    0 returned 46
        -: 2088:	  safefree(header_row);
       35: 2089:	  const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
       35: 2089-block  0
        -: 2090:	  for(size_t i=0; i<max_rows; i++) {
        -: 2091:		   size_t d_idx = 0;
    #####: 2092:		   if (inc_rownames) {
    %%%%%: 2092-block  0
        -: 2093:		        if (rownames_col) {
        -: 2094:		            SV **restrict rn_arr_ptr = hv_fetch(data_hv, rownames_col, strlen(rownames_col), 0);
       27: 2095:		            if (rn_arr_ptr && SvROK(*rn_arr_ptr)) {
       27: 2095-block  0
call    0 returned 27
       27: 2096:		                AV *restrict rn_arr = (AV*)SvRV(*rn_arr_ptr);
branch  0 taken 23 (fallthrough)
branch  1 taken 4
       23: 2096-block  0
branch  2 taken 23 (fallthrough)
branch  3 taken 0
       23: 2096-block  1
call    4 returned 23
        -: 2097:		                SV **restrict rn_val_ptr = av_fetch(rn_arr, i, 0);
        5: 2098:		                if (rn_val_ptr && SvOK(*rn_val_ptr)) {
        5: 2098-block  0
call    0 returned 5
        3: 2099:		                    if (SvROK(*rn_val_ptr)) {
        3: 2099-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 2100:		                        PerlIO_close(fh);
        3: 2101:		                        safefree(row_data);
        3: 2101-block  0
call    0 returned 3
        4: 2102:		                        if (headers_av) SvREFCNT_dec(headers_av);
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 2102-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 2102-block  1
        1: 2103:		                        croak("write_table: Cannot write nested reference types to table\n");
        3: 2104:		                    }
        1: 2104-block  0
        2: 2104-block  1
        3: 2104-block  2
call    0 returned 3
branch  1 taken 2
branch  2 taken 1 (fallthrough)
        2: 2105:		                    row_data[d_idx++] = SvPV_nolen(*rn_val_ptr);
        2: 2105-block  0
call    0 returned 2
        2: 2106:		                } else {
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 2106-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 2106-block  1
call    4 returned 2
call    5 returned 2
        -: 2107:		                    row_data[d_idx++] = undef_val;
        -: 2108:		                }
        2: 2109:		            } else {
        2: 2109-block  0
call    0 returned 2
        7: 2110:		                row_data[d_idx++] = undef_val;
        5: 2110-block  0
        7: 2110-block  1
branch  0 taken 5
branch  1 taken 2 (fallthrough)
        5: 2111:		            }
        5: 2111-block  0
call    0 returned 5
        5: 2112:		        } else {
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 2112-block  0
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 2113:		            char buf[32];
        5: 2114:		            snprintf(buf, sizeof(buf), "%ld", (long)(i + 1));
        5: 2114-block  0
call    0 returned 5
        -: 2115:		            row_data[d_idx++] = savepv(buf);
       14: 2116:		        }
       14: 2116-block  0
call    0 returned 14
branch  1 taken 9
branch  2 taken 5 (fallthrough)
        9: 2117:		   }
        9: 2117-block  0
call    0 returned 9
call    1 returned 9
call    2 returned 9
        -: 2118:		   for(size_t j=0; j<num_headers; j++) {
        -: 2119:		        SV**restrict h_ptr = av_fetch(headers_av, j, 0);
        -: 2120:		        const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        2: 2121:		        SV **restrict arr_ptr = hv_fetch(data_hv, col_name, strlen(col_name), 0);
        2: 2121-block  0
call    0 returned 2
        2: 2122:		        if (arr_ptr && SvROK(*arr_ptr)) {
call    0 returned 2
        7: 2123:		            AV *restrict arr = (AV*)SvRV(*arr_ptr);
        7: 2123-block  0
branch  0 taken 5
branch  1 taken 2 (fallthrough)
        5: 2124:		            SV **restrict cell_ptr = av_fetch(arr, i, 0);
        5: 2124-block  0
call    0 returned 5
        5: 2125:		            if (cell_ptr && SvOK(*cell_ptr)) {
call    0 returned 5
call    1 returned 5
        -: 2126:		                if (SvROK(*cell_ptr)) {
        2: 2127:		                    PerlIO_close(fh);
        2: 2127-block  0
call    0 returned 2
        7: 2128:		                    safefree(row_data);
        5: 2128-block  0
call    0 returned 5
call    1 returned 5
        7: 2128-block  1
branch  2 taken 5
branch  3 taken 2 (fallthrough)
        2: 2129:		                    if (headers_av) SvREFCNT_dec(headers_av);
        2: 2129-block  0
call    0 returned 2
        2: 2130:		                    croak("write_table: Cannot write nested reference types to table\n");
call    0 returned 2
        -: 2131:		                }
        3: 2132:		                row_data[d_idx++] = SvPV_nolen(*cell_ptr);
        3: 2132-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 2132-block  1
call    2 returned 1
branch  3 taken 0 (fallthrough)
branch  4 taken 1
    #####: 2133:		            } else {
    %%%%%: 2133-block  0
call    0 never executed
    #####: 2134:		                row_data[d_idx++] = undef_val;
call    0 never executed
    #####: 2135:		            }
    %%%%%: 2135-block  0
    %%%%%: 2135-block  1
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 2136:		        } else {
    %%%%%: 2136-block  0
call    0 never executed
    #####: 2137:		            row_data[d_idx++] = undef_val;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2137-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2137-block  1
    #####: 2138:		        }
    #####: 2139:		   }
    %%%%%: 2139-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 2140:		   print_string_row(fh, row_data, d_idx, sep);
    %%%%%: 2140-block  0
call    0 never executed
call    1 never executed
        -: 2141:		   if (inc_rownames && !rownames_col) safefree((char*)row_data[0]);
        -: 2142:	  }
    #####: 2143:	  safefree(row_data);
    %%%%%: 2143-block  0
call    0 never executed
    #####: 2144:	} else if (is_aoh) {// ----- Array of Hashes -----
        -: 2145:	  AV *restrict data_av = (AV*)data_ref;
        3: 2146:	  size_t num_rows = av_len(data_av) + 1;
        3: 2146-block  0
call    0 returned 3
        3: 2147:	  if (col_names_sv && SvOK(col_names_sv)) {
call    0 returned 3
        3: 2148:		   AV *restrict c_av = (AV*)SvRV(col_names_sv);
        3: 2149:		   for(size_t i=0; i<=av_len(c_av); i++) {
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 2149-block  0
       10: 2150:		        SV **restrict c = av_fetch(c_av, i, 0);
        3: 2150-block  0
       10: 2150-block  1
branch  0 taken 7
branch  1 taken 3 (fallthrough)
        7: 2151:		        if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
        7: 2151-block  0
call    0 returned 7
       7*: 2152:		   }
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2152-block  0
branch  2 taken 7 (fallthrough)
branch  3 taken 0
        7: 2152-block  1
call    4 returned 7
    %%%%%: 2152-block  2
        7: 2152-block  3
        -: 2153:	  } else {
        3: 2154:		   HV *restrict col_map = newHV();
        3: 2154-block  0
call    0 returned 3
        3: 2155:		   for(size_t i=0; i<num_rows; i++) {
call    0 returned 3
        3: 2156:		        SV **restrict row_ptr = av_fetch(data_av, i, 0);
call    0 returned 3
       10: 2157:		        if (row_ptr && SvROK(*row_ptr)) {
        7: 2157-block  0
       10: 2157-block  1
branch  0 taken 7
branch  1 taken 3 (fallthrough)
        7: 2158:		            HV *restrict row_hv = (HV*)SvRV(*row_ptr);
        7: 2159:		            hv_iterinit(row_hv);
        7: 2159-block  0
call    0 returned 7
       7*: 2160:		            HE *restrict entry;
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2160-block  0
branch  2 taken 7 (fallthrough)
branch  3 taken 0
        7: 2160-block  1
    %%%%%: 2160-block  2
        7: 2161:		            while((entry = hv_iternext(row_hv))) {
        7: 2161-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 4
        3: 2162:		                hv_store_ent(col_map, hv_iterkeysv(entry), newSViv(1), 0);
        3: 2162-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 2163:		            }
    %%%%%: 2163-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2163-block  1
call    2 never executed
    %%%%%: 2163-block  2
    #####: 2164:		        }
    %%%%%: 2164-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2164-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 2165:		   }
    %%%%%: 2165-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 2166:		   unsigned num_cols = hv_iterinit(col_map);
    %%%%%: 2166-block  0
call    0 never executed
    #####: 2167:		   const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
call    0 never executed
    #####: 2168:		   for(unsigned int i=0; i<num_cols; i++) {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2168-block  0
call    2 never executed
    #####: 2169:		        HE *restrict ce = hv_iternext(col_map);
    %%%%%: 2169-block  0
call    0 never executed
        -: 2170:		        col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
    #####: 2171:		   }
    %%%%%: 2171-block  0
call    0 never executed
        -: 2172:		   qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
    #####: 2173:		   for(unsigned int i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
    %%%%%: 2173-block  0
        -: 2174:		   safefree(col_array);
        -: 2175:		   SvREFCNT_dec(col_map);
        -: 2176:	  }
        3: 2177:	  if (inc_rownames && contains_nondigit(row_names_sv)) {
        3: 2178:		   rownames_col = SvPV_nolen(row_names_sv);
        3: 2178-block  0
call    0 returned 3
        -: 2179:		   AV *restrict filtered_headers = newAV();
        -: 2180:		   for(size_t i=0; i<=av_len(headers_av); i++) {
        -: 2181:		        SV**restrict h_ptr = av_fetch(headers_av, i, 0);
       24: 2182:		        if (!h_ptr || !*h_ptr) continue;
        7: 2182-block  0
       17: 2182-block  1
       24: 2182-block  2
branch  0 taken 17
branch  1 taken 7 (fallthrough)
       17: 2183:		        SV *restrict h_sv = *h_ptr;
       17: 2183-block  0
call    0 returned 17
      17*: 2184:		        if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
branch  0 taken 17 (fallthrough)
branch  1 taken 0
       17: 2184-block  0
branch  2 taken 17 (fallthrough)
branch  3 taken 0
       17: 2184-block  1
call    4 returned 17
    %%%%%: 2184-block  2
      17*: 2185:		            av_push(filtered_headers, newSVsv(h_sv));
       17: 2185-block  0
branch  0 taken 17 (fallthrough)
branch  1 taken 0
       17: 2185-block  1
call    2 returned 17
    %%%%%: 2185-block  2
       17: 2186:		        }
       17: 2186-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 4
       13: 2186-block  1
branch  2 taken 13 (fallthrough)
branch  3 taken 0
       13: 2187:		   }
       13: 2187-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    #####: 2188:		   SvREFCNT_dec(headers_av);
    %%%%%: 2188-block  0
call    0 never executed
    #####: 2189:		   headers_av = filtered_headers;
call    0 never executed
    #####: 2190:	  }
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2190-block  0
call    2 never executed
    #####: 2191:	  size_t num_headers = av_len(headers_av) + 1;
    %%%%%: 2191-block  0
call    0 never executed
        -: 2192:	  const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
       13: 2193:	  size_t h_idx = 0;
       13: 2193-block  0
call    0 returned 13
        -: 2194:	  if (inc_rownames) header_row[h_idx++] = "";
        4: 2195:	  for(size_t i=0; i<num_headers; i++) {
        4: 2195-block  0
        -: 2196:		   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        -: 2197:		   header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        7: 2198:	  }
        7: 2198-block  0
call    0 returned 7
        7: 2199:	  print_string_row(fh, header_row, h_idx, sep);
branch  0 taken 3 (fallthrough)
branch  1 taken 4
        3: 2199-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 2199-block  1
call    4 returned 3
        -: 2200:	  safefree(header_row);
        3: 2201:	  const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
        3: 2201-block  0
call    0 returned 3
        -: 2202:	  for(size_t i=0; i<num_rows; i++) {
       11: 2203:		   size_t d_idx = 0;
       11: 2203-block  0
branch  0 taken 11 (fallthrough)
branch  1 taken 0
       11: 2203-block  1
call    2 returned 11
       11: 2204:		   SV **restrict row_ptr = av_fetch(data_av, i, 0);
       11: 2204-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 8
        3: 2204-block  1
call    2 returned 3
       11: 2205:		   HV *restrict row_hv = (row_ptr && SvROK(*row_ptr)) ? (HV*)SvRV(*row_ptr) : NULL;
       11: 2205-block  0
call    0 returned 11
       11: 2206:		   if (inc_rownames) {
        -: 2207:		       if (rownames_col) {
        -: 2208:		         SV **restrict rn_val_ptr = row_hv ? hv_fetch(row_hv, rownames_col, strlen(rownames_col), 0) : NULL;
        -: 2209:		         if (rn_val_ptr && SvOK(*rn_val_ptr)) {
        -: 2210:		               if (SvROK(*rn_val_ptr)) {
        -: 2211:		                    PerlIO_close(fh);
        -: 2212:		                         safefree(row_data);
       13: 2213:		                         if (headers_av) SvREFCNT_dec(headers_av);
       13: 2214:		                    croak("write_table: Cannot write nested reference types to table\n");
       13: 2214-block  0
call    0 returned 13
       13: 2215:		               }
call    0 returned 13
       13: 2216:		               row_data[d_idx++] = SvPV_nolen(*rn_val_ptr);
        -: 2217:		         } else {
        -: 2218:		               row_data[d_idx++] = undef_val;
       13: 2219:		         }
        -: 2220:		       } else {
       13: 2221:		         char buf[32];
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2221-block  0
branch  2 taken 13 (fallthrough)
branch  3 taken 0
       13: 2221-block  1
branch  4 taken 13 (fallthrough)
branch  5 taken 0
       13: 2222:		         snprintf(buf, sizeof(buf), "%ld", (long)(i + 1));
       13: 2222-block  0
        -: 2223:		         row_data[d_idx++] = savepv(buf);
    #####: 2224:		       }
    %%%%%: 2224-block  0
call    0 never executed
        -: 2225:		   }
      13*: 2226:
       13: 2226-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2226-block  1
    %%%%%: 2226-block  2
      13*: 2227:		   for(size_t j=0; j<num_headers; j++) {
       13: 2227-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2227-block  1
    %%%%%: 2227-block  2
        -: 2228:		        SV**restrict h_ptr = av_fetch(headers_av, j, 0);
       13: 2229:		        const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
       13: 2229-block  0
call    0 returned 13
       13: 2230:		        SV **restrict cell_ptr = row_hv ? hv_fetch(row_hv, col_name, strlen(col_name), 0) : NULL;
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    #####: 2231:		        if (cell_ptr && SvOK(*cell_ptr)) {
    %%%%%: 2231-block  0
call    0 never executed
        -: 2232:		            if (SvROK(*cell_ptr)) {
       13: 2233:		                PerlIO_close(fh);
       13: 2233-block  0
call    0 returned 13
        -: 2234:		                safefree(row_data);
     3975: 2235:		                if (headers_av) SvREFCNT_dec(headers_av);
     3975: 2235-block  0
call    0 returned 3975
branch  1 taken 3962
branch  2 taken 13 (fallthrough)
     3962: 2236:		                croak("write_table: Cannot write nested reference types to table\n");
     3962: 2236-block  0
call    0 returned 3962
     3962: 2237:		            }
        -: 2238:		            row_data[d_idx++] = SvPV_nolen(*cell_ptr);
     3962: 2239:		        } else {
branch  0 taken 3962 (fallthrough)
branch  1 taken 0
     3962: 2239-block  0
branch  2 taken 3960 (fallthrough)
branch  3 taken 2
     3960: 2240:		            row_data[d_idx++] = undef_val;
     3960: 2241:		        }
     3960: 2241-block  0
branch  0 taken 3960 (fallthrough)
branch  1 taken 0
     3960: 2241-block  1
branch  2 taken 3696 (fallthrough)
branch  3 taken 264
     3696: 2242:		   }
     3696: 2242-block  0
        -: 2243:		   print_string_row(fh, row_data, d_idx, sep);
        -: 2244:		   if (inc_rownames && !rownames_col) safefree((char*)row_data[0]);
     3962: 2245:	  }
     3962: 2245-block  0
branch  0 taken 3961 (fallthrough)
branch  1 taken 1
        -: 2246:	  safefree(row_data);
     3961: 2247:	}
     3962: 2248:	if (headers_av) SvREFCNT_dec(headers_av);
     3961: 2248-block  0
        1: 2248-block  1
     3962: 2248-block  2
branch  0 taken 3962
branch  1 taken 0 (fallthrough)
     3962: 2249:	if (rows_av) SvREFCNT_dec(rows_av);
     3962: 2249-block  0
branch  0 taken 3962 (fallthrough)
branch  1 taken 0
     3962: 2249-block  1
branch  2 taken 3961 (fallthrough)
branch  3 taken 1
     3961: 2249-block  2
        -: 2250:	PerlIO_close(fh);
    3961*: 2251:	XSRETURN_EMPTY;
     3961: 2251-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3961
    %%%%%: 2251-block  1
        -: 2252:}
        -: 2253:
    3961*: 2254:SV*
     3961: 2254-block  0
branch  0 taken 3961 (fallthrough)
branch  1 taken 0
     3961: 2254-block  1
branch  2 taken 3961 (fallthrough)
branch  3 taken 0
     3961: 2254-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 3961
    #####: 2255:_parse_csv_file(char* file, const char* sep_str, const char* comment_str, SV* callback = &PL_sv_undef)
    %%%%%: 2255-block  0
        -: 2256:INIT:
        -: 2257:	PerlIO *restrict fp;
        -: 2258:	AV *restrict data = NULL;
   293588: 2259:	AV *restrict current_row = newAV();
     3962: 2259-block  0
   289626: 2259-block  1
   293588: 2259-block  2
branch  0 taken 289626
branch  1 taken 3962 (fallthrough)
   289626: 2260:	SV *restrict field = newSVpvs("");
   289626: 2261:	bool in_quotes = 0, post_quote = 0;
   289626: 2261-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 289625
        1: 2261-block  1
        1: 2261-block  2
   289625: 2262:	size_t sep_len, comment_len;
   289625: 2262-block  0
branch  0 taken 22325 (fallthrough)
branch  1 taken 267300
    22325: 2263:	SV *restrict line_sv;
    22325: 2263-block  0
branch  0 taken 11163 (fallthrough)
branch  1 taken 11162
    11163: 2263-block  1
branch  2 taken 11155 (fallthrough)
branch  3 taken 8
    11155: 2263-block  2
branch  4 taken 3 (fallthrough)
branch  5 taken 11152
        3: 2264:	bool use_cb = 0;
        3: 2264-block  0
call    0 returned 3
        3: 2265:CODE:
    22322: 2266:	if (SvOK(callback) && SvROK(callback) && SvTYPE(SvRV(callback)) == SVt_PVCV) {
    22322: 2266-block  0
branch  0 taken 11160 (fallthrough)
branch  1 taken 11162
    11160: 2267:		use_cb = 1;
    11160: 2268:	} else {
    11160: 2268-block  0
    11162: 2269:		data = newAV();
    11162: 2269-block  0
branch  0 taken 11161 (fallthrough)
branch  1 taken 1
    11161: 2270:	}
    11161: 2270-block  0
        -: 2271:	sep_len = sep_str ? strlen(sep_str) : 0;
   267300: 2272:	comment_len = comment_str ? strlen(comment_str) : 0;
   267300: 2272-block  0
branch  0 taken 205528 (fallthrough)
branch  1 taken 61772
   205528: 2272-block  1
branch  2 taken 205528 (fallthrough)
branch  3 taken 0
   205528: 2272-block  2
branch  4 taken 205528 (fallthrough)
branch  5 taken 0
   205528: 2272-block  3
branch  6 taken 51615 (fallthrough)
branch  7 taken 153913
    51615: 2273:
    51615: 2273-block  0
call    0 returned 51615
call    1 returned 51615
    51615: 2274:	fp = PerlIO_open(file, "r");
call    0 returned 51615
    51615: 2275:	if (!fp) {
    51615: 2276:		croak("Could not open file '%s'", file);
        -: 2277:	}
   215685: 2278:	line_sv = newSV_type(SVt_PV);
   215685: 2278-block  0
call    0 returned 215685
        -: 2279:	// Read line by line using PerlIO
        -: 2280:	while (sv_gets(line_sv, fp, 0) != NULL) {
     3962: 2281:		char *restrict line = SvPV_nolen(line_sv);
     3962: 2281-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3960
        -: 2282:		size_t len = SvCUR(line_sv);
        2: 2283:		// chomp \r\n (Handles Windows invisible \r natively)
        2: 2283-block  0
call    0 returned 2
        -: 2284:		if (len > 0 && line[len-1] == '\n') {
     3960: 2285:			len--;
        -: 2286:			if (len > 0 && line[len-1] == '\r') {
     3960: 2287:				len--;
     3960: 2287-block  0
call    0 returned 3960
call    1 returned 3960
     3960: 2288:			}
call    0 returned 3960
        -: 2289:		}
     3960: 2290:		if (!in_quotes) {
branch  0 taken 3960 (fallthrough)
branch  1 taken 0
     3960: 2291:			// Skip completely empty lines (\h*[\r\n]+$ equivalent)
     3960: 2292:			bool is_empty = 1;
     3960: 2292-block  0
call    0 returned 3960
     3960: 2293:			for (size_t i = 0; i < len; i++) {
call    0 returned 3960
    3960*: 2294:				if (line[i] != ' ' && line[i] != '\t') { is_empty = 0; break; }
branch  0 taken 0 (fallthrough)
branch  1 taken 3960
    %%%%%: 2294-block  0
call    2 never executed
    3960*: 2295:			}
     3960: 2295-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3960
    %%%%%: 2295-block  1
call    2 never executed
     3960: 2295-block  2
call    3 returned 3960
call    4 returned 3960
     3960: 2296:			if (is_empty) continue;
     3960: 2297:
call    0 returned 3960
     3960: 2298:			// Skip comments
branch  0 taken 3960 (fallthrough)
branch  1 taken 0
     3960: 2298-block  0
call    2 returned 3960
     3960: 2299:			if (comment_len > 0 && len >= comment_len && strncmp(line, comment_str, comment_len) == 0) {
     3960: 2299-block  0
call    0 returned 3960
     3960: 2300:				continue;
call    0 returned 3960
        -: 2301:			}
    #####: 2302:		}
    %%%%%: 2302-block  0
call    0 never executed
call    1 never executed
        -: 2303:		// --- CORE PARSING MACHINE ---
     3960: 2304:		for (size_t i = 0; i < len; i++) {
     3960: 2304-block  0
call    0 returned 3960
        -: 2305:			const char ch = line[i];
        -: 2306:			if (ch == '\r') continue;
       13: 2307:			if (ch == '"') {
       13: 2307-block  0
call    0 returned 13
       13: 2308:				if (in_quotes && (i + 1 < len) && line[i+1] == '"') {
call    0 returned 13
        -: 2309:					sv_catpvn(field, "\"", 1);
       13: 2310:					i++; // Skip the escaped second quote
branch  0 taken 1 (fallthrough)
branch  1 taken 12
        1: 2311:				} else if (in_quotes) {
        1: 2311-block  0
call    0 returned 1
call    1 returned 1
        1: 2312:					in_quotes = 0;  // Close quotes
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 2313:					post_quote = 1;
        1: 2314:				} else if (!post_quote) {
        1: 2314-block  0
call    0 returned 1
        1: 2315:					in_quotes = 1; // Open quotes (only when not in post-quote state)
call    0 returned 1
       1*: 2316:				}
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 2316-block  0
call    2 never executed
       1*: 2317:			} else if (!in_quotes && sep_len > 0 && (len - i) >= sep_len && strncmp(line + i, sep_str, sep_len) == 0) {
        1: 2317-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 2317-block  1
call    2 never executed
        1: 2317-block  2
call    3 returned 1
call    4 returned 1
        1: 2318:				av_push(current_row, newSVsv(field));
        1: 2319:				sv_setpvs(field, ""); // Reset for next field
call    0 returned 1
        1: 2320:				i += sep_len - 1;     // Advance past multi-char separators
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 2320-block  0
call    2 returned 1
        1: 2321:				post_quote = 0;
        1: 2321-block  0
call    0 returned 1
        1: 2322:			} else {
call    0 returned 1
        -: 2323:				sv_catpvn(field, &ch, 1);
    #####: 2324:			}
    %%%%%: 2324-block  0
call    0 never executed
call    1 never executed
        -: 2325:		}
        1: 2326:		if (in_quotes) {
        1: 2326-block  0
call    0 returned 1
        -: 2327:			// Line ended but quotes are still open! Append newline and fetch next
       13: 2328:			sv_catpvn(field, "\n", 1);
       13: 2328-block  0
call    0 returned 13
       13: 2329:		} else {
call    0 returned 13
       13: 2330:			post_quote = 0; // Reset post-quote state at row boundary
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2331:			// Push the final field of the record
       13: 2331-block  0
        -: 2332:			av_push(current_row, newSVsv(field));
    #####: 2333:			sv_setpvs(field, "");
    %%%%%: 2333-block  0
call    0 never executed
        -: 2334:			// If a callback is provided, invoke it in a streaming fashion
        -: 2335:			if (use_cb) {
        -: 2336:				dSP;
        -: 2337:				ENTER;
        -: 2338:				SAVETMPS;
        -: 2339:				PUSHMARK(SP);
        -: 2340:				XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
        -: 2341:				PUTBACK;
        4: 2342:				call_sv(callback, G_DISCARD);
        4: 2342-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 2342-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 4
    #####: 2343:				FREETMPS;
    %%%%%: 2343-block  0
call    0 never executed
        -: 2344:				LEAVE;
        4: 2345:				SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
        4: 2345-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 2345-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 4
    #####: 2346:			} else {
    %%%%%: 2346-block  0
call    0 never executed
        -: 2347:				av_push(data, newRV_noinc((SV*)current_row));
        -: 2348:			}
        -: 2349:			current_row = newAV();
        4: 2350:		}
        4: 2350-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 2
        2: 2351:	}
        2: 2351-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 2352:	PerlIO_close(fp);
        1: 2352-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 2353:	SvREFCNT_dec(line_sv);
    %%%%%: 2353-block  0
call    0 never executed
        -: 2354:
        -: 2355:	if (in_quotes) {
        4: 2356:		av_push(current_row, newSVsv(field));
        4: 2357:		if (use_cb) {
        4: 2358:			dSP;
        4: 2358-block  0
call    0 returned 4
        4: 2359:			ENTER;
call    0 returned 4
        -: 2360:			SAVETMPS;
        4: 2361:			PUSHMARK(SP);
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 2362:			XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
    %%%%%: 2362-block  0
call    0 never executed
        -: 2363:			PUTBACK;
        -: 2364:			call_sv(callback, G_DISCARD);
        -: 2365:			FREETMPS;
        -: 2366:			LEAVE;
        -: 2367:			SvREFCNT_dec(current_row);
        4: 2368:		} else {
        4: 2368-block  0
call    0 returned 4
        4: 2369:			av_push(data, newRV_noinc((SV*)current_row));
call    0 returned 4
        4: 2370:		}
        -: 2371:		current_row = newAV();
       24: 2372:	}
       20: 2372-block  0
       24: 2372-block  1
branch  0 taken 20
branch  1 taken 4 (fallthrough)
       20: 2373:	SvREFCNT_dec(field);
       20: 2373-block  0
call    0 returned 20
       20: 2374:	SvREFCNT_dec(current_row);
call    0 returned 20
        -: 2375:	if (use_cb) {
        -: 2376:		RETVAL = &PL_sv_undef; // Memory was fully handled by callback stream
      20*: 2377:	} else {
branch  0 taken 20 (fallthrough)
branch  1 taken 0
       20: 2377-block  0
branch  2 taken 20 (fallthrough)
branch  3 taken 0
       20: 2377-block  1
call    4 returned 20
branch  5 taken 20 (fallthrough)
branch  6 taken 0
       20: 2377-block  2
call    7 returned 20
    %%%%%: 2377-block  3
      20*: 2378:		RETVAL = newRV_noinc((SV*)data);
       20: 2378-block  0
branch  0 taken 20 (fallthrough)
branch  1 taken 0
       20: 2378-block  1
branch  2 taken 20 (fallthrough)
branch  3 taken 0
       20: 2378-block  2
call    4 returned 20
branch  5 taken 20 (fallthrough)
branch  6 taken 0
       20: 2378-block  3
call    7 returned 20
    %%%%%: 2378-block  4
        -: 2379:	}
        -: 2380:OUTPUT:
       20: 2381:	RETVAL
       20: 2381-block  0
branch  0 taken 20 (fallthrough)
branch  1 taken 0
       20: 2381-block  1
branch  2 taken 20 (fallthrough)
branch  3 taken 0
       20: 2382:
       20: 2383:SV* cov(SV* x_sv, SV* y_sv, const char* method = "pearson")
       20: 2384:	CODE:
       20: 2384-block  0
        -: 2385:	{
        -: 2386:		// 1. Validate inputs are Array References
        -: 2387:		if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV) {
        -: 2388:			croak("cov: first argument 'x' must be an ARRAY reference");
        4: 2389:		}
        4: 2389-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 2390:		if (!SvROK(y_sv) || SvTYPE(SvRV(y_sv)) != SVt_PVAV) {
    %%%%%: 2390-block  0
call    0 never executed
call    1 never executed
    #####: 2391:			croak("cov: second argument 'y' must be an ARRAY reference");
call    0 never executed
        -: 2392:		}
        4: 2393:
        -: 2394:		// 2. Validate method argument
        4: 2395:		if (strcmp(method, "pearson") != 0 && 
        4: 2395-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        -: 2396:			strcmp(method, "spearman") != 0 && 
        6: 2397:			strcmp(method, "kendall") != 0) {
        1: 2397-block  0
        5: 2397-block  1
        6: 2397-block  2
branch  0 taken 5
branch  1 taken 1
       30: 2398:			croak("cov: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')", method);
        5: 2398-block  0
       30: 2398-block  1
branch  0 taken 25
branch  1 taken 5 (fallthrough)
       25: 2399:		}
       25: 2400:
       25: 2401:		AV *restrict x_av = (AV*)SvRV(x_sv);
       25: 2401-block  0
        -: 2402:		AV *restrict y_av = (AV*)SvRV(y_sv);
        -: 2403:		size_t nx = av_len(x_av) + 1;
        -: 2404:		size_t ny = av_len(y_av) + 1;
        3: 2405:
        3: 2406:		if (nx != ny) {
        3: 2406-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        -: 2407:			croak("cov: incompatible dimensions (x has %lu, y has %lu)", 
        1: 2408:				   (unsigned long)nx, (unsigned long)ny);
        1: 2408-block  0
call    0 returned 1
        1: 2409:		}
call    0 returned 1
        -: 2410:
        -: 2411:		// 3. Extract Valid Pairwise Data
        1: 2412:		// Allocate temporary C arrays for numeric processing
call    0 returned 1
        1: 2413:		double *restrict x_val = (double*)safemalloc(nx * sizeof(double));
call    0 returned 1
        -: 2414:		double *restrict y_val = (double*)safemalloc(nx * sizeof(double));
        6: 2415:		size_t n = 0;
        6: 2415-block  0
branch  0 taken 5
branch  1 taken 1 (fallthrough)
        5: 2416:
        5: 2417:		for (size_t i = 0; i < nx; i++) {
        5: 2418:			SV **restrict x_tv = av_fetch(x_av, i, 0);
        5: 2419:			SV **restrict y_tv = av_fetch(y_av, i, 0);
        5: 2420:
        5: 2420-block  0
        -: 2421:			// Extract numeric values, defaulting to NAN for missing/invalid data
        -: 2422:			double xv = (x_tv && SvOK(*x_tv) && looks_like_number(*x_tv)) ? SvNV(*x_tv) : NAN;
        1: 2423:			double yv = (y_tv && SvOK(*y_tv) && looks_like_number(*y_tv)) ? SvNV(*y_tv) : NAN;
        1: 2423-block  0
call    0 returned 1
        1: 2424:
call    0 returned 1
        -: 2425:			// Pairwise complete observations (skips NAs seamlessly like R)
        -: 2426:			if (!isnan(xv) && !isnan(yv)) {
       12: 2427:				 x_val[n] = xv;
        2: 2427-block  0
       12: 2427-block  1
branch  0 taken 10
branch  1 taken 2 (fallthrough)
       10: 2428:				 y_val[n] = yv;
       10: 2429:				 n++;
       10: 2430:			}
       10: 2431:		}
       10: 2432:
       10: 2432-block  0
        -: 2433:		// 4. Handle edge cases where data is too sparse
        -: 2434:		if (n < 2) {
        -: 2435:			Safefree(x_val);	Safefree(y_val);
        -: 2436:			RETVAL = newSVnv(NAN);
        3: 2437:		} else {
        3: 2437-block  0
        -: 2438:			double ans = 0.0;			
        -: 2439:			// 5. Algorithm routing
        4: 2440:			if (strcmp(method, "kendall") == 0) {
        4: 2440-block  0
call    0 returned 4
        4: 2441:				 // R's default cov(..., method="kendall") iterates the full n x n space
call    0 returned 4
        4: 2442:				 for (size_t i = 0; i < n; i++) {
call    0 returned 4
        -: 2443:				     for (size_t j = 0; j < n; j++) {
        -: 2444:				         int sx = (x_val[i] > x_val[j]) - (x_val[i] < x_val[j]);
        -: 2445:				         int sy = (y_val[i] > y_val[j]) - (y_val[i] < y_val[j]);
        -: 2446:				         ans += (double)(sx * sy);
        -: 2447:				     }
        -: 2448:				 }
        -: 2449:			} else {
        -: 2450:				 double mean_x = 0.0, mean_y = 0.0, cov_sum = 0.0;
        7: 2451:				 if (strcmp(method, "spearman") == 0) {
        7: 2452:				     // Spearman: Rank the data first, then run standard covariance
        7: 2453:				     double *restrict rx = (double*)safemalloc(n * sizeof(double));
        -: 2454:				     double *restrict ry = (double*)safemalloc(n * sizeof(double));
        -: 2455:				     // Uses your existing rank_data() helper from LikeR.xs
        -: 2456:				     rank_data(x_val, rx, n);
        -: 2457:				     rank_data(y_val, ry, n);
        7: 2458:				     for (size_t i = 0; i < n; i++) {
        7: 2459:				         double dx = rx[i] - mean_x;
        7: 2460:				         mean_x += dx / (i + 1);
        7: 2461:				         double dy = ry[i] - mean_y;
        7: 2462:				         mean_y += dy / (i + 1);
        7: 2463:				         cov_sum += dx * (ry[i] - mean_y);
        7: 2464:				     }
        7: 2465:				     Safefree(rx); Safefree(ry);
        7: 2466:				 } else { 
        -: 2467:				     // Pearson: Welford's Single-Pass Covariance Algorithm
        7: 2468:				     for (size_t i = 0; i < n; i++) {
        7: 2469:				         double dx = x_val[i] - mean_x;
        7: 2470:				         mean_x += dx / (i + 1);
        7: 2471:				         double dy = y_val[i] - mean_y;
        7: 2472:				         mean_y += dy / (i + 1);
        -: 2473:				         cov_sum += dx * (y_val[i] - mean_y);
        7: 2474:				     }
        7: 2475:				 }
        7: 2476:				 
        7: 2477:				 // Unbiased Sample Covariance (N - 1) for Pearson & Spearman
        -: 2478:				 ans = cov_sum / (n - 1);
        -: 2479:			}
        -: 2480:			Safefree(x_val); Safefree(y_val);
        -: 2481:			RETVAL = newSVnv(ans);
        -: 2482:		}
       7*: 2483:	}
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 2483-block  0
call    2 never executed
        -: 2484:	OUTPUT:
       26: 2485:	  RETVAL
        7: 2485-block  0
       19: 2485-block  1
       26: 2485-block  2
branch  0 taken 19
branch  1 taken 7 (fallthrough)
       19: 2486:
       19: 2486-block  0
call    0 returned 19
       19: 2487:SV* glm(...)
       19: 2488:CODE:
branch  0 taken 7 (fallthrough)
branch  1 taken 12
        7: 2488-block  0
call    2 returned 7
       12: 2489:{
       12: 2489-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 5
        7: 2489-block  1
        5: 2490:	const char *restrict formula  = NULL;
        5: 2490-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 2490-block  1
call    2 returned 5
    #####: 2491:	SV *restrict data_sv = NULL;
    %%%%%: 2491-block  0
call    0 never executed
        -: 2492:	const char *restrict family_str = "gaussian";
       7*: 2493:	char f_cpy[512];
        7: 2493-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 2493-block  1
call    2 never executed
       7*: 2494:	char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
        7: 2494-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2494-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 7
    %%%%%: 2494-block  2
call    4 never executed
        -: 2495:
        7: 2496:	// Dynamic Term Arrays
        7: 2497:	char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
       7*: 2498:	bool *restrict is_dummy = NULL;
        7: 2498-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 2498-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    %%%%%: 2498-block  2
call    4 never executed
        -: 2499:	char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
        -: 2500:	unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
        7: 2501:	size_t n = 0, valid_n = 0, i;
        7: 2501-block  0
call    0 returned 7
call    1 returned 7
        7: 2502:	bool has_intercept = TRUE, converged = FALSE, boundary = FALSE;
call    0 returned 7
call    1 returned 7
        7: 2503:	unsigned int iter = 0, max_iter = 25, final_rank = 0, df_res = 0;
call    0 returned 7
call    1 returned 7
        -: 2504:	double deviance_old = 0.0, deviance_new = 0.0, null_dev = 0.0, aic = 0.0;
        7: 2505:	double dispersion = 0.0, epsilon = 1e-8;
       98: 2506:
       91: 2506-block  0
branch  0 taken 65 (fallthrough)
branch  1 taken 26
       65: 2506-block  1
       91: 2506-block  2
       98: 2506-block  3
branch  2 taken 91 (fallthrough)
branch  3 taken 7
       91: 2506-block  4
branch  4 taken 91
branch  5 taken 0 (fallthrough)
        7: 2507:	char **restrict row_names = NULL;
        -: 2508:	char **restrict valid_row_names = NULL;
        7: 2509:	HV **restrict row_hashes = NULL;
       7*: 2510:	HV *restrict data_hoa = NULL;
        7: 2510-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 2510-block  1
call    2 never executed
        7: 2511:	SV *restrict ref = NULL;
        7: 2512:
        -: 2513:	double *restrict X = NULL, *restrict Y = NULL, *restrict mu = NULL, *restrict eta = NULL;
       7*: 2514:	double *restrict W = NULL, *restrict Z = NULL, *restrict beta = NULL, *restrict beta_old = NULL;
        7: 2514-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 2514-block  1
        7: 2515:	bool *restrict aliased = NULL;
        7: 2515-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2515-block  1
call    2 returned 7
        -: 2516:	double *restrict XtWX = NULL, *restrict XtWZ = NULL;
        7: 2517:
        7: 2517-block  0
call    0 returned 7
       20: 2518:	HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
       20: 2518-block  0
branch  0 taken 13
branch  1 taken 7 (fallthrough)
       13: 2519:	AV *restrict terms_av;
       13: 2519-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    #####: 2520:	HE *restrict entry;
    #####: 2521:
    %%%%%: 2521-block  0
call    0 never executed
call    1 never executed
        -: 2522:	if (items % 2 != 0) croak("Usage: glm(formula => 'am ~ wt + hp', data => \\%mtcars)");
      13*: 2523:
       13: 2523-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2523-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 13
    #####: 2524:	for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
    %%%%%: 2524-block  0
call    0 never executed
    #####: 2525:	  const char *restrict key = SvPV_nolen(ST(i_arg));
        -: 2526:	  SV *restrict val = ST(i_arg + 1);
       13: 2527:	  if      (strEQ(key, "formula")) formula = SvPV_nolen(val);
       13: 2528:	  else if (strEQ(key, "data"))    data_sv = val;
       13: 2528-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    #####: 2529:	  else if (strEQ(key, "family"))  family_str = SvPV_nolen(val);
    #####: 2530:	  else croak("glm: unknown argument '%s'", key);
    #####: 2531:	}        
    %%%%%: 2531-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2531-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2531-block  2
    #####: 2532:	if (!formula) croak("glm: formula is required");
    %%%%%: 2532-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2532-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2532-block  2
        -: 2533:	if (!data_sv || !SvROK(data_sv)) croak("glm: data is required and must be a reference");
    #####: 2534:
    %%%%%: 2534-block  0
call    0 never executed
    #####: 2535:	bool is_binomial = (strcmp(family_str, "binomial") == 0);
call    0 never executed
    #####: 2536:	bool is_gaussian = (strcmp(family_str, "gaussian") == 0);
    #####: 2537:	if (!is_binomial && !is_gaussian) croak("glm: unsupported family '%s'", family_str);
call    0 never executed
    #####: 2538:
        -: 2539:	// --- Formula Parsing & Expansion ---
       13: 2540:	Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
      13*: 2541:	Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
       13: 2541-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    %%%%%: 2541-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2541-block  2
       13: 2542:	Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
       13: 2542-block  0
call    0 returned 13
        -: 2543:
       13: 2544:	src = (char*)formula; dst = f_cpy;
       13: 2544-block  0
call    0 returned 13
        -: 2545:	while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
        -: 2546:	*dst = '\0';
       27: 2547:
        7: 2547-block  0
       20: 2547-block  1
       27: 2547-block  2
branch  0 taken 20
branch  1 taken 7 (fallthrough)
       20: 2548:	tilde = strchr(f_cpy, '~');
       39: 2549:	if (!tilde) croak("glm: invalid formula, missing '~'");
       20: 2549-block  0
       19: 2549-block  1
       39: 2549-block  2
branch  0 taken 19
branch  1 taken 20 (fallthrough)
      19*: 2550:	*tilde = '\0';
       19: 2550-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 19
    %%%%%: 2550-block  1
        -: 2551:	lhs = f_cpy; rhs = tilde + 1;
       20: 2552:
       20: 2552-block  0
branch  0 taken 20 (fallthrough)
branch  1 taken 0
       20: 2552-block  1
call    2 returned 20
        -: 2553:	if (strstr(rhs, "-1")) has_intercept = FALSE;
        7: 2554:	if (has_intercept) terms[num_terms++] = savepv("Intercept");
        -: 2555:
        -: 2556:	chunk = strtok(rhs, "+");
        7: 2557:	while (chunk != NULL) {
        7: 2558:	  if (num_terms >= term_cap - 3) {
        7: 2558-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2559:		   term_cap *= 2;
       7*: 2560:		   Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
        7: 2560-block  0
call    0 returned 7
branch  1 taken 0 (fallthrough)
branch  2 taken 7
    %%%%%: 2560-block  1
call    3 never executed
        7: 2561:	  }
        7: 2561-block  0
call    0 returned 7
        7: 2562:	  if (strcmp(chunk, "1") == 0 || strcmp(chunk, "-1") == 0) {
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2563:		   chunk = strtok(NULL, "+");
        7: 2563-block  0
call    0 returned 7
        7: 2564:		   continue;
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2564-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 5
        2: 2565:	  }
        2: 2566:	  char *restrict star = strchr(chunk, '*');
        2: 2566-block  0
call    0 returned 2
       2*: 2567:	  if (star) {
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 2567-block  0
call    2 never executed
        2: 2567-block  1
call    3 returned 2
      122: 2568:		   *star = '\0';
      122: 2568-block  0
branch  0 taken 120
branch  1 taken 2 (fallthrough)
      120: 2569:		   char *restrict left = chunk; char *restrict right = star + 1;
      120: 2570:		   char *restrict c_l = strchr(left, '^'); if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
      120: 2570-block  0
call    0 returned 120
        -: 2571:		   char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
        5: 2572:		   
        5: 2572-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 2572-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 2573:		   terms[num_terms++] = savepv(left);
        5: 2573-block  0
call    0 returned 5
       5*: 2574:		   terms[num_terms++] = savepv(right);
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 2574-block  0
call    2 never executed
        5: 2574-block  1
call    3 returned 5
branch  4 taken 0 (fallthrough)
branch  5 taken 5
    %%%%%: 2574-block  2
call    6 never executed
        5: 2574-block  3
call    7 returned 5
        5: 2575:		   size_t inter_len = strlen(left) + strlen(right) + 2;
      165: 2576:		   terms[num_terms] = (char*)safemalloc(inter_len);
      165: 2576-block  0
call    0 returned 165
branch  1 taken 160
branch  2 taken 5 (fallthrough)
        -: 2577:		   snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
      160: 2578:	  } else {
      160: 2578-block  0
call    0 returned 160
call    1 returned 160
      160: 2579:		   char *restrict c_chunk = strchr(chunk, '^'); 
call    0 returned 160
      160: 2580:		   if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
        -: 2581:		   terms[num_terms++] = savepv(chunk);
    #####: 2582:	  }
    %%%%%: 2582-block  0
call    0 never executed
        -: 2583:	  chunk = strtok(NULL, "+");
    #####: 2584:	}
    %%%%%: 2584-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 2585:
    #####: 2586:	for (i = 0; i < num_terms; i++) {
    %%%%%: 2586-block  0
call    0 never executed
    #####: 2587:	  bool found = FALSE;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2587-block  0
call    2 never executed
    %%%%%: 2587-block  1
call    3 never executed
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 2587-block  2
call    6 never executed
    %%%%%: 2587-block  3
call    7 never executed
    #####: 2588:	  for (size_t j = 0; j < num_uniq; j++) {
    %%%%%: 2588-block  0
    %%%%%: 2588-block  1
branch  0 never executed
branch  1 never executed
    #####: 2589:		   if (strcmp(terms[i], uniq_terms[j]) == 0) { found = TRUE; break; }
    %%%%%: 2589-block  0
call    0 never executed
    #####: 2590:	  }
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2590-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2590-block  1
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 2591:	  if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
    #####: 2592:	}
    #####: 2593:	p = num_uniq;
    %%%%%: 2593-block  0
call    0 never executed
        -: 2594:
    #####: 2595:	// --- Data Extraction ---
    %%%%%: 2595-block  0
    %%%%%: 2595-block  1
call    0 never executed
    %%%%%: 2595-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 2596:	ref = SvRV(data_sv);
    %%%%%: 2596-block  0
call    0 never executed
call    1 never executed
    #####: 2597:	if (SvTYPE(ref) == SVt_PVHV) {
call    0 never executed
        -: 2598:		HV*restrict hv = (HV*)ref;
        -: 2599:		if (hv_iterinit(hv) == 0) croak("glm: Data hash is empty");
    #####: 2600:		entry = hv_iternext(hv);
    %%%%%: 2600-block  0
call    0 never executed
        -: 2601:		if (entry) {
        -: 2602:			SV*restrict val = hv_iterval(hv, entry);
       27: 2603:			if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
        7: 2603-block  0
       20: 2603-block  1
       27: 2603-block  2
branch  0 taken 20
branch  1 taken 7 (fallthrough)
       20: 2604:				 data_hoa = hv;
       20: 2604-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 20
    #####: 2605:				 n = av_len((AV*)SvRV(val)) + 1;
    #####: 2606:				 Newx(row_names, n, char*);
    %%%%%: 2606-block  0
call    0 never executed
call    1 never executed
    #####: 2607:				 for(i = 0; i < n; i++) {
call    0 never executed
call    1 never executed
        -: 2608:				     char buf[32]; snprintf(buf, sizeof(buf), "%lu", i+1);
       20: 2609:				     row_names[i] = savepv(buf);
       20: 2609-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 13
        7: 2610:				 }
        7: 2610-block  0
call    0 returned 7
        -: 2611:			} else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
       13: 2612:				 n = hv_iterinit(hv);
       13: 2612-block  0
call    0 returned 13
branch  1 taken 1 (fallthrough)
branch  2 taken 12
        1: 2613:				 Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
       1*: 2614:				 i = 0;
        1: 2614-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 2614-block  1
call    2 never executed
        1: 2614-block  2
call    3 returned 1
       61: 2615:				 while ((entry = hv_iternext(hv))) {
       60: 2615-block  0
       61: 2615-block  1
branch  0 taken 60
branch  1 taken 1 (fallthrough)
       60: 2616:				     I32 len;
       60: 2616-block  0
call    0 returned 60
       60: 2617:				     row_names[i] = savepv(hv_iterkey(entry, &len));
branch  0 taken 60 (fallthrough)
branch  1 taken 0
       60: 2618:				     row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
       90: 2619:				     i++;
       60: 2619-block  0
       30: 2619-block  1
       90: 2619-block  2
branch  0 taken 88
branch  1 taken 2 (fallthrough)
       88: 2620:				 }
       88: 2620-block  0
branch  0 taken 58 (fallthrough)
branch  1 taken 30
       58: 2620-block  1
        -: 2621:			} else croak("glm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
       60: 2622:		}
       60: 2622-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 58
       2*: 2623:	} else if (SvTYPE(ref) == SVt_PVAV) {
        2: 2623-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 2623-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2623-block  2
call    4 never executed
    %%%%%: 2623-block  3
call    5 never executed
        2: 2624:	  AV*restrict av = (AV*)ref;
        2: 2624-block  0
call    0 returned 2
        -: 2625:	  n = av_len(av) + 1;
       60: 2626:	  Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
       60: 2626-block  0
call    0 returned 60
        -: 2627:	  for (i = 0; i < n; i++) {
        -: 2628:		   SV**restrict val = av_fetch(av, i, 0);
        1: 2629:		   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
        1: 2629-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        2: 2630:		       row_hashes[i] = (HV*)SvRV(*val);
        1: 2630-block  0
        1: 2630-block  1
        2: 2630-block  2
branch  0 taken 1
branch  1 taken 1 (fallthrough)
        2: 2631:		       char buf[32]; snprintf(buf, sizeof(buf), "%lu", i + 1);
        1: 2631-block  0
        1: 2631-block  1
        2: 2631-block  2
branch  0 taken 1
branch  1 taken 1 (fallthrough)
        1: 2632:		       row_names[i] = savepv(buf);
        1: 2632-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 2633:		   } else {
        1: 2633-block  0
        -: 2634:		       for (size_t k = 0; k < i; k++) Safefree(row_names[k]);
        -: 2635:		       Safefree(row_names); Safefree(row_hashes);
        -: 2636:		       croak("glm: Array values must be HashRefs (AoH)");
        2: 2637:		   }
        1: 2637-block  0
        2: 2637-block  1
branch  0 taken 1
branch  1 taken 1 (fallthrough)
        1: 2638:	  }
        1: 2638-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 2639:	} else croak("glm: Data must be an Array or Hash reference");
    #####: 2640:
    %%%%%: 2640-block  0
call    0 never executed
call    1 never executed
    #####: 2641:	// --- Categorical Expansion ---
call    0 never executed
call    1 never executed
        -: 2642:	for (size_t j = 0; j < p; j++) {
        1: 2643:	  if (p_exp + 32 >= exp_cap) {
        1: 2644:		   exp_cap *= 2;
        1: 2644-block  0
call    0 returned 1
        1: 2645:		   Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
        1: 2646:		   Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
call    0 returned 1
call    1 returned 1
        1: 2647:	  }
        -: 2648:	  if (strcmp(uniq_terms[j], "Intercept") == 0) {
        3: 2649:		   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = FALSE; p_exp++; continue;
        1: 2649-block  0
        2: 2649-block  1
call    0 returned 2
        3: 2649-block  2
branch  1 taken 2
branch  2 taken 1 (fallthrough)
        1: 2650:	  }
        1: 2650-block  0
call    0 returned 1
        -: 2651:	  if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
    #####: 2652:		   char **restrict levels = NULL; size_t num_levels = 0, levels_cap = 8;
    %%%%%: 2652-block  0
call    0 never executed
call    1 never executed
        -: 2653:		   Newx(levels, levels_cap, char*);
        -: 2654:		   for (i = 0; i < n; i++) {
       12: 2655:		       char*restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
       12: 2655-block  0
call    0 returned 12
        -: 2656:		       if (str_val) {
        -: 2657:		           bool found = FALSE;
        7: 2658:		           for (size_t l = 0; l < num_levels; l++) {
        -: 2659:		               if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; }
       7*: 2660:		           }
        7: 2660-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 2660-block  1
call    2 never executed
        7: 2660-block  2
call    3 returned 7
branch  4 taken 0 (fallthrough)
branch  5 taken 7
    %%%%%: 2660-block  3
call    6 never executed
        7: 2660-block  4
call    7 returned 7
       7*: 2661:		           if (!found) {
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 2661-block  0
call    2 never executed
        7: 2661-block  1
call    3 returned 7
        -: 2662:		               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
        -: 2663:		               levels[num_levels++] = savepv(str_val);
      287: 2664:		           }
      280: 2664-block  0
      287: 2664-block  1
branch  0 taken 280
branch  1 taken 7 (fallthrough)
      280: 2665:		           Safefree(str_val);
      280: 2665-block  0
call    0 returned 280
     280*: 2666:		       }
branch  0 taken 0 (fallthrough)
branch  1 taken 280
    %%%%%: 2666-block  0
call    2 never executed
        -: 2667:		   }
      280: 2668:		   if (num_levels > 0) {
      280: 2669:		       for (size_t l1 = 0; l1 < num_levels - 1; l1++) {
      280: 2669-block  0
call    0 returned 280
     1060: 2670:		           for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
      780: 2670-block  0
     1060: 2670-block  1
branch  0 taken 780
branch  1 taken 280 (fallthrough)
      780: 2671:		               if (strcmp(levels[l1], levels[l2]) > 0) {
      780: 2671-block  0
branch  0 taken 280 (fallthrough)
branch  1 taken 500
      280: 2672:		                   char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp;
      280: 2672-block  0
      500: 2673:		               }
      500: 2673-block  0
branch  0 taken 60 (fallthrough)
branch  1 taken 440
       60: 2674:		           }
       60: 2674-block  0
call    0 returned 60
       60: 2675:		       }
branch  0 taken 60 (fallthrough)
branch  1 taken 0
       60: 2676:		       for (size_t l = 1; l < num_levels; l++) {
       60: 2676-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 30
       30: 2676-block  1
       30: 2676-block  2
       60: 2677:		           if (p_exp >= exp_cap) {
       60: 2677-block  0
call    0 returned 60
    #####: 2678:		               exp_cap *= 2;
    %%%%%: 2678-block  0
        -: 2679:		               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
      440: 2680:		               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
      440: 2680-block  0
call    0 returned 440
     440*: 2681:		           }
branch  0 taken 0 (fallthrough)
branch  1 taken 440
    %%%%%: 2681-block  0
        -: 2682:		           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
        -: 2683:		           exp_terms[p_exp] = (char*)safemalloc(t_len);
     280*: 2684:		           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
      280: 2684-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 280
    %%%%%: 2684-block  1
call    2 never executed
call    3 never executed
      280: 2685:		           is_dummy[p_exp] = TRUE; dummy_base[p_exp] = savepv(uniq_terms[j]); dummy_level[p_exp] = savepv(levels[l]);
     1060: 2686:		           p_exp++;
      280: 2686-block  0
      780: 2686-block  1
     1060: 2686-block  2
branch  0 taken 780
branch  1 taken 280 (fallthrough)
      280: 2687:		       }
      280: 2688:		       for (size_t l = 0; l < num_levels; l++) Safefree(levels[l]);
      280: 2689:		       Safefree(levels);
      280: 2689-block  0
call    0 returned 280
        -: 2690:		   } else {
        7: 2691:		       Safefree(levels); exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
        7: 2691-block  0
call    0 returned 7
        7: 2692:		   }
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    #####: 2693:	  } else {
    %%%%%: 2693-block  0
call    0 never executed
call    1 never executed
call    2 never executed
branch  3 never executed (fallthrough)
branch  4 never executed
    %%%%%: 2693-block  1
call    5 never executed
    #####: 2694:		   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
    %%%%%: 2694-block  0
call    0 never executed
        -: 2695:	  }
        -: 2696:	}
        7: 2697:	p = p_exp;
        7: 2697-block  0
call    0 returned 7
call    1 returned 7
        7: 2698:
call    0 returned 7
call    1 returned 7
        7: 2699:	Newx(X, n * p, double); Newx(Y, n, double);
call    0 returned 7
call    1 returned 7
        7: 2700:	Newx(valid_row_names, n, char*);
call    0 returned 7
        7: 2701:
call    0 returned 7
call    1 returned 7
       27: 2702:	// --- Listwise Deletion ---
       20: 2702-block  0
       27: 2702-block  1
branch  0 taken 20
branch  1 taken 7 (fallthrough)
        -: 2703:	for (size_t i = 0; i < n; i++) {
        7: 2704:		double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
      287: 2705:		if (isnan(y_val)) { Safefree(row_names[i]); continue; }
        7: 2705-block  0
      280: 2705-block  1
      287: 2705-block  2
branch  0 taken 280
branch  1 taken 7 (fallthrough)
        7: 2706:
      287: 2707:		bool row_ok = TRUE;
        7: 2707-block  0
      280: 2707-block  1
      287: 2707-block  2
branch  0 taken 280
branch  1 taken 7 (fallthrough)
      280: 2708:		double *restrict row_x = (double*)safemalloc(p * sizeof(double));
      280: 2708-block  0
branch  0 taken 32 (fallthrough)
branch  1 taken 248
      32*: 2709:		for (size_t j = 0; j < p; j++) {
       32: 2709-block  0
branch  0 taken 32 (fallthrough)
branch  1 taken 0
       32: 2709-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 32
    %%%%%: 2709-block  2
call    4 never executed
       32: 2710:			if (strcmp(exp_terms[j], "Intercept") == 0) {
       32: 2711:				 row_x[j] = 1.0;
       32: 2712:			} else if (is_dummy[j]) {
       32: 2713:				 char* str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
       32: 2713-block  0
branch  0 taken 19 (fallthrough)
branch  1 taken 13
       19: 2713-block  1
       13: 2714:				 if (str_val) {
       13: 2714-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2714-block  1
    #####: 2715:				     row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
    %%%%%: 2715-block  0
       32: 2716:				     Safefree(str_val);
       32: 2716-block  0
        -: 2717:				 } else { row_ok = FALSE; break; }
      248: 2718:			} else {
      248: 2719:				 row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
      248: 2719-block  0
        -: 2720:				 if (isnan(row_x[j])) { row_ok = FALSE; break; }
        -: 2721:			}
        -: 2722:		}
       21: 2723:		if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
        7: 2723-block  0
       14: 2723-block  1
       21: 2723-block  2
branch  0 taken 21
branch  1 taken 0 (fallthrough)
      805: 2724:		Y[valid_n] = y_val;
       21: 2724-block  0
      784: 2724-block  1
      805: 2724-block  2
branch  0 taken 784
branch  1 taken 21 (fallthrough)
      784: 2725:		for (size_t j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
      784: 2725-block  0
branch  0 taken 288 (fallthrough)
branch  1 taken 496
      288: 2726:		valid_row_names[valid_n] = row_names[i];
      288: 2727:		valid_n++;
     288*: 2728:		Safefree(row_x);
      288: 2728-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 288
    %%%%%: 2728-block  1
      288: 2729:	}
      288: 2730:	Safefree(row_names); 
      288: 2730-block  0
        -: 2731:	if (valid_n <= p) {
      496: 2732:	  Safefree(X); Safefree(Y); Safefree(valid_row_names); if (row_hashes) Safefree(row_hashes);
      496: 2733:	  croak("glm: 0 degrees of freedom (too many NAs or parameters > observations)");
      496: 2733-block  0
        -: 2734:	}
        -: 2735:	// --- R glm.fit IRLS Implementation ---
        -: 2736:	mu = (double*)safemalloc(valid_n * sizeof(double)); eta = (double*)safemalloc(valid_n * sizeof(double));
      261: 2737:	W = (double*)safemalloc(valid_n * sizeof(double)); Z = (double*)safemalloc(valid_n * sizeof(double));
       21: 2737-block  0
       61: 2737-block  1
      179: 2737-block  2
      240: 2737-block  3
branch  0 taken 179
branch  1 taken 61 (fallthrough)
       61: 2737-block  4
       82: 2737-block  5
branch  2 taken 61
branch  3 taken 21 (fallthrough)
      805: 2738:	beta = (double*)safemalloc(p * sizeof(double)); beta_old = (double*)safemalloc(p * sizeof(double));
       21: 2738-block  0
      784: 2738-block  1
      805: 2738-block  2
branch  0 taken 784
branch  1 taken 21 (fallthrough)
      784: 2739:	aliased = (bool*)safemalloc(p * sizeof(bool));
     3016: 2740:	XtWX = (double*)safemalloc(p * p * sizeof(double)); XtWZ = (double*)safemalloc(p * sizeof(double));
      784: 2740-block  0
     2232: 2740-block  1
     3016: 2740-block  2
branch  0 taken 2232
branch  1 taken 784 (fallthrough)
     2232: 2741:	for (i = 0; i < p; i++) { beta[i] = 0.0; beta_old[i] = 0.0; }
     2232: 2742:	// Initialize (mustart / etastart equivalent)
     8688: 2743:	double sum_y = 0.0;
     2232: 2743-block  0
     6456: 2743-block  1
     8688: 2743-block  2
branch  0 taken 6456
branch  1 taken 2232 (fallthrough)
        -: 2744:	for (i = 0; i < valid_n; i++) sum_y += Y[i];
        -: 2745:	double mean_y = sum_y / valid_n;
       21: 2746:	for (i = 0; i < valid_n; i++) {
       21: 2746-block  0
call    0 returned 21
       82: 2747:	  if (is_binomial) {
       61: 2747-block  0
       82: 2747-block  1
branch  0 taken 61
branch  1 taken 21 (fallthrough)
      61*: 2748:		   if (Y[i] < 0.0 || Y[i] > 1.0) croak("glm: binomial family requires response between 0 and 1");
       61: 2748-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 61
    %%%%%: 2748-block  1
       61: 2749:		   mu[i] = (Y[i] + 0.5) / 2.0; 
      240: 2750:		   eta[i] = log(mu[i] / (1.0 - mu[i]));
       61: 2750-block  0
      179: 2750-block  1
branch  0 taken 179 (fallthrough)
branch  1 taken 0
      179: 2750-block  2
      179: 2750-block  3
      240: 2750-block  4
branch  2 taken 179
branch  3 taken 61 (fallthrough)
       61: 2751:		   double dev = 0.0;
       61: 2751-block  0
        -: 2752:		   if (Y[i] == 0.0)      dev = -2.0 * log(1.0 - mu[i]);
        -: 2753:		   else if (Y[i] == 1.0) dev = -2.0 * log(mu[i]);
        -: 2754:		   else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
       21: 2755:		   deviance_old += dev;
      231: 2756:	  } else { 
       21: 2756-block  0
      210: 2756-block  1
      231: 2756-block  2
branch  0 taken 210
branch  1 taken 21 (fallthrough)
      210: 2757:		   mu[i] = mean_y; // R gaussian init
     8050: 2758:		   eta[i] = mu[i]; 
      210: 2758-block  0
     7840: 2758-block  1
     8050: 2758-block  2
branch  0 taken 7840
branch  1 taken 210 (fallthrough)
     7840: 2759:	  }
    30160: 2760:	}
     7840: 2760-block  0
    22320: 2760-block  1
branch  0 taken 22320 (fallthrough)
branch  1 taken 0
    22320: 2760-block  2
    22320: 2760-block  3
    30160: 2760-block  4
branch  2 taken 22320
branch  3 taken 7840 (fallthrough)
     7840: 2761:	// IRLS Loop
     7840: 2762:	for (iter = 1; iter <= max_iter; iter++) {
     7840: 2762-block  0
branch  0 taken 2880 (fallthrough)
branch  1 taken 4960
     2880: 2763:		for (i = 0; i < valid_n; i++) {
        -: 2764:			if (is_binomial) {
    2880*: 2765:				 double varmu = mu[i] * (1.0 - mu[i]);
     2880: 2765-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2880
    %%%%%: 2765-block  1
    2880*: 2766:				 double mu_eta = varmu; // Link derivative for logit
     2880: 2766-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2880
    %%%%%: 2766-block  1
        -: 2767:				 if (varmu < 1e-10) varmu = 1e-10;
     2880: 2768:				 Z[i] = eta[i] + (Y[i] - mu[i]) / mu_eta;
     2880: 2769:				 W[i] = (mu_eta * mu_eta) / varmu; 
     2880: 2769-block  0
branch  0 taken 1710 (fallthrough)
branch  1 taken 1170
     1710: 2769-block  1
     1170: 2770:			} else { 
     1170: 2770-block  0
branch  0 taken 1170 (fallthrough)
branch  1 taken 0
     1170: 2770-block  1
    #####: 2771:				 W[i] = 1.0; 
    %%%%%: 2771-block  0
     2880: 2772:				 Z[i] = Y[i]; 
     2880: 2772-block  0
        -: 2773:			}
     4960: 2774:		}
     4960: 2775:		// Formulate XtWX and XtWZ
     4960: 2776:		for (i = 0; i < p; i++) { XtWZ[i] = 0.0; for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
     4960: 2776-block  0
        -: 2777:		for (size_t k = 0; k < valid_n; k++) {
        -: 2778:			double w = W[k], z = Z[k];
        -: 2779:			for (i = 0; i < p; i++) {
      210: 2780:				 XtWZ[i] += X[k * p + i] * w * z;
      210: 2780-block  0
branch  0 taken 90 (fallthrough)
branch  1 taken 120
       90: 2780-block  1
branch  2 taken 10 (fallthrough)
branch  3 taken 80
       10: 2780-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 10
      200: 2781:				 double xw = X[k * p + i] * w;
      200: 2781-block  0
        -: 2782:				 for (size_t j = 0; j < p; j++) XtWX[i * p + j] += xw * X[k * p + j];
        -: 2783:			}
       10: 2784:		}
       40: 2785:		final_rank = sweep_matrix_ols(XtWX, p, aliased);
       10: 2785-block  0
       30: 2785-block  1
       40: 2785-block  2
branch  0 taken 30
branch  1 taken 10 (fallthrough)
        -: 2786:		for (i = 0; i < p; i++) {
        -: 2787:			if (aliased[i]) { beta[i] = NAN; } else {
       21: 2788:				 double sum = 0.0;
       21: 2788-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 14
        7: 2789:				 for (size_t j = 0; j < p; j++) if (!aliased[j]) sum += XtWX[i * p + j] * XtWZ[j];
        7: 2789-block  0
        -: 2790:				 beta[i] = sum;
       14: 2791:			}
       55: 2792:		}
       14: 2792-block  0
       41: 2792-block  1
       55: 2792-block  2
branch  0 taken 41
branch  1 taken 14 (fallthrough)
        -: 2793:		// Calculate updated ETA, MU, and Deviance (with Step-Halving)
        -: 2794:		boundary = FALSE;
       85: 2795:		for (unsigned short int half = 0; half < 10; half++) {
        7: 2795-block  0
       20: 2795-block  1
       58: 2795-block  2
       78: 2795-block  3
branch  0 taken 58
branch  1 taken 20 (fallthrough)
       20: 2795-block  4
       27: 2795-block  5
branch  2 taken 20
branch  3 taken 7 (fallthrough)
      287: 2796:			deviance_new = 0.0;
        7: 2796-block  0
      280: 2796-block  1
      287: 2796-block  2
branch  0 taken 280
branch  1 taken 7 (fallthrough)
      280: 2797:			for (i = 0; i < valid_n; i++) {
      280: 2797-block  0
branch  0 taken 32 (fallthrough)
branch  1 taken 248
       32: 2797-block  1
      248: 2797-block  2
     280*: 2798:				 double linear_pred = 0.0;
      280: 2798-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 280
    %%%%%: 2798-block  1
     1060: 2799:				 for (size_t j = 0; j < p; j++) if (!aliased[j]) linear_pred += X[i * p + j] * beta[j];
      280: 2799-block  0
      780: 2799-block  1
     1060: 2799-block  2
branch  0 taken 780
branch  1 taken 280 (fallthrough)
      780: 2800:				 eta[i] = linear_pred;
     3000: 2801:				 if (is_binomial) {
      780: 2801-block  0
     2220: 2801-block  1
     3000: 2801-block  2
branch  0 taken 2220
branch  1 taken 780 (fallthrough)
        -: 2802:				     mu[i] = 1.0 / (1.0 + exp(-eta[i]));
        -: 2803:				     // Boundary enforcement
        7: 2804:				     if (mu[i] < 10 * DBL_EPSILON) mu[i] = 10 * DBL_EPSILON;
        7: 2804-block  0
call    0 returned 7
        -: 2805:				     if (mu[i] > 1.0 - 10 * DBL_EPSILON) mu[i] = 1.0 - 10 * DBL_EPSILON;
        7: 2806:				     
      287: 2807:				     double dev = 0.0;
      280: 2807-block  0
      287: 2807-block  1
branch  0 taken 280
branch  1 taken 7 (fallthrough)
      280: 2808:				     if (Y[i] == 0.0)      dev = -2.0 * log(1.0 - mu[i]);
      280: 2808-block  0
branch  0 taken 32 (fallthrough)
branch  1 taken 248
       32: 2809:				     else if (Y[i] == 1.0) dev = -2.0 * log(mu[i]);
       32: 2809-block  0
branch  0 taken 19 (fallthrough)
branch  1 taken 13
       19: 2809-block  1
       13: 2810:				     else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
       13: 2810-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2810-block  1
    #####: 2811:				     deviance_new += dev;
    %%%%%: 2811-block  0
        -: 2812:				 } else {
      248: 2813:				     mu[i] = eta[i];
      248: 2814:				     double res = Y[i] - mu[i];
      248: 2814-block  0
        -: 2815:				     deviance_new += res * res;
        -: 2816:				 }
        -: 2817:			}
        7: 2818:			// Step halving divergence check
        7: 2818-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 2819:			if (!is_binomial || deviance_new <= deviance_old + 1e-7 || !isfinite(deviance_new)) {
        6: 2820:				 continue; 
        6: 2820-block  0
        1: 2821:			}
        1: 2821-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 2822:			
        1: 2822-block  0
        -: 2823:			boundary = TRUE;
        -: 2824:			for (size_t j = 0; j < p; j++) beta[j] = (beta[j] + beta_old[j]) / 2.0;
        7: 2825:		}
        7: 2825-block  0
call    0 returned 7
call    1 returned 7
call    2 returned 7
call    3 returned 7
        7: 2826:		// Convergence Check
       7*: 2827:		if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) { 
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 2827-block  0
        6: 2827-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 2827-block  2
    %%%%%: 2827-block  3
        6: 2827-block  4
      287: 2828:			converged = TRUE; break; 
        7: 2828-block  0
      287: 2828-block  1
branch  0 taken 280
branch  1 taken 7 (fallthrough)
      280: 2829:		}
      280: 2830:		deviance_old = deviance_new;
      280: 2830-block  0
branch  0 taken 32 (fallthrough)
branch  1 taken 248
        -: 2831:		for (size_t j = 0; j < p; j++) beta_old[j] = beta[j];
       32: 2832:	}
       32: 2833:	// Final accurate calculation of W for standard errors
       32: 2833-block  0
branch  0 taken 19 (fallthrough)
branch  1 taken 13
       19: 2833-block  1
       13: 2834:	for (i = 0; i < p; i++) { for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
       13: 2834-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13: 2834-block  1
    #####: 2835:	for (size_t k = 0; k < valid_n; k++) {
    %%%%%: 2835-block  0
       32: 2836:	  double w = is_binomial ? (mu[k] * (1.0 - mu[k])) : 1.0;
       32: 2836-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 19
       13: 2836-block  1
       19: 2836-block  2
       32: 2836-block  3
        -: 2837:	  if (w < 1e-10) w = 1e-10;
      280: 2838:	  for (i = 0; i < p; i++) {
      280: 2838-block  0
call    0 returned 280
call    1 returned 280
      280: 2839:		   double xw = X[k * p + i] * w;
call    0 returned 280
call    1 returned 280
      280: 2840:		   for (size_t j = 0; j < p; j++) XtWX[i * p + j] += xw * X[k * p + j];
call    0 returned 280
        -: 2841:	  }
        7: 2842:	}
        7: 2842-block  0
call    0 returned 7
        -: 2843:	final_rank = sweep_matrix_ols(XtWX, p, aliased);
        7: 2844:	// --- Null Deviance Calculation ---
call    0 returned 7
call    1 returned 7
       27: 2845:	double wtdmu = mean_y; // Since weights are 1.0 initially
       27: 2845-block  0
branch  0 taken 20
branch  1 taken 7 (fallthrough)
       20: 2846:	for (i = 0; i < valid_n; i++) {
       20: 2846-block  0
call    0 returned 20
call    1 returned 20
       20: 2847:	  if (is_binomial) {
call    0 returned 20
call    1 returned 20
        -: 2848:		   if (Y[i] == 0.0)      null_dev += -2.0 * log(1.0 - wtdmu);
       20: 2849:		   else if (Y[i] == 1.0) null_dev += -2.0 * log(wtdmu);
call    0 returned 20
       20: 2850:		   else null_dev += 2.0 * (Y[i] * log(Y[i] / wtdmu) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - wtdmu)));
branch  0 taken 0 (fallthrough)
branch  1 taken 20
    #####: 2851:	  } else {
    %%%%%: 2851-block  0
call    0 never executed
call    1 never executed
    #####: 2852:		   double diff = Y[i] - wtdmu;
call    0 never executed
call    1 never executed
    #####: 2853:		   null_dev += diff * diff;
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 2853-block  0
    %%%%%: 2853-block  1
    %%%%%: 2853-block  2
call    3 never executed
    #####: 2854:	  }
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 2854-block  0
    %%%%%: 2854-block  1
    %%%%%: 2854-block  2
call    3 never executed
        -: 2855:	}
       20: 2856:	// --- AIC Calculation ---
       20: 2857:	if (is_gaussian) {
       20: 2858:	  double n_f = (double)valid_n;
       20: 2858-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 17
        3: 2858-block  1
call    2 returned 3
       17: 2858-block  2
call    3 returned 17
        -: 2859:	  aic = n_f * (log(2.0 * M_PI) + 1.0 + log(deviance_new / n_f)) + 2.0 * (final_rank + 1.0);
       20: 2860:	} else if (is_binomial) { 
       20: 2860-block  0
call    0 returned 20
call    1 returned 20
       20: 2861:	  aic = deviance_new + 2.0 * final_rank; 
call    0 returned 20
call    1 returned 20
       20: 2862:	}
call    0 returned 20
branch  1 taken 3 (fallthrough)
branch  2 taken 17
        3: 2862-block  0
       17: 2862-block  1
       20: 2862-block  2
call    3 returned 20
       20: 2863:	// --- Return Structures ---
call    0 returned 20
branch  1 taken 3 (fallthrough)
branch  2 taken 17
        3: 2863-block  0
       17: 2863-block  1
       20: 2863-block  2
call    3 returned 20
        -: 2864:	res_hv = newHV(); coef_hv = newHV(); fitted_hv = newHV(); resid_hv = newHV();
       20: 2865:	df_res = valid_n - final_rank;
       20: 2865-block  0
call    0 returned 20
call    1 returned 20
        -: 2866:	dispersion = is_binomial ? 1.0 : ((df_res > 0) ? (deviance_new / df_res) : NAN);
        -: 2867:	for (size_t i = 0; i < valid_n; i++) {
        7: 2868:		double res = Y[i] - mu[i];
        7: 2868-block  0
call    0 returned 7
call    1 returned 7
        7: 2869:		if (is_binomial) {
call    0 returned 7
call    1 returned 7
        7: 2870:			// Deviance residuals for binomial
call    0 returned 7
call    1 returned 7
        7: 2871:			double d_res = 0.0;
call    0 returned 7
call    1 returned 7
        7: 2872:			if (Y[i] == 0.0)      d_res = sqrt(-2.0 * log(1.0 - mu[i]));
call    0 returned 7
call    1 returned 7
        7: 2873:			else if (Y[i] == 1.0) d_res = sqrt(-2.0 * log(mu[i]));
call    0 returned 7
call    1 returned 7
        7: 2874:			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]))));
call    0 returned 7
call    1 returned 7
        7: 2875:			res = (Y[i] > mu[i]) ? d_res : -d_res;
call    0 returned 7
call    1 returned 7
        7: 2876:		}
call    0 returned 7
call    1 returned 7
        7: 2877:		hv_store(fitted_hv, valid_row_names[i], strlen(valid_row_names[i]), newSVnv(mu[i]), 0);
call    0 returned 7
call    1 returned 7
        7: 2878:		hv_store(resid_hv,  valid_row_names[i], strlen(valid_row_names[i]), newSVnv(res), 0);
call    0 returned 7
call    1 returned 7
        7: 2879:		Safefree(valid_row_names[i]);
call    0 returned 7
call    1 returned 7
        7: 2880:	}
call    0 returned 7
call    1 returned 7
        7: 2881:	Safefree(valid_row_names);
call    0 returned 7
call    1 returned 7
        7: 2882:
call    0 returned 7
call    1 returned 7
        -: 2883:	summary_hv = newHV(); terms_av = newAV();
        -: 2884:	for (size_t j = 0; j < p; j++) {
       27: 2885:		hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
       20: 2885-block  0
call    0 returned 20
       27: 2885-block  1
branch  1 taken 20
branch  2 taken 7 (fallthrough)
        7: 2886:		av_push(terms_av, newSVpv(exp_terms[j], 0));
        7: 2886-block  0
call    0 returned 7
       27: 2887:
       20: 2887-block  0
call    0 returned 20
       27: 2887-block  1
branch  1 taken 20
branch  2 taken 7 (fallthrough)
        7: 2888:		HV *restrict row_hv = newHV();
        7: 2888-block  0
call    0 returned 7
       27: 2889:		if (aliased[j]) {
       20: 2889-block  0
       27: 2889-block  1
branch  0 taken 20
branch  1 taken 7 (fallthrough)
       20: 2890:			hv_store(row_hv, "Estimate",   8, newSVpv("NaN", 0), 0);
       20: 2890-block  0
call    0 returned 20
       20: 2891:			hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
branch  0 taken 1 (fallthrough)
branch  1 taken 19
        1: 2891-block  0
call    2 returned 1
call    3 returned 1
        -: 2892:			hv_store(row_hv, is_binomial ? "z value" : "t value", 7, newSVpv("NaN", 0), 0);
        7: 2893:			hv_store(row_hv, is_binomial ? "Pr(>|z|)" : "Pr(>|t|)", 8, newSVpv("NaN", 0), 0);
        7: 2893-block  0
call    0 returned 7
call    1 returned 7
call    2 returned 7
call    3 returned 7
        -: 2894:		} else {
        7: 2895:			double se = sqrt(dispersion * XtWX[j * p + j]);
call    0 returned 7
call    1 returned 7
call    2 returned 7
call    3 returned 7
        7: 2896:			double val_stat = beta[j] / se;
call    0 returned 7
call    1 returned 7
call    2 returned 7
        7: 2897:			double p_val = is_binomial ? 2.0 * (1.0 - approx_pnorm(fabs(val_stat))) : get_t_pvalue(val_stat, df_res, "two.sided");
call    0 returned 7
call    1 returned 7
call    2 returned 7
call    3 returned 7
        7: 2898:			
branch  0 taken 5 (fallthrough)
branch  1 taken 2
        5: 2898-block  0
call    2 returned 5
        -: 2899:			hv_store(row_hv, "Estimate",   8, newSVnv(beta[j]), 0);
        7: 2900:			hv_store(row_hv, "Std. Error", 10, newSVnv(se), 0);
        7: 2900-block  0
call    0 returned 7
        -: 2901:			hv_store(row_hv, is_binomial ? "z value" : "t value", 7, newSVnv(val_stat), 0);
        -: 2902:			hv_store(row_hv, is_binomial ? "Pr(>|z|)" : "Pr(>|t|)", 8, newSVnv(p_val), 0);
        -: 2903:		}
        -: 2904:		hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
        -: 2905:	}
        -: 2906:
        -: 2907:	hv_store(res_hv, "aic",            3, newSVnv(aic), 0);
        7: 2908:	hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv), 0);
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2908-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 7
    #####: 2909:	hv_store(res_hv, "converged",      9, newSVuv(converged ? 1 : 0), 0);
    %%%%%: 2909-block  0
call    0 never executed
        -: 2910:	hv_store(res_hv, "boundary",       8, newSVuv(boundary ? 1 : 0), 0);
        7: 2911:	hv_store(res_hv, "deviance",       8, newSVnv(deviance_new), 0);
        -: 2912:	hv_store(res_hv, "deviance.resid", 14, newRV_noinc((SV*)resid_hv), 0);
        7: 2913:	hv_store(res_hv, "df.null",        7, newSVuv(valid_n - has_intercept), 0);
        7: 2914:	hv_store(res_hv, "df.residual",   11, newSVuv(df_res), 0);
        7: 2915:	hv_store(res_hv, "family",         6, newSVpv(family_str, 0), 0);
        7: 2916:	hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
        7: 2917:	hv_store(res_hv, "iter",           4, newSVuv(iter > max_iter ? max_iter : iter), 0);
        -: 2918:	hv_store(res_hv, "null.deviance", 13, newSVnv(null_dev), 0);
        -: 2919:	hv_store(res_hv, "rank",           4, newSVuv(final_rank), 0);
       35: 2920:	hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv), 0);
        7: 2920-block  0
       28: 2920-block  1
       35: 2920-block  2
branch  0 taken 28
branch  1 taken 7 (fallthrough)
       28: 2921:	hv_store(res_hv, "terms",          5, newRV_noinc((SV*)terms_av), 0);
       28: 2921-block  0
call    0 returned 28
       28: 2922:
        -: 2923:	// --- Cleanup ---
       28: 2924:	for (i = 0; i < num_terms; i++) Safefree(terms[i]);
branch  0 taken 7 (fallthrough)
branch  1 taken 21
        7: 2924-block  0
call    2 returned 7
       21: 2925:	Safefree(terms);
       21: 2925-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 14
        7: 2925-block  1
call    2 returned 7
      14*: 2926:	for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]);
       14: 2926-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 14
    %%%%%: 2926-block  1
       14: 2927:	Safefree(uniq_terms);
       14: 2927-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 7
        7: 2927-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 7
        7: 2927-block  2
call    4 returned 7
        7: 2928:	for (size_t j = 0; j < p_exp; j++) {
        7: 2928-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2928-block  1
call    2 returned 7
    #####: 2929:		Safefree(exp_terms[j]);
    %%%%%: 2929-block  0
call    0 never executed
        -: 2930:		if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
        -: 2931:	}
        -: 2932:	Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
        -: 2933:
        7: 2934:	Safefree(mu); Safefree(eta); Safefree(Z); Safefree(W);
        -: 2935:	Safefree(beta); Safefree(beta_old); Safefree(aliased);
        7: 2936:	Safefree(XtWX); Safefree(XtWZ); Safefree(X); Safefree(Y);
        7: 2937:	if (row_hashes) Safefree(row_hashes);
        7: 2938:
        -: 2939:	RETVAL = newRV_noinc((SV*)res_hv);
        -: 2940:}
        7: 2941:OUTPUT:
        7: 2941-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2941-block  1
branch  2 taken 7 (fallthrough)
branch  3 taken 0
        7: 2941-block  2
branch  4 taken 7 (fallthrough)
branch  5 taken 0
        7: 2942:    RETVAL
        7: 2942-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 2942-block  1
branch  2 taken 7 (fallthrough)
branch  3 taken 0
        7: 2942-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 7
    #####: 2943:
    %%%%%: 2943-block  0
call    0 never executed
        -: 2944:SV* cor_test(...)
        -: 2945:CODE:
        7: 2946:{
        7: 2947:	if (items < 2 || items % 2 != 0)
        -: 2948:		croak("Usage: cor_test(\\@x, \\@y, method => 'pearson', ...)");
        7: 2949:
        7: 2949-block  0
call    0 returned 7
       7*: 2950:	SV *restrict x_ref = ST(0), *restrict y_ref = ST(1);
call    0 returned 7
branch  1 taken 0 (fallthrough)
branch  2 taken 7
    %%%%%: 2950-block  0
call    3 never executed
        -: 2951:
        7: 2952:	const char *restrict alternative = "two.sided";
        7: 2952-block  0
call    0 returned 7
        7: 2953:	const char *restrict method = "pearson";
call    0 returned 7
        -: 2954:	SV *restrict exact_sv = NULL;
        7: 2955:	double conf_level = 0.95;
       46: 2956:	bool continuity = 0;
       39: 2956-block  0
       46: 2956-block  1
branch  0 taken 39
branch  1 taken 7 (fallthrough)
       39: 2957:
       39: 2957-block  0
call    0 returned 39
       39: 2958:	/* Parse named arguments from the flat stack starting at index 2 */
call    0 returned 39
        -: 2959:	for (unsigned short int i = 2; i < items; i += 2) {
       39: 2960:	  const char *restrict key = SvPV_nolen(ST(i));
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2960-block  0
branch  2 taken 37 (fallthrough)
branch  3 taken 2
       37: 2960-block  1
call    4 returned 37
branch  5 taken 37 (fallthrough)
branch  6 taken 0
       37: 2960-block  2
call    7 returned 37
        2: 2960-block  3
       39: 2961:	  SV *restrict val = ST(i + 1);
       39: 2961-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2961-block  1
branch  2 taken 37 (fallthrough)
branch  3 taken 2
       37: 2961-block  2
call    4 returned 37
branch  5 taken 37 (fallthrough)
branch  6 taken 0
       37: 2961-block  3
call    7 returned 37
        2: 2961-block  4
        -: 2962:
        -: 2963:	  if      (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
       39: 2964:	  else if (strEQ(key, "method"))      method = SvPV_nolen(val);
       39: 2964-block  0
branch  0 taken 37 (fallthrough)
branch  1 taken 2
       37: 2964-block  1
branch  2 taken 35 (fallthrough)
branch  3 taken 2
       35: 2965:	  else if (strEQ(key, "exact"))       exact_sv = val;
       35: 2966:	  else if (strEQ(key, "conf.level") || strEQ(key, "conf_level")) conf_level = SvNV(val);
       35: 2967:	  else if (strEQ(key, "continuity"))  continuity = SvTRUE(val);
       35: 2967-block  0
        -: 2968:	  else croak("cor_test: unknown argument '%s'", key);
        -: 2969:	}
        -: 2970:
        7: 2971:	AV *restrict x_av, *restrict y_av;
        7: 2971-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    #####: 2972:	double *restrict x, *restrict y;
    %%%%%: 2972-block  0
call    0 never executed
    #####: 2973:	double estimate = 0, p_value = 0, statistic = 0, df = 0, ci_lower = 0, ci_upper = 0;
call    0 never executed
    #####: 2974:
call    0 never executed
        -: 2975:	bool is_pearson  = (strcmp(method, "pearson") == 0);
        -: 2976:	bool is_kendall  = (strcmp(method, "kendall") == 0);
        7: 2977:	bool is_spearman = (strcmp(method, "spearman") == 0);
        7: 2977-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 3
        -: 2978:	HV *restrict rhv;
        4: 2979:
       24: 2980:	if (!SvOK(x_ref) || !SvROK(x_ref) || SvTYPE(SvRV(x_ref)) != SVt_PVAV ||
        4: 2980-block  0
       24: 2980-block  1
branch  0 taken 20
branch  1 taken 4 (fallthrough)
       20: 2981:	    !SvOK(y_ref) || !SvROK(y_ref) || SvTYPE(SvRV(y_ref)) != SVt_PVAV) {
       20: 2982:	  croak("cor_test: x and y must be array references");
       20: 2983:	}
       20: 2984:
       20: 2985:	x_av = (AV*)SvRV(x_ref);
       20: 2986:	y_av = (AV*)SvRV(y_ref);
       20: 2987:	
       20: 2987-block  0
        -: 2988:	size_t n_raw = av_len(x_av) + 1;
       4*: 2989:	if (n_raw != av_len(y_av) + 1) croak("incompatible dimensions");
        4: 2989-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 2989-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        4: 2989-block  2
    %%%%%: 2989-block  3
        4: 2990:
        4: 2991:	x = safemalloc(n_raw * sizeof(double));
        -: 2992:	y = safemalloc(n_raw * sizeof(double));
        -: 2993:
        4: 2994:	size_t n = 0; /* Final count of pairwise complete observations */
        4: 2995:	for (size_t i = 0; i < n_raw; i++) {
        4: 2996:	  SV **restrict x_val = av_fetch(x_av, i, 0);
        4: 2997:	  SV **restrict y_val = av_fetch(y_av, i, 0);
        4: 2997-block  0
call    0 returned 4
        4: 2998:	  
        4: 2999:	  double xv = (x_val && SvOK(*x_val) && looks_like_number(*x_val)) ? SvNV(*x_val) : NAN;
        -: 3000:	  double yv = (y_val && SvOK(*y_val) && looks_like_number(*y_val)) ? SvNV(*y_val) : NAN;
        -: 3001:
        4: 3002:	  /* Pairwise complete observations (skips NAs seamlessly like R) */
call    0 returned 4
        3: 3003:	  if (!isnan(xv) && !isnan(yv)) {
        3: 3003-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 3004:	      x[n] = xv;
       10: 3005:	      y[n] = yv;
        2: 3005-block  0
        8: 3005-block  1
       10: 3005-block  2
branch  0 taken 8
branch  1 taken 2 (fallthrough)
       28: 3006:	      n++;
        8: 3006-block  0
       20: 3006-block  1
       28: 3006-block  2
branch  0 taken 20
branch  1 taken 8 (fallthrough)
       20: 3007:	  }
       20: 3008:	}
        -: 3009:
      20*: 3010:	if (n < 3) {
       20: 3010-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 20
    %%%%%: 3010-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 3010-block  2
      20*: 3011:	  Safefree(x); 
       20: 3011-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 20
    %%%%%: 3011-block  1
      20*: 3012:	  Safefree(y);
       20: 3012-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 20
    %%%%%: 3012-block  1
       20: 3013:	  croak("not enough finite observations");
       20: 3013-block  0
branch  0 taken 16 (fallthrough)
branch  1 taken 4
       16: 3013-block  1
        4: 3014:	}
        4: 3014-block  0
        -: 3015:
        -: 3016:	if (is_pearson) {
        2: 3017:	  // Welford's Method for Pearson Correlation
       2*: 3018:	  double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
        2: 3018-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3018-block  1
    %%%%%: 3018-block  2
        -: 3019:	  for (size_t i = 0; i < n; i++) {
       2*: 3020:		   double dx = x[i] - mean_x;
        2: 3020-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3020-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    %%%%%: 3020-block  2
        2: 3020-block  3
        -: 3021:		   mean_x += dx / (i + 1);
        -: 3022:		   double dy = y[i] - mean_y;
        -: 3023:		   mean_y += dy / (i + 1);
       2*: 3024:		   M2_x += dx * (x[i] - mean_x);
        2: 3024-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3024-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
       2*: 3025:		   M2_y += dy * (y[i] - mean_y);
        2: 3025-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3025-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 3025-block  2
    %%%%%: 3025-block  3
        2: 3025-block  4
        -: 3026:		   cov  += dx * (y[i] - mean_y);
    #####: 3027:	  }
    %%%%%: 3027-block  0
call    0 never executed
        -: 3028:	  estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
        -: 3029:	  df = n - 2;
       2*: 3030:	  statistic = estimate * sqrt(df / (1.0 - estimate * estimate));
        2: 3030-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3030-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    %%%%%: 3030-block  2
        -: 3031:
        2: 3032:	  // Confidence interval using Fisher's Z transform
        2: 3032-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3033:	  double z = 0.5 * log((1.0 + estimate) / (1.0 - estimate));
        2: 3034:	  double se = 1.0 / sqrt(n - 3);
        2: 3035:	  double alpha = 1.0 - conf_level;
        2: 3035-block  0
call    0 returned 2
        -: 3036:	  double q = inverse_normal_cdf(1.0 - alpha/2.0);
        -: 3037:	  ci_lower = tanh(z - q * se);
    #####: 3038:	  ci_upper = tanh(z + q * se);
    #####: 3039:
    #####: 3040:	  // HIGH-PRECISION P-VALUE USING INCOMPLETE BETA
    %%%%%: 3040-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 3040-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 3040-block  2
    %%%%%: 3040-block  3
    %%%%%: 3040-block  4
    #####: 3041:	  p_value = get_t_pvalue(statistic, df, alternative);
        -: 3042:	} else if (is_kendall) {
    #####: 3043:	  int c = 0, d = 0, tie_x = 0, tie_y = 0;
    %%%%%: 3043-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 3044:	  for (size_t i = 0; i < n - 1; i++) {
    %%%%%: 3044-block  0
call    0 never executed
    #####: 3045:		   for (size_t j = i + 1; j < n; j++) {
    %%%%%: 3045-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 3046:		       double sign_x = (x[i] - x[j] > 0) - (x[i] - x[j] < 0);
    %%%%%: 3046-block  0
call    0 never executed
        -: 3047:		       double sign_y = (y[i] - y[j] > 0) - (y[i] - y[j] < 0);
    #####: 3048:
    %%%%%: 3048-block  0
call    0 never executed
        -: 3049:		       if (sign_x == 0 && sign_y == 0) { /* Joint tie, ignore */ }
        -: 3050:		       else if (sign_x == 0) tie_x++;
        1: 3051:		       else if (sign_y == 0) tie_y++;
        1: 3051-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 3052:		       else if (sign_x * sign_y > 0) c++;
        1: 3052-block  0
call    0 returned 1
        1: 3053:		       else d++;
call    0 returned 1
        1: 3054:		   }
call    0 returned 1
        1: 3055:	  }
call    0 returned 1
        -: 3056:	  double denom = sqrt((double)(c + d + tie_x) * (double)(c + d + tie_y));
        -: 3057:	  estimate = (denom == 0.0) ? (0.0/0.0) : (double)(c - d) / denom;
        1: 3058:
        6: 3059:	  bool has_ties = (tie_x > 0 || tie_y > 0);
        6: 3059-block  0
branch  0 taken 5
branch  1 taken 1 (fallthrough)
        5: 3060:	  bool do_exact;
        5: 3061:
        5: 3062:	  /* Mirror R: exact defaults to TRUE if N < 50 and NO ties */
        5: 3063:	  if (!exact_sv || !SvOK(exact_sv)) {
        5: 3064:		   do_exact = (n < 50) && !has_ties;
        5: 3065:	  } else {
        5: 3066:		   do_exact = SvTRUE(exact_sv) ? 1 : 0;
        5: 3066-block  0
        -: 3067:	  }
       1*: 3068:	  // If forced exact but ties exist, R overrides and falls back to approximation anyway
        1: 3068-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 3068-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 3068-block  2
    %%%%%: 3068-block  3
        -: 3069:	  if (do_exact && has_ties) do_exact = 0;
        -: 3070:
        1: 3071:	  if (do_exact) {
        6: 3072:		   double S_stat = c - d;
        1: 3072-block  0
        6: 3072-block  1
branch  0 taken 5
branch  1 taken 1 (fallthrough)
        5: 3073:		   statistic = c;
        5: 3074:		   p_value = kendall_exact_pvalue(n, S_stat, alternative);
        5: 3074-block  0
        -: 3075:	  } else {
        -: 3076:		   // Normal approximation for large N or ties
        -: 3077:		   double var_S = n * (n - 1) * (2.0 * n + 5.0) / 18.0;
        1: 3078:		   double S = c - d;
        6: 3079:		   if (continuity) S -= (S > 0 ? 1 : -1);
        1: 3079-block  0
        5: 3079-block  1
        6: 3079-block  2
branch  0 taken 5
branch  1 taken 1 (fallthrough)
        5: 3080:		   statistic = S / sqrt(var_S);
        5: 3080-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 3080-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 5
    #####: 3081:
    #####: 3082:		   if (strcmp(alternative, "two.sided") == 0) {
    %%%%%: 3082-block  0
        -: 3083:		       p_value = 2.0 * (1.0 - approx_pnorm(fabs(statistic)));
        -: 3084:		   } else if (strcmp(alternative, "less") == 0) {
       1*: 3085:		       p_value = approx_pnorm(statistic);
        1: 3085-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 3085-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
       1*: 3086:		   } else {
        1: 3086-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 3086-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 3086-block  2
    %%%%%: 3086-block  3
        1: 3086-block  4
        -: 3087:		       p_value = 1.0 - approx_pnorm(statistic);
    #####: 3088:		   }
    %%%%%: 3088-block  0
call    0 never executed
        -: 3089:	  }
        -: 3090:	} else if (is_spearman) {
        1: 3091:	  double *restrict rank_x = safemalloc(n * sizeof(double));
        1: 3091-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 3092:	  double *restrict rank_y = safemalloc(n * sizeof(double));
        1: 3093:	  compute_ranks(x, rank_x, n);
        1: 3093-block  0
call    0 returned 1
        -: 3094:	  compute_ranks(y, rank_y, n);
    #####: 3095:
    #####: 3096:	  // Spearman rho = Pearson r of the ranks (Welford's Method)
    %%%%%: 3096-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 3097:	  double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
    %%%%%: 3097-block  0
    #####: 3098:	  for (size_t i = 0; i < n; i++) {
    #####: 3099:		   double dx = rank_x[i] - mean_x;
    %%%%%: 3099-block  0
call    0 never executed
        -: 3100:		   mean_x += dx / (i + 1);
        1: 3101:		   double dy = rank_y[i] - mean_y;
        1: 3101-block  0
call    0 returned 1
call    1 returned 1
        -: 3102:		   mean_y += dy / (i + 1);
    #####: 3103:		   M2_x += dx * (rank_x[i] - mean_x);
    %%%%%: 3103-block  0
call    0 never executed
call    1 never executed
    #####: 3104:		   M2_y += dy * (rank_y[i] - mean_y);
call    0 never executed
        -: 3105:		   cov  += dx * (rank_y[i] - mean_y);
        7: 3106:	  }
        7: 3106-block  0
call    0 returned 7
call    1 returned 7
        7: 3107:	  estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
call    0 returned 7
        7: 3108:	  
call    0 returned 7
call    1 returned 7
        7: 3109:	  // S = sum of squared rank differences (R's reported statistic)
call    0 returned 7
call    1 returned 7
        7: 3110:	  double S_stat = 0.0;
call    0 returned 7
call    1 returned 7
        7: 3111:	  for (size_t i = 0; i < n; i++) {
call    0 returned 7
call    1 returned 7
        7: 3112:		   double diff = rank_x[i] - rank_y[i];
call    0 returned 7
call    1 returned 7
        7: 3113:		   S_stat += diff * diff;
branch  0 taken 4 (fallthrough)
branch  1 taken 3
        4: 3114:	  }
        4: 3114-block  0
call    0 returned 4
call    1 returned 4
        4: 3115:
call    0 returned 4
        4: 3116:	  // Ties produce fractional (averaged) ranks — detect them
call    0 returned 4
call    1 returned 4
        4: 3117:	  bool has_ties = 0, do_exact;
call    0 returned 4
call    1 returned 4
        4: 3118:	  for (size_t i = 0; i < n; i++) {
call    0 returned 4
call    1 returned 4
        -: 3119:		   if (rank_x[i] != floor(rank_x[i]) || rank_y[i] != floor(rank_y[i])) {
        -: 3120:		       has_ties = 1;
        7: 3121:		       break;
        7: 3121-block  0
call    0 returned 7
        -: 3122:		   }
        -: 3123:	  }
        -: 3124:	  if (!exact_sv || !SvOK(exact_sv)) {
        -: 3125:		   do_exact = (n < 10) && !has_ties;
        -: 3126:	  } else {
        -: 3127:		   do_exact = SvTRUE(exact_sv) ? 1 : 0;
        -: 3128:	  }
        -: 3129:
        -: 3130:	  if (do_exact) {
        2: 3131:		   statistic = S_stat;
        2: 3132:		   p_value   = spearman_exact_pvalue(S_stat, n, alternative);
        -: 3133:	  } else {
        2: 3134:		   double r = estimate;
        2: 3134-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3134-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    #####: 3135:		   if (continuity)
    %%%%%: 3135-block  0
call    0 never executed
        -: 3136:		       r *= (1.0 - 1.0 / (2.0 * (n - 1)));
        -: 3137:		   statistic = r * sqrt((n - 2.0) / (1.0 - r * r));
        2: 3138:		   p_value = get_t_pvalue(statistic, (double)(n - 2), alternative);
        2: 3139:	  }
        2: 3139-block  0
call    0 returned 2
        -: 3140:	  Safefree(rank_x); Safefree(rank_y);
       2*: 3141:	} else {
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3141-block  0
call    2 never executed
        2: 3141-block  1
call    3 returned 2
        -: 3142:	  Safefree(x); Safefree(y);
        -: 3143:	  croak("Unknown method");
       26: 3144:	}
       24: 3144-block  0
       26: 3144-block  1
branch  0 taken 24
branch  1 taken 2 (fallthrough)
       24: 3145:	Safefree(x); Safefree(y);
       24: 3145-block  0
call    0 returned 24
       24: 3146:	rhv = newHV();
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 3146-block  0
branch  2 taken 24 (fallthrough)
branch  3 taken 0
       24: 3147:	hv_stores(rhv, "estimate", newSVnv(estimate));
       24: 3147-block  0
call    0 returned 24
       24: 3148:	hv_stores(rhv, "p.value", newSVnv(p_value));
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 3149:	hv_stores(rhv, "statistic", newSVnv(statistic));
       24: 3150:	hv_stores(rhv, "method", newSVpv(method, 0));
       24: 3151:	hv_stores(rhv, "alternative", newSVpv(alternative, 0));
       24: 3151-block  0
        -: 3152:	if (is_pearson) {
        -: 3153:	  hv_stores(rhv, "parameter", newSVnv(df));
        -: 3154:	  AV *restrict ci_av = newAV();
        -: 3155:	  av_push(ci_av, newSVnv(ci_lower));
        2: 3156:	  av_push(ci_av, newSVnv(ci_upper));
        2: 3156-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3156-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    #####: 3157:	  hv_stores(rhv, "conf.int", newRV_noinc((SV*)ci_av));
    %%%%%: 3157-block  0
call    0 never executed
    #####: 3158:	}
call    0 never executed
        -: 3159:
        -: 3160:	RETVAL = newRV_noinc((SV*)rhv);
        2: 3161:}
        -: 3162:OUTPUT:
       26: 3163:    RETVAL
        2: 3163-block  0
       26: 3163-block  1
branch  0 taken 24
branch  1 taken 2 (fallthrough)
       24: 3164:
       24: 3164-block  0
        -: 3165:void shapiro_test(data)
        2: 3166:	SV *data
        2: 3166-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 3167:PREINIT:
    %%%%%: 3167-block  0
call    0 never executed
    #####: 3168:	AV *restrict av;
call    0 never executed
        -: 3169:	HV *restrict ret_hash;
        2: 3170:	size_t n_raw, n = 0;
        2: 3170-block  0
call    0 returned 2
        -: 3171:	double *restrict x, w = 0.0, p_val = 0.0, mean = 0.0, ssq = 0.0;
        -: 3172:PPCODE:
        2: 3173:	if (!SvROK(data) || SvTYPE(SvRV(data)) != SVt_PVAV) {
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 3174:	  croak("Expected an array reference");
    #####: 3175:	}
    #####: 3176:
    #####: 3177:	av = (AV *)SvRV(data);
    %%%%%: 3177-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 3177-block  1
        -: 3178:	n_raw = av_len(av) + 1;
    #####: 3179:
    %%%%%: 3179-block  0
        -: 3180:	Newx(x, n_raw, double);
        -: 3181:
        2: 3182:	// Extract variables and calculate mean (skipping undefined/NaN values)
       2*: 3183:	for (size_t i = 0; i < n_raw; i++) {
        2: 3183-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3183-block  1
call    2 never executed
        2: 3183-block  2
call    3 returned 2
       2*: 3184:	  SV **restrict elem = av_fetch(av, i, 0);
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3184-block  0
call    2 never executed
        2: 3184-block  1
call    3 returned 2
       26: 3185:	  if (elem && SvOK(*elem)) {
       26: 3185-block  0
branch  0 taken 24
branch  1 taken 2 (fallthrough)
       24: 3186:		   double val = SvNV(*elem);
       24: 3186-block  0
call    0 returned 24
       24: 3187:		   if (!isnan(val)) {
        -: 3188:		       x[n] = val;
        2: 3189:		       mean += val;
        2: 3190:		       n++;
        2: 3191:		   }
        2: 3192:	  }
        3: 3193:	}
        2: 3193-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3193-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 1
        1: 3193-block  2
        1: 3194:
        4: 3195:	if (n < 3 || n > 5000) {
        1: 3195-block  0
        4: 3195-block  1
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3: 3196:	  Safefree(x);
        3: 3196-block  0
        -: 3197:	  croak("Sample size must be between 3 and 5000 (R's limit)");
        -: 3198:	}
        1: 3199:
        1: 3200:	mean /= n;
        1: 3201:	// Calculate Sum of Squares */
        1: 3202:	for (size_t i = 0; i < n; i++) {
       16: 3203:	  ssq += (x[i] - mean) * (x[i] - mean);
        1: 3203-block  0
       16: 3203-block  1
branch  0 taken 15
branch  1 taken 1 (fallthrough)
       15: 3204:	}
       15: 3204-block  0
        -: 3205:	if (ssq == 0.0) {
        -: 3206:	  Safefree(x);
       26: 3207:	  croak("Data is perfectly constant; cannot compute Shapiro-Wilk test");
        2: 3207-block  0
       26: 3207-block  1
branch  0 taken 24
branch  1 taken 2 (fallthrough)
       24: 3208:	}
       24: 3208-block  0
        -: 3209:	qsort(x, n, sizeof(double), compare_doubles);
        2: 3210:	
        -: 3211:	// --- Core AS R94 Algorithm: Weights and Statistic W ---
        -: 3212:	if (n == 3) {
        -: 3213:	  double a_val = 0.7071067811865475; /* sqrt(1/2) */
        -: 3214:	  double b_val = a_val * (x[2] - x[0]);
        2: 3215:	  w = (b_val * b_val) / ssq;
        -: 3216:	  if (w < 0.75) w = 0.75; 
        2: 3217:	  // Exact P-value for n=3
        2: 3217-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        -: 3218:	  p_val = 1.90985931710274 * (asin(sqrt(w)) - 1.04719755119660);
        -: 3219:	} else {
        -: 3220:	  double *restrict m, *restrict a;
        1: 3221:	  double sum_m2 = 0.0, b_val = 0.0;
        1: 3222:	  Newx(m, n, double);
        1: 3223:	  Newx(a, n, double);
        1: 3223-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 3224:	  for (size_t i = 0; i < n; i++) {
    %%%%%: 3224-block  0
        -: 3225:		   m[i] = inverse_normal_cdf((i + 1.0 - 0.375) / (n + 0.25));
        -: 3226:		   sum_m2 += m[i] * m[i];
        1: 3227:	  }
        1: 3228:	  double u = 1.0 / sqrt((double)n);
        1: 3229:	  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);
        1: 3230:	  a[n-1] = a_n;
        -: 3231:	  a[0]   = -a_n;
        -: 3232:	  if (n == 4 || n == 5) {
        1: 3233:		   double eps = (sum_m2 - 2.0 * m[n-1]*m[n-1]) / (1.0 - 2.0 * a_n*a_n);
        1: 3233-block  0
        -: 3234:		   for (unsigned int i = 1; i < n-1; i++) {
        -: 3235:		       a[i] = m[i] / sqrt(eps);
        -: 3236:		   }
        1: 3237:	  } else {
        -: 3238:		   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);
        1: 3239:		   a[n-2] = a_n1;
        1: 3240:		   a[1]   = -a_n1;
        1: 3241:		   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);
        1: 3242:		   for (unsigned int i = 2; i < n-2; i++) {
        1: 3243:		       a[i] = m[i] / sqrt(eps);
        1: 3243-block  0
        -: 3244:		   }
        -: 3245:	  }
       2*: 3246:	  for (size_t i = 0; i < n; i++) {
        2: 3246-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3246-block  1
       2*: 3247:		   b_val += a[i] * x[i];
        2: 3247-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3247-block  1
        -: 3248:	  }
        2: 3249:	  w = (b_val * b_val) / ssq;
        2: 3249-block  0
call    0 returned 2
call    1 returned 2
        -: 3250:	// --- AS R94 P-Value Calculation: High Precision Refinement ---
        2: 3251:	  /* NOTE: p_val is declared in PREINIT above;
        2: 3251-block  0
call    0 returned 2
        2: 3252:		* do NOT shadow it with a local 'double p_val' here or the result will never reach the caller.
call    0 returned 2
        2: 3253:		*/
call    0 returned 2
call    1 returned 2
        2: 3254:	  double y = log(1.0 - w);
call    0 returned 2
call    1 returned 2
        2: 3255:	  double z;
call    0 returned 2
call    1 returned 2
        2: 3256:	  if (n <= 11) {
call    0 returned 2
call    1 returned 2
       2*: 3257:		   // Royston's branch for 4 <= n <= 11 (AS R94, small-sample path).
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3257-block  0
call    2 never executed
        2: 3258:		   // gamma is the upper bound on y = log(1-W);
        2: 3258-block  0
call    0 returned 2
call    1 returned 2
        -: 3259:		   // if y reaches gamma the p-value is essentially zero
        -: 3260:		   double nn = (double)n;
        -: 3261:		   double gamma = 0.459 * nn - 2.273;
        -: 3262:		   if (y >= gamma) {
       12: 3263:		       p_val = 1e-19;
       12: 3264:		   } else {
       12: 3265:		       // Horner-form polynomials in n for mu and log(sigma)
        -: 3266:		       double mu     = 0.544  + nn * (-0.39978  + nn * ( 0.025054  - nn * 0.0006714));
    10038: 3267:		       double sig_val= 1.3822 + nn * (-0.77857  + nn * ( 0.062767  - nn * 0.0020322));
       12: 3267-block  0
    10026: 3267-block  1
    10038: 3267-block  2
branch  0 taken 10028
branch  1 taken 10 (fallthrough)
    10028: 3268:		       double sigma  = exp(sig_val);
    10031: 3269:		       z = (-log(gamma - y) - mu) / sigma;
    10028: 3269-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 10024
        4: 3269-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        3: 3269-block  2
        4: 3270:		       /* Upper-tail probability P(Z > z): small W → large z → small p-value.
        4: 3271:		       */
        4: 3271-block  0
call    0 returned 4
      216: 3272:		       p_val = 0.5 * erfc(z * M_SQRT1_2);
      212: 3272-block  0
      216: 3272-block  1
branch  0 taken 213
branch  1 taken 3 (fallthrough)
      213: 3273:		   }
      213: 3273-block  0
call    0 returned 213
      213: 3274:	  } else {
branch  0 taken 213 (fallthrough)
branch  1 taken 0
      213: 3274-block  0
branch  2 taken 212 (fallthrough)
branch  3 taken 1
      212: 3275:		   // Royston's branch for n >= 12 (AS R94, large-sample path)
      212: 3275-block  0
call    0 returned 212
      212: 3276:		   double ln_n   = log((double)n);
branch  0 taken 209 (fallthrough)
branch  1 taken 3
      209: 3276-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 209
        3: 3277:		   // Horner-form polynomials in log(n) for mu and log(sigma). */
        3: 3278:		   double mu     = -1.5861 + ln_n * (-0.31082 + ln_n * (-0.083751 + ln_n * 0.0038915));
        3: 3278-block  0
        -: 3279:		   double sig_val= -0.4803 + ln_n * (-0.082676 + ln_n * 0.0030302);
      212: 3280:		   double sigma  = exp(sig_val);
      212: 3280-block  0
        -: 3281:		   z = (y - mu) / sigma;
        1: 3282:		   p_val = 0.5 * erfc(z * M_SQRT1_2);
        1: 3282-block  0
call    0 returned 0
        -: 3283:	  }
        -: 3284:	  // Clamp the p-value
    10024: 3285:	  if (p_val > 1.0) p_val = 1.0;
    10024: 3285-block  0
branch  0 taken 10023 (fallthrough)
branch  1 taken 1
    10023: 3286:	  if (p_val < 0.0) p_val = 0.0;
    10023: 3286-block  0
call    0 returned 10023
    10023: 3287:	  
branch  0 taken 10015 (fallthrough)
branch  1 taken 8
    10015: 3287-block  0
branch  2 taken 16 (fallthrough)
branch  3 taken 9999
       24: 3288:	  Safefree(m); m = NULL;  Safefree(a); a = NULL;
       24: 3289:	}
       24: 3289-block  0
        -: 3290:	Safefree(x); x = NULL;
    10023: 3291:	ret_hash = newHV();
    10023: 3291-block  0
        -: 3292:	hv_stores(ret_hash, "statistic", newSVnv(w));
        1: 3293:	hv_stores(ret_hash, "W",         newSVnv(w));
        1: 3293-block  0
call    0 returned 0
        -: 3294:	hv_stores(ret_hash, "p_value",   newSVnv(p_val));
        -: 3295:	hv_stores(ret_hash, "p.value",   newSVnv(p_val));
       10: 3296:	EXTEND(SP, 1);
       10: 3296-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 9
        1: 3296-block  1
call    2 returned 0
        9: 3297:	PUSHs(sv_2mortal(newRV_noinc((SV *)ret_hash)));
        9: 3297-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
        -: 3298:
        -: 3299:double min(...)
        -: 3300:	PROTOTYPE: @
        -: 3301:	INIT:
        -: 3302:		double min_val = 0.0;
        -: 3303:		size_t count = 0;
       13: 3304:		bool first = TRUE;
       13: 3305:	CODE:
       13: 3306:		for (unsigned short int i = 0; i < items; i++) {
        -: 3307:			SV* restrict arg = ST(i);
    10039: 3308:			if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
       13: 3308-block  0
    10026: 3308-block  1
    10039: 3308-block  2
branch  0 taken 10028
branch  1 taken 11 (fallthrough)
    10028: 3309:				AV* restrict av = (AV*)SvRV(arg);
    10032: 3310:				size_t len = av_len(av) + 1;
    10028: 3310-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 10023
        5: 3310-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        4: 3310-block  2
        5: 3311:				for (size_t j = 0; j < len; j++) {
        5: 3312:				     SV** restrict tv = av_fetch(av, j, 0);
        5: 3312-block  0
call    0 returned 5
      317: 3313:				     if (tv && SvOK(*tv)) {
      312: 3313-block  0
      317: 3313-block  1
branch  0 taken 313
branch  1 taken 4 (fallthrough)
      313: 3314:				         double val = SvNV(*tv);
      313: 3314-block  0
call    0 returned 313
      313: 3315:				         if (first || val < min_val) {
branch  0 taken 313 (fallthrough)
branch  1 taken 0
      313: 3315-block  0
branch  2 taken 312 (fallthrough)
branch  3 taken 1
      312: 3316:				             min_val = val;
      312: 3316-block  0
call    0 returned 312
      312: 3317:				             first = FALSE;
branch  0 taken 308 (fallthrough)
branch  1 taken 4
      308: 3317-block  0
branch  2 taken 11 (fallthrough)
branch  3 taken 297
       15: 3318:				         }
       15: 3319:				         count++;
       15: 3319-block  0
        -: 3320:				     } else {
      312: 3321:				         croak("min: undefined value at array ref index %zu (argument %d)", j, (int)i);
      312: 3321-block  0
        -: 3322:				     }
        1: 3323:				 }
        1: 3323-block  0
call    0 returned 0
        -: 3324:			} else if (SvOK(arg)) {
        -: 3325:				 double val = SvNV(arg);
    10023: 3326:				 if (first || val < min_val) {
    10023: 3326-block  0
branch  0 taken 10022 (fallthrough)
branch  1 taken 1
    10022: 3327:				     min_val = val;
    10022: 3327-block  0
call    0 returned 10022
    10022: 3328:				     first = FALSE;
branch  0 taken 10014 (fallthrough)
branch  1 taken 8
    10014: 3328-block  0
branch  2 taken 19 (fallthrough)
branch  3 taken 9995
       27: 3329:				 }
       27: 3330:				 count++;
       27: 3330-block  0
        -: 3331:			} else {
    10022: 3332:				 croak("min: undefined value at argument index %d", (int)i);
    10022: 3332-block  0
        -: 3333:			}
        1: 3334:		}
        1: 3334-block  0
call    0 returned 0
        -: 3335:		if (count == 0) croak("min needs >= 1 numeric element");
        -: 3336:		RETVAL = min_val;
       11: 3337:	OUTPUT:
       11: 3337-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 10
        1: 3337-block  1
call    2 returned 0
       10: 3338:	  RETVAL
       10: 3338-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 10
        -: 3339:
        -: 3340:double max(...)
        -: 3341:	PROTOTYPE: @
        -: 3342:	INIT:
        -: 3343:		double max_val = 0.0;
        -: 3344:		size_t count = 0;
        4: 3345:		bool first = TRUE;
        4: 3346:	CODE:
        -: 3347:		for (size_t i = 0; i < items; i++) {
        -: 3348:		   SV* restrict arg = ST(i);
        4: 3349:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 3350:		       AV* restrict av = (AV*)SvRV(arg);
        4: 3351:		       size_t len = av_len(av) + 1;
        -: 3352:		       for (size_t j = 0; j < len; j++) {
        4: 3353:		           SV** restrict tv = av_fetch(av, j, 0);
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 3354:		           if (tv && SvOK(*tv)) {
    %%%%%: 3354-block  0
call    0 never executed
        -: 3355:		               double val = SvNV(*tv);
        -: 3356:		               if (first || val > max_val) {
       14: 3357:		                   max_val = val;
        4: 3357-block  0
       14: 3357-block  1
branch  0 taken 10
branch  1 taken 4 (fallthrough)
        -: 3358:		                   first = FALSE;
       10: 3359:		               }
       10: 3359-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 2
        8: 3359-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 2
        6: 3360:		               count++;
        6: 3360-block  0
call    0 returned 6
        6: 3361:		           } else {
branch  0 taken 2 (fallthrough)
branch  1 taken 4
        2: 3362:		               croak("max: undefined value at array ref index %zu (argument %zu)", j, i);
        2: 3362-block  0
call    0 returned 2
        2: 3363:		           }
        2: 3364:		       }
        2: 3365:		   } else if (SvOK(arg)) {
        4: 3366:		       double val = SvNV(arg);
        4: 3366-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 2
        2: 3367:		       if (first || val > max_val) {
        2: 3367-block  0
call    0 returned 2
        2: 3368:		           max_val = val;
        2: 3369:		           first = FALSE;
        2: 3370:		       }
        2: 3371:		       count++;
        2: 3371-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3372:		   } else {
        2: 3372-block  0
call    0 returned 2
        2: 3373:		       croak("max: undefined value at argument index %zu", i);
        2: 3374:		   }
        2: 3375:	  }
        -: 3376:	  if (count == 0) croak("max needs >= 1 numeric element");
        -: 3377:	  RETVAL = max_val;
        -: 3378:	OUTPUT:
        -: 3379:		RETVAL
        4: 3380:
        4: 3380-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 2
        2: 3381:SV* runif(...)
        2: 3381-block  0
call    0 returned 2
        2: 3382:CODE:
        2: 3383:{
        2: 3383-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 3384:	size_t n = 0;
        1: 3384-block  0
call    0 returned 1
        1: 3385:	double min = 0.0, max = 1.0;
        1: 3386:
        1: 3386-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 3387:	// Flags to track what has been assigned
        1: 3387-block  0
call    0 returned 1
        1: 3388:	bool n_set = 0, min_set = 0, max_set = 0;
        -: 3389:
    #####: 3390:	unsigned int i = 0;
    %%%%%: 3390-block  0
call    0 never executed
        -: 3391:
        4: 3392:	if (items == 0) {
        4: 3392-block  0
        -: 3393:	  croak("Usage: runif(n, [min=0], [max=1]) or runif(n => $n, ...)");
        4: 3394:	}
        4: 3394-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 3395:
    %%%%%: 3395-block  0
call    0 never executed
        -: 3396:	while (i < items) {
        -: 3397:		// 1. Check if the current argument is a string key for a named parameter
       4*: 3398:		if (i + 1 < items && SvPOK(ST(i))) {
        4: 3398-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 3398-block  1
call    2 never executed
call    3 never executed
        4: 3399:			char *restrict key = SvPV_nolen(ST(i));
        4: 3399-block  0
call    0 returned 4
        4: 3400:			if (strEQ(key, "n")) {
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 3401:				n = (size_t)SvUV(ST(i+1));
        4: 3401-block  0
call    0 returned 4
        -: 3402:				n_set = 1;
        4: 3403:				i += 2;
    20020: 3404:				continue;
        4: 3404-block  0
    20020: 3404-block  1
branch  0 taken 20016
branch  1 taken 4 (fallthrough)
        -: 3405:			} else if (strEQ(key, "min")) {
    20016: 3406:				min = SvNV(ST(i+1));
    20016: 3406-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 20016
    #####: 3407:				min_set = 1;
    %%%%%: 3407-block  0
        -: 3408:				i += 2;
    20016: 3409:				continue;
    20016: 3409-block  0
call    0 returned 20016
        -: 3410:			} else if (strEQ(key, "max")) {
    20016: 3411:				max = SvNV(ST(i+1));
    20016: 3411-block  0
call    0 returned 20016
call    1 returned 20016
        -: 3412:				max_set = 1;
        4: 3413:				i += 2;
        4: 3413-block  0
call    0 returned 4
        -: 3414:				continue;
        -: 3415:			}
        -: 3416:		}
        -: 3417:
        -: 3418:		// 2. Fallback to positional parsing if it's not a recognized key
        -: 3419:		if (!n_set) {
        -: 3420:			n = (size_t)SvUV(ST(i));
        -: 3421:			n_set = 1;
      12*: 3422:		} else if (!min_set) {
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 3422-block  0
call    2 never executed
call    3 never executed
       12: 3423:			min = SvNV(ST(i));
       12: 3423-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 11
        1: 3424:			min_set = 1;
        1: 3424-block  0
call    0 returned 0
        -: 3425:		} else if (!max_set) {
       11: 3426:			max = SvNV(ST(i));
       11: 3427:			max_set = 1;
        -: 3428:		} else {
       11: 3429:			croak("Too many arguments or unrecognized parameter passed to runif()");
        -: 3430:		}
       42: 3431:		i++;
       11: 3431-block  0
       31: 3431-block  1
       42: 3431-block  2
branch  0 taken 31
branch  1 taken 11 (fallthrough)
       31: 3432:	}
       31: 3432-block  0
call    0 returned 31
       31: 3433:	if (!n_set) {
        -: 3434:		croak("runif() requires at least the 'n' parameter");
       31: 3435:	}
branch  0 taken 11 (fallthrough)
branch  1 taken 20
       11: 3435-block  0
call    2 returned 11
       20: 3436:	// Ensure PRNG is seeded
       20: 3436-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 10
       10: 3436-block  1
call    2 returned 10
       10: 3437:	AUTO_SEED_PRNG();
       10: 3437-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 0
       10: 3437-block  1
call    2 returned 10
    #####: 3438:	AV *restrict results = newAV();
    %%%%%: 3438-block  0
call    0 never executed
        -: 3439:	if (n > 0) {
        -: 3440:		av_extend(results, n - 1);
        -: 3441:	}
       11: 3442:	const double range = max - min;
       11: 3442-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 1
       10: 3442-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 9
        2: 3442-block  2
call    4 returned 0
        9: 3443:	for (size_t j = 0; j < n; j++) {
        9: 3443-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 1
        8: 3443-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 7
        2: 3443-block  2
call    4 returned 0
        -: 3444:		double r;
        7: 3445:		if (max < min) {
        7: 3445-block  0
call    0 returned 7
        7: 3446:			r = NAN; // R behavior for inverted ranges
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 3447:		} else {
        7: 3447-block  0
call    0 returned 7
    20506: 3448:			r = min + range * Drand01();
    20506: 3448-block  0
branch  0 taken 20499
branch  1 taken 7 (fallthrough)
    20499: 3449:		}
    20499: 3449-block  0
call    0 returned 20499
call    1 returned 20499
call    2 returned 20499
        -: 3450:		av_push(results, newSVnv(r));
        -: 3451:	}
        -: 3452:	RETVAL = newRV_noinc((SV*)results);
        7: 3453:}
        7: 3453-block  0
call    0 returned 7
        -: 3454:OUTPUT:
        -: 3455:    RETVAL
        -: 3456:
        -: 3457:SV* rbinom(...)
        -: 3458:	CODE:
        -: 3459:	{
        -: 3460:	  // Auto-seed the PRNG if the Perl script hasn't done so yet
        -: 3461:	  AUTO_SEED_PRNG();
        -: 3462:	  if (items % 2 != 0)
        9: 3463:		   croak("Usage: rbinom(n => 10, size => 100, prob => 0.5)");
        9: 3463-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 1
        8: 3463-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 7
        2: 3464:	  //Parse named arguments
        2: 3464-block  0
call    0 returned 0
        -: 3465:	  size_t n = 0, size = 0;
        7: 3466:	  double prob = 0.5;
        7: 3467:	  
        7: 3467-block  0
call    0 returned 7
        7: 3468:	  bool size_set = FALSE, prob_set = FALSE;
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 3468-block  0
call    2 returned 0
        -: 3469:
        -: 3470:	  for (unsigned short i = 0; i < items; i += 2) {
        -: 3471:		   const char* restrict key = SvPV_nolen(ST(i));
       6*: 3472:		   SV* restrict val = ST(i + 1);
        6: 3472-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3472-block  1
call    2 never executed
        6: 3472-block  2
call    3 returned 6
        6: 3473:
        6: 3474:		   if      (strEQ(key, "n"))      n    = (unsigned int)SvUV(val);
        -: 3475:		   else if (strEQ(key, "size")) { size = (unsigned int)SvUV(val); size_set = TRUE; }
     2026: 3476:		   else if (strEQ(key, "prob")) { prob = SvNV(val); prob_set = TRUE; }
     2020: 3476-block  0
     2026: 3476-block  1
branch  0 taken 2021
branch  1 taken 5 (fallthrough)
     2021: 3477:		   else croak("rbinom: unknown argument '%s'", key);
     2021: 3477-block  0
call    0 returned 2021
     2021: 3478:	  }
branch  0 taken 2021 (fallthrough)
branch  1 taken 0
     2021: 3478-block  0
branch  2 taken 2021 (fallthrough)
branch  3 taken 0
     2021: 3479:
     2021: 3479-block  0
call    0 returned 2020
     2020: 3480:	  // R requires size and prob to be explicitly passed in rbinom
     2020: 3481:	  if (!size_set || !prob_set) croak("rbinom: 'size' and 'prob' are required arguments");
branch  0 taken 5 (fallthrough)
branch  1 taken 2015
        5: 3481-block  0
     2020: 3482:	  if (prob < 0.0 || prob > 1.0) croak("rbinom: prob must be between 0 and 1");
     2020: 3482-block  0
branch  0 taken 2012 (fallthrough)
branch  1 taken 8
     2012: 3482-block  1
        -: 3483:
        -: 3484:	  AV *restrict result_av = newAV();
        5: 3485:	  if (n > 0) {
        5: 3485-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    #####: 3486:		   av_extend(result_av, n - 1);
    %%%%%: 3486-block  0
call    0 never executed
    #####: 3487:		   for (unsigned int i = 0; i < n; i++) {
call    0 never executed
        -: 3488:		       av_store(result_av, i, newSVuv(generate_binomial(size, prob)));
        -: 3489:		   }
        5: 3490:	  }
        -: 3491:
        5: 3492:	  RETVAL = newRV_noinc((SV*)result_av);
        5: 3492-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
        -: 3493:	}
    #####: 3494:	OUTPUT:
    %%%%%: 3494-block  0
call    0 never executed
        5: 3495:		RETVAL
        5: 3495-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        -: 3496:
       5*: 3497:SV*
        5: 3497-block  0
    %%%%%: 3497-block  1
        5: 3497-block  2
branch  0 taken 5
branch  1 taken 0 (fallthrough)
        -: 3498:hist(SV* x_sv, ...)
        5: 3499:	CODE:
        5: 3499-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 3499-block  1
call    2 returned 5
branch  3 taken 5 (fallthrough)
branch  4 taken 0
        5: 3500:	{
        5: 3500-block  0
call    0 returned 5
        5: 3501:		// 1. Validate Input
        -: 3502:		if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 3503:			croak("hist: first argument must be an array reference");
        -: 3504:
       5*: 3505:		AV*restrict x_av = (AV*)SvRV(x_sv);
        5: 3505-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 3505-block  1
call    2 never executed
branch  3 never executed (fallthrough)
branch  4 never executed
    #####: 3506:		size_t n_raw = av_len(x_av) + 1;
    %%%%%: 3506-block  0
call    0 never executed
        -: 3507:		if (n_raw == 0) croak("hist: input array is empty");
        -: 3508:
       5*: 3509:		// 2. Extract Data & Find Range
        5: 3509-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 3509-block  1
call    2 never executed
        -: 3510:		double *restrict x;
        -: 3511:		Newx(x, n_raw, double);
        -: 3512:		size_t n = 0;
       5*: 3513:		double min_val = DBL_MAX, max_val = -DBL_MAX;
        5: 3513-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 3513-block  1
call    2 never executed
        5: 3513-block  2
call    3 returned 5
       5*: 3514:
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 3514-block  0
call    2 never executed
        5: 3514-block  1
call    3 returned 5
       5*: 3515:		for (size_t i = 0; i < n_raw; i++) {
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 3515-block  0
call    2 never executed
        5: 3515-block  1
call    3 returned 5
       5*: 3516:			SV**restrict tv = av_fetch(x_av, i, 0);
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 3516-block  0
call    2 never executed
        5: 3516-block  1
call    3 returned 5
        -: 3517:			if (tv && SvOK(*tv)) {
        -: 3518:				 double val = SvNV(*tv);
        5: 3519:				 x[n++] = val;
       28: 3520:				 if (val < min_val) min_val = val;
       28: 3520-block  0
branch  0 taken 23
branch  1 taken 5 (fallthrough)
       23: 3521:				 if (val > max_val) max_val = val;
       23: 3521-block  0
        -: 3522:			}
        -: 3523:		}
        -: 3524:		if (n == 0) {
        5: 3525:			Safefree(x);
        5: 3525-block  0
call    0 returned 5
        -: 3526:			croak("hist: input contains no valid numeric data");
        -: 3527:		}
        5: 3528:		// 3. Determine Bin Count (Sturges default or user-provided)
call    0 returned 5
        5: 3529:		size_t n_bins = 0;
call    0 returned 5
        5: 3530:
call    0 returned 5
        5: 3531:		if (items == 2) {
call    0 returned 5
        5: 3532:			// Support pure positional argument: hist($data, 22)
call    0 returned 5
       28: 3533:			n_bins = (size_t)SvIV(ST(1));
       23: 3533-block  0
       28: 3533-block  1
branch  0 taken 23
branch  1 taken 5 (fallthrough)
       23: 3534:		} else if (items > 2) {
       23: 3534-block  0
call    0 returned 23
call    1 returned 23
       23: 3535:			/* Support named parameters even if mixed with positional arguments */
branch  0 taken 18 (fallthrough)
branch  1 taken 5
       18: 3536:			for (unsigned short i = 1; i < items - 1; i++) {
       18: 3536-block  0
call    0 returned 18
call    1 returned 18
       18: 3537:				 /* Make sure the SV holds a string before doing string comparison */
call    0 returned 18
call    1 returned 18
       18: 3538:				 if (SvPOK(ST(i)) && strEQ(SvPV_nolen(ST(i)), "breaks")) {
call    0 returned 18
call    1 returned 18
        -: 3539:				     n_bins = (size_t)SvIV(ST(i+1));
        -: 3540:				     break;
        5: 3541:				 }
        5: 3541-block  0
call    0 returned 5
call    1 returned 5
        5: 3542:			}
call    0 returned 5
call    1 returned 5
        5: 3543:			/* Fallback: if 'breaks' wasn't found but a positional number was given first */
call    0 returned 5
call    1 returned 5
        5: 3544:			if (n_bins == 0 && looks_like_number(ST(1))) {
call    0 returned 5
call    1 returned 5
        -: 3545:				 n_bins = (size_t)SvIV(ST(1));
        -: 3546:			}
        5: 3547:		}
call    0 returned 5
call    1 returned 5
call    2 returned 5
        5: 3548:		if (n_bins == 0) n_bins = calculate_sturges_bins(n);
call    0 returned 5
call    1 returned 5
        -: 3549:		// 4. Allocate Result Arrays
        5: 3550:		double *restrict breaks, *restrict mids, *restrict density;
call    0 returned 5
        -: 3551:		size_t *restrict counts;
        -: 3552:		Newx(breaks,  n_bins + 1, double);
        -: 3553:		Newx(mids,    n_bins,     double);
        -: 3554:		Newx(density, n_bins,     double);
        -: 3555:		Newx(counts,  n_bins,     size_t);
        -: 3556:
        -: 3557:		// Generate simple linear breaks
        4: 3558:		double step = (max_val - min_val) / (double)n_bins;
        4: 3559:		for (size_t i = 0; i <= n_bins; i++) {
        4: 3560:			breaks[i] = min_val + (double)i * step;
        -: 3561:		}
        -: 3562:
        4: 3563:		// 5. Compute Statistics
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 3563-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 1
        3: 3563-block  1
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 3564:		compute_hist_logic(x, n, breaks, n_bins, counts, mids, density);
        3: 3565:
        3: 3565-block  0
        -: 3566:		// 6. Build Return HashRef
        -: 3567:		HV*restrict res_hv = newHV();
        -: 3568:		AV*restrict av_breaks  = newAV();
        4: 3569:		AV*restrict av_counts  = newAV();
        4: 3569-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 3570:		AV*restrict av_mids    = newAV();
    %%%%%: 3570-block  0
call    0 never executed
        -: 3571:		AV*restrict av_density = newAV();
        9: 3572:		for (size_t i = 0; i <= n_bins; i++) {
        4: 3572-block  0
        5: 3572-block  1
        9: 3572-block  2
branch  0 taken 5
branch  1 taken 4 (fallthrough)
        5: 3573:			av_push(av_breaks, newSVnv(breaks[i]));
        5: 3573-block  0
call    0 returned 5
        5: 3574:			if (i < n_bins) {
        -: 3575:				 av_push(av_counts,  newSViv(counts[i]));
        5: 3576:				 av_push(av_mids,    newSVnv(mids[i]));
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        1: 3576-block  0
        4: 3577:				 av_push(av_density, newSVnv(density[i]));
        4: 3577-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 3577-block  1
    #####: 3578:			}
    %%%%%: 3578-block  0
call    0 never executed
        -: 3579:		}
        4: 3580:		hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
        4: 3580-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 3580-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        4: 3580-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 4
    #####: 3581:		hv_stores(res_hv, "counts",  newRV_noinc((SV*)av_counts));
    %%%%%: 3581-block  0
call    0 never executed
        4: 3582:		hv_stores(res_hv, "mids",    newRV_noinc((SV*)av_mids));
        4: 3583:		hv_stores(res_hv, "density", newRV_noinc((SV*)av_density));
        4: 3583-block  0
call    0 returned 4
       4*: 3584:
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 3584-block  0
call    2 never executed
        -: 3585:		// Clean
        -: 3586:		Safefree(x); Safefree(breaks); Safefree(mids);
        -: 3587:		Safefree(density); Safefree(counts);
       4*: 3588:
        4: 3588-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 3588-block  1
call    2 never executed
        4: 3588-block  2
call    3 returned 4
        4: 3589:		RETVAL = newRV_noinc((SV*)res_hv);
      208: 3590:	}
      204: 3590-block  0
      208: 3590-block  1
branch  0 taken 204
branch  1 taken 4 (fallthrough)
      204: 3591:	OUTPUT:
      204: 3591-block  0
call    0 returned 204
      204: 3592:	  RETVAL
branch  0 taken 204 (fallthrough)
branch  1 taken 0
      204: 3592-block  0
branch  2 taken 204 (fallthrough)
branch  3 taken 0
      204: 3593:
      204: 3593-block  0
call    0 returned 204
        -: 3594:SV* quantile(...)
        -: 3595:	CODE:
        4: 3596:	{
        4: 3596-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 3597:		SV *restrict x_sv = NULL;
    %%%%%: 3597-block  0
call    0 never executed
    #####: 3598:		SV *restrict probs_sv = NULL;
call    0 never executed
        -: 3599:		int arg_idx = 0;
        -: 3600:
        4: 3601:		/* --- 1. Consume first positional arg as 'x' if it's an array ref --- */
        4: 3601-block  0
call    0 returned 4
        -: 3602:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        4: 3603:			 x_sv = ST(arg_idx);
        4: 3604:			 arg_idx++;
        -: 3605:		}
        -: 3606:
        8: 3607:		/* --- 2. Remaining args must be key-value pairs --- */
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 3607-block  0
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        4: 3607-block  1
branch  4 taken 4 (fallthrough)
branch  5 taken 0
        4: 3607-block  2
        4: 3608:		if ((items - arg_idx) % 2 != 0)
        4: 3609:			 croak("Usage: quantile(\\@data, probs => \\@probs)  OR  quantile(x => \\@data, probs => \\@probs)");
        4: 3609-block  0
call    0 returned 4
        4: 3610:
call    0 returned 4
       13: 3611:		for (; arg_idx < items; arg_idx += 2) {
        9: 3611-block  0
       13: 3611-block  1
branch  0 taken 9
branch  1 taken 4 (fallthrough)
        9: 3612:			 const char *restrict key = SvPV_nolen(ST(arg_idx));
        9: 3612-block  0
call    0 returned 9
       9*: 3613:			 SV *restrict val = ST(arg_idx + 1);
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 3613-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 3613-block  1
call    4 returned 9
    %%%%%: 3613-block  2
        9: 3614:
        9: 3614-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 3614-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 3615:			 if      (strEQ(key, "x"))     x_sv     = val;
    %%%%%: 3615-block  0
call    0 never executed
call    1 never executed
    #####: 3616:			 else if (strEQ(key, "probs")) probs_sv = val;
call    0 never executed
        -: 3617:			 else croak("quantile: unknown argument '%s'", key);
        -: 3618:		}
        -: 3619:		if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
    #####: 3620:			croak("quantile: 'x' must be an array reference");
    %%%%%: 3620-block  0
call    0 never executed
    #####: 3621:		AV *restrict x_av = (AV*)SvRV(x_sv);
    %%%%%: 3621-block  0
    %%%%%: 3621-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
        -: 3622:		size_t n_raw = av_len(x_av) + 1;
        -: 3623:		if (n_raw == 0) croak("quantile: 'x' is empty");
        -: 3624:
        4: 3625:		/* --- Extract valid numeric data & drop NAs --- */
        4: 3625-block  0
call    0 returned 4
        -: 3626:		double *restrict x;
       13: 3627:		Newx(x, n_raw, double);
       13: 3627-block  0
branch  0 taken 9
branch  1 taken 4 (fallthrough)
        9: 3628:		size_t n = 0;
        -: 3629:		for (size_t i = 0; i < n_raw; i++) {
        9: 3630:			SV **restrict tv = av_fetch(x_av, i, 0);
        9: 3630-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 8
        1: 3631:			if (tv && SvOK(*tv)) {
        1: 3631-block  0
        8: 3632:				 x[n++] = SvNV(*tv);
        8: 3632-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 7
        1: 3633:			}
        1: 3633-block  0
        7: 3634:		}
        7: 3634-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 3635:		if (n == 0) {
        1: 3635-block  0
        -: 3636:			Safefree(x);
        -: 3637:			croak("quantile: 'x' contains no valid numbers");
        6: 3638:		}
        6: 3639:		// --- Sort Data for Quantile Math ---
        6: 3640:		qsort(x, n, sizeof(double), compare_doubles);
        6: 3641:		// --- Parse Probabilities (Default matches R's c(0, .25, .5, .75, 1)) ---
        6: 3641-block  0
        -: 3642:		double default_probs[] = {0.0, 0.25, 0.50, 0.75, 1.0};
        -: 3643:		unsigned int n_probs = 5;
        -: 3644:		double *restrict probs;
        -: 3645:
        9: 3646:		if (probs_sv && SvROK(probs_sv) && SvTYPE(SvRV(probs_sv)) == SVt_PVAV) {
        -: 3647:			AV *restrict p_av = (AV*)SvRV(probs_sv);
        9: 3648:			n_probs = av_len(p_av) + 1;
        9: 3648-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 3649:			Newx(probs, n_probs, double);
        9: 3649-block  0
        -: 3650:			for (unsigned int i = 0; i < n_probs; i++) {
    #####: 3651:				 SV **tv = av_fetch(p_av, i, 0);
    %%%%%: 3651-block  0
        -: 3652:				 probs[i] = (tv && SvOK(*tv)) ? SvNV(*tv) : 0.0;
        -: 3653:				 if (probs[i] < 0.0 || probs[i] > 1.0) {
        9: 3654:				     Safefree(x); Safefree(probs);
        9: 3654-block  0
call    0 returned 9
call    1 returned 9
        -: 3655:				     croak("quantile: probabilities must be between 0 and 1");
        -: 3656:				 }
        4: 3657:			}
        4: 3657-block  0
call    0 returned 4
        4: 3658:		} else {
call    0 returned 4
        -: 3659:			Newx(probs, n_probs, double);
        4: 3660:			for (unsigned int i = 0; i < n_probs; i++) probs[i] = default_probs[i];
call    0 returned 4
        -: 3661:		}
        -: 3662:
        -: 3663:		/* --- Calculate Quantiles (R Type 7 Algorithm) --- */
        -: 3664:		HV *restrict res_hv = newHV();
        -: 3665:
        -: 3666:		for (size_t i = 0; i < n_probs; i++) {
        -: 3667:			double p = probs[i], q = 0.0;
        -: 3668:
       41: 3669:			if (n == 1) {
       41: 3670:				 q = x[0];
        -: 3671:			} else if (p == 1.0) {
       93: 3672:				 q = x[n - 1]; /* Prevent out-of-bounds mapping */
       41: 3672-block  0
       52: 3672-block  1
       93: 3672-block  2
branch  0 taken 54
branch  1 taken 39 (fallthrough)
       54: 3673:			} else if (p == 0.0) {
       91: 3674:				 q = x[0];
       54: 3674-block  0
branch  0 taken 38 (fallthrough)
branch  1 taken 16
       38: 3674-block  1
branch  2 taken 38 (fallthrough)
branch  3 taken 0
       37: 3674-block  2
       38: 3675:			} else {
       38: 3676:				 /* Continuous sample quantile interpolation (Type 7) */
       38: 3676-block  0
call    0 returned 38
    20307: 3677:				 double h = (n - 1) * p;
    20307: 3677-block  0
branch  0 taken 20270
branch  1 taken 37 (fallthrough)
    20270: 3678:				 unsigned int j = (unsigned int)h; /* floor via cast */
    20270: 3678-block  0
call    0 returned 20270
    20270: 3679:				 double gamma = h - j;
branch  0 taken 20270 (fallthrough)
branch  1 taken 0
    20270: 3679-block  0
branch  2 taken 20269 (fallthrough)
branch  3 taken 1
    20269: 3680:				 q = (1.0 - gamma) * x[j] + gamma * x[j + 1];
    20269: 3680-block  0
call    0 returned 20269
    20269: 3681:			}
        -: 3682:
        1: 3683:			/* Format hash key to exactly match R's naming convention ("25%", "33.3%") */
        1: 3683-block  0
call    0 returned 0
        -: 3684:			char key[32];
        -: 3685:			double pct = p * 100.0;
       16: 3686:			
       16: 3686-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 1
       15: 3687:			if (pct == (unsigned int)pct) {
       15: 3687-block  0
call    0 returned 15
       15: 3688:				 snprintf(key, sizeof(key), "%.0f%%", pct);
        -: 3689:			} else {
        1: 3690:				 snprintf(key, sizeof(key), "%.1f%%", pct);
        1: 3690-block  0
call    0 returned 0
        -: 3691:			}
        -: 3692:
       39: 3693:			hv_store(res_hv, key, strlen(key), newSVnv(q), 0);
       39: 3693-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 38
        1: 3693-block  1
call    2 returned 0
       38: 3694:		}
       38: 3694-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 8
        -: 3695:
        -: 3696:		Safefree(x);
        -: 3697:		Safefree(probs);
        -: 3698:
        -: 3699:		RETVAL = newRV_noinc((SV*)res_hv);
        -: 3700:	}
        5: 3701:	OUTPUT:
        5: 3702:	  RETVAL
        -: 3703:
       19: 3704:
        5: 3704-block  0
       14: 3704-block  1
       19: 3704-block  2
branch  0 taken 16
branch  1 taken 3 (fallthrough)
       16: 3705:double mean(...)
       17: 3706:	PROTOTYPE: @
       16: 3706-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 14
        2: 3706-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        1: 3706-block  2
        2: 3707:	INIT:
        2: 3708:	  double total = 0;
        2: 3708-block  0
call    0 returned 2
       11: 3709:	  size_t count = 0;
       11: 3709-block  0
branch  0 taken 10
branch  1 taken 1 (fallthrough)
       10: 3710:	CODE:
       10: 3710-block  0
call    0 returned 10
       10: 3711:		for (size_t i = 0; i < items; i++) {
branch  0 taken 10 (fallthrough)
branch  1 taken 0
       10: 3711-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 1
        9: 3712:			SV* restrict arg = ST(i);
        9: 3712-block  0
call    0 returned 9
        9: 3713:			if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 3714:				AV* restrict av = (AV*)SvRV(arg);
        1: 3715:				size_t len = av_len(av) + 1;
        1: 3715-block  0
call    0 returned 0
        -: 3716:				for (size_t j = 0; j < len; j++) {
        -: 3717:				     SV** restrict tv = av_fetch(av, j, 0);
       14: 3718:				     if (tv && SvOK(*tv)) {
       14: 3718-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 1
       13: 3719:				         total += SvNV(*tv);
       13: 3719-block  0
call    0 returned 13
       13: 3720:				         count++;
        -: 3721:				     } else {
        1: 3722:				         croak("mean: undefined value at array ref index %zu (argument %zu)", j, i);
        1: 3722-block  0
call    0 returned 0
        -: 3723:				     }
        -: 3724:				}
       3*: 3725:			} else if (SvOK(arg)) {
        3: 3725-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 3725-block  1
call    2 never executed
        3: 3726:				 total += SvNV(arg);
        3: 3726-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        -: 3727:				 count++;
        -: 3728:			} else {
        -: 3729:				 croak("mean: undefined value at argument index %zu", i);
        -: 3730:			}
        -: 3731:		}
        -: 3732:		if (count == 0) croak("mean needs >= 1 element");
        7: 3733:		RETVAL = total / count;
        7: 3734:	OUTPUT:
        -: 3735:	  RETVAL
        -: 3736:
       26: 3737:double sum(...)
        7: 3737-block  0
       19: 3737-block  1
       26: 3737-block  2
branch  0 taken 21
branch  1 taken 5 (fallthrough)
       21: 3738:	PROTOTYPE: @
       22: 3739:	INIT:
       21: 3739-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 19
        2: 3739-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        1: 3739-block  2
        2: 3740:	  double total = 0;
        2: 3741:	  size_t count = 0;
        2: 3741-block  0
call    0 returned 2
    10002: 3742:	CODE:
    10000: 3742-block  0
    10002: 3742-block  1
branch  0 taken 10001
branch  1 taken 1 (fallthrough)
    10001: 3743:	  for (size_t i = 0; i < items; i++) {
    10001: 3743-block  0
call    0 returned 10001
    10001: 3744:		   SV* restrict arg = ST(i);
branch  0 taken 10001 (fallthrough)
branch  1 taken 0
    10001: 3744-block  0
branch  2 taken 10000 (fallthrough)
branch  3 taken 1
    10000: 3745:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
    10000: 3746:		       AV* restrict av = (AV*)SvRV(arg);
    10000: 3746-block  0
call    0 returned 10000
    10000: 3747:		       size_t len = av_len(av) + 1;
    10000: 3748:		       for (size_t j = 0; j < len; j++) {
    10000: 3749:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 3750:		           if (tv && SvOK(*tv)) {
        1: 3751:		               total += SvNV(*tv);
        1: 3751-block  0
call    0 returned 0
        -: 3752:		               count++;
        -: 3753:		           } else {
       19: 3754:		               croak("sum: undefined value at array ref index %zu (argument %zu)", j, i);
       19: 3754-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 1
       18: 3755:		           }
       18: 3756:		       }
       18: 3756-block  0
call    0 returned 18
       18: 3757:		   } else if (SvOK(arg)) {
       18: 3758:		       total += SvNV(arg);
       18: 3759:		       count++;
        -: 3760:		   } else {
        1: 3761:		       croak("sum: undefined value at argument index %zu", i);
        1: 3761-block  0
call    0 returned 0
        -: 3762:		   }
        -: 3763:	  }
        5: 3764:	  if (count == 0) croak("sum needs >= 1 element");
        5: 3764-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        1: 3764-block  1
call    2 returned 0
        4: 3765:	  RETVAL = total;
        4: 3765-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
        -: 3766:	OUTPUT:
        -: 3767:	  RETVAL
        -: 3768:
        -: 3769:double sd(...)
        -: 3770:    PROTOTYPE: @
        -: 3771:    INIT:
        -: 3772:        double mean = 0.0, M2 = 0.0;
        8: 3773:        size_t count = 0;
        8: 3774:    CODE:
        -: 3775:        /* Single Pass Standard Deviation via Welford's Algorithm */
        -: 3776:        for (size_t i = 0; i < items; i++) {
       21: 3777:            SV* restrict arg = ST(i);
        8: 3777-block  0
       13: 3777-block  1
       21: 3777-block  2
branch  0 taken 15
branch  1 taken 6 (fallthrough)
       15: 3778:            if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
       18: 3779:                AV* restrict av = (AV*)SvRV(arg);
       15: 3779-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 11
        4: 3779-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        3: 3779-block  2
        4: 3780:                size_t len = av_len(av) + 1;
        4: 3781:                for (size_t j = 0; j < len; j++) {
        4: 3781-block  0
call    0 returned 4
    10015: 3782:                    SV** restrict tv = av_fetch(av, j, 0);
    10011: 3782-block  0
    10015: 3782-block  1
branch  0 taken 10012
branch  1 taken 3 (fallthrough)
    10012: 3783:                    if (tv && SvOK(*tv)) {
    10012: 3783-block  0
call    0 returned 10012
    10012: 3784:                        count++;
branch  0 taken 10012 (fallthrough)
branch  1 taken 0
    10012: 3784-block  0
branch  2 taken 10011 (fallthrough)
branch  3 taken 1
    10011: 3785:                        double val = SvNV(*tv);
    10011: 3786:                        double delta = val - mean;
    10011: 3786-block  0
call    0 returned 10011
    10011: 3787:                        mean += delta / count;
    10011: 3788:                        M2 += delta * (val - mean);
    10011: 3789:                    } else {
        -: 3790:                        croak("sd: undefined value at array ref index %zu (argument %zu)", j, i);
        1: 3791:                    }
        1: 3791-block  0
call    0 returned 0
        -: 3792:                }
        -: 3793:            } else if (SvOK(arg)) {
       11: 3794:                count++;
       11: 3794-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 1
       10: 3795:                double val = SvNV(arg);
       10: 3796:                double delta = val - mean;
       10: 3796-block  0
call    0 returned 10
       10: 3797:                mean += delta / count;
       10: 3798:                M2 += delta * (val - mean);
       10: 3799:            } else {
        -: 3800:                croak("sd: undefined value at argument index %zu", i);
        1: 3801:            }
        1: 3801-block  0
call    0 returned 0
        -: 3802:        }
        -: 3803:        if (count < 2) croak("sd needs >= 2 elements");
        6: 3804:        RETVAL = sqrt(M2 / (count - 1));
        6: 3804-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1: 3804-block  1
call    2 returned 0
        5: 3805:    OUTPUT:
        5: 3805-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        -: 3806:        RETVAL
        -: 3807:
        -: 3808:
        -: 3809:double var(...)
        -: 3810:	PROTOTYPE: @
        -: 3811:	INIT:
       47: 3812:	  double mean = 0.0, M2 = 0.0;
       47: 3813:	  size_t count = 0;
       47: 3814:	CODE:
       47: 3815:	  /* Single Pass Variance via Welford's Algorithm */
       47: 3816:	  for (size_t i = 0; i < items; i++) {
        -: 3817:		   SV* restrict arg = ST(i);
       47: 3818:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 3819:		       AV* restrict av = (AV*)SvRV(arg);
        -: 3820:		       size_t len = av_len(av) + 1;
       47: 3821:		       for (size_t j = 0; j < len; j++) {
branch  0 taken 47 (fallthrough)
branch  1 taken 0
       47: 3821-block  0
branch  2 taken 21 (fallthrough)
branch  3 taken 26
       21: 3821-block  1
branch  4 taken 21 (fallthrough)
branch  5 taken 0
       21: 3822:		           SV** restrict tv = av_fetch(av, j, 0);
       21: 3823:		           if (tv && SvOK(*tv)) {
       21: 3823-block  0
        -: 3824:		               count++;
        -: 3825:		               double val = SvNV(*tv);
        -: 3826:		               double delta = val - mean;
       47: 3827:		               mean += delta / count;
       47: 3827-block  0
branch  0 taken 47 (fallthrough)
branch  1 taken 0
       47: 3827-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 43
        4: 3827-block  2
branch  4 taken 4 (fallthrough)
branch  5 taken 0
        4: 3828:		               M2 += delta * (val - mean);
        4: 3829:		           } else {
        4: 3829-block  0
        -: 3830:		               croak("var: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 3831:		           }
        -: 3832:		       }
       47: 3833:		   } else if (SvOK(arg)) {
       47: 3833-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 47
    #####: 3834:		       count++;
    %%%%%: 3834-block  0
call    0 never executed
        -: 3835:		       double val = SvNV(arg);
        -: 3836:		       double delta = val - mean;
        -: 3837:		       mean += delta / count;
      123: 3838:		       M2 += delta * (val - mean);
       47: 3838-block  0
       76: 3838-block  1
      123: 3838-block  2
branch  0 taken 76
branch  1 taken 47 (fallthrough)
       76: 3839:		   } else {
       76: 3839-block  0
call    0 returned 76
       76: 3840:		       croak("var: undefined value at argument index %zu", i);
        -: 3841:		   }
       76: 3842:	  }
branch  0 taken 25 (fallthrough)
branch  1 taken 51
       25: 3842-block  0
       51: 3843:	  if (count < 2) croak("var needs >= 2 elements");
       51: 3843-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 46
        5: 3843-block  1
       46: 3844:	  RETVAL = M2 / (count - 1);
       46: 3844-block  0
branch  0 taken 35 (fallthrough)
branch  1 taken 11
       35: 3844-block  1
call    2 returned 35
       11: 3845:	OUTPUT:
       11: 3845-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 7
        4: 3845-block  1
call    2 returned 4
        7: 3846:	  RETVAL
        7: 3846-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 4
        3: 3846-block  1
call    2 returned 3
        4: 3847:
        4: 3847-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 2
        2: 3847-block  1
call    2 returned 2
        2: 3848:SV* t_test(...)
        2: 3848-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 3848-block  1
call    2 returned 2
    #####: 3849:	CODE:
    %%%%%: 3849-block  0
call    0 never executed
        -: 3850:	{
        -: 3851:		SV*restrict x_sv = NULL;
        -: 3852:		SV*restrict y_sv = NULL;
       47: 3853:		double mu = 0.0, conf_level = 0.95;
       47: 3853-block  0
branch  0 taken 46 (fallthrough)
branch  1 taken 1
       46: 3853-block  1
branch  2 taken 46 (fallthrough)
branch  3 taken 0
       46: 3853-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 46
        1: 3854:		bool paired = FALSE, var_equal = FALSE;
        1: 3854-block  0
call    0 returned 0
       46: 3855:		const char*restrict alternative = "two.sided";
       46: 3856:
       46: 3856-block  0
call    0 returned 46
      46*: 3857:		int arg_idx = 0;
branch  0 taken 0 (fallthrough)
branch  1 taken 46
    %%%%%: 3857-block  0
call    2 never executed
       46: 3858:
       46: 3859:		// 1. Shift first positional argument as 'x' if it's an array reference
       46: 3859-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 38
        8: 3859-block  1
branch  2 taken 8 (fallthrough)
branch  3 taken 0
        8: 3859-block  2
branch  4 taken 8 (fallthrough)
branch  5 taken 0
        8: 3860:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        8: 3860-block  0
        -: 3861:		  x_sv = ST(arg_idx);
       46: 3862:		  arg_idx++;
       46: 3862-block  0
branch  0 taken 46 (fallthrough)
branch  1 taken 0
       46: 3862-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 45
        1: 3863:		}
        1: 3863-block  0
call    0 returned 0
        -: 3864:
       45: 3865:		// 2. Shift second positional argument as 'y' if it's an array reference
       45: 3866:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
       45: 3866-block  0
call    0 returned 45
      411: 3867:		  y_sv = ST(arg_idx);
      411: 3867-block  0
branch  0 taken 366
branch  1 taken 45 (fallthrough)
      366: 3868:		  arg_idx++;
      366: 3868-block  0
call    0 returned 366
     366*: 3869:		}
branch  0 taken 366 (fallthrough)
branch  1 taken 0
      366: 3869-block  0
branch  2 taken 366 (fallthrough)
branch  3 taken 0
      366: 3869-block  1
call    4 returned 366
    %%%%%: 3869-block  2
      366: 3870:
      366: 3871:		// Ensure the remaining arguments form complete key-value pairs
      366: 3872:		if ((items - arg_idx) % 2 != 0) {
      366: 3872-block  0
        -: 3873:		  croak("Usage: t_test(\\@x, [\\@y], key => value, ...)");
       45: 3874:		}
       45: 3875:
       45: 3875-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 44
        1: 3875-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 3875-block  2
call    4 returned 0
        -: 3876:		// --- Parse named arguments from the remaining flat stack ---
       51: 3877:		for (; arg_idx < items; arg_idx += 2) {
       44: 3877-block  0
branch  0 taken 40 (fallthrough)
branch  1 taken 4
       40: 3877-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 35
        7: 3877-block  2
        9: 3878:			const char*restrict key = SvPV_nolen(ST(arg_idx));
        9: 3878-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 8
        1: 3878-block  1
call    2 returned 0
        8: 3879:			SV*restrict val = ST(arg_idx + 1);
        8: 3879-block  0
call    0 returned 8
        8: 3880:
branch  0 taken 3 (fallthrough)
branch  1 taken 5
        3: 3880-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 2
        1: 3880-block  1
call    4 returned 0
        7: 3881:			if      (strEQ(key, "x"))           x_sv        = val;
      104: 3882:			else if (strEQ(key, "y"))           y_sv        = val;
        7: 3882-block  0
      104: 3882-block  1
branch  0 taken 97
branch  1 taken 7 (fallthrough)
       97: 3883:			else if (strEQ(key, "mu"))          mu          = SvNV(val);
       97: 3883-block  0
call    0 returned 97
      97*: 3884:			else if (strEQ(key, "paired"))      paired      = SvTRUE(val);
branch  0 taken 97 (fallthrough)
branch  1 taken 0
       97: 3884-block  0
branch  2 taken 97 (fallthrough)
branch  3 taken 0
       97: 3884-block  1
call    4 returned 97
    %%%%%: 3884-block  2
       97: 3885:			else if (strEQ(key, "var_equal"))   var_equal   = SvTRUE(val);
       97: 3886:			else if (strEQ(key, "conf_level"))  conf_level  = SvNV(val);
       97: 3887:			else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
       97: 3887-block  0
        -: 3888:			else croak("t_test: unknown argument '%s'", key);
        7: 3889:		}
        7: 3890:
        7: 3890-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 5
        2: 3891:		// --- Validate required / types ---
       14: 3892:		if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        2: 3892-block  0
       14: 3892-block  1
branch  0 taken 12
branch  1 taken 2 (fallthrough)
       12: 3893:			croak("t_test: 'x' is a required argument and must be an ARRAY reference");
       12: 3893-block  0
call    0 returned 12
       12: 3894:		AV*restrict x_av = (AV*)SvRV(x_sv);
call    0 returned 12
      12*: 3895:		size_t nx = av_len(x_av) + 1;
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3895-block  0
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 3895-block  1
call    4 returned 12
    %%%%%: 3895-block  2
      12*: 3896:		if (nx < 2) croak("t_test: 'x' needs at least 2 elements");
       12: 3896-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3896-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 3896-block  2
call    4 returned 12
    %%%%%: 3896-block  3
       12: 3897:		AV*restrict y_av = NULL;
       12: 3898:		if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV)
       12: 3899:			y_av = (AV*)SvRV(y_sv);
       12: 3900:			
       12: 3900-block  0
        -: 3901:		if (conf_level <= 0.0 || conf_level >= 1.0)
        2: 3902:			croak("t_test: 'conf_level' must be between 0 and 1");
       2*: 3903:		// --- Computation via Welford's Algorithm --- */
        2: 3903-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3903-block  1
call    2 never executed
        2: 3904:		double mean_x = 0.0, M2_x = 0.0, var_x, t_stat, df, p_val, std_err, cint_est;
        2: 3905:		HV*restrict results = newHV();
        2: 3906:		for (size_t i = 0; i < nx; i++) {
        2: 3907:			SV**restrict tv = av_fetch(x_av, i, 0);
        2: 3908:			double val = (tv && SvOK(*tv)) ? SvNV(*tv) : 0;
        2: 3908-block  0
call    0 returned 2
call    1 returned 2
        5: 3909:			double delta = val - mean_x;
        5: 3909-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3
       2*: 3910:			mean_x += delta / (i + 1);
        2: 3910-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 3910-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 3910-block  2
call    4 never executed
        2: 3911:			M2_x += delta * (val - mean_x);
        2: 3912:		}
        2: 3913:		var_x = M2_x / (nx - 1);
        2: 3914:		if (var_x == 0.0 && !y_av) croak("t_test: data are essentially constant");
        2: 3915:
        2: 3916:		if (paired || y_av) {
        2: 3916-block  0
call    0 returned 2
call    1 returned 2
        2: 3917:			if (!y_av) croak("t_test: 'y' must be provided for paired or two-sample tests");
call    0 returned 2
call    1 returned 2
        -: 3918:			size_t ny = av_len(y_av) + 1;
       3*: 3919:			if (paired && ny != nx) croak("t_test: Paired arrays must be same length");
        3: 3919-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 3919-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 3919-block  2
call    4 never executed
        3: 3920:			double mean_y = 0.0, M2_y = 0.0, var_y;
        3: 3921:			for (size_t i = 0; i < ny; i++) {
        3: 3922:				 SV**restrict tv = av_fetch(y_av, i, 0);
        3: 3923:				 double val = (tv && SvOK(*tv)) ? SvNV(*tv) : 0;
        3: 3924:				 double delta = val - mean_y;
        3: 3925:				 mean_y += delta / (i + 1);
        3: 3926:				 M2_y += delta * (val - mean_y);
        3: 3927:			}
        3: 3927-block  0
call    0 returned 3
call    1 returned 3
        3: 3928:			var_y = M2_y / (ny - 1);
call    0 returned 3
call    1 returned 3
        -: 3929:			if (paired) {
        -: 3930:				 double mean_d = 0.0, M2_d = 0.0;
       35: 3931:				 for (size_t i = 0; i < nx; i++) {
       35: 3932:					  SV**restrict dx_ptr = av_fetch(x_av, i, 0);
       35: 3933:					  SV**restrict dy_ptr = av_fetch(y_av, i, 0);
       35: 3934:				     double dx = (dx_ptr && SvOK(*dx_ptr)) ? SvNV(*dx_ptr) : 0.0;
       35: 3935:				     double dy = (dy_ptr && SvOK(*dy_ptr)) ? SvNV(*dy_ptr) : 0.0;
       35: 3935-block  0
call    0 returned 35
call    1 returned 35
        -: 3936:				     double val = dx - dy;
       42: 3937:				     double delta = val - mean_d;
       42: 3937-block  0
call    0 returned 42
       42: 3938:				     mean_d += delta / (i + 1);
       42: 3939:				     M2_d += delta * (val - mean_d);
branch  0 taken 1 (fallthrough)
branch  1 taken 41
        1: 3940:				 }
        1: 3940-block  0
call    0 returned 1
        1: 3941:				 double var_d = M2_d / (nx - 1);
        1: 3942:				 if (var_d == 0.0) croak("t_test: data are essentially constant");
       41: 3943:				 cint_est = mean_d;
       41: 3943-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 40
        1: 3944:				 std_err  = sqrt(var_d / nx);
        1: 3944-block  0
call    0 returned 1
        1: 3945:				 t_stat   = (cint_est - mu) / std_err;
        1: 3946:				 df       = nx - 1;
        -: 3947:				 hv_store(results, "estimate", 8, newSVnv(mean_d), 0);
       40: 3948:			} else if (var_equal) {
       40: 3948-block  0
call    0 returned 40
       40: 3949:				 if (var_x == 0.0 && var_y == 0.0) croak("t_test: data are essentially constant");
       40: 3950:				 double pooled_var = ((nx - 1) * var_x + (ny - 1) * var_y) / (nx + ny - 2);
        -: 3951:				 cint_est = mean_x - mean_y;
       42: 3952:				 std_err  = sqrt(pooled_var * (1.0 / nx + 1.0 / ny));
       42: 3952-block  0
call    0 returned 42
       42: 3953:				 t_stat   = (cint_est - mu) / std_err;
call    0 returned 42
call    1 returned 42
       42: 3954:				 df       = nx + ny - 2;
call    0 returned 42
call    1 returned 42
       42: 3955:				 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
call    0 returned 42
call    1 returned 42
       42: 3956:				 hv_store(results, "estimate_y", 10, newSVnv(mean_y), 0);
call    0 returned 42
call    1 returned 42
       42: 3957:			} else {
call    0 returned 42
call    1 returned 42
       42: 3958:				 if (var_x == 0.0 && var_y == 0.0) croak("t_test: data are essentially constant");
call    0 returned 42
call    1 returned 42
       42: 3959:				 cint_est         = mean_x - mean_y;
call    0 returned 42
        -: 3960:				 double stderr_x2 = var_x / nx;
        -: 3961:				 double stderr_y2 = var_y / ny;
        -: 3962:				 std_err          = sqrt(stderr_x2 + stderr_y2);
        -: 3963:				 t_stat           = (cint_est - mu) / std_err;
        -: 3964:				 df = pow(stderr_x2 + stderr_y2, 2) /
        -: 3965:				      (pow(stderr_x2, 2) / (nx - 1) + pow(stderr_y2, 2) / (ny - 1));
       15: 3966:				 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
       15: 3966-block  0
branch  0 taken 14 (fallthrough)
branch  1 taken 1
       14: 3966-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 14
        1: 3967:				 hv_store(results, "estimate_y", 10, newSVnv(mean_y), 0);
        1: 3967-block  0
call    0 returned 0
        -: 3968:			}
       14: 3969:		} else {
       14: 3970:			cint_est = mean_x;
       14: 3970-block  0
call    0 returned 14
        -: 3971:			std_err  = sqrt(var_x / nx);
       14: 3972:			t_stat   = (cint_est - mu) / std_err;
branch  0 taken 1 (fallthrough)
branch  1 taken 13
        1: 3973:			df       = nx - 1;
        1: 3973-block  0
        -: 3974:			hv_store(results, "estimate", 8, newSVnv(mean_x), 0);
        -: 3975:		}
        -: 3976:		p_val = get_t_pvalue(t_stat, df, alternative);
       13: 3977:		double alpha = 1.0 - conf_level, t_crit, ci_lower, ci_upper;
      157: 3978:		if (strcmp(alternative, "less") == 0) {
       13: 3978-block  0
      144: 3978-block  1
      157: 3978-block  2
branch  0 taken 144
branch  1 taken 13 (fallthrough)
        -: 3979:			t_crit   = qt_tail(df, alpha);
       13: 3980:			ci_lower = -INFINITY;
       13: 3980-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 9
        4: 3980-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 2
        2: 3980-block  2
       13: 3981:			ci_upper = cint_est + t_crit * std_err;
       13: 3981-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 11
        2: 3981-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 3981-block  2
      13*: 3982:		} else if (strcmp(alternative, "greater") == 0) {
       13: 3982-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    %%%%%: 3982-block  1
        -: 3983:			t_crit   = qt_tail(df, alpha);
        -: 3984:			ci_lower = cint_est - t_crit * std_err;
        -: 3985:			ci_upper = INFINITY;
      13*: 3986:		} else {
       13: 3986-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    %%%%%: 3986-block  1
call    2 never executed
       13: 3986-block  2
call    3 returned 13
      13*: 3987:			t_crit   = qt_tail(df, alpha / 2.0);
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    %%%%%: 3987-block  0
call    2 never executed
       13: 3987-block  1
call    3 returned 13
        -: 3988:			ci_lower = cint_est - t_crit * std_err;
      369: 3989:			ci_upper = cint_est + t_crit * std_err;
      369: 3989-block  0
branch  0 taken 356
branch  1 taken 13 (fallthrough)
      356: 3990:		}
      356: 3990-block  0
call    0 returned 356
     356*: 3991:		AV*restrict conf_int = newAV();
branch  0 taken 356 (fallthrough)
branch  1 taken 0
      356: 3991-block  0
branch  2 taken 356 (fallthrough)
branch  3 taken 0
      356: 3991-block  1
call    4 returned 356
    %%%%%: 3991-block  2
      356: 3992:		av_push(conf_int, newSVnv(ci_lower));
      356: 3992-block  0
        -: 3993:		av_push(conf_int, newSVnv(ci_upper));
        -: 3994:		hv_store(results, "statistic", 9, newSVnv(t_stat), 0);
       13: 3995:		hv_store(results, "df",        2, newSVnv(df),     0);
       13: 3995-block  0
call    0 returned 13
        -: 3996:		hv_store(results, "p_value",   7, newSVnv(p_val),  0);
       13: 3997:		hv_store(results, "conf_int",  8, newRV_noinc((SV*)conf_int), 0);
branch  0 taken 2 (fallthrough)
branch  1 taken 11
       53: 3998:		RETVAL = newRV_noinc((SV*)results);
        2: 3998-block  0
       53: 3998-block  1
branch  0 taken 51
branch  1 taken 2
       51: 3999:	}
       51: 4000:	OUTPUT:
       51: 4000-block  0
branch  0 taken 19 (fallthrough)
branch  1 taken 32
       19: 4000-block  1
       32: 4000-block  2
       51: 4000-block  3
        -: 4001:	  RETVAL
       11: 4002:
       11: 4002-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 9
        2: 4003:void p_adjust(SV* p_sv, const char* method = "holm")
       53: 4004:	INIT:
        2: 4004-block  0
       53: 4004-block  1
branch  0 taken 51
branch  1 taken 2
       51: 4005:		if (!SvROK(p_sv) || SvTYPE(SvRV(p_sv)) != SVt_PVAV) {
       51: 4006:			croak("p_adjust: first argument must be an ARRAY reference of p-values");
       51: 4006-block  0
branch  0 taken 32 (fallthrough)
branch  1 taken 19
       32: 4006-block  1
       51: 4007:		}
       51: 4007-block  0
branch  0 taken 22 (fallthrough)
branch  1 taken 29
       22: 4007-block  1
       29: 4007-block  2
       51: 4007-block  3
        -: 4008:		AV *restrict p_av = (AV*)SvRV(p_sv);
        9: 4009:		size_t n = av_len(p_av) + 1;
        9: 4009-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 7
        2: 4010:		// Handle empty input
       53: 4011:		if (n == 0) {
        2: 4011-block  0
       53: 4011-block  1
branch  0 taken 51
branch  1 taken 2
       51: 4012:			XSRETURN_EMPTY;
       51: 4013:		}
       51: 4013-block  0
branch  0 taken 22 (fallthrough)
branch  1 taken 29
       22: 4013-block  1
      51*: 4014:		// Normalize method string
       51: 4014-block  0
branch  0 taken 51 (fallthrough)
branch  1 taken 0
       51: 4014-block  1
    %%%%%: 4014-block  2
       51: 4014-block  3
        -: 4015:		char meth[64];
        7: 4016:		strncpy(meth, method, 63); meth[63] = '\0';
        7: 4016-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 5
        2: 4017:		for(unsigned short int i = 0; meth[i]; i++) meth[i] = tolower(meth[i]);
       53: 4018:		// Resolve aliases
        2: 4018-block  0
       53: 4018-block  1
branch  0 taken 51
branch  1 taken 2
       51: 4019:		if (strstr(meth, "benjamini") && strstr(meth, "hochberg")) strcpy(meth, "bh");
       51: 4020:		if (strstr(meth, "benjamini") && strstr(meth, "yekutieli")) strcpy(meth, "by");
       51: 4020-block  0
branch  0 taken 41 (fallthrough)
branch  1 taken 10
       41: 4020-block  1
      51*: 4021:		if (strcmp(meth, "fdr") == 0) strcpy(meth, "bh");
       51: 4021-block  0
branch  0 taken 51 (fallthrough)
branch  1 taken 0
       51: 4021-block  1
    %%%%%: 4021-block  2
       51: 4021-block  3
        -: 4022:		// Allocate C memory
        5: 4023:		PVal *restrict arr;
        5: 4023-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 4024:		double *restrict adj;
       53: 4025:		Newx(arr, n, PVal);
        2: 4025-block  0
       51: 4025-block  1
       53: 4025-block  2
branch  0 taken 51
branch  1 taken 2 (fallthrough)
        2: 4026:		Newx(adj, n, double);
       53: 4027:
        2: 4027-block  0
       53: 4027-block  1
branch  0 taken 51
branch  1 taken 2
       51: 4028:		for (size_t i = 0; i < n; i++) {
       51: 4029:			SV**restrict tv = av_fetch(p_av, i, 0);
       51: 4029-block  0
branch  0 taken 22 (fallthrough)
branch  1 taken 29
       22: 4029-block  1
       51: 4030:			arr[i].p = (tv && SvOK(*tv)) ? SvNV(*tv) : 1.0;
       51: 4030-block  0
branch  0 taken 26 (fallthrough)
branch  1 taken 25
       26: 4030-block  1
       25: 4030-block  2
       51: 4030-block  3
        -: 4031:			arr[i].orig_idx = i;
        3: 4032:		}
        3: 4032-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        -: 4033:		// Sort ascending (Stable sort using original index)
       2*: 4034:		qsort(arr, n, sizeof(PVal), cmp_pval);
        2: 4034-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 4034-block  1
call    2 never executed
        2: 4034-block  2
call    3 returned 2
       2*: 4035:	PPCODE:
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 4035-block  0
call    2 never executed
        2: 4035-block  1
call    3 returned 2
        -: 4036:		if (strcmp(meth, "bonferroni") == 0) {
        2: 4037:			for (size_t i = 0; i < n; i++) {
       51: 4038:				double v = arr[i].p * n;
       49: 4038-block  0
       51: 4038-block  1
branch  0 taken 49
branch  1 taken 2 (fallthrough)
       49: 4039:				adj[arr[i].orig_idx] = (v < 1.0) ? v : 1.0;
       49: 4040:			}
       49: 4040-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 49
    #####: 4041:		} else if (strcmp(meth, "holm") == 0) {
    %%%%%: 4041-block  0
        -: 4042:			double cummax = 0.0;
        -: 4043:			for (size_t i = 0; i < n; i++) {
        -: 4044:				 double v = arr[i].p * (n - i);
       53: 4045:				 if (v > cummax) cummax = v;
        2: 4045-block  0
       53: 4045-block  1
branch  0 taken 51
branch  1 taken 2 (fallthrough)
       51: 4046:				 adj[arr[i].orig_idx] = (cummax < 1.0) ? cummax : 1.0;
       51: 4047:			}
       51: 4047-block  0
        -: 4048:		} else if (strcmp(meth, "hochberg") == 0) {
       50: 4049:			double cummin = 1.0;
        2: 4049-block  0
       48: 4049-block  1
       50: 4049-block  2
branch  0 taken 48
branch  1 taken 2 (fallthrough)
       48: 4050:			for (ssize_t i = n - 1; i >= 0; i--) {
       48: 4051:				 double v = arr[i].p * (n - i);
        -: 4052:				 if (v < cummin) cummin = v;
       48: 4053:				 adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
     1176: 4054:			}
       48: 4054-block  0
     1128: 4054-block  1
     1176: 4054-block  2
branch  0 taken 1128
branch  1 taken 48 (fallthrough)
     1128: 4055:		} else if (strcmp(meth, "bh") == 0) {
     1128: 4056:			double cummin = 1.0;
     1128: 4056-block  0
branch  0 taken 266 (fallthrough)
branch  1 taken 862
      266: 4057:			for (ssize_t i = n - 1; i >= 0; i--) {
      266: 4057-block  0
        -: 4058:				double v = arr[i].p * n / (i + 1.0);
        -: 4059:				if (v < cummin) cummin = v;
        -: 4060:				adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
     1272: 4061:			}
       48: 4061-block  0
     1272: 4061-block  1
branch  0 taken 1224
branch  1 taken 48 (fallthrough)
     1224: 4062:		} else if (strcmp(meth, "by") == 0) {
     1224: 4063:			double q = 0.0;
     1224: 4063-block  0
branch  0 taken 847 (fallthrough)
branch  1 taken 377
      847: 4063-block  1
      377: 4063-block  2
     1224: 4063-block  3
        -: 4064:			for (size_t i = 1; i <= n; i++) q += 1.0 / i;
        -: 4065:			double cummin = 1.0;
     1224: 4066:			for (ssize_t i = n - 1; i >= 0; i--) {
       48: 4066-block  0
     1224: 4066-block  1
branch  0 taken 1176
branch  1 taken 48 (fallthrough)
     1176: 4067:				double v = arr[i].p * n / (i + 1.0) * q;
     1176: 4067-block  0
        -: 4068:				if (v < cummin) cummin = v;
        -: 4069:				adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
     2448: 4070:			}
       48: 4070-block  0
     2400: 4070-block  1
     2448: 4070-block  2
branch  0 taken 2400
branch  1 taken 48 (fallthrough)
     2400: 4071:		} else if (strcmp(meth, "hommel") == 0) {
     2400: 4071-block  0
branch  0 taken 1401 (fallthrough)
branch  1 taken 999
     1401: 4072:			double *restrict pa, *restrict q_arr;
     1401: 4072-block  0
        -: 4073:			Newx(pa, n, double);
        -: 4074:			Newx(q_arr, n, double);
        -: 4075:			// Initial: min(n * p[i] / (i + 1))
        -: 4076:			double min_val = n * arr[0].p;
       53: 4077:			for (size_t i = 1; i < n; i++) {
        2: 4077-block  0
       53: 4077-block  1
branch  0 taken 51
branch  1 taken 2 (fallthrough)
       51: 4078:				double temp = (n * arr[i].p) / (i + 1.0);
       51: 4078-block  0
branch  0 taken 50 (fallthrough)
branch  1 taken 1
       50: 4078-block  1
        1: 4078-block  2
      51*: 4079:				if (temp < min_val) {
       51: 4079-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 51
    %%%%%: 4079-block  1
       51: 4080:				   min_val = temp;
       51: 4080-block  0
        -: 4081:				}
        2: 4082:			}
        2: 4082-block  0
call    0 returned 2
call    1 returned 2
        1: 4083:			// pa <- q <- rep(min, n)
        1: 4083-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 4084:			for (size_t i = 0; i < n; i++) {
    %%%%%: 4084-block  0
    %%%%%: 4084-block  1
branch  0 never executed
branch  1 never executed
    #####: 4085:				 pa[i] = min_val;
    %%%%%: 4085-block  0
        -: 4086:				 q_arr[i] = min_val;
        -: 4087:			}
        1: 4088:			for (size_t j = n - 1; j >= 2; j--) {
        1: 4088-block  0
call    0 returned 1
call    1 returned 1
        1: 4089:				 ssize_t n_mj = n - j;       // Max index for 'ij'. Length is n_mj + 1
call    0 returned 0
        -: 4090:				 ssize_t i2_len = j - 1;     // Length of 'i2
        -: 4091:				 // Calculate q1 = min(j * p[i2] / (2:j))
      12*: 4092:				 double q1 = (j * arr[n_mj + 1].p) / 2.0;
       12: 4092-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 4092-block  1
call    2 never executed
      318: 4093:				 for (size_t k = 1; k < i2_len; k++) {
       12: 4093-block  0
      318: 4093-block  1
branch  0 taken 306
branch  1 taken 12 (fallthrough)
      306: 4094:				     double temp_q1 = (j * arr[n_mj + 1 + k].p) / (2.0 + k);
      306: 4094-block  0
call    0 returned 306
call    1 returned 306
        -: 4095:				     if (temp_q1 < q1) {
       12: 4096:				         q1 = temp_q1;
       12: 4096-block  0
call    0 returned 12
       12: 4097:				     }
call    0 returned 12
        -: 4098:				 }
        -: 4099:				 // q[ij] <- pmin(j * p[ij], q1)
        -: 4100:				 for (size_t i = 0; i <= n_mj; i++) {
        -: 4101:				     double v = j * arr[i].p;
        8: 4102:				     q_arr[i] = (v < q1) ? v : q1;
        -: 4103:				 }
        8: 4104:				 // q[i2] <- q[n - j]
        -: 4105:				 for (size_t i = 0; i < i2_len; i++) {
        -: 4106:				     q_arr[n_mj + 1 + i] = q_arr[n_mj];
       18: 4107:				}
        8: 4107-block  0
       10: 4107-block  1
       18: 4107-block  2
branch  0 taken 12
branch  1 taken 6 (fallthrough)
       12: 4108:				 // pa <- pmax(pa, q)
       16: 4109:				for (size_t i = 0; i < n; i++) {
       12: 4109-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 7
        5: 4109-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        4: 4109-block  2
        5: 4110:				    if (pa[i] < q_arr[i]) {
        5: 4111:				       pa[i] = q_arr[i];
        5: 4111-block  0
call    0 returned 5
       45: 4112:				    }
       45: 4112-block  0
branch  0 taken 41
branch  1 taken 4 (fallthrough)
       41: 4113:				}
       41: 4113-block  0
call    0 returned 41
       41: 4114:			}
branch  0 taken 41 (fallthrough)
branch  1 taken 0
       41: 4114-block  0
branch  2 taken 40 (fallthrough)
branch  3 taken 1
       40: 4115:			// pmin(1, pmax(pa, p))[ro] — map sorted results back to original indices
       40: 4115-block  0
        -: 4116:			for (size_t i = 0; i < n; i++) {
        1: 4117:				double v = (pa[i] > arr[i].p) ? pa[i] : arr[i].p;
        1: 4117-block  0
call    0 returned 0
        -: 4118:				if (v > 1.0) v = 1.0;
        -: 4119:				adj[arr[i].orig_idx] = v;
        7: 4120:			}
        7: 4120-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 4121:			Safefree(pa);  Safefree(q_arr);
        6: 4121-block  0
        -: 4122:		} else if (strcmp(meth, "none") == 0) {
        1: 4123:			for (size_t i = 0; i < n; i++) {
        1: 4123-block  0
call    0 returned 0
        -: 4124:				adj[arr[i].orig_idx] = arr[i].p;
        -: 4125:			}
        6: 4126:		} else {
        6: 4126-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1: 4126-block  1
call    2 returned 0
        -: 4127:			Safefree(arr); Safefree(adj);
        -: 4128:			croak("Unknown p-value adjustment method: %s", method);
       5*: 4129:		}
        5: 4129-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 4129-block  1
call    2 never executed
        5: 4129-block  2
call    3 returned 5
        -: 4130:		// Push values onto the Perl stack as a flat list
        -: 4131:		EXTEND(SP, n);
       13: 4132:		for (size_t i = 0; i < n; i++) {
        8: 4132-block  0
       13: 4132-block  1
branch  0 taken 8
branch  1 taken 5 (fallthrough)
        8: 4133:			PUSHs(sv_2mortal(newSVnv(adj[i])));
       12: 4134:		}
        8: 4134-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 4
        4: 4134-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        4: 4134-block  2
        4: 4135:		Safefree(arr); arr = NULL;
        4: 4136:		Safefree(adj); adj = NULL;
        4: 4136-block  0
call    0 returned 4
       43: 4137:
       39: 4137-block  0
       43: 4137-block  1
branch  0 taken 39
branch  1 taken 4 (fallthrough)
       39: 4138:double median(...)
       39: 4138-block  0
call    0 returned 39
       39: 4139:	PROTOTYPE: @
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 4139-block  0
branch  2 taken 39 (fallthrough)
branch  3 taken 0
       39: 4140:	INIT:
       39: 4140-block  0
call    0 returned 39
        -: 4141:	  size_t total_count = 0, k = 0;
    #####: 4142:	  double* restrict nums;
    %%%%%: 4142-block  0
call    0 never executed
    #####: 4143:	  double median_val = 0.0;
call    0 never executed
        -: 4144:	CODE:
        -: 4145:	  /* Pass 1: Count valid elements — die immediately on any undef */
        4: 4146:	  for (size_t i = 0; i < items; i++) {
        4: 4146-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 4147:		   SV* restrict arg = ST(i);
        4: 4147-block  0
call    0 returned 4
        -: 4148:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
    #####: 4149:		       AV* restrict av = (AV*)SvRV(arg);
    %%%%%: 4149-block  0
call    0 never executed
    #####: 4150:		       size_t len = av_len(av) + 1;
call    0 never executed
        -: 4151:		       for (size_t j = 0; j < len; j++) {
        -: 4152:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 4153:		           if (tv && SvOK(*tv)) {
        -: 4154:		               total_count++;
        5: 4155:		           } else {
        5: 4155-block  0
call    0 returned 5
        5: 4156:		               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4: 4157:		           }
        4: 4157-block  0
        -: 4158:		       }
        1: 4159:		   } else if (SvOK(arg)) {
        1: 4159-block  0
        -: 4160:		       total_count++;
        5: 4161:		   } else {
        5: 4161-block  0
call    0 returned 5
        5: 4162:		       croak("median: undefined value at argument index %zu", i);
        5: 4163:		   }
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        -: 4164:	  }
        -: 4165:	  if (total_count == 0) croak("median needs >= 1 element");
        -: 4166:
        -: 4167:	  /* Allocate C array now that we know the exact size */
        -: 4168:	  Newx(nums, total_count, double);
        -: 4169:
        6: 4170:	  /* Pass 2: Populate the C array — Safefree before any croak */
        6: 4170-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 4171:	  for (size_t i = 0; i < items; i++) {
        3: 4171-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 4172:		   SV* restrict arg = ST(i);
        2: 4172-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 4173:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        1: 4173-block  0
call    0 returned 0
        -: 4174:		       AV* restrict av = (AV*)SvRV(arg);
        -: 4175:		       size_t len = av_len(av) + 1;
        -: 4176:		       for (size_t j = 0; j < len; j++) {
        5: 4177:		           SV** restrict tv = av_fetch(av, j, 0);
        5: 4177-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4177-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 5
    #####: 4178:		           if (tv && SvOK(*tv)) {
    %%%%%: 4178-block  0
call    0 never executed
        -: 4179:		               nums[k++] = SvNV(*tv);
        5: 4180:		           } else {
        5: 4181:		               Safefree(nums);
        5: 4181-block  0
call    0 returned 5
       5*: 4182:		               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 4182-block  0
call    2 never executed
        -: 4183:		           }
        -: 4184:		       }
        5: 4185:		   } else if (SvOK(arg)) {
        -: 4186:		       nums[k++] = SvNV(arg);
        5: 4187:		   } else {
        5: 4187-block  0
call    0 returned 5
        5: 4188:		       Safefree(nums);
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4188-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 4
        1: 4188-block  1
branch  4 taken 1 (fallthrough)
branch  5 taken 0
        1: 4189:		       croak("median: undefined value at argument index %zu", i);
        1: 4189-block  0
        -: 4190:		   }
        -: 4191:	  }
        -: 4192:
      10*: 4193:	  /* Sort and calculate median */
        5: 4193-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4193-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 4193-block  2
    %%%%%: 4193-block  3
        5: 4194:	  qsort(nums, total_count, sizeof(double), compare_doubles);
        5: 4194-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        -: 4195:	  if (total_count % 2 == 0) {
       5*: 4196:		   median_val = (nums[total_count / 2 - 1] + nums[total_count / 2]) / 2.0;
        5: 4196-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4196-block  1
    %%%%%: 4196-block  2
       5*: 4197:	  } else {
        5: 4197-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4197-block  1
call    2 returned 5
    %%%%%: 4197-block  2
        -: 4198:		   median_val = nums[total_count / 2];
        5: 4199:	  }
        5: 4200:	  Safefree(nums);
        5: 4200-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4200-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 4201:	  nums = NULL;
        5: 4201-block  0
call    0 returned 5
        5: 4202:	  RETVAL = median_val;
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4202-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 4
        1: 4202-block  1
branch  4 taken 1 (fallthrough)
branch  5 taken 0
        1: 4203:	OUTPUT:
        1: 4203-block  0
        -: 4204:	  RETVAL
        -: 4205:
        -: 4206:SV* cor(SV* x_sv, SV* y_sv = &PL_sv_undef, const char* method = "pearson")
        -: 4207:	INIT:
        5: 4208:	// --- validate method -------------------------------------------
        5: 4208-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 1
        4: 4208-block  1
branch  2 taken 4 (fallthrough)
branch  3 taken 0
        4: 4209:	if (strcmp(method, "pearson")  != 0 &&
        4: 4209-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
        -: 4210:		strcmp(method, "spearman") != 0 &&
    #####: 4211:		strcmp(method, "kendall")  != 0)
    %%%%%: 4211-block  0
call    0 never executed
        -: 4212:		  croak("cor: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')",
        4: 4213:		        method);
        4: 4213-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1: 4214:
        1: 4214-block  0
call    0 returned 0
        -: 4215:	// --- validate x ------------------------------------------------
        -: 4216:	if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        3: 4217:		  croak("cor: x must be an ARRAY reference");
        3: 4217-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 4218:
    %%%%%: 4218-block  0
call    0 never executed
        -: 4219:	AV*restrict x_av = (AV*)SvRV(x_sv);
        -: 4220:	size_t nx   = av_len(x_av) + 1;
       3*: 4221:	if (nx == 0) croak("cor: x is empty");
        3: 4221-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4221-block  1
call    2 never executed
        3: 4221-block  2
call    3 returned 3
       3*: 4222:
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4222-block  0
call    2 never executed
        3: 4222-block  1
call    3 returned 3
        -: 4223:	// --- detect whether x is a flat vector or a matrix (AoA) -------
       30: 4224:	bool x_is_matrix = 0;
       30: 4224-block  0
branch  0 taken 27
branch  1 taken 3 (fallthrough)
       27: 4225:	{
       27: 4225-block  0
call    0 returned 27
      27*: 4226:		  SV**restrict fp = av_fetch(x_av, 0, 0);
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 4226-block  0
branch  2 taken 27 (fallthrough)
branch  3 taken 0
       27: 4226-block  1
call    4 returned 27
branch  5 taken 27 (fallthrough)
branch  6 taken 0
       27: 4226-block  2
call    7 returned 27
    %%%%%: 4226-block  3
       27: 4226-block  4
        -: 4227:		  if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
       30: 4228:		      x_is_matrix = 1;
        3: 4228-block  0
       30: 4228-block  1
branch  0 taken 27
branch  1 taken 3 (fallthrough)
       27: 4229:	}
       27: 4229-block  0
call    0 returned 27
      27*: 4230:
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 4230-block  0
branch  2 taken 27 (fallthrough)
branch  3 taken 0
       27: 4230-block  1
call    4 returned 27
branch  5 taken 27 (fallthrough)
branch  6 taken 0
       27: 4230-block  2
call    7 returned 27
    %%%%%: 4230-block  3
       27: 4230-block  4
        -: 4231:	// --- detect y ----------------------------
        -: 4232:	bool has_y = (SvOK(y_sv) && SvROK(y_sv) &&
        3: 4233:		           SvTYPE(SvRV(y_sv)) == SVt_PVAV);
        3: 4233-block  0
call    0 returned 3
        3: 4234:
call    0 returned 3
call    1 returned 3
        3: 4235:	AV*restrict y_av = has_y ? (AV*)SvRV(y_sv) : NULL;
call    0 returned 3
        -: 4236:	size_t ny = has_y ? av_len(y_av) + 1 : 0;
        -: 4237:
        -: 4238:	bool y_is_matrix = 0;
        1: 4239:	if (has_y && ny > 0) {
        1: 4239-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 4240:		SV**restrict fp = av_fetch(y_av, 0, 0);
    %%%%%: 4240-block  0
call    0 never executed
        -: 4241:		if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
        -: 4242:			y_is_matrix = 1;
        1: 4243:	}
        1: 4243-block  0
call    0 returned 1
        1: 4244:
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4244-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4244-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    #####: 4245:	CODE:
    %%%%%: 4245-block  0
call    0 never executed
        -: 4246:	// Branch 1: both inputs are flat vectors  →  scalar result
        1: 4247:	if (!x_is_matrix && !y_is_matrix) {
        1: 4247-block  0
call    0 returned 1
       1*: 4248:		  if (!has_y) {
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4248-block  0
call    2 never executed
        -: 4249:		      /* cor(vector) == 1 by definition */
        1: 4250:		      RETVAL = newSVnv(1.0);
        -: 4251:		  } else {
        -: 4252:		      if (nx != ny)
        4: 4253:		          croak("cor: x and y must have the same length (%lu vs %lu)",
        1: 4253-block  0
        3: 4253-block  1
        4: 4253-block  2
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3: 4254:		                nx, ny);
        3: 4254-block  0
call    0 returned 3
        3: 4255:
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4255-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4255-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    #####: 4256:		      if (nx < 2)
    %%%%%: 4256-block  0
call    0 never executed
        -: 4257:		          croak("cor: need at least 2 observations");
        -: 4258:
        1: 4259:		      double *restrict xd, *restrict yd;
        1: 4259-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4259-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
       1*: 4260:		      Newx(xd, nx, double);
        1: 4260-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4260-block  1
call    2 never executed
        4: 4261:		      Newx(yd, ny, double);
        1: 4261-block  0
        3: 4261-block  1
        4: 4261-block  2
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3: 4262:
        3: 4262-block  0
call    0 returned 3
        3: 4263:		      bool x_sd0 = 1, y_sd0 = 1;
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4263-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4263-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    #####: 4264:		      double x_first = NAN, y_first = NAN;
    %%%%%: 4264-block  0
call    0 never executed
        -: 4265:
        -: 4266:		      for (size_t i = 0; i < nx; i++) {
        -: 4267:		          SV**restrict tv = av_fetch(x_av, i, 0);
        -: 4268:		          double val = (tv && SvOK(*tv) && looks_like_number(*tv)) ? SvNV(*tv) : NAN;
        -: 4269:		          xd[i] = val;
       1*: 4270:		          if (!isnan(val)) {
        1: 4270-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4270-block  1
call    2 never executed
        1: 4270-block  2
call    3 returned 1
        -: 4271:		              if (isnan(x_first)) x_first = val;
        3: 4272:		              else if (val != x_first) x_sd0 = 0;
        2: 4272-block  0
        3: 4272-block  1
branch  0 taken 2
branch  1 taken 1 (fallthrough)
       2*: 4273:		          }
        2: 4273-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 4273-block  1
call    2 never executed
        2: 4273-block  2
call    3 returned 2
        8: 4274:		      }
        8: 4274-block  0
branch  0 taken 6
branch  1 taken 2 (fallthrough)
        6: 4275:		      for (size_t i = 0; i < ny; i++) {
        6: 4275-block  0
call    0 returned 6
        6: 4276:		          SV**restrict tv = av_fetch(y_av, i, 0);
        6: 4277:		          double val = (tv && SvOK(*tv) && looks_like_number(*tv)) ? SvNV(*tv) : NAN;
call    0 returned 6
       6*: 4278:		          yd[i] = val;
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4278-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 4278-block  1
call    4 returned 6
branch  5 taken 6 (fallthrough)
branch  6 taken 0
        6: 4278-block  2
call    7 returned 6
    %%%%%: 4278-block  3
        6: 4278-block  4
        -: 4279:		          if (!isnan(val)) {
        -: 4280:		              if (isnan(y_first)) y_first = val;
        -: 4281:		              else if (val != y_first) y_sd0 = 0;
        -: 4282:		          }
        -: 4283:		      }
        1: 4284:
        1: 4285:		      if (x_sd0 || y_sd0) {
        -: 4286:		          Safefree(xd); Safefree(yd);
        -: 4287:		          if (x_sd0) croak("cor: standard deviation of x is 0");
        2: 4288:		          croak("cor: standard deviation of y is 0");
        1: 4288-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4288-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4288-block  2
        -: 4289:		      }
        1: 4290:
        1: 4290-block  0
call    0 returned 1
        1: 4291:		      double r = compute_cor(xd, yd, nx, method);
call    0 returned 1
       1*: 4292:		      Safefree(xd); Safefree(yd);
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4292-block  0
call    2 never executed
        -: 4293:		      RETVAL = newSVnv(r);
       1*: 4294:		  }
        1: 4294-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4294-block  1
call    2 never executed
        1: 4294-block  2
call    3 returned 1
        3: 4295:	} else {//Branch 2: x is a matrix (or y is a matrix)  →  AoA result
        2: 4295-block  0
        3: 4295-block  1
branch  0 taken 2
branch  1 taken 1 (fallthrough)
       2*: 4296:		  // -- resolve x matrix dimensions
        2: 4296-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 4296-block  1
call    2 never executed
        2: 4296-block  2
call    3 returned 2
        8: 4297:		  if (!x_is_matrix)
        8: 4297-block  0
branch  0 taken 6
branch  1 taken 2 (fallthrough)
        6: 4298:		      croak("cor: x must be a matrix (array ref of array refs) "
        6: 4298-block  0
call    0 returned 6
        6: 4299:		            "when y is a matrix");
        6: 4300:
call    0 returned 6
       6*: 4301:		  SV**restrict xr0 = av_fetch(x_av, 0, 0);
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4301-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 4301-block  1
call    4 returned 6
branch  5 taken 6 (fallthrough)
branch  6 taken 0
        6: 4301-block  2
call    7 returned 6
    %%%%%: 4301-block  3
        6: 4301-block  4
        -: 4302:		  if (!xr0 || !SvROK(*xr0) || SvTYPE(SvRV(*xr0)) != SVt_PVAV)
        -: 4303:		      croak("cor: each row of x must be an ARRAY reference");
        -: 4304:
    #####: 4305:		  size_t ncols_x = av_len((AV*)SvRV(*xr0)) + 1;
    #####: 4306:		  if (ncols_x == 0) croak("cor: x matrix has zero columns");
    #####: 4307:
    %%%%%: 4307-block  0
        -: 4308:		  size_t nrows   = nx;    /* observations */
        1: 4309:
        1: 4309-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 4310:		  // PRE-VALIDATION PASS: Ensure all rows are arrays to prevent memory leaks on croak
    %%%%%: 4310-block  0
call    0 never executed
        -: 4311:		  for (size_t i = 0; i < nrows; i++) {
        1: 4312:		      SV**restrict rv = av_fetch(x_av, i, 0);
        1: 4312-block  0
call    0 returned 1
        1: 4313:		      if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
call    0 returned 1
        -: 4314:		          croak("cor: x row %lu is not an array ref", i);
        -: 4315:		  }
       1*: 4316:		  
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4316-block  0
call    2 never executed
        1: 4316-block  1
call    3 returned 1
        3: 4317:		  if (has_y && y_is_matrix) {
        3: 4317-block  0
branch  0 taken 2
branch  1 taken 1 (fallthrough)
        2: 4318:		      if (ny != nrows) croak("cor: x and y must have the same number of rows (%lu vs %lu)", nrows, ny);
        2: 4318-block  0
call    0 returned 2
        2: 4319:		      for (size_t i = 0; i < nrows; i++) {
call    0 returned 2
        -: 4320:		          SV**restrict rv = av_fetch(y_av, i, 0);
        1: 4321:		          if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
        1: 4321-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
        -: 4322:		              croak("cor: y row %lu is not an array ref", i);
        -: 4323:		      }
    #####: 4324:		  }
    %%%%%: 4324-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4324-block  1
call    2 never executed
    %%%%%: 4324-block  2
call    3 never executed
    #####: 4325:
    %%%%%: 4325-block  0
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4326:		  // -- extract x columns
    %%%%%: 4326-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4326-block  1
call    2 never executed
    %%%%%: 4326-block  2
call    3 never executed
        -: 4327:		  double **restrict col_x;
    #####: 4328:		  Newx(col_x, ncols_x, double*);
    %%%%%: 4328-block  0
    %%%%%: 4328-block  1
    %%%%%: 4328-block  2
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4329:
    #####: 4330:		  for (size_t j = 0; j < ncols_x; j++) {
    %%%%%: 4330-block  0
    %%%%%: 4330-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4331:		      Newx(col_x[j], nrows, double);
    %%%%%: 4331-block  0
call    0 never executed
    #####: 4332:		      bool sd0 = 1;
    #####: 4333:		      double first = NAN;
        -: 4334:		      for (size_t i = 0; i < nrows; i++) {
        -: 4335:		          SV**restrict rv = av_fetch(x_av, i, 0);
        -: 4336:		          AV*restrict  row = (AV*)SvRV(*rv);
    #####: 4337:		          SV**restrict cv  = av_fetch(row, j, 0);
    %%%%%: 4337-block  0
    %%%%%: 4337-block  1
    %%%%%: 4337-block  2
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4338:		          double val = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
    %%%%%: 4338-block  0
    %%%%%: 4338-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4339:		          col_x[j][i] = val;
    %%%%%: 4339-block  0
call    0 never executed
call    1 never executed
        -: 4340:		          if (!isnan(val)) {
    #####: 4341:		              if (isnan(first)) first = val;
    %%%%%: 4341-block  0
    %%%%%: 4341-block  1
call    0 never executed
    %%%%%: 4341-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 4342:		              else if (val != first) sd0 = 0;
    %%%%%: 4342-block  0
call    0 never executed
        -: 4343:		          }
        -: 4344:		      }
        3: 4345:		      if (sd0) {
        1: 4345-block  0
        2: 4345-block  1
        3: 4345-block  2
branch  0 taken 2
branch  1 taken 1 (fallthrough)
        6: 4346:		          for (size_t k = 0; k <= j; k++) Safefree(col_x[k]);
        2: 4346-block  0
        6: 4346-block  1
branch  0 taken 4
branch  1 taken 2 (fallthrough)
        4: 4347:		          Safefree(col_x);
        4: 4347-block  0
call    0 returned 4
call    1 returned 4
call    2 returned 4
        -: 4348:		          croak("cor: standard deviation is 0 in x column %lu", j);
        -: 4349:		      }
        3: 4350:		  }
        1: 4350-block  0
        3: 4350-block  1
branch  0 taken 2
branch  1 taken 1 (fallthrough)
        2: 4351:
        2: 4351-block  0
call    0 returned 2
call    1 returned 2
        1: 4352:		  // -- resolve y: separate matrix or re-use x (symmetric)
        1: 4352-block  0
call    0 returned 1
        -: 4353:		  size_t ncols_y;
        3: 4354:		  double **restrict col_y   = NULL;
        2: 4354-block  0
call    0 returned 2
        3: 4354-block  1
branch  1 taken 2
branch  2 taken 1 (fallthrough)
        1: 4355:		  bool symmetric = 0;
        1: 4355-block  0
call    0 returned 1
        1: 4356:
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        3: 4357:		  // 1 = cor(X) — result is symmetric
        1: 4357-block  0
        2: 4357-block  1
call    0 returned 2
        3: 4357-block  2
branch  1 taken 2
branch  2 taken 1 (fallthrough)
        1: 4358:		  if (has_y && y_is_matrix) {
        1: 4358-block  0
call    0 returned 1
        -: 4359:		      // cross-correlation: X (nrows × p) vs Y (nrows × q)
        1: 4360:		      SV**restrict yr0 = av_fetch(y_av, 0, 0);
        1: 4360-block  0
call    0 returned 1
        -: 4361:		      ncols_y = av_len((AV*)SvRV(*yr0)) + 1;
        -: 4362:		      if (ncols_y == 0) croak("cor: y matrix has zero columns");
        -: 4363:
        -: 4364:		      Newx(col_y, ncols_y, double*);
        -: 4365:		      for (size_t j = 0; j < ncols_y; j++) {
        -: 4366:		          Newx(col_y[j], nrows, double);
        -: 4367:		          bool sd0 = 1;
        -: 4368:		          double first = NAN;
        5: 4369:		          for (size_t i = 0; i < nrows; i++) {
        5: 4370:		              SV**restrict  rv = av_fetch(y_av, i, 0);
        5: 4371:		              AV*restrict  row = (AV*)SvRV(*rv);
        -: 4372:		              SV**restrict cv  = av_fetch(row, j, 0);
        5: 4373:		              double val = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 4374:		              col_y[j][i] = val;
        5: 4375:		              if (!isnan(val)) {
        5: 4375-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 2
        3: 4375-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 1
        2: 4376:		                  if (isnan(first)) first = val;
        2: 4377:		                  else if (val != first) sd0 = 0;
        -: 4378:		              }
        2: 4379:		          }
        2: 4379-block  0
call    0 returned 2
        2: 4380:		          if (sd0) {
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 4381:		              for (size_t k = 0; k < ncols_x; k++) Safefree(col_x[k]);
        2: 4382:		              Safefree(col_x);
        2: 4382-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 4383:		              for (size_t k = 0; k <= j; k++) Safefree(col_y[k]);
    %%%%%: 4383-block  0
        -: 4384:		              Safefree(col_y);
        2: 4385:		              croak("cor: standard deviation is 0 in y column %lu", j);
        2: 4385-block  0
call    0 returned 2
        -: 4386:		          }
        2: 4387:		      }
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 4387-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 4387-block  1
branch  4 taken 1 (fallthrough)
branch  5 taken 1
        1: 4388:		  } else { // cor(X) — symmetric p×p result; share column arrays
        1: 4388-block  0
       1*: 4389:		      ncols_y  = ncols_x;
        1: 4389-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4389-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4389-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    %%%%%: 4389-block  3
branch  6 never executed (fallthrough)
branch  7 never executed
        1: 4390:		      col_y    = col_x;
        1: 4390-block  0
    #####: 4391:		      symmetric = 1;
    %%%%%: 4391-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4392:		  }
    %%%%%: 4392-block  0
call    0 never executed
    #####: 4393:		  if (nrows < 2)
    %%%%%: 4393-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4394:		      croak("cor: need at least 2 observations (got %lu)", nrows);
    %%%%%: 4394-block  0
        -: 4395:		  // -- build cache for symmetric case: compute upper triangle, store results, mirror to lower triangle
    #####: 4396:		  AV*restrict result_av = newAV();
    %%%%%: 4396-block  0
        -: 4397:		  av_extend(result_av, ncols_x - 1);
        -: 4398:		  // Allocate per-row AVs up front so we can fill them in order
        -: 4399:		  AV **restrict rows_out;
        -: 4400:		  Newx(rows_out, ncols_x, AV*);
        2: 4401:		  for (size_t i = 0; i < ncols_x; i++) {
        2: 4401-block  0
call    0 returned 2
        2: 4402:		      rows_out[i] = newAV();
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 4403:		      av_extend(rows_out[i], ncols_y - 1);
        1: 4404:		  }
        1: 4404-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 4405:		  if (symmetric) {
    %%%%%: 4405-block  0
        -: 4406:/* Upper triangle + diagonal, then mirror. r_cache[i][j] (j >= i) holds the computed value. */
        1: 4407:		      double **restrict r_cache;
        1: 4407-block  0
call    0 returned 1
        1: 4408:		      Newx(r_cache, ncols_x, double*);
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4408-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4408-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    #####: 4409:		      for (size_t i = 0; i < ncols_x; i++)
    %%%%%: 4409-block  0
       1*: 4410:		          Newx(r_cache[i], ncols_x, double);
        1: 4410-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4410-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4410-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    %%%%%: 4410-block  3
branch  6 never executed (fallthrough)
branch  7 never executed
        1: 4411:
        1: 4411-block  0
    #####: 4412:		      for (size_t i = 0; i < ncols_x; i++) {
    %%%%%: 4412-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4413:		          r_cache[i][i] = 1.0; // diagonal
    %%%%%: 4413-block  0
call    0 never executed
    #####: 4414:		          for (size_t j = i + 1; j < ncols_x; j++) {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4414-block  0
    #####: 4415:		              double r = compute_cor(col_x[i], col_x[j], nrows, method);
    %%%%%: 4415-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4416:		              r_cache[i][j] = r;
    %%%%%: 4416-block  0
        -: 4417:		              r_cache[j][i] = r; // symmetry
    #####: 4418:		          }
    %%%%%: 4418-block  0
        -: 4419:		      }
        -: 4420:		      // fill output AoA from cache
        -: 4421:		      for (size_t i = 0; i < ncols_x; i++)
        -: 4422:		          for (size_t j = 0; j < ncols_x; j++)
        -: 4423:		              av_store(rows_out[i], j, newSVnv(r_cache[i][j]));
        -: 4424:
        5: 4425:		      for (size_t i = 0; i < ncols_x; i++) Safefree(r_cache[i]);
        5: 4426:		      Safefree(r_cache); r_cache = NULL;
        5: 4426-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 4427:		  } else {
        2: 4428:		      // cross-correlation: every (i,j) pair is independent
        2: 4428-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 4428-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4429:		      for (size_t i = 0; i < ncols_x; i++)
        1: 4430:		          for (size_t j = 0; j < ncols_y; j++)
        1: 4430-block  0
call    0 returned 1
branch  1 taken 1 (fallthrough)
branch  2 taken 0
        1: 4431:		              av_store(rows_out[i], j, newSVnv(compute_cor(col_x[i], col_y[j], nrows, method)));
        1: 4431-block  0
call    0 returned 1
        1: 4432:		  }
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4432-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4432-block  1
branch  4 taken 1 (fallthrough)
branch  5 taken 0
        1: 4433:		  // push row AVs into result
        1: 4433-block  0
        -: 4434:		  for (size_t i = 0; i < ncols_x; i++)
        -: 4435:		      av_store(result_av, i, newRV_noinc((SV*)rows_out[i]));
        -: 4436:		  Safefree(rows_out); rows_out = NULL;
        -: 4437:		  // -- free column arrays -------------------------------------
        5: 4438:		  for (size_t j = 0; j < ncols_x; j++) Safefree(col_x[j]);
        5: 4438-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        -: 4439:		  Safefree(col_x); col_x = NULL;
        -: 4440:		  if (!symmetric) {
        -: 4441:		      for (size_t j = 0; j < ncols_y; j++) Safefree(col_y[j]);
        1: 4442:		      Safefree(col_y);
        1: 4443:		  }
        1: 4443-block  0
call    0 returned 1
        -: 4444:		  RETVAL = newRV_noinc((SV*)result_av);
        1: 4445:	}
call    0 returned 1
        1: 4446:	OUTPUT:
call    0 returned 1
        -: 4447:		RETVAL
       1*: 4448:
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4448-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 1
    %%%%%: 4448-block  1
call    4 never executed
        -: 4449:void scale(...)
        -: 4450:	PROTOTYPE: @
        1: 4451:	PPCODE:
        1: 4451-block  0
call    0 returned 1
        1: 4452:	{
call    0 returned 1
        1: 4453:		bool do_center_mean = TRUE, do_scale_sd = TRUE;
call    0 returned 1
        4: 4454:		double center_val = 0.0, scale_val = 1.0;
        4: 4454-block  0
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3: 4455:		size_t data_items = items;
        3: 4455-block  0
call    0 returned 3
        3: 4456:		// 1. Parse Options Hash (if it exists as the last argument)
call    0 returned 3
        3: 4457:		if (items > 0) {
call    0 returned 3
call    1 returned 3
        -: 4458:			SV*restrict last_arg = ST(items - 1);
        -: 4459:			if (SvROK(last_arg) && SvTYPE(SvRV(last_arg)) == SVt_PVHV) {
        3: 4460:				 data_items = items - 1; // Exclude hash from data processing
        1: 4460-block  0
        3: 4460-block  1
branch  0 taken 2
branch  1 taken 1 (fallthrough)
        2: 4461:				 HV*restrict opt_hv = (HV*)SvRV(last_arg);
        -: 4462:				 // --- Parse 'center'
       2*: 4463:				 SV**restrict center_sv = hv_fetch(opt_hv, "center", 6, 0);
        2: 4463-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 4463-block  1
call    2 never executed
        2: 4463-block  2
call    3 returned 2
        -: 4464:				 if (center_sv) {
        8: 4465:				     SV*restrict val_sv = *center_sv;
        8: 4465-block  0
branch  0 taken 6
branch  1 taken 2 (fallthrough)
        6: 4466:				     if (!SvOK(val_sv)) {
        6: 4466-block  0
call    0 returned 6
        6: 4467:				         do_center_mean = FALSE; center_val = 0.0;
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4467-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 4468:				     } else {
        6: 4469:				         char *restrict str = SvPV_nolen(val_sv);
        6: 4469-block  0
call    0 returned 6
       6*: 4470:				         /* Trap booleans and empty strings before numeric checks */
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4470-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 4470-block  1
call    4 returned 6
    %%%%%: 4470-block  2
        6: 4470-block  3
        -: 4471:				         if (strcasecmp(str, "mean") == 0 || strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) {
    #####: 4472:				             do_center_mean = TRUE;
    %%%%%: 4472-block  0
        -: 4473:				         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
        6: 4474:				             do_center_mean = FALSE; center_val = 0.0;
        6: 4474-block  0
        -: 4475:				         } else if (looks_like_number(val_sv)) {
        -: 4476:				             do_center_mean = FALSE; center_val = SvNV(val_sv);
       2*: 4477:				         } else if (SvTRUE(val_sv)) {
        2: 4477-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 4477-block  1
    %%%%%: 4477-block  2
        2: 4478:				             do_center_mean = TRUE;
        -: 4479:				         } else {
        2: 4480:				             do_center_mean = FALSE; center_val = 0.0;
        2: 4480-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 4481:				         }
        2: 4481-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 4482:				     }
    %%%%%: 4482-block  0
call    0 never executed
    #####: 4483:				 }
call    0 never executed
    #####: 4484:				 // --- Parse 'scale' ---
call    0 never executed
        -: 4485:				 SV**restrict scale_sv = hv_fetch(opt_hv, "scale", 5, 0);
        2: 4486:				 if (scale_sv) {
        8: 4487:				     SV*restrict val_sv = *scale_sv;
        2: 4487-block  0
        8: 4487-block  1
branch  0 taken 6
branch  1 taken 2 (fallthrough)
        6: 4488:				     if (!SvOK(val_sv)) {
        6: 4489:				         do_scale_sd = FALSE; scale_val = 1.0;
        6: 4489-block  0
        -: 4490:				     } else {
        2: 4491:				         char *restrict str = SvPV_nolen(val_sv);
        2: 4491-block  0
        -: 4492:				         if (strcasecmp(str, "sd") == 0 || strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) {
        -: 4493:				             do_scale_sd = TRUE;
        8: 4494:				         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
        2: 4494-block  0
        8: 4494-block  1
branch  0 taken 6
branch  1 taken 2 (fallthrough)
        6: 4495:				             do_scale_sd = FALSE; scale_val = 1.0;
       6*: 4496:				         } else if (looks_like_number(val_sv)) {
        6: 4496-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4496-block  1
    %%%%%: 4496-block  2
        6: 4497:				             do_scale_sd = FALSE; scale_val = SvNV(val_sv);
        6: 4497-block  0
call    0 returned 6
call    1 returned 6
        -: 4498:				             if (scale_val == 0.0) scale_val = 1.0; /* Prevent Division By Zero */
        2: 4499:				         } else if (SvTRUE(val_sv)) {
        2: 4499-block  0
call    0 returned 2
        -: 4500:				             do_scale_sd = TRUE;
        1: 4501:				         } else {
        1: 4501-block  0
call    0 returned 1
        -: 4502:				             do_scale_sd = FALSE; scale_val = 1.0;
       1*: 4503:				         }
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4503-block  0
call    2 never executed
        1: 4504:				     }
        1: 4504-block  0
call    0 returned 1
call    1 returned 1
        -: 4505:				 }
        -: 4506:			}
        -: 4507:		}
        -: 4508:		// 2. Detect if the input is a Matrix (Array of Arrays)
        4: 4509:		bool is_matrix = FALSE;
        -: 4510:		if (data_items == 1) {
        4: 4511:			SV*restrict first_arg = ST(0);
       20: 4512:			if (SvROK(first_arg) && SvTYPE(SvRV(first_arg)) == SVt_PVAV) {
        4: 4512-block  0
       16: 4512-block  1
       20: 4512-block  2
branch  0 taken 16
branch  1 taken 4 (fallthrough)
       16: 4513:				 AV*restrict av = (AV*)SvRV(first_arg);
      16*: 4514:				 if (av_len(av) >= 0) {
       16: 4514-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 16
    %%%%%: 4514-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4514-block  2
    #####: 4515:				     SV**restrict first_elem = av_fetch(av, 0, 0);
    #####: 4516:				     if (first_elem && SvROK(*first_elem) && SvTYPE(SvRV(*first_elem)) == SVt_PVAV) {
    %%%%%: 4516-block  0
call    0 never executed
    #####: 4517:				         is_matrix = TRUE;
    %%%%%: 4517-block  0
    %%%%%: 4517-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4518:				     }
    %%%%%: 4518-block  0
call    0 never executed
    #####: 4519:				 }
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4519-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4519-block  1
        -: 4520:			}
       16: 4521:		}
       16: 4521-block  0
branch  0 taken 16 (fallthrough)
branch  1 taken 0
       16: 4522:		if (is_matrix) {
       16: 4522-block  0
        -: 4523:			//=========================================================
        -: 4524:			// MATRIX MODE: Scale columns independently (Just like R)
       4*: 4525:			//=========================================================
        4: 4525-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 4525-block  1
call    2 never executed
       4*: 4526:			AV*restrict mat_av = (AV*)SvRV(ST(0));
        4: 4526-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    %%%%%: 4526-block  1
call    2 never executed
        4: 4526-block  2
call    3 returned 4
       20: 4527:			size_t nrow = av_len(mat_av) + 1, ncol = 0;
       16: 4527-block  0
       20: 4527-block  1
branch  0 taken 16
branch  1 taken 4 (fallthrough)
       16: 4528:			
      16*: 4529:			SV**restrict first_row = av_fetch(mat_av, 0, 0);
       16: 4529-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 16
    %%%%%: 4529-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4529-block  2
    #####: 4530:			ncol = av_len((AV*)SvRV(*first_row)) + 1;
    #####: 4531:
    %%%%%: 4531-block  0
call    0 never executed
    #####: 4532:			if (nrow == 0 || ncol == 0) croak("scale requires non-empty matrix");
    %%%%%: 4532-block  0
    %%%%%: 4532-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4533:
    %%%%%: 4533-block  0
call    0 never executed
    #####: 4534:			// Create a new matrix for the scaled output
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4534-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4535:			AV*restrict result_av = newAV();
    %%%%%: 4535-block  0
call    0 never executed
    #####: 4536:			av_extend(result_av, nrow - 1);
        -: 4537:			AV**restrict row_ptrs = (AV**)safemalloc(nrow * sizeof(AV*));
        -: 4538:			for (size_t r = 0; r < nrow; r++) {
       16: 4539:				 row_ptrs[r] = newAV();
       16: 4539-block  0
branch  0 taken 16 (fallthrough)
branch  1 taken 0
       16: 4540:				 av_extend(row_ptrs[r], ncol - 1);
       16: 4540-block  0
call    0 returned 16
       16: 4541:				 av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
        -: 4542:			}
        -: 4543:			// Calculate and apply scale per column
        4: 4544:			for (size_t c = 0; c < ncol; c++) {
        4: 4544-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 1
        3: 4544-block  1
        4: 4545:				 double col_sum = 0.0;
        4: 4545-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 1
        3: 4546:				 double *restrict col_data;
        3: 4546-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 4547:				 Newx(col_data, nrow, double);
        1: 4547-block  0
call    0 returned 1
        1: 4548:				 // Extract the column data
call    0 returned 0
        -: 4549:				 for (size_t r = 0; r < nrow; r++) {
        2: 4550:				     SV**restrict row_sv = av_fetch(mat_av, r, 0);
       12: 4551:				     if (row_sv && SvROK(*row_sv)) {
        2: 4551-block  0
       12: 4551-block  1
branch  0 taken 10
branch  1 taken 2 (fallthrough)
       10: 4552:				         AV*restrict row_av = (AV*)SvRV(*row_sv);
       10: 4553:				         SV**restrict cell_sv = av_fetch(row_av, c, 0);
       10: 4553-block  0
        -: 4554:				         col_data[r] = (cell_sv && SvOK(*cell_sv)) ? SvNV(*cell_sv) : 0.0;
        2: 4555:				     } else {
        2: 4555-block  0
        -: 4556:				         col_data[r] = 0.0;
       3*: 4557:				     }
        3: 4557-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4557-block  1
call    2 never executed
       18: 4558:				     col_sum += col_data[r];
        3: 4558-block  0
       18: 4558-block  1
branch  0 taken 15
branch  1 taken 3 (fallthrough)
       15: 4559:				 }
      15*: 4560:
       15: 4560-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4560-block  1
    %%%%%: 4560-block  2
       15: 4561:				 double col_center = do_center_mean ? (col_sum / nrow) : center_val;
       15: 4561-block  0
call    0 returned 15
call    1 returned 15
        -: 4562:				 double col_scale = scale_val;
        3: 4563:				 // Calculate Standard Deviation for this specific column if needed
        3: 4563-block  0
call    0 returned 3
        -: 4564:				 if (do_scale_sd) {
        -: 4565:				     if (nrow <= 1) {
        -: 4566:				         Safefree(col_data);
        -: 4567:				         safefree(row_ptrs);
        -: 4568:				         croak("scale needs >= 2 rows to calculate standard deviation for a matrix column");
        -: 4569:				     }
        4: 4570:				     double sum_sq = 0.0;
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 4571:				     for (size_t r = 0; r < nrow; r++) {
    %%%%%: 4571-block  0
call    0 never executed
        -: 4572:				         double diff = col_data[r] - col_center;
        4: 4573:				         sum_sq += diff * diff;
        4: 4574:				     }
        4: 4575:				     col_scale = sqrt(sum_sq / (nrow - 1));
        -: 4576:				 }
       12: 4577:				 // Store scaled values back into the new matrix rows
        4: 4577-block  0
        8: 4577-block  1
       12: 4577-block  2
branch  0 taken 8
branch  1 taken 4 (fallthrough)
        8: 4578:				 for (size_t r = 0; r < nrow; r++) {
        8: 4578-block  0
call    0 returned 8
        8: 4579:				     double centered = col_data[r] - col_center;
        8: 4580:				     double final_val = (col_scale == 0.0) ? (0.0 / 0.0) : (centered / col_scale);
branch  0 taken 4 (fallthrough)
branch  1 taken 4
        4: 4581:				     av_store(row_ptrs[r], c, newSVnv(final_val));
        4: 4581-block  0
        4: 4582:				 }
        4: 4582-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 1
        3: 4583:				 Safefree(col_data);
        3: 4583-block  0
call    0 returned 3
        3: 4584:			}
        1: 4585:			safefree(row_ptrs);
        1: 4585-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4586:			// Push the resulting matrix as a single Reference onto the Perl stack
        1: 4586-block  0
call    0 returned 1
        1: 4587:			EXTEND(SP, 1);
    #####: 4588:			PUSHs(sv_2mortal(newRV_noinc((SV*)result_av)));
    %%%%%: 4588-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 4589:		} else {
    %%%%%: 4589-block  0
call    0 never executed
        -: 4590:			// ======================================
    #####: 4591:			// FLAT LIST MODE: Original functionality
    %%%%%: 4591-block  0
call    0 never executed
        -: 4592:			// ======================================
        -: 4593:			size_t total_count = 0, k = 0;
        -: 4594:			double *restrict nums;
        4: 4595:			double sum = 0.0;
        4: 4595-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 0
        4: 4595-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 1
        3: 4595-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 3
        1: 4596:			for (size_t i = 0; i < data_items; i++) {
        1: 4596-block  0
call    0 returned 0
        -: 4597:				SV*restrict arg = ST(i);
        3: 4598:				if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
       3*: 4599:					AV*restrict av = (AV*)SvRV(arg);
        3: 4599-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4599-block  1
call    2 never executed
        3: 4599-block  2
        3: 4600:					size_t len = av_len(av) + 1;
        3: 4600-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 4601:					for (unsigned int j = 0; j < len; j++) {
        1: 4601-block  0
call    0 returned 0
        -: 4602:						SV**restrict tv = av_fetch(av, j, 0);
        -: 4603:						if (tv && SvOK(*tv)) { total_count++; }
       2*: 4604:					}
        2: 4604-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 4604-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4605:				} else if (SvOK(arg)) {
    #####: 4606:					total_count++;
    %%%%%: 4606-block  0
        2: 4607:				}
        2: 4607-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 4607-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 1
        1: 4608:			}
        1: 4608-block  0
       1*: 4609:			if (total_count == 0) croak("scale requires at least 1 numeric element");
        1: 4609-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4609-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4610:			Newx(nums, total_count, double);
    %%%%%: 4610-block  0
        -: 4611:			for (size_t i = 0; i < data_items; i++) {
        -: 4612:				 SV*restrict arg = ST(i);
        2: 4613:				 if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        2: 4613-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 4613-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 1
        1: 4614:				     AV*restrict av = (AV*)SvRV(arg);
        1: 4614-block  0
call    0 returned 0
        -: 4615:				     size_t len = av_len(av) + 1;
        -: 4616:				     for (size_t j = 0; j < len; j++) {
        1: 4617:				         SV**restrict tv = av_fetch(av, j, 0);
        1: 4617-block  0
call    0 returned 1
        1: 4618:				         if (tv && SvOK(*tv)) { 
call    0 returned 1
        -: 4619:				             double val = SvNV(*tv);
        1: 4620:				             nums[k++] = val; sum += val;
call    0 returned 1
        3: 4621:				         }
        3: 4621-block  0
branch  0 taken 2
branch  1 taken 1 (fallthrough)
        2: 4622:				     }
        2: 4622-block  0
call    0 returned 2
        2: 4623:				 } else if (SvOK(arg)) {
call    0 returned 2
        2: 4624:				     double val = SvNV(arg);
call    0 returned 2
call    1 returned 2
        -: 4625:				     nums[k++] = val; sum += val;
        -: 4626:				 }
        1: 4627:			}
        7: 4628:			if (do_center_mean) center_val = sum / total_count;
        1: 4628-block  0
        7: 4628-block  1
branch  0 taken 6
branch  1 taken 1 (fallthrough)
        -: 4629:			if (do_scale_sd) {
        6: 4630:				 if (total_count <= 1) {
        6: 4630-block  0
call    0 returned 6
       6*: 4631:				     Safefree(nums);
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4631-block  0
call    2 returned 6
    %%%%%: 4631-block  1
call    3 never executed
        6: 4632:				     croak("scale needs >= 2 elements to calculate SD");
        6: 4632-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 4633:				 }
    #####: 4634:				 double sum_sq = 0.0;
    %%%%%: 4634-block  0
        -: 4635:				 for (size_t i = 0; i < total_count; i++) {
        6: 4636:				     double diff = nums[i] - center_val;
        6: 4637:				     sum_sq += diff * diff;
        6: 4637-block  0
        -: 4638:				 }
        6: 4639:				 scale_val = sqrt(sum_sq / (total_count - 1));
        6: 4639-block  0
call    0 returned 6
        -: 4640:			}
        1: 4641:			EXTEND(SP, total_count);
        1: 4641-block  0
call    0 returned 1
        1: 4642:			for (size_t i = 0; i < total_count; i++) {
call    0 returned 1
        -: 4643:				double centered = nums[i] - center_val;
        -: 4644:				double final_val = (scale_val == 0.0) ? (0.0 / 0.0) : (centered / scale_val);
        -: 4645:				PUSHs(sv_2mortal(newSVnv(final_val)));
        -: 4646:			}
        -: 4647:			Safefree(nums); nums = NULL;
        -: 4648:		}
       22: 4649:	}
       22: 4650:
        -: 4651:SV* matrix(...) 
        -: 4652:CODE:
        -: 4653:	// Basic check: must have an even number of arguments for key => value
       22: 4654:	if (items % 2 != 0) {
       22: 4655:	  croak("Usage: matrix(data => [...], nrow => $n, ncol => $m, byrow => $bool)");
       22: 4656:	}
       22: 4657:	SV*restrict data_sv = NULL;
       22: 4658:	size_t nrow = 0, ncol = 0;
       22: 4659:	bool byrow = FALSE, nrow_set = FALSE, ncol_set = FALSE;
        -: 4660:	// Parse named arguments
       22: 4661:	for (size_t i = 0; i < items; i += 2) {
       22: 4662:	  char*restrict key = SvPV_nolen(ST(i));
       22: 4663:	  SV*restrict val   = ST(i + 1);
       22: 4664:	  if (strEQ(key, "data")) {
        -: 4665:		   data_sv = val;
       22: 4666:	  } else if (strEQ(key, "nrow")) {
       22: 4667:		   nrow = (size_t)SvUV(val);
       22: 4668:		   nrow_set = TRUE;
       22: 4669:	  } else if (strEQ(key, "ncol")) {
        -: 4670:		   ncol = (size_t)SvUV(val);
        -: 4671:		   ncol_set = TRUE;
       22: 4672:	  } else if (strEQ(key, "byrow")) {
        -: 4673:		   byrow = SvTRUE(val);
        -: 4674:	  } else {
      22*: 4675:		   croak("Unknown option: %s", key);
branch  0 taken 0 (fallthrough)
branch  1 taken 22
    %%%%%: 4675-block  0
call    2 never executed
        -: 4676:	  }
       64: 4677:	}
       22: 4677-block  0
       42: 4677-block  1
       64: 4677-block  2
branch  0 taken 42
branch  1 taken 22 (fallthrough)
       42: 4678:	// Validate data input
       42: 4678-block  0
call    0 returned 42
       42: 4679:	if (!data_sv || !SvROK(data_sv) || SvTYPE(SvRV(data_sv)) != SVt_PVAV) {
       42: 4680:	  croak("The 'data' option must be an array reference (e.g. data => [1..6])");
branch  0 taken 21 (fallthrough)
branch  1 taken 21
       21: 4680-block  0
call    2 returned 21
       21: 4681:	}
       21: 4681-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 4681-block  1
    #####: 4682:	AV*restrict data_av = (AV*)SvRV(data_sv);
    %%%%%: 4682-block  0
call    0 never executed
        -: 4683:	size_t data_len = (UV)(av_top_index(data_av) + 1);
       22: 4684:	if (data_len == 0) {
       22: 4684-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 21
        1: 4684-block  1
call    2 returned 0
       21: 4685:	  croak("Data array cannot be empty");
       21: 4685-block  0
branch  0 taken 20 (fallthrough)
branch  1 taken 1
       20: 4685-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 19
        2: 4685-block  2
call    4 returned 0
        -: 4686:	}
        -: 4687:	// R-style dimension inference
        -: 4688:	if (!nrow_set && !ncol_set) {
        -: 4689:	  nrow = data_len;
       19: 4690:	  ncol = 1;
       19: 4691:	} else if (nrow_set && !ncol_set) {
       19: 4691-block  0
branch  0 taken 19 (fallthrough)
branch  1 taken 0
       19: 4692:	  ncol = (data_len + nrow - 1) / nrow;
      19*: 4693:	} else if (!nrow_set && ncol_set) {
       19: 4693-block  0
call    0 returned 19
branch  1 taken 0 (fallthrough)
branch  2 taken 19
    %%%%%: 4693-block  1
call    3 never executed
       19: 4694:	  nrow = (data_len + ncol - 1) / ncol;
       19: 4694-block  0
call    0 returned 19
       19: 4695:	}
branch  0 taken 19 (fallthrough)
branch  1 taken 0
       19: 4696:	// Final safety check for dimensions
       19: 4696-block  0
call    0 returned 19
       19: 4697:	if (nrow == 0 || ncol == 0) {
branch  0 taken 19 (fallthrough)
branch  1 taken 0
       19: 4697-block  0
branch  2 taken 12 (fallthrough)
branch  3 taken 7
       12: 4698:	  croak("Dimensions must be greater than 0");
       12: 4699:	}
       12: 4699-block  0
call    0 returned 12
      12*: 4700:	// Create the matrix (Array of Arrays)
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 4700-block  0
call    2 never executed
       12: 4700-block  1
call    3 returned 12
       82: 4701:	AV*restrict result_av = newAV();
       82: 4701-block  0
branch  0 taken 70
branch  1 taken 12 (fallthrough)
        -: 4702:	av_extend(result_av, nrow - 1);
       70: 4703:	size_t r, c;// Use unsigned types for counters to prevent negative indexing
       70: 4704:	AV**restrict row_ptrs = (AV**restrict)safemalloc(nrow * sizeof(AV*)); /* Pre-allocate row pointers */
       70: 4704-block  0
call    0 returned 70
        -: 4705:	for (r = 0; r < nrow; r++) {
        7: 4706:	  row_ptrs[r] = newAV();
        7: 4706-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 4706-block  1
branch  2 taken 7 (fallthrough)
branch  3 taken 0
        7: 4707:	  av_extend(row_ptrs[r], ncol - 1);
        7: 4707-block  0
call    0 returned 7
       7*: 4708:	  av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 4708-block  0
call    2 never executed
        7: 4708-block  1
call    3 returned 7
branch  4 taken 0 (fallthrough)
branch  5 taken 7
    %%%%%: 4708-block  2
call    6 never executed
        7: 4708-block  3
call    7 returned 7
        7: 4709:	}
      231: 4710:	// Fill the matrix
      231: 4710-block  0
call    0 returned 231
branch  1 taken 224
branch  2 taken 7 (fallthrough)
        -: 4711:	size_t total_cells = nrow * ncol;
      224: 4712:	for (size_t i = 0; i < total_cells; i++) {
      224: 4712-block  0
call    0 returned 224
call    1 returned 224
      224: 4713:	  // Vector recycling logic
call    0 returned 224
      224: 4714:	  SV**restrict fetched = av_fetch(data_av, i % data_len, 0);
        -: 4715:	  SV*restrict val = fetched ? newSVsv(*fetched) : newSV(0);
    #####: 4716:	  if (byrow) {
    %%%%%: 4716-block  0
call    0 never executed
        -: 4717:		   r = i / ncol;
    #####: 4718:		   c = i % ncol;
    %%%%%: 4718-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 4719:	  } else {
    %%%%%: 4719-block  0
call    0 never executed
    #####: 4720:		   r = i % nrow;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4720-block  0
call    2 never executed
    %%%%%: 4720-block  1
call    3 never executed
    #####: 4721:		   c = i / nrow;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4721-block  0
call    2 never executed
    %%%%%: 4721-block  1
call    3 never executed
    #####: 4722:	  }
    %%%%%: 4722-block  0
    %%%%%: 4722-block  1
branch  0 never executed
branch  1 never executed
    #####: 4723:	  av_store(row_ptrs[r], c, val);
    %%%%%: 4723-block  0
call    0 never executed
    #####: 4724:	}
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4724-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4724-block  1
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4725:	safefree(row_ptrs);
    #####: 4726:	RETVAL = newRV_noinc((SV*)result_av);
    #####: 4727:	OUTPUT:
    %%%%%: 4727-block  0
call    0 never executed
        -: 4728:	RETVAL
    #####: 4729:
    %%%%%: 4729-block  0
    %%%%%: 4729-block  1
call    0 never executed
    %%%%%: 4729-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 4730:SV* lm(...)
    %%%%%: 4730-block  0
call    0 never executed
call    1 never executed
    #####: 4731:CODE:
call    0 never executed
        -: 4732:{
        -: 4733:	const char *restrict formula  = NULL;
    #####: 4734:	SV *restrict data_sv = NULL;
    %%%%%: 4734-block  0
call    0 never executed
        -: 4735:	char f_cpy[512];
        -: 4736:	char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
        -: 4737:
        -: 4738:	char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
       19: 4739:	bool *restrict is_dummy = NULL;
      215: 4740:	char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
       19: 4740-block  0
      196: 4740-block  1
branch  0 taken 137 (fallthrough)
branch  1 taken 59
      137: 4740-block  2
      196: 4740-block  3
      215: 4740-block  4
branch  2 taken 196 (fallthrough)
branch  3 taken 19
      196: 4740-block  5
branch  4 taken 196
branch  5 taken 0 (fallthrough)
       19: 4741:	unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
        -: 4742:	size_t n = 0, valid_n = 0, i, j, k, l, l1, l2;
       19: 4743:	bool has_intercept = TRUE;
       19: 4744:
       19: 4744-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 18
        3: 4745:	char **restrict row_names = NULL, **restrict valid_row_names = NULL;
        1: 4745-block  0
        2: 4745-block  1
call    0 returned 2
        3: 4745-block  2
branch  1 taken 2
branch  2 taken 1 (fallthrough)
       1*: 4746:	HV **restrict row_hashes = NULL;
        1: 4746-block  0
call    0 returned 1
branch  1 taken 0 (fallthrough)
branch  2 taken 1
    %%%%%: 4746-block  1
call    3 never executed
        1: 4747:	HV *restrict data_hoa = NULL;
        1: 4747-block  0
call    0 returned 0
        -: 4748:	SV *restrict ref = NULL;
       18: 4749:
       18: 4750:	double *restrict X = NULL, *restrict Y = NULL, *restrict XtX = NULL, *restrict XtY = NULL;
       18: 4751:	bool *restrict aliased = NULL;
        -: 4752:	double *restrict beta = NULL;
        -: 4753:	int final_rank = 0, df_res = 0;
        -: 4754:	HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
        -: 4755:	AV *restrict terms_av;
        -: 4756:	double rss = 0.0, rse_sq = 0.0;
       18: 4757:	HE *restrict entry;
       89: 4758:
       18: 4758-block  0
       89: 4758-block  1
branch  0 taken 71
branch  1 taken 18 (fallthrough)
        -: 4759:	if (items % 2 != 0) croak("Usage: lm(formula => 'mpg ~ wt * hp', data => \\%%mtcars)");
      71*: 4760:
       71: 4760-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 71
    %%%%%: 4760-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4761:	for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
    #####: 4762:	  const char *restrict key = SvPV_nolen(ST(i_arg));
    %%%%%: 4762-block  0
    %%%%%: 4762-block  1
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4762-block  2
    %%%%%: 4762-block  3
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4762-block  4
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 4762-block  5
    %%%%%: 4762-block  6
    %%%%%: 4762-block  7
branch  6 never executed
branch  7 never executed (fallthrough)
    #####: 4763:	  SV *restrict val = ST(i_arg + 1);
    %%%%%: 4763-block  0
        -: 4764:	  if      (strEQ(key, "formula")) formula = SvPV_nolen(val);
        -: 4765:	  else if (strEQ(key, "data"))    data_sv = val;
       71: 4766:	  else croak("lm: unknown argument '%s'", key);
       71: 4766-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 70
        1: 4766-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
       1*: 4767:	}
        1: 4767-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4767-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4767-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
        1: 4768:	if (!formula) croak("lm: formula is required");
        1: 4769:	if (!data_sv || !SvROK(data_sv)) croak("lm: data is required and must be a reference");
        1: 4770:
        1: 4770-block  0
        -: 4771:	// ========================================================================
        -: 4772:	// PHASE 1: Data Extraction
      70*: 4773:	// ========================================================================
       70: 4773-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 62
        8: 4773-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 8
    #####: 4774:	ref = SvRV(data_sv);
    %%%%%: 4774-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4774-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4774-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4775:	if (SvTYPE(ref) == SVt_PVHV) {
    #####: 4776:	  HV *restrict hv = (HV*)ref;
    #####: 4777:	  if (hv_iterinit(hv) == 0) croak("lm: Data hash is empty");
    %%%%%: 4777-block  0
        -: 4778:	  entry = hv_iternext(hv);
        -: 4779:	  if (entry) {
      70*: 4780:		   SV *restrict val = hv_iterval(hv, entry);
       70: 4780-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 52
       18: 4780-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 18
    %%%%%: 4780-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4781:		   if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
    #####: 4782:		       data_hoa = hv;
    #####: 4783:		       n = av_len((AV*)SvRV(val)) + 1;
    %%%%%: 4783-block  0
        -: 4784:		       Newx(row_names, n, char*);
        -: 4785:		       for (i = 0; i < n; i++) {
      70*: 4786:		           char buf[32];
       70: 4786-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 52
       18: 4786-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 18
    %%%%%: 4786-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4787:		           snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
    %%%%%: 4787-block  0
        -: 4788:		           row_names[i] = savepv(buf);
        -: 4789:		       }
      70*: 4790:		   } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
       70: 4790-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 62
        8: 4790-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 8
    #####: 4791:		       n = hv_iterinit(hv);
    %%%%%: 4791-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4791-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4791-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4792:		       Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
    #####: 4793:		       i = 0;
    %%%%%: 4793-block  0
        -: 4794:		       while ((entry = hv_iternext(hv))) {
        -: 4795:		           I32 len;
       70: 4796:		           row_names[i] = savepv(hv_iterkey(entry, &len));
       70: 4796-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 52
      18*: 4797:		           row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
       18: 4797-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 4797-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4797-block  2
      18*: 4798:		           i++;
       18: 4798-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 4798-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4798-block  2
        -: 4799:		       }
       70: 4800:		   } else croak("lm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
       70: 4800-block  0
        -: 4801:	  }
        -: 4802:	} else if (SvTYPE(ref) == SVt_PVAV) {
        -: 4803:	  AV *restrict av = (AV*)ref; n = av_len(av) + 1;
        -: 4804:	  Newx(row_names, n, char*);
        -: 4805:	  Newx(row_hashes, n, HV*);
        -: 4806:	  for (i = 0; i < n; i++) {
       18: 4807:		   SV **restrict val = av_fetch(av, i, 0);
       18: 4807-block  0
       18: 4807-block  1
branch  0 taken 0
branch  1 taken 18 (fallthrough)
    #####: 4808:		   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
    %%%%%: 4808-block  0
      18*: 4809:		       row_hashes[i] = (HV*)SvRV(*val);
       18: 4809-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 4809-block  1
       18: 4810:		       char buf[32]; snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
      18*: 4811:		       row_names[i] = savepv(buf);
       18: 4811-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4811-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 18
    %%%%%: 4811-block  2
        -: 4812:		   } else {
        -: 4813:		       for (k = 0; k < i; k++) Safefree(row_names[k]);
        -: 4814:		       Safefree(row_names); Safefree(row_hashes);
       18: 4815:		       croak("lm: Array values must be HashRefs (AoH)");
       18: 4816:		   }
       18: 4817:	  }
       18: 4817-block  0
call    0 returned 18
       44: 4818:	} else croak("lm: Data must be an Array or Hash reference");
       44: 4818-block  0
branch  0 taken 26
branch  1 taken 18 (fallthrough)
       26: 4819:
       26: 4819-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 25
        1: 4820:	// ========================================================================
        1: 4820-block  0
call    0 returned 1
        4: 4821:	// PHASE 2: Formula Parsing & `.` Expansion
        3: 4821-block  0
        4: 4821-block  1
call    0 returned 4
branch  1 taken 3
branch  2 taken 1 (fallthrough)
        3: 4822:	// ========================================================================
        3: 4822-block  0
call    0 returned 3
        3: 4823:	src = (char*)formula; dst = f_cpy;
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4823-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4824:	while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
        3: 4824-block  0
call    0 returned 3
        3: 4825:	*dst = '\0';
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 4826:
        2: 4827:	tilde = strchr(f_cpy, '~');
        2: 4827-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 4828:	if (!tilde) {
        2: 4828-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 4828-block  1
        2: 4829:	  for (i = 0; i < n; i++) Safefree(row_names[i]);
        2: 4830:	  Safefree(row_names); if (row_hashes) Safefree(row_hashes);
        2: 4830-block  0
        -: 4831:	  croak("lm: invalid formula, missing '~'");
        -: 4832:	}
        -: 4833:	*tilde = '\0';
        -: 4834:	lhs = f_cpy;
        1: 4835:	rhs = tilde + 1;
        1: 4835-block  0
call    0 returned 1
        -: 4836:
       25: 4837:	// Remove intercept-suppression markers from RHS.
       25: 4838:	// IMPORTANT: skip tokens that appear inside I(...) wrappers so that
       25: 4838-block  0
branch  0 taken 25 (fallthrough)
branch  1 taken 0
       25: 4839:	// expressions like I(x^-1) are never mistakenly treated as "-1".
       25: 4839-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 17
        8: 4839-block  1
       25: 4840:	{
       25: 4841:	  char *restrict p_idx = rhs;
       25: 4841-block  0
        -: 4842:	  while (*p_idx) {
        -: 4843:		   // Skip over I(...) sub-expressions entirely
       26: 4844:		   if (p_idx[0] == 'I' && p_idx[1] == '(') {
       26: 4844-block  0
call    0 returned 26
        -: 4845:		       int depth = 0;
        -: 4846:		       while (*p_idx) { if (*p_idx == '(') depth++; else if (*p_idx == ')') { depth--; if (depth == 0) { p_idx++; break; } } p_idx++; }
       18: 4847:		       continue;
       18: 4847-block  0
call    0 returned 18
call    1 returned 18
       18: 4848:		   }
call    0 returned 18
call    1 returned 18
       18: 4849:		   // Match bare -1
call    0 returned 18
call    1 returned 18
        -: 4850:		   if (p_idx[0] == '-' && p_idx[1] == '1' &&
       18: 4851:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
branch  0 taken 17 (fallthrough)
branch  1 taken 1
       17: 4851-block  0
call    2 returned 17
        -: 4852:		       has_intercept = FALSE;
       18: 4853:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
       18: 4853-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4854:		       continue; // re-examine same position
       18: 4854-block  0
call    0 returned 18
       45: 4855:		   }
       45: 4855-block  0
branch  0 taken 27
branch  1 taken 18 (fallthrough)
       27: 4856:		   // Match +0
       27: 4856-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 27
    #####: 4857:		   if (p_idx[0] == '+' && p_idx[1] == '0' &&
    #####: 4858:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
    %%%%%: 4858-block  0
call    0 never executed
call    1 never executed
        -: 4859:		       has_intercept = FALSE;
       27: 4860:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
       27: 4861:		       continue;
       27: 4861-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 26
        1: 4862:		   }
        1: 4863:		   // Match leading 0+
        1: 4864:		   if (p_idx == rhs && p_idx[0] == '0' && p_idx[1] == '+') {
        1: 4865:		       has_intercept = FALSE;
       1*: 4866:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
        1: 4866-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 4866-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4866-block  2
        1: 4867:		       continue;
        1: 4868:		   }
        1: 4868-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 4868-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 4868-block  2
        1: 4869:		   // Match bare 0 (entire rhs)
        1: 4869-block  0
call    0 returned 1
        1: 4870:		   if (p_idx == rhs && p_idx[0] == '0' && p_idx[1] == '\0') {
call    0 returned 1
        1: 4871:		       has_intercept = FALSE; p_idx[0] = '\0'; break;
        1: 4872:		   }
call    0 returned 1
        1: 4873:		   // Strip redundant +1 (keep intercept, just remove marker)
        -: 4874:		   if (p_idx[0] == '+' && p_idx[1] == '1' &&
       26: 4875:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
      26*: 4876:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
       26: 4876-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 26
    %%%%%: 4876-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4876-block  2
       26: 4877:		       continue;
       26: 4877-block  0
call    0 returned 26
        -: 4878:		   }
       27: 4879:		   // Strip leading bare 1 or 1+
       27: 4879-block  0
call    0 returned 27
        -: 4880:		   if (p_idx == rhs) {
        -: 4881:		       if (p_idx[0] == '1' && p_idx[1] == '\0') { p_idx[0] = '\0'; break; }
        -: 4882:		       if (p_idx[0] == '1' && p_idx[1] == '+') { memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); continue; }
       64: 4883:		   }
       18: 4883-block  0
       46: 4883-block  1
       64: 4883-block  2
branch  0 taken 46
branch  1 taken 18 (fallthrough)
       46: 4884:		   p_idx++;
      86*: 4885:	  }
       46: 4885-block  0
       40: 4885-block  1
branch  0 taken 0 (fallthrough)
branch  1 taken 40
    %%%%%: 4885-block  2
       40: 4885-block  3
       86: 4885-block  4
branch  2 taken 40
branch  3 taken 46 (fallthrough)
       46: 4886:	}
       46: 4886-block  0
branch  0 taken 46 (fallthrough)
branch  1 taken 0
       46: 4886-block  1
call    2 returned 46
        -: 4887:
       18: 4888:	// Clean up stray `++`, leading `+`, trailing `+`
        -: 4889:	{
        -: 4890:	  char *restrict p_idx;
        -: 4891:	  while ((p_idx = strstr(rhs, "++")) != NULL)
        -: 4892:		   memmove(p_idx, p_idx + 1, strlen(p_idx + 1) + 1);
       64: 4893:	  if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
       18: 4893-block  0
       46: 4893-block  1
       64: 4893-block  2
branch  0 taken 46
branch  1 taken 18 (fallthrough)
       46: 4894:	  size_t len_rhs = strlen(rhs);
       46: 4894-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 46
    #####: 4895:	  if (len_rhs > 0 && rhs[len_rhs - 1] == '+') rhs[len_rhs - 1] = '\0';
    #####: 4896:	}
    %%%%%: 4896-block  0
call    0 never executed
call    1 never executed
    #####: 4897:
call    0 never executed
call    1 never executed
        -: 4898:	// Expand `.` Operator
       46: 4899:	char rhs_expanded[2048] = "";
       46: 4899-block  0
branch  0 taken 17 (fallthrough)
branch  1 taken 29
       17: 4900:	size_t rhs_len = 0;
       17: 4900-block  0
call    0 returned 17
        -: 4901:	chunk = strtok(rhs, "+");
       29: 4902:	while (chunk != NULL) {
       29: 4902-block  0
call    0 returned 29
branch  1 taken 5 (fallthrough)
branch  2 taken 24
        5: 4903:	  if (strcmp(chunk, ".") == 0) {
        5: 4904:		   AV *cols = get_all_columns(data_hoa, row_hashes, n);
        5: 4905:		   for (size_t c = 0; c <= (size_t)av_len(cols); c++) {
        5: 4905-block  0
call    0 returned 5
       47: 4906:		       SV **col_sv = av_fetch(cols, c, 0);
       42: 4906-block  0
       47: 4906-block  1
branch  0 taken 42
branch  1 taken 5 (fallthrough)
       42: 4907:		       if (col_sv && SvOK(*col_sv)) {
       42: 4907-block  0
call    0 returned 42
       42: 4908:		           const char *col_name = SvPV_nolen(*col_sv);
branch  0 taken 42 (fallthrough)
branch  1 taken 0
       42: 4909:		           if (strcmp(col_name, lhs) != 0) {
       81: 4910:		               size_t slen = strlen(col_name);
       42: 4910-block  0
       67: 4910-block  1
branch  0 taken 28 (fallthrough)
branch  1 taken 39
       28: 4910-block  2
       39: 4910-block  3
       81: 4910-block  4
branch  2 taken 67
branch  3 taken 14 (fallthrough)
       42: 4911:		               if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
       42: 4911-block  0
branch  0 taken 14 (fallthrough)
branch  1 taken 28
      14*: 4912:		                   if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
       14: 4912-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 14
    %%%%%: 4912-block  1
call    2 never executed
       14: 4913:		                   strcat(rhs_expanded, col_name);
       14: 4913-block  0
call    0 returned 14
        -: 4914:		                   rhs_len += slen;
       42: 4915:		               }
       42: 4915-block  0
call    0 returned 42
        -: 4916:		           }
        -: 4917:		       }
        5: 4918:		   }
        5: 4918-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
       14: 4919:		   SvREFCNT_dec(cols);
        5: 4919-block  0
        9: 4919-block  1
       14: 4919-block  2
branch  0 taken 9
branch  1 taken 5 (fallthrough)
       22: 4920:	  } else {
        9: 4920-block  0
       13: 4920-block  1
       22: 4920-block  2
branch  0 taken 13
branch  1 taken 9 (fallthrough)
       13: 4921:		   size_t slen = strlen(chunk);
       13: 4921-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 7
        6: 4921-block  1
       14: 4922:		   if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
        5: 4922-block  0
       14: 4922-block  1
branch  0 taken 9
branch  1 taken 5 (fallthrough)
        9: 4923:		       if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        9: 4923-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    #####: 4924:		       strcat(rhs_expanded, chunk);
    #####: 4925:		       rhs_len += slen;
    %%%%%: 4925-block  0
call    0 never executed
call    1 never executed
    #####: 4926:		   }
call    0 never executed
call    1 never executed
        -: 4927:	  }
        9: 4928:	  chunk = strtok(NULL, "+");
        9: 4929:	}
        9: 4929-block  0
call    0 returned 9
        9: 4930:
        9: 4931:	Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
        9: 4932:	Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
call    0 returned 9
        9: 4933:	Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
call    0 returned 9
        9: 4934:
        -: 4935:	if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
       19: 4936:
        5: 4936-block  0
       14: 4936-block  1
call    0 returned 14
       19: 4936-block  2
branch  1 taken 14
branch  2 taken 5 (fallthrough)
        5: 4937:	if (strlen(rhs_expanded) > 0) {
        5: 4937-block  0
call    0 returned 5
        -: 4938:	  chunk = strtok(rhs_expanded, "+");
    #####: 4939:	  while (chunk != NULL) {
    %%%%%: 4939-block  0
call    0 never executed
    #####: 4940:		   if (num_terms >= term_cap - 3) {
call    0 never executed
        -: 4941:		       term_cap *= 2;
        -: 4942:		       Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
       24: 4943:		   }
       24: 4943-block  0
call    0 returned 24
        -: 4944:		   char *restrict star = strchr(chunk, '*');
        -: 4945:		   if (star) {
       18: 4946:		       *star = '\0';
      18*: 4947:		       char *restrict left = chunk;
       18: 4947-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 4947-block  1
call    2 never executed
       18: 4947-block  2
call    3 returned 18
branch  4 taken 0 (fallthrough)
branch  5 taken 18
    %%%%%: 4947-block  3
call    6 never executed
       18: 4947-block  4
call    7 returned 18
      18*: 4948:		       char *restrict right = star + 1;
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 4948-block  0
call    2 never executed
       18: 4948-block  1
call    3 returned 18
        -: 4949:		       char *restrict c_l = strchr(left, '^');
        -: 4950:		       if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
        -: 4951:		       char *restrict c_r = strchr(right, '^');
        -: 4952:		       if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
      310: 4953:		       terms[num_terms++] = savepv(left);
      292: 4953-block  0
      310: 4953-block  1
branch  0 taken 292
branch  1 taken 18 (fallthrough)
      292: 4954:		       terms[num_terms++] = savepv(right);
      292: 4954-block  0
call    0 returned 292
      292: 4955:		       size_t inter_len = strlen(left) + strlen(right) + 2;
branch  0 taken 3 (fallthrough)
branch  1 taken 289
        3: 4955-block  0
call    2 returned 3
        -: 4956:		       terms[num_terms] = (char*)safemalloc(inter_len);
      289: 4957:		       snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
      289: 4958:		   } else {
      289: 4958-block  0
call    0 returned 289
     1112: 4959:		       char *restrict c_chunk = strchr(chunk, '^');
      823: 4959-block  0
     1112: 4959-block  1
branch  0 taken 823
branch  1 taken 289 (fallthrough)
      823: 4960:		       if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
      823: 4960-block  0
branch  0 taken 257 (fallthrough)
branch  1 taken 566
      257: 4961:		       terms[num_terms++] = savepv(chunk);
      257: 4961-block  0
      566: 4962:		   }
      566: 4962-block  0
branch  0 taken 78 (fallthrough)
branch  1 taken 488
       78: 4963:		   chunk = strtok(NULL, "+");
       78: 4963-block  0
call    0 returned 78
       78: 4964:	  }
branch  0 taken 78 (fallthrough)
branch  1 taken 0
       78: 4965:	}
       78: 4965-block  0
branch  0 taken 27 (fallthrough)
branch  1 taken 51
       27: 4965-block  1
       51: 4965-block  2
       78: 4966:
       78: 4966-block  0
call    0 returned 78
    #####: 4967:	for (i = 0; i < num_terms; i++) {
    %%%%%: 4967-block  0
        -: 4968:	  bool found = FALSE;
      488: 4969:	  for (j = 0; j < num_uniq; j++) { if (strcmp(terms[i], uniq_terms[j]) == 0) { found = TRUE; break; } }
      488: 4969-block  0
call    0 returned 488
     488*: 4970:	  if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
branch  0 taken 0 (fallthrough)
branch  1 taken 488
    %%%%%: 4970-block  0
        -: 4971:	}
        -: 4972:	p = num_uniq;
     289*: 4973:
      289: 4973-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 289
    %%%%%: 4973-block  1
call    2 never executed
call    3 never executed
        -: 4974:	// ========================================================================
      289: 4975:	// PHASE 3: Categorical Expansion
     1112: 4976:	// ========================================================================
      289: 4976-block  0
      823: 4976-block  1
     1112: 4976-block  2
branch  0 taken 823
branch  1 taken 289 (fallthrough)
      289: 4977:	for (j = 0; j < p; j++) {
      289: 4978:	  if (p_exp + 32 >= exp_cap) {
      289: 4979:		   exp_cap *= 2;
      289: 4979-block  0
call    0 returned 289
        -: 4980:		   Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
       18: 4981:		   Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
       18: 4981-block  0
call    0 returned 18
        -: 4982:	  }
       18: 4983:	  if (strcmp(uniq_terms[j], "Intercept") == 0) {
branch  0 taken 2 (fallthrough)
branch  1 taken 16
        7: 4984:		   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = FALSE; p_exp++; continue;
        2: 4984-block  0
        5: 4984-block  1
call    0 returned 5
        7: 4984-block  2
branch  1 taken 5
branch  2 taken 2 (fallthrough)
        2: 4984-block  3
call    3 returned 2
        7: 4985:	  }
        5: 4985-block  0
call    0 returned 5
        7: 4985-block  1
branch  1 taken 5
branch  2 taken 2 (fallthrough)
        2: 4985-block  2
call    3 returned 2
        7: 4986:	  if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
        5: 4986-block  0
        7: 4986-block  1
branch  0 taken 5
branch  1 taken 2 (fallthrough)
        5: 4987:		   char **restrict levels = NULL;
        5: 4987-block  0
call    0 returned 5
       5*: 4988:		   unsigned int num_levels = 0, levels_cap = 8;
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 4988-block  0
call    2 never executed
call    3 never executed
        -: 4989:		   Newx(levels, levels_cap, char*);
        2: 4990:		   for (i = 0; i < n; i++) {
        2: 4990-block  0
call    0 returned 2
call    1 returned 2
call    2 returned 2
call    3 returned 2
        2: 4991:		       char *str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
call    0 returned 2
call    1 returned 2
call    2 returned 2
       2*: 4992:		       if (str_val) {
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 4992-block  0
call    2 never executed
        2: 4993:		           bool found = FALSE;
        2: 4993-block  0
call    0 returned 0
        -: 4994:		           for (l = 0; l < num_levels; l++) { if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; } }
        -: 4995:		           if (!found) {
        -: 4996:		               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
        -: 4997:		               levels[num_levels++] = savepv(str_val);
        -: 4998:		           }
       16: 4999:		           Safefree(str_val);
       16: 4999-block  0
call    0 returned 16
       61: 5000:		       }
       45: 5000-block  0
       61: 5000-block  1
branch  0 taken 45
branch  1 taken 16 (fallthrough)
      178: 5001:		   }
       45: 5001-block  0
      178: 5001-block  1
branch  0 taken 133
branch  1 taken 45 (fallthrough)
      133: 5002:		   if (num_levels > 0) {
     2620: 5003:		       for (l1 = 0; l1 < num_levels - 1; l1++)
      133: 5003-block  0
     2487: 5003-block  1
     2620: 5003-block  2
branch  0 taken 2487
branch  1 taken 133 (fallthrough)
      133: 5004:		           for (l2 = l1 + 1; l2 < num_levels; l2++)
      133: 5004-block  0
        -: 5005:		               if (strcmp(levels[l1], levels[l2]) > 0) { char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp; }
       16: 5006:		       for (l = 1; l < num_levels; l++) {
       16: 5006-block  0
call    0 returned 16
       61: 5007:		           if (p_exp >= exp_cap) {
       61: 5007-block  0
branch  0 taken 45
branch  1 taken 16 (fallthrough)
       45: 5008:		               exp_cap *= 2;
      860: 5009:		               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
       45: 5009-block  0
      815: 5009-block  1
      860: 5009-block  2
branch  0 taken 815
branch  1 taken 45 (fallthrough)
       45: 5010:		               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
       45: 5010-block  0
        -: 5011:		           }
       16: 5012:		           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
       16: 5012-block  0
call    0 returned 16
       16: 5013:		           exp_terms[p_exp] = (char*)safemalloc(t_len);
call    0 returned 16
       16: 5014:		           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
call    0 returned 16
       61: 5015:		           is_dummy[p_exp] = TRUE;
       45: 5015-block  0
       61: 5015-block  1
branch  0 taken 45
branch  1 taken 16 (fallthrough)
       45: 5016:		           dummy_base[p_exp]  = savepv(uniq_terms[j]);
       45: 5016-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 44
        1: 5016-block  1
        -: 5017:		           dummy_level[p_exp] = savepv(levels[l]);
       44: 5018:		           p_exp++;
      174: 5019:		       }
       44: 5019-block  0
      130: 5019-block  1
branch  0 taken 128 (fallthrough)
branch  1 taken 2
      128: 5019-block  2
      130: 5019-block  3
      174: 5019-block  4
branch  2 taken 130
branch  3 taken 44 (fallthrough)
       44: 5020:		       for (l = 0; l < num_levels; l++) Safefree(levels[l]);
       44: 5020-block  0
        -: 5021:		       Safefree(levels);
        -: 5022:		   } else {
        -: 5023:		       Safefree(levels);
        -: 5024:		       exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
        -: 5025:		   }
        -: 5026:	  } else {
       16: 5027:		   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
       16: 5027-block  0
call    0 returned 16
call    1 returned 16
call    2 returned 16
call    3 returned 16
       16: 5028:	  }
call    0 returned 16
call    1 returned 16
        -: 5029:	}
       16: 5030:	p = p_exp;
        -: 5031:	Newx(X, n * p, double); Newx(Y, n, double);
        -: 5032:	Newx(valid_row_names, n, char*);
       16: 5033:
      302: 5034:	// ========================================================================
      286: 5034-block  0
      302: 5034-block  1
branch  0 taken 286
branch  1 taken 16 (fallthrough)
       16: 5035:	// PHASE 4: Matrix Construction & Listwise Deletion
        -: 5036:	// ========================================================================
      302: 5037:	for (i = 0; i < n; i++) {
       16: 5037-block  0
      302: 5037-block  1
branch  0 taken 286
branch  1 taken 16 (fallthrough)
      286: 5038:	  double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
     1101: 5039:	  if (isnan(y_val)) { Safefree(row_names[i]); continue; }
      286: 5039-block  0
      815: 5039-block  1
branch  0 taken 810 (fallthrough)
branch  1 taken 5
      810: 5039-block  2
      815: 5039-block  3
     1101: 5039-block  4
branch  2 taken 815
branch  3 taken 286 (fallthrough)
      286: 5040:
      286: 5041:	  bool row_ok = TRUE;
      286: 5042:	  double *restrict row_x = (double*)safemalloc(p * sizeof(double));
      286: 5042-block  0
branch  0 taken 254 (fallthrough)
branch  1 taken 32
      254: 5042-block  1
       32: 5042-block  2
      286: 5043:	  for (j = 0; j < p; j++) {
      286: 5044:		   if (strcmp(exp_terms[j], "Intercept") == 0) {
      286: 5044-block  0
call    0 returned 286
call    1 returned 286
      286: 5045:		       row_x[j] = 1.0;
call    0 returned 286
call    1 returned 286
      286: 5046:		   } else if (is_dummy[j]) {
call    0 returned 286
        -: 5047:		       char *restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
       16: 5048:		       if (str_val) {
       16: 5048-block  0
call    0 returned 16
        -: 5049:		           row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
        -: 5050:		           Safefree(str_val);
      16*: 5051:		       } else { row_ok = FALSE; break; }
branch  0 taken 16 (fallthrough)
branch  1 taken 0
       16: 5051-block  0
    %%%%%: 5051-block  1
        -: 5052:		   } else {
       16: 5053:		       row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
       16: 5054:		       if (isnan(row_x[j])) { row_ok = FALSE; break; }
       16: 5055:		   }
        -: 5056:	  }
       16: 5057:	  if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
       16: 5057-block  0
branch  0 taken 16 (fallthrough)
branch  1 taken 0
       16: 5057-block  1
branch  2 taken 16 (fallthrough)
branch  3 taken 0
       16: 5058:
       16: 5059:	  Y[valid_n] = y_val;
       16: 5060:	  for (j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
       16: 5060-block  0
branch  0 taken 16 (fallthrough)
branch  1 taken 0
       16: 5060-block  1
branch  2 taken 16 (fallthrough)
branch  3 taken 0
       16: 5061:	  valid_row_names[valid_n] = row_names[i];
       16: 5062:	  valid_n++;
       16: 5062-block  0
call    0 returned 16
    #####: 5063:	  Safefree(row_x);
    %%%%%: 5063-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5064:	}
    #####: 5065:	Safefree(row_names);
    %%%%%: 5065-block  0
        -: 5066:
    #####: 5067:	if (valid_n <= p) {
    %%%%%: 5067-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5068:	  for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
    %%%%%: 5068-block  0
        -: 5069:	  for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
        -: 5070:	  for (j = 0; j < p_exp; j++) {
       61: 5071:		   Safefree(exp_terms[j]);
       16: 5071-block  0
       61: 5071-block  1
branch  0 taken 45
branch  1 taken 16 (fallthrough)
       45: 5072:		   if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
       45: 5072-block  0
call    0 returned 45
call    1 returned 45
       45: 5073:	  }
call    0 returned 45
call    1 returned 45
       45: 5074:	  Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
call    0 returned 45
       45: 5075:	  Safefree(X); Safefree(Y); Safefree(valid_row_names);
branch  0 taken 1 (fallthrough)
branch  1 taken 44
        1: 5076:	  if (row_hashes) Safefree(row_hashes);
        1: 5076-block  0
call    0 returned 1
call    1 returned 1
        1: 5077:	  croak("lm: 0 degrees of freedom (too many NAs or parameters > observations)");
call    0 returned 1
call    1 returned 1
        1: 5078:	}
call    0 returned 1
call    1 returned 1
        1: 5079:
call    0 returned 1
call    1 returned 1
        -: 5080:	// ========================================================================
       44: 5081:	// PHASE 5: OLS Math
      44*: 5082:	// ========================================================================
       44: 5082-block  0
branch  0 taken 44 (fallthrough)
branch  1 taken 0
       44: 5082-block  1
    %%%%%: 5082-block  2
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5082-block  3
    %%%%%: 5082-block  4
    %%%%%: 5082-block  5
       44: 5083:	Newxz(XtX, p * p, double);
       44: 5083-block  0
call    0 returned 44
       44: 5084:	for (i = 0; i < p; i++)
call    0 returned 44
call    1 returned 44
       44: 5085:	  for (j = 0; j < p; j++) {
call    0 returned 44
call    1 returned 44
       44: 5086:		   double sum = 0.0;
call    0 returned 44
call    1 returned 44
       44: 5087:		   for (k = 0; k < valid_n; k++) sum += X[k * p + i] * X[k * p + j];
call    0 returned 44
call    1 returned 44
        -: 5088:		   XtX[i * p + j] = sum;
       45: 5089:	  }
       45: 5089-block  0
call    0 returned 45
call    1 returned 45
        -: 5090:	Newxz(XtY, p, double);
        -: 5091:	for (i = 0; i < p; i++) {
       16: 5092:	  double sum = 0.0;
       16: 5092-block  0
call    0 returned 16
call    1 returned 16
       16: 5093:	  for (k = 0; k < valid_n; k++) sum += X[k * p + i] * Y[k];
call    0 returned 16
call    1 returned 16
       16: 5094:	  XtY[i] = sum;
call    0 returned 16
call    1 returned 16
       16: 5095:	}
call    0 returned 16
call    1 returned 16
       16: 5096:	Newx(aliased, p, bool);
call    0 returned 16
call    1 returned 16
       16: 5097:	final_rank = sweep_matrix_ols(XtX, p, aliased);
call    0 returned 16
call    1 returned 16
       16: 5098:	Newxz(beta, p, double);
call    0 returned 16
call    1 returned 16
       16: 5099:	for (i = 0; i < p; i++) {
call    0 returned 16
call    1 returned 16
       16: 5100:	  if (aliased[i]) { beta[i] = NAN; }
call    0 returned 16
call    1 returned 16
       16: 5101:	  else {
call    0 returned 16
call    1 returned 16
       16: 5102:		   double sum = 0.0;
branch  0 taken 16 (fallthrough)
branch  1 taken 0
       16: 5103:		   for (j = 0; j < p; j++) if (!aliased[j]) sum += XtX[i * p + j] * XtY[j];
       16: 5103-block  0
call    0 returned 16
       16: 5104:		   beta[i] = sum;
call    0 returned 16
call    1 returned 16
       16: 5105:	  }
call    0 returned 16
call    1 returned 16
       16: 5106:	}
call    0 returned 16
call    1 returned 16
       16: 5107:
call    0 returned 16
call    1 returned 16
       16: 5108:	// ========================================================================
call    0 returned 16
call    1 returned 16
        -: 5109:	// PHASE 6: Metrics & Cleanup
        -: 5110:	// ========================================================================
        -: 5111:	res_hv = newHV(); coef_hv = newHV(); fitted_hv = newHV(); resid_hv = newHV();
       57: 5112:	summary_hv = newHV(); terms_av = newAV();
       16: 5112-block  0
       41: 5112-block  1
call    0 returned 41
       57: 5112-block  2
branch  1 taken 41
branch  2 taken 16 (fallthrough)
       16: 5112-block  3
call    3 returned 16
       57: 5113:
       41: 5113-block  0
call    0 returned 41
       57: 5113-block  1
branch  1 taken 41
branch  2 taken 16 (fallthrough)
       16: 5113-block  2
call    3 returned 16
       61: 5114:	df_res = (int)valid_n - final_rank;
       45: 5114-block  0
       61: 5114-block  1
branch  0 taken 45
branch  1 taken 16 (fallthrough)
       45: 5115:
       45: 5115-block  0
call    0 returned 45
       45: 5116:	// rss / mss accumulated here — rse_sq computed AFTER this loop (not before)
branch  0 taken 9 (fallthrough)
branch  1 taken 36
        9: 5116-block  0
call    2 returned 9
call    3 returned 9
        -: 5117:	double sum_y = 0.0, mss = 0.0;
       16: 5118:	for (i = 0; i < valid_n; i++) sum_y += Y[i];
       16: 5118-block  0
call    0 returned 16
call    1 returned 16
call    2 returned 16
call    3 returned 16
       16: 5119:	double mean_y = sum_y / (double)valid_n;
call    0 returned 16
call    1 returned 16
call    2 returned 16
call    3 returned 16
       16: 5120:
call    0 returned 16
call    1 returned 16
       16: 5121:	for (i = 0; i < valid_n; i++) {
branch  0 taken 7 (fallthrough)
branch  1 taken 9
        7: 5121-block  0
call    2 returned 7
        -: 5122:	  double y_hat = 0.0;
       16: 5123:	  for (j = 0; j < p; j++) if (!aliased[j]) y_hat += X[i * p + j] * beta[j];
       16: 5123-block  0
call    0 returned 16
        -: 5124:	  double res   = Y[i] - y_hat;
        -: 5125:	  rss          += res * res;
        -: 5126:	  double diff_m = has_intercept ? (y_hat - mean_y) : y_hat;
        -: 5127:	  mss          += diff_m * diff_m;
        -: 5128:	  hv_store(fitted_hv, valid_row_names[i], strlen(valid_row_names[i]), newSVnv(y_hat), 0);
        -: 5129:	  hv_store(resid_hv,  valid_row_names[i], strlen(valid_row_names[i]), newSVnv(res),   0);
        -: 5130:	  Safefree(valid_row_names[i]);
        -: 5131:	}
        -: 5132:	Safefree(valid_row_names);
        -: 5133:
        -: 5134:	// Single, authoritative rse_sq calculation
        6: 5135:	rse_sq = (df_res > 0) ? (rss / (double)df_res) : NAN;
        6: 5135-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 5136:
    %%%%%: 5136-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5137:	int df_int = has_intercept ? 1 : 0;
    %%%%%: 5137-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5137-block  1
call    2 never executed
    #####: 5138:	double r_squared = 0.0, adj_r_squared = 0.0, f_stat = NAN, f_pvalue = NAN;
    %%%%%: 5138-block  0
call    0 never executed
call    1 never executed
    #####: 5139:	int numdf = final_rank - df_int;
        -: 5140:
    #####: 5141:	if (final_rank != df_int && (mss + rss) > 0.0) {
    %%%%%: 5141-block  0
call    0 never executed
        -: 5142:	  r_squared     = mss / (mss + rss);
        -: 5143:	  adj_r_squared = 1.0 - (1.0 - r_squared) * ((valid_n - df_int) / (double)df_res);
        -: 5144:	  if (rse_sq > 0.0 && numdf > 0) {
        6: 5145:		   f_stat   = (mss / (double)numdf) / rse_sq;
        6: 5145-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 1
        5: 5145-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        6: 5145-block  2
branch  4 taken 1 (fallthrough)
branch  5 taken 5
        1: 5145-block  3
branch  6 taken 0 (fallthrough)
branch  7 taken 1
    #####: 5146:		   f_pvalue = 1.0 - pf(f_stat, (double)numdf, (double)df_res);
    %%%%%: 5146-block  0
call    0 never executed
        -: 5147:	  } else if (rse_sq == 0.0) {
        -: 5148:		   f_stat   = INFINITY;
        -: 5149:		   f_pvalue = 0.0;
        -: 5150:	  }
        -: 5151:	} else if (final_rank == df_int) {
        6: 5152:	  r_squared = 0.0; adj_r_squared = 0.0;
       6*: 5153:	}
        6: 5153-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 5153-block  1
        6: 5154:
        -: 5155:	for (j = 0; j < p; j++) {
       6*: 5156:	  hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
        6: 5156-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 5156-block  1
call    2 never executed
     3033: 5157:	  av_push(terms_av, newSVpv(exp_terms[j], 0));
        6: 5157-block  0
     3033: 5157-block  1
branch  0 taken 3027
branch  1 taken 6 (fallthrough)
     3027: 5158:	  HV *restrict row_hv = newHV();
     3027: 5158-block  0
call    0 returned 3027
call    1 returned 3027
        -: 5159:	  if (aliased[j]) {
        6: 5160:		   hv_store(row_hv, "Estimate",   8,  newSVpv("NaN", 0), 0);
        6: 5160-block  0
        -: 5161:		   hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
        -: 5162:		   hv_store(row_hv, "t value",    7,  newSVpv("NaN", 0), 0);
        -: 5163:		   hv_store(row_hv, "Pr(>|t|)",   8,  newSVpv("NaN", 0), 0);
        -: 5164:	  } else {
        -: 5165:		   double se    = sqrt(rse_sq * XtX[j * p + j]);
        -: 5166:		   double t_val = (se > 0.0) ? (beta[j] / se) : (INFINITY * (beta[j] >= 0.0 ? 1.0 : -1.0));
        2: 5167:		   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 5167-block  0
call    2 returned 1
call    3 returned 1
        -: 5168:		   hv_store(row_hv, "Estimate",   8,  newSVnv(beta[j]), 0);
        2: 5169:		   hv_store(row_hv, "Std. Error", 10, newSVnv(se),      0);
        2: 5170:		   hv_store(row_hv, "t value",    7,  newSVnv(t_val),   0);
        2: 5171:		   hv_store(row_hv, "Pr(>|t|)",   8,  newSVnv(p_val),   0);
        -: 5172:	  }
        -: 5173:	  hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
       2*: 5174:	}
        2: 5174-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5174-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    %%%%%: 5174-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 5174-block  3
branch  6 never executed (fallthrough)
branch  7 never executed
    #####: 5175:
    %%%%%: 5175-block  0
call    0 never executed
    #####: 5176:	hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv),   0);
        -: 5177:	hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
        -: 5178:	hv_store(res_hv, "residuals",      9, newRV_noinc((SV*)resid_hv),  0);
        -: 5179:	hv_store(res_hv, "df.residual",   11, newSVuv(df_res),             0);
        2: 5180:	hv_store(res_hv, "rank",           4, newSVuv(final_rank),         0);
        2: 5180-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    #####: 5181:	hv_store(res_hv, "rss",            3, newSVnv(rss),                0);
    %%%%%: 5181-block  0
call    0 never executed
        -: 5182:	hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv),0);
        -: 5183:	hv_store(res_hv, "terms",          5, newRV_noinc((SV*)terms_av),  0);
        7: 5184:	hv_store(res_hv, "r.squared",      9, newSVnv(r_squared),          0);
        2: 5184-block  0
        5: 5184-block  1
        7: 5184-block  2
branch  0 taken 5
branch  1 taken 2 (fallthrough)
        5: 5185:	hv_store(res_hv, "adj.r.squared", 13, newSVnv(adj_r_squared),      0);
        5: 5185-block  0
call    0 returned 5
        5: 5186:	if (!isnan(f_stat)) {
        -: 5187:	  AV *fstat_av = newAV();
        5: 5188:	  av_push(fstat_av, newSVnv(f_stat));
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 5188-block  0
call    2 returned 2
        3: 5189:	  av_push(fstat_av, newSViv(numdf));
        3: 5189-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 5189-block  1
call    2 returned 1
        2: 5190:	  av_push(fstat_av, newSViv(df_res));
        2: 5190-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5190-block  1
call    2 returned 2
    #####: 5191:	  hv_store(res_hv, "fstatistic", 10, newRV_noinc((SV*)fstat_av), 0);
    %%%%%: 5191-block  0
call    0 never executed
        -: 5192:	  hv_store(res_hv, "f.pvalue",    8, newSVnv(f_pvalue),          0);
        -: 5193:	}
        2: 5194:
        2: 5194-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 5194-block  1
call    2 returned 0
        -: 5195:	// Deep Cleanup
        1: 5196:	for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
        1: 5196-block  0
call    0 returned 1
        1: 5197:	for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 5198:	for (j = 0; j < p_exp; j++) {
        1: 5198-block  0
call    0 returned 1
        -: 5199:	  Safefree(exp_terms[j]);
     5002: 5200:	  if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
     5001: 5200-block  0
branch  0 taken 5000
branch  1 taken 1 (fallthrough)
        -: 5201:	}
        -: 5202:	Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
        -: 5203:	Safefree(X); Safefree(Y); Safefree(XtX); Safefree(XtY);
     6318: 5204:	Safefree(beta); Safefree(aliased);
     6318: 5204-block  0
call    0 returned 6318
     6318: 5205:	if (row_hashes) Safefree(row_hashes);
call    0 returned 6318
     6318: 5206:
     6318: 5207:	RETVAL = newRV_noinc((SV*)res_hv);
branch  0 taken 1318
branch  1 taken 5000 (fallthrough)
     5000: 5207-block  0
branch  2 taken 0
branch  3 taken 5000 (fallthrough)
        -: 5208:}
     5000: 5209:OUTPUT:
        -: 5210:    RETVAL
     5000: 5211:
     5000: 5211-block  0
call    0 returned 5000
call    1 returned 5000
     5000: 5212:void seq(from, to, by = 1.0)
branch  0 taken 4999 (fallthrough)
branch  1 taken 1
     4999: 5213:	double from
     4999: 5213-block  0
call    0 returned 4999
call    1 returned 4999
        -: 5214:	double to
        -: 5215:	double by
        -: 5216:PPCODE:
        1: 5217:	{
        1: 5217-block  0
call    0 returned 1
        -: 5218:		//Handle the zero 'by' case
        -: 5219:		if (by == 0.0) {
        -: 5220:			if (from == to) {
        -: 5221:				 EXTEND(SP, 1);
        -: 5222:				 mPUSHn(from);
        -: 5223:				 XSRETURN(1);
        -: 5224:			} else {
        -: 5225:				 croak("invalid 'by' argument: cannot be zero when from != to");
        -: 5226:			}
        9: 5227:		}
        9: 5227-block  0
call    0 returned 9
        -: 5228:		// Check for wrong direction / infinite loop
        -: 5229:		if ((from < to && by < 0.0) || (from > to && by > 0.0)) {
        -: 5230:			croak("wrong sign in 'by' argument");
        9: 5231:		}
        9: 5232:		/* * Calculate number of elements. 
        9: 5233:		* R uses a small epsilon (like 1e-10) to avoid dropping the last 
        9: 5234:		* element due to floating point inaccuracies.
        9: 5235:		*/
        9: 5236:		double n_elements_d = (to - from) / by;
        9: 5237:		if (n_elements_d < 0.0) n_elements_d = 0.0;
        -: 5238:		size_t n_elements = (n_elements_d + 1e-10) + 1;
        9: 5239:		// Pre-extend the stack to avoid reallocating inside the loop
        9: 5240:		EXTEND(SP, n_elements);
        9: 5241:		for (size_t i = 0; i < n_elements; i++) {
        9: 5242:			mPUSHn(from + i * by);
        -: 5243:		}
        9: 5244:		XSRETURN(n_elements);
        9: 5245:	}
        -: 5246:
        9: 5247:SV* rnorm(...)
       9*: 5248:	CODE:
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 5248-block  0
call    2 never executed
        -: 5249:	{
        -: 5250:	  // Auto-seed the PRNG if the Perl script hasn't done so yet
        -: 5251:	  AUTO_SEED_PRNG();
        9: 5252:
        9: 5253:	  size_t n = 0;
        9: 5253-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5254:	  double mean = 0.0, sd = 1.0;
       9*: 5255:	  int arg_start = 0;
        9: 5255-block  0
call    0 returned 9
branch  1 taken 0 (fallthrough)
branch  2 taken 9
    %%%%%: 5255-block  1
call    3 never executed
        9: 5256:
        9: 5256-block  0
call    0 returned 9
        9: 5257:	  // Check if the first argument is a simple integer (rnorm(33))
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5258:	  if (items > 0 && SvIOK(ST(0)) && (items == 1 || items % 2 != 0)) {
        9: 5258-block  0
call    0 returned 9
        9: 5259:		   n = (unsigned int)SvUV(ST(0));
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5259-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 5260:		   arg_start = 1; // Start parsing named arguments from the second element
        9: 5261:	  }
        9: 5261-block  0
call    0 returned 9
       9*: 5262:
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 5262-block  0
call    2 never executed
        9: 5262-block  1
call    3 returned 9
       67: 5263:	  // --- Parse remaining named arguments from the flat stack ---
       67: 5263-block  0
branch  0 taken 58
branch  1 taken 9 (fallthrough)
       58: 5264:	  if ((items - arg_start) % 2 != 0) {
       58: 5265:		   croak("Usage: rnorm(n), rnorm(n => 10, mean => 0, sd => 1), or rnorm(33, mean => 0)");
       58: 5265-block  0
call    0 returned 58
        -: 5266:	  }
    #####: 5267:
    %%%%%: 5267-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5267-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 5268:	  for (int i = arg_start; i < items; i += 2) {
    %%%%%: 5268-block  0
call    0 never executed
    #####: 5269:		   const char* restrict key = SvPV_nolen(ST(i));
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5269-block  0
call    2 never executed
    %%%%%: 5269-block  1
call    3 never executed
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 5269-block  2
call    6 never executed
    %%%%%: 5269-block  3
call    7 never executed
    #####: 5270:		   SV* restrict val = ST(i + 1);
    #####: 5271:
    %%%%%: 5271-block  0
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
        -: 5272:		   if      (strEQ(key, "n"))    n    = (unsigned int)SvUV(val);
    #####: 5273:		   else if (strEQ(key, "mean")) mean = SvNV(val);
    %%%%%: 5273-block  0
call    0 never executed
call    1 never executed
    #####: 5274:		   else if (strEQ(key, "sd"))   sd   = SvNV(val);
call    0 never executed
    #####: 5275:		   else croak("rnorm: unknown argument '%s'", key);
        -: 5276:	  }
    #####: 5277:
    %%%%%: 5277-block  0
call    0 never executed
        -: 5278:	  if (sd < 0.0) croak("rnorm: standard deviation must be non-negative");
    #####: 5279:
    %%%%%: 5279-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5280:	  AV *restrict result_av = newAV();
    #####: 5281:	  if (n > 0) {
    %%%%%: 5281-block  0
call    0 never executed
    #####: 5282:		   av_extend(result_av, n - 1);
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5282-block  0
call    2 never executed
    %%%%%: 5282-block  1
call    3 never executed
    #####: 5283:		   // Generate random normals using the Box-Muller transform
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5283-block  0
call    2 never executed
    %%%%%: 5283-block  1
call    3 never executed
    #####: 5284:		   for (size_t i = 0; i < n; ) {
    %%%%%: 5284-block  0
    %%%%%: 5284-block  1
branch  0 never executed
branch  1 never executed
    #####: 5285:		        double u, v, s;
    %%%%%: 5285-block  0
call    0 never executed
    #####: 5286:		        do {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5286-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5286-block  1
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 5287:		            // Drand01() hooks into Perl's internal PRNG, respecting Perl's srand()
        -: 5288:		            u = 2.0 * Drand01() - 1.0;
    #####: 5289:		            v = 2.0 * Drand01() - 1.0;
    #####: 5290:		            s = u * u + v * v;
    %%%%%: 5290-block  0
call    0 never executed
        -: 5291:		        } while (s >= 1.0 || s == 0.0);
    #####: 5292:		        
    %%%%%: 5292-block  0
    %%%%%: 5292-block  1
call    0 never executed
    %%%%%: 5292-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 5293:		        double mul = sqrt(-2.0 * log(s) / s);
    %%%%%: 5293-block  0
call    0 never executed
call    1 never executed
    #####: 5294:		        // Box-Muller generates two independent values per iteration
call    0 never executed
        -: 5295:		        av_store(result_av, i++, newSVnv(mean + sd * u * mul));
        -: 5296:		        if (i < n) {
    #####: 5297:		            av_store(result_av, i++, newSVnv(mean + sd * v * mul));
    %%%%%: 5297-block  0
call    0 never executed
        -: 5298:		        }
        -: 5299:		   }
        -: 5300:	  }
        -: 5301:	  RETVAL = newRV_noinc((SV*)result_av);
        9: 5302:	}
      111: 5303:	OUTPUT:
        9: 5303-block  0
      102: 5303-block  1
branch  0 taken 74 (fallthrough)
branch  1 taken 28
       74: 5303-block  2
      102: 5303-block  3
      111: 5303-block  4
branch  2 taken 102 (fallthrough)
branch  3 taken 9
      102: 5303-block  5
branch  4 taken 102
branch  5 taken 0 (fallthrough)
        9: 5304:	RETVAL
        -: 5305:
        9: 5306:SV* aov(data_sv, formula_sv)
        9: 5307:	SV* data_sv
        9: 5307-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 8
        3: 5308:	SV* formula_sv
        1: 5308-block  0
        2: 5308-block  1
call    0 returned 2
        3: 5308-block  2
branch  1 taken 2
branch  2 taken 1 (fallthrough)
       1*: 5309:	CODE:
        1: 5309-block  0
call    0 returned 1
branch  1 taken 0 (fallthrough)
branch  2 taken 1
    %%%%%: 5309-block  1
call    3 never executed
        1: 5310:	{
        1: 5310-block  0
call    0 returned 0
        -: 5311:	  const char *restrict formula = SvPV_nolen(formula_sv);
        8: 5312:	  char f_cpy[512];
        8: 5313:	  char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
        8: 5314:
        -: 5315:	  char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL, **restrict parent_term = NULL;
        -: 5316:	  bool *restrict is_dummy = NULL, *is_interact = NULL;
       8*: 5317:	  char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
        8: 5317-block  0
    %%%%%: 5317-block  1
        8: 5317-block  2
branch  0 taken 0
branch  1 taken 8 (fallthrough)
       8*: 5318:	  int *restrict term_map = NULL, *restrict left_idx = NULL, *restrict right_idx = NULL;
        8: 5318-block  0
    %%%%%: 5318-block  1
        8: 5318-block  2
branch  0 taken 0
branch  1 taken 8 (fallthrough)
       8*: 5319:	  unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
        8: 5319-block  0
    %%%%%: 5319-block  1
        8: 5319-block  2
branch  0 taken 0
branch  1 taken 8 (fallthrough)
       8*: 5320:	  size_t n = 0, valid_n = 0, i, j;
        8: 5320-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    %%%%%: 5320-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5320-block  2
       8*: 5321:	  bool has_intercept = TRUE;
        8: 5321-block  0
    %%%%%: 5321-block  1
        8: 5321-block  2
branch  0 taken 0
branch  1 taken 8 (fallthrough)
       8*: 5322:
        8: 5322-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    %%%%%: 5322-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5322-block  2
       8*: 5323:	  char **restrict row_names = NULL;
        8: 5323-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    %%%%%: 5323-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5323-block  2
        -: 5324:	  HV **restrict row_hashes = NULL;
       8*: 5325:	  HV *restrict data_hoa = NULL;
        8: 5325-block  0
    %%%%%: 5325-block  1
        8: 5325-block  2
branch  0 taken 0
branch  1 taken 8 (fallthrough)
       8*: 5326:	  SV *restrict ref = NULL;
        8: 5326-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    %%%%%: 5326-block  1
        8: 5327:	  HE *restrict entry;
       8*: 5328:	  double **restrict X_mat = NULL;
        8: 5328-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 5328-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 8
    %%%%%: 5328-block  2
        -: 5329:	  double *restrict Y = NULL;
        8: 5330:
        8: 5331:	  char **restrict term_base_level = NULL;  /* reference level for each uniq_term (NULL if not categorical) */
        8: 5332:	  if (!SvROK(data_sv)) croak("aov: data is required and must be a reference");
        8: 5332-block  0
call    0 returned 8
       19: 5333:	  // ========================================================================
       19: 5333-block  0
branch  0 taken 11
branch  1 taken 8 (fallthrough)
       11: 5334:	  // PHASE 1: Data Extraction
       11: 5334-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 10
        1: 5335:	  // ========================================================================
        1: 5335-block  0
call    0 returned 1
        4: 5336:	  ref = SvRV(data_sv);
        3: 5336-block  0
        4: 5336-block  1
call    0 returned 4
branch  1 taken 3
branch  2 taken 1 (fallthrough)
        3: 5337:	  if (SvTYPE(ref) == SVt_PVHV) {
        3: 5337-block  0
call    0 returned 3
        3: 5338:		   HV*restrict hv = (HV*)ref;
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5338-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 5339:		   if (hv_iterinit(hv) == 0) croak("aov: Data hash is empty");
        3: 5339-block  0
call    0 returned 3
        3: 5340:		   entry = hv_iternext(hv);
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 5341:		   if (entry) {
        2: 5342:		        SV*restrict val = hv_iterval(hv, entry);
        2: 5342-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5343:		        if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
        2: 5343-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 5343-block  1
        2: 5344:		            data_hoa = hv;
        2: 5345:		            n = av_len((AV*)SvRV(val)) + 1;
        2: 5345-block  0
        -: 5346:		            Newx(row_names, n, char*);
        -: 5347:		            for(i = 0; i < n; i++) { 
        -: 5348:		                char buf[32]; snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i+1));
        -: 5349:		                row_names[i] = savepv(buf); 
        1: 5350:		            }
        1: 5350-block  0
call    0 returned 1
        -: 5351:		        } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
       10: 5352:		            n = hv_iterinit(hv);
       10: 5353:		            Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
       10: 5353-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 0
       10: 5354:		            i = 0;
       10: 5354-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 7
        3: 5354-block  1
       10: 5355:		            while ((entry = hv_iternext(hv))) {
       10: 5356:		                I32 len;
       10: 5356-block  0
        -: 5357:		                row_names[i] = savepv(hv_iterkey(entry, &len));
        -: 5358:		                row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
       11: 5359:		                i++;
       11: 5359-block  0
call    0 returned 11
        -: 5360:		            }
        -: 5361:		        } else croak("aov: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
        -: 5362:		   }
        8: 5363:	  } else if (SvTYPE(ref) == SVt_PVAV) {
        8: 5363-block  0
call    0 returned 8
        8: 5364:		   AV*restrict av = (AV*)ref;
call    0 returned 8
        8: 5365:		   n = av_len(av) + 1;
call    0 returned 8
call    1 returned 8
        8: 5366:		   Newx(row_names, n, char*);
call    0 returned 8
call    1 returned 8
        8: 5367:		   Newx(row_hashes, n, HV*);
call    0 returned 8
call    1 returned 8
        8: 5368:		   for (i = 0; i < n; i++) {
call    0 returned 8
call    1 returned 8
call    2 returned 8
        -: 5369:		        SV**restrict val = av_fetch(av, i, 0);
        8: 5370:		        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 5370-block  0
call    2 returned 8
        -: 5371:		            row_hashes[i] = (HV*)SvRV(*val);
        8: 5372:		            char buf[32];
        8: 5372-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 5373:		            snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
        8: 5373-block  0
call    0 returned 8
       20: 5374:		            row_names[i] = savepv(buf);
       20: 5374-block  0
branch  0 taken 12
branch  1 taken 8 (fallthrough)
       12: 5375:		        } else {
       12: 5375-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 5376:		            for (size_t k = 0; k < i; k++) Safefree(row_names[k]);
    #####: 5377:		            Safefree(row_names); Safefree(row_hashes);
    %%%%%: 5377-block  0
call    0 never executed
call    1 never executed
        -: 5378:		            croak("aov: Array values must be HashRefs (AoH)");
       12: 5379:		        }
       12: 5380:		   }
       12: 5380-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 11
        1: 5381:	  } else croak("aov: Data must be an Array or Hash reference");
        1: 5382:
        1: 5383:	  // ========================================================================
        1: 5384:	  // PHASE 2: Formula Parsing & `.` Expansion
       1*: 5385:	  // ========================================================================
        1: 5385-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 5385-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5385-block  2
       1*: 5386:	  src = (char*)formula; dst = f_cpy;
        1: 5386-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 5386-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5386-block  2
        1: 5387:	  while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
        1: 5387-block  0
call    0 returned 1
        1: 5388:	  *dst = '\0';
call    0 returned 1
        1: 5389:
        1: 5390:	  tilde = strchr(f_cpy, '~');
call    0 returned 1
        1: 5391:	  if (!tilde) {
        -: 5392:		   for (i = 0; i < n; i++) Safefree(row_names[i]);
       11: 5393:		   Safefree(row_names); if (row_hashes) Safefree(row_hashes);
      11*: 5394:		   croak("aov: invalid formula, missing '~'");
       11: 5394-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 11
    %%%%%: 5394-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5394-block  2
       11: 5395:	  }
       11: 5395-block  0
call    0 returned 11
        -: 5396:	  *tilde = '\0';
       12: 5397:	  lhs = f_cpy;
       12: 5397-block  0
call    0 returned 12
        -: 5398:	  rhs = tilde + 1;
        -: 5399:
        -: 5400:	  char *restrict p_idx;
       30: 5401:	  while ((p_idx = strstr(rhs, "-1")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        8: 5401-block  0
       22: 5401-block  1
       30: 5401-block  2
branch  0 taken 22
branch  1 taken 8 (fallthrough)
       22: 5402:	  while ((p_idx = strstr(rhs, "+0")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
       43: 5403:	  while ((p_idx = strstr(rhs, "0+")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
       22: 5403-block  0
       21: 5403-block  1
       43: 5403-block  2
branch  0 taken 21
branch  1 taken 22 (fallthrough)
      21*: 5404:	  if (rhs[0] == '0' && rhs[1] == '\0')        { has_intercept = FALSE; rhs[0] = '\0'; }
       21: 5404-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 5404-block  1
        -: 5405:	  while ((p_idx = strstr(rhs, "+1")) != NULL) { memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
       22: 5406:	  if (rhs[0] == '1' && rhs[1] == '\0')        { rhs[0] = '\0'; } 
       22: 5406-block  0
branch  0 taken 22 (fallthrough)
branch  1 taken 0
       22: 5406-block  1
call    2 returned 22
        -: 5407:	  else if (rhs[0] == '1' && rhs[1] == '+')    { memmove(rhs, rhs + 2, strlen(rhs + 2) + 1); }
        8: 5408:
        -: 5409:	  while ((p_idx = strstr(rhs, "++")) != NULL) memmove(p_idx, p_idx + 1, strlen(p_idx + 1) + 1);
        -: 5410:	  if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
        -: 5411:	  size_t len_rhs = strlen(rhs);
        8: 5412:	  if (len_rhs > 0 && rhs[len_rhs - 1] == '+') rhs[len_rhs - 1] = '\0';
        8: 5412-block  0
call    0 returned 8
        -: 5413:
        -: 5414:	  char rhs_expanded[2048] = "";
        -: 5415:	  size_t rhs_len = 0;
        -: 5416:	  chunk = strtok(rhs, "+");
        -: 5417:	  while (chunk != NULL) {
       29: 5418:		   if (strcmp(chunk, ".") == 0) {
       21: 5418-block  0
       29: 5418-block  1
branch  0 taken 22
branch  1 taken 7 (fallthrough)
       22: 5419:		        AV *restrict cols = get_all_columns(data_hoa, row_hashes, n);
       22: 5419-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 14
        8: 5420:		        for (size_t c = 0; c <= av_len(cols); c++) {
        8: 5421:		            SV **restrict col_sv = av_fetch(cols, c, 0);
        8: 5421-block  0
call    0 returned 8
call    1 returned 8
        8: 5422:		            if (col_sv && SvOK(*col_sv)) {
call    0 returned 8
call    1 returned 8
        8: 5423:		                const char *restrict col_name = SvPV_nolen(*col_sv);
call    0 returned 8
call    1 returned 8
        8: 5424:		                if (strcmp(col_name, lhs) != 0) {
call    0 returned 8
call    1 returned 8
call    2 returned 8
        -: 5425:		                    size_t slen = strlen(col_name);
        -: 5426:		                    if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
       22: 5427:		                        if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
       22: 5427-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 14
        8: 5428:		                        strcat(rhs_expanded, col_name);
        8: 5428-block  0
call    0 returned 8
        8: 5429:		                        rhs_len += slen;
call    0 returned 8
        8: 5430:		                    }
        8: 5431:		                }
        8: 5432:		            }
        8: 5433:		        }
        -: 5434:		        SvREFCNT_dec(cols);
        -: 5435:		   } else {
       14: 5436:		        size_t slen = strlen(chunk);
       14: 5437:		        if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
       14: 5437-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 12
        -: 5438:		            if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        2: 5439:		            strcat(rhs_expanded, chunk);
        2: 5440:		            rhs_len += slen;
        2: 5441:		        }
        -: 5442:		   }
        2: 5443:		   chunk = strtok(NULL, "+");
        2: 5443-block  0
call    0 returned 2
        2: 5444:	  }
call    0 returned 2
        6: 5445:
        4: 5445-block  0
        6: 5445-block  1
branch  0 taken 4
branch  1 taken 2 (fallthrough)
        4: 5446:	  // Setup arrays safely
        4: 5446-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1: 5446-block  1
        4: 5447:	  Newx(terms, term_cap, char*);
        4: 5447-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1: 5447-block  1
        -: 5448:	  Newx(uniq_terms, term_cap, char*);
        -: 5449:	  Newx(exp_terms, exp_cap, char*); Newx(parent_term, exp_cap, char*);
        2: 5450:	  Newx(is_dummy, exp_cap, bool); Newx(is_interact, exp_cap, bool);
        2: 5450-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 5450-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 1
        1: 5451:	  Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
        1: 5451-block  0
call    0 returned 1
call    1 returned 1
        1: 5452:	  Newx(term_map, exp_cap, int); Newx(left_idx, exp_cap, int); Newx(right_idx, exp_cap, int);
call    0 returned 0
        -: 5453:
        2: 5454:	  if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
        1: 5454-block  0
        1: 5454-block  1
        2: 5454-block  2
branch  0 taken 1
branch  1 taken 1 (fallthrough)
        2: 5455:
        1: 5455-block  0
        2: 5455-block  1
branch  0 taken 1
branch  1 taken 1 (fallthrough)
        1: 5456:	  if (strlen(rhs_expanded) > 0) {
        1: 5456-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    #####: 5457:		   chunk = strtok(rhs_expanded, "+");
    #####: 5458:		   while (chunk != NULL) {
    %%%%%: 5458-block  0
call    0 never executed
call    1 never executed
    #####: 5459:		        if (num_terms >= term_cap - 3) {
call    0 never executed
call    1 never executed
    #####: 5460:		            term_cap *= 2;
call    0 never executed
call    1 never executed
    #####: 5461:		            Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
call    0 never executed
call    1 never executed
call    2 never executed
        -: 5462:		        }
        1: 5463:		        char *restrict star = strchr(chunk, '*');
        1: 5464:		        if (star) {
        1: 5464-block  0
call    0 returned 1
        1: 5465:		            *star = '\0';
        1: 5466:		            char *restrict left = chunk;
call    0 returned 1
        1: 5467:		            char *right = star + 1;
        1: 5468:		            char *restrict c_l = strchr(left, '^');
        1: 5469:		            if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
        1: 5470:		            char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
        1: 5471:		            terms[num_terms++] = savepv(left);
        -: 5472:		            terms[num_terms++] = savepv(right);
        -: 5473:		            size_t inter_len = strlen(left) + strlen(right) + 2;
        -: 5474:		            terms[num_terms] = (char*)safemalloc(inter_len);
        1: 5475:		            snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
        1: 5475-block  0
call    0 returned 1
call    1 returned 1
        -: 5476:		        } else {
       12: 5477:		            char *restrict c_chunk = strchr(chunk, '^'); 
       12: 5477-block  0
call    0 returned 12
branch  1 taken 3 (fallthrough)
branch  2 taken 9
        3: 5478:		            if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
        3: 5479:		            terms[num_terms++] = savepv(chunk);
        3: 5480:		        }
        3: 5480-block  0
call    0 returned 3
       52: 5481:		        chunk = strtok(NULL, "+");
       49: 5481-block  0
       52: 5481-block  1
branch  0 taken 49
branch  1 taken 3 (fallthrough)
       49: 5482:		   }
       49: 5482-block  0
call    0 returned 49
       49: 5483:	  }
branch  0 taken 49 (fallthrough)
branch  1 taken 0
       49: 5484:
       78: 5485:	  for (i = 0; i < num_terms; i++) {
       49: 5485-block  0
       29: 5485-block  1
       78: 5485-block  2
branch  0 taken 71
branch  1 taken 7 (fallthrough)
       71: 5486:		   bool found = FALSE;
       71: 5486-block  0
branch  0 taken 42 (fallthrough)
branch  1 taken 29
       42: 5486-block  1
        -: 5487:		   for (size_t k = 0; k < num_uniq; k++) {
       49: 5488:		         if (strcmp(terms[i], uniq_terms[k]) == 0) { found = TRUE; break; }
       49: 5488-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 42
       7*: 5489:		   }
        7: 5489-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 5489-block  1
call    2 never executed
        7: 5490:		   if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
        7: 5490-block  0
call    0 returned 7
        -: 5491:	  }
       49: 5492:	  p = num_uniq;
       49: 5492-block  0
call    0 returned 49
        -: 5493:
        -: 5494:	  /* ---- NEW: allocate one slot per unique term, zero-initialised so
        -: 5495:		           non-categorical terms stay NULL                          ---- */
        3: 5496:	  Newxz(term_base_level, num_uniq, char*);
        3: 5496-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        7: 5497:	  /* ----------------------------------------------------------------------- */
        3: 5497-block  0
        4: 5497-block  1
        7: 5497-block  2
branch  0 taken 4
branch  1 taken 3 (fallthrough)
        9: 5498:
        4: 5498-block  0
        5: 5498-block  1
        9: 5498-block  2
branch  0 taken 5
branch  1 taken 4 (fallthrough)
        5: 5499:	  // ========================================================================
        5: 5499-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        1: 5500:	  // PHASE 3: Categorical & Interaction Expansion
        1: 5500-block  0
        -: 5501:	  // ========================================================================
        -: 5502:	  for (j = 0; j < p; j++) {
        -: 5503:		   if (p_exp + 64 >= exp_cap) {
        -: 5504:		         exp_cap *= 2;
        -: 5505:		         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
        3: 5506:		         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
        3: 5506-block  0
call    0 returned 3
        -: 5507:		         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 5508:		         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
        7: 5509:		   }
        7: 5509-block  0
branch  0 taken 4
branch  1 taken 3 (fallthrough)
        4: 5510:
        4: 5510-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4
    #####: 5511:		   if (strcmp(uniq_terms[j], "Intercept") == 0) {
    #####: 5512:		         exp_terms[p_exp] = savepv("Intercept");
    %%%%%: 5512-block  0
call    0 never executed
call    1 never executed
    #####: 5513:		         parent_term[p_exp] = savepv("Intercept");
call    0 never executed
call    1 never executed
    #####: 5514:		         is_dummy[p_exp] = FALSE; is_interact[p_exp] = FALSE;
call    0 never executed
call    1 never executed
    #####: 5515:		         term_map[p_exp] = j;
call    0 never executed
call    1 never executed
call    2 never executed
        -: 5516:		         p_exp++;
        4: 5517:		         continue;
        4: 5518:		   }
        4: 5518-block  0
call    0 returned 4
        4: 5519:		   
        4: 5520:		   char *restrict colon = strchr(uniq_terms[j], ':');
call    0 returned 4
        4: 5521:		   if (colon) {
        4: 5522:		         char left[256], right[256];
call    0 returned 4
        4: 5523:		         strncpy(left, uniq_terms[j], colon - uniq_terms[j]);
call    0 returned 4
        4: 5524:		         left[colon - uniq_terms[j]] = '\0';
        4: 5525:		         strcpy(right, colon + 1);
        -: 5526:
       10: 5527:		         int *restrict l_indices = (int*)safemalloc(p_exp * sizeof(int)); int l_count = 0;
        3: 5527-block  0
        7: 5527-block  1
call    0 returned 7
       10: 5527-block  2
branch  1 taken 7
branch  2 taken 3 (fallthrough)
        3: 5528:		         int *restrict r_indices = (int*)safemalloc(p_exp * sizeof(int)); int r_count = 0;
        3: 5528-block  0
call    0 returned 3
        -: 5529:		         for (size_t e = 0; e < p_exp; e++) {
    #####: 5530:		             if (strcmp(parent_term[e], left) == 0) l_indices[l_count++] = e;
    %%%%%: 5530-block  0
call    0 never executed
    #####: 5531:		             if (strcmp(parent_term[e], right) == 0) r_indices[r_count++] = e;
call    0 never executed
    #####: 5532:		         }
call    0 never executed
    #####: 5533:
    #####: 5534:		         if (l_count == 0 || r_count == 0) {
    #####: 5535:		             Safefree(l_indices); Safefree(r_indices);
        -: 5536:		             croak("aov: Interaction term '%s' requires its main effects to be explicitly included in the formula", uniq_terms[j]);
        -: 5537:		         } else {
        9: 5538:		             for (int li = 0; li < l_count; li++) {
        9: 5538-block  0
call    0 returned 9
        9: 5539:		                 for (int ri = 0; ri < r_count; ri++) {
call    0 returned 9
        9: 5540:		                     if (p_exp >= exp_cap) {
        9: 5541:		                         exp_cap *= 2;
        9: 5542:		                         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
        -: 5543:		                         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
        -: 5544:		                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 5545:		                         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
        7: 5546:		                     }
        7: 5546-block  0
call    0 returned 7
       59: 5547:		                     size_t t_len = strlen(exp_terms[l_indices[li]]) + strlen(exp_terms[r_indices[ri]]) + 2;
       52: 5547-block  0
call    0 returned 52
       59: 5547-block  1
branch  1 taken 52
branch  2 taken 7 (fallthrough)
       7*: 5548:		                     exp_terms[p_exp] = (char*)safemalloc(t_len);
        7: 5548-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 5548-block  1
call    2 never executed
        7: 5548-block  2
call    3 returned 7
        -: 5549:		                     snprintf(exp_terms[p_exp], t_len, "%s:%s", exp_terms[l_indices[li]], exp_terms[r_indices[ri]]);
        -: 5550:		                     parent_term[p_exp] = savepv(uniq_terms[j]);
        -: 5551:		                     is_dummy[p_exp] = FALSE; is_interact[p_exp] = TRUE;
       59: 5552:		                     left_idx[p_exp] = l_indices[li];
       52: 5552-block  0
       59: 5552-block  1
branch  0 taken 52
branch  1 taken 7 (fallthrough)
       52: 5553:		                     right_idx[p_exp] = r_indices[ri];
       52: 5553-block  0
call    0 returned 52
      52*: 5554:		                     term_map[p_exp] = j;
branch  0 taken 0 (fallthrough)
branch  1 taken 52
    %%%%%: 5554-block  0
call    2 never executed
       52: 5555:		                     p_exp++;
       52: 5556:		                 }
       52: 5556-block  0
call    0 returned 52
      222: 5557:		             }
      170: 5557-block  0
      222: 5557-block  1
branch  0 taken 170
branch  1 taken 52 (fallthrough)
      170: 5558:		         }
      170: 5558-block  0
branch  0 taken 52 (fallthrough)
branch  1 taken 118
       52: 5559:		         Safefree(l_indices); Safefree(r_indices);
       52: 5559-block  0
      118: 5560:		   } else {
      118: 5560-block  0
branch  0 taken 20 (fallthrough)
branch  1 taken 98
       20: 5561:		         if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
       20: 5561-block  0
       98: 5562:		             char **restrict levels = NULL;
       98: 5562-block  0
branch  0 taken 58 (fallthrough)
branch  1 taken 40
       58: 5563:		             unsigned int num_levels = 0, levels_cap = 8;
       58: 5563-block  0
call    0 returned 58
       58: 5564:		             Newx(levels, levels_cap, char*);
branch  0 taken 58 (fallthrough)
branch  1 taken 0
       58: 5565:		             for (i = 0; i < n; i++) {
       58: 5565-block  0
branch  0 taken 26 (fallthrough)
branch  1 taken 32
       26: 5565-block  1
       32: 5565-block  2
       58: 5566:		                 char* str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
       58: 5566-block  0
call    0 returned 58
    #####: 5567:		                 if (str_val) {
    %%%%%: 5567-block  0
        -: 5568:		                     bool found = FALSE;
       40: 5569:		                     for (size_t l = 0; l < num_levels; l++) {
       40: 5569-block  0
call    0 returned 40
      40*: 5570:		                         if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; }
branch  0 taken 0 (fallthrough)
branch  1 taken 40
    %%%%%: 5570-block  0
        -: 5571:		                     }
        -: 5572:		                     if (!found) {
      52*: 5573:		                         if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
       52: 5573-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 52
    %%%%%: 5573-block  1
call    2 never executed
call    3 never executed
        -: 5574:		                         levels[num_levels++] = savepv(str_val);
       52: 5575:		                     }
      222: 5576:		                     Safefree(str_val);
       52: 5576-block  0
      170: 5576-block  1
      222: 5576-block  2
branch  0 taken 170
branch  1 taken 52 (fallthrough)
       52: 5577:		                 }
       52: 5578:		             }
       52: 5578-block  0
call    0 returned 52
       52: 5579:		             
call    0 returned 52
        -: 5580:		             if (num_levels > 0) {
        7: 5581:		                 for (size_t l1 = 0; l1 < num_levels - 1; l1++) {
        7: 5581-block  0
call    0 returned 7
        7: 5582:		                     for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        -: 5583:		                         if (strcmp(levels[l1], levels[l2]) > 0) {
        4: 5584:		                             char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp;
        1: 5584-block  0
        3: 5584-block  1
call    0 returned 3
        4: 5584-block  2
branch  1 taken 3
branch  2 taken 1 (fallthrough)
        1: 5584-block  3
call    3 returned 1
        4: 5585:		                         }
        3: 5585-block  0
call    0 returned 3
        4: 5585-block  1
branch  1 taken 3
branch  2 taken 1 (fallthrough)
        1: 5585-block  2
call    3 returned 1
        4: 5586:		                     }
        3: 5586-block  0
        4: 5586-block  1
branch  0 taken 3
branch  1 taken 1 (fallthrough)
        3: 5587:		                 }
        3: 5587-block  0
call    0 returned 3
call    1 returned 3
       3*: 5588:
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5588-block  0
call    2 never executed
call    3 never executed
        -: 5589:		                 /* ---- NEW: record the reference (base) level for this term ---- */
        1: 5590:		                 term_base_level[j] = savepv(levels[0]);
        1: 5590-block  0
call    0 returned 1
call    1 returned 1
        1: 5591:		                 /* --------------------------------------------------------------- */
call    0 returned 1
call    1 returned 1
        1: 5592:
call    0 returned 1
call    1 returned 1
        1: 5593:		                 for (size_t l = 1; l < num_levels; l++) {
call    0 returned 1
call    1 returned 1
call    2 returned 1
        3: 5594:		                     if (p_exp >= exp_cap) {
        2: 5594-block  0
call    0 returned 2
        3: 5594-block  1
branch  1 taken 2
branch  2 taken 1 (fallthrough)
        1: 5595:		                         exp_cap *= 2;
        1: 5595-block  0
call    0 returned 1
call    1 returned 1
       1*: 5596:		                         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 5596-block  0
call    2 never executed
        -: 5597:		                         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
       4*: 5598:		                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        1: 5598-block  0
        3: 5598-block  1
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5598-block  2
call    2 never executed
        3: 5598-block  3
        4: 5598-block  4
branch  3 taken 3
branch  4 taken 1 (fallthrough)
        1: 5599:		                         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
        1: 5599-block  0
call    0 returned 1
        -: 5600:		                     }
        1: 5601:		                     size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
call    0 returned 0
        -: 5602:		                     exp_terms[p_exp] = (char*)safemalloc(t_len);
        -: 5603:		                     snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
        -: 5604:		                     parent_term[p_exp] = savepv(uniq_terms[j]);
        -: 5605:		                     is_dummy[p_exp] = TRUE; is_interact[p_exp] = FALSE;
        6: 5606:		                     dummy_base[p_exp] = savepv(uniq_terms[j]);
        6: 5606-block  0
call    0 returned 6
        6: 5607:		                     dummy_level[p_exp] = savepv(levels[l]);
call    0 returned 6
        6: 5608:		                     term_map[p_exp] = j;
call    0 returned 6
        -: 5609:		                     p_exp++;
        -: 5610:		                 }
        6: 5611:		                 for (size_t l = 0; l < num_levels; l++) Safefree(levels[l]);
call    0 returned 6
        6: 5612:		                 Safefree(levels);
call    0 returned 6
        -: 5613:		             } else {
       24: 5614:		                 Safefree(levels);
       18: 5614-block  0
       24: 5614-block  1
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 5615:		                 exp_terms[p_exp] = savepv(uniq_terms[j]);
       18: 5615-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 12
        6: 5615-block  1
       12: 5616:		                 parent_term[p_exp] = savepv(uniq_terms[j]);
       12: 5616-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 11
        1: 5616-block  1
       11: 5617:		                 is_dummy[p_exp] = FALSE; is_interact[p_exp] = FALSE;
       11: 5618:		                 term_map[p_exp] = j;
       11: 5619:		                 p_exp++;
       11: 5620:		             }
       11: 5620-block  0
        -: 5621:		         } else {
        6: 5622:		             exp_terms[p_exp] = savepv(uniq_terms[j]);
       24: 5623:		             parent_term[p_exp] = savepv(uniq_terms[j]);
        6: 5623-block  0
       18: 5623-block  1
       24: 5623-block  2
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 5624:		             is_dummy[p_exp] = FALSE; is_interact[p_exp] = FALSE;
       18: 5624-block  0
branch  0 taken 17 (fallthrough)
branch  1 taken 1
       17: 5624-block  1
        -: 5625:		             term_map[p_exp] = j;
        6: 5626:		             p_exp++;
       39: 5627:		         }
        6: 5627-block  0
       39: 5627-block  1
branch  0 taken 33
branch  1 taken 6 (fallthrough)
       33: 5628:		   }
       33: 5628-block  0
        -: 5629:	  }
        6: 5630:	  X_mat = (double**)safemalloc(n * sizeof(double*));
       6*: 5631:	  for(i = 0; i < n; i++) X_mat[i] = (double*)safemalloc(p_exp * sizeof(double));
        6: 5631-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5631-block  1
    %%%%%: 5631-block  2
        -: 5632:	  Newx(Y, n, double);
        6: 5633:	  // ========================================================================
        6: 5633-block  0
call    0 returned 6
       23: 5634:	  // PHASE 4: Matrix Construction & Listwise Deletion
       17: 5634-block  0
       23: 5634-block  1
branch  0 taken 17
branch  1 taken 6 (fallthrough)
       17: 5635:	  // ========================================================================
       17: 5635-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 11
        6: 5635-block  1
       11: 5636:	  for (i = 0; i < n; i++) {
       11: 5636-block  0
call    0 returned 11
       11: 5637:		   double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
       11: 5638:		   if (isnan(y_val)) { Safefree(row_names[i]); continue; }
       11: 5639:		   bool row_ok = TRUE;
branch  0 taken 10 (fallthrough)
branch  1 taken 1
       10: 5639-block  0
        1: 5639-block  1
        -: 5640:		   double *restrict row_x = (double*)safemalloc(p_exp * sizeof(double));
       11: 5641:		   for (j = 0; j < p_exp; j++) {
       11: 5641-block  0
call    0 returned 11
call    1 returned 11
       11: 5642:		         if (strcmp(exp_terms[j], "Intercept") == 0) {
call    0 returned 11
call    1 returned 11
       11: 5643:		             row_x[j] = 1.0;
call    0 returned 11
call    1 returned 11
       21: 5644:		         } else if (is_interact[j]) {
branch  0 taken 11 (fallthrough)
branch  1 taken 0
       11: 5644-block  0
branch  2 taken 10 (fallthrough)
branch  3 taken 1
       10: 5645:		             row_x[j] = row_x[left_idx[j]] * row_x[right_idx[j]];
       10: 5646:		         } else if (is_dummy[j]) {
       10: 5646-block  0
call    0 returned 10
call    1 returned 10
       10: 5647:		             char*restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
call    0 returned 10
call    1 returned 10
call    2 returned 10
        -: 5648:		             if (str_val) {
        1: 5649:		                 row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
        1: 5649-block  0
call    0 returned 1
call    1 returned 1
        1: 5650:		                 Safefree(str_val);
call    0 returned 1
call    1 returned 1
        -: 5651:		             } else { row_ok = FALSE; break; }
       11: 5652:		         } else {
       11: 5652-block  0
call    0 returned 11
call    1 returned 11
        -: 5653:		             row_x[j] = evaluate_term(data_hoa, row_hashes, i, parent_term[j]);
        -: 5654:		             if (isnan(row_x[j])) { row_ok = FALSE; break; }
        6: 5655:		         }
        6: 5655-block  0
call    0 returned 6
        6: 5656:		   }
call    0 returned 6
call    1 returned 6
        6: 5657:		   if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
call    0 returned 6
call    1 returned 6
        6: 5658:		   
call    0 returned 6
call    1 returned 6
        6: 5659:		   Y[valid_n] = y_val;
call    0 returned 6
call    1 returned 6
        -: 5660:		   for (j = 0; j < p_exp; j++) X_mat[valid_n][j] = row_x[j];
        -: 5661:		   valid_n++;
        -: 5662:		   Safefree(row_x);
        6: 5663:		   Safefree(row_names[i]);
call    0 returned 6
        6: 5664:	  }
call    0 returned 6
        6: 5665:	  Safefree(row_names);
call    0 returned 6
       22: 5666:	  if (valid_n <= p_exp) {
       16: 5666-block  0
       22: 5666-block  1
call    0 returned 22
branch  1 taken 16
branch  2 taken 6 (fallthrough)
       16: 5667:		   // Full Clean Up 
       16: 5667-block  0
call    0 returned 16
      16*: 5668:		   for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
branch  0 taken 16 (fallthrough)
branch  1 taken 0
       16: 5668-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 16
    %%%%%: 5668-block  1
       16: 5669:		   for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
       16: 5669-block  0
call    0 returned 16
       16: 5670:		   for (j = 0; j < p_exp; j++) {
       16: 5671:		        Safefree(exp_terms[j]); Safefree(parent_term[j]);
      151: 5672:		        if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
      135: 5672-block  0
      151: 5672-block  1
branch  0 taken 135
branch  1 taken 16 (fallthrough)
      135: 5673:		   }
      135: 5673-block  0
call    0 returned 135
      135: 5674:		   Safefree(exp_terms); Safefree(parent_term); 
branch  0 taken 86 (fallthrough)
branch  1 taken 49
       86: 5674-block  0
        -: 5675:		   Safefree(is_dummy); Safefree(is_interact); 
       16: 5676:		   Safefree(dummy_base); Safefree(dummy_level);
       16: 5676-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 3
       13: 5676-block  1
        3: 5676-block  2
       16: 5677:		   Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
       16: 5677-block  0
call    0 returned 16
call    1 returned 16
       16: 5678:		   for(i = 0; i < n; i++) Safefree(X_mat[i]);
call    0 returned 16
call    1 returned 16
        -: 5679:		   Safefree(X_mat); Safefree(Y);
        6: 5680:		   if (row_hashes) Safefree(row_hashes);
        6: 5680-block  0
call    0 returned 6
        6: 5681:		   /* ---- NEW ---- */
call    0 returned 6
        6: 5682:		   for (i = 0; i < num_uniq; i++) { if (term_base_level[i]) Safefree(term_base_level[i]); }
call    0 returned 6
call    1 returned 6
        6: 5683:		   Safefree(term_base_level);
call    0 returned 6
call    1 returned 6
        6: 5684:		   /* ------------ */
call    0 returned 6
call    1 returned 6
        -: 5685:		   croak("aov: 0 degrees of freedom (too many NAs or parameters > observations)");
        -: 5686:	  }
        -: 5687:	// ========================================================================
        -: 5688:	  // PHASE 5: Math & Output Formatting
       23: 5689:	  // ========================================================================
       17: 5689-block  0
call    0 returned 17
       23: 5689-block  1
branch  1 taken 17
branch  2 taken 6 (fallthrough)
        6: 5689-block  2
call    3 returned 6
       23: 5690:	  bool *restrict aliased_qr = (bool*)safemalloc(p_exp * sizeof(bool));
       17: 5690-block  0
call    0 returned 17
       23: 5690-block  1
branch  1 taken 17
branch  2 taken 6 (fallthrough)
        6: 5690-block  2
call    3 returned 6
       24: 5691:	  size_t *restrict rank_map = (size_t*)safemalloc(p_exp * sizeof(size_t));
       18: 5691-block  0
       24: 5691-block  1
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 5692:	  apply_householder_aov(X_mat, Y, valid_n, p_exp, aliased_qr, rank_map);
       18: 5692-block  0
call    0 returned 18
call    1 returned 18
       18: 5693:	  double *restrict term_ss;
branch  0 taken 4 (fallthrough)
branch  1 taken 14
        4: 5693-block  0
call    2 returned 4
call    3 returned 4
        -: 5694:	  int *restrict term_df;
        6: 5695:	  Newxz(term_ss, num_uniq, double);
        6: 5695-block  0
call    0 returned 6
call    1 returned 6
        6: 5696:	  Newxz(term_df, num_uniq, int);
call    0 returned 6
call    1 returned 6
        6: 5697:
call    0 returned 6
call    1 returned 6
        6: 5698:	  for (i = 0; i < p_exp; i++) {
call    0 returned 6
call    1 returned 6
call    2 returned 6
        6: 5699:		   if (strcmp(exp_terms[i], "Intercept") == 0) continue; 
call    0 returned 6
call    1 returned 6
       56: 5700:		   if (aliased_qr[i]) continue;
       50: 5700-block  0
call    0 returned 50
       56: 5700-block  1
branch  1 taken 50
branch  2 taken 6 (fallthrough)
        6: 5701:		   int t_idx = term_map[i];
        6: 5701-block  0
call    0 returned 6
call    1 returned 6
        6: 5702:		   size_t r_k = rank_map[i];
call    0 returned 6
call    1 returned 6
       6*: 5703:		   term_ss[t_idx] += Y[r_k] * Y[r_k];
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 5703-block  0
call    2 never executed
        6: 5704:		   term_df[t_idx] += 1;
        6: 5704-block  0
call    0 returned 6
        -: 5705:	  }
        -: 5706:	  int rank = 0;
        -: 5707:	  for (i = 0; i < p_exp; i++) {
        -: 5708:		   if (!aliased_qr[i]) rank++;
        -: 5709:	  }
        -: 5710:	  double rss_prev = 0.0;
        -: 5711:	  for (i = rank; i < valid_n; i++) {
        -: 5712:		   rss_prev += Y[i] * Y[i];
        -: 5713:	  }
        6: 5714:	  int res_df = valid_n - rank;
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1: 5714-block  0
call    2 returned 0
        -: 5715:	  double ms_res = (res_df > 0) ? rss_prev / res_df : 0.0;
        5: 5716:
        5: 5717:	  HV*restrict ret_hash = newHV();
        5: 5718:	  for (j = 0; j < num_uniq; j++) {
        -: 5719:		   if (strcmp(uniq_terms[j], "Intercept") == 0) continue;
        -: 5720:		   HV*restrict term_stats = newHV();
        7: 5721:		   double ss = term_ss[j];
        5: 5721-block  0
        2: 5721-block  1
        7: 5721-block  2
branch  0 taken 2
branch  1 taken 5 (fallthrough)
       2*: 5722:		   int df = term_df[j];
        2: 5722-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 5722-block  1
call    2 never executed
        2: 5723:		   double ms = (df > 0) ? ss / df : 0.0;
        2: 5723-block  0
call    0 returned 2
        2: 5724:
        2: 5725:		   hv_stores(term_stats, "Df", newSViv(df));
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5725-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 2
    #####: 5726:		   hv_stores(term_stats, "Sum Sq", newSVnv(ss));
    %%%%%: 5726-block  0
call    0 never executed
        2: 5727:		   hv_stores(term_stats, "Mean Sq", newSVnv(ms));
        2: 5727-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5728:		   if (ms_res > 0.0 && df > 0) {
        2: 5728-block  0
call    0 returned 2
        -: 5729:		         double f_val = ms / ms_res;
        -: 5730:		         hv_stores(term_stats, "F value", newSVnv(f_val));
        -: 5731:		         hv_stores(term_stats, "Pr(>F)", newSVnv(1.0 - pf(f_val, (double)df, (double)res_df)));
       5*: 5732:		   } else {
        5: 5732-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 5732-block  1
call    2 never executed
        5: 5733:		         hv_stores(term_stats, "F value", newSVnv(NAN));
        5: 5734:		         hv_stores(term_stats, "Pr(>F)", newSVnv(NAN));
        -: 5735:		   }
        5: 5736:		   hv_store(ret_hash, uniq_terms[j], strlen(uniq_terms[j]), newRV_noinc((SV*)term_stats), 0);
        5: 5736-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 3
        2: 5737:	  }
       2*: 5738:
        2: 5738-block  0
call    0 returned 2
branch  1 taken 0 (fallthrough)
branch  2 taken 2
    %%%%%: 5738-block  1
call    3 never executed
        2: 5739:	  HV*restrict res_stats = newHV();
        2: 5739-block  0
call    0 returned 2
        2: 5740:	  hv_stores(res_stats, "Df", newSViv(res_df));
call    0 returned 2
        2: 5741:	  hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5741-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 5741-block  1
branch  4 taken 2 (fallthrough)
branch  5 taken 0
        2: 5741-block  2
branch  6 taken 2 (fallthrough)
branch  7 taken 0
        2: 5742:	  hv_stores(res_stats, "Mean Sq", newSVnv(ms_res));
        2: 5743:	  hv_stores(ret_hash, "Residuals", newRV_noinc((SV*)res_stats));
        2: 5744:
        2: 5744-block  0
call    0 returned 2
        2: 5745:	  /* ---- NEW: group_stats => { mean => {...}, size => {...} } ---- */
call    0 returned 2
        2: 5746:	  {
call    0 returned 2
        2: 5747:		   AV *restrict all_cols = get_all_columns(data_hoa, row_hashes, n);
call    0 returned 2
       2*: 5748:		   HV *restrict mean_hv  = newHV();
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5748-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 5748-block  1
call    4 returned 2
    %%%%%: 5748-block  2
       2*: 5749:		   HV *restrict size_hv  = newHV();
        2: 5749-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5749-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 5749-block  2
call    4 returned 2
    %%%%%: 5749-block  3
       2*: 5750:		   for (size_t c = 0; c <= (size_t)av_len(all_cols); c++) {
        2: 5750-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5750-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 5750-block  2
call    4 returned 2
    %%%%%: 5750-block  3
       2*: 5751:		       SV **restrict col_sv = av_fetch(all_cols, c, 0);
        2: 5751-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5751-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 5751-block  2
call    4 returned 2
    %%%%%: 5751-block  3
        2: 5751-block  4
        -: 5752:		       if (!col_sv || !SvOK(*col_sv)) continue;
    #####: 5753:		       const char *restrict col_name = SvPV_nolen(*col_sv);
    %%%%%: 5753-block  0
call    0 never executed
        -: 5754:		       double col_sum = 0.0;
        3: 5755:		       IV     col_count = 0;
        3: 5755-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        -: 5756:		       for (i = 0; i < n; i++) {
        3: 5757:		           double val = evaluate_term(data_hoa, row_hashes, i, col_name);
       3*: 5758:		           if (!isnan(val)) { col_sum += val; col_count++; }
        3: 5758-block  0
call    0 returned 3
branch  1 taken 0 (fallthrough)
branch  2 taken 3
    %%%%%: 5758-block  1
call    3 never executed
        3: 5759:		       }
        3: 5759-block  0
call    0 returned 3
        3: 5760:		       double col_mean = (col_count > 0) ? col_sum / col_count : NAN;
call    0 returned 3
       3*: 5761:		       hv_store(mean_hv, col_name, strlen(col_name), newSVnv(col_mean), 0);
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5761-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    %%%%%: 5761-block  1
call    4 never executed
        3: 5762:		       hv_store(size_hv, col_name, strlen(col_name), newSViv(col_count), 0);
        3: 5762-block  0
call    0 returned 3
call    1 returned 3
        3: 5763:		   }
call    0 returned 3
call    1 returned 3
        3: 5764:		   SvREFCNT_dec(all_cols);
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 5764-block  0
        1: 5764-block  1
        3: 5765:		   HV *restrict gs_hv = newHV();
        3: 5765-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 5765-block  1
        1: 5765-block  2
        3: 5766:		   hv_stores(gs_hv, "mean", newRV_noinc((SV*)mean_hv));
        3: 5766-block  0
call    0 returned 3
        3: 5767:		   hv_stores(gs_hv, "size", newRV_noinc((SV*)size_hv));
call    0 returned 3
        3: 5768:		   hv_stores(ret_hash, "group_stats", newRV_noinc((SV*)gs_hv));
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5768-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 5769:	  }
        3: 5769-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5769-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 5770:	  /* -------------------------------------------------------------- */
    %%%%%: 5770-block  0
call    0 never executed
        -: 5771:
        3: 5772:	  // Deep Cleanup
        3: 5773:	  for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
       3*: 5774:	  for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
        3: 5774-block  0
call    0 returned 3
branch  1 taken 3 (fallthrough)
branch  2 taken 0
        3: 5774-block  1
call    3 returned 3
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    %%%%%: 5774-block  2
call    6 never executed
        3: 5775:	  for (j = 0; j < p_exp; j++) {
        3: 5775-block  0
call    0 returned 3
        3: 5776:		   Safefree(exp_terms[j]); Safefree(parent_term[j]);
call    0 returned 3
        3: 5777:		   if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
call    0 returned 3
call    1 returned 3
        3: 5778:	  }
call    0 returned 3
call    1 returned 3
       3*: 5779:	  Safefree(exp_terms); Safefree(parent_term); 
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5779-block  0
        3: 5779-block  1
       3*: 5780:	  Safefree(is_dummy); Safefree(is_interact); 
        3: 5780-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5780-block  1
        3: 5780-block  2
        3: 5781:	  Safefree(dummy_base); Safefree(dummy_level);
        3: 5781-block  0
call    0 returned 3
        3: 5782:	  Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
call    0 returned 3
        3: 5783:	  Safefree(term_ss); Safefree(term_df);
call    0 returned 3
call    1 returned 3
        3: 5784:	  for (i = 0; i < n; i++) Safefree(X_mat[i]);
call    0 returned 3
call    1 returned 3
       3*: 5785:	  Safefree(X_mat); Safefree(Y);
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5785-block  0
    %%%%%: 5785-block  1
       3*: 5786:	  Safefree(aliased_qr); Safefree(rank_map);
        3: 5786-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5786-block  1
    %%%%%: 5786-block  2
       3*: 5787:	  if (row_hashes) Safefree(row_hashes);
        3: 5787-block  0
call    0 returned 3
branch  1 taken 3 (fallthrough)
branch  2 taken 0
        3: 5787-block  1
call    3 returned 3
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 5787-block  2
call    6 returned 3
call    7 returned 3
    %%%%%: 5787-block  3
       3*: 5788:	  RETVAL = newRV_noinc((SV*)ret_hash);
        3: 5788-block  0
call    0 returned 3
branch  1 taken 3 (fallthrough)
branch  2 taken 0
        3: 5788-block  1
call    3 returned 3
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 5788-block  2
call    6 returned 3
call    7 returned 3
    %%%%%: 5788-block  3
       3*: 5789:	}
        3: 5789-block  0
call    0 returned 3
branch  1 taken 3 (fallthrough)
branch  2 taken 0
        3: 5789-block  1
call    3 returned 3
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 5789-block  2
call    6 returned 3
call    7 returned 3
    %%%%%: 5789-block  3
       3*: 5790:OUTPUT:
        3: 5790-block  0
call    0 returned 3
branch  1 taken 3 (fallthrough)
branch  2 taken 0
        3: 5790-block  1
call    3 returned 3
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 5790-block  2
call    6 returned 3
call    7 returned 3
    %%%%%: 5790-block  3
        3: 5790-block  4
        -: 5791:    RETVAL
    #####: 5792:
    %%%%%: 5792-block  0
call    0 never executed
        -: 5793:PROTOTYPES: DISABLE
        -: 5794:
        -: 5795:SV* fisher_test(...)
        5: 5796:CODE:
        5: 5796-block  0
call    0 returned 5
        -: 5797:{
        5: 5798:	if (items < 1) croak("fisher_test requires at least a data reference");
call    0 returned 5
        -: 5799:
        -: 5800:	SV*restrict data_ref = ST(0);
        5: 5801:	double conf_level = 0.95;
call    0 returned 5
        5: 5802:	const char*restrict alternative = "two.sided";
call    0 returned 5
call    1 returned 5
        5: 5803:
call    0 returned 5
call    1 returned 5
        5: 5804:	// Parse named arguments
call    0 returned 5
        5: 5805:	for (unsigned short int i = 1; i < items; i += 2) {
call    0 returned 5
call    1 returned 5
        5: 5806:		if (i + 1 >= items) croak("fisher_test: odd number of arguments");
call    0 returned 5
call    1 returned 5
        5: 5807:		const char*restrict key = SvPV_nolen(ST(i));
call    0 returned 5
call    1 returned 5
        5: 5808:		SV*restrict val = ST(i + 1);
call    0 returned 5
        5: 5809:		if (strEQ(key, "conf_level") || strEQ(key, "conf.level")) {
call    0 returned 5
call    1 returned 5
        5: 5810:			conf_level = SvNV(val);
call    0 returned 5
call    1 returned 5
        5: 5811:		} else if (strEQ(key, "alternative")) {
call    0 returned 5
call    1 returned 5
        -: 5812:			alternative = SvPV_nolen(val);
        5: 5813:		}
call    0 returned 5
        -: 5814:	}
        -: 5815:
        -: 5816:	if (!SvROK(data_ref)) croak("fisher_test requires a reference to an Array or Hash");
        -: 5817:	SV*restrict deref = SvRV(data_ref);
        -: 5818:	size_t a = 0, b = 0, c = 0, d = 0;
        -: 5819:	// Extract Data
        -: 5820:	if (SvTYPE(deref) == SVt_PVAV) {
        7: 5821:	  AV*restrict outer = (AV*)deref;
        7: 5822:	  if (av_len(outer) != 1) croak("Outer array must have exactly 2 rows");
        7: 5823:	  SV**restrict row1_ptr = av_fetch(outer, 0, 0);
        7: 5824:	  SV**restrict row2_ptr = av_fetch(outer, 1, 0);
        7: 5825:	  if (row1_ptr && row2_ptr && SvROK(*row1_ptr) && SvROK(*row2_ptr)) {
        -: 5826:		   AV*restrict row1 = (AV*)SvRV(*row1_ptr);
        7: 5827:		   AV*restrict row2 = (AV*)SvRV(*row2_ptr);
        7: 5828:		   SV**restrict a_ptr = av_fetch(row1, 0, 0);
        7: 5829:		   SV**restrict b_ptr = av_fetch(row1, 1, 0);
        7: 5830:		   SV**restrict c_ptr = av_fetch(row2, 0, 0);
        -: 5831:		   SV**restrict d_ptr = av_fetch(row2, 1, 0);
       7*: 5832:		   a = (a_ptr && SvOK(*a_ptr)) ? SvIV(*a_ptr) : 0;
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 5832-block  0
call    2 never executed
       34: 5833:		   b = (b_ptr && SvOK(*b_ptr)) ? SvIV(*b_ptr) : 0;
        7: 5833-block  0
       27: 5833-block  1
       34: 5833-block  2
branch  0 taken 27
branch  1 taken 7 (fallthrough)
       27: 5834:		   c = (c_ptr && SvOK(*c_ptr)) ? SvIV(*c_ptr) : 0;
       27: 5834-block  0
call    0 returned 27
       27: 5835:		   d = (d_ptr && SvOK(*d_ptr)) ? SvIV(*d_ptr) : 0;
        -: 5836:	  } else {
       27: 5837:		  croak("Invalid 2D Array structure");
branch  0 taken 1 (fallthrough)
branch  1 taken 26
        1: 5837-block  0
       26: 5838:	  }
       26: 5838-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 19
        7: 5838-block  1
       19: 5839:	} else if (SvTYPE(deref) == SVt_PVHV) {
       19: 5839-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 12
        7: 5839-block  1
       12: 5840:		// Fixed 2D Hash Logic: Sort keys lexically to enforce structured rows/columns
       12: 5840-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 5840-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 11
        1: 5840-block  2
       11: 5841:		HV*restrict outer = (HV*)deref;
       11: 5841-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 5
        6: 5841-block  1
        5: 5842:		if (hv_iterinit(outer) != 2) croak("Outer hash must have exactly 2 keys");
        5: 5842-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 2
        3: 5842-block  1
call    2 returned 3
        2: 5843:		HE*restrict he1 = hv_iternext(outer);
        2: 5843-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5843-block  1
call    2 returned 2
    #####: 5844:		HE*restrict he2 = hv_iternext(outer);
    %%%%%: 5844-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5844-block  1
call    2 never executed
    #####: 5845:		if (!he1 || !he2) croak("Invalid outer hash");
    %%%%%: 5845-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5845-block  1
call    2 never executed
    #####: 5846:		const char*restrict k1 = SvPV_nolen(hv_iterkeysv(he1));
    %%%%%: 5846-block  0
call    0 never executed
        -: 5847:		const char*restrict k2 = SvPV_nolen(hv_iterkeysv(he2));
        -: 5848:		HE*restrict row1_he = (strcmp(k1, k2) < 0) ? he1 : he2;
        7: 5849:		HE*restrict row2_he = (strcmp(k1, k2) < 0) ? he2 : he1;
        7: 5849-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 5849-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 1
        6: 5849-block  2
        1: 5849-block  3
       7*: 5850:		SV*restrict row1_sv = hv_iterval(outer, row1_he);
        7: 5850-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 5850-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 7
    %%%%%: 5850-block  2
        7: 5850-block  3
        7: 5851:		SV*restrict row2_sv = hv_iterval(outer, row2_he);
        7: 5851-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 5851-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
        1: 5851-block  2
        6: 5851-block  3
       7*: 5852:		if (!SvROK(row1_sv) || SvTYPE(SvRV(row1_sv)) != SVt_PVHV ||
        7: 5852-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 5852-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 7
    %%%%%: 5852-block  2
        7: 5852-block  3
       7*: 5853:			!SvROK(row2_sv) || SvTYPE(SvRV(row2_sv)) != SVt_PVHV) {
        7: 5853-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 5853-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 1
    %%%%%: 5853-block  2
        7: 5853-block  3
        -: 5854:			croak("Inner elements must be hashes");
        7: 5855:		}
        7: 5856:		HV*restrict in1 = (HV*)SvRV(row1_sv);
        7: 5856-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 5856-block  1
       7*: 5857:		HV*restrict in2 = (HV*)SvRV(row2_sv);
        7: 5857-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 5857-block  1
        7: 5858:		if (hv_iterinit(in1) != 2 || hv_iterinit(in2) != 2) croak("Inner hashes must have exactly 2 keys");
        7: 5858-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 5858-block  1
       7*: 5859:		HE*restrict in1_he1 = hv_iternext(in1);
        7: 5859-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 5859-block  1
       7*: 5860:		HE*restrict in1_he2 = hv_iternext(in1);
        7: 5860-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    %%%%%: 5860-block  1
        -: 5861:		const char*restrict in1_k1 = SvPV_nolen(hv_iterkeysv(in1_he1));
        7: 5862:		const char*restrict in1_k2 = SvPV_nolen(hv_iterkeysv(in1_he2));
        7: 5862-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 7
    #####: 5863:		HE*restrict in1_c1 = (strcmp(in1_k1, in1_k2) < 0) ? in1_he1 : in1_he2;
    %%%%%: 5863-block  0
call    0 never executed
        -: 5864:		HE*restrict in1_c2 = (strcmp(in1_k1, in1_k2) < 0) ? in1_he2 : in1_he1;
        -: 5865:		HE*restrict in2_he1 = hv_iternext(in2);
        7: 5866:		HE*restrict in2_he2 = hv_iternext(in2);
        7: 5866-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 5866-block  1
call    2 returned 1
        6: 5866-block  2
       7*: 5867:		const char*restrict in2_k1 = SvPV_nolen(hv_iterkeysv(in2_he1));
        7: 5867-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 5867-block  1
call    2 returned 7
    %%%%%: 5867-block  2
       7*: 5868:		const char*restrict in2_k2 = SvPV_nolen(hv_iterkeysv(in2_he2));
        7: 5868-block  0
branch  0 taken 7 (fallthrough)
branch  1 taken 0
        7: 5868-block  1
branch  2 taken 7 (fallthrough)
branch  3 taken 0
        7: 5868-block  2
call    4 returned 7
    %%%%%: 5868-block  3
        7: 5869:		HE*restrict in2_c1 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he1 : in2_he2;
        7: 5869-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 5869-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 5869-block  2
call    4 returned 1
        6: 5869-block  3
        7: 5870:		HE*restrict in2_c2 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he2 : in2_he1;
        7: 5870-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 5870-block  1
call    2 returned 6
        1: 5870-block  2
        7: 5871:		a = (hv_iterval(in1, in1_c1) && SvOK(hv_iterval(in1, in1_c1))) ? SvIV(hv_iterval(in1, in1_c1)) : 0;
        7: 5871-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 5871-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 5
        2: 5871-block  2
        5: 5871-block  3
        7: 5872:		b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
        7: 5872-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 5872-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 5872-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 6
        1: 5872-block  3
        6: 5872-block  4
        7: 5873:		c = (hv_iterval(in2, in2_c1) && SvOK(hv_iterval(in2, in2_c1))) ? SvIV(hv_iterval(in2, in2_c1)) : 0;
        7: 5873-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 5873-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 5873-block  2
        7: 5874:		d = (hv_iterval(in2, in2_c2) && SvOK(hv_iterval(in2, in2_c2))) ? SvIV(hv_iterval(in2, in2_c2)) : 0;
        7: 5874-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 6
        1: 5875:	} else {
        1: 5875-block  0
call    0 returned 1
        6: 5876:	  croak("Input must be a 2D Array or 2D Hash");
        6: 5876-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5877:	}
       6*: 5878:
        6: 5878-block  0
    %%%%%: 5878-block  1
        6: 5878-block  2
call    0 returned 6
branch  1 taken 0 (fallthrough)
branch  2 taken 6
    %%%%%: 5878-block  3
branch  3 never executed
branch  4 never executed (fallthrough)
      228: 5879:	// Perform Calculations via Helpers
        6: 5879-block  0
      228: 5879-block  1
branch  0 taken 222
branch  1 taken 6 (fallthrough)
      222: 5880:	double p_val = exact_p_value(a, b, c, d, alternative);
      222: 5881:	double mle_or, ci_low, ci_high;
      222: 5881-block  0
call    0 returned 222
branch  1 taken 49 (fallthrough)
branch  2 taken 173
       49: 5881-block  1
      173: 5882:	calculate_exact_stats(a, b, c, d, conf_level, alternative, &mle_or, &ci_low, &ci_high);
      173: 5882-block  0
        -: 5883:
        6: 5884:	// Construct the Return HashRef purely in C
        6: 5884-block  0
    #####: 5885:	HV*restrict ret_hash = newHV();
    %%%%%: 5885-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5886:	hv_stores(ret_hash, "method", newSVpv("Fisher's Exact Test for Count Data", 0));
    #####: 5887:	hv_stores(ret_hash, "alternative", newSVpv(alternative, 0));
    %%%%%: 5887-block  0
    %%%%%: 5887-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 5888:	AV*restrict ci_array = newAV();
    #####: 5889:	av_push(ci_array, newSVnv(ci_low));
    %%%%%: 5889-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5889-block  1
    #####: 5890:	av_push(ci_array, newSVnv(ci_high));
    %%%%%: 5890-block  0
        -: 5891:	hv_stores(ret_hash, "conf_int", newRV_noinc((SV*)ci_array));
    #####: 5892:	HV*restrict est_hash = newHV();
    %%%%%: 5892-block  0
    #####: 5893:	hv_stores(ret_hash, "estimate", newRV_noinc((SV*)est_hash));
    %%%%%: 5893-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5894:	hv_stores(est_hash, "odds ratio", newSVnv(mle_or));
    #####: 5895:	hv_stores(ret_hash, "p_value", newSVnv(p_val));
    %%%%%: 5895-block  0
    %%%%%: 5895-block  1
    %%%%%: 5895-block  2
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5895-block  3
branch  3 never executed
branch  4 never executed (fallthrough)
    #####: 5896:	// Return the HashRef
    %%%%%: 5896-block  0
    %%%%%: 5896-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 5897:	RETVAL = newRV_noinc((SV*)ret_hash);
    #####: 5898:}
    %%%%%: 5898-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5898-block  1
    #####: 5899:OUTPUT:
    %%%%%: 5899-block  0
        -: 5900:  RETVAL
    #####: 5901:
    %%%%%: 5901-block  0
    #####: 5902:SV* power_t_test(...)
    %%%%%: 5902-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5903:CODE:
    #####: 5904:{
    %%%%%: 5904-block  0
    %%%%%: 5904-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 5905:	SV*restrict sv_n = NULL;
    #####: 5906:	SV*restrict sv_delta = NULL;
    %%%%%: 5906-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5906-block  1
    #####: 5907:	SV*restrict sv_sd = NULL;
    %%%%%: 5907-block  0
        -: 5908:	SV*restrict sv_sig_level = NULL;
    #####: 5909:	SV*restrict sv_power = NULL;
    %%%%%: 5909-block  0
        -: 5910:
        7: 5911:	const char* restrict type = "two.sample";
        7: 5911-block  0
call    0 returned 7
        7: 5912:	const char* restrict alternative = "two.sided";
call    0 returned 7
call    1 returned 7
        7: 5913:	bool strict = FALSE;
call    0 returned 7
call    1 returned 7
        7: 5914:	double tol = pow(2.2204460492503131e-16, 0.25); 
call    0 returned 7
call    1 returned 7
        7: 5915:
call    0 returned 7
call    1 returned 7
        7: 5916:	if (items % 2 != 0) croak("Usage: power_t_test(n => 30, delta => 0.5, sd => 1.0, ...)");
call    0 returned 7
call    1 returned 7
        7: 5917:	for (unsigned short int i = 0; i < items; i += 2) {
call    0 returned 7
call    1 returned 7
        7: 5918:	  const char* restrict key = SvPV_nolen(ST(i));
branch  0 taken 2 (fallthrough)
branch  1 taken 5
        2: 5918-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 1
        1: 5918-block  1
        1: 5918-block  2
        2: 5918-block  3
        5: 5918-block  4
        7: 5919:	  SV* restrict val = ST(i+1);
        7: 5919-block  0
call    0 returned 7
call    1 returned 7
        7: 5920:
branch  0 taken 2 (fallthrough)
branch  1 taken 5
        2: 5920-block  0
branch  2 taken 1 (fallthrough)
branch  3 taken 1
        1: 5920-block  1
        1: 5920-block  2
        2: 5920-block  3
        5: 5920-block  4
        7: 5921:	  if      (strEQ(key, "n"))           sv_n = val;
        7: 5921-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 1
        6: 5921-block  1
call    2 returned 6
call    3 returned 6
        7: 5922:	  else if (strEQ(key, "delta"))       sv_delta = val;
        7: 5922-block  0
call    0 returned 7
        -: 5923:	  else if (strEQ(key, "sd"))          sv_sd = val;
        -: 5924:	  else if (strEQ(key, "sig.level") || strEQ(key, "sig_level")) sv_sig_level = val;
        -: 5925:	  else if (strEQ(key, "power"))       sv_power = val;
        -: 5926:	  else if (strEQ(key, "type"))        type = SvPV_nolen(val);
        -: 5927:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 5928:	  else if (strEQ(key, "strict"))      strict = SvTRUE(val);
        -: 5929:	  else if (strEQ(key, "tol"))         tol = SvNV(val);
        3: 5930:	  else croak("power_t_test: unknown argument '%s'", key);
        3: 5931:	}
        -: 5932:
        -: 5933:	bool is_null_n = (!sv_n || !SvOK(sv_n));
        -: 5934:	bool is_null_delta = (!sv_delta || !SvOK(sv_delta));
        3: 5935:	bool is_null_power = (!sv_power || !SvOK(sv_power));
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5935-block  0
branch  2 taken 2 (fallthrough)
branch  3 taken 1
        2: 5936:	bool is_null_sd = (sv_sd && !SvOK(sv_sd)); 
        2: 5937:	bool is_null_sig_level = (sv_sig_level && !SvOK(sv_sig_level));
        2: 5937-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 5938:
        1: 5938-block  0
        1: 5939:	unsigned int missing_count = 0;
        1: 5939-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 5940:	if (is_null_n) missing_count++;
        1: 5940-block  0
        -: 5941:	if (is_null_delta) missing_count++;
        -: 5942:	if (is_null_power) missing_count++;
        3: 5943:	if (is_null_sd) missing_count++;
        3: 5943-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 1
        2: 5943-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 5944:	if (is_null_sig_level) missing_count++;
        2: 5944-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 5945:
        1: 5945-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 5946:	if (missing_count != 1) {
        1: 5946-block  0
        -: 5947:	  croak("power_t_test: exactly one of 'n', 'delta', 'sd', 'power', and 'sig_level' must be undef/NULL");
        -: 5948:	}
        5: 5949:
        3: 5949-block  0
        2: 5949-block  1
        5: 5949-block  2
branch  0 taken 2
branch  1 taken 3 (fallthrough)
        2: 5950:	double n = is_null_n ? 0.0 : SvNV(sv_n);
        2: 5950-block  0
call    0 returned 2
        2: 5951:	double delta = is_null_delta ? 0.0 : SvNV(sv_delta);
        2: 5952:	double sd = (!sv_sd || is_null_sd) ? 1.0 : SvNV(sv_sd);
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 5952-block  0
        1: 5953:	double sig_level = (!sv_sig_level || is_null_sig_level) ? 0.05 : SvNV(sv_sig_level);
        1: 5953-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 5953-block  1
    #####: 5954:	double power = is_null_power ? 0.0 : SvNV(sv_power);
    %%%%%: 5954-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5954-block  1
    #####: 5955:	short int tsample = (strEQ(type, "one.sample") || strEQ(type, "paired")) ? 1 : 2;
    %%%%%: 5955-block  0
call    0 never executed
        -: 5956:	short int tside = (strEQ(alternative, "one.sided") || strEQ(alternative, "greater") || strEQ(alternative, "less")) ? 1 : 2;
        -: 5957:	if (tside == 2 && !is_null_delta) delta = fabs(delta);
        3: 5958:	if (is_null_power) {
        3: 5958-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 5958-block  1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        1: 5958-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 1
    #####: 5959:	  power = p_body(n, delta, sd, sig_level, tsample, tside, strict);
    %%%%%: 5959-block  0
call    0 never executed
        -: 5960:	} else if (is_null_n) {
        -: 5961:	  double low = 2.0, high = 1e7;
        -: 5962:	  while (p_body(high, delta, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
        -: 5963:	  while (high - low > tol) {
        3: 5964:		   double mid = low + (high - low) / 2.0;
        3: 5965:		   if (p_body(mid, delta, sd, sig_level, tsample, tside, strict) < power) low = mid;
        3: 5966:		   else high = mid;
        3: 5967:	  }
        -: 5968:	  n = low + (high - low) / 2.0;
        -: 5969:	} else if (is_null_sd) {
        -: 5970:	  double low = delta * 1e-7, high = delta * 1e7;
        -: 5971:	  while (high - low > tol) {
        -: 5972:		   double mid = low + (high - low) / 2.0;
        3: 5973:		   if (p_body(n, delta, mid, sig_level, tsample, tside, strict) > power) low = mid;
        3: 5973-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 2
        1: 5974:		   else high = mid;
        1: 5974-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 0
        1: 5974-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 1
    #####: 5975:	  }
    %%%%%: 5975-block  0
call    0 never executed
        1: 5976:	  sd = low + (high - low) / 2.0;
        -: 5977:	} else if (is_null_delta) {
        -: 5978:	  double low = sd * 1e-7, high = sd * 1e7;
        1: 5979:	  while (p_body(n, high, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
        1: 5980:	  while (high - low > tol) {
        1: 5980-block  0
call    0 returned 1
        -: 5981:		   double mid = low + (high - low) / 2.0;
        4: 5982:		   if (p_body(n, mid, sd, sig_level, tsample, tside, strict) < power) low = mid;
        4: 5982-block  0
call    0 returned 4
branch  1 taken 3
branch  2 taken 1 (fallthrough)
        3: 5983:		   else high = mid;
        3: 5984:	  }
        3: 5984-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5984-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 5985:	  delta = low + (high - low) / 2.0;
    %%%%%: 5985-block  0
call    0 never executed
        3: 5986:	} else if (is_null_sig_level) {
        3: 5986-block  0
call    0 returned 3
        -: 5987:	  double low = 1e-10, high = 1.0 - 1e-10;
       1*: 5988:	  while (high - low > tol) {
        1: 5988-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 5988-block  1
call    2 never executed
        -: 5989:		   double mid = low + (high - low) / 2.0;
        1: 5990:		   if (p_body(n, delta, sd, mid, tsample, tside, strict) < power) low = mid;
        1: 5990-block  0
call    0 returned 1
        -: 5991:		   else high = mid;
       1*: 5992:	  }
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 5992-block  0
call    2 never executed
        1: 5992-block  1
        1: 5993:	  sig_level = low + (high - low) / 2.0;
        1: 5993-block  0
call    0 returned 1
        -: 5994:	}
        -: 5995:	HV*restrict ret = newHV();
        1: 5996:	hv_stores(ret, "n", newSVnv(n));
        1: 5997:	hv_stores(ret, "delta", newSVnv(delta));
call    0 returned 1
        4: 5998:	hv_stores(ret, "sd", newSVnv(sd));
        4: 5998-block  0
call    0 returned 4
branch  1 taken 3
branch  2 taken 1 (fallthrough)
        -: 5999:	hv_stores(ret, "sig.level", newSVnv(sig_level));
       3*: 6000:	hv_stores(ret, "power", newSVnv(power));
        3: 6000-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 6000-block  1
call    2 never executed
        3: 6000-block  2
        3: 6001:	hv_stores(ret, "alternative", newSVpv(alternative, 0));
        3: 6001-block  0
call    0 returned 3
        -: 6002:	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";
        3: 6003:	hv_stores(ret, "method", newSVpv(m_str, 0));
        3: 6004:	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" : "");
call    0 returned 3
       17: 6005:	if (n_str[0] != '\0') hv_stores(ret, "note", newSVpv(n_str, 0));
       14: 6005-block  0
       17: 6005-block  1
branch  0 taken 14
branch  1 taken 3 (fallthrough)
       14: 6006:	RETVAL = newRV_noinc((SV*)ret);
       14: 6006-block  0
call    0 returned 14
       14: 6007:}
branch  0 taken 14 (fallthrough)
branch  1 taken 0
       14: 6007-block  0
branch  2 taken 14 (fallthrough)
branch  3 taken 0
       14: 6007-block  1
call    4 returned 14
branch  5 taken 14 (fallthrough)
branch  6 taken 0
       14: 6008:OUTPUT:
       14: 6008-block  0
call    0 returned 14
       14: 6009:    RETVAL
       14: 6010:
        -: 6011:SV* kruskal_test(...)
        -: 6012:CODE:
        3: 6013:{
        3: 6013-block  0
        -: 6014:	SV *restrict x_sv = NULL, *restrict g_sv = NULL, *restrict h_sv = NULL;
        1: 6015:	unsigned int arg_idx = 0;
        1: 6015-block  0
        -: 6016:
        -: 6017:	// 1. Shift positional arguments
        -: 6018:	//    Accept either: (arrayref, arrayref) or (hashref)
        -: 6019:	if (arg_idx < items && SvROK(ST(arg_idx))) {
        -: 6020:	  svtype t = SvTYPE(SvRV(ST(arg_idx)));
        2: 6021:	  if (t == SVt_PVAV) {
        2: 6021-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 6021-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 6021-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 2
    #####: 6022:		   x_sv = ST(arg_idx++);
    %%%%%: 6022-block  0
call    0 never executed
        2: 6023:	  } else if (t == SVt_PVHV) {
        2: 6023-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 6023-block  1
branch  2 taken 2 (fallthrough)
branch  3 taken 0
        2: 6023-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 2
    #####: 6024:		   h_sv = ST(arg_idx++);          /* hash-of-arrays shortcut */
    %%%%%: 6024-block  0
call    0 never executed
        -: 6025:	  }
        2: 6026:	}
        2: 6027:	if (!h_sv && arg_idx < items
        2: 6028:		     && SvROK(ST(arg_idx))
        2: 6028-block  0
call    0 returned 2
        2: 6029:		     && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
call    0 returned 2
       2*: 6030:	  g_sv = ST(arg_idx++);
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 6030-block  0
call    2 never executed
       2*: 6031:	}
        2: 6031-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 6031-block  1
call    2 never executed
        -: 6032:	// 2. Parse named arguments (fallback)
        2: 6033:	for (; arg_idx < items; arg_idx += 2) {
        2: 6033-block  0
call    0 returned 2
        2: 6034:	  const char *restrict key = SvPV_nolen(ST(arg_idx));
call    0 returned 2
        -: 6035:	  SV         *restrict val = ST(arg_idx + 1);
        -: 6036:	  if      (strEQ(key, "x")) x_sv = val;
        2: 6037:	  else if (strEQ(key, "g")) g_sv = val;
call    0 returned 2
        2: 6038:	  else if (strEQ(key, "h")) h_sv = val;
        -: 6039:	  else croak("kruskal_test: unknown argument '%s'", key);
       30: 6040:	}
       28: 6040-block  0
       30: 6040-block  1
branch  0 taken 28
branch  1 taken 2 (fallthrough)
       28: 6041:	// 3. Mutual-exclusion guard
       28: 6041-block  0
call    0 returned 28
       28: 6042:	if (h_sv && (x_sv || g_sv))
call    0 returned 28
       28: 6043:	  croak("kruskal_test: cannot mix 'h' (hash-of-arrays) with 'x'/'g' inputs");
branch  0 taken 28 (fallthrough)
branch  1 taken 0
       28: 6043-block  0
branch  2 taken 28 (fallthrough)
branch  3 taken 0
       28: 6043-block  1
call    4 returned 28
branch  5 taken 28 (fallthrough)
branch  6 taken 0
       28: 6044:
       28: 6044-block  0
branch  0 taken 28 (fallthrough)
branch  1 taken 0
       28: 6044-block  1
branch  2 taken 28 (fallthrough)
branch  3 taken 0
       28: 6045:	/* ------------------------------------------------------------------ */
       28: 6045-block  0
call    0 returned 28
       28: 6046:	/* Shared state filled by whichever input branch runs                 */
       28: 6047:	/* ------------------------------------------------------------------ */
call    0 returned 28
        -: 6048:	RankInfo *restrict ri = NULL;
       28: 6049:	char **restrict group_names = NULL; /* Track names to build group_stats */
branch  0 taken 22 (fallthrough)
branch  1 taken 6
       22: 6050:	size_t valid_n = 0;
       22: 6050-block  0
call    0 returned 22
        -: 6051:	size_t k       = 0;
        6: 6052:
        6: 6053:	/* ------------------------------------------------------------------ */
        6: 6053-block  0
call    0 returned 6
call    1 returned 6
        6: 6054:	/* 4a. Hash-of-arrays input path                                      */
call    0 returned 6
        -: 6055:	/*     my %x = ( group1 => [...], group2 => [...], ... )              */
       28: 6056:	/* ------------------------------------------------------------------ */
       28: 6056-block  0
call    0 returned 28
       28: 6057:	if (h_sv) {
       28: 6058:	  if (!SvROK(h_sv) || SvTYPE(SvRV(h_sv)) != SVt_PVHV)
        -: 6059:		   croak("kruskal_test: 'h' must be a HASH reference");
        -: 6060:	  HV *restrict h_hv = (HV*)SvRV(h_sv);
        2: 6061:	  // First pass – validate values and tally total elements
        2: 6062:	  size_t total = 0;
        2: 6062-block  0
call    0 returned 2
        -: 6063:	  hv_iterinit(h_hv);
        -: 6064:	  HE *restrict he;
        -: 6065:	  while ((he = hv_iternext(h_hv))) {
        -: 6066:		   SV *restrict val = HeVAL(he);
        -: 6067:		   if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVAV)
        3: 6068:		       croak("kruskal_test: every value in 'h' must be an ARRAY reference");
        3: 6068-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 6068-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 6069:		   total += (size_t)(av_len((AV*)SvRV(val)) + 1);
    %%%%%: 6069-block  0
call    0 never executed
    #####: 6070:	  }
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 6071:	  if (total < 2) croak("not enough observations");
    %%%%%: 6071-block  0
    %%%%%: 6071-block  1
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 6071-block  2
call    2 never executed
    %%%%%: 6071-block  3
    %%%%%: 6071-block  4
branch  3 never executed
branch  4 never executed (fallthrough)
    #####: 6072:
    %%%%%: 6072-block  0
call    0 never executed
        -: 6073:	  ri = (RankInfo *)safemalloc(total * sizeof(RankInfo));
    #####: 6074:	  size_t num_keys = HvKEYS(h_hv);
    %%%%%: 6074-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 6074-block  1
call    2 never executed
    #####: 6075:	  group_names = (char **)safecalloc(num_keys, sizeof(char*));
    %%%%%: 6075-block  0
call    0 never executed
        -: 6076:	  /* Second pass – fill ri[], assigning one group_id per hash key */
        -: 6077:	  size_t group_id = 0;
        -: 6078:	  hv_iterinit(h_hv);
        3: 6079:	  while ((he = hv_iternext(h_hv))) {
        3: 6080:		   STRLEN klen;
        3: 6080-block  0
call    0 returned 3
        -: 6081:		   const char *restrict key_str = HePV(he, klen);
        -: 6082:		   group_names[group_id] = savepvn(key_str, klen); // Save string key
        3: 6083:
call    0 returned 3
        3: 6084:		   AV *restrict av  = (AV*)SvRV(HeVAL(he));
call    0 returned 3
        3: 6085:		   size_t       n_g = (size_t)(av_len(av) + 1);
call    0 returned 3
        -: 6086:		   for (size_t i = 0; i < n_g; i++) {
       45: 6087:		       SV **restrict el = av_fetch(av, i, 0);
       45: 6087-block  0
branch  0 taken 42
branch  1 taken 3 (fallthrough)
       42: 6088:		       if (el && SvOK(*el) && looks_like_number(*el)) {
       42: 6089:		           ri[valid_n].val = SvNV(*el);
       42: 6090:		           ri[valid_n].idx = group_id;   /* group identity */
       42: 6091:		           valid_n++;
       42: 6091-block  0
        -: 6092:		       }
        -: 6093:		   }
        -: 6094:		   group_id++;
        3: 6095:	  }
       12: 6096:	  k = group_id;   /* number of unique groups = number of hash keys */
        3: 6096-block  0
        9: 6096-block  1
       12: 6096-block  2
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 6097:
        9: 6097-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 6098:	/* ------------------------------------------------------------------ */
        9: 6099:	/* 4b. Original x / g array-pair input path                           */
        9: 6099-block  0
        -: 6100:	/* ------------------------------------------------------------------ */
        -: 6101:	} else {
        3: 6102:	  if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        3: 6103:		   croak("kruskal_test: 'x' is a required argument and must be an ARRAY reference");
        3: 6104:	  if (!g_sv || !SvROK(g_sv) || SvTYPE(SvRV(g_sv)) != SVt_PVAV)
        3: 6104-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 6105:		   croak("kruskal_test: 'g' is a required argument and must be an ARRAY reference");
    #####: 6106:
    %%%%%: 6106-block  0
        -: 6107:	  AV *restrict x_av = (AV*)SvRV(x_sv);
        3: 6108:	  AV *restrict g_av = (AV*)SvRV(g_sv);
        3: 6109:	  size_t nx = (size_t)(av_len(x_av) + 1);
        3: 6109-block  0
call    0 returned 3
        -: 6110:	  size_t ng = (size_t)(av_len(g_av) + 1);
        -: 6111:	  if (nx != ng) croak("kruskal_test: 'x' and 'g' must have the same length");
        3: 6112:	  if (nx < 2)   croak("not enough observations");
call    0 returned 3
        3: 6113:
call    0 returned 3
call    1 returned 3
        3: 6114:	  ri = (RankInfo *)safemalloc(nx * sizeof(RankInfo));
call    0 returned 3
call    1 returned 3
        3: 6115:	  group_names = (char **)safecalloc(nx, sizeof(char*)); // Upper bound
call    0 returned 3
call    1 returned 3
        3: 6116:
call    0 returned 3
call    1 returned 3
        3: 6117:	  // Map string group names → contiguous integer IDs
call    0 returned 3
call    1 returned 3
        -: 6118:	  HV *restrict group_map    = newHV();
        -: 6119:	  size_t          next_group_id = 0;
        3: 6120:
call    0 returned 3
        3: 6121:	  for (size_t i = 0; i < nx; i++) {
call    0 returned 3
        3: 6122:		   SV **restrict x_el = av_fetch(x_av, i, 0);
call    0 returned 3
        -: 6123:		   SV **restrict g_el = av_fetch(g_av, i, 0);
       12: 6124:		   if (x_el && SvOK(*x_el) && looks_like_number(*x_el)
        9: 6124-block  0
       12: 6124-block  1
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 6125:		            && g_el && SvOK(*g_el)) {
        9: 6125-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 6125-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 6126:		       const char *restrict g_str = SvPV_nolen(*g_el);
        9: 6127:		       STRLEN               glen  = strlen(g_str);
        -: 6128:		       SV   **restrict id_sv = hv_fetch(group_map, g_str, glen, 0);
        9: 6129:		       size_t group_id;
        9: 6129-block  0
call    0 returned 9
call    1 returned 9
        9: 6130:		       if (id_sv) {
call    0 returned 9
call    1 returned 9
        -: 6131:		           group_id = SvUV(*id_sv);
        9: 6132:		       } else {
        9: 6132-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 6132-block  1
call    2 returned 9
        -: 6133:		           group_id = next_group_id++;
        -: 6134:		           hv_store(group_map, g_str, glen, newSVuv(group_id), 0);
        -: 6135:		           group_names[group_id] = savepvn(g_str, glen); // Save string key
        3: 6136:		       }
        3: 6136-block  0
call    0 returned 3
call    1 returned 3
        3: 6137:		       ri[valid_n].val = SvNV(*x_el);
call    0 returned 3
call    1 returned 3
        3: 6138:		       ri[valid_n].idx = group_id;
call    0 returned 3
call    1 returned 3
        -: 6139:		       valid_n++;
        -: 6140:		   }
        3: 6141:	  }
call    0 returned 3
        3: 6142:	  k = next_group_id;
call    0 returned 3
        3: 6143:	  SvREFCNT_dec(group_map);
call    0 returned 3
        3: 6144:	}
call    0 returned 3
        3: 6145:
call    0 returned 3
        -: 6146:	/* ------------------------------------------------------------------ */
        3: 6147:	/* 5. Shared post-extraction validation                               */
call    0 returned 3
        -: 6148:	/* ------------------------------------------------------------------ */
        -: 6149:	if (valid_n < 2 || k < 2) { 
        -: 6150:	  Safefree(ri); 
        -: 6151:	  if (group_names) {
        -: 6152:		   for (size_t i = 0; i < k; i++) { if (group_names[i]) Safefree(group_names[i]); }
        -: 6153:		   Safefree(group_names);
        -: 6154:	  }
        6: 6155:	  if (valid_n < 2) croak("not enough observations");
        6: 6156:	  croak("all observations are in the same group");
        6: 6157:	}
        6: 6158:
        6: 6159:	// 6. Ranking and Tie Accumulation (Reusing LikeR Helper)
        -: 6160:	bool   has_ties = 0;
        -: 6161:	double tie_adj  = rank_and_count_ties(ri, valid_n, &has_ties);
        6: 6162:
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 6162-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 6162-block  1
branch  4 taken 6 (fallthrough)
branch  5 taken 0
        6: 6163:	// 7. Aggregate Sum of Ranks AND Actual Values by Group
        6: 6164:	double *restrict group_rank_sums = (double *)safecalloc(k, sizeof(double));
        6: 6164-block  0
        -: 6165:	double *restrict group_val_sums  = (double *)safecalloc(k, sizeof(double)); // For Mean
        -: 6166:	size_t *restrict group_counts    = (size_t *)safecalloc(k, sizeof(size_t));
        -: 6167:
        6: 6168:	for (size_t i = 0; i < valid_n; i++) {
        6: 6168-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 6168-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 6168-block  2
branch  4 taken 6 (fallthrough)
branch  5 taken 0
        6: 6169:	  size_t g_id = ri[i].idx;
        6: 6170:	  group_rank_sums[g_id] += ri[i].rank;
        6: 6170-block  0
        -: 6171:	  group_val_sums[g_id]  += ri[i].val;
        -: 6172:	  group_counts[g_id]++;
        -: 6173:	}
        6: 6174:
        6: 6174-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 6175:	// 8. Calculate STATISTIC
    %%%%%: 6175-block  0
call    0 never executed
        -: 6176:	double stat_base = 0.0;
        -: 6177:	for (size_t i = 0; i < k; i++) {
        -: 6178:	  if (group_counts[i] > 0)
        8: 6179:		   stat_base += (group_rank_sums[i] * group_rank_sums[i])
        6: 6179-block  0
        2: 6179-block  1
        8: 6179-block  2
branch  0 taken 2
branch  1 taken 6 (fallthrough)
        2: 6180:		                / (double)group_counts[i];
        2: 6180-block  0
call    0 returned 2
        2: 6181:	}
        -: 6182:
       2*: 6183:	double n_d  = (double)valid_n;
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 6183-block  0
       2*: 6184:	double stat = (12.0 * stat_base / (n_d * (n_d + 1.0))) - 3.0 * (n_d + 1.0);
        2: 6184-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 2
    %%%%%: 6184-block  1
        2: 6185:	if (tie_adj > 0.0) {
        2: 6185-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 1
        1: 6185-block  1
call    2 returned 1
       1*: 6186:	  double tie_denom = 1.0 - (tie_adj / (n_d * n_d * n_d - n_d));
        1: 6186-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1
    %%%%%: 6186-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
        1: 6186-block  2
call    4 returned 1
    #####: 6187:	  stat /= tie_denom;
    %%%%%: 6187-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 6187-block  1
call    2 never executed
    #####: 6188:	}
    %%%%%: 6188-block  0
call    0 never executed
        -: 6189:	int    df    = (int)k - 1;
        -: 6190:	double p_val = get_p_value(stat, df);
        -: 6191:
        6: 6192:	// 9. Return structured data exactly like R's htest
        6: 6192-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 6192-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 6192-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 6
    #####: 6193:	HV *restrict res = newHV();
    %%%%%: 6193-block  0
call    0 never executed
        6: 6194:	hv_stores(res, "statistic", newSVnv(stat));
        6: 6194-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 6194-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 6194-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 6
    #####: 6195:	hv_stores(res, "parameter", newSViv(df));
    %%%%%: 6195-block  0
call    0 never executed
        -: 6196:	hv_stores(res, "p_value",   newSVnv(p_val));
        6: 6197:	hv_stores(res, "p.value",   newSVnv(p_val));
        6: 6197-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 6197-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    #####: 6198:	hv_stores(res, "method",    newSVpv("Kruskal-Wallis rank sum test", 0));
    %%%%%: 6198-block  0
call    0 never executed
        6: 6199:
        6: 6199-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 6199-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 6199-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 6
    #####: 6200:	// 10. Build the group_stats hash
    %%%%%: 6200-block  0
call    0 never executed
        -: 6201:	HV *restrict group_stats = newHV();
        6: 6202:	HV *restrict stats_mean  = newHV();
        6: 6203:	HV *restrict stats_size  = newHV();
        6: 6204:
        6: 6204-block  0
call    0 returned 6
        6: 6205:	for (size_t i = 0; i < k; i++) {
call    0 returned 6
        -: 6206:	  if (group_counts[i] > 0 && group_names[i]) {
        -: 6207:		   double mean = group_val_sums[i] / (double)group_counts[i];
        6: 6208:		   size_t nlen = strlen(group_names[i]);
        6: 6209:		   
       32: 6210:		   hv_store(stats_mean, group_names[i], nlen, newSVnv(mean), 0);
       26: 6210-block  0
       32: 6210-block  1
branch  0 taken 26
branch  1 taken 6 (fallthrough)
       26: 6211:		   hv_store(stats_size, group_names[i], nlen, newSVuv(group_counts[i]), 0);
       26: 6211-block  0
call    0 returned 26
       26: 6212:	  }
branch  0 taken 26 (fallthrough)
branch  1 taken 0
       26: 6212-block  0
branch  2 taken 26 (fallthrough)
branch  3 taken 0
       26: 6212-block  1
call    4 returned 26
branch  5 taken 26 (fallthrough)
branch  6 taken 0
       26: 6213:	  if (group_names[i]) Safefree(group_names[i]); // Clean up name copy
       26: 6213-block  0
call    0 returned 26
       26: 6214:	}
branch  0 taken 26 (fallthrough)
branch  1 taken 0
       26: 6214-block  0
branch  2 taken 26 (fallthrough)
branch  3 taken 0
       26: 6215:
       26: 6216:	// Embed the nested hashes
       26: 6217:	hv_stores(group_stats, "mean", newRV_noinc((SV*)stats_mean));
       26: 6218:	hv_stores(group_stats, "size", newRV_noinc((SV*)stats_size));
       26: 6218-block  0
        -: 6219:	hv_stores(res, "group_stats",  newRV_noinc((SV*)group_stats));
        -: 6220:
        -: 6221:	// Memory Cleanup
        -: 6222:	Safefree(group_names);    Safefree(group_rank_sums); 
        6: 6223:	Safefree(group_val_sums); Safefree(group_counts); 
        6: 6224:	Safefree(ri);
       27: 6225:
        6: 6225-block  0
       21: 6225-block  1
       27: 6225-block  2
branch  0 taken 21
branch  1 taken 6 (fallthrough)
       21: 6226:	RETVAL = newRV_noinc((SV*)res);
       21: 6226-block  0
call    0 returned 21
       21: 6227:}
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 6227-block  0
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 6227-block  1
call    4 returned 21
branch  5 taken 21 (fallthrough)
branch  6 taken 0
       21: 6228:OUTPUT:
       21: 6228-block  0
call    0 returned 21
       21: 6229:    RETVAL
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 6229-block  0
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 6230:
       21: 6231:SV* var_test(...)
       21: 6232:CODE:
       21: 6233:{
       21: 6233-block  0
        -: 6234:	SV* restrict x_sv = NULL;
        -: 6235:	SV* restrict y_sv = NULL;
        -: 6236:	double ratio = 1.0, conf_level = 0.95;
        -: 6237:	const char* restrict alternative = "two.sided";
        6: 6238:	unsigned int arg_idx = 0;
        6: 6238-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 5
        1: 6238-block  1
call    2 returned 0
        5: 6239:
        5: 6239-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 4
        1: 6239-block  1
call    2 returned 0
        -: 6240:	// 1. Shift positional argument 'x' if it's an array reference
        4: 6241:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        4: 6242:	  x_sv = ST(arg_idx);
        4: 6243:	  arg_idx++;
        4: 6244:	}
        -: 6245:
        4: 6246:	// 2. Shift positional argument 'y' if it's an array reference
        4: 6246-block  0
branch  0 taken 1 (fallthrough)
branch  1 taken 3
        1: 6246-block  1
call    2 returned 0
        -: 6247:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 6248:	  y_sv = ST(arg_idx);
        3: 6249:	  arg_idx++;
        3: 6250:	}
        -: 6251:
        3: 6252:	// Ensure the remaining arguments form complete key-value pairs
        3: 6252-block  0
call    0 returned 3
        3: 6253:	if ((items - arg_idx) % 2 != 0) {
        -: 6254:	  croak("Usage: var_test(\\@x, \\@y, key => value, ...)");
        3: 6255:	}
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 6256:
    %%%%%: 6256-block  0
call    0 never executed
        3: 6257:	// --- Parse named arguments from the remaining flat stack ---
        3: 6257-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 6258:	for (; arg_idx < items; arg_idx += 2) {
    #####: 6259:	  const char* restrict key = SvPV_nolen(ST(arg_idx));
    %%%%%: 6259-block  0
call    0 never executed
        -: 6260:	  SV* restrict val = ST(arg_idx + 1);
        -: 6261:
        3: 6262:	  if      (strEQ(key, "x"))           x_sv        = val;
        3: 6263:	  else if (strEQ(key, "y"))           y_sv        = val;
       3*: 6264:	  else if (strEQ(key, "ratio"))       ratio       = SvNV(val);
        3: 6264-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 6264-block  1
    %%%%%: 6264-block  2
        -: 6265:	  else if (strEQ(key, "conf_level") || strEQ(key, "conf.level")) conf_level = SvNV(val);
        3: 6266:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        3: 6267:	  else croak("var_test: unknown argument '%s'", key);
        3: 6267-block  0
call    0 returned 3
        3: 6268:	}
call    0 returned 3
        -: 6269:
        -: 6270:	// --- Validate required inputs / types ---
        -: 6271:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        3: 6272:	  croak("var_test: 'x' is a required argument and must be an ARRAY reference");
        3: 6272-block  0
call    0 returned 3
        3: 6273:	if (!y_sv || !SvROK(y_sv) || SvTYPE(SvRV(y_sv)) != SVt_PVAV)
call    0 returned 3
call    1 returned 3
        -: 6274:	  croak("var_test: 'y' is a required argument and must be an ARRAY reference");
        3: 6275:
call    0 returned 3
        3: 6276:	if (ratio <= 0.0 || !isfinite(ratio)) 
call    0 returned 3
call    1 returned 3
        3: 6277:	  croak("var_test: 'ratio' must be a single positive number");
call    0 returned 3
call    1 returned 3
        3: 6278:	if (conf_level <= 0.0 || conf_level >= 1.0 || !isfinite(conf_level))
call    0 returned 3
call    1 returned 3
        -: 6279:	  croak("var_test: 'conf.level' must be a single number between 0 and 1");
        3: 6280:
call    0 returned 3
call    1 returned 3
        -: 6281:	AV* restrict x_av = (AV*)SvRV(x_sv);
        3: 6282:	AV* restrict y_av = (AV*)SvRV(y_sv);
call    0 returned 3
        3: 6283:	size_t nx_raw = av_len(x_av) + 1;
call    0 returned 3
call    1 returned 3
        3: 6284:	size_t ny_raw = av_len(y_av) + 1;
call    0 returned 3
call    1 returned 3
        3: 6285:
call    0 returned 3
call    1 returned 3
        -: 6286:	// --- Computation via Welford's Algorithm (ignoring NaNs) ---
        3: 6287:	double mean_x = 0.0, M2_x = 0.0;
call    0 returned 3
call    1 returned 3
        3: 6288:	size_t nx = 0;
call    0 returned 3
call    1 returned 3
        3: 6289:	for (size_t i = 0; i < nx_raw; i++) {
call    0 returned 3
call    1 returned 3
        3: 6290:		SV** restrict tv = av_fetch(x_av, i, 0);
call    0 returned 3
call    1 returned 3
        -: 6291:		if (tv && SvOK(*tv) && looks_like_number(*tv)) {
        3: 6292:			double val = SvNV(*tv);
call    0 returned 3
        -: 6293:			if (!isnan(val) && isfinite(val)) {
        -: 6294:				nx++;
        -: 6295:				double delta = val - mean_x;
        -: 6296:				mean_x += delta / nx;
        -: 6297:				M2_x += delta * (val - mean_x);
        -: 6298:			}
        -: 6299:		}
        -: 6300:	}
        -: 6301:
        -: 6302:	double mean_y = 0.0, M2_y = 0.0;
        -: 6303:	size_t ny = 0;
        -: 6304:	for (size_t i = 0; i < ny_raw; i++) {
        -: 6305:	  SV** restrict tv = av_fetch(y_av, i, 0);
        -: 6306:	  if (tv && SvOK(*tv) && looks_like_number(*tv)) {
        -: 6307:		   double val = SvNV(*tv);
        -: 6308:		   if (!isnan(val) && isfinite(val)) {
        -: 6309:		       ny++;
        -: 6310:		       double delta = val - mean_y;
        -: 6311:		       mean_y += delta / ny;
        -: 6312:		       M2_y += delta * (val - mean_y);
        -: 6313:		   }
        -: 6314:	  }
        -: 6315:	}
        -: 6316:
        -: 6317:	if (nx < 2) croak("not enough 'x' observations");
        -: 6318:	if (ny < 2) croak("not enough 'y' observations");
        -: 6319:
        -: 6320:	double df_x = (double)(nx - 1);
        -: 6321:	double df_y = (double)(ny - 1);
        -: 6322:	double var_x = M2_x / df_x;
        -: 6323:	double var_y = M2_y / df_y;
        -: 6324:
        -: 6325:	if (var_y == 0.0) croak("var_test: variance of 'y' is zero (cannot divide by zero)");
        -: 6326:
        -: 6327:	// --- Statistics Math ---
        -: 6328:	double estimate = var_x / var_y;
        -: 6329:	double statistic = estimate / ratio;
        -: 6330:
        -: 6331:	double p_val = pf(statistic, df_x, df_y);
        -: 6332:	double ci_lower = 0.0, ci_upper = INFINITY;
        -: 6333:
        -: 6334:	if (strcmp(alternative, "less") == 0) {
        -: 6335:	  ci_upper = estimate / qf_bisection(1.0 - conf_level, df_x, df_y);
        -: 6336:	} else if (strcmp(alternative, "greater") == 0) {
        -: 6337:	  p_val = 1.0 - p_val;
        -: 6338:	  ci_lower = estimate / qf_bisection(conf_level, df_x, df_y);
        -: 6339:	} else {
        -: 6340:	  // two.sided
        -: 6341:	  double p1 = p_val;
        -: 6342:	  double p2 = 1.0 - p_val;
        -: 6343:	  p_val = 2.0 * (p1 < p2 ? p1 : p2);
        -: 6344:	  
        -: 6345:	  double beta = (1.0 - conf_level) / 2.0;
        -: 6346:	  ci_lower = estimate / qf_bisection(1.0 - beta, df_x, df_y);
        -: 6347:	  ci_upper = estimate / qf_bisection(beta, df_x, df_y);
        -: 6348:	}
        -: 6349:
        -: 6350:	// --- Pack Results ---
        -: 6351:	HV* restrict results = newHV();
        -: 6352:	hv_store(results, "statistic", 9, newSVnv(statistic), 0);
        -: 6353:
        -: 6354:	AV* restrict param_av = newAV();
        -: 6355:	av_push(param_av, newSVnv(df_x));
        -: 6356:	av_push(param_av, newSVnv(df_y));
        -: 6357:	hv_store(results, "parameter", 9, newRV_noinc((SV*)param_av), 0);
        -: 6358:
        -: 6359:	hv_store(results, "p_value", 7, newSVnv(p_val), 0);
        -: 6360:
        -: 6361:	AV* restrict conf_int = newAV();
        -: 6362:	av_push(conf_int, newSVnv(ci_lower));
        -: 6363:	av_push(conf_int, newSVnv(ci_upper));
        -: 6364:	hv_store(results, "conf_int", 8, newRV_noinc((SV*)conf_int), 0);
        -: 6365:
        -: 6366:	hv_store(results, "estimate", 8, newSVnv(estimate), 0);
        -: 6367:	hv_store(results, "null_value", 10, newSVnv(ratio), 0);
        -: 6368:	hv_store(results, "alternative", 11, newSVpv(alternative, 0), 0);
        -: 6369:	hv_store(results, "method", 6, newSVpv("F test to compare two variances", 0), 0);
        -: 6370:
        -: 6371:	RETVAL = newRV_noinc((SV*)results);
        -: 6372:}
        -: 6373:OUTPUT:
        -: 6374:    RETVAL
        -: 6375:
        -: 6376:SV *sample(ref, n = 1)
        -: 6377:    SV *ref
        -: 6378:    IV n
        -: 6379:PREINIT:
        -: 6380:    SV *restrict ret = &PL_sv_undef;
        -: 6381:CODE:
        -: 6382:	if (!PL_srand_called) {
        -: 6383:	  (void)seedDrand01((Rand_seed_t)Perl_seed(aTHX));
        -: 6384:	  PL_srand_called = TRUE;
        -: 6385:	}
        -: 6386:	if (n < 0) n = 0;
        -: 6387:	if (SvROK(ref)) {
        -: 6388:		SV *restrict rv = SvRV(ref);
        -: 6389:		/* --- HASH REFERENCE --- */
        -: 6390:		if (SvTYPE(rv) == SVt_PVHV) {
        -: 6391:			HV *restrict hv    = (HV *)rv;
        -: 6392:			I32 count = hv_iterinit(hv);
        -: 6393:			I32 limit = (n < (IV)count) ? (I32)n : count;
        -: 6394:			HV *restrict ret_hv = newHV();
        -: 6395:
        -: 6396:			if (count > 0 && limit > 0) {
        -: 6397:				 HE **restrict entries;
        -: 6398:				 HE  *restrict entry;
        -: 6399:				 unsigned i;
        -: 6400:
        -: 6401:				 Newx(entries, count, HE *);
        -: 6402:
        -: 6403:				 /* Collect all HE pointers in one pass */
        -: 6404:				 i = 0;
        -: 6405:				 while ((entry = hv_iternext(hv)))
        -: 6406:				     entries[i++] = entry;
        -: 6407:
        -: 6408:				 /* Partial Fisher-Yates (only 'limit' passes) */
        -: 6409:				 for (i = 0; i < limit; i++) {
        -: 6410:				     I32 j    = i + (I32)(Drand01() * (count - i));
        -: 6411:				     HE *restrict tmp  = entries[i];
        -: 6412:				     entries[i] = entries[j];
        -: 6413:				     entries[j] = tmp;
        -: 6414:				 }
        -: 6415:
        -: 6416:				 /* Pre-size result hash to avoid rehashing during population */
        -: 6417:				 hv_ksplit(ret_hv, limit);
        -: 6418:
        -: 6419:				 for (i = 0; i < limit; i++) {
        -: 6420:				     HEK *restrict hek = HeKEY_hek(entries[i]);
        -: 6421:				     /*
        -: 6422:				      * hv_store() with a precomputed hash skips the hash
        -: 6423:				      * computation entirely.  Negative klen signals UTF-8.
        -: 6424:				      */
        -: 6425:				     (void)hv_store(
        -: 6426:				         ret_hv,
        -: 6427:				         HEK_KEY(hek),
        -: 6428:				         HEK_UTF8(hek) ? -(I32)HEK_LEN(hek) : (I32)HEK_LEN(hek),
        -: 6429:				         SvREFCNT_inc(HeVAL(entries[i])),  /* HeVAL: direct macro, no call */
        -: 6430:				         HeHASH(entries[i])                /* reuse precomputed hash */
        -: 6431:				     );
        -: 6432:				 }
        -: 6433:				 Safefree(entries);
        -: 6434:			}
        -: 6435:			ret = newRV_noinc((SV *)ret_hv);
        -: 6436:		} else if (SvTYPE(rv) == SVt_PVAV) {/* --- ARRAY REFERENCE --- */
        -: 6437:			AV    *restrict av    = (AV *)rv;
        -: 6438:			size_t count = av_top_index(av) + 1;  /* signed; 0 for empty AV */
        -: 6439:			size_t limit = (n < count) ? (size_t)n : count;
        -: 6440:			AV    *restrict ret_av = newAV();
        -: 6441:
        -: 6442:			/* Pre-allocate the result array to avoid incremental reallocs */
        -: 6443:			if (n > 0)
        -: 6444:				 av_extend(ret_av, (size_t)n - 1);
        -: 6445:
        -: 6446:			if (count > 0) {
        -: 6447:				 SV    **restrict src = AvARRAY(av);   /* direct pointer into AV's C array */
        -: 6448:				 size_t *restrict idx;
        -: 6449:				 size_t  i;
        -: 6450:
        -: 6451:				 /* Shuffle indices rather than SV** to keep the original AV intact */
        -: 6452:				 Newx(idx, count, size_t);
        -: 6453:				 for (i = 0; i < count; i++)
        -: 6454:				     idx[i] = i;
        -: 6455:
        -: 6456:				 /* Partial Fisher-Yates on the index array */
        -: 6457:				 for (i = 0; i < limit; i++) {
        -: 6458:				     size_t j   = i + (size_t)(Drand01() * (count - i));
        -: 6459:				     size_t tmp = idx[i];
        -: 6460:				     idx[i]  = idx[j];
        -: 6461:				     idx[j]  = tmp;
        -: 6462:				 }
        -: 6463:
        -: 6464:				 for (i = 0; i < (size_t)n; i++) {
        -: 6465:				     if (i < limit) {
        -: 6466:				         SV *restrict sv = src[idx[i]];   /* AvARRAY direct access — no av_fetch call */
        -: 6467:				         SV *push_sv;
        -: 6468:							if (sv && sv != &PL_sv_undef)
        -: 6469:								 push_sv = SvREFCNT_inc(sv);
        -: 6470:							else
        -: 6471:								 push_sv = newSV(0);
        -: 6472:							av_push(ret_av, push_sv);
        -: 6473:				     } else {
        -: 6474:				         av_push(ret_av, newSV(0));
        -: 6475:				     }
        -: 6476:				 }
        -: 6477:				 Safefree(idx);
        -: 6478:			} else {
        -: 6479:				for (size_t i = 0; i < (size_t)n; i++)
        -: 6480:				    av_push(ret_av, newSV(0));
        -: 6481:			}
        -: 6482:			ret = newRV_noinc((SV *)ret_av);
        -: 6483:		}
        -: 6484:	}
        -: 6485:	RETVAL = ret;
        -: 6486:OUTPUT:
        -: 6487:    RETVAL
