        -:    0:Source:LikeR.xs
        -:    0:Graph:./Stats-LikeR-0.01/LikeR.gcno
        -:    0:Data:./Stats-LikeR-0.01/LikeR.gcda
        -:    0:Runs:3
        -:    0:Source is newer than graph
        -:    1:/* --- C HELPER SECTION --- */
        -:    2:#define PERL_NO_GET_CONTEXT
        -:    3:#include "EXTERN.h"
        -:    4:#include "perl.h"
        -:    5:#include "XSUB.h"
        -:    6:#include "ppport.h"
        -:    7:#include <math.h>
        -:    8:#include <ctype.h>
        -:    9:#include <stdlib.h>
        -:   10:#include <float.h>
        -:   11:#include <string.h>
        -:   12:/* Ensure Perl's PRNG is seeded, matching the lazy-evaluation of Perl's rand() */
        -:   13:#define AUTO_SEED_PRNG() \
        -:   14:    do { \
        -:   15:        if (!PL_srand_called) { \
        -:   16:            (void)seedDrand01((Rand_seed_t)Perl_seed(aTHX)); \
        -:   17:            PL_srand_called = TRUE; \
        -:   18:        } \
        -:   19:    } while (0)
        -:   20:
        -:   21:// ---------------------------------------
        -:   22://   Helpers for Random Number Generation
        -:   23:// ---------------------------------------
        -:   24:#ifndef M_PI
        -:   25:#define M_PI 3.14159265358979323846
        -:   26:#endif
        -:   27:// C helper for EXACT Non-central T-distribution CDF via Numerical Integration.
        -:   28:// This perfectly replicates R's pt(..., ncp) exactness without requiring complex Beta functions.
function exact_pnt called 687 returned 100% blocks executed 90%
      687:   29:static double exact_pnt(double t, double df, double ncp) {
     687*:   30:	if (df <= 0.0) return 0.0;
      687:   30-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 687
    %%%%%:   30-block  1
        -:   31:	
      687:   32:	unsigned short int n_steps = 30000;
      687:   33:	double step = 1.0 / n_steps;
      687:   34:	double integral = 0.0;
      687:   35:	double half_df = df / 2.0;
        -:   36:
      687:   37:	double log_coef = log(2.0) + half_df * log(half_df) - lgamma(half_df);
      687:   38:	double root_half = 0.70710678118654752440; // 1 / sqrt(2)
        -:   39:
 20610000:   40:	for (unsigned short i = 1; i < n_steps; i++) {
      687:   40-block  0
 20610000:   40-block  1
branch  0 taken 20609313
branch  1 taken 687 (fallthrough)
 20609313:   41:		double u = i * step;
 20609313:   42:		double w = u / (1.0 - u);
        -:   43:		
        -:   44:		// Scaled Chi-distribution log-density
 20609313:   45:		double log_M = log_coef + (df - 1.0) * log(w) - half_df * w * w;
 20609313:   46:		double M = exp(log_M);
        -:   47:		
        -:   48:		// Exact Normal CDF using the C standard library's erfc function
 20609313:   49:		double z = t * w - ncp;
 20609313:   50:		double pnorm_val = 0.5 * erfc(-z * root_half);
        -:   51:		
 20609313:   52:		double weight = (i % 2 != 0) ? 4.0 : 2.0;
 20609313:   52-block  0
branch  0 taken 10305000 (fallthrough)
branch  1 taken 10304313
 10305000:   52-block  1
 10304313:   52-block  2
 20609313:   53:		integral += weight * (pnorm_val * M / ((1.0 - u) * (1.0 - u)));
 20609313:   53-block  0
        -:   54:	}
        -:   55:	
      687:   56:	return integral * (step / 3.0);
      687:   56-block  0
        -:   57:}
        -:   58:// --- Math Helpers for P-values and Confidence Intervals --- 
        -:   59:
        -:   60:// Ranking helper with tie adjustment (matches R's tie handling)
        -:   61:typedef struct { double val; size_t idx; double rank; } RankInfo;
function compare_rank called 33 returned 100% blocks executed 100%
       33:   62:static int compare_rank(const void *restrict a, const void *restrict b) {
       33:   63:	double diff = ((RankInfo*)a)->val - ((RankInfo*)b)->val;
       33:   64:	return (diff > 0) - (diff < 0);
       33:   64-block  0
        -:   65:}
        -:   66:
function compare_index called 33 returned 100% blocks executed 100%
       33:   67:static int compare_index(const void *restrict a, const void *restrict b) {
       33:   68:	return ((RankInfo*)a)->idx - ((RankInfo*)b)->idx;
       33:   68-block  0
        -:   69:}
        -:   70:
function compute_ranks called 6 returned 100% blocks executed 95%
        6:   71:static void compute_ranks(double *restrict data, double *restrict ranks, size_t n) {
        6:   72:	RankInfo *restrict items = safemalloc(n * sizeof(RankInfo));
        6:   72-block  0
call    0 returned 6
       36:   73:	for (size_t i = 0; i < n; i++) {
       36:   73-block  0
branch  0 taken 30
branch  1 taken 6 (fallthrough)
       30:   74:		items[i].val = data[i];
       30:   75:		items[i].idx = i;
       30:   75-block  0
        -:   76:	}
        6:   77:	qsort(items, n, sizeof(RankInfo), compare_rank);
        6:   77-block  0
call    0 returned 6
        -:   78:	// Handle ties by averaging ranks
       36:   79:	for (size_t i = 0; i < n; ) {
       36:   79-block  0
branch  0 taken 30
branch  1 taken 6 (fallthrough)
       30:   80:		size_t j = i + 1;
      30*:   81:		while (j < n && items[j].val == items[i].val) j++;
       30:   81-block  0
    %%%%%:   81-block  1
       30:   81-block  2
branch  0 taken 24 (fallthrough)
branch  1 taken 6
       24:   81-block  3
branch  2 taken 0
branch  3 taken 24 (fallthrough)
       30:   82:		double avg_rank = (i + 1 + j) / 2.0;
       60:   83:		for (size_t k = i; k < j; k++) items[k].rank = avg_rank;
       30:   83-block  0
       30:   83-block  1
       60:   83-block  2
branch  0 taken 30
branch  1 taken 30 (fallthrough)
       30:   84:		i = j;
       30:   84-block  0
        -:   85:	}
        6:   86:	qsort(items, n, sizeof(RankInfo), compare_index);
        6:   86-block  0
call    0 returned 6
       36:   87:	for (size_t i = 0; i < n; i++) ranks[i] = items[i].rank;
       30:   87-block  0
       36:   87-block  1
branch  0 taken 30
branch  1 taken 6 (fallthrough)
        6:   88:	Safefree(items);
        6:   88-block  0
call    0 returned 6
        6:   89:}
        -:   90:// Generates a single binomial random variate. 
        -:   91://Uses the standard Bernoulli trial loop. Drand01() taps into Perl's PRNG.
function generate_binomial called 61497 returned 100% blocks executed 100%
    61497:   92:static size_t generate_binomial(const size_t size, const double prob) {
    61497:   93:	if (prob <= 0.0) return 0;
    61497:   93-block  0
branch  0 taken 300 (fallthrough)
branch  1 taken 61197
      300:   93-block  1
    61197:   94:	if (prob >= 1.0) return size;
    61197:   94-block  0
branch  0 taken 300 (fallthrough)
branch  1 taken 60897
      300:   94-block  1
        -:   95:
    60897:   96:	size_t successes = 0;
   936870:   97:	for (size_t i = 0; i < size; i++) {
    60897:   97-block  0
   875973:   97-block  1
   936870:   97-block  2
branch  0 taken 875973
branch  1 taken 60897 (fallthrough)
   875973:   98:		if (Drand01() <= prob) successes++;
   875973:   98-block  0
call    0 returned 875973
branch  1 taken 318938 (fallthrough)
branch  2 taken 557035
   318938:   98-block  1
        -:   99:	}
    60897:  100:	return successes;
    60897:  100-block  0
        -:  101:}
        -:  102:// Helper: log combination
function log_choose called 426 returned 100% blocks executed 100%
      426:  103:static double log_choose(size_t n, size_t k) {
      426:  104:	return lgamma((double)n + 1.0) - lgamma((double)k + 1.0) - lgamma((double)(n - k) + 1.0);
      426:  104-block  0
        -:  105:}
        -:  106:
        -:  107:// Log-space tails for non-central hypergeometric
function calc_tails_logspace called 1212 returned 100% blocks executed 100%
     1212:  108: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) {
     1212:  109:	double max_d = -1e300, log_omega = log(omega);
        -:  110:
    10056:  111:	for(size_t k = 0; k <= max_x - min_x; ++k) {
     1212:  111-block  0
     8844:  111-block  1
    10056:  111-block  2
branch  0 taken 8844
branch  1 taken 1212 (fallthrough)
     8844:  112:	  double d_val = logdc[k] + log_omega * (min_x + k);
     8844:  113:	  if (d_val > max_d) max_d = d_val;
     8844:  113-block  0
branch  0 taken 6627 (fallthrough)
branch  1 taken 2217
     6627:  113-block  1
        -:  114:	}
        -:  115:
     1212:  116:	double sum_d = 0.0;
    10056:  117:	for(size_t k = 0; k <= max_x - min_x; ++k) {
     1212:  117-block  0
    10056:  117-block  1
branch  0 taken 8844
branch  1 taken 1212 (fallthrough)
     8844:  118:	  sum_d += exp(logdc[k] + log_omega * (min_x + k) - max_d);
     8844:  118-block  0
        -:  119:	}
        -:  120:
     1212:  121:	*lower_tail = 0.0;
     1212:  122:	*upper_tail = 0.0;
        -:  123:
    10056:  124:	for(size_t k = 0; k <= max_x - min_x; ++k) {
     1212:  124-block  0
     8844:  124-block  1
    10056:  124-block  2
branch  0 taken 8844
branch  1 taken 1212 (fallthrough)
     8844:  125:	  double p_prob = exp(logdc[k] + log_omega * (min_x + k) - max_d) / sum_d;
     8844:  126:	  if (min_x + k <= a) *lower_tail += p_prob;
     8844:  126-block  0
branch  0 taken 7458 (fallthrough)
branch  1 taken 1386
     7458:  126-block  1
     8844:  127:	  if (min_x + k >= a) *upper_tail += p_prob;
     8844:  127-block  0
branch  0 taken 2598 (fallthrough)
branch  1 taken 6246
     2598:  127-block  1
        -:  128:	}
     1212:  129:}
        -:  130:
        -:  131:// Exact stats using log-space
function calculate_exact_stats called 15 returned 100% blocks executed 96%
       15:  132: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) {
       15:  133:    double alpha = 1.0 - conf_level;
       15:  134:    size_t r1 = a + b, r2 = c + d, c1 = a + c;
       15:  135:    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
       15:  135-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 3
       12:  135-block  1
        3:  135-block  2
       15:  136:    size_t max_x = (r1 < c1) ? r1 : c1;
        -:  137:    
       15:  138:    bool is_less = (strcmp(alt, "less") == 0);
       15:  139:    bool is_greater = (strcmp(alt, "greater") == 0);
        -:  140:
       15:  141:    double *restrict logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
       15:  141-block  0
call    0 returned 15
       15:  142:    double denom = log_choose(r1 + r2, c1);
call    0 returned 15
      114:  143:    for(size_t x = min_x; x <= max_x; ++x) {
      114:  143-block  0
branch  0 taken 99
branch  1 taken 15 (fallthrough)
       99:  144:        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
       99:  144-block  0
call    0 returned 99
call    1 returned 99
        -:  145:    }
        -:  146:
        -:  147:    // MLE
      15*:  148:    if (a == min_x && a == max_x) *mle_or = 1.0;
       15:  148-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%:  148-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%:  148-block  2
      15*:  149:    else if (a == min_x) *mle_or = 0.0;
       15:  149-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%:  149-block  1
       15:  150:    else if (a == max_x) *mle_or = INFINITY;
       15:  150-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 12
        3:  150-block  1
        -:  151:    else {
       12:  152:        double log_low = -100.0, log_high = 100.0;
      696:  153:        for (unsigned short int i = 0; i < 3000; i++) {
       12:  153-block  0
      684:  153-block  1
      696:  153-block  2
branch  0 taken 696
branch  1 taken 0 (fallthrough)
      696:  154:            double log_mid = 0.5 * (log_low + log_high);
      696:  155:            double max_d = -1e300;
     5568:  156:            for(size_t k = 0; k <= max_x - min_x; ++k) {
      696:  156-block  0
     4872:  156-block  1
     5568:  156-block  2
branch  0 taken 4872
branch  1 taken 696 (fallthrough)
     4872:  157:                double d_val = logdc[k] + log_mid * (min_x + k);
     4872:  158:                if (d_val > max_d) max_d = d_val;
     4872:  158-block  0
branch  0 taken 4038 (fallthrough)
branch  1 taken 834
     4038:  158-block  1
        -:  159:            }
      696:  160:            double sum_d = 0.0, exp_val = 0.0;
     5568:  161:            for(size_t k = 0; k <= max_x - min_x; ++k) {
      696:  161-block  0
     5568:  161-block  1
branch  0 taken 4872
branch  1 taken 696 (fallthrough)
     4872:  162:                double p_prob = exp(logdc[k] + log_mid * (min_x + k) - max_d);
     4872:  163:                sum_d += p_prob;
     4872:  164:                exp_val += (min_x + k) * p_prob;
     4872:  164-block  0
        -:  165:            }
      696:  166:            exp_val /= sum_d;
        -:  167:            
      696:  168:            if (exp_val > a) log_high = log_mid;
      696:  168-block  0
branch  0 taken 420 (fallthrough)
branch  1 taken 276
      420:  168-block  1
      276:  169:            else log_low = log_mid;
      276:  169-block  0
      696:  170:            if (log_high - log_low < 1e-15) break;
      696:  170-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 684
       12:  170-block  1
        -:  171:        }
       12:  172:        *mle_or = exp(0.5 * (log_low + log_high));
       12:  172-block  0
        -:  173:    }
        -:  174:
       15:  175:    *ci_low = 0.0;
       15:  176:    *ci_high = INFINITY;
        -:  177:
        -:  178:    // Lower CI
       15:  179:    if (!is_less) { 
       15:  179-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 3
       12:  180:        double target_alpha = is_greater ? alpha : alpha / 2.0;
       12:  180-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        3:  180-block  1
        9:  180-block  2
       12:  181:        if (a != min_x) {
       12:  181-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12:  182:            double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
      696:  183:            for (unsigned short int i = 0; i < 1000; i++) {
       12:  183-block  0
      684:  183-block  1
      696:  183-block  2
branch  0 taken 696
branch  1 taken 0 (fallthrough)
      696:  184:                double log_mid = 0.5 * (log_low + log_high);
      696:  185:                double mid = exp(log_mid);
      696:  186:                calc_tails_logspace(a, min_x, max_x, mid, logdc, &lt, &ut);
      696:  186-block  0
call    0 returned 696
      696:  187:                double err = fabs(ut - target_alpha);
      696:  188:                if (err < best_err) { best_err = err; best = mid; }
branch  0 taken 309 (fallthrough)
branch  1 taken 387
      309:  188-block  0
      696:  189:                if (ut > target_alpha) log_high = log_mid;
      696:  189-block  0
branch  0 taken 378 (fallthrough)
branch  1 taken 318
      378:  189-block  1
      318:  190:                else log_low = log_mid;
      318:  190-block  0
      696:  191:                if (log_high - log_low < 1e-15) break;
      696:  191-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 684
       12:  191-block  1
        -:  192:            }
       12:  193:            *ci_low = best;
       12:  193-block  0
        -:  194:        }
        -:  195:    }
        -:  196:
        -:  197:    // Upper CI
       15:  198:    if (!is_greater) { 
       15:  198-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 3
       12:  199:        double target_alpha = is_less ? alpha : alpha / 2.0;
       12:  199-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        3:  199-block  1
        9:  199-block  2
       12:  200:        if (a != max_x) {
       12:  200-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9:  201:            double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
      516:  202:            for (unsigned short int i = 0; i < 1000; i++) {
        9:  202-block  0
      507:  202-block  1
      516:  202-block  2
branch  0 taken 516
branch  1 taken 0 (fallthrough)
      516:  203:                double log_mid = 0.5 * (log_low + log_high);
      516:  204:                double mid = exp(log_mid);
      516:  205:                calc_tails_logspace(a, min_x, max_x, mid, logdc, &lt, &ut);
      516:  205-block  0
call    0 returned 516
      516:  206:                double err = fabs(lt - target_alpha);
      516:  207:                if (err < best_err) { best_err = err; best = mid; }
branch  0 taken 252 (fallthrough)
branch  1 taken 264
      252:  207-block  0
      516:  208:                if (lt > target_alpha) log_low = log_mid;
      516:  208-block  0
branch  0 taken 252 (fallthrough)
branch  1 taken 264
      252:  208-block  1
      264:  209:                else log_high = log_mid;
      264:  209-block  0
      516:  210:                if (log_high - log_low < 1e-15) break;
      516:  210-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 507
        9:  210-block  1
        -:  211:            }
        9:  212:            *ci_high = best;
        9:  212-block  0
        -:  213:        }
        -:  214:    }
       15:  215:    safefree(logdc);
       15:  215-block  0
call    0 returned 15
       15:  216:}
        -:  217:
        -:  218:// Exact p-value using log-space
function exact_p_value called 15 returned 100% blocks executed 97%
       15:  219:static double exact_p_value(size_t a, size_t b, size_t c, size_t d, const char* alt) {
       15:  220:    size_t r1 = a + b, r2 = c + d, c1 = a + c;
       15:  221:    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
       15:  221-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 3
       12:  221-block  1
        3:  221-block  2
       15:  222:    size_t max_x = (r1 < c1) ? r1 : c1;
        -:  223:
       15:  224:    double *logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
       15:  224-block  0
call    0 returned 15
       15:  225:    double denom = log_choose(r1 + r2, c1);
call    0 returned 15
      114:  226:    for(size_t x = min_x; x <= max_x; ++x) {
      114:  226-block  0
branch  0 taken 99
branch  1 taken 15 (fallthrough)
       99:  227:        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
       99:  227-block  0
call    0 returned 99
call    1 returned 99
        -:  228:    }
        -:  229:
       15:  230:    double p_val = 0.0;
        -:  231:    
       15:  232:    if (strcmp(alt, "less") == 0) {
       15:  232-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 12
       15:  233:        for(size_t x = min_x; x <= a; ++x) p_val += exp(logdc[x - min_x]);
        3:  233-block  0
       12:  233-block  1
       15:  233-block  2
branch  0 taken 12
branch  1 taken 3
       12:  234:    } else if (strcmp(alt, "greater") == 0) {
       12:  234-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        9:  235:        for(size_t x = a; x <= max_x; ++x) p_val += exp(logdc[x - min_x]);
        3:  235-block  0
        6:  235-block  1
        9:  235-block  2
branch  0 taken 6
branch  1 taken 3
        -:  236:    } else {
        9:  237:        double p_obs = exp(logdc[a - min_x]);
        9:  238:        double relErr = 1.0 + 1e-7;
       78:  239:        for(size_t x = min_x; x <= max_x; ++x) {
        9:  239-block  0
       69:  239-block  1
       78:  239-block  2
branch  0 taken 69
branch  1 taken 9 (fallthrough)
       69:  240:            double p_cur = exp(logdc[x - min_x]);
       69:  241:            if (p_cur <= p_obs * relErr) p_val += p_cur;
       69:  241-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 39
       30:  241-block  1
        -:  242:        }
        -:  243:    }
        -:  244:    
       15:  245:    safefree(logdc);
       15:  245-block  0
call    0 returned 15
      15*:  246:    return (p_val > 1.0) ? 1.0 : p_val;
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%:  246-block  0
       15:  246-block  1
       15:  246-block  2
        -:  247:}
        -:  248:/* -----------------------------------------------------------------------
        -:  249: * Helpers for lm Linear Regression: OLS Matrix Math & Formula Parsing
        -:  250: * ----------------------------------------------------------------------- */
        -:  251:
        -:  252:/* Sweep operator for symmetric positive-definite matrices (e.g., XtX).
        -:  253: * This gracefully handles collinearity by bypassing aliased columns.
        -:  254: * Utilizes a relative tolerance check to prevent dropping micro-variance features.
        -:  255: */
function sweep_matrix_ols called 132 returned 100% blocks executed 100%
      132:  256:static int sweep_matrix_ols(double *restrict A, size_t n, bool *restrict aliased) {
      132:  257:	int rank = 0;
      132:  258:	double *restrict orig_diag = (double*)safemalloc(n * sizeof(double));
      132:  258-block  0
call    0 returned 132
        -:  259:
        -:  260:	// Save the original diagonal values to use as a baseline for relative variance
      510:  261:	for (size_t k = 0; k < n; k++) {
      510:  261-block  0
branch  0 taken 378
branch  1 taken 132 (fallthrough)
      378:  262:		aliased[k] = false;
      378:  263:		orig_diag[k] = A[k * n + k];
      378:  263-block  0
        -:  264:	}
        -:  265:
      510:  266:	for (size_t k = 0; k < n; k++) {
      132:  266-block  0
      378:  266-block  1
      510:  266-block  2
branch  0 taken 378
branch  1 taken 132 (fallthrough)
        -:  267:		// Check pivot for collinearity using a RELATIVE tolerance
        -:  268:		// (Fallback to a tiny absolute tolerance of 1e-24 to catch literal zero vectors)
      378:  269:		if (fabs(A[k * n + k]) <= 1e-10 * orig_diag[k] || fabs(A[k * n + k]) < 1e-24) {
      378:  269-block  0
branch  0 taken 375 (fallthrough)
branch  1 taken 3
      375:  269-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 375
        3:  270:			aliased[k] = true;
        -:  271:			// Isolate this column so it doesn't affect the rest of the matrix
       12:  272:			for (size_t i = 0; i < n; i++) { 
        3:  272-block  0
       12:  272-block  1
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9:  273:				A[k * n + i] = 0.0; 
        9:  274:				A[i * n + k] = 0.0; 
        9:  274-block  0
        -:  275:			}
        3:  276:			continue;
        3:  276-block  0
        -:  277:		}
      375:  278:		rank++;
      375:  279:		double pivot = 1.0 / A[k * n + k];
      375:  280:		A[k * n + k] = 1.0;
     1476:  281:		for (size_t j = 0; j < n; j++) A[k * n + j] *= pivot;
      375:  281-block  0
     1101:  281-block  1
     1476:  281-block  2
branch  0 taken 1101
branch  1 taken 375 (fallthrough)
     1476:  282:		for (size_t i = 0; i < n; i++) {
      375:  282-block  0
     1101:  282-block  1
     1476:  282-block  2
branch  0 taken 1101
branch  1 taken 375 (fallthrough)
     1101:  283:			if (i != k && A[i * n + k] != 0.0) {
     1101:  283-block  0
branch  0 taken 726 (fallthrough)
branch  1 taken 375
      726:  283-block  1
branch  2 taken 708 (fallthrough)
branch  3 taken 18
      708:  284:				  double factor = A[i * n + k];
      708:  285:				  A[i * n + k] = 0.0;
     2838:  286:				  for (size_t j = 0; j < n; j++) {
      708:  286-block  0
     2838:  286-block  1
branch  0 taken 2130
branch  1 taken 708 (fallthrough)
     2130:  287:				       A[i * n + j] -= factor * A[k * n + j];
     2130:  287-block  0
        -:  288:				  }
        -:  289:			}
        -:  290:		}
        -:  291:	}
      132:  292:	Safefree(orig_diag);
      132:  292-block  0
call    0 returned 132
      132:  293:	return rank;
        -:  294:}
        -:  295:
        -:  296:// Internal extractor resolving single data values. Returns NAN on missing or non-numeric.
function get_data_value called 4872 returned 100% blocks executed 95%
     4872:  297:static double get_data_value(HV *restrict data_hoa, HV **restrict row_hashes, unsigned int i, const char *restrict var) {
     4872:  298:    SV **restrict val = NULL;
     4872:  299:    if (row_hashes) {
     4872:  299-block  0
branch  0 taken 3552 (fallthrough)
branch  1 taken 1320
     3552:  300:        val = hv_fetch(row_hashes[i], var, strlen(var), 0);
     3552:  300-block  0
call    0 returned 3552
     3552:  301:        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
branch  0 taken 3552 (fallthrough)
branch  1 taken 0
     3552:  301-block  0
branch  2 taken 3552 (fallthrough)
branch  3 taken 0
     3552:  301-block  1
branch  4 taken 3552 (fallthrough)
branch  5 taken 0
     3552:  302:            AV*restrict av = (AV*)SvRV(*val);
     3552:  303:            val = av_fetch(av, 0, 0);
     3552:  303-block  0
call    0 returned 3552
        -:  304:        }
     1320:  305:    } else if (data_hoa) {
     1320:  305-block  0
branch  0 taken 1320 (fallthrough)
branch  1 taken 0
     1320:  306:        SV**restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
     1320:  306-block  0
call    0 returned 1320
     1320:  307:        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
branch  0 taken 1320 (fallthrough)
branch  1 taken 0
     1320:  307-block  0
branch  2 taken 1320 (fallthrough)
branch  3 taken 0
     1320:  307-block  1
branch  4 taken 1320 (fallthrough)
branch  5 taken 0
     1320:  308:            AV*restrict av = (AV*)SvRV(*col);
     1320:  309:            val = av_fetch(av, i, 0);
     1320:  309-block  0
call    0 returned 1320
        -:  310:        }
        -:  311:    }
     4872:  312:    if (val && SvOK(*val)) {
     4872:  312-block  0
branch  0 taken 4872 (fallthrough)
branch  1 taken 0
     4872:  312-block  1
branch  2 taken 4863 (fallthrough)
branch  3 taken 9
     4863:  313:        if (looks_like_number(*val)) return SvNV(*val);
     4863:  313-block  0
call    0 returned 4863
branch  1 taken 4863 (fallthrough)
branch  2 taken 0
     4863:  313-block  1
call    3 returned 4863
    #####:  314:        return NAN; // Catch strings like "blue"
    %%%%%:  314-block  0
        -:  315:    }
        9:  316:    return NAN; // Catch undef/missing keys
        9:  316-block  0
        -:  317:}
        -:  318:
        -:  319:// Helper: Get all available columns for the '.' operator expansion
function get_all_columns called 6 returned 100% blocks executed 52%
        6:  320:static AV* get_all_columns(HV *restrict data_hoa, HV **restrict row_hashes, size_t n) {
        6:  321:    AV *cols = newAV();
        6:  321-block  0
call    0 returned 6
        6:  322:    if (data_hoa) {
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6:  323:        hv_iterinit(data_hoa);
        6:  323-block  0
call    0 returned 6
        -:  324:        HE *entry;
       24:  325:        while ((entry = hv_iternext(data_hoa))) {
       24:  325-block  0
call    0 returned 24
branch  1 taken 18
branch  2 taken 6
       18:  326:            av_push(cols, newSVsv(hv_iterkeysv(entry)));
       18:  326-block  0
call    0 returned 18
call    1 returned 18
call    2 returned 18
        -:  327:        }
    #####:  328:    } else if (row_hashes && n > 0 && row_hashes[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:        hv_iterinit(row_hashes[0]);
    %%%%%:  329-block  0
call    0 never executed
        -:  330:        HE *entry;
    #####:  331:        while ((entry = hv_iternext(row_hashes[0]))) {
    %%%%%:  331-block  0
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
    #####:  332:            av_push(cols, newSVsv(hv_iterkeysv(entry)));
    %%%%%:  332-block  0
call    0 never executed
call    1 never executed
call    2 never executed
        -:  333:        }
        -:  334:    }
        6:  335:    return cols;
        6:  335-block  0
        -:  336:}
        -:  337:
        -:  338:// Recursive formula resolver with tightened NaN and Null handling
function evaluate_term called 4968 returned 100% blocks executed 52%
     4968:  339:static double evaluate_term(HV *restrict data_hoa, HV **restrict row_hashes, unsigned int i, const char *restrict term) {
    4968*:  340:    if (!term || term[0] == '\0') return NAN;
     4968:  340-block  0
branch  0 taken 4968 (fallthrough)
branch  1 taken 0
     4968:  340-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 4968
    %%%%%:  340-block  2
        -:  341:
     4968:  342:    char *restrict term_cpy = savepv(term); 
     4968:  342-block  0
call    0 returned 4968
     4968:  343:    char *restrict colon = strchr(term_cpy, ':');
     4968:  344:    if (colon) {
branch  0 taken 96 (fallthrough)
branch  1 taken 4872
       96:  345:        *colon = '\0';
       96:  346:        double left = evaluate_term(data_hoa, row_hashes, i, term_cpy);
       96:  346-block  0
call    0 returned 96
       96:  347:        double right = evaluate_term(data_hoa, row_hashes, i, colon + 1);
call    0 returned 96
       96:  348:        Safefree(term_cpy); 
call    0 returned 96
        -:  349:        
      96*:  350:        if (isnan(left) || isnan(right)) return NAN;
branch  0 taken 96 (fallthrough)
branch  1 taken 0
       96:  350-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 96
    %%%%%:  350-block  1
       96:  351:        return left * right;
       96:  351-block  0
        -:  352:    }
     4872:  353:    if (strncmp(term_cpy, "I(", 2) == 0) {
     4872:  353-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 4872
    #####:  354:        char *restrict end = strrchr(term_cpy, ')');
    #####:  355:        if (end) *end = '\0';
    %%%%%:  355-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  355-block  1
    #####:  356:        char *restrict inner = term_cpy + 2;
    #####:  357:        char *restrict caret = strchr(inner, '^');
    #####:  358:        int power = 1;
    #####:  359:        if (caret) {
    %%%%%:  359-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####:  360:            *caret = '\0';
    #####:  361:            power = atoi(caret + 1);
    %%%%%:  361-block  0
        -:  362:        }
    #####:  363:        double v = get_data_value(data_hoa, row_hashes, i, inner);
    %%%%%:  363-block  0
call    0 never executed
    #####:  364:        Safefree(term_cpy); 
call    0 never executed
        -:  365:        
    #####:  366:        if (isnan(v)) return NAN;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  366-block  0
    #####:  367:        return power == 1 ? v : pow(v, power);
    %%%%%:  367-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  367-block  1
    %%%%%:  367-block  2
    %%%%%:  367-block  3
        -:  368:    }
     4872:  369:    double result = get_data_value(data_hoa, row_hashes, i, term_cpy);
     4872:  369-block  0
call    0 returned 4872
     4872:  370:    Safefree(term_cpy); 
call    0 returned 4872
     4872:  371:    return result;
        -:  372:}
        -:  373:
        -:  374:// Helper to infer column type from its first valid element
function is_column_categorical called 162 returned 100% blocks executed 100%
      162:  375:static bool is_column_categorical(HV *restrict data_hoa, HV **restrict row_hashes, size_t n, const char *restrict var) {
      258:  376:	for (size_t i = 0; i < n; i++) {
      162:  376-block  0
       96:  376-block  1
      258:  376-block  2
branch  0 taken 255
branch  1 taken 3 (fallthrough)
      255:  377:		SV **restrict val = NULL;
      255:  378:		if (row_hashes) {
      255:  378-block  0
branch  0 taken 165 (fallthrough)
branch  1 taken 90
      165:  379:			val = hv_fetch(row_hashes[i], var, strlen(var), 0);
      165:  379-block  0
call    0 returned 165
      165:  380:			if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
branch  0 taken 69 (fallthrough)
branch  1 taken 96
       69:  380-block  0
branch  2 taken 69 (fallthrough)
branch  3 taken 0
       69:  380-block  1
branch  4 taken 69 (fallthrough)
branch  5 taken 0
       69:  381:				 AV*restrict av = (AV*)SvRV(*val);
       69:  382:				 val = av_fetch(av, 0, 0);
       69:  382-block  0
call    0 returned 69
        -:  383:			}
       90:  384:		} else if (data_hoa) {
       90:  384-block  0
branch  0 taken 90 (fallthrough)
branch  1 taken 0
       90:  385:			SV **restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
       90:  385-block  0
call    0 returned 90
       90:  386:			if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
branch  0 taken 90 (fallthrough)
branch  1 taken 0
       90:  386-block  0
branch  2 taken 90 (fallthrough)
branch  3 taken 0
       90:  386-block  1
branch  4 taken 90 (fallthrough)
branch  5 taken 0
       90:  387:				 AV*restrict av = (AV*)SvRV(*col);
       90:  388:				 val = av_fetch(av, i, 0);
       90:  388-block  0
call    0 returned 90
        -:  389:			}
        -:  390:		}
      255:  391:		if (val && SvOK(*val)) {
      255:  391-block  0
branch  0 taken 159 (fallthrough)
branch  1 taken 96
      159:  391-block  1
branch  2 taken 159 (fallthrough)
branch  3 taken 0
      159:  392:			if (looks_like_number(*val)) return false; // First valid is number -> Numeric Column
      159:  392-block  0
call    0 returned 159
branch  1 taken 132 (fallthrough)
branch  2 taken 27
      132:  392-block  1
       27:  393:			return true; // First valid is string -> Categorical Column
       27:  393-block  0
        -:  394:		}
        -:  395:	}
        3:  396:	return false;
        3:  396-block  0
        -:  397:}
        -:  398:
        -:  399:/* Internal extractor resolving single data string values using dynamic allocation. */
function get_data_string_alloc called 1041 returned 100% blocks executed 68%
     1041:  400:static char* get_data_string_alloc(HV *restrict data_hoa, HV **restrict row_hashes, size_t i, const char *restrict var) {
     1041:  401:	SV **restrict val = NULL;
     1041:  402:	if (row_hashes) {
     1041:  402-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1041
    #####:  403:		val = hv_fetch(row_hashes[i], var, strlen(var), 0);
    %%%%%:  403-block  0
call    0 never executed
    #####:  404:		if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
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:			AV*restrict av = (AV*)SvRV(*val);
    #####:  406:			val = av_fetch(av, 0, 0);
    %%%%%:  406-block  0
call    0 never executed
        -:  407:		}
     1041:  408:	} else if (data_hoa) {
     1041:  408-block  0
branch  0 taken 1041 (fallthrough)
branch  1 taken 0
     1041:  409:		SV **restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
     1041:  409-block  0
call    0 returned 1041
     1041:  410:		if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
branch  0 taken 1041 (fallthrough)
branch  1 taken 0
     1041:  410-block  0
branch  2 taken 1041 (fallthrough)
branch  3 taken 0
     1041:  410-block  1
branch  4 taken 1041 (fallthrough)
branch  5 taken 0
     1041:  411:			AV*restrict av = (AV*)SvRV(*col);
     1041:  412:			val = av_fetch(av, i, 0);
     1041:  412-block  0
call    0 returned 1041
        -:  413:		}
        -:  414:	}
     1041:  415:	if (val && SvOK(*val)) {
     1041:  415-block  0
branch  0 taken 1041 (fallthrough)
branch  1 taken 0
     1041:  415-block  1
branch  2 taken 1041 (fallthrough)
branch  3 taken 0
     1041:  416:	  return savepv(SvPV_nolen(*val)); /* Allocates and returns string */
     1041:  416-block  0
call    0 returned 1041
call    1 returned 1041
        -:  417:	}
    #####:  418:	return NULL;
    %%%%%:  418-block  0
        -:  419:}
        -:  420:
        -:  421:// Struct for sorting p-values while remembering their original index
        -:  422:typedef struct {
        -:  423:	double p;
        -:  424:	size_t orig_idx;
        -:  425:} PVal;
        -:  426:
        -:  427:// Comparator for qsort
function cmp_pval called 4557 returned 100% blocks executed 83%
     4557:  428:static int cmp_pval(const void *restrict a, const void *restrict b) {
     4557:  429:	double diff = ((PVal*)a)->p - ((PVal*)b)->p;
     4557:  430:	if (diff < 0) return -1;
     4557:  430-block  0
branch  0 taken 2121 (fallthrough)
branch  1 taken 2436
     2121:  430-block  1
     2436:  431:	if (diff > 0) return 1;
     2436:  431-block  0
branch  0 taken 2436 (fallthrough)
branch  1 taken 0
     2436:  431-block  1
        -:  432:	/* Stabilize sort by falling back to original index */
    #####:  433:	return ((PVal*)a)->orig_idx - ((PVal*)b)->orig_idx; 
    %%%%%:  433-block  0
        -:  434:}
        -:  435:/* -----------------------------------------------------------------------
        -:  436: * Helpers for cor(): ranking (Spearman), Pearson r, Kendall tau-b
        -:  437: * ----------------------------------------------------------------------- */
        -:  438:/* Item used to sort values while remembering their original index,
        -:  439: * needed for average-rank tie-breaking in Spearman correlation.        */
        -:  440:typedef struct {
        -:  441:	double val;
        -:  442:	size_t idx;
        -:  443:} RankItem;
        -:  444:
function cmp_rank_item called 111 returned 100% blocks executed 100%
      111:  445:static int cmp_rank_item(const void *restrict a, const void *restrict b) {
      111:  446:	double diff = ((RankItem*)a)->val - ((RankItem*)b)->val;
      111:  447:	if (diff < 0) return -1;
      111:  447-block  0
branch  0 taken 99 (fallthrough)
branch  1 taken 12
       99:  447-block  1
       12:  448:	if (diff > 0) return  1;
       12:  448-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9:  448-block  1
        3:  449:	return 0;
        3:  449-block  0
        -:  450:}
        -:  451:
        -:  452:/* Compute 1-based average ranks with tie-breaking into out[].
        -:  453: * in[] is not modified.                                                 */
function rank_data called 12 returned 100% blocks executed 95%
       12:  454:static void rank_data(const double *restrict in, double *restrict out, size_t n) {
        -:  455:	RankItem *restrict ri;
      12*:  456:	Newx(ri, n, RankItem);
       12:  456-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%:  456-block  1
call    2 never executed
       12:  456-block  2
call    3 returned 12
       96:  457:	for (size_t i = 0; i < n; i++) { ri[i].val = in[i]; ri[i].idx = i; }
       84:  457-block  0
       96:  457-block  1
branch  0 taken 84
branch  1 taken 12 (fallthrough)
       12:  458:	qsort(ri, n, sizeof(RankItem), cmp_rank_item);
       12:  458-block  0
call    0 returned 12
        -:  459:
       12:  460:	size_t i = 0;
       93:  461:	while (i < n) {
       93:  461-block  0
branch  0 taken 81
branch  1 taken 12 (fallthrough)
       81:  462:		size_t j = i;
        -:  463:		/* Find the full extent of this tie group */
       84:  464:		while (j + 1 < n && ri[j + 1].val == ri[j].val) j++;
       81:  464-block  0
        3:  464-block  1
       84:  464-block  2
branch  0 taken 72 (fallthrough)
branch  1 taken 12
       72:  464-block  3
branch  2 taken 3
branch  3 taken 69 (fallthrough)
        -:  465:		/* All members get the average of ranks i+1 … j+1 (1-based) */
       81:  466:		double avg = (double)(i + j) / 2.0 + 1.0;
      165:  467:		for (size_t k = i; k <= j; k++) out[ri[k].idx] = avg;
       81:  467-block  0
       84:  467-block  1
      165:  467-block  2
branch  0 taken 84
branch  1 taken 81 (fallthrough)
       81:  468:		i = j + 1;
       81:  468-block  0
        -:  469:	}
       12:  470:	Safefree(ri);
       12:  470-block  0
call    0 returned 12
       12:  471:}
        -:  472:
        -:  473:/* Pearson product-moment r between two n-element arrays.
        -:  474: * Returns NAN when either variable has zero variance (matches R).       */
function pearson_corr called 18 returned 100% blocks executed 86%
       18:  475:static double pearson_corr(const double *restrict x, const double *restrict y, size_t n) {
       18:  476:	double sx = 0, sy = 0, sxy = 0, sx2 = 0, sy2 = 0;
      108:  477:	for (size_t i = 0; i < n; i++) {
       18:  477-block  0
      108:  477-block  1
branch  0 taken 90
branch  1 taken 18 (fallthrough)
       90:  478:	  sx  += x[i];     sy  += y[i];
       90:  479:	  sxy += x[i]*y[i]; sx2 += x[i]*x[i]; sy2 += y[i]*y[i];
       90:  479-block  0
        -:  480:	}
       18:  481:	double num = (double)n * sxy - sx * sy;
       18:  482:	double den = sqrt(((double)n * sx2 - sx*sx) * ((double)n * sy2 - sy*sy));
      18*:  483:	if (den == 0.0) return NAN;
       18:  483-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%:  483-block  1
       18:  484:	return num / den;
       18:  484-block  0
        -:  485:}
        -:  486:
        -:  487:/* Kendall's tau-b between two n-element arrays.
        -:  488: *
        -:  489: *   tau-b = (C − D) / sqrt((C + D + T_x)(C + D + T_y))
        -:  490: *
        -:  491: * where C = concordant pairs, D = discordant, T_x = pairs tied only on
        -:  492: * x, T_y = pairs tied only on y.  Joint ties (both zero) are excluded
        -:  493: * from numerator and denominator, matching R's cor(method="kendall").
        -:  494: * Returns NAN when the denominator is zero.                             */
function kendall_tau_b called 3 returned 100% blocks executed 80%
        3:  495:static double kendall_tau_b(const double *restrict x, const double *restrict y, unsigned int n) {
        3:  496:	size_t C = 0, D = 0, tie_x = 0, tie_y = 0;
       27:  497:	for (size_t i = 0; i < n - 1; i++) {
        3:  497-block  0
       24:  497-block  1
       27:  497-block  2
branch  0 taken 24
branch  1 taken 3 (fallthrough)
      132:  498:	  for (size_t j = i + 1; j < n; j++) {
       24:  498-block  0
      108:  498-block  1
      132:  498-block  2
branch  0 taken 108
branch  1 taken 24 (fallthrough)
      108:  499:		   int sx = (x[i] > x[j]) - (x[i] < x[j]);   /* sign of x[i]-x[j] */
      108:  500:		   int sy = (y[i] > y[j]) - (y[i] < y[j]);
     108*:  501:		   if      (sx == 0 && sy == 0) { /* joint tie — not counted */ }
      108:  501-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 105
        3:  501-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    %%%%%:  501-block  2
      108:  502:		   else if (sx == 0)            tie_x++;
      108:  502-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 105
        3:  502-block  1
     105*:  503:		   else if (sy == 0)            tie_y++;
      105:  503-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 105
    %%%%%:  503-block  1
      105:  504:		   else if (sx == sy)           C++;
      105:  504-block  0
branch  0 taken 105 (fallthrough)
branch  1 taken 0
      105:  504-block  1
    #####:  505:		   else                         D++;
    %%%%%:  505-block  0
        -:  506:	  }
        -:  507:	}
        3:  508:	double denom = sqrt((double)(C + D + tie_x) * (double)(C + D + tie_y));
       3*:  509:	if (denom == 0.0) return NAN;
        3:  509-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%:  509-block  1
        3:  510:	return (double)(C - D) / denom;
        3:  510-block  0
        -:  511:}
        -:  512:
        -:  513:/* Single dispatch: compute correlation according to method string.
        -:  514: * Allocates and frees temporary rank arrays internally for Spearman.   */
function compute_cor called 21 returned 100% blocks executed 89%
       21:  515:static double compute_cor(const double *restrict x, const double *restrict y,
        -:  516:                           size_t n, const char *restrict method) {
       21:  517:	if (strcmp(method, "spearman") == 0) {
       21:  517-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        -:  518:	  double *restrict rx, *restrict ry;
       3*:  519:	  Newx(rx, n, double); Newx(ry, n, double);
        3:  519-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%:  519-block  1
call    2 never executed
        3:  519-block  2
call    3 returned 3
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    %%%%%:  519-block  3
call    6 never executed
        3:  519-block  4
call    7 returned 3
        3:  520:	  rank_data(x, rx, n);
call    0 returned 3
        3:  521:	  rank_data(y, ry, n);
call    0 returned 3
        3:  522:	  double r = pearson_corr(rx, ry, n);
call    0 returned 3
        3:  523:	  Safefree(rx); Safefree(ry);
call    0 returned 3
call    1 returned 3
        3:  524:	  return r;
        -:  525:	}
       18:  526:	if (strcmp(method, "kendall") == 0)
       18:  526-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 15
        3:  527:	  return kendall_tau_b(x, y, n);
        3:  527-block  0
call    0 returned 3
        -:  528:	/* default: pearson */
       15:  529:	return pearson_corr(x, y, n);
       15:  529-block  0
call    0 returned 15
        -:  530:}
        -:  531:
        -:  532:// Math macros
        -:  533:#define MAX_ITER 500
        -:  534:#define EPS 3.0e-15
        -:  535:#define FPMIN 1.0e-30
        -:  536:
function _incbeta_cf called 24459 returned 100% blocks executed 71%
    24459:  537:static double _incbeta_cf(double a, double b, double x) {
        -:  538:	int m;
        -:  539:	double aa, c, d, del, h, qab, qam, qap;
    24459:  540:	qab = a + b; qap = a + 1.0; qam = a - 1.0;
    24459:  541:	c = 1.0; d = 1.0 - qab * x / qap;
   24459*:  542:	if (fabs(d) < FPMIN) d = FPMIN;
    24459:  542-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24459
    %%%%%:  542-block  1
    24459:  543:	d = 1.0 / d; h = d;
   540939:  544:	for (m = 1; m <= MAX_ITER; m++) {
    24459:  544-block  0
   516480:  544-block  1
   540939:  544-block  2
branch  0 taken 540939
branch  1 taken 0 (fallthrough)
   540939:  545:	  int m2 = 2 * m;
   540939:  546:	  aa = m * (b - m) * x / ((qam + m2) * (a + m2));
   540939:  547:	  d = 1.0 + aa * d;
  540939*:  548:	  if (fabs(d) < FPMIN) d = FPMIN;
   540939:  548-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 540939
    %%%%%:  548-block  1
   540939:  549:	  c = 1.0 + aa / c;
  540939*:  550:	  if (fabs(c) < FPMIN) c = FPMIN;
   540939:  550-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 540939
    %%%%%:  550-block  1
   540939:  551:	  d = 1.0 / d; h *= d * c;
   540939:  552:	  aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
   540939:  553:	  d = 1.0 + aa * d;
  540939*:  554:	  if (fabs(d) < FPMIN) d = FPMIN;
   540939:  554-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 540939
    %%%%%:  554-block  1
   540939:  555:	  c = 1.0 + aa / c;
  540939*:  556:	  if (fabs(c) < FPMIN) c = FPMIN;
   540939:  556-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 540939
    %%%%%:  556-block  1
   540939:  557:	  d = 1.0 / d; del = d * c; h *= del;
   540939:  558:	  if (fabs(del - 1.0) < EPS) break;
   540939:  558-block  0
branch  0 taken 24459 (fallthrough)
branch  1 taken 516480
    24459:  558-block  1
        -:  559:	}
    24459:  560:	return h;
    24459:  560-block  0
        -:  561:}
        -:  562:
function incbeta called 24582 returned 100% blocks executed 90%
    24582:  563:static double incbeta(double a, double b, double x) {
   24582*:  564:	if (x <= 0.0) return 0.0;
    24582:  564-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24582
    %%%%%:  564-block  1
    24582:  565:	if (x >= 1.0) return 1.0;
    24582:  565-block  0
branch  0 taken 123 (fallthrough)
branch  1 taken 24459
      123:  565-block  1
    24459:  566:	double bt = exp(lgamma(a + b) - lgamma(a) - lgamma(b) + a * log(x) + b * log(1.0 - x));
    24459:  567:	if (x < (a + 1.0) / (a + b + 2.0)) return bt * _incbeta_cf(a, b, x) / a;
    24459:  567-block  0
branch  0 taken 20175 (fallthrough)
branch  1 taken 4284
    20175:  567-block  1
call    2 returned 20175
     4284:  568:	return 1.0 - bt * _incbeta_cf(b, a, 1.0 - x) / b;
     4284:  568-block  0
call    0 returned 4284
        -:  569:}
        -:  570:
function get_t_pvalue called 24504 returned 100% blocks executed 92%
    24504:  571:static double get_t_pvalue(double t, double df, const char*restrict alt) {
    24504:  572:	double x = df / (df + t * t);
    24504:  573:	double prob_2tail = incbeta(df / 2.0, 0.5, x);
    24504:  573-block  0
call    0 returned 24504
    24504:  574:	if (strcmp(alt, "less") == 0) return (t < 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
branch  0 taken 6 (fallthrough)
branch  1 taken 24498
        6:  574-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 3
        3:  574-block  1
        3:  574-block  2
        6:  574-block  3
   24498*:  575:	if (strcmp(alt, "greater") == 0) return (t > 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
    24498:  575-block  0
branch  0 taken 24186 (fallthrough)
branch  1 taken 312
    24186:  575-block  1
branch  2 taken 24186 (fallthrough)
branch  3 taken 0
    24186:  575-block  2
    %%%%%:  575-block  3
    24186:  575-block  4
      312:  576:	return prob_2tail;
      312:  576-block  0
        -:  577:}
        -:  578:
        -:  579:// Bisection algorithm to find the inverse t-distribution (Critical t-value)
function qt_tail called 813 returned 100% blocks executed 94%
      813:  580:static double qt_tail(double df, double p_tail) {
      813:  581:	double low = 0.0, high = 1.0;
        -:  582:	// Find upper bound
     1929:  583:	while (get_t_pvalue(high, df, "greater") > p_tail) {
      813:  583-block  0
     1929:  583-block  1
call    0 returned 1929
branch  1 taken 1116
branch  2 taken 813 (fallthrough)
     1116:  584:	  low = high;
     1116:  585:	  high *= 2.0;
    1116*:  586:	  if (high > 1000000.0) break; /* Fallback limit */
     1116:  586-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 1116
    %%%%%:  586-block  1
        -:  587:	}
        -:  588:	// Bisect to find the root
    22254:  589:	for (unsigned short int i = 0; i < 100; i++) {
      813:  589-block  0
    21441:  589-block  1
    22254:  589-block  2
branch  0 taken 22254
branch  1 taken 0 (fallthrough)
    22254:  590:	  double mid = (low + high) / 2.0;
    22254:  591:	  double p_mid = get_t_pvalue(mid, df, "greater");
    22254:  591-block  0
call    0 returned 22254
    22254:  592:	  if (p_mid > p_tail) {
branch  0 taken 10869 (fallthrough)
branch  1 taken 11385
    10869:  593:		   low = mid;
    10869:  593-block  0
        -:  594:	  } else {
    11385:  595:		   high = mid;
    11385:  595-block  0
        -:  596:	  }
    22254:  597:	  if (high - low < 1e-8) break;
    22254:  597-block  0
branch  0 taken 813 (fallthrough)
branch  1 taken 21441
      813:  597-block  1
        -:  598:	}
      813:  599:	return (low + high) / 2.0;
      813:  599-block  0
        -:  600:}
        -:  601:
function compare_doubles called 8505 returned 100% blocks executed 100%
     8505:  602:int compare_doubles(const void *restrict a, const void *restrict b) {
     8505:  603:	double da = *(const double*restrict)a;
     8505:  604:	double db = *(const double*restrict)b;
     8505:  605:	return (da > db) - (da < db);
     8505:  605-block  0
        -:  606:}
        -:  607:/* Helper to calculate the number of bins using Sturges' formula: log2(n) + 1 */
function calculate_sturges_bins called 0 returned 0% blocks executed 0%
    #####:  608:static size_t calculate_sturges_bins(size_t n) {
    #####:  609:	if (n == 0) return 1;
    %%%%%:  609-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%:  609-block  1
    #####:  610:	return (size_t)(log((double)n) / log(2.0) + 1.0);
    %%%%%:  610-block  0
        -:  611:}
        -:  612:
        -:  613:// Logic for distributing data into bins (Optimized to O(N))
function compute_hist_logic called 15 returned 100% blocks executed 89%
       15:  614:static void compute_hist_logic(double *restrict x, size_t n, double *restrict breaks, size_t n_bins, 
        -:  615:                               size_t *restrict counts, double *restrict mids, double *restrict density) {
       15:  616:	double total_n = (double)n;
       15:  617:	double min_val = breaks[0];
      15*:  618:	double step = (n_bins > 0) ? (breaks[1] - breaks[0]) : 0.0;
       15:  618-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15:  618-block  1
    %%%%%:  618-block  2
        -:  619:	// Initialize counts and compute midpoints
       69:  620:	for (size_t i = 0; i < n_bins; i++) {
       15:  620-block  0
       69:  620-block  1
branch  0 taken 54
branch  1 taken 15 (fallthrough)
       54:  621:	  counts[i] = 0;
       54:  622:	  mids[i] = (breaks[i] + breaks[i+1]) / 2.0;
       54:  622-block  0
        -:  623:	}
        -:  624:	// Single O(N) pass to assign elements to bins
       15:  625:	if (step > 0.0) {
       15:  625-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 6
     6051:  626:	  for (size_t j = 0; j < n; j++) {
        9:  626-block  0
     6042:  626-block  1
     6051:  626-block  2
branch  0 taken 6042
branch  1 taken 9
     6042:  627:		   double val = x[j];
        -:  628:		   // Ignore out-of-bounds or invalid values
    6042*:  629:		   if (isnan(val) || isinf(val) || val < min_val) continue;
     6042:  629-block  0
branch  0 taken 6042 (fallthrough)
branch  1 taken 0
     6042:  629-block  1
branch  2 taken 6042 (fallthrough)
branch  3 taken 0
     6042:  629-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 6042
    %%%%%:  629-block  3
        -:  630:		   // Calculate initial bin index mathematically
     6042:  631:		   size_t idx = (size_t)((val - min_val) / step);
        -:  632:		   // Clamp to valid array bounds first to prevent overflow */
     6042:  633:		   if (idx >= n_bins) {
     6042:  633-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 6033
        9:  634:		       idx = n_bins - 1;
        9:  634-block  0
        -:  635:		   }
        -:  636:		   /* Adjust for exact boundaries (R's right-inclusive default: (a, b]) */
        -:  637:		   /* If value is exactly on or slightly below the lower boundary of the assigned bin, 
        -:  638:		      it belongs in the previous bin. (First bin [a, b] is inclusive on both ends) */
     6069:  639:		   while (idx > 0 && val <= breaks[idx]) {
     6042:  639-block  0
     6069:  639-block  1
branch  0 taken 5448 (fallthrough)
branch  1 taken 621
     5448:  639-block  2
branch  2 taken 27
branch  3 taken 5421 (fallthrough)
       27:  640:		       idx--;
       27:  640-block  0
        -:  641:		   }
        -:  642:		   // Conversely, if floating-point truncation placed it too low, push it up
     6042:  643:		   while (idx < n_bins - 1 && val > breaks[idx + 1]) {
     6042:  643-block  0
     6042:  643-block  1
branch  0 taken 5433 (fallthrough)
branch  1 taken 609
     5433:  643-block  2
branch  2 taken 0
branch  3 taken 5433 (fallthrough)
    #####:  644:		       idx++;
    %%%%%:  644-block  0
        -:  645:		   }
     6042:  646:		   counts[idx]++;
     6042:  646-block  0
        -:  647:	  }
        6:  648:	} else if (n_bins > 0) {
        6:  648-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        -:  649:	  // Edge case: All data points have the exact same value (step == 0)
        6:  650:	  counts[0] = n;
        6:  650-block  0
        -:  651:	}
        -:  652:	// Compute densities
       69:  653:	for (size_t i = 0; i < n_bins; i++) {
       15:  653-block  0
       54:  653-block  1
       69:  653-block  2
branch  0 taken 54
branch  1 taken 15 (fallthrough)
       54:  654:	  double bin_width = breaks[i+1] - breaks[i];
       54:  655:	  if (bin_width > 0) {
       54:  655-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 6
       48:  656:		   density[i] = (double)counts[i] / (total_n * bin_width);
       48:  656-block  0
        -:  657:	  } else {
       6*:  658:		   density[i] = (n_bins == 1) ? 1.0 : 0.0;
        6:  658-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6:  658-block  1
    %%%%%:  658-block  2
        6:  658-block  3
        -:  659:	  }
        -:  660:	}
       15:  661:}
        -:  662:
        -:  663:// Standard Normal CDF approximation
function approx_pnorm called 168 returned 100% blocks executed 100%
      168:  664:double approx_pnorm(double x) {
      168:  665:	return 0.5 * erfc(-x * 0.70710678118654752440); // 0.707... = 1/sqrt(2)
      168:  665-block  0
        -:  666:}
        -:  667:#ifndef M_SQRT1_2
        -:  668:#define M_SQRT1_2 0.70710678118654752440
        -:  669:#endif
        -:  670:
        -:  671:/* Macro for exact Wilcoxon 3D array indexing */
        -:  672:#define DP_INDEX(i, j, k, n2, max_u) ((i) * ((n2) + 1) * ((max_u) + 1) + (j) * ((max_u) + 1) + (k))
function inverse_normal_cdf called 84 returned 100% blocks executed 100%
       84:  673:static double inverse_normal_cdf(double p) {
       84:  674:	double a[4] = {2.50662823884, -18.61500062529, 41.39119773534, -25.44106049637};
       84:  675:	double b[4] = {-8.47351093090, 23.08336743743, -21.06224101826, 3.13082909833};
       84:  676:	double c[9] = {0.3374754822726147, 0.9761690190917186, 0.1607979714918209,
        -:  677:		          0.0276438810333863, 0.0038405729373609, 0.0003951896511919,
        -:  678:		          0.0000321767881768, 0.0000002888167364, 0.0000003960315187};
        -:  679:	double x, r, y;
       84:  680:	y = p - 0.5;
       84:  681:	if (fabs(y) < 0.42) {
       84:  681-block  0
branch  0 taken 66 (fallthrough)
branch  1 taken 18
       66:  682:	  r = y * y;
       66:  683:	  x = y * (((a[3]*r + a[2])*r + a[1])*r + a[0]) /
       66:  684:		       ((((b[3]*r + b[2])*r + b[1])*r + b[0])*r + 1.0);
       66:  684-block  0
        -:  685:	} else {
       18:  686:	  r = p;
       18:  687:	  if (y > 0) r = 1.0 - p;
       18:  687-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 3
       15:  687-block  1
       18:  688:	  r = log(-log(r));
       18:  689:	  x = c[0] + r * (c[1] + r * (c[2] + r * (c[3] + r * (c[4] +
       18:  690:		   r * (c[5] + r * (c[6] + r * (c[7] + r * c[8])))))));
       18:  691:	  if (y < 0) x = -x;
       18:  691-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 15
        3:  691-block  1
        -:  692:	}
       84:  693:	return x;
       84:  693-block  0
        -:  694:}
        -:  695:/* -----------------------------------------------------------------------
        -:  696: * Exact Spearman p-value via exhaustive permutation enumeration.
        -:  697: *
        -:  698: * Under H0, all n! orderings of ranks are equally probable.  We visit
        -:  699: * every permutation of {1..n} with Heap's algorithm (O(n!), no allocs
        -:  700: * inside the loop) and count how many yield S ≤ s_obs ("lower tail",
        -:  701: * i.e. rho ≥ rho_obs) and how many yield S ≥ s_obs ("upper tail").
        -:  702: *
        -:  703: * Mirrors R's default: exact = (n < 10) with no ties.
        -:  704: * Valid up to n = 9 (362 880 iterations — negligible cost).
        -:  705: * ----------------------------------------------------------------------- */
function spearman_exact_pvalue called 3 returned 100% blocks executed 88%
        3:  706:static double spearman_exact_pvalue(double s_obs, size_t n, const char *restrict alt) {
        3:  707:	int *restrict perm = (int*)safemalloc(n * sizeof(int));
        3:  707-block  0
call    0 returned 3
        3:  708:	int *restrict c    = (int*)safemalloc(n * sizeof(int));
call    0 returned 3
       18:  709:	for (size_t i = 0; i < n; i++) { perm[i] = i + 1; c[i] = 0; }
       15:  709-block  0
       18:  709-block  1
branch  0 taken 15
branch  1 taken 3 (fallthrough)
        -:  710:
        3:  711:	long count_le = 0, count_ge = 0, total = 0;
        -:  712:
        -:  713:	#define TALLY_PERM() do {                                    \
        -:  714:	  double s_ = 0.0;                                     \
        -:  715:	  for (int ii = 0; ii < n; ii++) {                    \
        -:  716:		   double d_ = (double)(ii + 1) - (double)perm[ii];\
        -:  717:		   s_ += d_ * d_;                                   \
        -:  718:	  }                                                    \
        -:  719:	  if (s_ <= s_obs + 1e-9) count_le++;                 \
        -:  720:	  if (s_ >= s_obs - 1e-9) count_ge++;                 \
        -:  721:	  total++;                                             \
        -:  722:	} while (0)
        -:  723:
      18*:  724:	TALLY_PERM();   /* initial permutation [1, 2, ..., n] */
        3:  724-block  0
       15:  724-block  1
       18:  724-block  2
branch  0 taken 15
branch  1 taken 3 (fallthrough)
        3:  724-block  3
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3:  724-block  4
        3:  724-block  5
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    %%%%%:  724-block  6
        -:  725:
        3:  726:	unsigned int k = 1;
      618:  727:	while (k < n) {
        3:  727-block  0
      618:  727-block  1
branch  0 taken 615
branch  1 taken 3 (fallthrough)
      615:  728:	  if (c[k] < k) {
      615:  728-block  0
branch  0 taken 357 (fallthrough)
branch  1 taken 258
        -:  729:		   int tmp;
      357:  730:		   if (k % 2 == 0) {
      357:  730-block  0
branch  0 taken 132 (fallthrough)
branch  1 taken 225
      132:  731:		       tmp = perm[0]; perm[0] = perm[k]; perm[k] = tmp;
      132:  731-block  0
        -:  732:		   } else {
      225:  733:		       tmp = perm[c[k]]; perm[c[k]] = perm[k]; perm[k] = tmp;
      225:  733-block  0
        -:  734:		   }
     2142:  735:		   TALLY_PERM();
      357:  735-block  0
     1785:  735-block  1
     2142:  735-block  2
branch  0 taken 1785
branch  1 taken 357 (fallthrough)
      357:  735-block  3
branch  2 taken 21 (fallthrough)
branch  3 taken 336
       21:  735-block  4
      357:  735-block  5
branch  4 taken 345 (fallthrough)
branch  5 taken 12
      345:  735-block  6
      357:  736:		   c[k]++;
      357:  737:		   k = 1;
      357:  737-block  0
        -:  738:	  } else {
      258:  739:		   c[k] = 0;
      258:  740:		   k++;
      258:  740-block  0
        -:  741:	  }
        -:  742:	}
        -:  743:	#undef TALLY_PERM
        -:  744:
        3:  745:	Safefree(perm); Safefree(c);
        3:  745-block  0
call    0 returned 3
call    1 returned 3
        -:  746:	/* p_le = P(S ≤ s_obs) ≡ P(rho ≥ rho_obs)  — upper rho tail
        -:  747:	* p_ge = P(S ≥ s_obs) ≡ P(rho ≤ rho_obs)  — lower rho tail  */
        3:  748:	double p_le = (double)count_le / (double)total;
        3:  749:	double p_ge = (double)count_ge / (double)total;
        -:  750:
       3*:  751:	if (strcmp(alt, "greater") == 0) return p_le;
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%:  751-block  0
       3*:  752:	if (strcmp(alt, "less")    == 0) return p_ge;
        3:  752-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%:  752-block  1
        -:  753:	/* two.sided: 2 × the smaller tail, clamped to 1 */
       3*:  754:	double p = 2.0 * (p_le < p_ge ? p_le : p_ge);
        3:  754-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3:  754-block  1
    %%%%%:  754-block  2
       3*:  755:	return (p > 1.0) ? 1.0 : p;
        3:  755-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%:  755-block  1
        3:  755-block  2
        3:  755-block  3
        -:  756:}
        -:  757:/* -----------------------------------------------------------------------
        -:  758: * Exact Kendall p-value via Mahonian Numbers (Inversions distribution)
        -:  759: * Matches R's behavior for N < 50 without ties.
        -:  760: * ----------------------------------------------------------------------- */
function kendall_exact_pvalue called 6 returned 100% blocks executed 88%
        6:  761:static double kendall_exact_pvalue(size_t n, double s_obs, const char *restrict alt) {
        6:  762:	long max_inv = (long)n * (n - 1) / 2;
        6:  763:	double *restrict dp = (double*)safemalloc((max_inv + 1) * sizeof(double));
        6:  763-block  0
call    0 returned 6
       72:  764:	for (long i = 0; i <= max_inv; i++) dp[i] = 0.0;
       66:  764-block  0
       72:  764-block  1
branch  0 taken 66
branch  1 taken 6 (fallthrough)
        6:  765:	dp[0] = 1.0;
        -:  766:	/* Build the distribution of inversions via DP */
       30:  767:	for (size_t i = 2; i <= n; i++) {
        6:  767-block  0
       30:  767-block  1
branch  0 taken 24
branch  1 taken 6 (fallthrough)
       24:  768:	  double *restrict next_dp = (double*)safemalloc((max_inv + 1) * sizeof(double));
       24:  768-block  0
call    0 returned 24
      288:  769:	  for (long k = 0; k <= max_inv; k++) next_dp[k] = 0.0;
      264:  769-block  0
      288:  769-block  1
branch  0 taken 264
branch  1 taken 24 (fallthrough)
       24:  770:	  int current_max_inv = i * (i - 1) / 2;
      168:  771:	  for (int k = 0; k <= current_max_inv; k++) {
       24:  771-block  0
      168:  771-block  1
branch  0 taken 144
branch  1 taken 24 (fallthrough)
      144:  772:		   double sum = 0;
      618:  773:		   for (int j = 0; j <= i - 1 && k - j >= 0; j++) {
      144:  773-block  0
      618:  773-block  1
branch  0 taken 534 (fallthrough)
branch  1 taken 84
      534:  773-block  2
branch  2 taken 474
branch  3 taken 60 (fallthrough)
      474:  774:		       sum += dp[k - j];
      474:  774-block  0
        -:  775:		   }
        -:  776:		   // Divide by 'i' directly to keep array as pure probabilities and prevent overflow
      144:  777:		   next_dp[k] = sum / (double)i;
      144:  777-block  0
        -:  778:	  }
       24:  779:	  Safefree(dp);
       24:  779-block  0
call    0 returned 24
       24:  780:	  dp = next_dp;
        -:  781:	}
        -:  782:	// Convert S statistic to target number of inversions
        6:  783:	long i_obs = (long)round((max_inv - s_obs) / 2.0);
       6*:  784:	if (i_obs < 0) i_obs = 0;
        6:  784-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  784-block  1
       6*:  785:	if (i_obs > max_inv) i_obs = max_inv;
        6:  785-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  785-block  1
        6:  786:	double p_le = 0.0; /* P(S <= S_obs) */
       60:  787:	for (long k = i_obs; k <= max_inv; k++) p_le += dp[k];
        6:  787-block  0
       54:  787-block  1
       60:  787-block  2
branch  0 taken 54
branch  1 taken 6 (fallthrough)
        6:  788:	double p_ge = 0.0; /* P(S >= S_obs) */
       24:  789:	for (long k = 0; k <= i_obs; k++) p_ge += dp[k];
        6:  789-block  0
       18:  789-block  1
       24:  789-block  2
branch  0 taken 18
branch  1 taken 6 (fallthrough)
        6:  790:	Safefree(dp);
        6:  790-block  0
call    0 returned 6
       6*:  791:	if (strcmp(alt, "greater") == 0) return p_ge;
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  791-block  0
        6:  792:	if (strcmp(alt, "less") == 0) return p_le;
        6:  792-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3:  792-block  1
        -:  793:	// two.sided
       3*:  794:	double p = 2.0 * (p_ge < p_le ? p_ge : p_le);
        3:  794-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3:  794-block  1
    %%%%%:  794-block  2
       3*:  795:	return p > 1.0 ? 1.0 : p;
        3:  795-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%:  795-block  1
        3:  795-block  2
        3:  795-block  3
        -:  796:}
        -:  797:// F-distribution Cumulative Distribution Function P(F <= f)
function pf called 78 returned 100% blocks executed 75%
       78:  798:static double pf(double f, double df1, double df2) {
      78*:  799:	if (f <= 0.0) return 0.0;
       78:  799-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 78
    %%%%%:  799-block  1
       78:  800:	double x = (df1 * f) / (df1 * f + df2);
       78:  801:	return incbeta(df1 / 2.0, df2 / 2.0, x);
       78:  801-block  0
call    0 returned 78
        -:  802:}
        -:  803:
        -:  804:/* Householder QR Decomposition for Sequential Sums of Squares */
        -:  805:/* Householder QR Decomposition for Sequential Sums of Squares */
function apply_householder_aov called 18 returned 100% blocks executed 97%
       18:  806: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) {
       18:  807:	size_t r = 0; // Rank/Row tracker
       72:  808:	for (size_t k = 0; k < p; k++) {
       18:  808-block  0
       54:  808-block  1
       72:  808-block  2
branch  0 taken 54
branch  1 taken 18 (fallthrough)
       54:  809:		aliased[k] = false;
      54*:  810:		if (r >= n) {
       54:  810-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 54
    #####:  811:			aliased[k] = true;
    #####:  812:			continue;
    %%%%%:  812-block  0
        -:  813:		}
        -:  814:
       54:  815:		double max_val = 0;
      489:  816:		for (size_t i = r; i < n; i++) {
       54:  816-block  0
      435:  816-block  1
      489:  816-block  2
branch  0 taken 435
branch  1 taken 54 (fallthrough)
      435:  817:			if (fabs(X[i][k]) > max_val) max_val = fabs(X[i][k]);
      435:  817-block  0
branch  0 taken 108 (fallthrough)
branch  1 taken 327
      108:  817-block  1
        -:  818:		}
       54:  819:		if (max_val < 1e-10) { 
       54:  819-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 51
        3:  820:			aliased[k] = true; 
        3:  821:			continue; 
        3:  821-block  0
        -:  822:		} // Collinear or zero column
        -:  823:
       51:  824:		double norm = 0;
      477:  825:		for (size_t i = r; i < n; i++) {
       51:  825-block  0
      477:  825-block  1
branch  0 taken 426
branch  1 taken 51 (fallthrough)
      426:  826:			X[i][k] /= max_val;
      426:  827:			norm += X[i][k] * X[i][k];
      426:  827-block  0
        -:  828:		}
       51:  829:		norm = sqrt(norm);
       51:  830:		double s = (X[r][k] > 0) ? -norm : norm;
       51:  830-block  0
branch  0 taken 27 (fallthrough)
branch  1 taken 24
       27:  830-block  1
       24:  830-block  2
       51:  831:		double u1 = X[r][k] - s;
       51:  832:		X[r][k] = s * max_val;
        -:  833:
      108:  834:		for (size_t j = k + 1; j < p; j++) {
       51:  834-block  0
       57:  834-block  1
      108:  834-block  2
branch  0 taken 57
branch  1 taken 51 (fallthrough)
       57:  835:			double dot = u1 * X[r][j];
      570:  836:			for (size_t i = r + 1; i < n; i++) dot += X[i][j] * X[i][k];
       57:  836-block  0
      513:  836-block  1
      570:  836-block  2
branch  0 taken 513
branch  1 taken 57 (fallthrough)
       57:  837:			double tau = dot / (s * u1);
       57:  838:			X[r][j] += tau * u1;
      570:  839:			for (size_t i = r + 1; i < n; i++) X[i][j] += tau * X[i][k];
       57:  839-block  0
      513:  839-block  1
      570:  839-block  2
branch  0 taken 513
branch  1 taken 57 (fallthrough)
        -:  840:		}
        -:  841:
        -:  842:		// Transform the response vector y
       51:  843:		double dot_y = u1 * y[r];
      426:  844:		for (size_t i = r + 1; i < n; i++) dot_y += y[i] * X[i][k];
       51:  844-block  0
      375:  844-block  1
      426:  844-block  2
branch  0 taken 375
branch  1 taken 51 (fallthrough)
       51:  845:		double tau_y = dot_y / (s * u1);
       51:  846:		y[r] += tau_y * u1;
      426:  847:		for (size_t i = r + 1; i < n; i++) y[i] += tau_y * X[i][k];
       51:  847-block  0
      375:  847-block  1
      426:  847-block  2
branch  0 taken 375
branch  1 taken 51 (fallthrough)
        -:  848:
       51:  849:		rank_map[k] = r; // Map original column index to orthogonal row index
       51:  850:		r++;
       51:  850-block  0
        -:  851:	}
       18:  852:}
        -:  853:
        -:  854:// --- write_table Helpers ---
        -:  855:
        -:  856:// Sorts string arrays alphabetically
function cmp_string_wt called 75 returned 100% blocks executed 100%
       75:  857:static int cmp_string_wt(const void *a, const void *b) {
       75:  858:	return strcmp(*(const char**)a, *(const char**)b);
       75:  858-block  0
        -:  859:}
        -:  860:
        -:  861:// Emulates Perl's /\D/ check
function contains_nondigit called 13 returned 100% blocks executed 83%
       13:  862:static bool contains_nondigit(SV *restrict sv) {
      13*:  863:	if (!sv || !SvOK(sv)) return 0;
       13:  863-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 0
       13:  863-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 13
    %%%%%:  863-block  2
        -:  864:	STRLEN len;
       13:  865:	const char *restrict s = SvPVbyte(sv, len);
       13:  865-block  0
call    0 returned 13
       26:  866:	for (size_t i = 0; i < len; i++) {
       13:  866-block  0
       26:  866-block  1
branch  0 taken 13
branch  1 taken 13 (fallthrough)
      13*:  867:	  if (!isdigit(s[i])) return 1;
       13:  867-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 13
    %%%%%:  867-block  1
        -:  868:	}
       13:  869:	return 0;
       13:  869-block  0
        -:  870:}
        -:  871:
        -:  872:// Writes a properly quoted string dynamically
function print_str_quoted called 522 returned 100% blocks executed 95%
      522:  873:static void print_str_quoted(PerlIO *fh, const char *str, const char *sep) {
     522*:  874:	if (!str) str = "";
      522:  874-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 522
    %%%%%:  874-block  1
      522:  875:	bool needs_quotes = 0;
      522:  876:	if (strstr(str, sep) != NULL || strchr(str, '"') != NULL || strchr(str, '\r') != NULL || strchr(str, '\n') != NULL) {
      522:  876-block  0
branch  0 taken 507 (fallthrough)
branch  1 taken 15
      507:  876-block  1
branch  2 taken 489 (fallthrough)
branch  3 taken 18
      489:  876-block  2
branch  4 taken 489 (fallthrough)
branch  5 taken 0
      489:  876-block  3
branch  6 taken 0 (fallthrough)
branch  7 taken 489
       33:  877:	  needs_quotes = 1;
       33:  877-block  0
        -:  878:	}
        -:  879:
      522:  880:	if (needs_quotes) {
      522:  880-block  0
branch  0 taken 33 (fallthrough)
branch  1 taken 489
       33:  881:	  PerlIO_putc(fh, '"');
       33:  881-block  0
call    0 returned 33
      354:  882:	  for (const char *restrict p = str; *p; p++) {
      321:  882-block  0
      354:  882-block  1
branch  0 taken 321
branch  1 taken 33 (fallthrough)
      321:  883:		   if (*p == '"') {
      321:  883-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 300
       21:  884:		       PerlIO_putc(fh, '"');
       21:  884-block  0
call    0 returned 21
       21:  885:		       PerlIO_putc(fh, '"');
call    0 returned 21
        -:  886:		   } else {
      300:  887:		       PerlIO_putc(fh, *p);
      300:  887-block  0
call    0 returned 300
        -:  888:		   }
        -:  889:	  }
       33:  890:	  PerlIO_putc(fh, '"');
       33:  890-block  0
call    0 returned 33
        -:  891:	} else {
      489:  892:	  PerlIO_puts(fh, str);
      489:  892-block  0
call    0 returned 489
        -:  893:	}
      522:  894:}
        -:  895:
        -:  896:// Writes an array of strings joined by sep
function print_string_row called 141 returned 100% blocks executed 90%
      141:  897:static void print_string_row(PerlIO *fh, const char **row, size_t len, const char *sep) {
      141:  898:	size_t sep_len = strlen(sep);
      663:  899:	for (size_t i = 0; i < len; i++) {
      141:  899-block  0
      522:  899-block  1
      663:  899-block  2
branch  0 taken 522
branch  1 taken 141 (fallthrough)
      522:  900:	  if (i > 0) PerlIO_write(fh, sep, sep_len);
      522:  900-block  0
branch  0 taken 381 (fallthrough)
branch  1 taken 141
      381:  900-block  1
call    2 returned 381
      522:  901:	  if (row[i]) {
      522:  901-block  0
branch  0 taken 522 (fallthrough)
branch  1 taken 0
      522:  902:		   print_str_quoted(fh, row[i], sep);
      522:  902-block  0
call    0 returned 522
        -:  903:	  } else {
    #####:  904:		   print_str_quoted(fh, "", sep);
    %%%%%:  904-block  0
call    0 never executed
        -:  905:	  }
        -:  906:	}
      141:  907:	PerlIO_putc(fh, '\n');
      141:  907-block  0
call    0 returned 141
      141:  908:}
        -:  909:// Calculates the Regularized Upper Incomplete Gamma Function Q(a, x)
        -:  910:// This perfectly replicates R's pchisq(..., lower.tail=FALSE)
function igamc called 17 returned 100% blocks executed 81%
       17:  911:double igamc(double a, double x) {
      17*:  912:	if (x < 0.0 || a <= 0.0) return 1.0;
       17:  912-block  0
branch  0 taken 17 (fallthrough)
branch  1 taken 0
       17:  912-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 17
    %%%%%:  912-block  2
      17*:  913:	if (x == 0.0) return 1.0;
       17:  913-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 17
    %%%%%:  913-block  1
        -:  914:
        -:  915:	// Series expansion for x < a + 1
       17:  916:	if (x < a + 1.0) {
       17:  916-block  0
branch  0 taken 8 (fallthrough)
branch  1 taken 9
        8:  917:		double sum = 1.0 / a;
        8:  918:		double term = 1.0 / a;
        8:  919:		double n = 1.0;
      112:  920:		while (fabs(term) > 1e-15) {
        8:  920-block  0
      112:  920-block  1
branch  0 taken 104
branch  1 taken 8 (fallthrough)
      104:  921:			term *= x / (a + n);
      104:  922:			sum += term;
      104:  923:			n += 1.0;
      104:  923-block  0
        -:  924:		}
        8:  925:		return 1.0 - (sum * exp(-x + a * log(x) - lgamma(a)));
        8:  925-block  0
        -:  926:	}
        -:  927:
        -:  928:	// Continued fraction for x >= a + 1
        9:  929:	double b = x + 1.0 - a;
        9:  930:	double c = 1.0 / 1e-30;
        9:  931:	double d = 1.0 / b;
        9:  932:	double h = d, i = 1.0;
      141:  933:	while (i < 10000) { // Safety bound
        9:  933-block  0
      141:  933-block  1
branch  0 taken 141
branch  1 taken 0 (fallthrough)
      141:  934:		double an = -i * (i - a);
      141:  935:		b += 2.0;
      141:  936:		d = an * d + b;
     141*:  937:		if (fabs(d) < 1e-30) d = 1e-30;
      141:  937-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 141
    %%%%%:  937-block  1
      141:  938:		c = b + an / c;
     141*:  939:		if (fabs(c) < 1e-30) c = 1e-30;
      141:  939-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 141
    %%%%%:  939-block  1
      141:  940:		d = 1.0 / d;
      141:  941:		double del = d * c;
      141:  942:		h *= del;
      141:  943:		if (fabs(del - 1.0) < 1e-15) break;
      141:  943-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 132
        9:  943-block  1
      132:  944:		i += 1.0;
      132:  944-block  0
        -:  945:	}
        9:  946:	return h * exp(-x + a * log(x) - lgamma(a));
        9:  946-block  0
        -:  947:}
        -:  948:
        -:  949:// Chi-Squared p-value is simply the Incomplete Gamma of (df/2, stat/2)
function get_p_value called 17 returned 100% blocks executed 67%
       17:  950:double get_p_value(double stat, int df) {
      17*:  951:	if (df <= 0) return 1.0;
       17:  951-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 17
    %%%%%:  951-block  1
      17*:  952:	if (stat <= 0.0) return 1.0;
       17:  952-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 17
    %%%%%:  952-block  1
       17:  953:	return igamc((double)df / 2.0, stat / 2.0);
       17:  953-block  0
call    0 returned 17
        -:  954:}
        -:  955:
        -:  956:/* --- C HELPER SECTION --- */
        -:  957:#ifndef M_SQRT1_2
        -:  958:#define M_SQRT1_2 0.70710678118654752440
        -:  959:#endif
        -:  960:
        -:  961:/* Robust Binomial Coefficient using long double */
function choose_comb called 6 returned 100% blocks executed 80%
        6:  962:static long double choose_comb(int n, int k) {
       6*:  963:	if (k < 0 || k > n) return 0.0L;
        6:  963-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6:  963-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    %%%%%:  963-block  2
       6*:  964:	if (k > n / 2) k = n - k;
        6:  964-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  964-block  1
        6:  965:	long double res = 1.0L;
       24:  966:	for (int i = 1; i <= k; i++) {
        6:  966-block  0
       24:  966-block  1
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18:  967:	  res = res * (long double)(n - i + 1) / (long double)i;
       18:  967-block  0
        -:  968:	}
        6:  969:	return res;
        6:  969-block  0
        -:  970:}
        -:  971:
        -:  972:/* Exact CDF for Mann-Whitney U: P(U <= q) 
        -:  973:   Mathematically identical to R's cwilcox generating function */
function exact_pwilcox called 12 returned 100% blocks executed 95%
       12:  974:static double exact_pwilcox(double q, int m, int n) {
       12:  975:    int k = (int)floor(q + 1e-7); // R uses 1e-7 fuzz
       12:  976:    int max_u = m * n;
       12:  977:    if (k < 0) return 0.0;
       12:  977-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 6
        6:  977-block  1
       6*:  978:    if (k >= max_u) return 1.0;
        6:  978-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%:  978-block  1
        -:  979:
        6:  980:    long double *restrict w = (long double *)safecalloc(max_u + 1, sizeof(long double));
        6:  980-block  0
call    0 returned 6
        6:  981:    w[0] = 1.0L;
        -:  982:
       24:  983:    for (int j = 1; j <= n; j++) {
       18:  983-block  0
       24:  983-block  1
branch  0 taken 18
branch  1 taken 6 (fallthrough)
      162:  984:        for (int i = j; i <= max_u; i++) w[i] += w[i - j];
       18:  984-block  0
      144:  984-block  1
      162:  984-block  2
branch  0 taken 144
branch  1 taken 18 (fallthrough)
      108:  985:        for (int i = max_u; i >= j + m; i--) w[i] -= w[i - j - m];
       18:  985-block  0
       90:  985-block  1
      108:  985-block  2
branch  0 taken 90
branch  1 taken 18 (fallthrough)
        -:  986:    }
        -:  987:
        6:  988:    long double cum_p = 0.0L;
       12:  989:    for (int i = 0; i <= k; i++) cum_p += w[i];
        6:  989-block  0
        6:  989-block  1
       12:  989-block  2
branch  0 taken 6
branch  1 taken 6 (fallthrough)
        -:  990:    
        6:  991:    long double total = choose_comb(m + n, n);
        6:  991-block  0
call    0 returned 6
        6:  992:    double result = (double)(cum_p / total);
        -:  993:    
        6:  994:    Safefree(w);
call    0 returned 6
        6:  995:    return result;
        -:  996:}
        -:  997:
        -:  998:/* Exact CDF for Wilcoxon Signed Rank: P(V <= q) 
        -:  999:   Mathematically identical to R's csignrank subset-sum DP */
function exact_psignrank called 18 returned 100% blocks executed 94%
       18: 1000:static double exact_psignrank(double q, int n) {
       18: 1001:	int k = (int)floor(q + 1e-7);
       18: 1002:	int max_v = n * (n + 1) / 2;
      18*: 1003:	if (k < 0) return 0.0;
       18: 1003-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 1003-block  1
       18: 1004:	if (k >= max_v) return 1.0;
       18: 1004-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 15
        3: 1004-block  1
        -: 1005:
       15: 1006:	long double *restrict w = (long double *)safecalloc(max_v + 1, sizeof(long double));
       15: 1006-block  0
call    0 returned 15
       15: 1007:	w[0] = 1.0L;
        -: 1008:
      138: 1009:	for (int i = 1; i <= n; i++) {
      123: 1009-block  0
      138: 1009-block  1
branch  0 taken 123
branch  1 taken 15 (fallthrough)
     4746: 1010:	  for (int j = max_v; j >= i; j--) w[j] += w[j - i];
      123: 1010-block  0
     4623: 1010-block  1
     4746: 1010-block  2
branch  0 taken 4623
branch  1 taken 123 (fallthrough)
        -: 1011:	}
        -: 1012:
       15: 1013:	long double cum_p = 0.0L;
      546: 1014:	for (int i = 0; i <= k; i++) cum_p += w[i];
       15: 1014-block  0
      531: 1014-block  1
      546: 1014-block  2
branch  0 taken 531
branch  1 taken 15 (fallthrough)
        -: 1015:
       15: 1016:	long double total = powl(2.0L, (long double)n);
       15: 1017:	double result = (double)(cum_p / total);
        -: 1018:
       15: 1019:	Safefree(w);
       15: 1019-block  0
call    0 returned 15
       15: 1020:	return result;
        -: 1021:}
        -: 1022:
function cmp_rank_info called 860 returned 100% blocks executed 100%
      860: 1023:static int cmp_rank_info(const void *a, const void *b) {
      860: 1024:	double da = ((const RankInfo*)a)->val;
      860: 1025:	double db = ((const RankInfo*)b)->val;
      860: 1026:	return (da > db) - (da < db);
      860: 1026-block  0
        -: 1027:}
        -: 1028:
function rank_and_count_ties called 32 returned 100% blocks executed 94%
       32: 1029:static double rank_and_count_ties(RankInfo *restrict ri, size_t n, bool *restrict has_ties) {
      32*: 1030:	if (n == 0) return 0.0;
       32: 1030-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 32
    %%%%%: 1030-block  1
       32: 1031:	qsort(ri, n, sizeof(RankInfo), cmp_rank_info);
       32: 1031-block  0
call    0 returned 32
       32: 1032:	size_t i = 0;
       32: 1033:	double tie_adj = 0.0;
       32: 1034:	*has_ties = 0;
      357: 1035:	while (i < n) {
      357: 1035-block  0
branch  0 taken 325
branch  1 taken 32 (fallthrough)
      325: 1036:		size_t j = i + 1;
      349: 1037:		while (j < n && ri[j].val == ri[i].val) j++;
      325: 1037-block  0
       24: 1037-block  1
      349: 1037-block  2
branch  0 taken 317 (fallthrough)
branch  1 taken 32
      317: 1037-block  3
branch  2 taken 24
branch  3 taken 293 (fallthrough)
      325: 1038:		double r = (double)(i + 1 + j) / 2.0; 
      674: 1039:		for (size_t k = i; k < j; k++) ri[k].rank = r;
      325: 1039-block  0
      349: 1039-block  1
      674: 1039-block  2
branch  0 taken 349
branch  1 taken 325 (fallthrough)
      325: 1040:		size_t t = j - i;
      325: 1041:		if (t > 1) { *has_ties = 1; tie_adj += ((double)t * t * t - t); }
      325: 1041-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 307
       18: 1041-block  1
      325: 1042:		i = j;
      325: 1042-block  0
        -: 1043:	}
       32: 1044:	return tie_adj;
       32: 1044-block  0
        -: 1045:}
        -: 1046:/* --- KS-TEST C HELPER SECTION --- */
        -: 1047:#ifndef M_PI_2
        -: 1048:#define M_PI_2 1.57079632679489661923
        -: 1049:#endif
        -: 1050:#ifndef M_PI_4
        -: 1051:#define M_PI_4 0.78539816339744830962
        -: 1052:#endif
        -: 1053:#ifndef M_1_SQRT_2PI
        -: 1054:#define M_1_SQRT_2PI 0.39894228040143267794
        -: 1055:#endif
        -: 1056:
        -: 1057:// Scalar integer power used by K2x
function r_pow_di called 117 returned 100% blocks executed 70%
      117: 1058:static double r_pow_di(double x, int n) {
     117*: 1059:	if (n == 0) return 1.0;
      117: 1059-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 117
    %%%%%: 1059-block  1
     117*: 1060:	if (n < 0) return 1.0 / r_pow_di(x, -n);
      117: 1060-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 117
    %%%%%: 1060-block  1
call    2 never executed
      117: 1061:	double val = 1.0;
     1314: 1062:	for (int i = 0; i < n; i++) val *= x;
      117: 1062-block  0
     1197: 1062-block  1
     1314: 1062-block  2
branch  0 taken 1197
branch  1 taken 117 (fallthrough)
      117: 1063:	return val;
      117: 1063-block  0
        -: 1064:}
        -: 1065:
        -: 1066:// Two-sample two-sided asymptotic distribution
function K2l called 0 returned 0% blocks executed 0%
    #####: 1067:static double K2l(double x, int lower, double tol) {
        -: 1068:	double s, z, p;
        -: 1069:	int k;
    #####: 1070:	if(x <= 0.) {
    %%%%%: 1070-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1071:	  if(lower) p = 0.;
    %%%%%: 1071-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1071-block  1
    #####: 1072:	  else p = 1.;
    %%%%%: 1072-block  0
    #####: 1073:	} else if(x < 1.) {
    %%%%%: 1073-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1074:	  int k_max = (int) sqrt(2.0 - log(tol));
    #####: 1075:	  double w = log(x);
    #####: 1076:	  z = - (M_PI_2 * M_PI_4) / (x * x);
    #####: 1077:	  s = 0;
    #####: 1078:	  for(k = 1; k < k_max; k += 2) {
    %%%%%: 1078-block  0
    %%%%%: 1078-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 1079:		   s += exp(k * k * z - w);
    %%%%%: 1079-block  0
        -: 1080:	  }
    #####: 1081:	  p = s / M_1_SQRT_2PI;
    #####: 1082:	  if(!lower) p = 1.0 - p;
    %%%%%: 1082-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1082-block  1
        -: 1083:	} else {
        -: 1084:	  double new_val, old_val;
    #####: 1085:	  z = -2.0 * x * x;
    #####: 1086:	  s = -1.0;
    #####: 1087:	  if(lower) {
    %%%%%: 1087-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1088:		   k = 1; old_val = 0.0; new_val = 1.0;
    %%%%%: 1088-block  0
        -: 1089:	  } else {
    #####: 1090:		   k = 2; old_val = 0.0; new_val = 2.0 * exp(z);
    %%%%%: 1090-block  0
        -: 1091:	  }
    #####: 1092:	  while(fabs(old_val - new_val) > tol) {
    %%%%%: 1092-block  0
    %%%%%: 1092-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 1093:		   old_val = new_val;
    #####: 1094:		   new_val += 2.0 * s * exp(z * k * k);
    #####: 1095:		   s *= -1.0;
    #####: 1096:		   k++;
    %%%%%: 1096-block  0
        -: 1097:	  }
    #####: 1098:	  p = new_val;
    %%%%%: 1098-block  0
        -: 1099:	}
    #####: 1100:	return p;
    %%%%%: 1100-block  0
        -: 1101:}
        -: 1102:
        -: 1103:// Auxiliary routines used by K2x() for matrix operations
function m_multiply called 21 returned 100% blocks executed 100%
       21: 1104:static void m_multiply(double *A, double *B, double *C, unsigned int m) {
      420: 1105:	for(unsigned int i = 0; i < m; i++) {
       21: 1105-block  0
      399: 1105-block  1
      420: 1105-block  2
branch  0 taken 399
branch  1 taken 21 (fallthrough)
     7980: 1106:	  for(unsigned int j = 0; j < m; j++) {
      399: 1106-block  0
     7980: 1106-block  1
branch  0 taken 7581
branch  1 taken 399 (fallthrough)
     7581: 1107:		   double s = 0.;
   151620: 1108:		   for(unsigned int k = 0; k < m; k++) s += A[i * m + k] * B[k * m + j];
     7581: 1108-block  0
   144039: 1108-block  1
   151620: 1108-block  2
branch  0 taken 144039
branch  1 taken 7581 (fallthrough)
     7581: 1109:		   C[i * m + j] = s;
     7581: 1109-block  0
        -: 1110:	  }
        -: 1111:	}
       21: 1112:}
        -: 1113:
function m_power called 18 returned 100% blocks executed 82%
       18: 1114:static void m_power(double *A, int eA, double *V, int *eV, int m, int n) {
       18: 1115:    if(n == 1) {
       18: 1115-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 15
     1086: 1116:        for(int i = 0; i < m * m; i++) V[i] = A[i];
        3: 1116-block  0
     1083: 1116-block  1
     1086: 1116-block  2
branch  0 taken 1083
branch  1 taken 3 (fallthrough)
        3: 1117:        *eV = eA;
        3: 1118:        return;
        3: 1118-block  0
        -: 1119:    }
       15: 1120:    m_power(A, eA, V, eV, m, n / 2);
       15: 1120-block  0
call    0 returned 15
       15: 1121:    double *restrict B = (double*) safecalloc(m * m, sizeof(double));
call    0 returned 15
       15: 1122:    m_multiply(V, V, B, m);
call    0 returned 15
       15: 1123:    int eB = 2 * (*eV);
       15: 1124:    if((n % 2) == 0) {
branch  0 taken 9 (fallthrough)
branch  1 taken 6
     3258: 1125:        for(int i = 0; i < m * m; i++) V[i] = B[i];
        9: 1125-block  0
     3249: 1125-block  1
     3258: 1125-block  2
branch  0 taken 3249
branch  1 taken 9 (fallthrough)
        9: 1126:        *eV = eB;
        9: 1126-block  0
        -: 1127:    } else {
        6: 1128:        m_multiply(A, B, V, m);
        6: 1128-block  0
call    0 returned 6
        6: 1129:        *eV = eA + eB;
        -: 1130:    }
       15: 1131:    if(V[(m / 2) * m + (m / 2)] > 1e140) {
       15: 1131-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    #####: 1132:        for(int i = 0; i < m * m; i++) V[i] = V[i] * 1e-140;
    %%%%%: 1132-block  0
    %%%%%: 1132-block  1
    %%%%%: 1132-block  2
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 1133:        *eV += 140;
    %%%%%: 1133-block  0
        -: 1134:    }
       15: 1135:    Safefree(B);
       15: 1135-block  0
call    0 returned 15
        -: 1136:}
        -: 1137:
        -: 1138:// One-sample two-sided exact distribution
function K2x called 3 returned 100% blocks executed 95%
        3: 1139:static double K2x(int n, double d) {
        3: 1140:	int k = (int) (n * d) + 1;
        3: 1141:	int m = 2 * k - 1;
        3: 1142:	double h = k - n * d;
        3: 1143:	double *restrict H = (double*) safecalloc(m * m, sizeof(double));
        3: 1143-block  0
call    0 returned 3
        3: 1144:	double *restrict Q = (double*) safecalloc(m * m, sizeof(double));
call    0 returned 3
        -: 1145:
       60: 1146:	for(int i = 0; i < m; i++) {
       57: 1146-block  0
       60: 1146-block  1
branch  0 taken 57
branch  1 taken 3 (fallthrough)
     1140: 1147:	  for(int j = 0; j < m; j++) {
       57: 1147-block  0
     1083: 1147-block  1
     1140: 1147-block  2
branch  0 taken 1083
branch  1 taken 57 (fallthrough)
     1083: 1148:		   if(i - j + 1 < 0) H[i * m + j] = 0;
     1083: 1148-block  0
branch  0 taken 459 (fallthrough)
branch  1 taken 624
      459: 1148-block  1
      624: 1149:		   else H[i * m + j] = 1;
      624: 1149-block  0
        -: 1150:	  }
        -: 1151:	}
       60: 1152:	for(int i = 0; i < m; i++) {
        3: 1152-block  0
       60: 1152-block  1
branch  0 taken 57
branch  1 taken 3 (fallthrough)
       57: 1153:	  H[i * m] -= r_pow_di(h, i + 1);
       57: 1153-block  0
call    0 returned 57
       57: 1154:	  H[(m - 1) * m + i] -= r_pow_di(h, (m - i));
call    0 returned 57
        -: 1155:	}
       3*: 1156:	H[(m - 1) * m] += ((2 * h - 1 > 0) ? r_pow_di(2 * h - 1, m) : 0);
        3: 1156-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1156-block  1
call    2 returned 3
    %%%%%: 1156-block  2
        -: 1157:
       60: 1158:	for(int i = 0; i < m; i++) {
        3: 1158-block  0
       57: 1158-block  1
       60: 1158-block  2
branch  0 taken 57
branch  1 taken 3 (fallthrough)
     1140: 1159:	  for(int j = 0; j < m; j++) {
       57: 1159-block  0
     1083: 1159-block  1
     1140: 1159-block  2
branch  0 taken 1083
branch  1 taken 57 (fallthrough)
     1083: 1160:		   if(i - j + 1 > 0) {
     1083: 1160-block  0
branch  0 taken 570 (fallthrough)
branch  1 taken 513
     4560: 1161:		       for(int g = 1; g <= i - j + 1; g++) H[i * m + j] /= g;
      570: 1161-block  0
     3990: 1161-block  1
     4560: 1161-block  2
branch  0 taken 3990
branch  1 taken 570 (fallthrough)
        -: 1162:		   }
        -: 1163:	  }
        -: 1164:	}
        -: 1165:
        3: 1166:	int eH = 0, eQ;
        3: 1167:	m_power(H, eH, Q, &eQ, m, n);
        3: 1167-block  0
call    0 returned 3
        3: 1168:	double s = Q[(k - 1) * m + k - 1];
        -: 1169:
      153: 1170:	for(int i = 1; i <= n; i++) {
      150: 1170-block  0
      153: 1170-block  1
branch  0 taken 150
branch  1 taken 3 (fallthrough)
      150: 1171:	  s = s * (double)i / (double)n;
      150: 1172:	  if(s < 1e-140) {
      150: 1172-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 150
    #####: 1173:		   s *= 1e140;
    #####: 1174:		   eQ -= 140;
    %%%%%: 1174-block  0
        -: 1175:	  }
        -: 1176:	}
        3: 1177:	s *= pow(10.0, eQ);
        3: 1178:	Safefree(H);
        3: 1178-block  0
call    0 returned 3
        3: 1179:	Safefree(Q);
call    0 returned 3
        3: 1180:	return s;
        -: 1181:}
        -: 1182:
        -: 1183:// Calculate D (two-sided), D+ (greater), and D- (less) simultaneously
function calc_2sample_stats called 9 returned 100% blocks executed 97%
        9: 1184:static void calc_2sample_stats(double *x, size_t nx, double *y, size_t ny,
        -: 1185:                               double *d, double *d_plus, double *d_minus) {
        9: 1186:	qsort(x, nx, sizeof(double), compare_doubles);
        9: 1186-block  0
call    0 returned 9
        9: 1187:	qsort(y, ny, sizeof(double), compare_doubles);
call    0 returned 9
        9: 1188:	double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
        9: 1189:	size_t i = 0, j = 0;
        -: 1190:
      729: 1191:	while(i < nx || j < ny) {
      729: 1191-block  0
branch  0 taken 720
branch  1 taken 9 (fallthrough)
        9: 1191-block  1
branch  2 taken 0
branch  3 taken 9 (fallthrough)
        -: 1192:	  double val;
      720: 1193:	  if (i < nx && j < ny) val = (x[i] < y[j]) ? x[i] : y[j];
      720: 1193-block  0
branch  0 taken 720 (fallthrough)
branch  1 taken 0
      720: 1193-block  1
branch  2 taken 603 (fallthrough)
branch  3 taken 117
      603: 1193-block  2
branch  4 taken 333 (fallthrough)
branch  5 taken 270
      333: 1193-block  3
      270: 1193-block  4
      603: 1193-block  5
      117: 1194:	  else if (i < nx) val = x[i];
      117: 1194-block  0
branch  0 taken 117 (fallthrough)
branch  1 taken 0
      117: 1194-block  1
    #####: 1195:	  else val = y[j];
    %%%%%: 1195-block  0
        -: 1196:	  
     1170: 1197:	  while(i < nx && x[i] <= val) i++;
      720: 1197-block  0
      450: 1197-block  1
     1170: 1197-block  2
branch  0 taken 1161 (fallthrough)
branch  1 taken 9
     1161: 1197-block  3
branch  2 taken 450
branch  3 taken 711 (fallthrough)
      990: 1198:	  while(j < ny && y[j] <= val) j++;
      720: 1198-block  0
      270: 1198-block  1
      990: 1198-block  2
branch  0 taken 864 (fallthrough)
branch  1 taken 126
      864: 1198-block  3
branch  2 taken 270
branch  3 taken 594 (fallthrough)
        -: 1199:	  
      720: 1200:	  double cdf1 = (double)i / nx;
      720: 1201:	  double cdf2 = (double)j / ny;
      720: 1202:	  double diff = cdf1 - cdf2;
        -: 1203:	  
      720: 1204:	  if (diff > max_d_plus) max_d_plus = diff;
      720: 1204-block  0
branch  0 taken 189 (fallthrough)
branch  1 taken 531
      189: 1204-block  1
      720: 1205:	  if (-diff > max_d_minus) max_d_minus = -diff;
      720: 1205-block  0
branch  0 taken 90 (fallthrough)
branch  1 taken 630
       90: 1205-block  1
      720: 1206:	  if (fabs(diff) > max_d) max_d = fabs(diff);
      720: 1206-block  0
branch  0 taken 189 (fallthrough)
branch  1 taken 531
      189: 1206-block  1
        -: 1207:	}
        9: 1208:	*d = max_d;
        9: 1209:	*d_plus = max_d_plus;
        9: 1210:	*d_minus = max_d_minus;
        9: 1211:}
        -: 1212:
        -: 1213:// Branch the DP boundary check based on the 'alternative'
function psmirnov_exact_test called 14220 returned 100% blocks executed 100%
    14220: 1214:static int psmirnov_exact_test(double q, double r, double s, int two_sided) {
    14220: 1215:	if (two_sided) return (fabs(r - s) >= q);
    14220: 1215-block  0
branch  0 taken 4740 (fallthrough)
branch  1 taken 9480
     4740: 1215-block  1
     9480: 1216:	return ((r - s) >= q); // Used for both D+ and D- via symmetry
     9480: 1216-block  0
        -: 1217:}
        -: 1218:
        -: 1219:// Evaluate the exact 2-sample probability
function psmirnov_exact_uniq_upper called 9 returned 100% blocks executed 100%
        9: 1220:static double psmirnov_exact_uniq_upper(double q, int m, int n, int two_sided) {
        9: 1221:	double md = (double) m, nd = (double) n;
        9: 1222:	double *u = (double *) safecalloc(n + 1, sizeof(double));
        9: 1222-block  0
call    0 returned 9
        9: 1223:	u[0] = 0.;
        -: 1224:
      279: 1225:	for(unsigned int j = 1; j <= n; j++) {
      270: 1225-block  0
      279: 1225-block  1
branch  0 taken 270
branch  1 taken 9 (fallthrough)
      270: 1226:	  if(psmirnov_exact_test(q, 0., j / nd, two_sided)) u[j] = 1.;
      270: 1226-block  0
call    0 returned 270
branch  1 taken 54 (fallthrough)
branch  2 taken 216
       54: 1226-block  1
      216: 1227:	  else u[j] = u[j - 1];
      216: 1227-block  0
        -: 1228:	}
      459: 1229:	for(unsigned int i = 1; i <= m; i++) {
        9: 1229-block  0
      450: 1229-block  1
      459: 1229-block  2
branch  0 taken 450
branch  1 taken 9 (fallthrough)
      450: 1230:	  if(psmirnov_exact_test(q, i / md, 0., two_sided)) u[0] = 1.;
      450: 1230-block  0
call    0 returned 450
branch  1 taken 294 (fallthrough)
branch  2 taken 156
      294: 1230-block  1
    13950: 1231:	  for(int j = 1; j <= n; j++) {
      450: 1231-block  0
    13500: 1231-block  1
    13950: 1231-block  2
branch  0 taken 13500
branch  1 taken 450 (fallthrough)
    13500: 1232:		   if(psmirnov_exact_test(q, i / md, j / nd, two_sided)) u[j] = 1.;
    13500: 1232-block  0
call    0 returned 13500
branch  1 taken 3498 (fallthrough)
branch  2 taken 10002
     3498: 1232-block  1
        -: 1233:		   else {
    10002: 1234:		       double v = (double)(i) / (double)(i + j);
    10002: 1235:		       double w = (double)(j) / (double)(i + j);
    10002: 1236:		       u[j] = v * u[j] + w * u[j - 1];
    10002: 1236-block  0
        -: 1237:		   }
        -: 1238:	  }
        -: 1239:	}
        9: 1240:	double res = u[n];
        9: 1241:	Safefree(u);
        9: 1241-block  0
call    0 returned 9
        9: 1242:	return res;
        -: 1243:}
        -: 1244:
function p_body called 687 returned 100% blocks executed 55%
      687: 1245:static double p_body(double n, double delta, double sd, double sig_level, int tsample, int tside, bool strict) {
      687: 1246:	double nu = (n - 1.0) * (double)tsample;
     687*: 1247:	if (nu < 1e-7) nu = 1e-7; 
      687: 1247-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 687
    %%%%%: 1247-block  1
        -: 1248:
        -: 1249:	// Ensure sig_level/tside is not truncated
      687: 1250:	double p_tail = sig_level / (double)tside;
      687: 1251:	double qu = qt_tail(nu, p_tail); // qt(p, df, lower.tail=FALSE)
      687: 1251-block  0
call    0 returned 687
        -: 1252:
      687: 1253:	double ncp = sqrt(n / (double)tsample) * (delta / sd);
        -: 1254:
     687*: 1255:	if (strict && tside == 2) {
branch  0 taken 0 (fallthrough)
branch  1 taken 687
    %%%%%: 1255-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
        -: 1256:	  // Use R-style tail calls: 1 - P(T < qu) + P(T < -qu)
    #####: 1257:	  return (1.0 - exact_pnt(qu, nu, ncp)) + exact_pnt(-qu, nu, ncp);
    %%%%%: 1257-block  0
call    0 never executed
call    1 never executed
        -: 1258:	} else {
        -: 1259:	  // Default: 1 - P(T < qu)
        -: 1260:	  // Ensure exact_pnt is using a convergence tolerance of at least 1e-15
      687: 1261:	  return 1.0 - exact_pnt(qu, nu, ncp);
      687: 1261-block  0
call    0 returned 687
        -: 1262:	}
        -: 1263:}
        -: 1264:
        -: 1265:// Bisection algorithm to find the inverse F-distribution (Quantile function)
        -: 1266:// Equivalent to R's qf(p, df1, df2)
        -: 1267:static double qf_bisection(double p, double df1, double df2) {
        -: 1268:	if (p <= 0.0) return 0.0;
        -: 1269:	if (p >= 1.0) return INFINITY;
       12: 1270:	double low = 0.0, high = 1.0;
       12: 1271:	// Find upper bound
       12: 1272:	while (pf(high, df1, df2) < p) {
       12: 1273:	  low = high;
        -: 1274:	  high *= 2.0;
        -: 1275:	  if (high > 1e100) break; /* Fallback limit */
       12: 1276:	}
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1276-block  0
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 1276-block  1
branch  4 taken 12 (fallthrough)
branch  5 taken 0
       12: 1277:
       12: 1278:	// Bisect to find the root
       12: 1278-block  0
        -: 1279:	for (unsigned short int i = 0; i < 150; i++) {
        -: 1280:		double mid = low + (high - low) / 2.0;
       12: 1281:		double p_mid = pf(mid, df1, df2);
       12: 1281-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1282:
       12: 1282-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9: 1282-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 1283:		if (p_mid < p) {
        9: 1284:			low = mid;
        9: 1284-block  0
        3: 1285:		} else {
        3: 1285-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1286:			high = mid;
        3: 1287:		}
        3: 1287-block  0
        -: 1288:		if (high - low < 1e-12) break;
        -: 1289:	}
        -: 1290:	return (low + high) / 2.0;
        -: 1291:}
       18: 1292:// --- XS SECTION ---
       12: 1292-block  0
        6: 1292-block  1
       18: 1292-block  2
branch  0 taken 6
branch  1 taken 12 (fallthrough)
        6: 1293:MODULE = Stats::LikeR  PACKAGE = Stats::LikeR
        6: 1293-block  0
call    0 returned 6
        6: 1294:
       6*: 1295:SV* ks_test(...)
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 1295-block  0
       6*: 1296:CODE:
        6: 1296-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 1296-block  1
        6: 1297:{
        6: 1297-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 1298:	SV *restrict x_sv = NULL, *restrict y_sv = NULL;
    %%%%%: 1298-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1298-block  1
    #####: 1299:	short int exact = -1;
    %%%%%: 1299-block  0
call    0 never executed
        -: 1300:	const char *restrict alternative = "two.sided";
        6: 1301:	int arg_idx = 0;
        6: 1301-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 1301-block  1
call    2 returned 6
    #####: 1302:
    %%%%%: 1302-block  0
call    0 never executed
        -: 1303:	// Shift arrays if provided positionally
        -: 1304:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
       12: 1305:		x_sv = ST(arg_idx);
       12: 1305-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1305-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 1305-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 12
    #####: 1306:		arg_idx++;
    %%%%%: 1306-block  0
call    0 never executed
        -: 1307:	}
        -: 1308:	// Check if second argument is an array (2-sample) or a string representing a CDF (1-sample)
       12: 1309:	if (arg_idx < items) {
       12: 1310:		if (SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
       12: 1311:			y_sv = ST(arg_idx);
        -: 1312:			arg_idx++;
       12: 1313:		} else if (SvPOK(ST(arg_idx))) {
       12: 1313-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 6
        6: 1313-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 3
        3: 1313-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    #####: 1314:			y_sv = ST(arg_idx); // Save string (e.g., "pnorm") for 1-sample test logic
    %%%%%: 1314-block  0
call    0 never executed
        -: 1315:			arg_idx++;
        -: 1316:		}
       12: 1317:	}
       12: 1318:
       12: 1318-block  0
call    0 returned 12
      12*: 1319:	// Parse named arguments
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 1319-block  0
call    2 never executed
        -: 1320:	for (; arg_idx < items; arg_idx += 2) {
        -: 1321:	  const char *restrict key = SvPV_nolen(ST(arg_idx));
       12: 1322:	  SV *restrict val = ST(arg_idx + 1);
       12: 1322-block  0
call    0 returned 12
       12: 1323:	  if      (strEQ(key, "x"))           x_sv = val;
      612: 1324:	  else if (strEQ(key, "y"))           y_sv = val;
      600: 1324-block  0
      612: 1324-block  1
branch  0 taken 600
branch  1 taken 12 (fallthrough)
      600: 1325:	  else if (strEQ(key, "exact"))       {
      600: 1325-block  0
call    0 returned 600
      600: 1326:		   if (!SvOK(val)) exact = -1;
branch  0 taken 600 (fallthrough)
branch  1 taken 0
      600: 1326-block  0
branch  2 taken 600 (fallthrough)
branch  3 taken 0
      600: 1326-block  1
call    4 returned 600
branch  5 taken 600 (fallthrough)
branch  6 taken 0
      600: 1327:		   else exact = SvTRUE(val) ? 1 : 0;
      600: 1327-block  0
call    0 returned 600
        -: 1328:	  }
        -: 1329:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 1330:	  else croak("ks_test: unknown argument '%s'", key);
       12: 1331:	}
       12: 1332:
        -: 1333:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV) {
        -: 1334:	  croak("ks_test: 'x' is a required argument and must be an ARRAY reference");
       21: 1335:	}
       12: 1335-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1335-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 3
        9: 1335-block  2
branch  4 taken 9 (fallthrough)
branch  5 taken 0
        9: 1336:
        9: 1337:	bool is_two_sided = strEQ(alternative, "two.sided") ? 1 : 0;
        9: 1337-block  0
call    0 returned 9
        -: 1338:	bool is_greater   = strEQ(alternative, "greater") ? 1 : 0;
        9: 1339:	bool is_less      = strEQ(alternative, "less") ? 1 : 0;
call    0 returned 9
        9: 1340:
      279: 1341:	if (!is_two_sided && !is_greater && !is_less) {
      270: 1341-block  0
      279: 1341-block  1
branch  0 taken 270
branch  1 taken 9 (fallthrough)
      270: 1342:	  croak("ks_test: alternative must be 'two.sided', 'less', or 'greater'");
      270: 1342-block  0
call    0 returned 270
      270: 1343:	}
branch  0 taken 270 (fallthrough)
branch  1 taken 0
      270: 1343-block  0
branch  2 taken 270 (fallthrough)
branch  3 taken 0
      270: 1343-block  1
call    4 returned 270
branch  5 taken 270 (fallthrough)
branch  6 taken 0
      270: 1344:
      270: 1344-block  0
call    0 returned 270
        -: 1345:	AV *restrict x_av = (AV*)SvRV(x_sv);
        -: 1346:	size_t nx = av_len(x_av) + 1;
        -: 1347:	if (nx == 0) croak("Not enough 'x' observations");
        9: 1348:
        9: 1348-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1348-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 1349:	// Extract 'x' array to C-array
    %%%%%: 1349-block  0
call    0 never executed
call    1 never executed
    #####: 1350:	double *restrict x_data = (double *)safemalloc(nx * sizeof(double));
call    0 never executed
        -: 1351:	size_t valid_nx = 0;
        -: 1352:	for (size_t i = 0; i < nx; i++) {
        -: 1353:	  SV**restrict el = av_fetch(x_av, i, 0);
        9: 1354:	  if (el && SvOK(*el) && looks_like_number(*el)) {
        9: 1354-block  0
call    0 returned 9
        -: 1355:		   x_data[valid_nx++] = SvNV(*el);
        -: 1356:	  }
        9: 1357:	}
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 1357-block  0
        6: 1358:
        6: 1358-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 1358-block  1
        3: 1359:	double statistic = 0.0, p_value = 0.0;
        3: 1359-block  0
        -: 1360:	const char *restrict method_desc = "";
        -: 1361:
        9: 1362:	// --- TWO SAMPLE ---
       9*: 1363:	if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
        9: 1363-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1363-block  1
       9*: 1364:	  AV *restrict y_av = (AV*)SvRV(y_sv);
        9: 1364-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1364-block  1
        9: 1365:	  size_t ny = av_len(y_av) + 1;
        9: 1365-block  0
        -: 1366:	  
        -: 1367:	  double *restrict y_data = (double *)safemalloc(ny * sizeof(double));
        9: 1368:	  size_t valid_ny = 0;
        9: 1369:	  for (size_t i = 0; i < ny; i++) {
        9: 1369-block  0
call    0 returned 9
      459: 1370:		   SV**restrict el = av_fetch(y_av, i, 0);
      450: 1370-block  0
      459: 1370-block  1
branch  0 taken 450
branch  1 taken 9 (fallthrough)
      279: 1371:		   if (el && SvOK(*el) && looks_like_number(*el)) {
        9: 1371-block  0
      270: 1371-block  1
      279: 1371-block  2
branch  0 taken 270
branch  1 taken 9 (fallthrough)
        9: 1372:		       y_data[valid_ny++] = SvNV(*el);
        9: 1372-block  0
call    0 returned 9
        -: 1373:		   }
        9: 1374:	  }
      720: 1375:
      711: 1375-block  0
      720: 1375-block  1
branch  0 taken 711
branch  1 taken 9 (fallthrough)
     711*: 1376:	  if (valid_nx < 1 || valid_ny < 1) {
      711: 1376-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 711
    %%%%%: 1376-block  1
        -: 1377:		   Safefree(x_data); Safefree(y_data);
        9: 1378:		   croak("Not enough non-missing observations for KS test");
        9: 1378-block  0
call    0 returned 9
        9: 1379:	  }
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1379-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 1380:
    %%%%%: 1380-block  0
call    0 never executed
    #####: 1381:	  double d, d_plus, d_minus;
        -: 1382:	  calc_2sample_stats(x_data, valid_nx, y_data, valid_ny, &d, &d_plus, &d_minus);
        9: 1383:
        9: 1383-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1384:	  // Map alternative to the correct statistic
        9: 1385:	  if (is_greater) statistic = d_plus;
        9: 1386:	  else if (is_less) statistic = d_minus;
        9: 1386-block  0
call    0 returned 9
        -: 1387:	  else statistic = d;
    #####: 1388:
    #####: 1389:	  // Determine if exact or asymptotic
    #####: 1390:	  bool use_exact = false;
    %%%%%: 1390-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1391:	  if (exact == 1) use_exact = true;
    %%%%%: 1391-block  0
call    0 never executed
        -: 1392:	  else if (exact == 0) use_exact = false;
    #####: 1393:	  else use_exact = (valid_nx * valid_ny < 10000); 
    %%%%%: 1393-block  0
        -: 1394:
        -: 1395:	  // Check for ties in combined set
        9: 1396:	  size_t total_n = valid_nx + valid_ny;
        9: 1396-block  0
call    0 returned 9
        -: 1397:	  double *restrict comb = (double *)safemalloc(total_n * sizeof(double));
        -: 1398:	  for(size_t i=0; i<valid_nx; i++) comb[i] = x_data[i];
        6: 1399:	  for(size_t i=0; i<valid_ny; i++) comb[valid_nx+i] = y_data[i];
        3: 1399-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1399-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1399-block  2
        3: 1400:	  qsort(comb, total_n, sizeof(double), compare_doubles);
        3: 1400-block  0
call    0 returned 3
        3: 1401:
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1402:	  bool has_ties = false;
        3: 1402-block  0
call    0 returned 3
        3: 1403:	  for(size_t i = 1; i < total_n; i++) {
      153: 1404:		   if(comb[i] == comb[i-1]) { has_ties = true; break; }
      150: 1404-block  0
      153: 1404-block  1
branch  0 taken 150
branch  1 taken 3 (fallthrough)
      150: 1405:	  }
      150: 1406:	  Safefree(comb);
      150: 1407:	  if (use_exact && has_ties) {
      150: 1407-block  0
call    0 returned 150
        -: 1408:		   warn("cannot compute exact p-value with ties; falling back to asymptotic");
      150: 1409:		   use_exact = false;
      150: 1410:	  }
        -: 1411:	  if (use_exact) {
     150*: 1412:		   method_desc = "Two-sample Kolmogorov-Smirnov exact test";
branch  0 taken 0 (fallthrough)
branch  1 taken 150
    %%%%%: 1412-block  0
      150: 1413:		   double q = (0.5 + floor(statistic * valid_nx * valid_ny - 1e-7)) / ((double)valid_nx * valid_ny);
      150: 1413-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 147
        3: 1413-block  1
      150: 1414:		   p_value = psmirnov_exact_uniq_upper(q, valid_nx, valid_ny, is_two_sided);
      150: 1414-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 126
       24: 1414-block  1
     150*: 1415:	  } else {
      150: 1415-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 150
    %%%%%: 1415-block  1
        -: 1416:		   method_desc = "Two-sample Kolmogorov-Smirnov test (asymptotic)";
      150: 1417:		   double z = statistic * sqrt((double)(valid_nx * valid_ny) / (valid_nx + valid_ny));
      150: 1417-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 126
       24: 1417-block  1
     150*: 1418:		   if (is_two_sided) {
      150: 1418-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 150
    %%%%%: 1418-block  1
        -: 1419:		       p_value = K2l(z, 0, 1e-9); 
       3*: 1420:		   } else {
        3: 1420-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1420-block  1
       3*: 1421:		       p_value = exp(-2.0 * z * z); // One-sided limit distribution
        3: 1421-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 1421-block  1
        3: 1422:		   }
        3: 1422-block  0
       3*: 1423:	  }
        3: 1423-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1423-block  1
    %%%%%: 1423-block  2
        3: 1424:	  Safefree(y_data);
        3: 1424-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1425:	} 
        3: 1426:	// --- ONE SAMPLE (e.g. against pnorm) ---
        3: 1426-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1427:	else if (y_sv && SvPOK(y_sv)) {
        3: 1427-block  0
call    0 returned 3
        -: 1428:	  const char *restrict dist = SvPV_nolen(y_sv);
    #####: 1429:	  if (strEQ(dist, "pnorm")) {
    %%%%%: 1429-block  0
call    0 never executed
    #####: 1430:		   qsort(x_data, valid_nx, sizeof(double), compare_doubles);
    #####: 1431:		   double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
        -: 1432:		   for(size_t i = 0; i < valid_nx; i++) {
        -: 1433:		       double cdf_obs_low  = (double)i / valid_nx;
    #####: 1434:		       double cdf_obs_high = (double)(i + 1) / valid_nx;
    #####: 1435:		       double cdf_theor    = approx_pnorm(x_data[i]);
    #####: 1436:		       
    %%%%%: 1436-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1436-block  1
call    2 never executed
    #####: 1437:		       double diff1 = cdf_obs_low - cdf_theor;
    %%%%%: 1437-block  0
        -: 1438:		       double diff2 = cdf_obs_high - cdf_theor;
        -: 1439:		       
    #####: 1440:		       if (diff1 > max_d_plus) max_d_plus = diff1;
    %%%%%: 1440-block  0
call    0 never executed
    #####: 1441:		       if (diff2 > max_d_plus) max_d_plus = diff2;
call    0 never executed
        -: 1442:		       if (-diff1 > max_d_minus) max_d_minus = -diff1;
        -: 1443:		       if (-diff2 > max_d_minus) max_d_minus = -diff2;
    #####: 1444:		       
    %%%%%: 1444-block  0
call    0 never executed
    #####: 1445:		       if (fabs(diff1) > max_d) max_d = fabs(diff1);
call    0 never executed
        -: 1446:		       if (fabs(diff2) > max_d) max_d = fabs(diff2);
       12: 1447:		   }
       12: 1447-block  0
call    0 returned 12
      12*: 1448:		   if (is_greater) statistic = max_d_plus;
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 1448-block  0
      12*: 1449:		   else if (is_less) statistic = max_d_minus;
       12: 1449-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 1449-block  1
       12: 1450:		   else statistic = max_d;
       12: 1450-block  0
call    0 returned 12
       12: 1451:		   bool use_exact = (exact == -1) ? (valid_nx < 100) : (exact == 1);
call    0 returned 12
call    1 returned 12
       12: 1452:		   if (use_exact) {
call    0 returned 12
call    1 returned 12
       12: 1453:		       method_desc = "One-sample Kolmogorov-Smirnov exact test";
call    0 returned 12
call    1 returned 12
       12: 1454:		       if (is_two_sided) {
call    0 returned 12
call    1 returned 12
       12: 1455:		           p_value = 1.0 - K2x(valid_nx, statistic);
call    0 returned 12
        -: 1456:		       } else {
        -: 1457:		           warn("exact 1-sample 1-sided KS test not implemented; using asymptotic");
        -: 1458:		           double z = statistic * sqrt((double)valid_nx);
        -: 1459:		           p_value = exp(-2.0 * z * z);
        -: 1460:		       }
        -: 1461:		   } else {
        -: 1462:		       method_desc = "One-sample Kolmogorov-Smirnov test (asymptotic)";
       30: 1463:		       double z = statistic * sqrt((double)valid_nx);
       30: 1464:		       if (is_two_sided) p_value = K2l(z, 0, 1e-6); 
       30: 1465:		       else p_value = exp(-2.0 * z * z);
       30: 1466:		   }
       30: 1467:	  } else {
       30: 1468:		    Safefree(x_data);
        -: 1469:		    croak("ks_test: Unsupported 1-sample distribution '%s'. Use arrays for 2-sample.", dist);
       30: 1470:	  }
branch  0 taken 30 (fallthrough)
branch  1 taken 0
       30: 1470-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 24
        6: 1470-block  1
branch  4 taken 6 (fallthrough)
branch  5 taken 0
        6: 1471:	} else {
        6: 1472:	  Safefree(x_data);
        6: 1472-block  0
        -: 1473:	  croak("ks_test: Invalid arguments for 'y'.");
        -: 1474:	}
       30: 1475:	Safefree(x_data);
       30: 1475-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 0
       30: 1475-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 24
        6: 1475-block  2
branch  4 taken 6 (fallthrough)
branch  5 taken 0
        6: 1476:	if (p_value > 1.0) p_value = 1.0;
        6: 1477:	if (p_value < 0.0) p_value = 0.0;
        6: 1477-block  0
        -: 1478:	HV *restrict res = newHV();
        -: 1479:	hv_stores(res, "statistic", newSVnv(statistic));
       30: 1480:	hv_stores(res, "p_value", newSVnv(p_value));
       30: 1480-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 30
    #####: 1481:	hv_stores(res, "method", newSVpv(method_desc, 0));
    %%%%%: 1481-block  0
call    0 never executed
        -: 1482:	hv_stores(res, "alternative", newSVpv(alternative, 0));
        -: 1483:	RETVAL = newRV_noinc((SV*)res);
       90: 1484:}
       30: 1484-block  0
       60: 1484-block  1
       90: 1484-block  2
branch  0 taken 60
branch  1 taken 30 (fallthrough)
       60: 1485:OUTPUT:
       60: 1485-block  0
call    0 returned 60
       60: 1486:    RETVAL
       60: 1487:
branch  0 taken 21 (fallthrough)
branch  1 taken 39
       21: 1487-block  0
       39: 1488:SV* wilcox_test(...)
       39: 1488-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 18
       21: 1488-block  1
       18: 1489:CODE:
       18: 1489-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 9
        9: 1489-block  1
call    2 returned 9
       9*: 1490:{
        9: 1490-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1490-block  1
call    2 never executed
        9: 1491:	SV *restrict x_sv = NULL, *restrict y_sv = NULL;
        9: 1491-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 1491-block  1
call    2 returned 3
        6: 1492:	bool paired = false, correct = true;
        6: 1492-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 1493:	double mu = 0.0;
    %%%%%: 1493-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1493-block  1
    #####: 1494:	short int exact = -1;
    %%%%%: 1494-block  0
call    0 never executed
        -: 1495:	const char *restrict alternative = "two.sided";
        6: 1496:	int arg_idx = 0;
        6: 1496-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 1496-block  1
call    2 returned 6
    #####: 1497:	// 1. Shift first positional argument as 'x' if it's an array reference
    %%%%%: 1497-block  0
call    0 never executed
        -: 1498:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 1499:		x_sv = ST(arg_idx);
       30: 1500:		arg_idx++;
       30: 1500-block  0
branch  0 taken 27 (fallthrough)
branch  1 taken 3
       27: 1500-block  1
branch  2 taken 27 (fallthrough)
branch  3 taken 0
       27: 1500-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 27
        3: 1501:	}
        3: 1501-block  0
call    0 returned 0
       27: 1502:	// 2. Shift second positional argument as 'y' if it's an array reference
       27: 1503:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
       27: 1503-block  0
call    0 returned 27
      27*: 1504:		y_sv = ST(arg_idx);
branch  0 taken 0 (fallthrough)
branch  1 taken 27
    %%%%%: 1504-block  0
call    2 never executed
        -: 1505:		arg_idx++;
       27: 1506:	}
       27: 1507:	// Ensure the remaining arguments form complete key-value pairs
       27: 1508:	if ((items - arg_idx) % 2 != 0) {
       27: 1508-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 3
       24: 1508-block  1
branch  2 taken 24 (fallthrough)
branch  3 taken 0
       24: 1508-block  2
branch  4 taken 24 (fallthrough)
branch  5 taken 0
       24: 1509:		croak("Usage: wilcox_test(\\@x, [\\@y], key => value, ...)");
       24: 1510:	}
       24: 1510-block  0
call    0 returned 24
        -: 1511:	// --- Parse named arguments from the remaining flat stack ---
       27: 1512:	for (; arg_idx < items; arg_idx += 2) {
       27: 1513:		const char *restrict key = SvPV_nolen(ST(arg_idx));
       27: 1514:		SV *restrict val = ST(arg_idx + 1);
        -: 1515:		if      (strEQ(key, "x"))           x_sv = val;
       42: 1516:		else if (strEQ(key, "y"))           y_sv = val;
       27: 1516-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 3
       24: 1516-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 9
       15: 1517:		else if (strEQ(key, "paired"))      paired = SvTRUE(val);
       15: 1517-block  0
call    0 returned 15
       15: 1518:		else if (strEQ(key, "correct"))     correct = SvTRUE(val);
       99: 1519:		else if (strEQ(key, "mu"))          mu = SvNV(val);
       84: 1519-block  0
       99: 1519-block  1
branch  0 taken 84
branch  1 taken 15 (fallthrough)
       84: 1520:		else if (strEQ(key, "exact"))       {
       84: 1520-block  0
call    0 returned 84
       84: 1521:			if (!SvOK(val)) exact = -1;
branch  0 taken 84 (fallthrough)
branch  1 taken 0
       84: 1521-block  0
branch  2 taken 84 (fallthrough)
branch  3 taken 0
       84: 1521-block  1
call    4 returned 84
branch  5 taken 84 (fallthrough)
branch  6 taken 0
       84: 1522:			else exact = SvTRUE(val) ? 1 : 0;
       84: 1522-block  0
call    0 returned 84
       84: 1523:		}
       84: 1524:		else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 1525:		else croak("wilcox_test: unknown argument '%s'", key);
        -: 1526:	}
       99: 1527:	// --- Validate required / types ---
       15: 1527-block  0
       84: 1527-block  1
       99: 1527-block  2
branch  0 taken 84
branch  1 taken 15 (fallthrough)
       84: 1528:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
       84: 1528-block  0
call    0 returned 84
       84: 1529:		croak("wilcox_test: 'x' is a required argument and must be an ARRAY reference");
branch  0 taken 84 (fallthrough)
branch  1 taken 0
       84: 1529-block  0
branch  2 taken 84 (fallthrough)
branch  3 taken 0
       84: 1529-block  1
call    4 returned 84
branch  5 taken 84 (fallthrough)
branch  6 taken 0
       84: 1530:	AV *restrict x_av = (AV*)SvRV(x_sv);
       84: 1530-block  0
call    0 returned 84
       84: 1531:	size_t nx = av_len(x_av) + 1;
       84: 1532:	if (nx == 0) croak("Not enough 'x' observations");
        -: 1533:
        -: 1534:	AV *restrict y_av = NULL;
      15*: 1535:	size_t ny = 0;
       15: 1535-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 1535-block  1
call    2 never executed
call    3 never executed
      15*: 1536:	if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
       15: 1536-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 1536-block  1
call    2 never executed
call    3 never executed
       15: 1537:		y_av = (AV*)SvRV(y_sv);
       15: 1538:		ny = av_len(y_av) + 1;
       15: 1539:	}
       15: 1539-block  0
call    0 returned 15
       15: 1540:	double p_value = 0.0, statistic = 0.0;
      183: 1541:	const char *restrict method_desc = "";
      168: 1541-block  0
branch  0 taken 84 (fallthrough)
branch  1 taken 84
       84: 1541-block  1
      168: 1541-block  2
      183: 1541-block  3
branch  2 taken 168
branch  3 taken 15 (fallthrough)
       15: 1542:	bool use_exact = false;
        -: 1543:	// --- TWO SAMPLE (Mann-Whitney) ---
      15*: 1544:	if (ny > 0 && !paired) {
       15: 1544-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 1544-block  1
      15*: 1545:		RankInfo *restrict ri = (RankInfo *)safemalloc((nx + ny) * sizeof(RankInfo));
       15: 1545-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 1545-block  1
       15: 1546:		size_t valid_nx = 0, valid_ny = 0;
       15: 1546-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 1546-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 0
       15: 1546-block  2
branch  4 taken 6 (fallthrough)
branch  5 taken 9
        6: 1546-block  3
        9: 1546-block  4
       15: 1546-block  5
        -: 1547:		for (size_t i = 0; i < nx; i++) {
       15: 1548:			SV**restrict el = av_fetch(x_av, i, 0);
       15: 1548-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 9
        6: 1548-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    #####: 1549:			if (el && SvOK(*el) && looks_like_number(*el)) {
    %%%%%: 1549-block  0
call    0 never executed
    #####: 1550:				ri[valid_nx].val = SvNV(*el) - mu; // R subtracts mu from x
        -: 1551:				ri[valid_nx].idx = 1;
       15: 1552:				valid_nx++;
       15: 1552-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 9
        6: 1553:			}
        6: 1554:		}
        6: 1554-block  0
call    0 returned 6
        6: 1555:		for (size_t i = 0; i < ny; i++) {
call    0 returned 6
        -: 1556:			SV**restrict el = av_fetch(y_av, i, 0);
        6: 1557:			if (el && SvOK(*el) && looks_like_number(*el)) {
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 1557-block  0
        3: 1558:				ri[valid_nx + valid_ny].val = SvNV(*el);
        3: 1558-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1558-block  1
        -: 1559:				ri[valid_nx + valid_ny].idx = 2;
    #####: 1560:				valid_ny++;
    %%%%%: 1560-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1560-block  1
    %%%%%: 1560-block  2
    #####: 1561:			}
    %%%%%: 1561-block  0
        -: 1562:		}
        -: 1563:		if (valid_nx == 0) { Safefree(ri); croak("not enough (non-missing) 'x' observations"); }
       9*: 1564:		if (valid_ny == 0) { Safefree(ri); croak("not enough 'y' observations"); }
        9: 1564-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1564-block  1
    %%%%%: 1564-block  2
        9: 1565:		size_t total_n = valid_nx + valid_ny;
        9: 1566:		bool has_ties = 0;
        9: 1567:		double tie_adj = rank_and_count_ties(ri, total_n, &has_ties);
        -: 1568:		double w_rank_sum = 0.0;
        9: 1569:		for (size_t i = 0; i < total_n; i++) if (ri[i].idx == 1) w_rank_sum += ri[i].rank;
        9: 1570:		statistic = w_rank_sum - (double)valid_nx * (valid_nx + 1.0) / 2.0;
        9: 1570-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1571:		
        9: 1571-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1571-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 3
        6: 1571-block  2
        3: 1571-block  3
        9: 1571-block  4
    #####: 1572:		if (exact == 1) use_exact = true;
    %%%%%: 1572-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1572-block  1
    #####: 1573:		else if (exact == 0) use_exact = false;
    %%%%%: 1573-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1573-block  1
        -: 1574:		else use_exact = (valid_nx < 50 && valid_ny < 50 && !has_ties);
        9: 1575:		
        -: 1576:		if (use_exact && has_ties) {
       9*: 1577:			warn("cannot compute exact p-value with ties; falling back to approximation");
        9: 1577-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1577-block  1
call    2 never executed
       9*: 1578:			use_exact = false;
        9: 1578-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1578-block  1
call    2 never executed
        9: 1579:		}
        9: 1579-block  0
call    0 returned 9
        -: 1580:		if (use_exact) {
       15: 1581:			method_desc = "Wilcoxon rank sum exact test";
       15: 1581-block  0
call    0 returned 15
        -: 1582:			double p_less = exact_pwilcox(statistic, valid_nx, valid_ny);
       12: 1583:			double p_greater = 1.0 - exact_pwilcox(statistic - 1.0, valid_nx, valid_ny);
       12: 1583-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9: 1583-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 1583-block  2
branch  4 taken 3 (fallthrough)
branch  5 taken 6
        3: 1583-block  3
call    6 returned 0
        9: 1584:
        9: 1584-block  0
call    0 returned 9
        9: 1585:			if (strcmp(alternative, "less") == 0) p_value = p_less;
        9: 1586:			else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
       78: 1587:			else {
       69: 1587-block  0
       78: 1587-block  1
branch  0 taken 69
branch  1 taken 9 (fallthrough)
       69: 1588:				double p = (p_less < p_greater) ? p_less : p_greater;
       69: 1588-block  0
call    0 returned 69
      69*: 1589:				p_value = 2.0 * p;
branch  0 taken 69 (fallthrough)
branch  1 taken 0
       69: 1589-block  0
branch  2 taken 69 (fallthrough)
branch  3 taken 0
       69: 1589-block  1
call    4 returned 69
branch  5 taken 0 (fallthrough)
branch  6 taken 69
    %%%%%: 1589-block  2
       69: 1590:			}
       69: 1590-block  0
call    0 returned 69
        -: 1591:		} else {
       69: 1592:			method_desc = correct ? "Wilcoxon rank sum test with continuity correction" : "Wilcoxon rank sum test";
branch  0 taken 54 (fallthrough)
branch  1 taken 15
       54: 1593:			double exp = (double)valid_nx * valid_ny / 2.0;
       54: 1593-block  0
call    0 returned 54
      54*: 1594:			double var = ((double)valid_nx * valid_ny / 12.0) * ((total_n + 1.0) - tie_adj / (total_n * (total_n - 1.0)));
branch  0 taken 54 (fallthrough)
branch  1 taken 0
       54: 1594-block  0
branch  2 taken 54 (fallthrough)
branch  3 taken 0
       54: 1594-block  1
call    4 returned 54
branch  5 taken 0 (fallthrough)
branch  6 taken 54
    %%%%%: 1594-block  2
       54: 1595:			double z = statistic - exp;
       54: 1595-block  0
call    0 returned 54
       54: 1596:			
      54*: 1597:			double CORRECTION = 0.0;
branch  0 taken 0 (fallthrough)
branch  1 taken 54
    %%%%%: 1597-block  0
       54: 1598:			if (correct) {
       54: 1598-block  0
        -: 1599:				if (strcmp(alternative, "two.sided") == 0) CORRECTION = (z > 0 ? 0.5 : -0.5);
       15: 1600:				else if (strcmp(alternative, "greater") == 0) CORRECTION = 0.5;
      15*: 1601:				else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
       15: 1601-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 1601-block  1
       15: 1602:			}
       15: 1602-block  0
        -: 1603:			z = (z - CORRECTION) / sqrt(var);
        -: 1604:
        9: 1605:			if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
        9: 1605-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    #####: 1606:			else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
    %%%%%: 1606-block  0
call    0 never executed
    #####: 1607:			else p_value = 2.0 * approx_pnorm(-fabs(z));
call    0 never executed
        -: 1608:		}
        9: 1609:		Safefree(ri);
        9: 1609-block  0
call    0 returned 9
       78: 1610:	} else { // --- ONE SAMPLE / PAIRED ---
       78: 1610-block  0
branch  0 taken 69
branch  1 taken 9 (fallthrough)
       69: 1611:		if (paired && (!y_av || nx != ny)) croak("'x' and 'y' must have the same length for paired test");
       69: 1612:		double *restrict diffs = (double *)safemalloc(nx * sizeof(double));
       69: 1612-block  0
        -: 1613:		size_t n_nz = 0;
        9: 1614:		bool has_zeroes = false;
        9: 1615:		for (size_t i = 0; i < nx; i++) {
        9: 1615-block  0
call    0 returned 9
        9: 1616:			SV**restrict x_el = av_fetch(x_av, i, 0);
       78: 1617:			if (!x_el || !SvOK(*x_el) || !looks_like_number(*x_el)) continue;
       69: 1617-block  0
       78: 1617-block  1
branch  0 taken 69
branch  1 taken 9 (fallthrough)
       69: 1618:			double dx = SvNV(*x_el);
       69: 1618-block  0
branch  0 taken 57 (fallthrough)
branch  1 taken 12
       57: 1618-block  1
        -: 1619:
       9*: 1620:			if (paired) {
        9: 1620-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1620-block  1
       9*: 1621:				SV**restrict y_el = av_fetch(y_av, i, 0);
        9: 1621-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1621-block  1
       9*: 1622:				if (!y_el || !SvOK(*y_el) || !looks_like_number(*y_el)) continue;
        9: 1622-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1622-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 1622-block  2
    %%%%%: 1622-block  3
        9: 1622-block  4
        9: 1623:				double dy = SvNV(*y_el);
        9: 1623-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1623-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 1624:				double d = dx - dy - mu;
    %%%%%: 1624-block  0
call    0 never executed
    #####: 1625:				if (d == 0.0) has_zeroes = true; // Drop exact zeroes
        -: 1626:				else diffs[n_nz++] = d;
        9: 1627:			} else {
        9: 1627-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1627-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 1628:				double d = dx - mu;
    %%%%%: 1628-block  0
call    0 never executed
    #####: 1629:				if (d == 0.0) has_zeroes = true;
        -: 1630:				else diffs[n_nz++] = d;
        9: 1631:			}
        9: 1631-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1632:		}
        9: 1633:		if (n_nz == 0) {
        9: 1633-block  0
call    0 returned 9
        9: 1634:			Safefree(diffs);
call    0 returned 9
        -: 1635:			croak("not enough (non-missing) observations");
       9*: 1636:		}
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1636-block  0
       9*: 1637:		RankInfo *ri = (RankInfo *)safemalloc(n_nz * sizeof(RankInfo));
        9: 1637-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1637-block  1
        -: 1638:		for (size_t i = 0; i < n_nz; i++) { 
       9*: 1639:			ri[i].val = fabs(diffs[i]); 
        9: 1639-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1639-block  1
        9: 1639-block  2
        9: 1640:			ri[i].idx = (diffs[i] > 0);
        9: 1640-block  0
        -: 1641:		}
        -: 1642:		bool has_ties = 0;
    #####: 1643:		double tie_adj = rank_and_count_ties(ri, n_nz, &has_ties);
    %%%%%: 1643-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1643-block  1
    %%%%%: 1643-block  2
    #####: 1644:		statistic = 0.0;
    #####: 1645:		for (size_t i = 0; i < n_nz; i++) {
    #####: 1646:			if (ri[i].idx) statistic += ri[i].rank;
    #####: 1647:		}
    #####: 1648:		if (exact == 1) use_exact = true;
    %%%%%: 1648-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 1649:		else if (exact == 0) use_exact = false;
    %%%%%: 1649-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1649-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 1649-block  2
    %%%%%: 1649-block  3
    %%%%%: 1649-block  4
    #####: 1650:		else use_exact = (n_nz < 50 && !has_ties);
    %%%%%: 1650-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1650-block  1
    #####: 1651:		if (use_exact && has_ties) {
    %%%%%: 1651-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1651-block  1
        -: 1652:			warn("cannot compute exact p-value with ties; falling back to approximation");
    #####: 1653:			use_exact = false;
        -: 1654:		}
    #####: 1655:		if (use_exact && has_zeroes) {
    %%%%%: 1655-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1655-block  1
call    2 never executed
    #####: 1656:			warn("cannot compute exact p-value with zeroes; falling back to approximation");
    %%%%%: 1656-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1656-block  1
call    2 never executed
    #####: 1657:			use_exact = false;
    %%%%%: 1657-block  0
call    0 never executed
        -: 1658:		}
        9: 1659:		if (use_exact) {
        9: 1659-block  0
call    0 returned 9
call    1 returned 9
        -: 1660:			method_desc = paired ? "Wilcoxon exact signed rank test" : "Wilcoxon exact signed rank test";
      24*: 1661:			double p_less = exact_psignrank(statistic, n_nz);
       24: 1661-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24
    %%%%%: 1661-block  1
       24: 1662:			double p_greater = 1.0 - exact_psignrank(statistic - 1.0, n_nz);
       24: 1662-block  0
call    0 returned 24
       24: 1663:
call    0 returned 24
call    1 returned 24
       24: 1664:			if (strcmp(alternative, "less") == 0) p_value = p_less;
call    0 returned 24
call    1 returned 24
       24: 1665:			else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
call    0 returned 24
call    1 returned 24
       24: 1666:			else {
call    0 returned 24
call    1 returned 24
       24: 1667:				double p = (p_less < p_greater) ? p_less : p_greater;
call    0 returned 24
        -: 1668:				p_value = 2.0 * p;
        -: 1669:			}
        -: 1670:		} else {
        -: 1671:			method_desc = correct ? "Wilcoxon signed rank test with continuity correction" : "Wilcoxon signed rank test";
        -: 1672:			double exp = (double)n_nz * (n_nz + 1.0) / 4.0;
        -: 1673:			double var = (n_nz * (n_nz + 1.0) * (2.0 * n_nz + 1.0) / 24.0) - (tie_adj / 48.0);
        -: 1674:			double z = statistic - exp;
        -: 1675:			double CORRECTION = 0.0;
        9: 1676:			if (correct) {
       9*: 1677:				if (strcmp(alternative, "two.sided") == 0) CORRECTION = (z > 0 ? 0.5 : -0.5);
        9: 1677-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 1677-block  1
call    2 never executed
        9: 1677-block  2
        9: 1678:				else if (strcmp(alternative, "greater") == 0) CORRECTION = 0.5;
        9: 1679:				else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
        9: 1679-block  0
call    0 returned 9
        9: 1680:			}
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1680-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 3
        6: 1680-block  1
branch  4 taken 6 (fallthrough)
branch  5 taken 0
        6: 1681:			z = (z - CORRECTION) / sqrt(var);
        6: 1682:
       6*: 1683:			if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
        6: 1683-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 1683-block  1
call    2 never executed
        6: 1683-block  2
        6: 1683-block  3
        -: 1684:			else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
        3: 1685:			else p_value = 2.0 * approx_pnorm(-fabs(z));
        3: 1686:		}
        3: 1686-block  0
        -: 1687:		Safefree(ri); Safefree(diffs);
        -: 1688:	}
        9: 1689:	if (p_value > 1.0) p_value = 1.0;
        9: 1690:	HV *restrict res = newHV();
        9: 1691:	hv_stores(res, "statistic", newSVnv(statistic));
        9: 1691-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        6: 1691-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 1691-block  2
branch  4 taken 3 (fallthrough)
branch  5 taken 3
        3: 1691-block  3
        6: 1691-block  4
        -: 1692:	hv_stores(res, "p_value", newSVnv(p_value));
        9: 1693:	hv_stores(res, "method", newSVpv(method_desc, 0));
        9: 1693-block  0
call    0 returned 9
        9: 1694:	hv_stores(res, "alternative", newSVpv(alternative, 0));
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        6: 1695:	RETVAL = newRV_noinc((SV*)res);
        6: 1695-block  0
call    0 returned 6
        6: 1696:}
call    0 returned 6
       18: 1697:OUTPUT:
       12: 1697-block  0
       18: 1697-block  1
branch  0 taken 12
branch  1 taken 6 (fallthrough)
       21: 1698:	RETVAL
        6: 1698-block  0
       15: 1698-block  1
       21: 1698-block  2
branch  0 taken 15
branch  1 taken 6 (fallthrough)
       18: 1699:
        6: 1699-block  0
       12: 1699-block  1
       18: 1699-block  2
branch  0 taken 12
branch  1 taken 6 (fallthrough)
       12: 1700:SV* _chisq_c(data_ref)
       12: 1700-block  0
call    0 returned 12
       12: 1701:    SV* data_ref;
       42: 1702:CODE:
       42: 1702-block  0
branch  0 taken 30
branch  1 taken 12 (fallthrough)
       30: 1703:{
       30: 1703-block  0
call    0 returned 30
       30: 1704:	AV*restrict obs_av = (AV*)SvRV(data_ref);
call    0 returned 30
       30: 1705:	int r = av_top_index(obs_av) + 1, c = 0;
       30: 1706:	bool is_2d = 0;
       30: 1707:	SV**restrict first_elem = av_fetch(obs_av, 0, 0);
        -: 1708:	if (first_elem && SvROK(*first_elem) && SvTYPE(SvRV(*first_elem)) == SVt_PVAV) {
        -: 1709:		is_2d = 1;
       18: 1710:		AV*restrict first_row = (AV*)SvRV(*first_elem);
        6: 1710-block  0
       18: 1710-block  1
branch  0 taken 12
branch  1 taken 6 (fallthrough)
       12: 1711:		c = av_top_index(first_row) + 1;
       12: 1711-block  0
call    0 returned 12
       12: 1712:	} else {
call    0 returned 12
       12: 1713:		c = r;
       42: 1714:		r = 1;
       30: 1714-block  0
       42: 1714-block  1
branch  0 taken 30
branch  1 taken 12 (fallthrough)
       30: 1715:	}
       30: 1716:
       30: 1716-block  0
call    0 returned 30
       30: 1717:	double stat = 0.0, grand_total = 0.0;
call    0 returned 30
       30: 1718:	int df = 0;
call    0 returned 30
call    1 returned 30
       30: 1719:	int yates = (is_2d && r == 2 && c == 2) ? 1 : 0;
branch  0 taken 12 (fallthrough)
branch  1 taken 18
        -: 1720:
       12: 1721:	AV*restrict expected_av = newAV();
      12*: 1722:	if (is_2d) {
       12: 1722-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1722-block  1
    %%%%%: 1722-block  2
       12: 1723:		double *restrict row_sum = (double*)safemalloc(r * sizeof(double));
       12: 1724:		double *restrict col_sum = (double*)safemalloc(c * sizeof(double));
       12: 1724-block  0
        -: 1725:		for(unsigned int i=0; i<r; i++) row_sum[i] = 0.0;
       18: 1726:		for(unsigned int j=0; j<c; j++) col_sum[j] = 0.0;
       18: 1726-block  0
        -: 1727:		for (unsigned int i = 0; i < r; i++) {
        -: 1728:			SV**restrict row_sv = av_fetch(obs_av, i, 0);
       12: 1729:			AV*restrict row = (AV*)SvRV(*row_sv);
       12: 1729-block  0
call    0 returned 12
call    1 returned 12
        -: 1730:			for (unsigned int j = 0; j < c; j++) {
        6: 1731:				 SV**restrict val_sv = av_fetch(row, j, 0);
        6: 1731-block  0
call    0 returned 6
call    1 returned 6
        6: 1732:				 double val = SvNV(*val_sv);
        -: 1733:				 row_sum[i] += val;
       12: 1734:				 col_sum[j] += val;
        3: 1734-block  0
       12: 1734-block  1
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 1735:				 grand_total += val;
        9: 1735-block  0
call    0 returned 9
        9: 1736:			}
call    0 returned 9
        -: 1737:		}
        3: 1738:		for (unsigned int i = 0; i < r; i++) {
       12: 1739:			AV*restrict exp_row = newAV();
        3: 1739-block  0
       12: 1739-block  1
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 1740:			SV**restrict row_sv = av_fetch(obs_av, i, 0);
        9: 1740-block  0
call    0 returned 9
        9: 1741:			AV*restrict row = (AV*)SvRV(*row_sv);
call    0 returned 9
        9: 1742:			for (unsigned int j = 0; j < c; j++) {
call    0 returned 9
call    1 returned 9
        9: 1743:				double E = (row_sum[i] * col_sum[j]) / grand_total;
        -: 1744:				SV**restrict val_sv = av_fetch(row, j, 0);
        3: 1745:				double O = SvNV(*val_sv);
        3: 1745-block  0
        -: 1746:				av_push(exp_row, newSVnv(E));
        9: 1747:				if (yates) {
        9: 1747-block  0
call    0 returned 9
        9: 1748:				  // Exact R logic: min(0.5, abs(O - E))
call    0 returned 9
        9: 1749:				  double abs_diff = fabs(O - E);
call    0 returned 9
call    1 returned 9
        9: 1750:				  double y_corr = (abs_diff > 0.5) ? 0.5 : abs_diff;
call    0 returned 9
call    1 returned 9
        9: 1751:				  double diff = abs_diff - y_corr;
call    0 returned 9
call    1 returned 9
        9: 1752:				  stat += (diff * diff) / E;
call    0 returned 9
call    1 returned 9
        9: 1753:				} else {
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        6: 1754:				  stat += ((O - E) * (O - E)) / E;
        6: 1754-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 1755:				}
        3: 1755-block  0
call    0 returned 3
call    1 returned 3
        -: 1756:			}
        3: 1757:			av_push(expected_av, newRV_noinc((SV*)exp_row));
        3: 1757-block  0
call    0 returned 3
call    1 returned 3
        -: 1758:		}
        -: 1759:		safefree(row_sum); safefree(col_sum);
        3: 1760:		df = (r - 1) * (c - 1);
        3: 1760-block  0
call    0 returned 3
call    1 returned 3
        -: 1761:	} else {
        9: 1762:	  for (unsigned int j = 0; j < c; j++) {
        9: 1762-block  0
call    0 returned 9
        -: 1763:		   SV**restrict val_sv = av_fetch(obs_av, j, 0);
        -: 1764:		   grand_total += SvNV(*val_sv);
        -: 1765:	  }
        -: 1766:	  double E = grand_total / (double)c;
        -: 1767:	  for (unsigned int j = 0; j < c; j++) {
        -: 1768:		   SV**restrict val_sv = av_fetch(obs_av, j, 0);
        -: 1769:		   double O = SvNV(*val_sv);
        -: 1770:		   av_push(expected_av, newSVnv(E));
        -: 1771:		   stat += ((O - E) * (O - E)) / E;
       37: 1772:	  }
       37: 1773:	  df = c - 1;
       37: 1774:	}
        -: 1775:	double p_val = get_p_value(stat, df);
        -: 1776:	HV*restrict results = newHV();
       37: 1777:	hv_store(results, "statistic", 9, newSVnv(stat), 0);
branch  0 taken 37 (fallthrough)
branch  1 taken 0
       37: 1777-block  0
branch  2 taken 37 (fallthrough)
branch  3 taken 0
       37: 1778:	hv_store(results, "df", 2, newSViv(df), 0);
       37: 1779:	hv_store(results, "p_value", 7, newSVnv(p_val), 0);
       37: 1779-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 28
        9: 1779-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
       37: 1780:	hv_store(results, "expected", 8, newRV_noinc((SV*)expected_av), 0);
       37: 1781:	if (is_2d) {
       37: 1781-block  0
        -: 1782:		if (yates) {
        -: 1783:			hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test with Yates' continuity correction", 0), 0);
       37: 1784:		} else {
       37: 1784-block  0
branch  0 taken 37 (fallthrough)
branch  1 taken 0
       37: 1785:			hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test", 0), 0);
       37: 1786:		}
       37: 1786-block  0
        -: 1787:	} else {
        -: 1788:	  hv_store(results, "method", 6, newSVpv("Chi-squared test for given probabilities", 0), 0);
       37: 1789:	}
       37: 1790:	RETVAL = newRV_noinc((SV*)results);
       37: 1790-block  0
call    0 returned 37
call    1 returned 37
       37: 1791:}
        -: 1792:OUTPUT:
        -: 1793:	RETVAL
      104: 1794:
       67: 1794-block  0
      104: 1794-block  1
branch  0 taken 67
branch  1 taken 37 (fallthrough)
      67*: 1795:PROTOTYPES: ENABLE
       67: 1795-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 67
    %%%%%: 1795-block  1
call    2 never executed
       67: 1796:
       67: 1796-block  0
call    0 returned 67
       67: 1797:void write_table(...)
      67*: 1798:PPCODE:
branch  0 taken 0 (fallthrough)
branch  1 taken 67
    %%%%%: 1798-block  0
       67: 1799:{
       67: 1799-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 55
       12: 1799-block  1
      55*: 1800:	SV *restrict data_sv = NULL;
       55: 1800-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 55
    %%%%%: 1800-block  1
       55: 1801:	SV *restrict file_sv = NULL;
       55: 1801-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 31
       24: 1801-block  1
       31: 1802:	unsigned int arg_idx = 0;
       31: 1802-block  0
branch  0 taken 31 (fallthrough)
branch  1 taken 0
       31: 1802-block  1
call    2 returned 31
    #####: 1803:
    %%%%%: 1803-block  0
call    0 never executed
        -: 1804:	// Mimic the Perl shift logic
        -: 1805:	if (arg_idx < items && SvROK(ST(arg_idx))) {
       37: 1806:	  int type = SvTYPE(SvRV(ST(arg_idx)));
       37: 1806-block  0
branch  0 taken 37 (fallthrough)
branch  1 taken 0
       37: 1806-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 37
    #####: 1807:	  if (type == SVt_PVHV || type == SVt_PVAV) {
    %%%%%: 1807-block  0
call    0 never executed
        -: 1808:		   data_sv = ST(arg_idx);
       37: 1809:		   arg_idx++;
       37: 1810:	  }
       37: 1810-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 28
        9: 1810-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 1811:	}
    %%%%%: 1811-block  0
call    0 never executed
        -: 1812:	if (arg_idx < items) {
        -: 1813:	  file_sv = ST(arg_idx);
      37*: 1814:	  arg_idx++;
       37: 1814-block  0
branch  0 taken 37 (fallthrough)
branch  1 taken 0
       37: 1814-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 37
    %%%%%: 1814-block  2
call    4 never executed
       37: 1815:	}
       37: 1815-block  0
call    0 returned 37
        -: 1816:
       37: 1817:	const char *restrict sep = ",";
branch  0 taken 12 (fallthrough)
branch  1 taken 25
       12: 1817-block  0
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 1818:	SV *restrict row_names_sv = sv_2mortal(newSViv(1));
       12: 1818-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9: 1818-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
        3: 1819:	SV *restrict col_names_sv = NULL;
        3: 1819-block  0
call    0 returned 0
        -: 1820:
        -: 1821:	// Read the remaining Hash-style arguments
        -: 1822:	for (; arg_idx < items; arg_idx += 2) {
       34: 1823:	  if (arg_idx + 1 >= items) croak("write_table: Odd number of arguments passed");
       34: 1824:	  const char *restrict key = SvPV_nolen(ST(arg_idx));
        -: 1825:	  SV *restrict val = ST(arg_idx + 1);
        -: 1826:	  if (strEQ(key, "data")) data_sv = val;
       34: 1827:	  else if (strEQ(key, "col.names")) col_names_sv = val;
       34: 1827-block  0
branch  0 taken 25 (fallthrough)
branch  1 taken 9
       25: 1828:	  else if (strEQ(key, "file")) file_sv = val;
      25*: 1829:	  else if (strEQ(key, "row.names")) row_names_sv = val;
       25: 1829-block  0
call    0 returned 25
branch  1 taken 0 (fallthrough)
branch  2 taken 25
    %%%%%: 1829-block  1
        -: 1830:	  else if (strEQ(key, "sep")) sep = SvPV_nolen(val);
       25: 1831:	  else croak("write_table: Unknown arguments passed: %s", key);
       25: 1831-block  0
call    0 returned 25
       25: 1832:	}
call    0 returned 25
       25: 1833:
branch  0 taken 25 (fallthrough)
branch  1 taken 0
       25: 1833-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 25
    #####: 1834:	if (!data_sv || !SvROK(data_sv)) {
    %%%%%: 1834-block  0
call    0 never executed
        -: 1835:	  croak("write_table: 'data' must be a HASH or ARRAY reference\n");
       25: 1836:	}
       25: 1837:	SV *restrict data_ref = SvRV(data_sv);
       25: 1837-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 12
       13: 1837-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 13
    #####: 1838:	if (SvTYPE(data_ref) != SVt_PVHV && SvTYPE(data_ref) != SVt_PVAV) {
    %%%%%: 1838-block  0
call    0 never executed
        -: 1839:	  croak("write_table: 'data' must be a HASH or ARRAY reference\n");
        -: 1840:	}
       25: 1841:
       25: 1842:	if (!file_sv || !SvOK(file_sv)) croak("write_table: file name missing\n");
        -: 1843:	const char *restrict file = SvPV_nolen(file_sv);
       25: 1844:
       25: 1844-block  0
call    0 returned 25
       88: 1845:	if (col_names_sv && SvOK(col_names_sv)) {
       88: 1845-block  0
call    0 returned 88
branch  1 taken 63
branch  2 taken 25 (fallthrough)
       63: 1846:	  if (!SvROK(col_names_sv) || SvTYPE(SvRV(col_names_sv)) != SVt_PVAV) {
       63: 1846-block  0
call    0 returned 63
       63: 1847:		   croak("write_table: 'col.names' must be an ARRAY reference\n");
branch  0 taken 63 (fallthrough)
branch  1 taken 0
       63: 1847-block  0
branch  2 taken 63 (fallthrough)
branch  3 taken 0
       63: 1847-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 63
    #####: 1848:	  }
    %%%%%: 1848-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 1848-block  1
    %%%%%: 1848-block  2
    %%%%%: 1848-block  3
call    2 never executed
        -: 1849:	}
        -: 1850:
        -: 1851:	bool is_hoh = 0, is_hoa = 0, is_aoh = 0;
       25: 1852:	AV *restrict rows_av = NULL;
       25: 1852-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 13
       12: 1853:
       12: 1853-block  0
call    0 returned 12
       12: 1854:	// Validate Input Structures & Homogeneity 
call    0 returned 12
       36: 1855:	if (SvTYPE(data_ref) == SVt_PVHV) {
       36: 1855-block  0
call    0 returned 36
branch  1 taken 24
branch  2 taken 12
       24: 1856:		HV *restrict hv = (HV*)data_ref;
       24: 1856-block  0
call    0 returned 24
call    1 returned 24
call    2 returned 24
        -: 1857:		if (hv_iterinit(hv) == 0) XSRETURN_EMPTY;
        -: 1858:
        -: 1859:		HE *restrict entry = hv_iternext(hv);
        9: 1860:		SV *restrict first_val = hv_iterval(hv, entry);
       9*: 1861:		if (!first_val || !SvROK(first_val)) {
        9: 1861-block  0
call    0 returned 9
branch  1 taken 0 (fallthrough)
branch  2 taken 9
    %%%%%: 1861-block  1
        9: 1862:			croak("write_table: Data values must be either all HASHes or all ARRAYs\n");
        9: 1862-block  0
call    0 returned 9
        9: 1863:		}
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1863-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 1863-block  1
branch  4 taken 9 (fallthrough)
branch  5 taken 0
        9: 1863-block  2
branch  6 taken 0 (fallthrough)
branch  7 taken 9
    #####: 1864:		int first_type = SvTYPE(SvRV(first_val));
    %%%%%: 1864-block  0
call    0 never executed
        -: 1865:		if (first_type != SVt_PVHV && first_type != SVt_PVAV) {
        -: 1866:			croak("write_table: Data values must be either all HASHes or all ARRAYs\n");
       30: 1867:		}
        9: 1867-block  0
       21: 1867-block  1
       30: 1867-block  2
call    0 returned 30
branch  1 taken 21
branch  2 taken 9 (fallthrough)
       21: 1868:		is_hoh = (first_type == SVt_PVHV);
       21: 1868-block  0
call    0 returned 21
       21: 1869:		is_hoa = (first_type == SVt_PVAV);
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 1869-block  0
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 1869-block  1
branch  4 taken 21 (fallthrough)
branch  5 taken 0
       21: 1869-block  2
branch  6 taken 0 (fallthrough)
branch  7 taken 21
    #####: 1870:		hv_iterinit(hv);
    %%%%%: 1870-block  0
call    0 never executed
        -: 1871:		while ((entry = hv_iternext(hv))) {
        -: 1872:			SV *restrict val = hv_iterval(hv, entry);
        9: 1873:			if (!val || !SvROK(val) || SvTYPE(SvRV(val)) != first_type) {
        9: 1873-block  0
        -: 1874:				 croak("write_table: Mixed data types detected. Ensure all values are %s references.\n", is_hoh ? "HASH" : "ARRAY");
        -: 1875:			}
       34: 1876:		}
       34: 1876-block  0
call    0 returned 34
      34*: 1877:		if (is_hoh) {
branch  0 taken 0 (fallthrough)
branch  1 taken 34
    %%%%%: 1877-block  0
call    2 never executed
        -: 1878:			rows_av = newAV();
       34: 1879:			hv_iterinit(hv);
       34: 1879-block  0
call    0 returned 34
       34: 1880:			while ((entry = hv_iternext(hv))) {
branch  0 taken 34 (fallthrough)
branch  1 taken 0
       34: 1880-block  0
call    2 returned 34
branch  3 taken 25 (fallthrough)
branch  4 taken 9
       25: 1880-block  1
        9: 1880-block  2
       34: 1881:				 av_push(rows_av, newSVsv(hv_iterkeysv(entry)));
        -: 1882:			}
        -: 1883:		}
       34: 1884:	} else {
       34: 1884-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 22
       15: 1885:		AV *restrict av = (AV*)data_ref;
       12: 1885-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        3: 1885-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1885-block  2
        3: 1886:		if (av_len(av) < 0) XSRETURN_EMPTY;
       12: 1887:		SV **restrict first_ptr = av_fetch(av, 0, 0);
        3: 1887-block  0
        9: 1887-block  1
       12: 1887-block  2
call    0 returned 12
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        9: 1888:		if (!first_ptr || !*first_ptr || !SvROK(*first_ptr) || SvTYPE(SvRV(*first_ptr)) != SVt_PVHV) {
        9: 1888-block  0
call    0 returned 9
        9: 1889:			croak("write_table: For ARRAY data, all elements must be HASH references (Array of Hashes)\n");
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1889-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 1889-block  1
call    4 returned 9
call    5 returned 9
        -: 1890:		}
        -: 1891:
        9: 1892:		for (size_t i = 0; i <= av_len(av); i++) {
        9: 1892-block  0
call    0 returned 9
        9: 1893:			SV **restrict ptr = av_fetch(av, i, 0);
call    0 returned 9
        -: 1894:			if (!ptr || !*ptr || !SvROK(*ptr) || SvTYPE(SvRV(*ptr)) != SVt_PVHV) {
       27: 1895:				 croak("write_table: Mixed data types detected in Array of Hashes. All elements must be HASH references.\n");
       27: 1895-block  0
call    0 returned 27
branch  1 taken 18
branch  2 taken 9 (fallthrough)
       18: 1896:			}
       18: 1896-block  0
call    0 returned 18
       18: 1897:		}
call    0 returned 18
        -: 1898:		is_aoh = 1;
       54: 1899:	}
       54: 1899-block  0
call    0 returned 54
branch  1 taken 36
branch  2 taken 18 (fallthrough)
       36: 1900:
       36: 1900-block  0
call    0 returned 36
call    1 returned 36
call    2 returned 36
        -: 1901:	PerlIO *restrict fh = PerlIO_open(file, "w");
        -: 1902:	if (!fh) croak("write_table: Could not open '%s' for writing", file);
        9: 1903:
        9: 1903-block  0
call    0 returned 9
        9: 1904:	AV *restrict headers_av = newAV();
call    0 returned 9
       36: 1905:	bool inc_rownames = (row_names_sv && SvTRUE(row_names_sv)) ? 1 : 0;
       36: 1905-block  0
branch  0 taken 27
branch  1 taken 9 (fallthrough)
       27: 1906:	const char *restrict rownames_col = NULL;
       27: 1906-block  0
call    0 returned 27
       27: 1907:
call    0 returned 27
call    1 returned 27
        -: 1908:	// ----- Hash of Hashes -----
        9: 1909:	if (is_hoh) {
        9: 1909-block  0
call    0 returned 9
       36: 1910:		if (col_names_sv && SvOK(col_names_sv)) {
       27: 1910-block  0
call    0 returned 27
call    1 returned 27
       36: 1910-block  1
branch  2 taken 27
branch  3 taken 9 (fallthrough)
        9: 1911:			AV *restrict c_av = (AV*)SvRV(col_names_sv);
        9: 1911-block  0
call    0 returned 9
        9: 1912:			for(size_t i=0; i<=av_len(c_av); i++) {
call    0 returned 9
        -: 1913:				SV **restrict c = av_fetch(c_av, i, 0);
       12: 1914:				if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
       12: 1914-block  0
call    0 returned 12
       12: 1915:			}
call    0 returned 12
        -: 1916:		} else {
       12: 1917:			HV *restrict col_map = newHV();
       12: 1918:			hv_iterinit((HV*)data_ref);
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 1918-block  0
       48: 1919:			HE *restrict entry;
       12: 1919-block  0
       48: 1919-block  1
branch  0 taken 36
branch  1 taken 12 (fallthrough)
       36: 1920:			while((entry = hv_iternext((HV*)data_ref))) {
       36: 1920-block  0
call    0 returned 36
      36*: 1921:				HV *restrict inner = (HV*)SvRV(hv_iterval((HV*)data_ref, entry));
branch  0 taken 36 (fallthrough)
branch  1 taken 0
       36: 1921-block  0
branch  2 taken 36 (fallthrough)
branch  3 taken 0
       36: 1921-block  1
call    4 returned 36
    %%%%%: 1921-block  2
       36: 1921-block  3
        -: 1922:				hv_iterinit(inner);
       12: 1923:				HE *restrict inner_entry;
       12: 1923-block  0
call    0 returned 12
       12: 1924:				while((inner_entry = hv_iternext(inner))) {
call    0 returned 12
        -: 1925:					hv_store_ent(col_map, hv_iterkeysv(inner_entry), newSViv(1), 0);
       12: 1926:				}
call    0 returned 12
       12: 1927:			}
call    0 returned 12
       36: 1928:			unsigned num_cols = hv_iterinit(col_map);
       36: 1928-block  0
branch  0 taken 24
branch  1 taken 12 (fallthrough)
       24: 1929:			const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
       24: 1929-block  0
call    0 returned 24
call    1 returned 24
        -: 1930:			for(unsigned i=0; i<num_cols; i++) {
       12: 1931:				HE *restrict ce = hv_iternext(col_map);
       12: 1931-block  0
call    0 returned 12
        -: 1932:				col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
       12: 1933:			}
       12: 1934:			qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
call    0 returned 12
        -: 1935:			for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
       33: 1936:			safefree(col_array);
       33: 1936-block  0
branch  0 taken 24
branch  1 taken 9 (fallthrough)
       24: 1937:			SvREFCNT_dec(col_map);
       24: 1938:	  }
       24: 1938-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 1938-block  1
        -: 1939:	  size_t num_headers = av_len(headers_av) + 1;
       24: 1940:	  const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
       24: 1940-block  0
call    0 returned 24
      24*: 1941:
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 1941-block  0
    %%%%%: 1941-block  1
        -: 1942:	  size_t h_idx = 0;
       99: 1943:	  if (inc_rownames) header_row[h_idx++] = "";
       24: 1943-block  0
       75: 1943-block  1
       99: 1943-block  2
branch  0 taken 78
branch  1 taken 21 (fallthrough)
       78: 1944:	  for(unsigned short int i=0; i<num_headers; i++) {
       78: 1944-block  0
call    0 returned 78
      78*: 1945:		   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
branch  0 taken 78 (fallthrough)
branch  1 taken 0
       78: 1945-block  0
branch  2 taken 78 (fallthrough)
branch  3 taken 0
       78: 1945-block  1
call    4 returned 78
    %%%%%: 1945-block  2
      78*: 1946:		   header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
       78: 1946-block  0
branch  0 taken 78 (fallthrough)
branch  1 taken 0
       78: 1946-block  1
call    2 returned 78
    %%%%%: 1946-block  2
       78: 1947:	  }
       78: 1947-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 30
       48: 1947-block  1
branch  2 taken 45 (fallthrough)
branch  3 taken 3
       45: 1948:	  print_string_row(fh, header_row, h_idx, sep);
       45: 1948-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 42
        3: 1949:	  safefree(header_row);
        3: 1949-block  0
call    0 returned 3
        3: 1950:
call    0 returned 3
        3: 1951:	  size_t num_rows = av_len(rows_av) + 1;
call    0 returned 3
        3: 1952:	  const char **restrict row_array = safemalloc(num_rows * sizeof(char*));
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1952-block  0
call    2 returned 3
        3: 1953:	  for(size_t i=0; i<num_rows; i++) {
        3: 1953-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 1953-block  1
call    2 returned 3
        3: 1954:	  	row_array[i] = SvPV_nolen(*av_fetch(rows_av, i, 0));
        3: 1954-block  0
call    0 returned 0
        -: 1955:	  }
       42: 1956:	  qsort(row_array, num_rows, sizeof(char*), cmp_string_wt);
       42: 1956-block  0
call    0 returned 42
        -: 1957:
       33: 1958:	  HV *restrict data_hv = (HV*)data_ref;
       33: 1958-block  0
        -: 1959:	  const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
        -: 1960:
       21: 1961:	  for(size_t i=0; i<num_rows; i++) {
       21: 1961-block  0
call    0 returned 21
        -: 1962:		   size_t d_idx = 0;
        9: 1963:		   if (inc_rownames) row_data[d_idx++] = row_array[i];
        9: 1963-block  0
call    0 returned 9
call    1 returned 9
        -: 1964:
       22: 1965:		   SV **restrict inner_hv_ptr = hv_fetch(data_hv, row_array[i], strlen(row_array[i]), 0);
       22: 1965-block  0
branch  0 taken 13 (fallthrough)
branch  1 taken 9
       13: 1966:		   HV *restrict inner_hv = inner_hv_ptr ? (HV*)SvRV(*inner_hv_ptr) : NULL;
       13: 1967:
       13: 1968:		   for(size_t j=0; j<num_headers; j++) {
       13: 1968-block  0
call    0 returned 13
        -: 1969:		       SV**restrict h_ptr = av_fetch(headers_av, j, 0);
       52: 1970:		       const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
       52: 1970-block  0
call    0 returned 52
branch  1 taken 39
branch  2 taken 13 (fallthrough)
       39: 1971:		       SV **restrict cell_ptr = inner_hv ? hv_fetch(inner_hv, col_name, strlen(col_name), 0) : NULL;
       39: 1971-block  0
call    0 returned 39
       39: 1972:		       if (cell_ptr && SvOK(*cell_ptr)) {
call    0 returned 39
       39: 1973:		           if (SvROK(*cell_ptr)) {
branch  0 taken 15 (fallthrough)
branch  1 taken 24
       15: 1973-block  0
        -: 1974:		               PerlIO_close(fh);
        -: 1975:		               safefree(row_array);
       16: 1976:                     safefree(row_data);
       13: 1976-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 10
        3: 1976-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 1976-block  2
        3: 1977:                       if (headers_av) SvREFCNT_dec(headers_av);
       12: 1978:                       if (rows_av) SvREFCNT_dec(rows_av);
        3: 1978-block  0
        9: 1978-block  1
       12: 1978-block  2
call    0 returned 12
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        9: 1979:		               croak("write_table: Cannot write nested reference types to table\n");
        9: 1979-block  0
call    0 returned 9
        9: 1980:		           }
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 1980-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 1980-block  1
call    4 returned 9
call    5 returned 9
        -: 1981:		           row_data[d_idx++] = SvPV_nolen(*cell_ptr);
        -: 1982:		       } else {
       10: 1983:		           row_data[d_idx++] = "NA";
       10: 1983-block  0
call    0 returned 10
       10: 1984:		       }
call    0 returned 10
       40: 1985:		   }
       40: 1985-block  0
branch  0 taken 30
branch  1 taken 10 (fallthrough)
       30: 1986:		   print_string_row(fh, row_data, d_idx, sep);
       30: 1986-block  0
call    0 returned 30
       30: 1987:	  }
call    0 returned 30
call    1 returned 30
        -: 1988:	  safefree(row_array); safefree(row_data);
       10: 1989:
       10: 1989-block  0
call    0 returned 10
       40: 1990:	} else if (is_hoa) { // ----- Hash of Arrays -----
       30: 1990-block  0
call    0 returned 30
call    1 returned 30
       40: 1990-block  1
branch  2 taken 30
branch  3 taken 10 (fallthrough)
       10: 1991:		HV *restrict data_hv = (HV*)data_ref;
       10: 1991-block  0
call    0 returned 10
        -: 1992:		size_t max_rows = 0;
      13*: 1993:		hv_iterinit(data_hv);
       13: 1993-block  0
call    0 returned 13
branch  1 taken 0 (fallthrough)
branch  2 taken 13
    %%%%%: 1993-block  1
call    3 never executed
       13: 1994:		HE *restrict entry;
       13: 1994-block  0
branch  0 taken 10 (fallthrough)
branch  1 taken 3
       10: 1994-block  1
call    2 returned 10
branch  3 taken 0 (fallthrough)
branch  4 taken 10
    #####: 1995:		while((entry = hv_iternext(data_hv))) {
    %%%%%: 1995-block  0
call    0 never executed
    #####: 1996:			AV *restrict arr = (AV*)SvRV(hv_iterval(data_hv, entry));
call    0 never executed
call    1 never executed
        -: 1997:			size_t len = av_len(arr) + 1;
    #####: 1998:			if (len > max_rows) max_rows = len;
    %%%%%: 1998-block  0
    %%%%%: 1998-block  1
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 1999:		}
    %%%%%: 1999-block  0
call    0 never executed
    #####: 2000:
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2000-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2000-block  1
    #####: 2001:		if (col_names_sv && SvOK(col_names_sv)) {
    #####: 2002:			AV *restrict c_av = (AV*)SvRV(col_names_sv);
    %%%%%: 2002-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 2003:			for(size_t i=0; i<=av_len(c_av); i++) {
    %%%%%: 2003-block  0
call    0 never executed
call    1 never executed
        -: 2004:				 SV **restrict c = av_fetch(c_av, i, 0);
        -: 2005:				 if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
    #####: 2006:			}
    %%%%%: 2006-block  0
call    0 never executed
    #####: 2007:		} else {
        -: 2008:			unsigned int num_cols = hv_iterinit(data_hv);
       13: 2009:			const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
       13: 2009-block  0
call    0 returned 13
       13: 2010:			for(unsigned int i=0; i<num_cols; i++) {
call    0 returned 13
       13: 2011:				 HE *restrict ce = hv_iternext(data_hv);
       13: 2012:				 col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
branch  0 taken 10 (fallthrough)
branch  1 taken 3
       10: 2012-block  0
       52: 2013:			}
       13: 2013-block  0
       52: 2013-block  1
branch  0 taken 39
branch  1 taken 13 (fallthrough)
       39: 2014:			qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
       39: 2014-block  0
call    0 returned 39
      39*: 2015:			for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2015-block  0
branch  2 taken 39 (fallthrough)
branch  3 taken 0
       39: 2015-block  1
call    4 returned 39
    %%%%%: 2015-block  2
       39: 2015-block  3
        -: 2016:			safefree(col_array);
       13: 2017:		}
       13: 2017-block  0
call    0 returned 13
       13: 2018:		if (av_len(headers_av) < 0) croak("Could not get headers in write_table");
call    0 returned 13
       13: 2019:		if (inc_rownames && contains_nondigit(row_names_sv)) {
call    0 returned 13
       78: 2020:			rownames_col = SvPV_nolen(row_names_sv);
       65: 2020-block  0
       78: 2020-block  1
branch  0 taken 65
branch  1 taken 13 (fallthrough)
       65: 2021:			AV *restrict filtered_headers = (AV*)sv_2mortal((SV*)newAV());
       65: 2022:
       65: 2022-block  0
branch  0 taken 53 (fallthrough)
branch  1 taken 12
       53: 2023:			for(size_t i=0; i<=av_len(headers_av); i++) {
       53: 2023-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 53
    #####: 2024:				 SV**restrict h_ptr = av_fetch(headers_av, i, 0);
    %%%%%: 2024-block  0
call    0 never executed
    #####: 2025:				 if (!h_ptr || !*h_ptr) continue;
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:				 SV *restrict h_sv = *h_ptr;
    #####: 2027:				 if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
    %%%%%: 2027-block  0
call    0 never executed
    #####: 2028:				     av_push(filtered_headers, newSVsv(h_sv));
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2028-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 2029:				 }
    %%%%%: 2029-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 2030:			}
    %%%%%: 2030-block  0
call    0 never executed
    #####: 2031:			SvREFCNT_dec(headers_av);
call    0 never executed
    #####: 2032:			headers_av = filtered_headers;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2032-block  0
call    2 never executed
    #####: 2033:		}
    %%%%%: 2033-block  0
call    0 never executed
        -: 2034:		size_t num_headers = av_len(headers_av) + 1;
    #####: 2035:		const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
    %%%%%: 2035-block  0
call    0 never executed
        -: 2036:		size_t h_idx = 0;
    #####: 2037:		if (inc_rownames) header_row[h_idx++] = "";
    %%%%%: 2037-block  0
        -: 2038:		for(size_t i=0; i<num_headers; i++) {
        -: 2039:			SV**restrict h_ptr = av_fetch(headers_av, i, 0);
    #####: 2040:			header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
    %%%%%: 2040-block  0
        -: 2041:		}
        -: 2042:		print_string_row(fh, header_row, h_idx, sep);
        -: 2043:		safefree(header_row);
       53: 2044:		const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
       53: 2045:		for(size_t i=0; i<max_rows; i++) {
       53: 2045-block  0
call    0 returned 53
        -: 2046:			size_t d_idx = 0;
        -: 2047:			if (inc_rownames) {
      260: 2048:				 if (rownames_col) {
       65: 2048-block  0
      195: 2048-block  1
      260: 2048-block  2
branch  0 taken 195
branch  1 taken 65 (fallthrough)
      195: 2049:				     SV **restrict rn_arr_ptr = hv_fetch(data_hv, rownames_col, strlen(rownames_col), 0);
      195: 2049-block  0
call    0 returned 195
     195*: 2050:				     if (rn_arr_ptr && SvROK(*rn_arr_ptr)) {
branch  0 taken 195 (fallthrough)
branch  1 taken 0
      195: 2050-block  0
branch  2 taken 195 (fallthrough)
branch  3 taken 0
      195: 2050-block  1
call    4 returned 195
    %%%%%: 2050-block  2
      195: 2051:				         AV *restrict rn_arr = (AV*)SvRV(*rn_arr_ptr);
      195: 2051-block  0
call    0 returned 195
      390: 2052:				         SV **restrict rn_val_ptr = av_fetch(rn_arr, i, 0);
branch  0 taken 195 (fallthrough)
branch  1 taken 0
      195: 2052-block  0
branch  2 taken 195 (fallthrough)
branch  3 taken 0
      195: 2052-block  1
      195: 2053:				         if (rn_val_ptr && SvOK(*rn_val_ptr)) {
      195: 2054:				             if (SvROK(*rn_val_ptr)) {
      195: 2054-block  0
call    0 returned 195
      195: 2055:				                 PerlIO_close(fh);
branch  0 taken 167 (fallthrough)
branch  1 taken 28
      167: 2055-block  0
branch  2 taken 104 (fallthrough)
branch  3 taken 63
      104: 2056:				                 safefree(row_data);
      104: 2056-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 104
    #####: 2057:				                 if (headers_av) SvREFCNT_dec(headers_av);
    %%%%%: 2057-block  0
call    0 never executed
    #####: 2058:				                 croak("write_table: Cannot write nested reference types to table\n");
call    0 never executed
    #####: 2059:				             }
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2059-block  0
call    2 never executed
    #####: 2060:				             row_data[d_idx++] = SvPV_nolen(*rn_val_ptr);
    %%%%%: 2060-block  0
call    0 never executed
        -: 2061:				         } else {
      104: 2062:				             row_data[d_idx++] = "NA";
      104: 2062-block  0
call    0 returned 104
        -: 2063:				         }
       91: 2064:				     } else {
       91: 2064-block  0
        -: 2065:				         row_data[d_idx++] = "NA";
        -: 2066:				     }
    #####: 2067:				 } else {
    %%%%%: 2067-block  0
        -: 2068:				     char buf[32];
        -: 2069:				     snprintf(buf, sizeof(buf), "%ld", (long)(i + 1));
       65: 2070:				     row_data[d_idx++] = savepv(buf);
       65: 2070-block  0
call    0 returned 65
       65: 2071:				 }
branch  0 taken 53 (fallthrough)
branch  1 taken 12
       53: 2071-block  0
branch  2 taken 53 (fallthrough)
branch  3 taken 0
       53: 2071-block  1
call    4 returned 53
        -: 2072:			}
       13: 2073:			for(size_t j=0; j<num_headers; j++) {
       13: 2073-block  0
call    0 returned 13
        9: 2074:				 SV**restrict h_ptr = av_fetch(headers_av, j, 0);
        9: 2074-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 2075:				 const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        9: 2076:				 SV **restrict arr_ptr = hv_fetch(data_hv, col_name, strlen(col_name), 0);
        9: 2076-block  0
call    0 returned 9
       12: 2077:				 if (arr_ptr && SvROK(*arr_ptr)) {
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 2077-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 2077-block  1
        3: 2078:				     AV *restrict arr = (AV*)SvRV(*arr_ptr);
        9: 2079:				     SV **restrict cell_ptr = av_fetch(arr, i, 0);
        3: 2079-block  0
        6: 2079-block  1
        9: 2079-block  2
call    0 returned 9
branch  1 taken 6
branch  2 taken 3 (fallthrough)
        6: 2080:				     if (cell_ptr && SvOK(*cell_ptr)) {
        6: 2080-block  0
call    0 returned 6
        6: 2081:				         if (SvROK(*cell_ptr)) {
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 2081-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 2081-block  1
call    4 returned 6
call    5 returned 6
        -: 2082:				             PerlIO_close(fh);
        -: 2083:				             safefree(row_data);
        6: 2084:				             if (headers_av) SvREFCNT_dec(headers_av);
        6: 2084-block  0
call    0 returned 6
       21: 2085:				             croak("write_table: Cannot write nested reference types to table\n");
       15: 2085-block  0
       21: 2085-block  1
branch  0 taken 15
branch  1 taken 6 (fallthrough)
       15: 2086:				         }
       15: 2086-block  0
call    0 returned 15
       15: 2087:				         row_data[d_idx++] = SvPV_nolen(*cell_ptr);
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 2087-block  0
branch  2 taken 15 (fallthrough)
branch  3 taken 0
       15: 2088:				     } else {
       15: 2089:				         row_data[d_idx++] = "NA";
       15: 2089-block  0
call    0 returned 15
        -: 2090:				     }
       42: 2091:				 } else {
       42: 2091-block  0
call    0 returned 42
branch  1 taken 27
branch  2 taken 15 (fallthrough)
       27: 2092:				     row_data[d_idx++] = "NA";
       27: 2092-block  0
call    0 returned 27
call    1 returned 27
call    2 returned 27
        -: 2093:				 }
        -: 2094:			}
        -: 2095:			print_string_row(fh, row_data, d_idx, sep);
        6: 2096:			if (inc_rownames && !rownames_col) safefree((char*)row_data[0]);
        6: 2096-block  0
call    0 returned 6
        6: 2097:		}
call    0 returned 6
       21: 2098:		safefree(row_data);
       21: 2098-block  0
branch  0 taken 15
branch  1 taken 6 (fallthrough)
       15: 2099:	} else if (is_aoh) {// ----- Array of Hashes -----
       15: 2099-block  0
call    0 returned 15
       15: 2100:		AV *restrict data_av = (AV*)data_ref;
call    0 returned 15
call    1 returned 15
        -: 2101:		size_t num_rows = av_len(data_av) + 1;
        6: 2102:		if (col_names_sv && SvOK(col_names_sv)) {
        6: 2102-block  0
call    0 returned 6
       21: 2103:			AV *restrict c_av = (AV*)SvRV(col_names_sv);
       15: 2103-block  0
call    0 returned 15
call    1 returned 15
       21: 2103-block  1
branch  2 taken 15
branch  3 taken 6 (fallthrough)
        6: 2104:			for(size_t i=0; i<=av_len(c_av); i++) {
        6: 2104-block  0
call    0 returned 6
        6: 2105:				 SV **restrict c = av_fetch(c_av, i, 0);
call    0 returned 6
        -: 2106:				 if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
        9: 2107:			}
        9: 2107-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 2107-block  1
call    2 returned 3
branch  3 taken 0 (fallthrough)
branch  4 taken 3
    #####: 2108:		} else {
    %%%%%: 2108-block  0
call    0 never executed
    #####: 2109:			HV *restrict col_map = newHV();
call    0 never executed
    #####: 2110:			for(size_t i=0; i<num_rows; i++) {
    %%%%%: 2110-block  0
    %%%%%: 2110-block  1
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 2111:				 SV **restrict row_ptr = av_fetch(data_av, i, 0);
    %%%%%: 2111-block  0
call    0 never executed
    #####: 2112:				 if (row_ptr && SvROK(*row_ptr)) {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2112-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2112-block  1
    #####: 2113:				     HV *restrict row_hv = (HV*)SvRV(*row_ptr);
    #####: 2114:				     hv_iterinit(row_hv);
    %%%%%: 2114-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 2115:				     HE *restrict entry;
    %%%%%: 2115-block  0
call    0 never executed
call    1 never executed
        -: 2116:				     while((entry = hv_iternext(row_hv))) {
        -: 2117:				         hv_store_ent(col_map, hv_iterkeysv(entry), newSViv(1), 0);
    #####: 2118:				     }
    %%%%%: 2118-block  0
call    0 never executed
    #####: 2119:				 }
        -: 2120:			}
        9: 2121:			unsigned num_cols = hv_iterinit(col_map);
        9: 2121-block  0
call    0 returned 9
        9: 2122:			const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
call    0 returned 9
        9: 2123:			for(unsigned int i=0; i<num_cols; i++) {
        9: 2124:				 HE *restrict ce = hv_iternext(col_map);
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 2124-block  0
       30: 2125:				 col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
        9: 2125-block  0
       30: 2125-block  1
branch  0 taken 21
branch  1 taken 9 (fallthrough)
       21: 2126:			}
       21: 2126-block  0
call    0 returned 21
      21*: 2127:			qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2127-block  0
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 2127-block  1
call    4 returned 21
    %%%%%: 2127-block  2
       21: 2127-block  3
        -: 2128:			for(unsigned int i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
        9: 2129:			safefree(col_array);
        9: 2129-block  0
call    0 returned 9
        9: 2130:			SvREFCNT_dec(col_map);
call    0 returned 9
        9: 2131:		}
call    0 returned 9
       30: 2132:		if (inc_rownames && contains_nondigit(row_names_sv)) {
       21: 2132-block  0
       30: 2132-block  1
branch  0 taken 21
branch  1 taken 9 (fallthrough)
       21: 2133:			rownames_col = SvPV_nolen(row_names_sv);
       21: 2134:			AV *restrict filtered_headers = newAV();
       21: 2134-block  0
call    0 returned 21
      21*: 2135:			for(size_t i=0; i<=av_len(headers_av); i++) {
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2135-block  0
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 2135-block  1
    %%%%%: 2135-block  2
       21: 2136:				 SV**restrict h_ptr = av_fetch(headers_av, i, 0);
       21: 2136-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 12
        9: 2137:				 if (!h_ptr || !*h_ptr) continue;
        9: 2137-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    #####: 2138:				 SV *restrict h_sv = *h_ptr;
    %%%%%: 2138-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2138-block  1
call    2 never executed
    %%%%%: 2138-block  2
    #####: 2139:				 if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
    %%%%%: 2139-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2139-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 2140:				     av_push(filtered_headers, newSVsv(h_sv));
    %%%%%: 2140-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 2141:				 }
    %%%%%: 2141-block  0
call    0 never executed
    #####: 2142:			}
call    0 never executed
    #####: 2143:			SvREFCNT_dec(headers_av);
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2143-block  0
call    2 never executed
    #####: 2144:			headers_av = filtered_headers;
    %%%%%: 2144-block  0
call    0 never executed
        -: 2145:		}
    #####: 2146:		size_t num_headers = av_len(headers_av) + 1;
    %%%%%: 2146-block  0
call    0 never executed
        -: 2147:		const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
    #####: 2148:		size_t h_idx = 0;
    %%%%%: 2148-block  0
        -: 2149:		if (inc_rownames) header_row[h_idx++] = "";
        -: 2150:		for(size_t i=0; i<num_headers; i++) {
        -: 2151:			SV**restrict h_ptr = av_fetch(headers_av, i, 0);
        9: 2152:			header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        9: 2153:		}
        9: 2153-block  0
call    0 returned 9
        -: 2154:		print_string_row(fh, header_row, h_idx, sep);
        -: 2155:		safefree(header_row);
        -: 2156:		const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
       72: 2157:		for(size_t i=0; i<num_rows; i++) {
       21: 2157-block  0
       51: 2157-block  1
       72: 2157-block  2
branch  0 taken 51
branch  1 taken 21 (fallthrough)
       51: 2158:			size_t d_idx = 0;
       51: 2158-block  0
call    0 returned 51
      51*: 2159:			SV **restrict row_ptr = av_fetch(data_av, i, 0);
branch  0 taken 51 (fallthrough)
branch  1 taken 0
       51: 2159-block  0
branch  2 taken 51 (fallthrough)
branch  3 taken 0
       51: 2159-block  1
call    4 returned 51
    %%%%%: 2159-block  2
      51*: 2160:			HV *restrict row_hv = (row_ptr && SvROK(*row_ptr)) ? (HV*)SvRV(*row_ptr) : NULL;
       51: 2160-block  0
branch  0 taken 51 (fallthrough)
branch  1 taken 0
       51: 2160-block  1
call    2 returned 51
    %%%%%: 2160-block  2
       51: 2161:			if (inc_rownames) {
       51: 2161-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 12
       39: 2161-block  1
branch  2 taken 39 (fallthrough)
branch  3 taken 0
       39: 2162:				if (rownames_col) {
       39: 2162-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    #####: 2163:				  SV **restrict rn_val_ptr = row_hv ? hv_fetch(row_hv, rownames_col, strlen(rownames_col), 0) : NULL;
    %%%%%: 2163-block  0
call    0 never executed
    #####: 2164:				  if (rn_val_ptr && SvOK(*rn_val_ptr)) {
call    0 never executed
    #####: 2165:						if (SvROK(*rn_val_ptr)) {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2165-block  0
call    2 never executed
    #####: 2166:							 PerlIO_close(fh);
    %%%%%: 2166-block  0
call    0 never executed
        -: 2167:								  safefree(row_data);
       39: 2168:								  if (headers_av) SvREFCNT_dec(headers_av);
       39: 2168-block  0
call    0 returned 39
        -: 2169:							 croak("write_table: Cannot write nested reference types to table\n");
       12: 2170:						}
       12: 2170-block  0
        -: 2171:						row_data[d_idx++] = SvPV_nolen(*rn_val_ptr);
        -: 2172:				  } else {
       21: 2173:						row_data[d_idx++] = "NA";
       21: 2173-block  0
call    0 returned 21
       21: 2174:				  }
branch  0 taken 9 (fallthrough)
branch  1 taken 12
        9: 2174-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 2174-block  1
call    4 returned 9
        -: 2175:				} else {
        9: 2176:				  char buf[32];
        9: 2176-block  0
call    0 returned 9
        -: 2177:				  snprintf(buf, sizeof(buf), "%ld", (long)(i + 1));
       31: 2178:				  row_data[d_idx++] = savepv(buf);
       31: 2178-block  0
branch  0 taken 31 (fallthrough)
branch  1 taken 0
       31: 2178-block  1
call    2 returned 31
       31: 2179:				}
       31: 2179-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 22
        9: 2179-block  1
call    2 returned 9
       31: 2180:			}
       31: 2180-block  0
call    0 returned 31
       31: 2181:
        -: 2182:			for(size_t j=0; j<num_headers; j++) {
        -: 2183:				 SV**restrict h_ptr = av_fetch(headers_av, j, 0);
        -: 2184:				 const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
        -: 2185:				 SV **restrict cell_ptr = row_hv ? hv_fetch(row_hv, col_name, strlen(col_name), 0) : NULL;
        -: 2186:				 if (cell_ptr && SvOK(*cell_ptr)) {
        -: 2187:				     if (SvROK(*cell_ptr)) {
       39: 2188:				         PerlIO_close(fh);
       39: 2189:                         safefree(row_data);
       39: 2189-block  0
call    0 returned 39
       39: 2190:                         if (headers_av) SvREFCNT_dec(headers_av);
call    0 returned 39
       39: 2191:				         croak("write_table: Cannot write nested reference types to table\n");
        -: 2192:				     }
        -: 2193:				     row_data[d_idx++] = SvPV_nolen(*cell_ptr);
       39: 2194:				 } else {
        -: 2195:				     row_data[d_idx++] = "NA";
       39: 2196:				 }
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2196-block  0
branch  2 taken 39 (fallthrough)
branch  3 taken 0
       39: 2196-block  1
branch  4 taken 39 (fallthrough)
branch  5 taken 0
       39: 2197:			}
       39: 2197-block  0
        -: 2198:			print_string_row(fh, row_data, d_idx, sep);
    #####: 2199:			if (inc_rownames && !rownames_col) safefree((char*)row_data[0]);
    %%%%%: 2199-block  0
call    0 never executed
        -: 2200:		}
      39*: 2201:		safefree(row_data);
       39: 2201-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2201-block  1
    %%%%%: 2201-block  2
      39*: 2202:	}
       39: 2202-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2202-block  1
    %%%%%: 2202-block  2
        -: 2203:	if (headers_av) SvREFCNT_dec(headers_av);
       39: 2204:	if (rows_av) SvREFCNT_dec(rows_av);
       39: 2204-block  0
call    0 returned 39
       39: 2205:	PerlIO_close(fh);
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    #####: 2206:	XSRETURN_EMPTY;
    %%%%%: 2206-block  0
call    0 never executed
        -: 2207:}
       39: 2208:
       39: 2208-block  0
call    0 returned 39
        -: 2209:SV*
    11925: 2210:_parse_csv_file(char* file, const char* sep_str, const char* comment_str, SV* callback = &PL_sv_undef)
    11925: 2210-block  0
call    0 returned 11925
branch  1 taken 11886
branch  2 taken 39 (fallthrough)
    11886: 2211:INIT:
    11886: 2211-block  0
call    0 returned 11886
    11886: 2212:	PerlIO *restrict fp;
        -: 2213:	AV *restrict data = NULL;
    11886: 2214:	AV *restrict current_row = newAV();
branch  0 taken 11886 (fallthrough)
branch  1 taken 0
    11886: 2214-block  0
branch  2 taken 11880 (fallthrough)
branch  3 taken 6
    11880: 2215:	SV *restrict field = newSVpvs("");
    11880: 2216:	bool in_quotes = 0, post_quote = 0;
    11880: 2216-block  0
branch  0 taken 11880 (fallthrough)
branch  1 taken 0
    11880: 2216-block  1
branch  2 taken 11088 (fallthrough)
branch  3 taken 792
    11088: 2217:	size_t sep_len, comment_len;
    11088: 2217-block  0
        -: 2218:	SV *restrict line_sv;
        -: 2219:	bool use_cb = 0;
    11886: 2220:CODE:
    11886: 2220-block  0
branch  0 taken 11883 (fallthrough)
branch  1 taken 3
        -: 2221:	if (SvOK(callback) && SvROK(callback) && SvTYPE(SvRV(callback)) == SVt_PVCV) {
    11883: 2222:		use_cb = 1;
    11886: 2223:	} else {
    11883: 2223-block  0
        3: 2223-block  1
    11886: 2223-block  2
branch  0 taken 11886
branch  1 taken 0 (fallthrough)
    11886: 2224:		data = newAV();
    11886: 2224-block  0
branch  0 taken 11886 (fallthrough)
branch  1 taken 0
    11886: 2224-block  1
branch  2 taken 11883 (fallthrough)
branch  3 taken 3
    11883: 2224-block  2
        -: 2225:	}
   11883*: 2226:	sep_len = sep_str ? strlen(sep_str) : 0;
    11883: 2226-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 11883
    %%%%%: 2226-block  1
        -: 2227:	comment_len = comment_str ? strlen(comment_str) : 0;
        -: 2228:
   11883*: 2229:	fp = PerlIO_open(file, "r");
    11883: 2229-block  0
branch  0 taken 11883 (fallthrough)
branch  1 taken 0
    11883: 2229-block  1
branch  2 taken 11883 (fallthrough)
branch  3 taken 0
    11883: 2229-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 11883
    #####: 2230:	if (!fp) {
    %%%%%: 2230-block  0
        -: 2231:		croak("Could not open file '%s'", file);
        -: 2232:	}
        -: 2233:	line_sv = newSV_type(SVt_PV);
   880764: 2234:	// Read line by line using PerlIO
    11886: 2234-block  0
   868878: 2234-block  1
   880764: 2234-block  2
branch  0 taken 868878
branch  1 taken 11886 (fallthrough)
   868878: 2235:	while (sv_gets(line_sv, fp, 0) != NULL) {
   868878: 2236:		char *restrict line = SvPV_nolen(line_sv);
   868878: 2236-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 868875
        3: 2236-block  1
        3: 2236-block  2
   868875: 2237:		size_t len = SvCUR(line_sv);
   868875: 2237-block  0
branch  0 taken 66975 (fallthrough)
branch  1 taken 801900
    66975: 2238:		// chomp \r\n (Handles Windows invisible \r natively)
    66975: 2238-block  0
branch  0 taken 33489 (fallthrough)
branch  1 taken 33486
    33489: 2238-block  1
branch  2 taken 33465 (fallthrough)
branch  3 taken 24
    33465: 2238-block  2
branch  4 taken 9 (fallthrough)
branch  5 taken 33456
        9: 2239:		if (len > 0 && line[len-1] == '\n') {
        9: 2239-block  0
call    0 returned 9
        9: 2240:			len--;
    66966: 2241:			if (len > 0 && line[len-1] == '\r') {
    66966: 2241-block  0
branch  0 taken 33480 (fallthrough)
branch  1 taken 33486
    33480: 2242:				len--;
    33480: 2243:			}
    33480: 2243-block  0
    33486: 2244:		}
    33486: 2244-block  0
branch  0 taken 33483 (fallthrough)
branch  1 taken 3
    33483: 2245:		if (!in_quotes) {
    33483: 2245-block  0
        -: 2246:			// Skip completely empty lines (\h*[\r\n]+$ equivalent)
   801900: 2247:			bool is_empty = 1;
   801900: 2247-block  0
branch  0 taken 616584 (fallthrough)
branch  1 taken 185316
   616584: 2247-block  1
branch  2 taken 616584 (fallthrough)
branch  3 taken 0
   616584: 2247-block  2
branch  4 taken 616584 (fallthrough)
branch  5 taken 0
   616584: 2247-block  3
branch  6 taken 154845 (fallthrough)
branch  7 taken 461739
   154845: 2248:			for (size_t i = 0; i < len; i++) {
   154845: 2248-block  0
call    0 returned 154845
call    1 returned 154845
   154845: 2249:				if (line[i] != ' ' && line[i] != '\t') { is_empty = 0; break; }
call    0 returned 154845
   154845: 2250:			}
   154845: 2251:			if (is_empty) continue;
        -: 2252:
   647055: 2253:			// Skip comments
   647055: 2253-block  0
call    0 returned 647055
        -: 2254:			if (comment_len > 0 && len >= comment_len && strncmp(line, comment_str, comment_len) == 0) {
        -: 2255:				continue;
    11886: 2256:			}
    11886: 2256-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 11880
        -: 2257:		}
        6: 2258:		// --- CORE PARSING MACHINE ---
        6: 2258-block  0
call    0 returned 6
        -: 2259:		for (size_t i = 0; i < len; i++) {
    11880: 2260:			const char ch = line[i];
        -: 2261:			if (ch == '\r') continue;
    11880: 2262:			if (ch == '"') {
    11880: 2262-block  0
call    0 returned 11880
call    1 returned 11880
    11880: 2263:				if (in_quotes && (i + 1 < len) && line[i+1] == '"') {
call    0 returned 11880
        -: 2264:					sv_catpvn(field, "\"", 1);
    11880: 2265:					i++; // Skip the escaped second quote
branch  0 taken 11880 (fallthrough)
branch  1 taken 0
    11880: 2266:				} else if (in_quotes) {
    11880: 2267:					in_quotes = 0;  // Close quotes
    11880: 2267-block  0
call    0 returned 11880
    11880: 2268:					post_quote = 1;
call    0 returned 11880
   11880*: 2269:				} else if (!post_quote) {
branch  0 taken 0 (fallthrough)
branch  1 taken 11880
    %%%%%: 2269-block  0
call    2 never executed
   11880*: 2270:					in_quotes = 1; // Open quotes (only when not in post-quote state)
    11880: 2270-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 11880
    %%%%%: 2270-block  1
call    2 never executed
    11880: 2270-block  2
call    3 returned 11880
call    4 returned 11880
    11880: 2271:				}
    11880: 2272:			} else if (!in_quotes && sep_len > 0 && (len - i) >= sep_len && strncmp(line + i, sep_str, sep_len) == 0) {
call    0 returned 11880
    11880: 2273:				av_push(current_row, newSVsv(field));
branch  0 taken 11880 (fallthrough)
branch  1 taken 0
    11880: 2273-block  0
call    2 returned 11880
    11880: 2274:				sv_setpvs(field, ""); // Reset for next field
    11880: 2274-block  0
call    0 returned 11880
    11880: 2275:				i += sep_len - 1;     // Advance past multi-char separators
call    0 returned 11880
        -: 2276:				post_quote = 0;
    #####: 2277:			} else {
    %%%%%: 2277-block  0
call    0 never executed
call    1 never executed
        -: 2278:				sv_catpvn(field, &ch, 1);
    11880: 2279:			}
    11880: 2279-block  0
call    0 returned 11880
        -: 2280:		}
        -: 2281:		if (in_quotes) {
       39: 2282:			// Line ended but quotes are still open! Append newline and fetch next
       39: 2282-block  0
call    0 returned 39
       39: 2283:			sv_catpvn(field, "\n", 1);
call    0 returned 39
        -: 2284:		} else {
       39: 2285:			post_quote = 0; // Reset post-quote state at row boundary
branch  0 taken 3 (fallthrough)
branch  1 taken 36
        3: 2286:			// Push the final field of the record
        3: 2286-block  0
call    0 returned 3
call    1 returned 3
        3: 2287:			av_push(current_row, newSVsv(field));
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 2288:			sv_setpvs(field, "");
        3: 2289:			// If a callback is provided, invoke it in a streaming fashion
        3: 2289-block  0
call    0 returned 3
        3: 2290:			if (use_cb) {
call    0 returned 3
       3*: 2291:				dSP;
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 2291-block  0
call    2 never executed
       3*: 2292:				ENTER;
        3: 2292-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 2292-block  1
call    2 never executed
        3: 2292-block  2
call    3 returned 3
call    4 returned 3
        3: 2293:				SAVETMPS;
        3: 2294:				PUSHMARK(SP);
call    0 returned 3
        3: 2295:				XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 2295-block  0
call    2 returned 3
        3: 2296:				PUTBACK;
        3: 2296-block  0
call    0 returned 3
        3: 2297:				call_sv(callback, G_DISCARD);
call    0 returned 3
        -: 2298:				FREETMPS;
    #####: 2299:				LEAVE;
    %%%%%: 2299-block  0
call    0 never executed
call    1 never executed
        -: 2300:				SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
        3: 2301:			} else {
        3: 2301-block  0
call    0 returned 3
        -: 2302:				av_push(data, newRV_noinc((SV*)current_row));
       39: 2303:			}
       39: 2303-block  0
call    0 returned 39
       39: 2304:			current_row = newAV();
call    0 returned 39
        -: 2305:		}
       39: 2306:	}
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2307:	PerlIO_close(fp);
       39: 2307-block  0
        -: 2308:	SvREFCNT_dec(line_sv);
    #####: 2309:
    %%%%%: 2309-block  0
call    0 never executed
        -: 2310:	if (in_quotes) {
        -: 2311:		av_push(current_row, newSVsv(field));
        -: 2312:		if (use_cb) {
        -: 2313:			dSP;
        -: 2314:			ENTER;
        -: 2315:			SAVETMPS;
        -: 2316:			PUSHMARK(SP);
        -: 2317:			XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
       12: 2318:			PUTBACK;
       12: 2318-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 2318-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 12
    #####: 2319:			call_sv(callback, G_DISCARD);
    %%%%%: 2319-block  0
call    0 never executed
        -: 2320:			FREETMPS;
       12: 2321:			LEAVE;
       12: 2321-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 2321-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 12
    #####: 2322:			SvREFCNT_dec(current_row);
    %%%%%: 2322-block  0
call    0 never executed
        -: 2323:		} else {
        -: 2324:			av_push(data, newRV_noinc((SV*)current_row));
        -: 2325:		}
       12: 2326:		current_row = newAV();
       12: 2326-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 6
        6: 2327:	}
        6: 2327-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 2328:	SvREFCNT_dec(field);
        3: 2328-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 2329:	SvREFCNT_dec(current_row);
    %%%%%: 2329-block  0
call    0 never executed
        -: 2330:	if (use_cb) {
        -: 2331:		RETVAL = &PL_sv_undef; // Memory was fully handled by callback stream
       12: 2332:	} else {
       12: 2333:		RETVAL = newRV_noinc((SV*)data);
       12: 2334:	}
       12: 2334-block  0
call    0 returned 12
       12: 2335:OUTPUT:
call    0 returned 12
        -: 2336:	RETVAL
       12: 2337:
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 2338:SV* cov(SV* x_sv, SV* y_sv, const char* method = "pearson")
    %%%%%: 2338-block  0
call    0 never executed
        -: 2339:	CODE:
        -: 2340:	{
        -: 2341:		// 1. Validate inputs are Array References
        -: 2342:		if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV) {
        -: 2343:			croak("cov: first argument 'x' must be an ARRAY reference");
       12: 2344:		}
       12: 2344-block  0
call    0 returned 12
       12: 2345:		if (!SvROK(y_sv) || SvTYPE(SvRV(y_sv)) != SVt_PVAV) {
call    0 returned 12
       12: 2346:			croak("cov: second argument 'y' must be an ARRAY reference");
        -: 2347:		}
       72: 2348:
       60: 2348-block  0
       72: 2348-block  1
branch  0 taken 60
branch  1 taken 12 (fallthrough)
       60: 2349:		// 2. Validate method argument
       60: 2349-block  0
call    0 returned 60
       60: 2350:		if (strcmp(method, "pearson") != 0 && 
call    0 returned 60
        -: 2351:			strcmp(method, "spearman") != 0 && 
        -: 2352:			strcmp(method, "kendall") != 0) {
      60*: 2353:			croak("cov: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')", method);
branch  0 taken 60 (fallthrough)
branch  1 taken 0
       60: 2353-block  0
branch  2 taken 60 (fallthrough)
branch  3 taken 0
       60: 2353-block  1
call    4 returned 60
branch  5 taken 60 (fallthrough)
branch  6 taken 0
       60: 2353-block  2
call    7 returned 60
    %%%%%: 2353-block  3
      60*: 2354:		}
       60: 2354-block  0
branch  0 taken 60 (fallthrough)
branch  1 taken 0
       60: 2354-block  1
branch  2 taken 60 (fallthrough)
branch  3 taken 0
       60: 2354-block  2
call    4 returned 60
branch  5 taken 60 (fallthrough)
branch  6 taken 0
       60: 2354-block  3
call    7 returned 60
    %%%%%: 2354-block  4
        -: 2355:
        -: 2356:		AV *restrict x_av = (AV*)SvRV(x_sv);
       60: 2357:		AV *restrict y_av = (AV*)SvRV(y_sv);
       60: 2357-block  0
branch  0 taken 60 (fallthrough)
branch  1 taken 0
       60: 2357-block  1
branch  2 taken 60 (fallthrough)
branch  3 taken 0
       60: 2358:		size_t nx = av_len(x_av) + 1;
       60: 2359:		size_t ny = av_len(y_av) + 1;
       60: 2360:
       60: 2360-block  0
        -: 2361:		if (nx != ny) {
        -: 2362:			croak("cov: incompatible dimensions (x has %lu, y has %lu)", 
        -: 2363:				   (unsigned long)nx, (unsigned long)ny);
        -: 2364:		}
       12: 2365:
       12: 2365-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 2366:		// 3. Extract Valid Pairwise Data
    %%%%%: 2366-block  0
call    0 never executed
    #####: 2367:		// Allocate temporary C arrays for numeric processing
call    0 never executed
    #####: 2368:		double *restrict x_val = (double*)safemalloc(nx * sizeof(double));
call    0 never executed
        -: 2369:		double *restrict y_val = (double*)safemalloc(nx * sizeof(double));
       12: 2370:		size_t n = 0;
        -: 2371:
        -: 2372:		for (size_t i = 0; i < nx; i++) {
       12: 2373:			SV **restrict x_tv = av_fetch(x_av, i, 0);
       12: 2373-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        -: 2374:			SV **restrict y_tv = av_fetch(y_av, i, 0);
       18: 2375:
        3: 2375-block  0
       15: 2375-block  1
       18: 2375-block  2
branch  0 taken 15
branch  1 taken 3
       90: 2376:			// Extract numeric values, defaulting to NAN for missing/invalid data
       15: 2376-block  0
       90: 2376-block  1
branch  0 taken 75
branch  1 taken 15 (fallthrough)
       75: 2377:			double xv = (x_tv && SvOK(*x_tv) && looks_like_number(*x_tv)) ? SvNV(*x_tv) : NAN;
       75: 2378:			double yv = (y_tv && SvOK(*y_tv) && looks_like_number(*y_tv)) ? SvNV(*y_tv) : NAN;
       75: 2379:
       75: 2379-block  0
        -: 2380:			// Pairwise complete observations (skips NAs seamlessly like R)
        -: 2381:			if (!isnan(xv) && !isnan(yv)) {
        -: 2382:				 x_val[n] = xv;
        9: 2383:				 y_val[n] = yv;
        -: 2384:				 n++;
        9: 2385:			}
        9: 2385-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        -: 2386:		}
        3: 2387:
        3: 2387-block  0
call    0 returned 3
        3: 2388:		// 4. Handle edge cases where data is too sparse
call    0 returned 3
        -: 2389:		if (n < 2) {
        -: 2390:			Safefree(x_val);	Safefree(y_val);
        3: 2391:			RETVAL = newSVnv(NAN);
call    0 returned 3
        3: 2392:		} else {
call    0 returned 3
        -: 2393:			double ans = 0.0;			
       18: 2394:			// 5. Algorithm routing
       18: 2394-block  0
branch  0 taken 15
branch  1 taken 3 (fallthrough)
       15: 2395:			if (strcmp(method, "kendall") == 0) {
       15: 2396:				 // R's default cov(..., method="kendall") iterates the full n x n space
       15: 2397:				 for (size_t i = 0; i < n; i++) {
       15: 2398:				     for (size_t j = 0; j < n; j++) {
       15: 2399:				         int sx = (x_val[i] > x_val[j]) - (x_val[i] < x_val[j]);
       15: 2399-block  0
        -: 2400:				         int sy = (y_val[i] > y_val[j]) - (y_val[i] < y_val[j]);
        -: 2401:				         ans += (double)(sx * sy);
        3: 2402:				     }
        3: 2402-block  0
call    0 returned 3
        3: 2403:				 }
call    0 returned 3
        -: 2404:			} else {
        -: 2405:				 double mean_x = 0.0, mean_y = 0.0, cov_sum = 0.0;
       36: 2406:				 if (strcmp(method, "spearman") == 0) {
        6: 2406-block  0
       36: 2406-block  1
branch  0 taken 30
branch  1 taken 6 (fallthrough)
       30: 2407:				     // Spearman: Rank the data first, then run standard covariance
       30: 2408:				     double *restrict rx = (double*)safemalloc(n * sizeof(double));
       30: 2409:				     double *restrict ry = (double*)safemalloc(n * sizeof(double));
       30: 2410:				     
       30: 2411:				     // Uses your existing rank_data() helper from LikeR.xs
       30: 2411-block  0
        -: 2412:				     rank_data(x_val, rx, n);
        -: 2413:				     rank_data(y_val, ry, n);
        -: 2414:				     
        -: 2415:				     for (size_t i = 0; i < n; i++) {
        9: 2416:				         double dx = rx[i] - mean_x;
        9: 2416-block  0
        -: 2417:				         mean_x += dx / (i + 1);
        -: 2418:				         double dy = ry[i] - mean_y;
       12: 2419:				         mean_y += dy / (i + 1);
       12: 2419-block  0
call    0 returned 12
       12: 2420:				         cov_sum += dx * (ry[i] - mean_y);
call    0 returned 12
       12: 2421:				     }
call    0 returned 12
        -: 2422:				     
        -: 2423:				     Safefree(rx);
        -: 2424:				     Safefree(ry);
        -: 2425:				 } else { 
        -: 2426:				     // Pearson: Welford's Single-Pass Covariance Algorithm
        -: 2427:				     for (size_t i = 0; i < n; i++) {
        -: 2428:				         double dx = x_val[i] - mean_x;
        -: 2429:				         mean_x += dx / (i + 1);
       21: 2430:				         double dy = y_val[i] - mean_y;
       21: 2431:				         mean_y += dy / (i + 1);
       21: 2432:				         cov_sum += dx * (y_val[i] - mean_y);
        -: 2433:				     }
        -: 2434:				 }
        -: 2435:				 
        -: 2436:				 // Unbiased Sample Covariance (N - 1) for Pearson & Spearman
       21: 2437:				 ans = cov_sum / (n - 1);
       21: 2438:			}
       21: 2439:			
       21: 2440:			Safefree(x_val);
       21: 2441:			Safefree(y_val);
       21: 2442:			RETVAL = newSVnv(ans);
       21: 2443:		}
       21: 2444:	}
       21: 2445:	OUTPUT:
        -: 2446:	  RETVAL
       21: 2447:
       21: 2448:SV* glm(...)
       21: 2449:CODE:
       21: 2450:{
       21: 2451:	const char *restrict formula  = NULL;
        -: 2452:	SV *restrict data_sv = NULL;
       21: 2453:	const char *restrict family_str = "gaussian";
       21: 2454:	char f_cpy[512];
       21: 2455:	char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
       21: 2456:
        -: 2457:	// Dynamic Term Arrays
        -: 2458:	char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
        -: 2459:	bool *restrict is_dummy = NULL;
        -: 2460:	char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
        -: 2461:	unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
      21*: 2462:	size_t n = 0, valid_n = 0, i;
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 2462-block  0
call    2 never executed
        -: 2463:	bool has_intercept = true, converged = false, boundary = false;
       78: 2464:	int iter = 0, max_iter = 25, final_rank = 0, df_res = 0;
       21: 2464-block  0
       57: 2464-block  1
       78: 2464-block  2
branch  0 taken 57
branch  1 taken 21 (fallthrough)
       57: 2465:	double deviance_old = 0.0, deviance_new = 0.0, null_dev = 0.0, aic = 0.0;
       57: 2465-block  0
call    0 returned 57
       57: 2466:	double dispersion = 0.0, epsilon = 1e-8;
       57: 2467:
branch  0 taken 21 (fallthrough)
branch  1 taken 36
       21: 2467-block  0
call    2 returned 21
       36: 2468:	char **restrict row_names = NULL;
       36: 2468-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 15
       21: 2468-block  1
       15: 2469:	char **restrict valid_row_names = NULL;
       15: 2469-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 2469-block  1
call    2 returned 15
    #####: 2470:	HV **restrict row_hashes = NULL;
    %%%%%: 2470-block  0
call    0 never executed
        -: 2471:	HV *restrict data_hoa = NULL;
      21*: 2472:	SV *restrict ref = NULL;
       21: 2472-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 2472-block  1
call    2 never executed
      21*: 2473:
       21: 2473-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2473-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 21
    %%%%%: 2473-block  2
call    4 never executed
        -: 2474:	double *restrict X = NULL, *restrict Y = NULL, *restrict mu = NULL, *restrict eta = NULL;
       21: 2475:	double *restrict W = NULL, *restrict Z = NULL, *restrict beta = NULL, *restrict beta_old = NULL;
       21: 2476:	bool *restrict aliased = NULL;
      21*: 2477:	double *restrict XtWX = NULL, *restrict XtWZ = NULL;
       21: 2477-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 2477-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 18
    %%%%%: 2477-block  2
call    4 never executed
        -: 2478:
        -: 2479:	HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
       21: 2480:	AV *restrict terms_av;
       21: 2480-block  0
call    0 returned 21
call    1 returned 21
       21: 2481:	HE *restrict entry;
call    0 returned 21
call    1 returned 21
       21: 2482:
call    0 returned 21
call    1 returned 21
        -: 2483:	if (items % 2 != 0) croak("Usage: glm(formula => 'am ~ wt + hp', data => \\%mtcars)");
       21: 2484:
      294: 2485:	for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
      273: 2485-block  0
branch  0 taken 195 (fallthrough)
branch  1 taken 78
      195: 2485-block  1
      273: 2485-block  2
      294: 2485-block  3
branch  2 taken 273 (fallthrough)
branch  3 taken 21
      273: 2485-block  4
branch  4 taken 273
branch  5 taken 0 (fallthrough)
       21: 2486:	  const char *restrict key = SvPV_nolen(ST(i_arg));
        -: 2487:	  SV *restrict val = ST(i_arg + 1);
       21: 2488:	  if      (strEQ(key, "formula")) formula = SvPV_nolen(val);
      21*: 2489:	  else if (strEQ(key, "data"))    data_sv = val;
       21: 2489-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 2489-block  1
call    2 never executed
       21: 2490:	  else if (strEQ(key, "family"))  family_str = SvPV_nolen(val);
       21: 2491:	  else croak("glm: unknown argument '%s'", key);
        -: 2492:	}        
      21*: 2493:	if (!formula) croak("glm: formula is required");
       21: 2493-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 2493-block  1
       21: 2494:	if (!data_sv || !SvROK(data_sv)) croak("glm: data is required and must be a reference");
       21: 2494-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2494-block  1
call    2 returned 21
        -: 2495:
       21: 2496:	bool is_binomial = (strcmp(family_str, "binomial") == 0);
       21: 2496-block  0
call    0 returned 21
       60: 2497:	bool is_gaussian = (strcmp(family_str, "gaussian") == 0);
       60: 2497-block  0
branch  0 taken 39
branch  1 taken 21 (fallthrough)
       39: 2498:	if (!is_binomial && !is_gaussian) croak("glm: unsupported family '%s'", family_str);
       39: 2498-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    #####: 2499:
    #####: 2500:	// --- Formula Parsing & Expansion ---
    %%%%%: 2500-block  0
call    0 never executed
call    1 never executed
        -: 2501:	Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
      39*: 2502:	Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
       39: 2502-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2502-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 39
    #####: 2503:	Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
    %%%%%: 2503-block  0
call    0 never executed
    #####: 2504:
        -: 2505:	src = (char*)formula; dst = f_cpy;
       39: 2506:	while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
       39: 2507:	*dst = '\0';
       39: 2507-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    #####: 2508:
    #####: 2509:	tilde = strchr(f_cpy, '~');
    #####: 2510:	if (!tilde) croak("glm: invalid formula, missing '~'");
    %%%%%: 2510-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2510-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2510-block  2
    #####: 2511:	*tilde = '\0';
    %%%%%: 2511-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2511-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2511-block  2
        -: 2512:	lhs = f_cpy; rhs = tilde + 1;
    #####: 2513:
    %%%%%: 2513-block  0
call    0 never executed
    #####: 2514:	if (strstr(rhs, "-1")) has_intercept = false;
call    0 never executed
    #####: 2515:	if (has_intercept) terms[num_terms++] = savepv("Intercept");
    #####: 2516:
call    0 never executed
    #####: 2517:	chunk = strtok(rhs, "+");
        -: 2518:	while (chunk != NULL) {
       39: 2519:	  if (num_terms >= term_cap - 3) {
      39*: 2520:		   term_cap *= 2;
       39: 2520-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    %%%%%: 2520-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2520-block  2
       39: 2521:		   Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
       39: 2521-block  0
call    0 returned 39
        -: 2522:	  }
       39: 2523:	  if (strcmp(chunk, "1") == 0 || strcmp(chunk, "-1") == 0) {
       39: 2523-block  0
call    0 returned 39
        -: 2524:		   chunk = strtok(NULL, "+");
        -: 2525:		   continue;
       81: 2526:	  }
       21: 2526-block  0
       60: 2526-block  1
       81: 2526-block  2
branch  0 taken 60
branch  1 taken 21 (fallthrough)
       60: 2527:	  char *restrict star = strchr(chunk, '*');
      117: 2528:	  if (star) {
       60: 2528-block  0
       57: 2528-block  1
      117: 2528-block  2
branch  0 taken 57
branch  1 taken 60 (fallthrough)
      57*: 2529:		   *star = '\0';
       57: 2529-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 57
    %%%%%: 2529-block  1
        -: 2530:		   char *restrict left = chunk; char *restrict right = star + 1;
       60: 2531:		   char *restrict c_l = strchr(left, '^'); if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
       60: 2531-block  0
branch  0 taken 60 (fallthrough)
branch  1 taken 0
       60: 2531-block  1
call    2 returned 60
        -: 2532:		   char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
       21: 2533:		   
        -: 2534:		   terms[num_terms++] = savepv(left);
        -: 2535:		   terms[num_terms++] = savepv(right);
       21: 2536:		   size_t inter_len = strlen(left) + strlen(right) + 2;
       21: 2537:		   terms[num_terms] = (char*)safemalloc(inter_len);
       21: 2537-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2538:		   snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
      21*: 2539:	  } else {
       21: 2539-block  0
call    0 returned 21
branch  1 taken 0 (fallthrough)
branch  2 taken 21
    %%%%%: 2539-block  1
call    3 never executed
       21: 2540:		   char *restrict c_chunk = strchr(chunk, '^'); 
       21: 2540-block  0
call    0 returned 21
       21: 2541:		   if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2542:		   terms[num_terms++] = savepv(chunk);
       21: 2542-block  0
call    0 returned 21
       21: 2543:	  }
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2543-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 15
        6: 2544:	  chunk = strtok(NULL, "+");
        6: 2545:	}
        6: 2545-block  0
call    0 returned 6
       6*: 2546:
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 2546-block  0
call    2 never executed
        6: 2546-block  1
call    3 returned 6
      366: 2547:	for (i = 0; i < num_terms; i++) {
      366: 2547-block  0
branch  0 taken 360
branch  1 taken 6 (fallthrough)
      360: 2548:	  bool found = false;
      360: 2549:	  for (size_t j = 0; j < num_uniq; j++) {
      360: 2549-block  0
call    0 returned 360
        -: 2550:		   if (strcmp(terms[i], uniq_terms[j]) == 0) { found = true; break; }
       15: 2551:	  }
       15: 2551-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 2551-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 0
       15: 2552:	  if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
       15: 2552-block  0
call    0 returned 15
      15*: 2553:	}
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 2553-block  0
call    2 never executed
       15: 2553-block  1
call    3 returned 15
branch  4 taken 0 (fallthrough)
branch  5 taken 15
    %%%%%: 2553-block  2
call    6 never executed
       15: 2553-block  3
call    7 returned 15
       15: 2554:	p = num_uniq;
      495: 2555:
      495: 2555-block  0
call    0 returned 495
branch  1 taken 480
branch  2 taken 15 (fallthrough)
        -: 2556:	// --- Data Extraction ---
      480: 2557:	ref = SvRV(data_sv);
      480: 2557-block  0
call    0 returned 480
call    1 returned 480
      480: 2558:	if (SvTYPE(ref) == SVt_PVHV) {
call    0 returned 480
      480: 2559:		HV*restrict hv = (HV*)ref;
        -: 2560:		if (hv_iterinit(hv) == 0) croak("glm: Data hash is empty");
    #####: 2561:		entry = hv_iternext(hv);
    %%%%%: 2561-block  0
call    0 never executed
        -: 2562:		if (entry) {
    #####: 2563:			SV*restrict val = hv_iterval(hv, entry);
    %%%%%: 2563-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 2564:			if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
    #####: 2565:				 data_hoa = hv;
    %%%%%: 2565-block  0
call    0 never executed
    #####: 2566:				 n = av_len((AV*)SvRV(val)) + 1;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2566-block  0
call    2 never executed
    %%%%%: 2566-block  1
call    3 never executed
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 2566-block  2
call    6 never executed
    %%%%%: 2566-block  3
call    7 never executed
    #####: 2567:				 Newx(row_names, n, char*);
    %%%%%: 2567-block  0
    %%%%%: 2567-block  1
branch  0 never executed
branch  1 never executed
    #####: 2568:				 for(i = 0; i < n; i++) {
    %%%%%: 2568-block  0
call    0 never executed
    #####: 2569:				     char buf[32]; snprintf(buf, sizeof(buf), "%lu", i+1);
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 2569-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2569-block  1
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 2570:				     row_names[i] = savepv(buf);
    #####: 2571:				 }
    #####: 2572:			} else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
    %%%%%: 2572-block  0
call    0 never executed
        -: 2573:				 n = hv_iterinit(hv);
    #####: 2574:				 Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
    %%%%%: 2574-block  0
    %%%%%: 2574-block  1
call    0 never executed
    %%%%%: 2574-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 2575:				 i = 0;
    %%%%%: 2575-block  0
call    0 never executed
call    1 never executed
    #####: 2576:				 while ((entry = hv_iternext(hv))) {
call    0 never executed
        -: 2577:				     unsigned int len;
        -: 2578:				     row_names[i] = savepv(hv_iterkey(entry, &len));
    #####: 2579:				     row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
    %%%%%: 2579-block  0
call    0 never executed
        -: 2580:				     i++;
        -: 2581:				 }
       81: 2582:			} else croak("glm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
       21: 2582-block  0
       60: 2582-block  1
       81: 2582-block  2
branch  0 taken 60
branch  1 taken 21 (fallthrough)
       60: 2583:		}
       60: 2583-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 60
    #####: 2584:	} else if (SvTYPE(ref) == SVt_PVAV) {
    #####: 2585:	  AV*restrict av = (AV*)ref;
    %%%%%: 2585-block  0
call    0 never executed
call    1 never executed
    #####: 2586:	  n = av_len(av) + 1;
call    0 never executed
call    1 never executed
        -: 2587:	  Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
       60: 2588:	  for (i = 0; i < n; i++) {
       60: 2588-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 39
       21: 2589:		   SV**restrict val = av_fetch(av, i, 0);
       21: 2589-block  0
call    0 returned 21
        -: 2590:		   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
       39: 2591:		       row_hashes[i] = (HV*)SvRV(*val);
       39: 2591-block  0
call    0 returned 39
branch  1 taken 3 (fallthrough)
branch  2 taken 36
        3: 2592:		       char buf[32]; snprintf(buf, sizeof(buf), "%lu", i + 1);
       3*: 2593:		       row_names[i] = savepv(buf);
        3: 2593-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 2593-block  1
call    2 never executed
        3: 2593-block  2
call    3 returned 3
      183: 2594:		   } else {
      180: 2594-block  0
      183: 2594-block  1
branch  0 taken 180
branch  1 taken 3 (fallthrough)
      180: 2595:		       for (size_t k = 0; k < i; k++) Safefree(row_names[k]);
      180: 2595-block  0
call    0 returned 180
      180: 2596:		       Safefree(row_names); Safefree(row_hashes);
branch  0 taken 180 (fallthrough)
branch  1 taken 0
      180: 2597:		       croak("glm: Array values must be HashRefs (AoH)");
      270: 2598:		   }
      180: 2598-block  0
       90: 2598-block  1
      270: 2598-block  2
branch  0 taken 264
branch  1 taken 6 (fallthrough)
      264: 2599:	  }
      264: 2599-block  0
branch  0 taken 174 (fallthrough)
branch  1 taken 90
      174: 2599-block  1
        -: 2600:	} else croak("glm: Data must be an Array or Hash reference");
      180: 2601:
      180: 2601-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 174
       6*: 2602:	// --- Categorical Expansion ---
        6: 2602-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 2602-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2602-block  2
call    4 never executed
    %%%%%: 2602-block  3
call    5 never executed
        6: 2603:	for (size_t j = 0; j < p; j++) {
        6: 2603-block  0
call    0 returned 6
        -: 2604:	  if (p_exp + 32 >= exp_cap) {
      180: 2605:		   exp_cap *= 2;
      180: 2605-block  0
call    0 returned 180
        -: 2606:		   Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
        -: 2607:		   Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        3: 2608:	  }
        3: 2608-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        6: 2609:	  if (strcmp(uniq_terms[j], "Intercept") == 0) {
        3: 2609-block  0
        3: 2609-block  1
        6: 2609-block  2
branch  0 taken 3
branch  1 taken 3 (fallthrough)
        6: 2610:		   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = false; p_exp++; continue;
        3: 2610-block  0
        3: 2610-block  1
        6: 2610-block  2
branch  0 taken 3
branch  1 taken 3 (fallthrough)
        3: 2611:	  }
        3: 2611-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 2612:	  if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
        3: 2612-block  0
        -: 2613:		   char **restrict levels = NULL; size_t num_levels = 0, levels_cap = 8;
        -: 2614:		   Newx(levels, levels_cap, char*);
        -: 2615:		   for (i = 0; i < n; i++) {
        6: 2616:		       char*restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
        3: 2616-block  0
        6: 2616-block  1
branch  0 taken 3
branch  1 taken 3 (fallthrough)
        3: 2617:		       if (str_val) {
        3: 2617-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 2618:		           bool found = false;
    #####: 2619:		           for (size_t l = 0; l < num_levels; l++) {
    %%%%%: 2619-block  0
call    0 never executed
call    1 never executed
    #####: 2620:		               if (strcmp(levels[l], str_val) == 0) { found = true; break; }
call    0 never executed
call    1 never executed
        -: 2621:		           }
        3: 2622:		           if (!found) {
        3: 2623:		               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
        3: 2623-block  0
call    0 returned 3
        3: 2624:		               levels[num_levels++] = savepv(str_val);
        3: 2625:		           }
call    0 returned 3
call    1 returned 3
        3: 2626:		           Safefree(str_val);
        -: 2627:		       }
        9: 2628:		   }
        3: 2628-block  0
        6: 2628-block  1
call    0 returned 6
        9: 2628-block  2
branch  1 taken 6
branch  2 taken 3 (fallthrough)
        3: 2629:		   if (num_levels > 0) {
        3: 2629-block  0
call    0 returned 3
        -: 2630:		       for (size_t l1 = 0; l1 < num_levels - 1; l1++) {
    #####: 2631:		           for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
    %%%%%: 2631-block  0
call    0 never executed
call    1 never executed
        -: 2632:		               if (strcmp(levels[l1], levels[l2]) > 0) {
        -: 2633:		                   char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp;
       36: 2634:		               }
       36: 2634-block  0
call    0 returned 36
        -: 2635:		           }
        -: 2636:		       }
       21: 2637:		       for (size_t l = 1; l < num_levels; l++) {
        -: 2638:		           if (p_exp >= exp_cap) {
      21*: 2639:		               exp_cap *= 2;
       21: 2639-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 2639-block  1
call    2 never executed
       21: 2639-block  2
call    3 returned 21
branch  4 taken 0 (fallthrough)
branch  5 taken 21
    %%%%%: 2639-block  3
call    6 never executed
       21: 2639-block  4
call    7 returned 21
      21*: 2640:		               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 2640-block  0
call    2 never executed
       21: 2640-block  1
call    3 returned 21
        -: 2641:		               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
        -: 2642:		           }
      861: 2643:		           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
      840: 2643-block  0
      861: 2643-block  1
branch  0 taken 840
branch  1 taken 21 (fallthrough)
      840: 2644:		           exp_terms[p_exp] = (char*)safemalloc(t_len);
      840: 2644-block  0
call    0 returned 840
     840*: 2645:		           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
branch  0 taken 0 (fallthrough)
branch  1 taken 840
    %%%%%: 2645-block  0
call    2 never executed
        -: 2646:		           is_dummy[p_exp] = true; dummy_base[p_exp] = savepv(uniq_terms[j]); dummy_level[p_exp] = savepv(levels[l]);
      840: 2647:		           p_exp++;
      840: 2648:		       }
      840: 2648-block  0
call    0 returned 840
     3180: 2649:		       for (size_t l = 0; l < num_levels; l++) Safefree(levels[l]);
     2340: 2649-block  0
     3180: 2649-block  1
branch  0 taken 2340
branch  1 taken 840 (fallthrough)
     2340: 2650:		       Safefree(levels);
     2340: 2650-block  0
branch  0 taken 840 (fallthrough)
branch  1 taken 1500
      840: 2651:		   } else {
      840: 2651-block  0
     1500: 2652:		       Safefree(levels); exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = false; p_exp++;
     1500: 2652-block  0
branch  0 taken 180 (fallthrough)
branch  1 taken 1320
      180: 2653:		   }
      180: 2653-block  0
call    0 returned 180
      180: 2654:	  } else {
branch  0 taken 180 (fallthrough)
branch  1 taken 0
      180: 2655:		   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = false; p_exp++;
      180: 2655-block  0
branch  0 taken 90 (fallthrough)
branch  1 taken 90
       90: 2655-block  1
       90: 2655-block  2
      180: 2656:	  }
      180: 2656-block  0
call    0 returned 180
    #####: 2657:	}
    %%%%%: 2657-block  0
        -: 2658:	p = p_exp;
     1320: 2659:
     1320: 2659-block  0
call    0 returned 1320
    1320*: 2660:	Newx(X, n * p, double); Newx(Y, n, double);
branch  0 taken 0 (fallthrough)
branch  1 taken 1320
    %%%%%: 2660-block  0
        -: 2661:	Newx(valid_row_names, n, char*);
        -: 2662:
     840*: 2663:	// --- Listwise Deletion ---
      840: 2663-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 840
    %%%%%: 2663-block  1
call    2 never executed
call    3 never executed
      840: 2664:	for (size_t i = 0; i < n; i++) {
     3180: 2665:		double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
      840: 2665-block  0
     2340: 2665-block  1
     3180: 2665-block  2
branch  0 taken 2340
branch  1 taken 840 (fallthrough)
      840: 2666:		if (isnan(y_val)) { Safefree(row_names[i]); continue; }
      840: 2667:
      840: 2668:		bool row_ok = true;
      840: 2668-block  0
call    0 returned 840
        -: 2669:		double *restrict row_x = (double*)safemalloc(p * sizeof(double));
       21: 2670:		for (size_t j = 0; j < p; j++) {
       21: 2670-block  0
call    0 returned 21
       21: 2671:			if (strcmp(exp_terms[j], "Intercept") == 0) {
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    #####: 2672:				 row_x[j] = 1.0;
    %%%%%: 2672-block  0
call    0 never executed
call    1 never executed
call    2 never executed
branch  3 never executed (fallthrough)
branch  4 never executed
    %%%%%: 2672-block  1
call    5 never executed
    #####: 2673:			} else if (is_dummy[j]) {
    %%%%%: 2673-block  0
call    0 never executed
        -: 2674:				 char* str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
        -: 2675:				 if (str_val) {
       21: 2676:				     row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
       21: 2676-block  0
call    0 returned 21
call    1 returned 21
       21: 2677:				     Safefree(str_val);
call    0 returned 21
call    1 returned 21
       21: 2678:				 } else { row_ok = false; break; }
call    0 returned 21
call    1 returned 21
       21: 2679:			} else {
call    0 returned 21
       21: 2680:				 row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
call    0 returned 21
call    1 returned 21
       81: 2681:				 if (isnan(row_x[j])) { row_ok = false; break; }
       60: 2681-block  0
       81: 2681-block  1
branch  0 taken 60
branch  1 taken 21 (fallthrough)
        -: 2682:			}
       21: 2683:		}
      861: 2684:		if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
       21: 2684-block  0
      840: 2684-block  1
      861: 2684-block  2
branch  0 taken 840
branch  1 taken 21 (fallthrough)
       21: 2685:		Y[valid_n] = y_val;
      861: 2686:		for (size_t j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
       21: 2686-block  0
      840: 2686-block  1
      861: 2686-block  2
branch  0 taken 840
branch  1 taken 21 (fallthrough)
      840: 2687:		valid_row_names[valid_n] = row_names[i];
      840: 2687-block  0
branch  0 taken 96 (fallthrough)
branch  1 taken 744
      96*: 2688:		valid_n++;
       96: 2688-block  0
branch  0 taken 96 (fallthrough)
branch  1 taken 0
       96: 2688-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 96
    %%%%%: 2688-block  2
call    4 never executed
       96: 2689:		Safefree(row_x);
       96: 2690:	}
       96: 2691:	Safefree(row_names); 
       96: 2692:	if (valid_n <= p) {
       96: 2692-block  0
branch  0 taken 57 (fallthrough)
branch  1 taken 39
       57: 2692-block  1
       39: 2693:	  Safefree(X); Safefree(Y); Safefree(valid_row_names); if (row_hashes) Safefree(row_hashes);
       39: 2693-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2693-block  1
    #####: 2694:	  croak("glm: 0 degrees of freedom (too many NAs or parameters > observations)");
    %%%%%: 2694-block  0
       96: 2695:	}
       96: 2695-block  0
        -: 2696:	// --- R glm.fit IRLS Implementation ---
      744: 2697:	mu = (double*)safemalloc(valid_n * sizeof(double)); eta = (double*)safemalloc(valid_n * sizeof(double));
      744: 2698:	W = (double*)safemalloc(valid_n * sizeof(double)); Z = (double*)safemalloc(valid_n * sizeof(double));
      744: 2698-block  0
        -: 2699:	beta = (double*)safemalloc(p * sizeof(double)); beta_old = (double*)safemalloc(p * sizeof(double));
        -: 2700:	aliased = (bool*)safemalloc(p * sizeof(bool));
        -: 2701:	XtWX = (double*)safemalloc(p * p * sizeof(double)); XtWZ = (double*)safemalloc(p * sizeof(double));
       63: 2702:	for (i = 0; i < p; i++) { beta[i] = 0.0; beta_old[i] = 0.0; }
       21: 2702-block  0
       42: 2702-block  1
       63: 2702-block  2
branch  0 taken 63
branch  1 taken 0 (fallthrough)
     2415: 2703:	// Initialize (mustart / etastart equivalent)
       63: 2703-block  0
     2352: 2703-block  1
     2415: 2703-block  2
branch  0 taken 2352
branch  1 taken 63 (fallthrough)
     2352: 2704:	double sum_y = 0.0;
     2352: 2704-block  0
branch  0 taken 864 (fallthrough)
branch  1 taken 1488
      864: 2705:	for (i = 0; i < valid_n; i++) sum_y += Y[i];
      864: 2706:	double mean_y = sum_y / valid_n;
     864*: 2707:	for (i = 0; i < valid_n; i++) {
      864: 2707-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 864
    %%%%%: 2707-block  1
      864: 2708:	  if (is_binomial) {
      864: 2709:		   if (Y[i] < 0.0 || Y[i] > 1.0) croak("glm: binomial family requires response between 0 and 1");
      864: 2709-block  0
        -: 2710:		   mu[i] = (Y[i] + 0.5) / 2.0; 
     1488: 2711:		   eta[i] = log(mu[i] / (1.0 - mu[i]));
     1488: 2712:		   double dev = 0.0;
     1488: 2712-block  0
        -: 2713:		   if (Y[i] == 0.0)      dev = -2.0 * log(1.0 - mu[i]);
        -: 2714:		   else if (Y[i] == 1.0) dev = -2.0 * log(mu[i]);
        -: 2715:		   else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
      783: 2716:		   deviance_old += dev;
       63: 2716-block  0
      183: 2716-block  1
      537: 2716-block  2
      720: 2716-block  3
branch  0 taken 537
branch  1 taken 183 (fallthrough)
      183: 2716-block  4
      246: 2716-block  5
branch  2 taken 183
branch  3 taken 63 (fallthrough)
     2415: 2717:	  } else { 
       63: 2717-block  0
     2352: 2717-block  1
     2415: 2717-block  2
branch  0 taken 2352
branch  1 taken 63 (fallthrough)
     2352: 2718:		   mu[i] = mean_y; // R gaussian init
     9048: 2719:		   eta[i] = mu[i]; 
     2352: 2719-block  0
     6696: 2719-block  1
     9048: 2719-block  2
branch  0 taken 6696
branch  1 taken 2352 (fallthrough)
     6696: 2720:	  }
     6696: 2721:	}
    26064: 2722:	// IRLS Loop
     6696: 2722-block  0
    19368: 2722-block  1
    26064: 2722-block  2
branch  0 taken 19368
branch  1 taken 6696 (fallthrough)
        -: 2723:	for (iter = 1; iter <= max_iter; iter++) {
        -: 2724:		for (i = 0; i < valid_n; i++) {
       63: 2725:			if (is_binomial) {
       63: 2725-block  0
call    0 returned 63
      246: 2726:				 double varmu = mu[i] * (1.0 - mu[i]);
      183: 2726-block  0
      246: 2726-block  1
branch  0 taken 183
branch  1 taken 63 (fallthrough)
     183*: 2727:				 double mu_eta = varmu; // Link derivative for logit
      183: 2727-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 183
    %%%%%: 2727-block  1
      183: 2728:				 if (varmu < 1e-10) varmu = 1e-10;
      720: 2729:				 Z[i] = eta[i] + (Y[i] - mu[i]) / mu_eta;
      183: 2729-block  0
      537: 2729-block  1
branch  0 taken 537 (fallthrough)
branch  1 taken 0
      537: 2729-block  2
      537: 2729-block  3
      720: 2729-block  4
branch  2 taken 537
branch  3 taken 183 (fallthrough)
      183: 2730:				 W[i] = (mu_eta * mu_eta) / varmu; 
      183: 2730-block  0
        -: 2731:			} else { 
        -: 2732:				 W[i] = 1.0; 
        -: 2733:				 Z[i] = Y[i]; 
       63: 2734:			}
      693: 2735:		}
       63: 2735-block  0
      630: 2735-block  1
      693: 2735-block  2
branch  0 taken 630
branch  1 taken 63 (fallthrough)
      630: 2736:		// Formulate XtWX and XtWZ
    24150: 2737:		for (i = 0; i < p; i++) { XtWZ[i] = 0.0; for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
      630: 2737-block  0
    23520: 2737-block  1
    24150: 2737-block  2
branch  0 taken 23520
branch  1 taken 630 (fallthrough)
    23520: 2738:		for (size_t k = 0; k < valid_n; k++) {
    90480: 2739:			double w = W[k], z = Z[k];
    23520: 2739-block  0
    66960: 2739-block  1
branch  0 taken 66960 (fallthrough)
branch  1 taken 0
    66960: 2739-block  2
    66960: 2739-block  3
    90480: 2739-block  4
branch  2 taken 66960
branch  3 taken 23520 (fallthrough)
    23520: 2740:			for (i = 0; i < p; i++) {
    23520: 2741:				 XtWZ[i] += X[k * p + i] * w * z;
    23520: 2741-block  0
branch  0 taken 8640 (fallthrough)
branch  1 taken 14880
     8640: 2742:				 double xw = X[k * p + i] * w;
        -: 2743:				 for (size_t j = 0; j < p; j++) XtWX[i * p + j] += xw * X[k * p + j];
    8640*: 2744:			}
     8640: 2744-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8640
    %%%%%: 2744-block  1
    8640*: 2745:		}
     8640: 2745-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8640
    %%%%%: 2745-block  1
        -: 2746:		final_rank = sweep_matrix_ols(XtWX, p, aliased);
     8640: 2747:		for (i = 0; i < p; i++) {
     8640: 2748:			if (aliased[i]) { beta[i] = NAN; } else {
     8640: 2748-block  0
branch  0 taken 5130 (fallthrough)
branch  1 taken 3510
     5130: 2748-block  1
     3510: 2749:				 double sum = 0.0;
     3510: 2749-block  0
branch  0 taken 3510 (fallthrough)
branch  1 taken 0
     3510: 2749-block  1
    #####: 2750:				 for (size_t j = 0; j < p; j++) if (!aliased[j]) sum += XtWX[i * p + j] * XtWZ[j];
    %%%%%: 2750-block  0
     8640: 2751:				 beta[i] = sum;
     8640: 2751-block  0
        -: 2752:			}
    14880: 2753:		}
    14880: 2754:		// Calculate updated ETA, MU, and Deviance (with Step-Halving)
    14880: 2755:		boundary = false;
    14880: 2755-block  0
        -: 2756:		for (unsigned short int half = 0; half < 10; half++) {
        -: 2757:			deviance_new = 0.0;
        -: 2758:			for (i = 0; i < valid_n; i++) {
      630: 2759:				 double linear_pred = 0.0;
      630: 2759-block  0
branch  0 taken 270 (fallthrough)
branch  1 taken 360
      270: 2759-block  1
branch  2 taken 30 (fallthrough)
branch  3 taken 240
       30: 2759-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 30
      600: 2760:				 for (size_t j = 0; j < p; j++) if (!aliased[j]) linear_pred += X[i * p + j] * beta[j];
      600: 2760-block  0
        -: 2761:				 eta[i] = linear_pred;
        -: 2762:				 if (is_binomial) {
       30: 2763:				     mu[i] = 1.0 / (1.0 + exp(-eta[i]));
      120: 2764:				     // Boundary enforcement
       30: 2764-block  0
       90: 2764-block  1
      120: 2764-block  2
branch  0 taken 90
branch  1 taken 30 (fallthrough)
        -: 2765:				     if (mu[i] < 10 * DBL_EPSILON) mu[i] = 10 * DBL_EPSILON;
        -: 2766:				     if (mu[i] > 1.0 - 10 * DBL_EPSILON) mu[i] = 1.0 - 10 * DBL_EPSILON;
       63: 2767:				     
       63: 2767-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 42
       21: 2768:				     double dev = 0.0;
       21: 2768-block  0
        -: 2769:				     if (Y[i] == 0.0)      dev = -2.0 * log(1.0 - mu[i]);
       42: 2770:				     else if (Y[i] == 1.0) dev = -2.0 * log(mu[i]);
      165: 2771:				     else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
       42: 2771-block  0
      123: 2771-block  1
      165: 2771-block  2
branch  0 taken 123
branch  1 taken 42 (fallthrough)
        -: 2772:				     deviance_new += dev;
        -: 2773:				 } else {
      255: 2774:				     mu[i] = eta[i];
       21: 2774-block  0
       60: 2774-block  1
      174: 2774-block  2
      234: 2774-block  3
branch  0 taken 174
branch  1 taken 60 (fallthrough)
       60: 2774-block  4
       81: 2774-block  5
branch  2 taken 60
branch  3 taken 21 (fallthrough)
      861: 2775:				     double res = Y[i] - mu[i];
       21: 2775-block  0
      840: 2775-block  1
      861: 2775-block  2
branch  0 taken 840
branch  1 taken 21 (fallthrough)
      840: 2776:				     deviance_new += res * res;
      840: 2776-block  0
branch  0 taken 96 (fallthrough)
branch  1 taken 744
       96: 2776-block  1
      744: 2776-block  2
     840*: 2777:				 }
      840: 2777-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 840
    %%%%%: 2777-block  1
     3180: 2778:			}
      840: 2778-block  0
     2340: 2778-block  1
     3180: 2778-block  2
branch  0 taken 2340
branch  1 taken 840 (fallthrough)
     2340: 2779:			// Step halving divergence check
     9000: 2780:			if (!is_binomial || deviance_new <= deviance_old + 1e-7 || !isfinite(deviance_new)) {
     2340: 2780-block  0
     6660: 2780-block  1
     9000: 2780-block  2
branch  0 taken 6660
branch  1 taken 2340 (fallthrough)
        -: 2781:				 continue; 
        -: 2782:			}
       21: 2783:			
       21: 2783-block  0
call    0 returned 21
        -: 2784:			boundary = true;
       21: 2785:			for (size_t j = 0; j < p; j++) beta[j] = (beta[j] + beta_old[j]) / 2.0;
      861: 2786:		}
      840: 2786-block  0
      861: 2786-block  1
branch  0 taken 840
branch  1 taken 21 (fallthrough)
      840: 2787:		// Convergence Check
      840: 2787-block  0
branch  0 taken 96 (fallthrough)
branch  1 taken 744
       96: 2788:		if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) { 
       96: 2788-block  0
branch  0 taken 57 (fallthrough)
branch  1 taken 39
       57: 2788-block  1
       39: 2789:			converged = true; break; 
       39: 2789-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2789-block  1
    #####: 2790:		}
    %%%%%: 2790-block  0
        -: 2791:		deviance_old = deviance_new;
      744: 2792:		for (size_t j = 0; j < p; j++) beta_old[j] = beta[j];
      744: 2793:	}
      744: 2793-block  0
        -: 2794:	// Final accurate calculation of W for standard errors
        -: 2795:	for (i = 0; i < p; i++) { for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
        -: 2796:	for (size_t k = 0; k < valid_n; k++) {
       21: 2797:	  double w = is_binomial ? (mu[k] * (1.0 - mu[k])) : 1.0;
       21: 2797-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 2798:	  if (w < 1e-10) w = 1e-10;
       18: 2799:	  for (i = 0; i < p; i++) {
       18: 2799-block  0
        3: 2800:		   double xw = X[k * p + i] * w;
        3: 2800-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 2801:		   for (size_t j = 0; j < p; j++) XtWX[i * p + j] += xw * X[k * p + j];
        3: 2801-block  0
        -: 2802:	  }
        -: 2803:	}
       21: 2804:	final_rank = sweep_matrix_ols(XtWX, p, aliased);
       21: 2804-block  0
call    0 returned 21
call    1 returned 21
call    2 returned 21
call    3 returned 21
       21: 2805:	// --- Null Deviance Calculation ---
      21*: 2806:	double wtdmu = mean_y; // Since weights are 1.0 initially
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 2806-block  0
       18: 2806-block  1
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 2806-block  2
    %%%%%: 2806-block  3
       18: 2806-block  4
      861: 2807:	for (i = 0; i < valid_n; i++) {
       21: 2807-block  0
      861: 2807-block  1
branch  0 taken 840
branch  1 taken 21 (fallthrough)
      840: 2808:	  if (is_binomial) {
      840: 2809:		   if (Y[i] == 0.0)      null_dev += -2.0 * log(1.0 - wtdmu);
      840: 2809-block  0
branch  0 taken 96 (fallthrough)
branch  1 taken 744
        -: 2810:		   else if (Y[i] == 1.0) null_dev += -2.0 * log(wtdmu);
       96: 2811:		   else null_dev += 2.0 * (Y[i] * log(Y[i] / wtdmu) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - wtdmu)));
       96: 2812:	  } else {
       96: 2812-block  0
branch  0 taken 57 (fallthrough)
branch  1 taken 39
       57: 2812-block  1
       39: 2813:		   double diff = Y[i] - wtdmu;
       39: 2813-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 2813-block  1
    #####: 2814:		   null_dev += diff * diff;
    %%%%%: 2814-block  0
       96: 2815:	  }
       96: 2815-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 57
       39: 2815-block  1
       57: 2815-block  2
       96: 2815-block  3
        -: 2816:	}
      840: 2817:	// --- AIC Calculation ---
      840: 2817-block  0
call    0 returned 840
call    1 returned 840
      840: 2818:	if (is_gaussian) {
call    0 returned 840
call    1 returned 840
      840: 2819:	  double n_f = (double)valid_n;
call    0 returned 840
        -: 2820:	  aic = n_f * (log(2.0 * M_PI) + 1.0 + log(deviance_new / n_f)) + 2.0 * (final_rank + 1.0);
       21: 2821:	} else if (is_binomial) { 
       21: 2821-block  0
call    0 returned 21
        -: 2822:	  aic = deviance_new + 2.0 * final_rank; 
       21: 2823:	}
call    0 returned 21
call    1 returned 21
       81: 2824:	// --- Return Structures ---
       81: 2824-block  0
branch  0 taken 60
branch  1 taken 21 (fallthrough)
       60: 2825:	res_hv = newHV(); coef_hv = newHV(); fitted_hv = newHV(); resid_hv = newHV();
       60: 2825-block  0
call    0 returned 60
call    1 returned 60
       60: 2826:	df_res = valid_n - final_rank;
call    0 returned 60
call    1 returned 60
        -: 2827:	dispersion = is_binomial ? 1.0 : ((df_res > 0) ? (deviance_new / df_res) : NAN);
       60: 2828:	for (size_t i = 0; i < valid_n; i++) {
call    0 returned 60
       60: 2829:		double res = Y[i] - mu[i];
branch  0 taken 0 (fallthrough)
branch  1 taken 60
    #####: 2830:		if (is_binomial) {
    %%%%%: 2830-block  0
call    0 never executed
call    1 never executed
    #####: 2831:			// Deviance residuals for binomial
call    0 never executed
call    1 never executed
    #####: 2832:			double d_res = 0.0;
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 2832-block  0
    %%%%%: 2832-block  1
    %%%%%: 2832-block  2
call    3 never executed
    #####: 2833:			if (Y[i] == 0.0)      d_res = sqrt(-2.0 * log(1.0 - mu[i]));
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 2833-block  0
    %%%%%: 2833-block  1
    %%%%%: 2833-block  2
call    3 never executed
        -: 2834:			else if (Y[i] == 1.0) d_res = sqrt(-2.0 * log(mu[i]));
       60: 2835:			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]))));
       60: 2836:			res = (Y[i] > mu[i]) ? d_res : -d_res;
       60: 2837:		}
       60: 2837-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 51
        9: 2837-block  1
call    2 returned 9
       51: 2837-block  2
call    3 returned 51
        -: 2838:		hv_store(fitted_hv, valid_row_names[i], strlen(valid_row_names[i]), newSVnv(mu[i]), 0);
       60: 2839:		hv_store(resid_hv,  valid_row_names[i], strlen(valid_row_names[i]), newSVnv(res), 0);
       60: 2839-block  0
call    0 returned 60
call    1 returned 60
       60: 2840:		Safefree(valid_row_names[i]);
call    0 returned 60
call    1 returned 60
       60: 2841:	}
call    0 returned 60
branch  1 taken 9 (fallthrough)
branch  2 taken 51
        9: 2841-block  0
       51: 2841-block  1
       60: 2841-block  2
call    3 returned 60
       60: 2842:	Safefree(valid_row_names);
call    0 returned 60
branch  1 taken 9 (fallthrough)
branch  2 taken 51
        9: 2842-block  0
       51: 2842-block  1
       60: 2842-block  2
call    3 returned 60
        -: 2843:
       60: 2844:	summary_hv = newHV(); terms_av = newAV();
       60: 2844-block  0
call    0 returned 60
call    1 returned 60
        -: 2845:	for (size_t j = 0; j < p; j++) {
        -: 2846:		hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
       21: 2847:		av_push(terms_av, newSVpv(exp_terms[j], 0));
       21: 2847-block  0
call    0 returned 21
call    1 returned 21
       21: 2848:
call    0 returned 21
call    1 returned 21
       21: 2849:		HV *restrict row_hv = newHV();
call    0 returned 21
call    1 returned 21
       21: 2850:		if (aliased[j]) {
call    0 returned 21
call    1 returned 21
       21: 2851:			hv_store(row_hv, "Estimate",   8, newSVpv("NaN", 0), 0);
call    0 returned 21
call    1 returned 21
       21: 2852:			hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
call    0 returned 21
call    1 returned 21
       21: 2853:			hv_store(row_hv, is_binomial ? "z value" : "t value", 7, newSVpv("NaN", 0), 0);
call    0 returned 21
call    1 returned 21
       21: 2854:			hv_store(row_hv, is_binomial ? "Pr(>|z|)" : "Pr(>|t|)", 8, newSVpv("NaN", 0), 0);
call    0 returned 21
call    1 returned 21
       21: 2855:		} else {
call    0 returned 21
call    1 returned 21
       21: 2856:			double se = sqrt(dispersion * XtWX[j * p + j]);
call    0 returned 21
call    1 returned 21
       21: 2857:			double val_stat = beta[j] / se;
call    0 returned 21
call    1 returned 21
       21: 2858:			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 21
call    1 returned 21
       21: 2859:			
call    0 returned 21
call    1 returned 21
       21: 2860:			hv_store(row_hv, "Estimate",   8, newSVnv(beta[j]), 0);
call    0 returned 21
call    1 returned 21
       21: 2861:			hv_store(row_hv, "Std. Error", 10, newSVnv(se), 0);
call    0 returned 21
call    1 returned 21
        -: 2862:			hv_store(row_hv, is_binomial ? "z value" : "t value", 7, newSVnv(val_stat), 0);
        -: 2863:			hv_store(row_hv, is_binomial ? "Pr(>|z|)" : "Pr(>|t|)", 8, newSVnv(p_val), 0);
       81: 2864:		}
       60: 2864-block  0
call    0 returned 60
       81: 2864-block  1
branch  1 taken 60
branch  2 taken 21 (fallthrough)
       21: 2865:		hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
       21: 2865-block  0
call    0 returned 21
       81: 2866:	}
       60: 2866-block  0
call    0 returned 60
       81: 2866-block  1
branch  1 taken 60
branch  2 taken 21 (fallthrough)
       21: 2867:
       21: 2867-block  0
call    0 returned 21
       81: 2868:	hv_store(res_hv, "aic",            3, newSVnv(aic), 0);
       60: 2868-block  0
       81: 2868-block  1
branch  0 taken 60
branch  1 taken 21 (fallthrough)
       60: 2869:	hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv), 0);
       60: 2869-block  0
call    0 returned 60
       60: 2870:	hv_store(res_hv, "converged",      9, newSVuv(converged ? 1 : 0), 0);
branch  0 taken 3 (fallthrough)
branch  1 taken 57
        3: 2870-block  0
call    2 returned 3
call    3 returned 3
        -: 2871:	hv_store(res_hv, "boundary",       8, newSVuv(boundary ? 1 : 0), 0);
       21: 2872:	hv_store(res_hv, "deviance",       8, newSVnv(deviance_new), 0);
       21: 2872-block  0
call    0 returned 21
call    1 returned 21
call    2 returned 21
call    3 returned 21
        -: 2873:	hv_store(res_hv, "deviance.resid", 14, newRV_noinc((SV*)resid_hv), 0);
       21: 2874:	hv_store(res_hv, "df.null",        7, newSVuv(valid_n - has_intercept), 0);
call    0 returned 21
call    1 returned 21
call    2 returned 21
call    3 returned 21
       21: 2875:	hv_store(res_hv, "df.residual",   11, newSVuv(df_res), 0);
call    0 returned 21
call    1 returned 21
call    2 returned 21
       21: 2876:	hv_store(res_hv, "family",         6, newSVpv(family_str, 0), 0);
call    0 returned 21
call    1 returned 21
call    2 returned 21
call    3 returned 21
       21: 2877:	hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
branch  0 taken 15 (fallthrough)
branch  1 taken 6
       15: 2877-block  0
call    2 returned 15
        -: 2878:	hv_store(res_hv, "iter",           4, newSVuv(iter > max_iter ? max_iter : iter), 0);
       21: 2879:	hv_store(res_hv, "null.deviance", 13, newSVnv(null_dev), 0);
       21: 2879-block  0
call    0 returned 21
        -: 2880:	hv_store(res_hv, "rank",           4, newSVuv(final_rank), 0);
        -: 2881:	hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv), 0);
        -: 2882:	hv_store(res_hv, "terms",          5, newRV_noinc((SV*)terms_av), 0);
        -: 2883:
        -: 2884:	// --- Cleanup ---
        -: 2885:	for (i = 0; i < num_terms; i++) Safefree(terms[i]);
        -: 2886:	Safefree(terms);
       21: 2887:	for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]);
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2887-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 21
    #####: 2888:	Safefree(uniq_terms);
    %%%%%: 2888-block  0
call    0 never executed
        -: 2889:	for (size_t j = 0; j < p_exp; j++) {
       21: 2890:		Safefree(exp_terms[j]);
        -: 2891:		if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
       21: 2892:	}
       21: 2893:	Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
       21: 2894:
       21: 2895:	Safefree(mu); Safefree(eta); Safefree(Z); Safefree(W);
       21: 2896:	Safefree(beta); Safefree(beta_old); Safefree(aliased);
        -: 2897:	Safefree(XtWX); Safefree(XtWZ); Safefree(X); Safefree(Y);
        -: 2898:	if (row_hashes) Safefree(row_hashes);
      105: 2899:
       21: 2899-block  0
       84: 2899-block  1
      105: 2899-block  2
branch  0 taken 84
branch  1 taken 21 (fallthrough)
       84: 2900:	RETVAL = newRV_noinc((SV*)res_hv);
       84: 2900-block  0
call    0 returned 84
       84: 2901:}
        -: 2902:OUTPUT:
       84: 2903:    RETVAL
branch  0 taken 21 (fallthrough)
branch  1 taken 63
       21: 2903-block  0
call    2 returned 21
       63: 2904:
       63: 2904-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 42
       21: 2904-block  1
call    2 returned 21
      42*: 2905:SV* cor_test(...)
       42: 2905-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 42
    %%%%%: 2905-block  1
       42: 2906:CODE:
       42: 2906-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 21
       21: 2906-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 21
       21: 2906-block  2
call    4 returned 21
       21: 2907:{
       21: 2907-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2907-block  1
call    2 returned 21
    #####: 2908:	if (items < 2 || items % 2 != 0)
    %%%%%: 2908-block  0
call    0 never executed
        -: 2909:		croak("Usage: cor_test(\\@x, \\@y, method => 'pearson', ...)");
        -: 2910:
        -: 2911:	SV *restrict x_ref = ST(0), *restrict y_ref = ST(1);
        -: 2912:
       21: 2913:	const char *restrict alternative = "two.sided";
        -: 2914:	const char *restrict method = "pearson";
       21: 2915:	SV *restrict exact_sv = NULL;
       21: 2916:	double conf_level = 0.95;
       21: 2917:	bool continuity = 0;
        -: 2918:
        -: 2919:	/* Parse named arguments from the flat stack starting at index 2 */
       21: 2920:	for (unsigned short int i = 2; i < items; i += 2) {
       21: 2920-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2920-block  1
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 2920-block  2
branch  4 taken 21 (fallthrough)
branch  5 taken 0
       21: 2921:	  const char *restrict key = SvPV_nolen(ST(i));
       21: 2921-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 2921-block  1
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 2921-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 21
    #####: 2922:	  SV *restrict val = ST(i + 1);
    %%%%%: 2922-block  0
call    0 never executed
        -: 2923:
        -: 2924:	  if      (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
       21: 2925:	  else if (strEQ(key, "method"))      method = SvPV_nolen(val);
       21: 2926:	  else if (strEQ(key, "exact"))       exact_sv = val;
        -: 2927:	  else if (strEQ(key, "conf.level") || strEQ(key, "conf_level")) conf_level = SvNV(val);
       21: 2928:	  else if (strEQ(key, "continuity"))  continuity = SvTRUE(val);
       21: 2928-block  0
call    0 returned 21
      21*: 2929:	  else croak("cor_test: unknown argument '%s'", key);
call    0 returned 21
branch  1 taken 0 (fallthrough)
branch  2 taken 21
    %%%%%: 2929-block  0
call    3 never executed
        -: 2930:	}
       21: 2931:
       21: 2931-block  0
call    0 returned 21
       21: 2932:	AV *restrict x_av, *restrict y_av;
call    0 returned 21
        -: 2933:	double *restrict x, *restrict y;
       21: 2934:	double estimate = 0, p_value = 0, statistic = 0, df = 0, ci_lower = 0, ci_upper = 0;
      138: 2935:
      117: 2935-block  0
      138: 2935-block  1
branch  0 taken 117
branch  1 taken 21 (fallthrough)
      117: 2936:	bool is_pearson  = (strcmp(method, "pearson") == 0);
      117: 2936-block  0
call    0 returned 117
      117: 2937:	bool is_kendall  = (strcmp(method, "kendall") == 0);
call    0 returned 117
        -: 2938:	bool is_spearman = (strcmp(method, "spearman") == 0);
      117: 2939:	HV *restrict rhv;
branch  0 taken 117 (fallthrough)
branch  1 taken 0
      117: 2939-block  0
branch  2 taken 111 (fallthrough)
branch  3 taken 6
      111: 2939-block  1
call    4 returned 111
branch  5 taken 111 (fallthrough)
branch  6 taken 0
      111: 2939-block  2
call    7 returned 111
        6: 2939-block  3
      117: 2940:
      117: 2940-block  0
branch  0 taken 117 (fallthrough)
branch  1 taken 0
      117: 2940-block  1
branch  2 taken 111 (fallthrough)
branch  3 taken 6
      111: 2940-block  2
call    4 returned 111
branch  5 taken 111 (fallthrough)
branch  6 taken 0
      111: 2940-block  3
call    7 returned 111
        6: 2940-block  4
        -: 2941:	if (!SvOK(x_ref) || !SvROK(x_ref) || SvTYPE(SvRV(x_ref)) != SVt_PVAV ||
        -: 2942:	    !SvOK(y_ref) || !SvROK(y_ref) || SvTYPE(SvRV(y_ref)) != SVt_PVAV) {
      117: 2943:	  croak("cor_test: x and y must be array references");
      117: 2943-block  0
branch  0 taken 111 (fallthrough)
branch  1 taken 6
      111: 2943-block  1
branch  2 taken 105 (fallthrough)
branch  3 taken 6
      105: 2944:	}
      105: 2945:
      105: 2946:	x_av = (AV*)SvRV(x_ref);
      105: 2946-block  0
        -: 2947:	y_av = (AV*)SvRV(y_ref);
        -: 2948:	
        -: 2949:	size_t n_raw = av_len(x_av) + 1;
       21: 2950:	if (n_raw != av_len(y_av) + 1) croak("incompatible dimensions");
       21: 2950-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    #####: 2951:
    %%%%%: 2951-block  0
call    0 never executed
    #####: 2952:	x = safemalloc(n_raw * sizeof(double));
call    0 never executed
    #####: 2953:	y = safemalloc(n_raw * sizeof(double));
call    0 never executed
        -: 2954:
        -: 2955:	size_t n = 0; /* Final count of pairwise complete observations */
       21: 2956:	for (size_t i = 0; i < n_raw; i++) {
       21: 2956-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 9
        -: 2957:	  SV **restrict x_val = av_fetch(x_av, i, 0);
       12: 2958:	  SV **restrict y_val = av_fetch(y_av, i, 0);
       72: 2959:	  
       12: 2959-block  0
       72: 2959-block  1
branch  0 taken 60
branch  1 taken 12 (fallthrough)
       60: 2960:	  double xv = (x_val && SvOK(*x_val) && looks_like_number(*x_val)) ? SvNV(*x_val) : NAN;
       60: 2961:	  double yv = (y_val && SvOK(*y_val) && looks_like_number(*y_val)) ? SvNV(*y_val) : NAN;
       60: 2962:
       60: 2963:	  /* Pairwise complete observations (skips NAs seamlessly like R) */
       60: 2964:	  if (!isnan(xv) && !isnan(yv)) {
       60: 2965:	      x[n] = xv;
       60: 2966:	      y[n] = yv;
       60: 2966-block  0
        -: 2967:	      n++;
      12*: 2968:	  }
       12: 2968-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 2968-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 2968-block  2
    %%%%%: 2968-block  3
       12: 2969:	}
       12: 2970:
        -: 2971:	if (n < 3) {
        -: 2972:	  Safefree(x); 
       12: 2973:	  Safefree(y);
       12: 2974:	  croak("not enough finite observations");
       12: 2975:	}
       12: 2976:
       12: 2976-block  0
call    0 returned 12
       12: 2977:	if (is_pearson) {
       12: 2978:	  // Welford's Method for Pearson Correlation
        -: 2979:	  double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
        -: 2980:	  for (size_t i = 0; i < n; i++) {
       12: 2981:		   double dx = x[i] - mean_x;
call    0 returned 12
        9: 2982:		   mean_x += dx / (i + 1);
        9: 2982-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        6: 2983:		   double dy = y[i] - mean_y;
       30: 2984:		   mean_y += dy / (i + 1);
        6: 2984-block  0
       24: 2984-block  1
       30: 2984-block  2
branch  0 taken 24
branch  1 taken 6 (fallthrough)
       84: 2985:		   M2_x += dx * (x[i] - mean_x);
       24: 2985-block  0
       60: 2985-block  1
       84: 2985-block  2
branch  0 taken 60
branch  1 taken 24 (fallthrough)
       60: 2986:		   M2_y += dy * (y[i] - mean_y);
       60: 2987:		   cov  += dx * (y[i] - mean_y);
        -: 2988:	  }
      60*: 2989:	  estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
       60: 2989-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 60
    %%%%%: 2989-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 2989-block  2
      60*: 2990:	  df = n - 2;
       60: 2990-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 60
    %%%%%: 2990-block  1
      60*: 2991:	  statistic = estimate * sqrt(df / (1.0 - estimate * estimate));
       60: 2991-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 60
    %%%%%: 2991-block  1
       60: 2992:
       60: 2992-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 12
       48: 2992-block  1
       12: 2993:	  // Confidence interval using Fisher's Z transform
       12: 2993-block  0
        -: 2994:	  double z = 0.5 * log((1.0 + estimate) / (1.0 - estimate));
        -: 2995:	  double se = 1.0 / sqrt(n - 3);
        6: 2996:	  double alpha = 1.0 - conf_level;
       6*: 2997:	  double q = inverse_normal_cdf(1.0 - alpha/2.0);
        6: 2997-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 2997-block  1
    %%%%%: 2997-block  2
        -: 2998:	  ci_lower = tanh(z - q * se);
       6*: 2999:	  ci_upper = tanh(z + q * se);
        6: 2999-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 2999-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    %%%%%: 2999-block  2
        6: 2999-block  3
        -: 3000:
        -: 3001:	  // HIGH-PRECISION P-VALUE USING INCOMPLETE BETA
        -: 3002:	  p_value = get_t_pvalue(statistic, df, alternative);
       6*: 3003:	} else if (is_kendall) {
        6: 3003-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3003-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
       6*: 3004:	  int c = 0, d = 0, tie_x = 0, tie_y = 0;
        6: 3004-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3004-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 3004-block  2
    %%%%%: 3004-block  3
        6: 3004-block  4
        -: 3005:	  for (size_t i = 0; i < n - 1; i++) {
    #####: 3006:		   for (size_t j = i + 1; j < n; j++) {
    %%%%%: 3006-block  0
call    0 never executed
        -: 3007:		       double sign_x = (x[i] - x[j] > 0) - (x[i] - x[j] < 0);
        -: 3008:		       double sign_y = (y[i] - y[j] > 0) - (y[i] - y[j] < 0);
       6*: 3009:
        6: 3009-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3009-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    %%%%%: 3009-block  2
        -: 3010:		       if (sign_x == 0 && sign_y == 0) { /* Joint tie, ignore */ }
        6: 3011:		       else if (sign_x == 0) tie_x++;
        6: 3011-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3012:		       else if (sign_y == 0) tie_y++;
        6: 3013:		       else if (sign_x * sign_y > 0) c++;
        6: 3014:		       else d++;
        6: 3014-block  0
call    0 returned 6
        -: 3015:		   }
        -: 3016:	  }
    #####: 3017:	  double denom = sqrt((double)(c + d + tie_x) * (double)(c + d + tie_y));
    #####: 3018:	  estimate = (denom == 0.0) ? (0.0/0.0) : (double)(c - d) / denom;
    #####: 3019:
    %%%%%: 3019-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 3019-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 3019-block  2
    %%%%%: 3019-block  3
    %%%%%: 3019-block  4
    #####: 3020:	  bool has_ties = (tie_x > 0 || tie_y > 0);
        -: 3021:	  bool do_exact;
    #####: 3022:
    %%%%%: 3022-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 3023:	  /* Mirror R: exact defaults to TRUE if N < 50 and NO ties */
    %%%%%: 3023-block  0
call    0 never executed
    #####: 3024:	  if (!exact_sv || !SvOK(exact_sv)) {
    %%%%%: 3024-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 3025:		   do_exact = (n < 50) && !has_ties;
    %%%%%: 3025-block  0
call    0 never executed
        -: 3026:	  } else {
    #####: 3027:		   do_exact = SvTRUE(exact_sv) ? 1 : 0;
    %%%%%: 3027-block  0
call    0 never executed
        -: 3028:	  }
        -: 3029:	  // If forced exact but ties exist, R overrides and falls back to approximation anyway
        3: 3030:	  if (do_exact && has_ties) do_exact = 0;
        3: 3030-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 3031:
        3: 3031-block  0
call    0 returned 3
        3: 3032:	  if (do_exact) {
call    0 returned 3
        3: 3033:		   double S_stat = c - d;
call    0 returned 3
        3: 3034:		   statistic = c;
call    0 returned 3
        -: 3035:		   p_value = kendall_exact_pvalue(n, S_stat, alternative);
        -: 3036:	  } else {
        3: 3037:		   // Normal approximation for large N or ties
       18: 3038:		   double var_S = n * (n - 1) * (2.0 * n + 5.0) / 18.0;
       18: 3038-block  0
branch  0 taken 15
branch  1 taken 3 (fallthrough)
       15: 3039:		   double S = c - d;
       15: 3040:		   if (continuity) S -= (S > 0 ? 1 : -1);
       15: 3041:		   statistic = S / sqrt(var_S);
       15: 3042:
       15: 3043:		   if (strcmp(alternative, "two.sided") == 0) {
       15: 3044:		       p_value = 2.0 * (1.0 - approx_pnorm(fabs(statistic)));
       15: 3045:		   } else if (strcmp(alternative, "less") == 0) {
       15: 3045-block  0
        -: 3046:		       p_value = approx_pnorm(statistic);
       3*: 3047:		   } else {
        3: 3047-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 3047-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 3047-block  2
    %%%%%: 3047-block  3
        -: 3048:		       p_value = 1.0 - approx_pnorm(statistic);
        -: 3049:		   }
        3: 3050:	  }
       18: 3051:	} else if (is_spearman) {
        3: 3051-block  0
       18: 3051-block  1
branch  0 taken 15
branch  1 taken 3 (fallthrough)
       15: 3052:	  double *restrict rank_x = safemalloc(n * sizeof(double));
       15: 3053:	  double *restrict rank_y = safemalloc(n * sizeof(double));
       15: 3053-block  0
        -: 3054:	  compute_ranks(x, rank_x, n);
        -: 3055:	  compute_ranks(y, rank_y, n);
        -: 3056:
        3: 3057:	  // Spearman rho = Pearson r of the ranks (Welford's Method)
       18: 3058:	  double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
        3: 3058-block  0
       15: 3058-block  1
       18: 3058-block  2
branch  0 taken 15
branch  1 taken 3 (fallthrough)
       15: 3059:	  for (size_t i = 0; i < n; i++) {
       15: 3059-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 3059-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 15
    #####: 3060:		   double dx = rank_x[i] - mean_x;
    #####: 3061:		   mean_x += dx / (i + 1);
    %%%%%: 3061-block  0
        -: 3062:		   double dy = rank_y[i] - mean_y;
        -: 3063:		   mean_y += dy / (i + 1);
       3*: 3064:		   M2_x += dx * (rank_x[i] - mean_x);
        3: 3064-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 3064-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
       3*: 3065:		   M2_y += dy * (rank_y[i] - mean_y);
        3: 3065-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 3065-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 3065-block  2
    %%%%%: 3065-block  3
        3: 3065-block  4
        -: 3066:		   cov  += dx * (rank_y[i] - mean_y);
    #####: 3067:	  }
    %%%%%: 3067-block  0
call    0 never executed
        -: 3068:	  estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
        -: 3069:	  
        3: 3070:	  // S = sum of squared rank differences (R's reported statistic)
        3: 3070-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 3071:	  double S_stat = 0.0;
        3: 3072:	  for (size_t i = 0; i < n; i++) {
        3: 3072-block  0
call    0 returned 3
        -: 3073:		   double diff = rank_x[i] - rank_y[i];
    #####: 3074:		   S_stat += diff * diff;
    #####: 3075:	  }
    %%%%%: 3075-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 3076:
    %%%%%: 3076-block  0
    #####: 3077:	  // Ties produce fractional (averaged) ranks — detect them
    #####: 3078:	  bool has_ties = 0, do_exact;
    %%%%%: 3078-block  0
call    0 never executed
        -: 3079:	  for (size_t i = 0; i < n; i++) {
        3: 3080:		   if (rank_x[i] != floor(rank_x[i]) || rank_y[i] != floor(rank_y[i])) {
        3: 3080-block  0
call    0 returned 3
call    1 returned 3
        -: 3081:		       has_ties = 1;
    #####: 3082:		       break;
    %%%%%: 3082-block  0
call    0 never executed
call    1 never executed
    #####: 3083:		   }
call    0 never executed
        -: 3084:	  }
       21: 3085:	  if (!exact_sv || !SvOK(exact_sv)) {
       21: 3085-block  0
call    0 returned 21
call    1 returned 21
       21: 3086:		   do_exact = (n < 10) && !has_ties;
call    0 returned 21
       21: 3087:	  } else {
call    0 returned 21
call    1 returned 21
       21: 3088:		   do_exact = SvTRUE(exact_sv) ? 1 : 0;
call    0 returned 21
call    1 returned 21
       21: 3089:	  }
call    0 returned 21
call    1 returned 21
       21: 3090:
call    0 returned 21
call    1 returned 21
       21: 3091:	  if (do_exact) {
call    0 returned 21
call    1 returned 21
       21: 3092:		   statistic = S_stat;
branch  0 taken 12 (fallthrough)
branch  1 taken 9
       12: 3093:		   p_value   = spearman_exact_pvalue(S_stat, n, alternative);
       12: 3093-block  0
call    0 returned 12
call    1 returned 12
       12: 3094:	  } else {
call    0 returned 12
       12: 3095:		   double r = estimate;
call    0 returned 12
call    1 returned 12
       12: 3096:		   if (continuity)
call    0 returned 12
call    1 returned 12
       12: 3097:		       r *= (1.0 - 1.0 / (2.0 * (n - 1)));
call    0 returned 12
call    1 returned 12
        -: 3098:		   statistic = r * sqrt((n - 2.0) / (1.0 - r * r));
        -: 3099:		   p_value = get_t_pvalue(statistic, (double)(n - 2), alternative);
       21: 3100:	  }
       21: 3100-block  0
call    0 returned 21
        -: 3101:	  Safefree(rank_x); Safefree(rank_y);
        -: 3102:	} else {
        -: 3103:	  Safefree(x); Safefree(y);
        -: 3104:	  croak("Unknown method");
        -: 3105:	}
        -: 3106:	Safefree(x); Safefree(y);
        -: 3107:	rhv = newHV();
        -: 3108:	hv_stores(rhv, "estimate", newSVnv(estimate));
        -: 3109:	hv_stores(rhv, "p.value", newSVnv(p_value));
        -: 3110:	hv_stores(rhv, "statistic", newSVnv(statistic));
        6: 3111:	hv_stores(rhv, "method", newSVpv(method, 0));
        6: 3112:	hv_stores(rhv, "alternative", newSVpv(alternative, 0));
        -: 3113:	if (is_pearson) {
        6: 3114:	  hv_stores(rhv, "parameter", newSVnv(df));
        6: 3114-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3114-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    #####: 3115:	  AV *restrict ci_av = newAV();
    %%%%%: 3115-block  0
call    0 never executed
        -: 3116:	  av_push(ci_av, newSVnv(ci_lower));
        -: 3117:	  av_push(ci_av, newSVnv(ci_upper));
        6: 3118:	  hv_stores(rhv, "conf.int", newRV_noinc((SV*)ci_av));
        6: 3119:	}
        6: 3119-block  0
call    0 returned 6
        -: 3120:
       6*: 3121:	RETVAL = newRV_noinc((SV*)rhv);
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3121-block  0
call    2 never executed
        6: 3121-block  1
call    3 returned 6
        -: 3122:}
        -: 3123:OUTPUT:
       78: 3124:    RETVAL
       72: 3124-block  0
       78: 3124-block  1
branch  0 taken 72
branch  1 taken 6 (fallthrough)
       72: 3125:
       72: 3125-block  0
call    0 returned 72
       72: 3126:void shapiro_test(data)
branch  0 taken 72 (fallthrough)
branch  1 taken 0
       72: 3126-block  0
branch  2 taken 72 (fallthrough)
branch  3 taken 0
       72: 3127:	SV *data
       72: 3127-block  0
call    0 returned 72
       72: 3128:PREINIT:
branch  0 taken 72 (fallthrough)
branch  1 taken 0
       72: 3129:	AV *restrict av;
       72: 3130:	HV *restrict ret_hash;
       72: 3131:	size_t n_raw, n = 0;
       72: 3131-block  0
        -: 3132:	double *restrict x, w = 0.0, p_val = 0.0, mean = 0.0, ssq = 0.0;
        -: 3133:PPCODE:
        -: 3134:	if (!SvROK(data) || SvTYPE(SvRV(data)) != SVt_PVAV) {
        -: 3135:	  croak("Expected an array reference");
        6: 3136:	}
        6: 3136-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3136-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    #####: 3137:
    %%%%%: 3137-block  0
call    0 never executed
    #####: 3138:	av = (AV *)SvRV(data);
call    0 never executed
        -: 3139:	n_raw = av_len(av) + 1;
        -: 3140:
        6: 3141:	Newx(x, n_raw, double);
        -: 3142:
       78: 3143:	// Extract variables and calculate mean (skipping undefined/NaN values)
        6: 3143-block  0
       78: 3143-block  1
branch  0 taken 72
branch  1 taken 6 (fallthrough)
       72: 3144:	for (size_t i = 0; i < n_raw; i++) {
       72: 3144-block  0
        -: 3145:	  SV **restrict elem = av_fetch(av, i, 0);
        6: 3146:	  if (elem && SvOK(*elem)) {
        6: 3146-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 3147:		   double val = SvNV(*elem);
    %%%%%: 3147-block  0
call    0 never executed
    #####: 3148:		   if (!isnan(val)) {
call    0 never executed
        -: 3149:		       x[n] = val;
        6: 3150:		       mean += val;
        6: 3150-block  0
call    0 returned 6
        -: 3151:		       n++;
        -: 3152:		   }
        6: 3153:	  }
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 3154:	}
    #####: 3155:
    #####: 3156:	if (n < 3 || n > 5000) {
    #####: 3157:	  Safefree(x);
    %%%%%: 3157-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 3157-block  1
        -: 3158:	  croak("Sample size must be between 3 and 5000 (R's limit)");
    #####: 3159:	}
    %%%%%: 3159-block  0
        -: 3160:
        -: 3161:	mean /= n;
        6: 3162:	// Calculate Sum of Squares */
       6*: 3163:	for (size_t i = 0; i < n; i++) {
        6: 3163-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3163-block  1
call    2 never executed
        6: 3163-block  2
call    3 returned 6
       6*: 3164:	  ssq += (x[i] - mean) * (x[i] - mean);
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3164-block  0
call    2 never executed
        6: 3164-block  1
call    3 returned 6
       78: 3165:	}
       78: 3165-block  0
branch  0 taken 72
branch  1 taken 6 (fallthrough)
       72: 3166:	if (ssq == 0.0) {
       72: 3166-block  0
call    0 returned 72
       72: 3167:	  Safefree(x);
        -: 3168:	  croak("Data is perfectly constant; cannot compute Shapiro-Wilk test");
        6: 3169:	}
        6: 3170:	qsort(x, n, sizeof(double), compare_doubles);
        6: 3171:	
        6: 3172:	// --- Core AS R94 Algorithm: Weights and Statistic W ---
        9: 3173:	if (n == 3) {
        6: 3173-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3173-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 3
        3: 3173-block  2
        3: 3174:	  double a_val = 0.7071067811865475; /* sqrt(1/2) */
       12: 3175:	  double b_val = a_val * (x[2] - x[0]);
        3: 3175-block  0
       12: 3175-block  1
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 3176:	  w = (b_val * b_val) / ssq;
        9: 3176-block  0
        -: 3177:	  if (w < 0.75) w = 0.75; 
        -: 3178:	  // Exact P-value for n=3
        3: 3179:	  p_val = 1.90985931710274 * (asin(sqrt(w)) - 1.04719755119660);
        3: 3180:	} else {
        3: 3181:	  double *restrict m, *restrict a;
        3: 3182:	  double sum_m2 = 0.0, b_val = 0.0;
       48: 3183:	  Newx(m, n, double);
        3: 3183-block  0
       48: 3183-block  1
branch  0 taken 45
branch  1 taken 3 (fallthrough)
       45: 3184:	  Newx(a, n, double);
       45: 3184-block  0
        -: 3185:	  for (size_t i = 0; i < n; i++) {
        -: 3186:		   m[i] = inverse_normal_cdf((i + 1.0 - 0.375) / (n + 0.25));
       78: 3187:		   sum_m2 += m[i] * m[i];
        6: 3187-block  0
       78: 3187-block  1
branch  0 taken 72
branch  1 taken 6 (fallthrough)
       72: 3188:	  }
       72: 3188-block  0
        -: 3189:	  double u = 1.0 / sqrt((double)n);
        6: 3190:	  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);
        -: 3191:	  a[n-1] = a_n;
        -: 3192:	  a[0]   = -a_n;
        -: 3193:	  if (n == 4 || n == 5) {
        -: 3194:		   double eps = (sum_m2 - 2.0 * m[n-1]*m[n-1]) / (1.0 - 2.0 * a_n*a_n);
        6: 3195:		   for (unsigned int i = 1; i < n-1; i++) {
        -: 3196:		       a[i] = m[i] / sqrt(eps);
        6: 3197:		   }
        6: 3197-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        -: 3198:	  } else {
        -: 3199:		   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);
        -: 3200:		   a[n-2] = a_n1;
        3: 3201:		   a[1]   = -a_n1;
        3: 3202:		   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);
        3: 3203:		   for (unsigned int i = 2; i < n-2; i++) {
        3: 3203-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 3204:		       a[i] = m[i] / sqrt(eps);
    %%%%%: 3204-block  0
        -: 3205:		   }
        -: 3206:	  }
        3: 3207:	  for (size_t i = 0; i < n; i++) {
        3: 3208:		   b_val += a[i] * x[i];
        3: 3209:	  }
        3: 3210:	  w = (b_val * b_val) / ssq;
        -: 3211:	// --- AS R94 P-Value Calculation: High Precision Refinement ---
        -: 3212:	  /* NOTE: p_val is declared in PREINIT above;
        3: 3213:		* do NOT shadow it with a local 'double p_val' here or the result will never reach the caller.
        3: 3213-block  0
        -: 3214:		*/
        -: 3215:	  double y = log(1.0 - w);
        -: 3216:	  double z;
        3: 3217:	  if (n <= 11) {
        -: 3218:		   // Royston's branch for 4 <= n <= 11 (AS R94, small-sample path).
        3: 3219:		   // gamma is the upper bound on y = log(1-W);
        3: 3220:		   // if y reaches gamma the p-value is essentially zero
        3: 3221:		   double nn = (double)n;
        3: 3222:		   double gamma = 0.459 * nn - 2.273;
        3: 3223:		   if (y >= gamma) {
        3: 3223-block  0
        -: 3224:		       p_val = 1e-19;
        -: 3225:		   } else {
       6*: 3226:		       // Horner-form polynomials in n for mu and log(sigma)
        6: 3226-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3226-block  1
       6*: 3227:		       double mu     = 0.544  + nn * (-0.39978  + nn * ( 0.025054  - nn * 0.0006714));
        6: 3227-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3227-block  1
        -: 3228:		       double sig_val= 1.3822 + nn * (-0.77857  + nn * ( 0.062767  - nn * 0.0020322));
        6: 3229:		       double sigma  = exp(sig_val);
        6: 3229-block  0
call    0 returned 6
call    1 returned 6
        -: 3230:		       z = (-log(gamma - y) - mu) / sigma;
        6: 3231:		       /* Upper-tail probability P(Z > z): small W → large z → small p-value.
        6: 3231-block  0
call    0 returned 6
        6: 3232:		       */
call    0 returned 6
        6: 3233:		       p_val = 0.5 * erfc(z * M_SQRT1_2);
call    0 returned 6
call    1 returned 6
        6: 3234:		   }
call    0 returned 6
call    1 returned 6
        6: 3235:	  } else {
call    0 returned 6
call    1 returned 6
        6: 3236:		   // Royston's branch for n >= 12 (AS R94, large-sample path)
call    0 returned 6
call    1 returned 6
       6*: 3237:		   double ln_n   = log((double)n);
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3237-block  0
call    2 never executed
        6: 3238:		   // Horner-form polynomials in log(n) for mu and log(sigma). */
        6: 3238-block  0
call    0 returned 6
call    1 returned 6
        -: 3239:		   double mu     = -1.5861 + ln_n * (-0.31082 + ln_n * (-0.083751 + ln_n * 0.0038915));
        -: 3240:		   double sig_val= -0.4803 + ln_n * (-0.082676 + ln_n * 0.0030302);
        -: 3241:		   double sigma  = exp(sig_val);
        -: 3242:		   z = (y - mu) / sigma;
       30: 3243:		   p_val = 0.5 * erfc(z * M_SQRT1_2);
       30: 3244:	  }
       30: 3245:	  // Clamp the p-value
        -: 3246:	  if (p_val > 1.0) p_val = 1.0;
    30102: 3247:	  if (p_val < 0.0) p_val = 0.0;
       30: 3247-block  0
    30072: 3247-block  1
    30102: 3247-block  2
branch  0 taken 30072
branch  1 taken 30 (fallthrough)
    30072: 3248:	  
    30081: 3249:	  Safefree(m); m = NULL;  Safefree(a); a = NULL;
    30072: 3249-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 30063
        9: 3249-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 3249-block  2
        9: 3250:	}
        9: 3251:	Safefree(x); x = NULL;
        9: 3251-block  0
call    0 returned 9
      642: 3252:	ret_hash = newHV();
      633: 3252-block  0
      642: 3252-block  1
branch  0 taken 633
branch  1 taken 9 (fallthrough)
      633: 3253:	hv_stores(ret_hash, "statistic", newSVnv(w));
      633: 3253-block  0
call    0 returned 633
      633: 3254:	hv_stores(ret_hash, "W",         newSVnv(w));
branch  0 taken 633 (fallthrough)
branch  1 taken 0
      633: 3254-block  0
branch  2 taken 633 (fallthrough)
branch  3 taken 0
      633: 3255:	hv_stores(ret_hash, "p_value",   newSVnv(p_val));
      633: 3255-block  0
call    0 returned 633
      633: 3256:	hv_stores(ret_hash, "p.value",   newSVnv(p_val));
branch  0 taken 624 (fallthrough)
branch  1 taken 9
      624: 3256-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 624
        9: 3257:	EXTEND(SP, 1);
        9: 3258:	PUSHs(sv_2mortal(newRV_noinc((SV *)ret_hash)));
        9: 3258-block  0
        -: 3259:
      633: 3260:double min(...)
      633: 3260-block  0
        -: 3261:	PROTOTYPE: @
        -: 3262:	INIT:
    30063: 3263:		double min_val = 0.0;
    30063: 3263-block  0
branch  0 taken 30063 (fallthrough)
branch  1 taken 0
    30063: 3264:		size_t count = 0;
    30063: 3264-block  0
call    0 returned 30063
    30063: 3265:		bool first = TRUE;
branch  0 taken 30045 (fallthrough)
branch  1 taken 18
    30045: 3265-block  0
branch  2 taken 30 (fallthrough)
branch  3 taken 30015
       48: 3266:	CODE:
       48: 3267:		for (unsigned short int i = 0; i < items; i++) {
       48: 3267-block  0
        -: 3268:			SV* restrict arg = ST(i);
    30063: 3269:			if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
    30063: 3269-block  0
        -: 3270:				AV* restrict av = (AV*)SvRV(arg);
        -: 3271:				size_t len = av_len(av) + 1;
       30: 3272:				for (size_t j = 0; j < len; j++) {
       30: 3272-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 27
        3: 3272-block  1
call    2 returned 0
       27: 3273:				     SV** restrict tv = av_fetch(av, j, 0);
       27: 3273-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 27
        -: 3274:				     if (tv && SvOK(*tv)) {
        -: 3275:				         double val = SvNV(*tv);
        -: 3276:				         if (first || val < min_val) {
        -: 3277:				             min_val = val;
        -: 3278:				             first = FALSE;
        -: 3279:				         }
       33: 3280:				         count++;
       33: 3281:				     } else {
       33: 3282:				         croak("min: undefined value at array ref index %zu (argument %d)", j, (int)i);
        -: 3283:				     }
    30105: 3284:				 }
       33: 3284-block  0
    30072: 3284-block  1
    30105: 3284-block  2
branch  0 taken 30072
branch  1 taken 33 (fallthrough)
    30072: 3285:			} else if (SvOK(arg)) {
    30084: 3286:				 double val = SvNV(arg);
    30072: 3286-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 30060
       12: 3286-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 3286-block  2
       12: 3287:				 if (first || val < min_val) {
       12: 3288:				     min_val = val;
       12: 3288-block  0
call    0 returned 12
      945: 3289:				     first = FALSE;
      933: 3289-block  0
      945: 3289-block  1
branch  0 taken 933
branch  1 taken 12 (fallthrough)
      933: 3290:				 }
      933: 3290-block  0
call    0 returned 933
      933: 3291:				 count++;
branch  0 taken 933 (fallthrough)
branch  1 taken 0
      933: 3291-block  0
branch  2 taken 933 (fallthrough)
branch  3 taken 0
      933: 3292:			} else {
      933: 3292-block  0
call    0 returned 933
      933: 3293:				 croak("min: undefined value at argument index %d", (int)i);
branch  0 taken 921 (fallthrough)
branch  1 taken 12
      921: 3293-block  0
branch  2 taken 30 (fallthrough)
branch  3 taken 891
       42: 3294:			}
       42: 3295:		}
       42: 3295-block  0
        -: 3296:		if (count == 0) croak("min needs >= 1 numeric element");
      933: 3297:		RETVAL = min_val;
      933: 3297-block  0
        -: 3298:	OUTPUT:
        -: 3299:	  RETVAL
    30060: 3300:
    30060: 3300-block  0
branch  0 taken 30060 (fallthrough)
branch  1 taken 0
    30060: 3301:double max(...)
    30060: 3301-block  0
call    0 returned 30060
    30060: 3302:	PROTOTYPE: @
branch  0 taken 30042 (fallthrough)
branch  1 taken 18
    30042: 3302-block  0
branch  2 taken 71 (fallthrough)
branch  3 taken 29971
       89: 3303:	INIT:
       89: 3304:		double max_val = 0.0;
       89: 3304-block  0
        -: 3305:		size_t count = 0;
    30060: 3306:		bool first = TRUE;
    30060: 3306-block  0
        -: 3307:	CODE:
        -: 3308:		for (size_t i = 0; i < items; i++) {
       33: 3309:		   SV* restrict arg = ST(i);
       33: 3309-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 30
        3: 3309-block  1
call    2 returned 0
       30: 3310:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
       30: 3310-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 30
        -: 3311:		       AV* restrict av = (AV*)SvRV(arg);
        -: 3312:		       size_t len = av_len(av) + 1;
        -: 3313:		       for (size_t j = 0; j < len; j++) {
        -: 3314:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 3315:		           if (tv && SvOK(*tv)) {
        -: 3316:		               double val = SvNV(*tv);
       12: 3317:		               if (first || val > max_val) {
       12: 3318:		                   max_val = val;
        -: 3319:		                   first = FALSE;
        -: 3320:		               }
       12: 3321:		               count++;
        -: 3322:		           } else {
       12: 3323:		               croak("max: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 3324:		           }
       12: 3325:		       }
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 3326:		   } else if (SvOK(arg)) {
    %%%%%: 3326-block  0
call    0 never executed
        -: 3327:		       double val = SvNV(arg);
        -: 3328:		       if (first || val > max_val) {
       42: 3329:		           max_val = val;
       12: 3329-block  0
       42: 3329-block  1
branch  0 taken 30
branch  1 taken 12 (fallthrough)
        -: 3330:		           first = FALSE;
       30: 3331:		       }
       30: 3331-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 6
       24: 3331-block  1
branch  2 taken 18 (fallthrough)
branch  3 taken 6
       18: 3332:		       count++;
       18: 3332-block  0
call    0 returned 18
       18: 3333:		   } else {
branch  0 taken 6 (fallthrough)
branch  1 taken 12
        6: 3334:		       croak("max: undefined value at argument index %zu", i);
        6: 3334-block  0
call    0 returned 6
        6: 3335:		   }
        6: 3336:	  }
        6: 3337:	  if (count == 0) croak("max needs >= 1 numeric element");
       12: 3338:	  RETVAL = max_val;
       12: 3338-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 6
        6: 3339:	OUTPUT:
        6: 3339-block  0
call    0 returned 6
        6: 3340:		RETVAL
        6: 3341:
        6: 3342:SV* runif(...)
        6: 3343:CODE:
        6: 3343-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3344:{
        6: 3344-block  0
call    0 returned 6
        6: 3345:	size_t n = 0;
        6: 3346:	double min = 0.0, max = 1.0;
        6: 3347:
        -: 3348:	// Flags to track what has been assigned
        -: 3349:	bool n_set = 0, min_set = 0, max_set = 0;
        -: 3350:
        -: 3351:	unsigned int i = 0;
       12: 3352:
       12: 3352-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 6
        6: 3353:	if (items == 0) {
        6: 3353-block  0
call    0 returned 6
        6: 3354:	  croak("Usage: runif(n, [min=0], [max=1]) or runif(n => $n, ...)");
        6: 3355:	}
        6: 3355-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 3356:
        3: 3356-block  0
call    0 returned 3
        3: 3357:	while (i < items) {
        3: 3358:		// 1. Check if the current argument is a string key for a named parameter
        3: 3358-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 3359:		if (i + 1 < items && SvPOK(ST(i))) {
        3: 3359-block  0
call    0 returned 3
        3: 3360:			char *restrict key = SvPV_nolen(ST(i));
        -: 3361:			if (strEQ(key, "n")) {
    #####: 3362:				n = (size_t)SvUV(ST(i+1));
    %%%%%: 3362-block  0
call    0 never executed
        -: 3363:				n_set = 1;
       12: 3364:				i += 2;
       12: 3364-block  0
        -: 3365:				continue;
       12: 3366:			} else if (strEQ(key, "min")) {
       12: 3366-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 3367:				min = SvNV(ST(i+1));
    %%%%%: 3367-block  0
call    0 never executed
        -: 3368:				min_set = 1;
        -: 3369:				i += 2;
      12*: 3370:				continue;
       12: 3370-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 3370-block  1
call    2 never executed
call    3 never executed
       12: 3371:			} else if (strEQ(key, "max")) {
       12: 3371-block  0
call    0 returned 12
       12: 3372:				max = SvNV(ST(i+1));
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3373:				max_set = 1;
       12: 3373-block  0
call    0 returned 12
        -: 3374:				i += 2;
       12: 3375:				continue;
    60060: 3376:			}
       12: 3376-block  0
    60060: 3376-block  1
branch  0 taken 60048
branch  1 taken 12 (fallthrough)
        -: 3377:		}
    60048: 3378:
    60048: 3378-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 60048
    #####: 3379:		// 2. Fallback to positional parsing if it's not a recognized key
    %%%%%: 3379-block  0
        -: 3380:		if (!n_set) {
    60048: 3381:			n = (size_t)SvUV(ST(i));
    60048: 3381-block  0
call    0 returned 60048
        -: 3382:			n_set = 1;
    60048: 3383:		} else if (!min_set) {
    60048: 3383-block  0
call    0 returned 60048
call    1 returned 60048
        -: 3384:			min = SvNV(ST(i));
       12: 3385:			min_set = 1;
       12: 3385-block  0
call    0 returned 12
        -: 3386:		} else if (!max_set) {
        -: 3387:			max = SvNV(ST(i));
        -: 3388:			max_set = 1;
        -: 3389:		} else {
        -: 3390:			croak("Too many arguments or unrecognized parameter passed to runif()");
        -: 3391:		}
        -: 3392:		i++;
        -: 3393:	}
      36*: 3394:	if (!n_set) {
branch  0 taken 0 (fallthrough)
branch  1 taken 36
    %%%%%: 3394-block  0
call    2 never executed
call    3 never executed
       36: 3395:		croak("runif() requires at least the 'n' parameter");
       36: 3395-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 33
        3: 3396:	}
        3: 3396-block  0
call    0 returned 0
        -: 3397:	// Ensure PRNG is seeded
       33: 3398:	AUTO_SEED_PRNG();
       33: 3399:	AV *restrict results = newAV();
        -: 3400:	if (n > 0) {
       33: 3401:		av_extend(results, n - 1);
        -: 3402:	}
      126: 3403:	const double range = max - min;
       33: 3403-block  0
       93: 3403-block  1
      126: 3403-block  2
branch  0 taken 93
branch  1 taken 33 (fallthrough)
       93: 3404:	for (size_t j = 0; j < n; j++) {
       93: 3404-block  0
call    0 returned 93
       93: 3405:		double r;
        -: 3406:		if (max < min) {
       93: 3407:			r = NAN; // R behavior for inverted ranges
branch  0 taken 33 (fallthrough)
branch  1 taken 60
       33: 3407-block  0
call    2 returned 33
       60: 3408:		} else {
       60: 3408-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 30
       30: 3408-block  1
call    2 returned 30
       30: 3409:			r = min + range * Drand01();
       30: 3409-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 0
       30: 3409-block  1
call    2 returned 30
    #####: 3410:		}
    %%%%%: 3410-block  0
call    0 never executed
        -: 3411:		av_push(results, newSVnv(r));
        -: 3412:	}
        -: 3413:	RETVAL = newRV_noinc((SV*)results);
       33: 3414:}
       33: 3414-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 3
       30: 3414-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 27
        6: 3414-block  2
call    4 returned 0
       27: 3415:OUTPUT:
       27: 3415-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 3
       24: 3415-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 21
        6: 3415-block  2
call    4 returned 0
        -: 3416:    RETVAL
       21: 3417:
       21: 3417-block  0
call    0 returned 21
       21: 3418:SV* rbinom(...)
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 3419:	CODE:
       21: 3419-block  0
call    0 returned 21
    61518: 3420:	{
    61518: 3420-block  0
branch  0 taken 61497
branch  1 taken 21 (fallthrough)
    61497: 3421:	  // Auto-seed the PRNG if the Perl script hasn't done so yet
    61497: 3421-block  0
call    0 returned 61497
call    1 returned 61497
call    2 returned 61497
        -: 3422:	  AUTO_SEED_PRNG();
        -: 3423:	  if (items % 2 != 0)
        -: 3424:		   croak("Usage: rbinom(n => 10, size => 100, prob => 0.5)");
       21: 3425:	  //Parse named arguments
       21: 3425-block  0
call    0 returned 21
        -: 3426:	  size_t n = 0, size = 0;
        -: 3427:	  double prob = 0.5;
        -: 3428:	  
        -: 3429:	  bool size_set = false, prob_set = false;
        -: 3430:
        -: 3431:	  for (unsigned short i = 0; i < items; i += 2) {
        -: 3432:		   const char* restrict key = SvPV_nolen(ST(i));
        -: 3433:		   SV* restrict val = ST(i + 1);
        -: 3434:
       27: 3435:		   if      (strEQ(key, "n"))      n    = (unsigned int)SvUV(val);
       27: 3435-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 3
       24: 3435-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 21
        6: 3436:		   else if (strEQ(key, "size")) { size = (unsigned int)SvUV(val); size_set = true; }
        6: 3436-block  0
call    0 returned 0
        -: 3437:		   else if (strEQ(key, "prob")) { prob = SvNV(val); prob_set = true; }
       21: 3438:		   else croak("rbinom: unknown argument '%s'", key);
       21: 3439:	  }
       21: 3439-block  0
call    0 returned 21
       21: 3440:
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 3440-block  0
call    2 returned 0
        -: 3441:	  // R requires size and prob to be explicitly passed in rbinom
        -: 3442:	  if (!size_set || !prob_set) croak("rbinom: 'size' and 'prob' are required arguments");
        -: 3443:	  if (prob < 0.0 || prob > 1.0) croak("rbinom: prob must be between 0 and 1");
      18*: 3444:
       18: 3444-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 3444-block  1
call    2 never executed
       18: 3444-block  2
call    3 returned 18
       18: 3445:	  AV *restrict result_av = newAV();
       18: 3446:	  if (n > 0) {
        -: 3447:		   av_extend(result_av, n - 1);
     6078: 3448:		   for (unsigned int i = 0; i < n; i++) {
     6060: 3448-block  0
     6078: 3448-block  1
branch  0 taken 6063
branch  1 taken 15 (fallthrough)
     6063: 3449:		       av_store(result_av, i, newSVuv(generate_binomial(size, prob)));
     6063: 3449-block  0
call    0 returned 6063
     6063: 3450:		   }
branch  0 taken 6063 (fallthrough)
branch  1 taken 0
     6063: 3450-block  0
branch  2 taken 6063 (fallthrough)
branch  3 taken 0
     6063: 3451:	  }
     6063: 3451-block  0
call    0 returned 6060
     6060: 3452:
     6060: 3453:	  RETVAL = newRV_noinc((SV*)result_av);
branch  0 taken 15 (fallthrough)
branch  1 taken 6045
       15: 3453-block  0
     6060: 3454:	}
     6060: 3454-block  0
branch  0 taken 6036 (fallthrough)
branch  1 taken 24
     6036: 3454-block  1
        -: 3455:	OUTPUT:
        -: 3456:	  RETVAL
       15: 3457:
       15: 3457-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    #####: 3458:SV*
    %%%%%: 3458-block  0
call    0 never executed
    #####: 3459:hist(SV* x_sv, ...)
call    0 never executed
        -: 3460:	CODE:
        -: 3461:	{
       15: 3462:		// 1. Validate Input
        -: 3463:		if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
       15: 3464:			croak("hist: first argument must be an array reference");
       15: 3464-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
        -: 3465:
    #####: 3466:		AV*restrict x_av = (AV*)SvRV(x_sv);
    %%%%%: 3466-block  0
call    0 never executed
       15: 3467:		size_t n_raw = av_len(x_av) + 1;
       15: 3467-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
        -: 3468:		if (n_raw == 0) croak("hist: input array is empty");
      15*: 3469:
       15: 3469-block  0
    %%%%%: 3469-block  1
       15: 3469-block  2
branch  0 taken 15
branch  1 taken 0 (fallthrough)
        -: 3470:		// 2. Extract Data & Find Range
       15: 3471:		double *restrict x;
       15: 3471-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 3471-block  1
call    2 returned 15
branch  3 taken 15 (fallthrough)
branch  4 taken 0
       15: 3472:		Newx(x, n_raw, double);
       15: 3472-block  0
call    0 returned 15
       15: 3473:		size_t n = 0;
        -: 3474:		double min_val = DBL_MAX, max_val = -DBL_MAX;
        -: 3475:
        -: 3476:		for (size_t i = 0; i < n_raw; i++) {
      15*: 3477:			SV**restrict tv = av_fetch(x_av, i, 0);
       15: 3477-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 3477-block  1
call    2 never executed
branch  3 never executed (fallthrough)
branch  4 never executed
    #####: 3478:			if (tv && SvOK(*tv)) {
    %%%%%: 3478-block  0
call    0 never executed
        -: 3479:				 double val = SvNV(*tv);
        -: 3480:				 x[n++] = val;
      15*: 3481:				 if (val < min_val) min_val = val;
       15: 3481-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 3481-block  1
call    2 never executed
        -: 3482:				 if (val > max_val) max_val = val;
        -: 3483:			}
        -: 3484:		}
      15*: 3485:		if (n == 0) {
       15: 3485-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 3485-block  1
call    2 never executed
       15: 3485-block  2
call    3 returned 15
      15*: 3486:			Safefree(x);
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 3486-block  0
call    2 never executed
       15: 3486-block  1
call    3 returned 15
      15*: 3487:			croak("hist: input contains no valid numeric data");
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 3487-block  0
call    2 never executed
       15: 3487-block  1
call    3 returned 15
      15*: 3488:		}
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 3488-block  0
call    2 never executed
       15: 3488-block  1
call    3 returned 15
        -: 3489:		// 3. Determine Bin Count (Sturges default or user-provided)
        -: 3490:		size_t n_bins = 0;
       15: 3491:
       84: 3492:		if (items == 2) {
       84: 3492-block  0
branch  0 taken 69
branch  1 taken 15 (fallthrough)
       69: 3493:			// Support pure positional argument: hist($data, 22)
       69: 3493-block  0
        -: 3494:			n_bins = (size_t)SvIV(ST(1));
        -: 3495:		} else if (items > 2) {
        -: 3496:			/* Support named parameters even if mixed with positional arguments */
       15: 3497:			for (unsigned short i = 1; i < items - 1; i++) {
       15: 3497-block  0
call    0 returned 15
        -: 3498:				 /* Make sure the SV holds a string before doing string comparison */
        -: 3499:				 if (SvPOK(ST(i)) && strEQ(SvPV_nolen(ST(i)), "breaks")) {
       15: 3500:				     n_bins = (size_t)SvIV(ST(i+1));
call    0 returned 15
       15: 3501:				     break;
call    0 returned 15
       15: 3502:				 }
call    0 returned 15
       15: 3503:			}
call    0 returned 15
       15: 3504:			/* Fallback: if 'breaks' wasn't found but a positional number was given first */
call    0 returned 15
       84: 3505:			if (n_bins == 0 && looks_like_number(ST(1))) {
       69: 3505-block  0
       84: 3505-block  1
branch  0 taken 69
branch  1 taken 15 (fallthrough)
       69: 3506:				 n_bins = (size_t)SvIV(ST(1));
       69: 3506-block  0
call    0 returned 69
call    1 returned 69
       69: 3507:			}
branch  0 taken 54 (fallthrough)
branch  1 taken 15
       54: 3508:		}
       54: 3508-block  0
call    0 returned 54
call    1 returned 54
       54: 3509:		if (n_bins == 0) n_bins = calculate_sturges_bins(n);
call    0 returned 54
call    1 returned 54
       54: 3510:		// 4. Allocate Result Arrays
call    0 returned 54
call    1 returned 54
        -: 3511:		double *restrict breaks, *restrict mids, *restrict density;
        -: 3512:		size_t *restrict counts;
       15: 3513:		Newx(breaks,  n_bins + 1, double);
       15: 3513-block  0
call    0 returned 15
call    1 returned 15
       15: 3514:		Newx(mids,    n_bins,     double);
call    0 returned 15
call    1 returned 15
       15: 3515:		Newx(density, n_bins,     double);
call    0 returned 15
call    1 returned 15
       15: 3516:		Newx(counts,  n_bins,     size_t);
call    0 returned 15
call    1 returned 15
        -: 3517:
        -: 3518:		// Generate simple linear breaks
       15: 3519:		double step = (max_val - min_val) / (double)n_bins;
call    0 returned 15
call    1 returned 15
call    2 returned 15
       15: 3520:		for (size_t i = 0; i <= n_bins; i++) {
call    0 returned 15
call    1 returned 15
        -: 3521:			breaks[i] = min_val + (double)i * step;
       15: 3522:		}
call    0 returned 15
        -: 3523:
        -: 3524:		// 5. Compute Statistics
        -: 3525:		compute_hist_logic(x, n, breaks, n_bins, counts, mids, density);
        -: 3526:
        -: 3527:		// 6. Build Return HashRef
        -: 3528:		HV*restrict res_hv = newHV();
        -: 3529:		AV*restrict av_breaks  = newAV();
       12: 3530:		AV*restrict av_counts  = newAV();
       12: 3531:		AV*restrict av_mids    = newAV();
       12: 3532:		AV*restrict av_density = newAV();
        -: 3533:		for (size_t i = 0; i <= n_bins; i++) {
        -: 3534:			av_push(av_breaks, newSVnv(breaks[i]));
       12: 3535:			if (i < n_bins) {
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3535-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 3
        9: 3535-block  1
branch  4 taken 9 (fallthrough)
branch  5 taken 0
        9: 3536:				 av_push(av_counts,  newSViv(counts[i]));
        9: 3537:				 av_push(av_mids,    newSVnv(mids[i]));
        9: 3537-block  0
        -: 3538:				 av_push(av_density, newSVnv(density[i]));
        -: 3539:			}
        -: 3540:		}
       12: 3541:		hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
       12: 3541-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 3542:		hv_stores(res_hv, "counts",  newRV_noinc((SV*)av_counts));
    %%%%%: 3542-block  0
call    0 never executed
        -: 3543:		hv_stores(res_hv, "mids",    newRV_noinc((SV*)av_mids));
       27: 3544:		hv_stores(res_hv, "density", newRV_noinc((SV*)av_density));
       12: 3544-block  0
       15: 3544-block  1
       27: 3544-block  2
branch  0 taken 15
branch  1 taken 12 (fallthrough)
       15: 3545:
       15: 3545-block  0
call    0 returned 15
       15: 3546:		// Clean
        -: 3547:		Safefree(x); Safefree(breaks); Safefree(mids);
       15: 3548:		Safefree(density); Safefree(counts);
branch  0 taken 3 (fallthrough)
branch  1 taken 12
        3: 3548-block  0
       12: 3549:
       12: 3549-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3549-block  1
    #####: 3550:		RETVAL = newRV_noinc((SV*)res_hv);
    %%%%%: 3550-block  0
call    0 never executed
        -: 3551:	}
       12: 3552:	OUTPUT:
       12: 3552-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3552-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 3552-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 12
    #####: 3553:	  RETVAL
    %%%%%: 3553-block  0
call    0 never executed
       12: 3554:
       12: 3555:SV* quantile(...)
       12: 3555-block  0
call    0 returned 12
      12*: 3556:	CODE:
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 3556-block  0
call    2 never executed
        -: 3557:	{
        -: 3558:		SV *restrict x_sv = NULL;
        -: 3559:		SV *restrict probs_sv = NULL;
      12*: 3560:		int arg_idx = 0;
       12: 3560-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 3560-block  1
call    2 never executed
       12: 3560-block  2
call    3 returned 12
       12: 3561:
      624: 3562:		/* --- 1. Consume first positional arg as 'x' if it's an array ref --- */
      612: 3562-block  0
      624: 3562-block  1
branch  0 taken 612
branch  1 taken 12 (fallthrough)
      612: 3563:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
      612: 3563-block  0
call    0 returned 612
      612: 3564:			 x_sv = ST(arg_idx);
branch  0 taken 612 (fallthrough)
branch  1 taken 0
      612: 3564-block  0
branch  2 taken 612 (fallthrough)
branch  3 taken 0
      612: 3565:			 arg_idx++;
      612: 3565-block  0
call    0 returned 612
        -: 3566:		}
        -: 3567:
       12: 3568:		/* --- 2. Remaining args must be key-value pairs --- */
       12: 3568-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 3569:		if ((items - arg_idx) % 2 != 0)
    %%%%%: 3569-block  0
call    0 never executed
    #####: 3570:			 croak("Usage: quantile(\\@data, probs => \\@probs)  OR  quantile(x => \\@data, probs => \\@probs)");
call    0 never executed
        -: 3571:
        -: 3572:		for (; arg_idx < items; arg_idx += 2) {
       12: 3573:			 const char *restrict key = SvPV_nolen(ST(arg_idx));
       12: 3573-block  0
call    0 returned 12
        -: 3574:			 SV *restrict val = ST(arg_idx + 1);
       12: 3575:
       12: 3576:			 if      (strEQ(key, "x"))     x_sv     = val;
        -: 3577:			 else if (strEQ(key, "probs")) probs_sv = val;
        -: 3578:			 else croak("quantile: unknown argument '%s'", key);
       24: 3579:		}
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 3579-block  0
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 3579-block  1
branch  4 taken 12 (fallthrough)
branch  5 taken 0
       12: 3579-block  2
       12: 3580:		if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
       12: 3581:			croak("quantile: 'x' must be an array reference");
       12: 3581-block  0
call    0 returned 12
       12: 3582:		AV *restrict x_av = (AV*)SvRV(x_sv);
call    0 returned 12
       39: 3583:		size_t n_raw = av_len(x_av) + 1;
       27: 3583-block  0
       39: 3583-block  1
branch  0 taken 27
branch  1 taken 12 (fallthrough)
       27: 3584:		if (n_raw == 0) croak("quantile: 'x' is empty");
       27: 3584-block  0
call    0 returned 27
      27*: 3585:
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 3585-block  0
branch  2 taken 27 (fallthrough)
branch  3 taken 0
       27: 3585-block  1
call    4 returned 27
    %%%%%: 3585-block  2
       27: 3586:		/* --- Extract valid numeric data & drop NAs --- */
       27: 3586-block  0
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 3586-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 27
    #####: 3587:		double *restrict x;
    %%%%%: 3587-block  0
call    0 never executed
call    1 never executed
    #####: 3588:		Newx(x, n_raw, double);
call    0 never executed
        -: 3589:		size_t n = 0;
        -: 3590:		for (size_t i = 0; i < n_raw; i++) {
        -: 3591:			SV **restrict tv = av_fetch(x_av, i, 0);
    #####: 3592:			if (tv && SvOK(*tv)) {
    %%%%%: 3592-block  0
call    0 never executed
    #####: 3593:				 x[n++] = SvNV(*tv);
    %%%%%: 3593-block  0
    %%%%%: 3593-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
        -: 3594:			}
        -: 3595:		}
        -: 3596:		if (n == 0) {
       12: 3597:			Safefree(x);
       12: 3597-block  0
call    0 returned 12
        -: 3598:			croak("quantile: 'x' contains no valid numbers");
       39: 3599:		}
       39: 3599-block  0
branch  0 taken 27
branch  1 taken 12 (fallthrough)
       27: 3600:		// --- Sort Data for Quantile Math ---
        -: 3601:		qsort(x, n, sizeof(double), compare_doubles);
       27: 3602:		// --- Parse Probabilities (Default matches R's c(0, .25, .5, .75, 1)) ---
       27: 3602-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 24
        3: 3603:		double default_probs[] = {0.0, 0.25, 0.50, 0.75, 1.0};
        3: 3603-block  0
       24: 3604:		unsigned int n_probs = 5;
       24: 3604-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 21
        3: 3605:		double *restrict probs;
        3: 3605-block  0
       21: 3606:
       21: 3606-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 3607:		if (probs_sv && SvROK(probs_sv) && SvTYPE(SvRV(probs_sv)) == SVt_PVAV) {
        3: 3607-block  0
        -: 3608:			AV *restrict p_av = (AV*)SvRV(probs_sv);
        -: 3609:			n_probs = av_len(p_av) + 1;
       18: 3610:			Newx(probs, n_probs, double);
       18: 3611:			for (unsigned int i = 0; i < n_probs; i++) {
       18: 3612:				 SV **tv = av_fetch(p_av, i, 0);
       18: 3613:				 probs[i] = (tv && SvOK(*tv)) ? SvNV(*tv) : 0.0;
       18: 3613-block  0
        -: 3614:				 if (probs[i] < 0.0 || probs[i] > 1.0) {
        -: 3615:				     Safefree(x); Safefree(probs);
        -: 3616:				     croak("quantile: probabilities must be between 0 and 1");
        -: 3617:				 }
       27: 3618:			}
        -: 3619:		} else {
       27: 3620:			Newx(probs, n_probs, double);
       27: 3620-block  0
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 3621:			for (unsigned int i = 0; i < n_probs; i++) probs[i] = default_probs[i];
       27: 3621-block  0
        -: 3622:		}
    #####: 3623:
    %%%%%: 3623-block  0
        -: 3624:		/* --- Calculate Quantiles (R Type 7 Algorithm) --- */
        -: 3625:		HV *restrict res_hv = newHV();
       27: 3626:
       27: 3626-block  0
call    0 returned 27
call    1 returned 27
        -: 3627:		for (size_t i = 0; i < n_probs; i++) {
        -: 3628:			double p = probs[i], q = 0.0;
       12: 3629:
       12: 3629-block  0
call    0 returned 12
       12: 3630:			if (n == 1) {
call    0 returned 12
        -: 3631:				 q = x[0];
       12: 3632:			} else if (p == 1.0) {
call    0 returned 12
        -: 3633:				 q = x[n - 1]; /* Prevent out-of-bounds mapping */
        -: 3634:			} else if (p == 0.0) {
        -: 3635:				 q = x[0];
        -: 3636:			} else {
        -: 3637:				 /* Continuous sample quantile interpolation (Type 7) */
        -: 3638:				 double h = (n - 1) * p;
        -: 3639:				 unsigned int j = (unsigned int)h; /* floor via cast */
        -: 3640:				 double gamma = h - j;
      117: 3641:				 q = (1.0 - gamma) * x[j] + gamma * x[j + 1];
      117: 3642:			}
        -: 3643:
      267: 3644:			/* Format hash key to exactly match R's naming convention ("25%", "33.3%") */
      117: 3644-block  0
      150: 3644-block  1
      267: 3644-block  2
branch  0 taken 150
branch  1 taken 117 (fallthrough)
      150: 3645:			char key[32];
      261: 3646:			double pct = p * 100.0;
      150: 3646-block  0
branch  0 taken 111 (fallthrough)
branch  1 taken 39
      111: 3646-block  1
branch  2 taken 111 (fallthrough)
branch  3 taken 0
      111: 3646-block  2
      111: 3647:			
      111: 3648:			if (pct == (unsigned int)pct) {
      111: 3648-block  0
call    0 returned 111
    60915: 3649:				 snprintf(key, sizeof(key), "%.0f%%", pct);
    60804: 3649-block  0
    60915: 3649-block  1
branch  0 taken 60804
branch  1 taken 111 (fallthrough)
    60804: 3650:			} else {
    60804: 3650-block  0
call    0 returned 60804
    60804: 3651:				 snprintf(key, sizeof(key), "%.1f%%", pct);
branch  0 taken 60804 (fallthrough)
branch  1 taken 0
    60804: 3651-block  0
branch  2 taken 60804 (fallthrough)
branch  3 taken 0
    60804: 3651-block  1
call    4 returned 60804
        -: 3652:			}
       39: 3653:
       39: 3653-block  0
branch  0 taken 39 (fallthrough)
branch  1 taken 0
       39: 3654:			hv_store(res_hv, key, strlen(key), newSVnv(q), 0);
       39: 3654-block  0
call    0 returned 39
        -: 3655:		}
        -: 3656:
      117: 3657:		Safefree(x);
      117: 3657-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 114
        3: 3657-block  1
call    2 returned 0
      114: 3658:		Safefree(probs);
      114: 3658-block  0
branch  0 taken 90 (fallthrough)
branch  1 taken 24
        -: 3659:
        -: 3660:		RETVAL = newRV_noinc((SV*)res_hv);
        -: 3661:	}
        -: 3662:	OUTPUT:
        -: 3663:	  RETVAL
        -: 3664:
       15: 3665:
       15: 3666:double mean(...)
        -: 3667:	PROTOTYPE: @
        -: 3668:	INIT:
       66: 3669:	  double total = 0;
       15: 3669-block  0
       51: 3669-block  1
       66: 3669-block  2
branch  0 taken 51
branch  1 taken 15 (fallthrough)
       51: 3670:	  size_t count = 0;
       54: 3671:	CODE:
       51: 3671-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 48
        3: 3671-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 3671-block  2
        3: 3672:		for (size_t i = 0; i < items; i++) {
        3: 3673:			SV* restrict arg = ST(i);
        3: 3673-block  0
call    0 returned 3
    30000: 3674:			if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
    29997: 3674-block  0
    30000: 3674-block  1
branch  0 taken 29997
branch  1 taken 3 (fallthrough)
    29997: 3675:				AV* restrict av = (AV*)SvRV(arg);
    29997: 3675-block  0
call    0 returned 29997
    29997: 3676:				size_t len = av_len(av) + 1;
branch  0 taken 29997 (fallthrough)
branch  1 taken 0
    29997: 3676-block  0
branch  2 taken 29997 (fallthrough)
branch  3 taken 0
    29997: 3677:				for (size_t j = 0; j < len; j++) {
    29997: 3678:				     SV** restrict tv = av_fetch(av, j, 0);
    29997: 3678-block  0
call    0 returned 29997
    29997: 3679:				     if (tv && SvOK(*tv)) {
    29997: 3680:				         total += SvNV(*tv);
    29997: 3681:				         count++;
        -: 3682:				     } else {
        -: 3683:				         croak("mean: undefined value at array ref index %zu (argument %zu)", j, i);
       48: 3684:				     }
       48: 3684-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 0
       48: 3685:				}
       48: 3686:			} else if (SvOK(arg)) {
       48: 3686-block  0
call    0 returned 48
       48: 3687:				 total += SvNV(arg);
       48: 3688:				 count++;
       48: 3689:			} else {
        -: 3690:				 croak("mean: undefined value at argument index %zu", i);
        -: 3691:			}
       15: 3692:		}
       15: 3692-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 12
        3: 3692-block  1
call    2 returned 0
       12: 3693:		if (count == 0) croak("mean needs >= 1 element");
       12: 3693-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
        -: 3694:		RETVAL = total / count;
        -: 3695:	OUTPUT:
        -: 3696:	  RETVAL
        -: 3697:
        -: 3698:double sum(...)
        -: 3699:	PROTOTYPE: @
       18: 3700:	INIT:
       18: 3701:	  double total = 0;
        -: 3702:	  size_t count = 0;
        -: 3703:	CODE:
       51: 3704:	  for (size_t i = 0; i < items; i++) {
       18: 3704-block  0
       33: 3704-block  1
       51: 3704-block  2
branch  0 taken 33
branch  1 taken 18 (fallthrough)
       33: 3705:		   SV* restrict arg = ST(i);
       42: 3706:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
       33: 3706-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 24
        9: 3706-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 3706-block  2
        9: 3707:		       AV* restrict av = (AV*)SvRV(arg);
        9: 3708:		       size_t len = av_len(av) + 1;
        9: 3708-block  0
call    0 returned 9
    30039: 3709:		       for (size_t j = 0; j < len; j++) {
    30030: 3709-block  0
    30039: 3709-block  1
branch  0 taken 30030
branch  1 taken 9 (fallthrough)
    30030: 3710:		           SV** restrict tv = av_fetch(av, j, 0);
    30030: 3710-block  0
call    0 returned 30030
    30030: 3711:		           if (tv && SvOK(*tv)) {
branch  0 taken 30030 (fallthrough)
branch  1 taken 0
    30030: 3711-block  0
branch  2 taken 30030 (fallthrough)
branch  3 taken 0
    30030: 3712:		               total += SvNV(*tv);
    30030: 3713:		               count++;
    30030: 3713-block  0
call    0 returned 30030
    30030: 3714:		           } else {
    30030: 3715:		               croak("sum: undefined value at array ref index %zu (argument %zu)", j, i);
    30030: 3716:		           }
        -: 3717:		       }
        -: 3718:		   } else if (SvOK(arg)) {
       24: 3719:		       total += SvNV(arg);
       24: 3719-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 3720:		       count++;
       24: 3721:		   } else {
       24: 3721-block  0
call    0 returned 24
       24: 3722:		       croak("sum: undefined value at argument index %zu", i);
       24: 3723:		   }
       24: 3724:	  }
        -: 3725:	  if (count == 0) croak("sum needs >= 1 element");
        -: 3726:	  RETVAL = total;
       18: 3727:	OUTPUT:
       18: 3727-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 15
        3: 3727-block  1
call    2 returned 0
       15: 3728:	  RETVAL
       15: 3728-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 12
        -: 3729:
        -: 3730:double sd(...)
        -: 3731:    PROTOTYPE: @
        -: 3732:    INIT:
        -: 3733:        double mean = 0.0, M2 = 0.0;
        -: 3734:        size_t count = 0;
      141: 3735:    CODE:
      141: 3736:        /* Single Pass Standard Deviation via Welford's Algorithm */
      141: 3737:        for (size_t i = 0; i < items; i++) {
      141: 3738:            SV* restrict arg = ST(i);
      141: 3739:            if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
        -: 3740:                AV* restrict av = (AV*)SvRV(arg);
      141: 3741:                size_t len = av_len(av) + 1;
        -: 3742:                for (size_t j = 0; j < len; j++) {
        -: 3743:                    SV** restrict tv = av_fetch(av, j, 0);
      141: 3744:                    if (tv && SvOK(*tv)) {
branch  0 taken 141 (fallthrough)
branch  1 taken 0
      141: 3744-block  0
branch  2 taken 63 (fallthrough)
branch  3 taken 78
       63: 3744-block  1
branch  4 taken 63 (fallthrough)
branch  5 taken 0
       63: 3745:                        count++;
       63: 3746:                        double val = SvNV(*tv);
       63: 3746-block  0
        -: 3747:                        double delta = val - mean;
        -: 3748:                        mean += delta / count;
        -: 3749:                        M2 += delta * (val - mean);
      141: 3750:                    } else {
      141: 3750-block  0
branch  0 taken 141 (fallthrough)
branch  1 taken 0
      141: 3750-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 129
       12: 3750-block  2
branch  4 taken 12 (fallthrough)
branch  5 taken 0
       12: 3751:                        croak("sd: undefined value at array ref index %zu (argument %zu)", j, i);
       12: 3752:                    }
       12: 3752-block  0
        -: 3753:                }
        -: 3754:            } else if (SvOK(arg)) {
        -: 3755:                count++;
      141: 3756:                double val = SvNV(arg);
      141: 3756-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 141
    #####: 3757:                double delta = val - mean;
    %%%%%: 3757-block  0
call    0 never executed
        -: 3758:                mean += delta / count;
        -: 3759:                M2 += delta * (val - mean);
        -: 3760:            } else {
      369: 3761:                croak("sd: undefined value at argument index %zu", i);
      141: 3761-block  0
      228: 3761-block  1
      369: 3761-block  2
branch  0 taken 228
branch  1 taken 141 (fallthrough)
      228: 3762:            }
      228: 3762-block  0
call    0 returned 228
      228: 3763:        }
        -: 3764:        if (count < 2) croak("sd needs >= 2 elements");
      228: 3765:        RETVAL = sqrt(M2 / (count - 1));
branch  0 taken 75 (fallthrough)
branch  1 taken 153
       75: 3765-block  0
      153: 3766:    OUTPUT:
      153: 3766-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 138
       15: 3766-block  1
      138: 3767:        RETVAL
      138: 3767-block  0
branch  0 taken 105 (fallthrough)
branch  1 taken 33
      105: 3767-block  1
call    2 returned 105
       33: 3768:
       33: 3768-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 21
       12: 3768-block  1
call    2 returned 12
       21: 3769:
       21: 3769-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 12
        9: 3769-block  1
call    2 returned 9
       12: 3770:double var(...)
       12: 3770-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 6
        6: 3770-block  1
call    2 returned 6
        6: 3771:	PROTOTYPE: @
        6: 3771-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 3771-block  1
call    2 returned 6
    #####: 3772:	INIT:
    %%%%%: 3772-block  0
call    0 never executed
        -: 3773:	  double mean = 0.0, M2 = 0.0;
        -: 3774:	  size_t count = 0;
        -: 3775:	CODE:
      141: 3776:	  /* Single Pass Variance via Welford's Algorithm */
      141: 3776-block  0
branch  0 taken 138 (fallthrough)
branch  1 taken 3
      138: 3776-block  1
branch  2 taken 138 (fallthrough)
branch  3 taken 0
      138: 3776-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 138
        3: 3777:	  for (size_t i = 0; i < items; i++) {
        3: 3777-block  0
call    0 returned 0
      138: 3778:		   SV* restrict arg = ST(i);
      138: 3779:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
      138: 3779-block  0
call    0 returned 138
     138*: 3780:		       AV* restrict av = (AV*)SvRV(arg);
branch  0 taken 0 (fallthrough)
branch  1 taken 138
    %%%%%: 3780-block  0
call    2 never executed
      138: 3781:		       size_t len = av_len(av) + 1;
      138: 3782:		       for (size_t j = 0; j < len; j++) {
      138: 3782-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 114
       24: 3782-block  1
branch  2 taken 24 (fallthrough)
branch  3 taken 0
       24: 3782-block  2
branch  4 taken 24 (fallthrough)
branch  5 taken 0
       24: 3783:		           SV** restrict tv = av_fetch(av, j, 0);
       24: 3783-block  0
        -: 3784:		           if (tv && SvOK(*tv)) {
      138: 3785:		               count++;
      138: 3785-block  0
branch  0 taken 138 (fallthrough)
branch  1 taken 0
      138: 3785-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 135
        3: 3786:		               double val = SvNV(*tv);
        3: 3786-block  0
call    0 returned 0
        -: 3787:		               double delta = val - mean;
      135: 3788:		               mean += delta / count;
      135: 3789:		               M2 += delta * (val - mean);
      135: 3789-block  0
call    0 returned 135
     1233: 3790:		           } else {
     1233: 3790-block  0
branch  0 taken 1098
branch  1 taken 135 (fallthrough)
     1098: 3791:		               croak("var: undefined value at array ref index %zu (argument %zu)", j, i);
     1098: 3791-block  0
call    0 returned 1098
    1098*: 3792:		           }
branch  0 taken 1098 (fallthrough)
branch  1 taken 0
     1098: 3792-block  0
branch  2 taken 1098 (fallthrough)
branch  3 taken 0
     1098: 3792-block  1
call    4 returned 1098
    %%%%%: 3792-block  2
     1098: 3793:		       }
     1098: 3794:		   } else if (SvOK(arg)) {
     1098: 3795:		       count++;
     1098: 3795-block  0
        -: 3796:		       double val = SvNV(arg);
      135: 3797:		       double delta = val - mean;
      135: 3798:		       mean += delta / count;
      135: 3798-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 132
        3: 3798-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 3798-block  2
call    4 returned 0
        -: 3799:		       M2 += delta * (val - mean);
      153: 3800:		   } else {
      132: 3800-block  0
branch  0 taken 120 (fallthrough)
branch  1 taken 12
      120: 3800-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 105
       21: 3800-block  2
       27: 3801:		       croak("var: undefined value at argument index %zu", i);
       27: 3801-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 24
        3: 3801-block  1
call    2 returned 0
       24: 3802:		   }
       24: 3802-block  0
call    0 returned 24
       24: 3803:	  }
branch  0 taken 9 (fallthrough)
branch  1 taken 15
        9: 3803-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 6
        3: 3803-block  1
call    4 returned 0
       21: 3804:	  if (count < 2) croak("var needs >= 2 elements");
      312: 3805:	  RETVAL = M2 / (count - 1);
       21: 3805-block  0
      312: 3805-block  1
branch  0 taken 291
branch  1 taken 21 (fallthrough)
      291: 3806:	OUTPUT:
      291: 3806-block  0
call    0 returned 291
     291*: 3807:	  RETVAL
branch  0 taken 291 (fallthrough)
branch  1 taken 0
      291: 3807-block  0
branch  2 taken 291 (fallthrough)
branch  3 taken 0
      291: 3807-block  1
call    4 returned 291
    %%%%%: 3807-block  2
      291: 3808:
      291: 3809:SV* t_test(...)
      291: 3810:	CODE:
      291: 3810-block  0
        -: 3811:	{
       21: 3812:		SV*restrict x_sv = NULL;
       21: 3813:		SV*restrict y_sv = NULL;
       21: 3813-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 15
        6: 3814:		double mu = 0.0, conf_level = 0.95;
       42: 3815:		bool paired = FALSE, var_equal = FALSE;
        6: 3815-block  0
       42: 3815-block  1
branch  0 taken 36
branch  1 taken 6 (fallthrough)
       36: 3816:		const char*restrict alternative = "two.sided";
       36: 3816-block  0
call    0 returned 36
       36: 3817:
call    0 returned 36
      36*: 3818:		int arg_idx = 0;
branch  0 taken 36 (fallthrough)
branch  1 taken 0
       36: 3818-block  0
branch  2 taken 36 (fallthrough)
branch  3 taken 0
       36: 3818-block  1
call    4 returned 36
    %%%%%: 3818-block  2
      36*: 3819:
       36: 3819-block  0
branch  0 taken 36 (fallthrough)
branch  1 taken 0
       36: 3819-block  1
branch  2 taken 36 (fallthrough)
branch  3 taken 0
       36: 3819-block  2
call    4 returned 36
    %%%%%: 3819-block  3
       36: 3820:		// 1. Shift first positional argument as 'x' if it's an array reference
       36: 3821:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
       36: 3822:		  x_sv = ST(arg_idx);
       36: 3823:		  arg_idx++;
       36: 3823-block  0
        -: 3824:		}
        6: 3825:
       6*: 3826:		// 2. Shift second positional argument as 'y' if it's an array reference
        6: 3826-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3826-block  1
call    2 never executed
        6: 3827:		if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        6: 3828:		  y_sv = ST(arg_idx);
        6: 3829:		  arg_idx++;
        6: 3830:		}
        6: 3831:
        6: 3831-block  0
call    0 returned 6
call    1 returned 6
       15: 3832:		// Ensure the remaining arguments form complete key-value pairs
       15: 3832-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 9
       6*: 3833:		if ((items - arg_idx) % 2 != 0) {
        6: 3833-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3833-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 3833-block  2
call    4 never executed
        6: 3834:		  croak("Usage: t_test(\\@x, [\\@y], key => value, ...)");
        6: 3835:		}
        6: 3836:
        6: 3837:		// --- Parse named arguments from the remaining flat stack ---
        6: 3838:		for (; arg_idx < items; arg_idx += 2) {
        6: 3839:			const char*restrict key = SvPV_nolen(ST(arg_idx));
        6: 3839-block  0
call    0 returned 6
call    1 returned 6
        6: 3840:			SV*restrict val = ST(arg_idx + 1);
call    0 returned 6
call    1 returned 6
        -: 3841:
       9*: 3842:			if      (strEQ(key, "x"))           x_sv        = val;
        9: 3842-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 3842-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 3842-block  2
call    4 never executed
        9: 3843:			else if (strEQ(key, "y"))           y_sv        = val;
        9: 3844:			else if (strEQ(key, "mu"))          mu          = SvNV(val);
        9: 3845:			else if (strEQ(key, "paired"))      paired      = SvTRUE(val);
        9: 3846:			else if (strEQ(key, "var_equal"))   var_equal   = SvTRUE(val);
        9: 3847:			else if (strEQ(key, "conf_level"))  conf_level  = SvNV(val);
        9: 3848:			else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        9: 3849:			else croak("t_test: unknown argument '%s'", key);
        9: 3850:		}
        9: 3850-block  0
call    0 returned 9
call    1 returned 9
        9: 3851:
call    0 returned 9
call    1 returned 9
        -: 3852:		// --- Validate required / types ---
        -: 3853:		if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
      105: 3854:			croak("t_test: 'x' is a required argument and must be an ARRAY reference");
      105: 3855:		AV*restrict x_av = (AV*)SvRV(x_sv);
      105: 3856:		size_t nx = av_len(x_av) + 1;
      105: 3857:		if (nx < 2) croak("t_test: 'x' needs at least 2 elements");
      105: 3858:		AV*restrict y_av = NULL;
      105: 3858-block  0
call    0 returned 105
call    1 returned 105
        -: 3859:		if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV)
      126: 3860:			y_av = (AV*)SvRV(y_sv);
      126: 3860-block  0
call    0 returned 126
      126: 3861:			
      126: 3862:		if (conf_level <= 0.0 || conf_level >= 1.0)
branch  0 taken 3 (fallthrough)
branch  1 taken 123
        3: 3863:			croak("t_test: 'conf_level' must be between 0 and 1");
        3: 3863-block  0
call    0 returned 3
        3: 3864:		// --- Computation via Welford's Algorithm --- */
        3: 3865:		double mean_x = 0.0, M2_x = 0.0, var_x, t_stat, df, p_val, std_err, cint_est;
      123: 3866:		HV*restrict results = newHV();
      123: 3866-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 120
        3: 3867:		for (size_t i = 0; i < nx; i++) {
        3: 3867-block  0
call    0 returned 3
        3: 3868:			SV**restrict tv = av_fetch(x_av, i, 0);
        3: 3869:			double val = (tv && SvOK(*tv)) ? SvNV(*tv) : 0;
        -: 3870:			double delta = val - mean_x;
      120: 3871:			mean_x += delta / (i + 1);
      120: 3871-block  0
call    0 returned 120
      120: 3872:			M2_x += delta * (val - mean_x);
      120: 3873:		}
        -: 3874:		var_x = M2_x / (nx - 1);
      126: 3875:		if (var_x == 0.0 && !y_av) croak("t_test: data are essentially constant");
      126: 3875-block  0
call    0 returned 126
      126: 3876:
call    0 returned 126
call    1 returned 126
      126: 3877:		if (paired || y_av) {
call    0 returned 126
call    1 returned 126
      126: 3878:			if (!y_av) croak("t_test: 'y' must be provided for paired or two-sample tests");
call    0 returned 126
call    1 returned 126
      126: 3879:			size_t ny = av_len(y_av) + 1;
call    0 returned 126
call    1 returned 126
      126: 3880:			if (paired && ny != nx) croak("t_test: Paired arrays must be same length");
call    0 returned 126
call    1 returned 126
      126: 3881:			double mean_y = 0.0, M2_y = 0.0, var_y;
call    0 returned 126
call    1 returned 126
      126: 3882:			for (size_t i = 0; i < ny; i++) {
call    0 returned 126
        -: 3883:				 SV**restrict tv = av_fetch(y_av, i, 0);
        -: 3884:				 double val = (tv && SvOK(*tv)) ? SvNV(*tv) : 0;
        -: 3885:				 double delta = val - mean_y;
        -: 3886:				 mean_y += delta / (i + 1);
        -: 3887:				 M2_y += delta * (val - mean_y);
        -: 3888:			}
       45: 3889:			var_y = M2_y / (ny - 1);
       45: 3889-block  0
branch  0 taken 42 (fallthrough)
branch  1 taken 3
       42: 3889-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 42
        3: 3890:			if (paired) {
        3: 3890-block  0
call    0 returned 0
        -: 3891:				 double mean_d = 0.0, M2_d = 0.0;
       42: 3892:				 for (size_t i = 0; i < nx; i++) {
       42: 3893:					  SV**restrict dx_ptr = av_fetch(x_av, i, 0);
       42: 3893-block  0
call    0 returned 42
        -: 3894:					  SV**restrict dy_ptr = av_fetch(y_av, i, 0);
       42: 3895:				     double dx = (dx_ptr && SvOK(*dx_ptr)) ? SvNV(*dx_ptr) : 0.0;
branch  0 taken 3 (fallthrough)
branch  1 taken 39
        3: 3896:				     double dy = (dy_ptr && SvOK(*dy_ptr)) ? SvNV(*dy_ptr) : 0.0;
        3: 3896-block  0
        -: 3897:				     double val = dx - dy;
        -: 3898:				     double delta = val - mean_d;
        -: 3899:				     mean_d += delta / (i + 1);
       39: 3900:				     M2_d += delta * (val - mean_d);
      471: 3901:				 }
       39: 3901-block  0
      432: 3901-block  1
      471: 3901-block  2
branch  0 taken 432
branch  1 taken 39 (fallthrough)
        -: 3902:				 double var_d = M2_d / (nx - 1);
       39: 3903:				 if (var_d == 0.0) croak("t_test: data are essentially constant");
       39: 3903-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 27
       12: 3903-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 6
        6: 3903-block  2
       39: 3904:				 cint_est = mean_d;
       39: 3904-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 33
        6: 3904-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 3904-block  2
      39*: 3905:				 std_err  = sqrt(var_d / nx);
       39: 3905-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    %%%%%: 3905-block  1
        -: 3906:				 t_stat   = (cint_est - mu) / std_err;
        -: 3907:				 df       = nx - 1;
        -: 3908:				 hv_store(results, "estimate", 8, newSVnv(mean_d), 0);
      39*: 3909:			} else if (var_equal) {
       39: 3909-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    %%%%%: 3909-block  1
call    2 never executed
       39: 3909-block  2
call    3 returned 39
      39*: 3910:				 if (var_x == 0.0 && var_y == 0.0) croak("t_test: data are essentially constant");
branch  0 taken 0 (fallthrough)
branch  1 taken 39
    %%%%%: 3910-block  0
call    2 never executed
       39: 3910-block  1
call    3 returned 39
        -: 3911:				 double pooled_var = ((nx - 1) * var_x + (ny - 1) * var_y) / (nx + ny - 2);
     1107: 3912:				 cint_est = mean_x - mean_y;
     1107: 3912-block  0
branch  0 taken 1068
branch  1 taken 39 (fallthrough)
     1068: 3913:				 std_err  = sqrt(pooled_var * (1.0 / nx + 1.0 / ny));
     1068: 3913-block  0
call    0 returned 1068
    1068*: 3914:				 t_stat   = (cint_est - mu) / std_err;
branch  0 taken 1068 (fallthrough)
branch  1 taken 0
     1068: 3914-block  0
branch  2 taken 1068 (fallthrough)
branch  3 taken 0
     1068: 3914-block  1
call    4 returned 1068
    %%%%%: 3914-block  2
     1068: 3915:				 df       = nx + ny - 2;
     1068: 3915-block  0
        -: 3916:				 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
        -: 3917:				 hv_store(results, "estimate_y", 10, newSVnv(mean_y), 0);
       39: 3918:			} else {
       39: 3918-block  0
call    0 returned 39
        -: 3919:				 if (var_x == 0.0 && var_y == 0.0) croak("t_test: data are essentially constant");
       39: 3920:				 cint_est         = mean_x - mean_y;
branch  0 taken 6 (fallthrough)
branch  1 taken 33
      159: 3921:				 double stderr_x2 = var_x / nx;
        6: 3921-block  0
      159: 3921-block  1
branch  0 taken 153
branch  1 taken 6
      153: 3922:				 double stderr_y2 = var_y / ny;
      153: 3923:				 std_err          = sqrt(stderr_x2 + stderr_y2);
      153: 3923-block  0
branch  0 taken 57 (fallthrough)
branch  1 taken 96
       57: 3923-block  1
       96: 3923-block  2
      153: 3923-block  3
        -: 3924:				 t_stat           = (cint_est - mu) / std_err;
       33: 3925:				 df = pow(stderr_x2 + stderr_y2, 2) /
       33: 3925-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 27
        6: 3926:				      (pow(stderr_x2, 2) / (nx - 1) + pow(stderr_y2, 2) / (ny - 1));
      159: 3927:				 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
        6: 3927-block  0
      159: 3927-block  1
branch  0 taken 153
branch  1 taken 6
      153: 3928:				 hv_store(results, "estimate_y", 10, newSVnv(mean_y), 0);
      153: 3929:			}
      153: 3929-block  0
branch  0 taken 96 (fallthrough)
branch  1 taken 57
       96: 3929-block  1
      153: 3930:		} else {
      153: 3930-block  0
branch  0 taken 66 (fallthrough)
branch  1 taken 87
       66: 3930-block  1
       87: 3930-block  2
      153: 3930-block  3
        -: 3931:			cint_est = mean_x;
       27: 3932:			std_err  = sqrt(var_x / nx);
       27: 3932-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 21
        6: 3933:			t_stat   = (cint_est - mu) / std_err;
      159: 3934:			df       = nx - 1;
        6: 3934-block  0
      159: 3934-block  1
branch  0 taken 153
branch  1 taken 6
      153: 3935:			hv_store(results, "estimate", 8, newSVnv(mean_x), 0);
      153: 3936:		}
      153: 3936-block  0
branch  0 taken 66 (fallthrough)
branch  1 taken 87
       66: 3936-block  1
     153*: 3937:		p_val = get_t_pvalue(t_stat, df, alternative);
      153: 3937-block  0
branch  0 taken 153 (fallthrough)
branch  1 taken 0
      153: 3937-block  1
    %%%%%: 3937-block  2
      153: 3937-block  3
        -: 3938:		double alpha = 1.0 - conf_level, t_crit, ci_lower, ci_upper;
       21: 3939:		if (strcmp(alternative, "less") == 0) {
       21: 3939-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 15
        6: 3940:			t_crit   = qt_tail(df, alpha);
      159: 3941:			ci_lower = -INFINITY;
        6: 3941-block  0
      159: 3941-block  1
branch  0 taken 153
branch  1 taken 6
      153: 3942:			ci_upper = cint_est + t_crit * std_err;
      153: 3943:		} else if (strcmp(alternative, "greater") == 0) {
      153: 3943-block  0
branch  0 taken 123 (fallthrough)
branch  1 taken 30
      123: 3943-block  1
     153*: 3944:			t_crit   = qt_tail(df, alpha);
      153: 3944-block  0
branch  0 taken 153 (fallthrough)
branch  1 taken 0
      153: 3944-block  1
    %%%%%: 3944-block  2
      153: 3944-block  3
        -: 3945:			ci_lower = cint_est - t_crit * std_err;
       15: 3946:			ci_upper = INFINITY;
       15: 3946-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 9
        6: 3947:		} else {
      159: 3948:			t_crit   = qt_tail(df, alpha / 2.0);
        6: 3948-block  0
      153: 3948-block  1
      159: 3948-block  2
branch  0 taken 153
branch  1 taken 6 (fallthrough)
        6: 3949:			ci_lower = cint_est - t_crit * std_err;
      159: 3950:			ci_upper = cint_est + t_crit * std_err;
        6: 3950-block  0
      159: 3950-block  1
branch  0 taken 153
branch  1 taken 6
      153: 3951:		}
      153: 3952:		AV*restrict conf_int = newAV();
      153: 3952-block  0
branch  0 taken 66 (fallthrough)
branch  1 taken 87
       66: 3952-block  1
      153: 3953:		av_push(conf_int, newSVnv(ci_lower));
      153: 3953-block  0
branch  0 taken 78 (fallthrough)
branch  1 taken 75
       78: 3953-block  1
       75: 3953-block  2
      153: 3953-block  3
        -: 3954:		av_push(conf_int, newSVnv(ci_upper));
        9: 3955:		hv_store(results, "statistic", 9, newSVnv(t_stat), 0);
        9: 3955-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        -: 3956:		hv_store(results, "df",        2, newSVnv(df),     0);
       6*: 3957:		hv_store(results, "p_value",   7, newSVnv(p_val),  0);
        6: 3957-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3957-block  1
call    2 never executed
        6: 3957-block  2
call    3 returned 6
       6*: 3958:		hv_store(results, "conf_int",  8, newRV_noinc((SV*)conf_int), 0);
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 3958-block  0
call    2 never executed
        6: 3958-block  1
call    3 returned 6
        -: 3959:		RETVAL = newRV_noinc((SV*)results);
        6: 3960:	}
      153: 3961:	OUTPUT:
      147: 3961-block  0
      153: 3961-block  1
branch  0 taken 147
branch  1 taken 6 (fallthrough)
      147: 3962:	  RETVAL
      147: 3963:
      147: 3963-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 147
    #####: 3964:void p_adjust(SV* p_sv, const char* method = "holm")
    %%%%%: 3964-block  0
        -: 3965:	INIT:
        -: 3966:		if (!SvROK(p_sv) || SvTYPE(SvRV(p_sv)) != SVt_PVAV) {
        -: 3967:			croak("p_adjust: first argument must be an ARRAY reference of p-values");
      159: 3968:		}
        6: 3968-block  0
      159: 3968-block  1
branch  0 taken 153
branch  1 taken 6 (fallthrough)
      153: 3969:		AV *restrict p_av = (AV*)SvRV(p_sv);
      153: 3970:		size_t n = av_len(p_av) + 1;
      153: 3970-block  0
        -: 3971:		// Handle empty input
      150: 3972:		if (n == 0) {
        6: 3972-block  0
      144: 3972-block  1
      150: 3972-block  2
branch  0 taken 144
branch  1 taken 6 (fallthrough)
      144: 3973:			XSRETURN_EMPTY;
      144: 3974:		}
        -: 3975:		// Normalize method string
      144: 3976:		char meth[64];
     3528: 3977:		strncpy(meth, method, 63); meth[63] = '\0';
      144: 3977-block  0
     3384: 3977-block  1
     3528: 3977-block  2
branch  0 taken 3384
branch  1 taken 144 (fallthrough)
     3384: 3978:		for(unsigned short int i = 0; meth[i]; i++) meth[i] = tolower(meth[i]);
     3384: 3979:		// Resolve aliases
     3384: 3979-block  0
branch  0 taken 798 (fallthrough)
branch  1 taken 2586
      798: 3980:		if (strstr(meth, "benjamini") && strstr(meth, "hochberg")) strcpy(meth, "bh");
      798: 3980-block  0
        -: 3981:		if (strstr(meth, "benjamini") && strstr(meth, "yekutieli")) strcpy(meth, "by");
        -: 3982:		if (strcmp(meth, "fdr") == 0) strcpy(meth, "bh");
        -: 3983:		// Allocate C memory
     3816: 3984:		PVal *restrict arr;
      144: 3984-block  0
     3816: 3984-block  1
branch  0 taken 3672
branch  1 taken 144 (fallthrough)
     3672: 3985:		double *restrict adj;
     3672: 3986:		Newx(arr, n, PVal);
     3672: 3986-block  0
branch  0 taken 2541 (fallthrough)
branch  1 taken 1131
     2541: 3986-block  1
     1131: 3986-block  2
     3672: 3986-block  3
        -: 3987:		Newx(adj, n, double);
        -: 3988:
     3672: 3989:		for (size_t i = 0; i < n; i++) {
      144: 3989-block  0
     3672: 3989-block  1
branch  0 taken 3528
branch  1 taken 144 (fallthrough)
     3528: 3990:			SV**restrict tv = av_fetch(p_av, i, 0);
     3528: 3990-block  0
        -: 3991:			arr[i].p = (tv && SvOK(*tv)) ? SvNV(*tv) : 1.0;
        -: 3992:			arr[i].orig_idx = i;
     7344: 3993:		}
      144: 3993-block  0
     7200: 3993-block  1
     7344: 3993-block  2
branch  0 taken 7200
branch  1 taken 144 (fallthrough)
     7200: 3994:		// Sort ascending (Stable sort using original index)
     7200: 3994-block  0
branch  0 taken 4203 (fallthrough)
branch  1 taken 2997
     4203: 3995:		qsort(arr, n, sizeof(PVal), cmp_pval);
     4203: 3995-block  0
        -: 3996:	PPCODE:
        -: 3997:		if (strcmp(meth, "bonferroni") == 0) {
        -: 3998:			for (size_t i = 0; i < n; i++) {
        -: 3999:				double v = arr[i].p * n;
      159: 4000:				adj[arr[i].orig_idx] = (v < 1.0) ? v : 1.0;
        6: 4000-block  0
      159: 4000-block  1
branch  0 taken 153
branch  1 taken 6 (fallthrough)
      153: 4001:			}
      153: 4001-block  0
branch  0 taken 150 (fallthrough)
branch  1 taken 3
      150: 4001-block  1
        3: 4001-block  2
     153*: 4002:		} else if (strcmp(meth, "holm") == 0) {
      153: 4002-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 153
    %%%%%: 4002-block  1
      153: 4003:			double cummax = 0.0;
      153: 4003-block  0
        -: 4004:			for (size_t i = 0; i < n; i++) {
        6: 4005:				 double v = arr[i].p * (n - i);
        6: 4005-block  0
call    0 returned 6
call    1 returned 6
        3: 4006:				 if (v > cummax) cummax = v;
        3: 4006-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 4007:				 adj[arr[i].orig_idx] = (cummax < 1.0) ? cummax : 1.0;
    %%%%%: 4007-block  0
    %%%%%: 4007-block  1
branch  0 never executed
branch  1 never executed
    #####: 4008:			}
    %%%%%: 4008-block  0
        -: 4009:		} else if (strcmp(meth, "hochberg") == 0) {
        -: 4010:			double cummin = 1.0;
        3: 4011:			for (ssize_t i = n - 1; i >= 0; i--) {
        3: 4011-block  0
call    0 returned 3
call    1 returned 3
        3: 4012:				 double v = arr[i].p * (n - i);
call    0 returned 0
        -: 4013:				 if (v < cummin) cummin = v;
        -: 4014:				 adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
      36*: 4015:			}
       36: 4015-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 36
    %%%%%: 4015-block  1
call    2 never executed
      954: 4016:		} else if (strcmp(meth, "bh") == 0) {
       36: 4016-block  0
      954: 4016-block  1
branch  0 taken 918
branch  1 taken 36 (fallthrough)
      918: 4017:			double cummin = 1.0;
      918: 4017-block  0
call    0 returned 918
call    1 returned 918
        -: 4018:			for (ssize_t i = n - 1; i >= 0; i--) {
       36: 4019:				double v = arr[i].p * n / (i + 1.0);
       36: 4019-block  0
call    0 returned 36
       36: 4020:				if (v < cummin) cummin = v;
call    0 returned 36
        -: 4021:				adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
        -: 4022:			}
        -: 4023:		} else if (strcmp(meth, "by") == 0) {
        -: 4024:			double q = 0.0;
       18: 4025:			for (size_t i = 1; i <= n; i++) q += 1.0 / i;
        -: 4026:			double cummin = 1.0;
       18: 4027:			for (ssize_t i = n - 1; i >= 0; i--) {
        -: 4028:				double v = arr[i].p * n / (i + 1.0) * q;
        -: 4029:				if (v < cummin) cummin = v;
       42: 4030:				adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
       18: 4030-block  0
       24: 4030-block  1
       42: 4030-block  2
branch  0 taken 24
branch  1 taken 18 (fallthrough)
       24: 4031:			}
       36: 4032:		} else if (strcmp(meth, "hommel") == 0) {
       24: 4032-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 12
       12: 4032-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 4032-block  2
       12: 4033:			double *restrict pa, *restrict q_arr;
       12: 4034:			Newx(pa, n, double);
       12: 4034-block  0
call    0 returned 12
      129: 4035:			Newx(q_arr, n, double);
      117: 4035-block  0
      129: 4035-block  1
branch  0 taken 117
branch  1 taken 12 (fallthrough)
      117: 4036:			// Initial: min(n * p[i] / (i + 1))
      117: 4036-block  0
call    0 returned 117
      117: 4037:			double min_val = n * arr[0].p;
branch  0 taken 117 (fallthrough)
branch  1 taken 0
      117: 4037-block  0
branch  2 taken 117 (fallthrough)
branch  3 taken 0
      117: 4037-block  1
        -: 4038:			for (size_t i = 1; i < n; i++) {
       12: 4039:				double temp = (n * arr[i].p) / (i + 1.0);
       12: 4039-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 4040:				if (temp < min_val) {
       12: 4040-block  0
        -: 4041:				   min_val = temp;
        -: 4042:				}
       18: 4043:			}
       18: 4043-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 15
        3: 4043-block  1
call    2 returned 0
        -: 4044:			// pa <- q <- rep(min, n)
      15*: 4045:			for (size_t i = 0; i < n; i++) {
       15: 4045-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 4045-block  1
call    2 never executed
       15: 4045-block  2
call    3 returned 15
        -: 4046:				 pa[i] = min_val;
       39: 4047:				 q_arr[i] = min_val;
       24: 4047-block  0
       39: 4047-block  1
branch  0 taken 24
branch  1 taken 15 (fallthrough)
       24: 4048:			}
       36: 4049:			for (size_t j = n - 1; j >= 2; j--) {
       24: 4049-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 12
       12: 4049-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 4049-block  2
       12: 4050:				 ssize_t n_mj = n - j;       // Max index for 'ij'. Length is n_mj + 1
       12: 4051:				 ssize_t i2_len = j - 1;     // Length of 'i2
       12: 4051-block  0
call    0 returned 12
      129: 4052:				 // Calculate q1 = min(j * p[i2] / (2:j))
      117: 4052-block  0
      129: 4052-block  1
branch  0 taken 117
branch  1 taken 12 (fallthrough)
      117: 4053:				 double q1 = (j * arr[n_mj + 1].p) / 2.0;
      117: 4053-block  0
call    0 returned 117
      117: 4054:				 for (size_t k = 1; k < i2_len; k++) {
branch  0 taken 117 (fallthrough)
branch  1 taken 0
      117: 4054-block  0
branch  2 taken 117 (fallthrough)
branch  3 taken 0
      117: 4055:				     double temp_q1 = (j * arr[n_mj + 1 + k].p) / (2.0 + k);
      117: 4055-block  0
call    0 returned 117
        -: 4056:				     if (temp_q1 < q1) {
        -: 4057:				         q1 = temp_q1;
       12: 4058:				     }
       12: 4058-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 4059:				 }
       12: 4059-block  0
call    0 returned 12
        -: 4060:				 // q[ij] <- pmin(j * p[ij], q1)
        -: 4061:				 for (size_t i = 0; i <= n_mj; i++) {
        -: 4062:				     double v = j * arr[i].p;
       15: 4063:				     q_arr[i] = (v < q1) ? v : q1;
       15: 4063-block  0
call    0 returned 15
       15: 4064:				 }
branch  0 taken 12 (fallthrough)
branch  1 taken 3
       12: 4065:				 // q[i2] <- q[n - j]
       12: 4065-block  0
        -: 4066:				 for (size_t i = 0; i < i2_len; i++) {
        3: 4067:				     q_arr[n_mj + 1 + i] = q_arr[n_mj];
        3: 4067-block  0
        -: 4068:				}
       15: 4069:				 // pa <- pmax(pa, q)
       15: 4069-block  0
call    0 returned 15
       15: 4070:				for (size_t i = 0; i < n; i++) {
branch  0 taken 6 (fallthrough)
branch  1 taken 9
        -: 4071:				    if (pa[i] < q_arr[i]) {
        -: 4072:				       pa[i] = q_arr[i];
        -: 4073:				    }
        -: 4074:				}
        -: 4075:			}
        -: 4076:			// pmin(1, pmax(pa, p))[ro] — map sorted results back to original indices
       18: 4077:			for (size_t i = 0; i < n; i++) {
       18: 4077-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 9
        9: 4078:				double v = (pa[i] > arr[i].p) ? pa[i] : arr[i].p;
        9: 4078-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        6: 4079:				if (v > 1.0) v = 1.0;
        6: 4079-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 4080:				adj[arr[i].orig_idx] = v;
        3: 4080-block  0
call    0 returned 0
        -: 4081:			}
        -: 4082:			Safefree(pa);  Safefree(q_arr);
        -: 4083:		} else if (strcmp(meth, "none") == 0) {
       15: 4084:			for (size_t i = 0; i < n; i++) {
       15: 4084-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4084-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 15
    #####: 4085:				adj[arr[i].orig_idx] = arr[i].p;
    %%%%%: 4085-block  0
call    0 never executed
        -: 4086:			}
       15: 4087:		} else {
       15: 4088:			Safefree(arr); Safefree(adj);
       15: 4088-block  0
call    0 returned 15
      15*: 4089:			croak("Unknown p-value adjustment method: %s", method);
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 4089-block  0
call    2 never executed
        -: 4090:		}
        -: 4091:		// Push values onto the Perl stack as a flat list
       15: 4092:		EXTEND(SP, n);
        -: 4093:		for (size_t i = 0; i < n; i++) {
       15: 4094:			PUSHs(sv_2mortal(newSVnv(adj[i])));
       15: 4094-block  0
call    0 returned 15
       15: 4095:		}
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4095-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 12
        3: 4095-block  1
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 4096:		Safefree(arr); arr = NULL;
        3: 4096-block  0
        -: 4097:		Safefree(adj); adj = NULL;
        -: 4098:
        -: 4099:double median(...)
      30*: 4100:	PROTOTYPE: @
       15: 4100-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4100-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 0
       15: 4100-block  2
    %%%%%: 4100-block  3
       15: 4101:	INIT:
       15: 4101-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
        -: 4102:	  size_t total_count = 0, k = 0;
      15*: 4103:	  double* restrict nums;
       15: 4103-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4103-block  1
    %%%%%: 4103-block  2
      15*: 4104:	  double median_val = 0.0;
       15: 4104-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4104-block  1
call    2 returned 15
    %%%%%: 4104-block  2
        -: 4105:	CODE:
       15: 4106:	  /* Pass 1: Count valid elements — die immediately on any undef */
       15: 4107:	  for (size_t i = 0; i < items; i++) {
       15: 4107-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4107-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 0
       15: 4108:		   SV* restrict arg = ST(i);
       15: 4108-block  0
call    0 returned 15
       15: 4109:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4109-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 12
        3: 4109-block  1
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 4110:		       AV* restrict av = (AV*)SvRV(arg);
        3: 4110-block  0
        -: 4111:		       size_t len = av_len(av) + 1;
        -: 4112:		       for (size_t j = 0; j < len; j++) {
        -: 4113:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 4114:		           if (tv && SvOK(*tv)) {
       15: 4115:		               total_count++;
       15: 4115-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 3
       12: 4115-block  1
branch  2 taken 12 (fallthrough)
branch  3 taken 0
       12: 4116:		           } else {
       12: 4116-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
        -: 4117:		               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
    #####: 4118:		           }
    %%%%%: 4118-block  0
call    0 never executed
        -: 4119:		       }
       12: 4120:		   } else if (SvOK(arg)) {
       12: 4120-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        3: 4121:		       total_count++;
        3: 4121-block  0
call    0 returned 0
        -: 4122:		   } else {
        -: 4123:		       croak("median: undefined value at argument index %zu", i);
        9: 4124:		   }
        9: 4124-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    #####: 4125:	  }
    %%%%%: 4125-block  0
call    0 never executed
        -: 4126:	  if (total_count == 0) croak("median needs >= 1 element");
        -: 4127:
       9*: 4128:	  /* Allocate C array now that we know the exact size */
        9: 4128-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 4128-block  1
call    2 never executed
        9: 4128-block  2
call    3 returned 9
       9*: 4129:	  Newx(nums, total_count, double);
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 4129-block  0
call    2 never executed
        9: 4129-block  1
call    3 returned 9
        -: 4130:
       90: 4131:	  /* Pass 2: Populate the C array — Safefree before any croak */
       90: 4131-block  0
branch  0 taken 81
branch  1 taken 9 (fallthrough)
       81: 4132:	  for (size_t i = 0; i < items; i++) {
       81: 4132-block  0
call    0 returned 81
      81*: 4133:		   SV* restrict arg = ST(i);
branch  0 taken 81 (fallthrough)
branch  1 taken 0
       81: 4133-block  0
branch  2 taken 81 (fallthrough)
branch  3 taken 0
       81: 4133-block  1
call    4 returned 81
branch  5 taken 81 (fallthrough)
branch  6 taken 0
       81: 4133-block  2
call    7 returned 81
    %%%%%: 4133-block  3
       81: 4133-block  4
        -: 4134:		   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
       90: 4135:		       AV* restrict av = (AV*)SvRV(arg);
        9: 4135-block  0
       90: 4135-block  1
branch  0 taken 81
branch  1 taken 9 (fallthrough)
       81: 4136:		       size_t len = av_len(av) + 1;
       81: 4136-block  0
call    0 returned 81
      81*: 4137:		       for (size_t j = 0; j < len; j++) {
branch  0 taken 81 (fallthrough)
branch  1 taken 0
       81: 4137-block  0
branch  2 taken 81 (fallthrough)
branch  3 taken 0
       81: 4137-block  1
call    4 returned 81
branch  5 taken 81 (fallthrough)
branch  6 taken 0
       81: 4137-block  2
call    7 returned 81
    %%%%%: 4137-block  3
       81: 4137-block  4
        -: 4138:		           SV** restrict tv = av_fetch(av, j, 0);
        -: 4139:		           if (tv && SvOK(*tv)) {
        9: 4140:		               nums[k++] = SvNV(*tv);
        9: 4140-block  0
call    0 returned 9
        9: 4141:		           } else {
call    0 returned 9
call    1 returned 9
        9: 4142:		               Safefree(nums);
call    0 returned 9
        -: 4143:		               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
        -: 4144:		           }
        -: 4145:		       }
        3: 4146:		   } else if (SvOK(arg)) {
        3: 4146-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 4147:		       nums[k++] = SvNV(arg);
    %%%%%: 4147-block  0
call    0 never executed
        -: 4148:		   } else {
        -: 4149:		       Safefree(nums);
        3: 4150:		       croak("median: undefined value at argument index %zu", i);
        3: 4150-block  0
call    0 returned 3
        3: 4151:		   }
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4151-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4151-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    #####: 4152:	  }
    %%%%%: 4152-block  0
call    0 never executed
        -: 4153:
        3: 4154:	  /* Sort and calculate median */
        3: 4154-block  0
call    0 returned 3
       3*: 4155:	  qsort(nums, total_count, sizeof(double), compare_doubles);
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4155-block  0
call    2 never executed
        -: 4156:	  if (total_count % 2 == 0) {
        3: 4157:		   median_val = (nums[total_count / 2 - 1] + nums[total_count / 2]) / 2.0;
        -: 4158:	  } else {
        -: 4159:		   median_val = nums[total_count / 2];
       12: 4160:	  }
        3: 4160-block  0
        9: 4160-block  1
       12: 4160-block  2
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 4161:	  Safefree(nums);
        9: 4161-block  0
call    0 returned 9
        9: 4162:	  nums = NULL;
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 4162-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 4162-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 9
    #####: 4163:	  RETVAL = median_val;
    %%%%%: 4163-block  0
call    0 never executed
        -: 4164:	OUTPUT:
        -: 4165:	  RETVAL
        3: 4166:
        3: 4166-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4166-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
       3*: 4167:SV* cor(SV* x_sv, SV* y_sv = &PL_sv_undef, const char* method = "pearson")
        3: 4167-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4167-block  1
call    2 never executed
       12: 4168:	INIT:
        3: 4168-block  0
        9: 4168-block  1
       12: 4168-block  2
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 4169:	  // --- validate method -------------------------------------------
        9: 4169-block  0
call    0 returned 9
        9: 4170:	  if (strcmp(method, "pearson")  != 0 &&
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 4170-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 4170-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 9
    #####: 4171:		   strcmp(method, "spearman") != 0 &&
    %%%%%: 4171-block  0
call    0 never executed
        -: 4172:		   strcmp(method, "kendall")  != 0)
        -: 4173:		   croak("cor: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')",
        -: 4174:		         method);
        -: 4175:
        -: 4176:	  // --- validate x ------------------------------------------------
       3*: 4177:	  if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        3: 4177-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4177-block  1
call    2 never executed
        3: 4177-block  2
call    3 returned 3
        -: 4178:		   croak("cor: x must be an ARRAY reference");
        9: 4179:
        6: 4179-block  0
        9: 4179-block  1
branch  0 taken 6
branch  1 taken 3 (fallthrough)
       6*: 4180:	  AV*restrict x_av = (AV*)SvRV(x_sv);
        6: 4180-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 4180-block  1
call    2 never executed
        6: 4180-block  2
call    3 returned 6
       24: 4181:	  size_t nx   = av_len(x_av) + 1;
       24: 4181-block  0
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 4182:	  if (nx == 0) croak("cor: x is empty");
       18: 4182-block  0
call    0 returned 18
       18: 4183:
       18: 4184:	  // --- detect whether x is a flat vector or a matrix (AoA) -------
call    0 returned 18
      18*: 4185:	  bool x_is_matrix = 0;
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4185-block  0
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 4185-block  1
call    4 returned 18
branch  5 taken 18 (fallthrough)
branch  6 taken 0
       18: 4185-block  2
call    7 returned 18
    %%%%%: 4185-block  3
       18: 4185-block  4
        -: 4186:	  {
        -: 4187:		   SV**restrict fp = av_fetch(x_av, 0, 0);
        -: 4188:		   if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
        -: 4189:		       x_is_matrix = 1;
        -: 4190:	  }
        3: 4191:
        3: 4192:	  // --- detect y ----------------------------
        -: 4193:	  bool has_y = (SvOK(y_sv) && SvROK(y_sv) &&
        -: 4194:		            SvTYPE(SvRV(y_sv)) == SVt_PVAV);
        6: 4195:
        3: 4195-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4195-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4195-block  2
        -: 4196:	  AV*restrict y_av = has_y ? (AV*)SvRV(y_sv) : NULL;
        3: 4197:	  size_t ny = has_y ? av_len(y_av) + 1 : 0;
        3: 4197-block  0
call    0 returned 3
        3: 4198:
call    0 returned 3
       3*: 4199:	  bool y_is_matrix = 0;
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4199-block  0
call    2 never executed
        -: 4200:	  if (has_y && ny > 0) {
       3*: 4201:		   SV**restrict fp = av_fetch(y_av, 0, 0);
        3: 4201-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4201-block  1
call    2 never executed
        3: 4201-block  2
call    3 returned 3
        9: 4202:		   if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
        6: 4202-block  0
        9: 4202-block  1
branch  0 taken 6
branch  1 taken 3 (fallthrough)
       6*: 4203:		       y_is_matrix = 1;
        6: 4203-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 4203-block  1
call    2 never executed
        6: 4203-block  2
call    3 returned 6
       24: 4204:	  }
       24: 4204-block  0
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 4205:
       18: 4205-block  0
call    0 returned 18
       18: 4206:	CODE:
       18: 4207:      // Branch 1: both inputs are flat vectors  →  scalar result
call    0 returned 18
      18*: 4208:	  if (!x_is_matrix && !y_is_matrix) {
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4208-block  0
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 4208-block  1
call    4 returned 18
branch  5 taken 18 (fallthrough)
branch  6 taken 0
       18: 4208-block  2
call    7 returned 18
    %%%%%: 4208-block  3
       18: 4208-block  4
        -: 4209:		   if (!has_y) {
        -: 4210:		       /* cor(vector) == 1 by definition */
        -: 4211:		       RETVAL = newSVnv(1.0);
    #####: 4212:		   } else {
    #####: 4213:		       if (nx != ny)
    #####: 4214:		           croak("cor: x and y must have the same length (%lu vs %lu)",
    %%%%%: 4214-block  0
        -: 4215:		                 nx, ny);
        3: 4216:
        3: 4216-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 4217:		       if (nx < 2)
    %%%%%: 4217-block  0
call    0 never executed
        -: 4218:		           croak("cor: need at least 2 observations");
        3: 4219:
        3: 4219-block  0
call    0 returned 3
        3: 4220:		       double *restrict xd, *restrict yd;
call    0 returned 3
        -: 4221:		       Newx(xd, nx, double);
        -: 4222:		       Newx(yd, ny, double);
       3*: 4223:
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4223-block  0
call    2 never executed
        3: 4223-block  1
call    3 returned 3
        9: 4224:		       for (size_t i = 0; i < nx; i++) {
        9: 4224-block  0
branch  0 taken 6
branch  1 taken 3 (fallthrough)
        6: 4225:		           SV**restrict tv = av_fetch(x_av, i, 0);
        6: 4225-block  0
call    0 returned 6
        6: 4226:		           xd[i] = (tv && SvOK(*tv) && looks_like_number(*tv)) ? SvNV(*tv) : NAN;
call    0 returned 6
        -: 4227:		       }
        3: 4228:		       for (size_t i = 0; i < ny; i++) {
        3: 4228-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
        -: 4229:		           SV**restrict tv = av_fetch(y_av, i, 0);
        -: 4230:		           yd[i] = (tv && SvOK(*tv) && looks_like_number(*tv)) ? SvNV(*tv) : NAN;
    #####: 4231:		       }
    %%%%%: 4231-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4231-block  1
call    2 never executed
    %%%%%: 4231-block  2
call    3 never executed
    #####: 4232:
    %%%%%: 4232-block  0
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4233:		       double r = compute_cor(xd, yd, nx, method);
    %%%%%: 4233-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4233-block  1
call    2 never executed
    %%%%%: 4233-block  2
call    3 never executed
        -: 4234:		       Safefree(xd); Safefree(yd);
    #####: 4235:		       RETVAL = newSVnv(r);
    %%%%%: 4235-block  0
    %%%%%: 4235-block  1
    %%%%%: 4235-block  2
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4236:		   }
    #####: 4237:	  } else {//Branch 2: x is a matrix (or y is a matrix)  →  AoA result
    %%%%%: 4237-block  0
    %%%%%: 4237-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4238:		   // -- resolve x matrix dimensions
    %%%%%: 4238-block  0
call    0 never executed
    #####: 4239:		   if (!x_is_matrix)
    #####: 4240:		       croak("cor: x must be a matrix (array ref of array refs) "
        -: 4241:		             "when y is a matrix");
        -: 4242:
        -: 4243:		   SV**restrict xr0 = av_fetch(x_av, 0, 0);
    #####: 4244:		   if (!xr0 || !SvROK(*xr0) || SvTYPE(SvRV(*xr0)) != SVt_PVAV)
    %%%%%: 4244-block  0
    %%%%%: 4244-block  1
    %%%%%: 4244-block  2
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4245:		       croak("cor: each row of x must be an ARRAY reference");
    %%%%%: 4245-block  0
    %%%%%: 4245-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4246:
    %%%%%: 4246-block  0
call    0 never executed
call    1 never executed
        -: 4247:		   size_t ncols_x = av_len((AV*)SvRV(*xr0)) + 1;
    #####: 4248:		   if (ncols_x == 0) croak("cor: x matrix has zero columns");
    %%%%%: 4248-block  0
    %%%%%: 4248-block  1
call    0 never executed
    %%%%%: 4248-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 4249:
    %%%%%: 4249-block  0
call    0 never executed
        -: 4250:		   size_t nrows   = nx;   /* observations */
        -: 4251:
        9: 4252:		   // PRE-VALIDATION PASS: Ensure all rows are arrays to prevent memory leaks on croak
        3: 4252-block  0
        6: 4252-block  1
        9: 4252-block  2
branch  0 taken 6
branch  1 taken 3 (fallthrough)
       18: 4253:		   for (size_t i = 0; i < nrows; i++) {
        6: 4253-block  0
       18: 4253-block  1
branch  0 taken 12
branch  1 taken 6 (fallthrough)
       12: 4254:		       SV**restrict rv = av_fetch(x_av, i, 0);
       12: 4254-block  0
call    0 returned 12
call    1 returned 12
call    2 returned 12
        -: 4255:		       if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
        -: 4256:		           croak("cor: x row %lu is not an array ref", i);
        9: 4257:		   }
        3: 4257-block  0
        9: 4257-block  1
branch  0 taken 6
branch  1 taken 3 (fallthrough)
        6: 4258:           
        6: 4258-block  0
call    0 returned 6
call    1 returned 6
        3: 4259:           if (has_y && y_is_matrix) {
        3: 4259-block  0
call    0 returned 3
        -: 4260:		       if (ny != nrows) croak("cor: x and y must have the same number of rows (%lu vs %lu)", nrows, ny);
        9: 4261:               for (size_t i = 0; i < nrows; i++) {
        6: 4261-block  0
call    0 returned 6
        9: 4261-block  1
branch  1 taken 6
branch  2 taken 3 (fallthrough)
        3: 4262:		           SV**restrict rv = av_fetch(y_av, i, 0);
        3: 4262-block  0
call    0 returned 3
        3: 4263:		           if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        9: 4264:		               croak("cor: y row %lu is not an array ref", i);
        3: 4264-block  0
        6: 4264-block  1
call    0 returned 6
        9: 4264-block  2
branch  1 taken 6
branch  2 taken 3 (fallthrough)
        3: 4265:		       }
        3: 4265-block  0
call    0 returned 3
        -: 4266:           }
        3: 4267:
        3: 4267-block  0
call    0 returned 3
        -: 4268:		   // -- extract x columns
        -: 4269:		   double **restrict col_x;
        -: 4270:		   Newx(col_x, ncols_x, double*);
        -: 4271:
        -: 4272:		   for (size_t j = 0; j < ncols_x; j++) {
        -: 4273:		       Newx(col_x[j], nrows, double);
        -: 4274:		       for (size_t i = 0; i < nrows; i++) {
        -: 4275:		           SV**restrict rv = av_fetch(x_av, i, 0);
       15: 4276:		           AV*restrict  row = (AV*)SvRV(*rv);
       15: 4277:		           SV**restrict cv  = av_fetch(row, j, 0);
       15: 4278:		           col_x[j][i] = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
        -: 4279:		       }
       15: 4280:		   }
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       15: 4281:
       15: 4282:		   // -- resolve y: separate matrix or re-use x (symmetric)
       15: 4282-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 6
        9: 4282-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 3
        6: 4283:		   size_t ncols_y;
        6: 4284:		   double **restrict col_y   = NULL;
        -: 4285:		   bool symmetric = 0;
        6: 4286:
        6: 4286-block  0
call    0 returned 6
        6: 4287:		   // 1 = cor(X) — result is symmetric
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4288:		   if (has_y && y_is_matrix) {
        6: 4289:		       // cross-correlation: X (nrows × p) vs Y (nrows × q)
        6: 4289-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 4290:		       SV**restrict yr0 = av_fetch(y_av, 0, 0);
    %%%%%: 4290-block  0
        -: 4291:		       ncols_y = av_len((AV*)SvRV(*yr0)) + 1;
        6: 4292:		       if (ncols_y == 0) croak("cor: y matrix has zero columns");
        6: 4292-block  0
call    0 returned 6
        -: 4293:
        6: 4294:		       Newx(col_y, ncols_y, double*);
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4294-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 4294-block  1
branch  4 taken 3 (fallthrough)
branch  5 taken 3
        3: 4295:		       for (size_t j = 0; j < ncols_y; j++) {
        3: 4295-block  0
        3: 4296:		           Newx(col_y[j], nrows, double);
        3: 4296-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4296-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4296-block  2
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 4296-block  3
branch  6 taken 3 (fallthrough)
branch  7 taken 0
        3: 4297:		           for (size_t i = 0; i < nrows; i++) {
        3: 4297-block  0
    #####: 4298:		               SV**restrict  rv = av_fetch(y_av, i, 0);
    %%%%%: 4298-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4299:		               AV*restrict  row = (AV*)SvRV(*rv);
    %%%%%: 4299-block  0
call    0 never executed
    #####: 4300:		               SV**restrict cv  = av_fetch(row, j, 0);
    %%%%%: 4300-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4301:		               col_y[j][i] = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
    %%%%%: 4301-block  0
        -: 4302:		           }
    #####: 4303:		       }
    %%%%%: 4303-block  0
        -: 4304:		   } else { // cor(X) — symmetric p×p result; share column arrays
        -: 4305:		       ncols_y  = ncols_x;
        -: 4306:		       col_y    = col_x;
        -: 4307:		       symmetric = 1;
        6: 4308:		   }
        6: 4308-block  0
call    0 returned 6
        6: 4309:		   if (nrows < 2)
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 4310:		       croak("cor: need at least 2 observations (got %lu)", nrows);
        3: 4311:		   // -- build cache for symmetric case: compute upper triangle, store results, mirror to lower triangle
        3: 4311-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 4312:		   AV*restrict result_av = newAV();
    %%%%%: 4312-block  0
        -: 4313:		   av_extend(result_av, ncols_x - 1);
        3: 4314:		   // Allocate per-row AVs up front so we can fill them in order
        3: 4314-block  0
call    0 returned 3
        3: 4315:		   AV **restrict rows_out;
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4315-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4315-block  1
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    #####: 4316:		   Newx(rows_out, ncols_x, AV*);
    %%%%%: 4316-block  0
        3: 4317:		   for (size_t i = 0; i < ncols_x; i++) {
        3: 4317-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4317-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4317-block  2
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 4317-block  3
branch  6 taken 3 (fallthrough)
branch  7 taken 0
        3: 4318:		       rows_out[i] = newAV();
        3: 4318-block  0
    #####: 4319:		       av_extend(rows_out[i], ncols_y - 1);
    %%%%%: 4319-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4320:		   }
    %%%%%: 4320-block  0
call    0 never executed
    #####: 4321:		   if (symmetric) {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4321-block  0
    #####: 4322:		       /* Upper triangle + diagonal, then mirror. r_cache[i][j] (j >= i) holds the computed value. */
    %%%%%: 4322-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    #####: 4323:		       double **restrict r_cache;
    %%%%%: 4323-block  0
        -: 4324:		       Newx(r_cache, ncols_x, double*);
    #####: 4325:		       for (size_t i = 0; i < ncols_x; i++)
    %%%%%: 4325-block  0
        -: 4326:		           Newx(r_cache[i], ncols_x, double);
        -: 4327:
        -: 4328:		       for (size_t i = 0; i < ncols_x; i++) {
        -: 4329:		           r_cache[i][i] = 1.0; // diagonal
        -: 4330:		           for (size_t j = i + 1; j < ncols_x; j++) {
        -: 4331:		               double r = compute_cor(col_x[i], col_x[j], nrows, method);
       15: 4332:		               r_cache[i][j] = r;
       15: 4333:		               r_cache[j][i] = r; // symmetry
       15: 4333-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 9
        6: 4334:		           }
        6: 4335:		       }
        6: 4335-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 4335-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4336:		       // fill output AoA from cache
        3: 4337:		       for (size_t i = 0; i < ncols_x; i++)
        3: 4337-block  0
call    0 returned 3
branch  1 taken 3 (fallthrough)
branch  2 taken 0
        3: 4338:		           for (size_t j = 0; j < ncols_x; j++)
        3: 4338-block  0
call    0 returned 3
        3: 4339:		               av_store(rows_out[i], j, newSVnv(r_cache[i][j]));
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4339-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4339-block  1
branch  4 taken 3 (fallthrough)
branch  5 taken 0
        3: 4340:
        3: 4340-block  0
        -: 4341:		       for (size_t i = 0; i < ncols_x; i++) Safefree(r_cache[i]);
        -: 4342:		       Safefree(r_cache); r_cache = NULL;
        -: 4343:		   } else {
        -: 4344:		       // cross-correlation: every (i,j) pair is independent
       15: 4345:		       for (size_t i = 0; i < ncols_x; i++)
       15: 4345-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 12
        -: 4346:		           for (size_t j = 0; j < ncols_y; j++)
        -: 4347:		               av_store(rows_out[i], j, newSVnv(compute_cor(col_x[i], col_y[j], nrows, method)));
        -: 4348:		   }
        3: 4349:		   // push row AVs into result
        3: 4350:		   for (size_t i = 0; i < ncols_x; i++)
        3: 4350-block  0
call    0 returned 3
        -: 4351:		       av_store(result_av, i, newRV_noinc((SV*)rows_out[i]));
        3: 4352:		   Safefree(rows_out); rows_out = NULL;
call    0 returned 3
        3: 4353:		   // -- free column arrays -------------------------------------
call    0 returned 3
        -: 4354:		   for (size_t j = 0; j < ncols_x; j++) Safefree(col_x[j]);
       3*: 4355:		   Safefree(col_x); col_x = NULL;
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4355-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    %%%%%: 4355-block  1
call    4 never executed
        -: 4356:		   if (!symmetric) {
        -: 4357:		       for (size_t j = 0; j < ncols_y; j++) Safefree(col_y[j]);
        3: 4358:		       Safefree(col_y);
        3: 4358-block  0
call    0 returned 3
        3: 4359:		   }
call    0 returned 3
        3: 4360:		   RETVAL = newRV_noinc((SV*)result_av);
call    0 returned 3
       12: 4361:	  }
       12: 4361-block  0
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 4362:	OUTPUT:
        9: 4362-block  0
call    0 returned 9
        9: 4363:		RETVAL
call    0 returned 9
        9: 4364:
call    0 returned 9
call    1 returned 9
        -: 4365:void scale(...)
        -: 4366:	PROTOTYPE: @
        9: 4367:	PPCODE:
        3: 4367-block  0
        9: 4367-block  1
branch  0 taken 6
branch  1 taken 3 (fallthrough)
        6: 4368:	{
        -: 4369:		bool do_center_mean = true, do_scale_sd = true;
       6*: 4370:		double center_val = 0.0, scale_val = 1.0;
        6: 4370-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 4370-block  1
call    2 never executed
        6: 4370-block  2
call    3 returned 6
        -: 4371:		size_t data_items = items;
       24: 4372:		// 1. Parse Options Hash (if it exists as the last argument)
       24: 4372-block  0
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 4373:		if (items > 0) {
       18: 4373-block  0
call    0 returned 18
       18: 4374:			SV*restrict last_arg = ST(items - 1);
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4374-block  0
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 4375:			if (SvROK(last_arg) && SvTYPE(SvRV(last_arg)) == SVt_PVHV) {
       18: 4376:				 data_items = items - 1; // Exclude hash from data processing
       18: 4376-block  0
call    0 returned 18
      18*: 4377:				 HV*restrict opt_hv = (HV*)SvRV(last_arg);
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4377-block  0
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 4377-block  1
call    4 returned 18
    %%%%%: 4377-block  2
       18: 4377-block  3
        -: 4378:				 // --- Parse 'center'
    #####: 4379:				 SV**restrict center_sv = hv_fetch(opt_hv, "center", 6, 0);
    %%%%%: 4379-block  0
        -: 4380:				 if (center_sv) {
       18: 4381:				     SV*restrict val_sv = *center_sv;
       18: 4381-block  0
        -: 4382:				     if (!SvOK(val_sv)) {
        -: 4383:				         do_center_mean = false; center_val = 0.0;
       6*: 4384:				     } else {
        6: 4384-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4384-block  1
    %%%%%: 4384-block  2
        6: 4385:				         char *restrict str = SvPV_nolen(val_sv);
        -: 4386:				         /* Trap booleans and empty strings before numeric checks */
        6: 4387:				         if (strcasecmp(str, "mean") == 0 || strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) {
        6: 4387-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4388:				             do_center_mean = true;
        6: 4388-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 4389:				         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
    %%%%%: 4389-block  0
call    0 never executed
    #####: 4390:				             do_center_mean = false; center_val = 0.0;
call    0 never executed
    #####: 4391:				         } else if (looks_like_number(val_sv)) {
call    0 never executed
        -: 4392:				             do_center_mean = false; center_val = SvNV(val_sv);
        6: 4393:				         } else if (SvTRUE(val_sv)) {
       24: 4394:				             do_center_mean = true;
        6: 4394-block  0
       24: 4394-block  1
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 4395:				         } else {
       18: 4396:				             do_center_mean = false; center_val = 0.0;
       18: 4396-block  0
        -: 4397:				         }
        6: 4398:				     }
        6: 4398-block  0
        -: 4399:				 }
        -: 4400:				 // --- Parse 'scale' ---
       24: 4401:				 SV**restrict scale_sv = hv_fetch(opt_hv, "scale", 5, 0);
        6: 4401-block  0
       24: 4401-block  1
branch  0 taken 18
branch  1 taken 6 (fallthrough)
       18: 4402:				 if (scale_sv) {
      18*: 4403:				     SV*restrict val_sv = *scale_sv;
       18: 4403-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4403-block  1
    %%%%%: 4403-block  2
       18: 4404:				     if (!SvOK(val_sv)) {
       18: 4404-block  0
call    0 returned 18
call    1 returned 18
        -: 4405:				         do_scale_sd = false; scale_val = 1.0;
        6: 4406:				     } else {
        6: 4406-block  0
call    0 returned 6
        -: 4407:				         char *restrict str = SvPV_nolen(val_sv);
        3: 4408:				         if (strcasecmp(str, "sd") == 0 || strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) {
        3: 4408-block  0
call    0 returned 3
        -: 4409:				             do_scale_sd = true;
       3*: 4410:				         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4410-block  0
call    2 never executed
        3: 4411:				             do_scale_sd = false; scale_val = 1.0;
        3: 4411-block  0
call    0 returned 3
call    1 returned 3
        -: 4412:				         } else if (looks_like_number(val_sv)) {
        -: 4413:				             do_scale_sd = false; scale_val = SvNV(val_sv);
        -: 4414:				             if (scale_val == 0.0) scale_val = 1.0; /* Prevent Division By Zero */
        -: 4415:				         } else if (SvTRUE(val_sv)) {
       12: 4416:				             do_scale_sd = true;
        -: 4417:				         } else {
       12: 4418:				             do_scale_sd = false; scale_val = 1.0;
       60: 4419:				         }
       12: 4419-block  0
       48: 4419-block  1
       60: 4419-block  2
branch  0 taken 48
branch  1 taken 12 (fallthrough)
       48: 4420:				     }
      48*: 4421:				 }
       48: 4421-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 48
    %%%%%: 4421-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4421-block  2
    #####: 4422:			}
    #####: 4423:		}
    %%%%%: 4423-block  0
call    0 never executed
    #####: 4424:		// 2. Detect if the input is a Matrix (Array of Arrays)
    %%%%%: 4424-block  0
    %%%%%: 4424-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4425:		bool is_matrix = false;
    %%%%%: 4425-block  0
call    0 never executed
    #####: 4426:		if (data_items == 1) {
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4426-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4426-block  1
        -: 4427:			SV*restrict first_arg = ST(0);
       48: 4428:			if (SvROK(first_arg) && SvTYPE(SvRV(first_arg)) == SVt_PVAV) {
       48: 4428-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 0
       48: 4429:				 AV*restrict av = (AV*)SvRV(first_arg);
       48: 4429-block  0
        -: 4430:				 if (av_len(av) >= 0) {
        -: 4431:				     SV**restrict first_elem = av_fetch(av, 0, 0);
      12*: 4432:				     if (first_elem && SvROK(*first_elem) && SvTYPE(SvRV(*first_elem)) == SVt_PVAV) {
       12: 4432-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 4432-block  1
call    2 never executed
      12*: 4433:				         is_matrix = true;
       12: 4433-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    %%%%%: 4433-block  1
call    2 never executed
       12: 4433-block  2
call    3 returned 12
       60: 4434:				     }
       48: 4434-block  0
       60: 4434-block  1
branch  0 taken 48
branch  1 taken 12 (fallthrough)
       48: 4435:				 }
      48*: 4436:			}
       48: 4436-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 48
    %%%%%: 4436-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4436-block  2
    #####: 4437:		}
    #####: 4438:		if (is_matrix) {
    %%%%%: 4438-block  0
call    0 never executed
    #####: 4439:			//=========================================================
    %%%%%: 4439-block  0
    %%%%%: 4439-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 4440:			// MATRIX MODE: Scale columns independently (Just like R)
    %%%%%: 4440-block  0
call    0 never executed
    #####: 4441:			//=========================================================
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4441-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4442:			AV*restrict mat_av = (AV*)SvRV(ST(0));
    %%%%%: 4442-block  0
call    0 never executed
    #####: 4443:			size_t nrow = av_len(mat_av) + 1, ncol = 0;
        -: 4444:			
        -: 4445:			SV**restrict first_row = av_fetch(mat_av, 0, 0);
       48: 4446:			ncol = av_len((AV*)SvRV(*first_row)) + 1;
       48: 4446-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 0
       48: 4447:
       48: 4447-block  0
call    0 returned 48
       48: 4448:			if (nrow == 0 || ncol == 0) croak("scale requires non-empty matrix");
        -: 4449:
        -: 4450:			// Create a new matrix for the scaled output
        -: 4451:			AV*restrict result_av = newAV();
       12: 4452:			av_extend(result_av, nrow - 1);
       12: 4452-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9: 4452-block  1
        -: 4453:			AV**restrict row_ptrs = (AV**)safemalloc(nrow * sizeof(AV*));
       12: 4454:			for (size_t r = 0; r < nrow; r++) {
       12: 4454-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9: 4455:				 row_ptrs[r] = newAV();
        9: 4455-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 4456:				 av_extend(row_ptrs[r], ncol - 1);
        3: 4456-block  0
call    0 returned 3
        3: 4457:				 av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
call    0 returned 0
        -: 4458:			}
        6: 4459:			// Calculate and apply scale per column
       36: 4460:			for (size_t c = 0; c < ncol; c++) {
        6: 4460-block  0
       36: 4460-block  1
branch  0 taken 30
branch  1 taken 6 (fallthrough)
       30: 4461:				 double col_sum = 0.0;
       30: 4462:				 double *restrict col_data;
       30: 4462-block  0
        -: 4463:				 Newx(col_data, nrow, double);
        6: 4464:				 // Extract the column data
        6: 4464-block  0
        -: 4465:				 for (size_t r = 0; r < nrow; r++) {
       9*: 4466:				     SV**restrict row_sv = av_fetch(mat_av, r, 0);
        9: 4466-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 4466-block  1
call    2 never executed
       54: 4467:				     if (row_sv && SvROK(*row_sv)) {
        9: 4467-block  0
       54: 4467-block  1
branch  0 taken 45
branch  1 taken 9 (fallthrough)
       45: 4468:				         AV*restrict row_av = (AV*)SvRV(*row_sv);
      45*: 4469:				         SV**restrict cell_sv = av_fetch(row_av, c, 0);
       45: 4469-block  0
branch  0 taken 45 (fallthrough)
branch  1 taken 0
       45: 4469-block  1
    %%%%%: 4469-block  2
       45: 4470:				         col_data[r] = (cell_sv && SvOK(*cell_sv)) ? SvNV(*cell_sv) : 0.0;
       45: 4470-block  0
call    0 returned 45
call    1 returned 45
        -: 4471:				     } else {
        9: 4472:				         col_data[r] = 0.0;
        9: 4472-block  0
call    0 returned 9
        -: 4473:				     }
        -: 4474:				     col_sum += col_data[r];
        -: 4475:				 }
        -: 4476:
        -: 4477:				 double col_center = do_center_mean ? (col_sum / nrow) : center_val;
        -: 4478:				 double col_scale = scale_val;
       12: 4479:				 // Calculate Standard Deviation for this specific column if needed
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 4480:				 if (do_scale_sd) {
    %%%%%: 4480-block  0
call    0 never executed
        -: 4481:				     if (nrow <= 1) {
       12: 4482:				         Safefree(col_data);
       12: 4483:				         safefree(row_ptrs);
       12: 4484:				         croak("scale needs >= 2 rows to calculate standard deviation for a matrix column");
        -: 4485:				     }
       36: 4486:				     double sum_sq = 0.0;
       12: 4486-block  0
       24: 4486-block  1
       36: 4486-block  2
branch  0 taken 24
branch  1 taken 12 (fallthrough)
       24: 4487:				     for (size_t r = 0; r < nrow; r++) {
       24: 4487-block  0
call    0 returned 24
       24: 4488:				         double diff = col_data[r] - col_center;
       24: 4489:				         sum_sq += diff * diff;
branch  0 taken 12 (fallthrough)
branch  1 taken 12
       12: 4490:				     }
       12: 4490-block  0
       12: 4491:				     col_scale = sqrt(sum_sq / (nrow - 1));
       12: 4491-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 3
        9: 4492:				 }
        9: 4492-block  0
call    0 returned 9
        9: 4493:				 // Store scaled values back into the new matrix rows
        3: 4494:				 for (size_t r = 0; r < nrow; r++) {
        3: 4494-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4495:				     double centered = col_data[r] - col_center;
        3: 4495-block  0
call    0 returned 3
        3: 4496:				     double final_val = (col_scale == 0.0) ? (0.0 / 0.0) : (centered / col_scale);
    #####: 4497:				     av_store(row_ptrs[r], c, newSVnv(final_val));
    %%%%%: 4497-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 4498:				 }
    %%%%%: 4498-block  0
call    0 never executed
        -: 4499:				 Safefree(col_data);
    #####: 4500:			}
    %%%%%: 4500-block  0
call    0 never executed
        -: 4501:			safefree(row_ptrs);
        -: 4502:			// Push the resulting matrix as a single Reference onto the Perl stack
        -: 4503:			EXTEND(SP, 1);
       12: 4504:			PUSHs(sv_2mortal(newRV_noinc((SV*)result_av)));
       12: 4504-block  0
branch  0 taken 12 (fallthrough)
branch  1 taken 0
       12: 4504-block  1
branch  2 taken 9 (fallthrough)
branch  3 taken 3
        9: 4504-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 9
        3: 4505:		} else {
        3: 4505-block  0
call    0 returned 0
        -: 4506:			// ======================================
        9: 4507:			// FLAT LIST MODE: Original functionality
       9*: 4508:			// ======================================
        9: 4508-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 4508-block  1
call    2 never executed
        9: 4508-block  2
        9: 4509:			size_t total_count = 0, k = 0;
        9: 4509-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 4510:			double *restrict nums;
        3: 4510-block  0
call    0 returned 0
        -: 4511:			double sum = 0.0;
        -: 4512:			for (size_t i = 0; i < data_items; i++) {
       6*: 4513:				SV*restrict arg = ST(i);
        6: 4513-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 4513-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4514:				if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
    #####: 4515:					AV*restrict av = (AV*)SvRV(arg);
    %%%%%: 4515-block  0
        6: 4516:					size_t len = av_len(av) + 1;
        6: 4516-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4516-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 3
        3: 4517:					for (unsigned int j = 0; j < len; j++) {
        3: 4517-block  0
       3*: 4518:						SV**restrict tv = av_fetch(av, j, 0);
        3: 4518-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4518-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4519:						if (tv && SvOK(*tv)) { total_count++; }
    %%%%%: 4519-block  0
        -: 4520:					}
        -: 4521:				} else if (SvOK(arg)) {
        6: 4522:					total_count++;
        6: 4522-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 4522-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
        3: 4523:				}
        3: 4523-block  0
call    0 returned 0
        -: 4524:			}
        -: 4525:			if (total_count == 0) croak("scale requires at least 1 numeric element");
        3: 4526:			Newx(nums, total_count, double);
        3: 4526-block  0
call    0 returned 3
        3: 4527:			for (size_t i = 0; i < data_items; i++) {
call    0 returned 3
        -: 4528:				 SV*restrict arg = ST(i);
        3: 4529:				 if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
call    0 returned 3
        9: 4530:				     AV*restrict av = (AV*)SvRV(arg);
        9: 4530-block  0
branch  0 taken 6
branch  1 taken 3 (fallthrough)
        6: 4531:				     size_t len = av_len(av) + 1;
        6: 4531-block  0
call    0 returned 6
        6: 4532:				     for (size_t j = 0; j < len; j++) {
call    0 returned 6
        6: 4533:				         SV**restrict tv = av_fetch(av, j, 0);
call    0 returned 6
call    1 returned 6
        -: 4534:				         if (tv && SvOK(*tv)) { 
        -: 4535:				             double val = SvNV(*tv);
        3: 4536:				             nums[k++] = val; sum += val;
       21: 4537:				         }
        3: 4537-block  0
       21: 4537-block  1
branch  0 taken 18
branch  1 taken 3 (fallthrough)
        -: 4538:				     }
       18: 4539:				 } else if (SvOK(arg)) {
       18: 4539-block  0
call    0 returned 18
      18*: 4540:				     double val = SvNV(arg);
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 4540-block  0
call    2 returned 18
    %%%%%: 4540-block  1
call    3 never executed
       18: 4541:				     nums[k++] = val; sum += val;
       18: 4541-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    #####: 4542:				 }
    #####: 4543:			}
    %%%%%: 4543-block  0
        -: 4544:			if (do_center_mean) center_val = sum / total_count;
       18: 4545:			if (do_scale_sd) {
       18: 4546:				 if (total_count <= 1) {
       18: 4546-block  0
        -: 4547:				     Safefree(nums);
       18: 4548:				     croak("scale needs >= 2 elements to calculate SD");
       18: 4548-block  0
call    0 returned 18
        -: 4549:				 }
        3: 4550:				 double sum_sq = 0.0;
        3: 4550-block  0
call    0 returned 3
        3: 4551:				 for (size_t i = 0; i < total_count; i++) {
call    0 returned 3
        -: 4552:				     double diff = nums[i] - center_val;
        -: 4553:				     sum_sq += diff * diff;
        -: 4554:				 }
        -: 4555:				 scale_val = sqrt(sum_sq / (total_count - 1));
        -: 4556:			}
        -: 4557:			EXTEND(SP, total_count);
       66: 4558:			for (size_t i = 0; i < total_count; i++) {
       66: 4559:				double centered = nums[i] - center_val;
        -: 4560:				double final_val = (scale_val == 0.0) ? (0.0 / 0.0) : (centered / scale_val);
        -: 4561:				PUSHs(sv_2mortal(newSVnv(final_val)));
        -: 4562:			}
       66: 4563:			Safefree(nums); nums = NULL;
       66: 4564:		}
       66: 4565:	}
       66: 4566:
       66: 4567:SV* matrix(...) 
       66: 4568:CODE:
        -: 4569:	// Basic check: must have an even number of arguments for key => value
       66: 4570:	if (items % 2 != 0) {
       66: 4571:	  croak("Usage: matrix(data => [...], nrow => $n, ncol => $m, byrow => $bool)");
       66: 4572:	}
       66: 4573:	SV*restrict data_sv = NULL;
        -: 4574:	size_t nrow = 0, ncol = 0;
       66: 4575:	bool byrow = FALSE, nrow_set = FALSE, ncol_set = FALSE;
       66: 4576:	// Parse named arguments
       66: 4577:	for (size_t i = 0; i < items; i += 2) {
       66: 4578:	  char*restrict key = SvPV_nolen(ST(i));
        -: 4579:	  SV*restrict val   = ST(i + 1);
        -: 4580:	  if (strEQ(key, "data")) {
       66: 4581:		   data_sv = val;
        -: 4582:	  } else if (strEQ(key, "nrow")) {
        -: 4583:		   nrow = (size_t)SvUV(val);
      66*: 4584:		   nrow_set = TRUE;
branch  0 taken 0 (fallthrough)
branch  1 taken 66
    %%%%%: 4584-block  0
call    2 never executed
        -: 4585:	  } else if (strEQ(key, "ncol")) {
      192: 4586:		   ncol = (size_t)SvUV(val);
       66: 4586-block  0
      126: 4586-block  1
      192: 4586-block  2
branch  0 taken 126
branch  1 taken 66 (fallthrough)
      126: 4587:		   ncol_set = TRUE;
      126: 4587-block  0
call    0 returned 126
      126: 4588:	  } else if (strEQ(key, "byrow")) {
      126: 4589:		   byrow = SvTRUE(val);
branch  0 taken 63 (fallthrough)
branch  1 taken 63
       63: 4589-block  0
call    2 returned 63
       63: 4590:	  } else {
       63: 4590-block  0
branch  0 taken 63 (fallthrough)
branch  1 taken 0
       63: 4590-block  1
    #####: 4591:		   croak("Unknown option: %s", key);
    %%%%%: 4591-block  0
call    0 never executed
        -: 4592:	  }
       66: 4593:	}
       66: 4593-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 63
        3: 4593-block  1
call    2 returned 0
       63: 4594:	// Validate data input
       63: 4594-block  0
branch  0 taken 60 (fallthrough)
branch  1 taken 3
       60: 4594-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 57
        6: 4594-block  2
call    4 returned 0
        -: 4595:	if (!data_sv || !SvROK(data_sv) || SvTYPE(SvRV(data_sv)) != SVt_PVAV) {
        -: 4596:	  croak("The 'data' option must be an array reference (e.g. data => [1..6])");
        -: 4597:	}
        -: 4598:	AV*restrict data_av = (AV*)SvRV(data_sv);
       57: 4599:	size_t data_len = (UV)(av_top_index(data_av) + 1);
       57: 4600:	if (data_len == 0) {
       57: 4600-block  0
branch  0 taken 57 (fallthrough)
branch  1 taken 0
       57: 4601:	  croak("Data array cannot be empty");
      57*: 4602:	}
       57: 4602-block  0
call    0 returned 57
branch  1 taken 0 (fallthrough)
branch  2 taken 57
    %%%%%: 4602-block  1
call    3 never executed
       57: 4603:	// R-style dimension inference
       57: 4603-block  0
call    0 returned 57
       57: 4604:	if (!nrow_set && !ncol_set) {
branch  0 taken 57 (fallthrough)
branch  1 taken 0
       57: 4605:	  nrow = data_len;
       57: 4605-block  0
call    0 returned 57
       57: 4606:	  ncol = 1;
branch  0 taken 57 (fallthrough)
branch  1 taken 0
       57: 4606-block  0
branch  2 taken 36 (fallthrough)
branch  3 taken 21
       36: 4607:	} else if (nrow_set && !ncol_set) {
       36: 4608:	  ncol = (data_len + nrow - 1) / nrow;
       36: 4608-block  0
call    0 returned 36
      36*: 4609:	} else if (!nrow_set && ncol_set) {
branch  0 taken 0 (fallthrough)
branch  1 taken 36
    %%%%%: 4609-block  0
call    2 never executed
       36: 4609-block  1
call    3 returned 36
      246: 4610:	  nrow = (data_len + ncol - 1) / ncol;
      246: 4610-block  0
branch  0 taken 210
branch  1 taken 36 (fallthrough)
        -: 4611:	}
      210: 4612:	// Final safety check for dimensions
      210: 4613:	if (nrow == 0 || ncol == 0) {
      210: 4613-block  0
call    0 returned 210
        -: 4614:	  croak("Dimensions must be greater than 0");
       21: 4615:	}
       21: 4615-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 4615-block  1
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 4616:	// Create the matrix (Array of Arrays)
       21: 4616-block  0
call    0 returned 21
      21*: 4617:	AV*restrict result_av = newAV();
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 4617-block  0
call    2 never executed
       21: 4617-block  1
call    3 returned 21
branch  4 taken 0 (fallthrough)
branch  5 taken 21
    %%%%%: 4617-block  2
call    6 never executed
       21: 4617-block  3
call    7 returned 21
       21: 4618:	av_extend(result_av, nrow - 1);
      693: 4619:	size_t r, c;// Use unsigned types for counters to prevent negative indexing
      693: 4619-block  0
call    0 returned 693
branch  1 taken 672
branch  2 taken 21 (fallthrough)
        -: 4620:	AV**restrict row_ptrs = (AV**restrict)safemalloc(nrow * sizeof(AV*)); /* Pre-allocate row pointers */
      672: 4621:	for (r = 0; r < nrow; r++) {
      672: 4621-block  0
call    0 returned 672
call    1 returned 672
      672: 4622:	  row_ptrs[r] = newAV();
call    0 returned 672
      672: 4623:	  av_extend(row_ptrs[r], ncol - 1);
        -: 4624:	  av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
    #####: 4625:	}
    %%%%%: 4625-block  0
call    0 never executed
        -: 4626:	// Fill the matrix
    #####: 4627:	size_t total_cells = nrow * ncol;
    %%%%%: 4627-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 4628:	for (size_t i = 0; i < total_cells; i++) {
    %%%%%: 4628-block  0
call    0 never executed
    #####: 4629:	  // Vector recycling logic
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4629-block  0
call    2 never executed
    %%%%%: 4629-block  1
call    3 never executed
    #####: 4630:	  SV**restrict fetched = av_fetch(data_av, i % data_len, 0);
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4630-block  0
call    2 never executed
    %%%%%: 4630-block  1
call    3 never executed
    #####: 4631:	  SV*restrict val = fetched ? newSVsv(*fetched) : newSV(0);
    %%%%%: 4631-block  0
    %%%%%: 4631-block  1
branch  0 never executed
branch  1 never executed
    #####: 4632:	  if (byrow) {
    %%%%%: 4632-block  0
call    0 never executed
    #####: 4633:		   r = i / ncol;
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4633-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4633-block  1
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4634:		   c = i % ncol;
    #####: 4635:	  } else {
    #####: 4636:		   r = i % nrow;
    %%%%%: 4636-block  0
call    0 never executed
        -: 4637:		   c = i / nrow;
    #####: 4638:	  }
    %%%%%: 4638-block  0
    %%%%%: 4638-block  1
call    0 never executed
    %%%%%: 4638-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 4639:	  av_store(row_ptrs[r], c, val);
    %%%%%: 4639-block  0
call    0 never executed
call    1 never executed
    #####: 4640:	}
call    0 never executed
        -: 4641:	safefree(row_ptrs);
        -: 4642:	RETVAL = newRV_noinc((SV*)result_av);
    #####: 4643:	OUTPUT:
    %%%%%: 4643-block  0
call    0 never executed
        -: 4644:	RETVAL
        -: 4645:
        -: 4646:SV* lm(...)
        -: 4647:CODE:
       57: 4648:{
      645: 4649:	const char *restrict formula  = NULL;
       57: 4649-block  0
      588: 4649-block  1
branch  0 taken 411 (fallthrough)
branch  1 taken 177
      411: 4649-block  2
      588: 4649-block  3
      645: 4649-block  4
branch  2 taken 588 (fallthrough)
branch  3 taken 57
      588: 4649-block  5
branch  4 taken 588
branch  5 taken 0 (fallthrough)
       57: 4650:	SV *restrict data_sv = NULL;
        -: 4651:	char f_cpy[512];
       57: 4652:	char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
       57: 4653:
       57: 4653-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 54
        9: 4654:	char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
        3: 4654-block  0
        6: 4654-block  1
call    0 returned 6
        9: 4654-block  2
branch  1 taken 6
branch  2 taken 3 (fallthrough)
       3*: 4655:	bool *restrict is_dummy = NULL;
        3: 4655-block  0
call    0 returned 3
branch  1 taken 0 (fallthrough)
branch  2 taken 3
    %%%%%: 4655-block  1
call    3 never executed
        3: 4656:	char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
        3: 4656-block  0
call    0 returned 0
        -: 4657:	unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
       54: 4658:	size_t n = 0, valid_n = 0, i, j, k, l, l1, l2;
       54: 4659:	bool has_intercept = true;
       54: 4660:
        -: 4661:	char **restrict row_names = NULL, **restrict valid_row_names = NULL;
        -: 4662:	HV **restrict row_hashes = NULL;
        -: 4663:	HV *restrict data_hoa = NULL;
        -: 4664:	SV *restrict ref = NULL;
        -: 4665:
       54: 4666:	double *restrict X = NULL, *restrict Y = NULL, *restrict XtX = NULL, *restrict XtY = NULL;
      267: 4667:	bool *restrict aliased = NULL;
       54: 4667-block  0
      267: 4667-block  1
branch  0 taken 213
branch  1 taken 54 (fallthrough)
        -: 4668:	double *restrict beta = NULL;
     213*: 4669:	int final_rank = 0, df_res = 0;
      213: 4669-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 213
    %%%%%: 4669-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 4670:	HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
    #####: 4671:	AV *restrict terms_av;
    %%%%%: 4671-block  0
    %%%%%: 4671-block  1
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4671-block  2
    %%%%%: 4671-block  3
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4671-block  4
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 4671-block  5
    %%%%%: 4671-block  6
    %%%%%: 4671-block  7
branch  6 never executed
branch  7 never executed (fallthrough)
    #####: 4672:	double rss = 0.0, rse_sq = 0.0;
    %%%%%: 4672-block  0
        -: 4673:	HE *restrict entry;
        -: 4674:
      213: 4675:	if (items % 2 != 0) croak("Usage: lm(formula => 'mpg ~ wt * hp', data => \\%%mtcars)");
      213: 4675-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 210
        3: 4675-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
       3*: 4676:
        3: 4676-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4676-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4676-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
        3: 4677:	for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
        3: 4678:	  const char *restrict key = SvPV_nolen(ST(i_arg));
        3: 4679:	  SV *restrict val = ST(i_arg + 1);
        3: 4679-block  0
        -: 4680:	  if      (strEQ(key, "formula")) formula = SvPV_nolen(val);
        -: 4681:	  else if (strEQ(key, "data"))    data_sv = val;
     210*: 4682:	  else croak("lm: unknown argument '%s'", key);
      210: 4682-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 186
       24: 4682-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 24
    #####: 4683:	}
    %%%%%: 4683-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4683-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4683-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4684:	if (!formula) croak("lm: formula is required");
    #####: 4685:	if (!data_sv || !SvROK(data_sv)) croak("lm: data is required and must be a reference");
    #####: 4686:
    %%%%%: 4686-block  0
        -: 4687:	// ========================================================================
        -: 4688:	// PHASE 1: Data Extraction
     210*: 4689:	// ========================================================================
      210: 4689-block  0
branch  0 taken 54 (fallthrough)
branch  1 taken 156
       54: 4689-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 54
    %%%%%: 4689-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4690:	ref = SvRV(data_sv);
    #####: 4691:	if (SvTYPE(ref) == SVt_PVHV) {
    #####: 4692:	  HV *restrict hv = (HV*)ref;
    %%%%%: 4692-block  0
        -: 4693:	  if (hv_iterinit(hv) == 0) croak("lm: Data hash is empty");
        -: 4694:	  entry = hv_iternext(hv);
     210*: 4695:	  if (entry) {
      210: 4695-block  0
branch  0 taken 54 (fallthrough)
branch  1 taken 156
       54: 4695-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 54
    %%%%%: 4695-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4696:		   SV *restrict val = hv_iterval(hv, entry);
    %%%%%: 4696-block  0
        -: 4697:		   if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
        -: 4698:		       data_hoa = hv;
     210*: 4699:		       n = av_len((AV*)SvRV(val)) + 1;
      210: 4699-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 186
       24: 4699-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 24
    #####: 4700:		       Newx(row_names, n, char*);
    %%%%%: 4700-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 4700-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4700-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 4701:		       for (i = 0; i < n; i++) {
    #####: 4702:		           char buf[32];
    %%%%%: 4702-block  0
        -: 4703:		           snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
        -: 4704:		           row_names[i] = savepv(buf);
      210: 4705:		       }
      210: 4705-block  0
branch  0 taken 54 (fallthrough)
branch  1 taken 156
      54*: 4706:		   } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
       54: 4706-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 54
    %%%%%: 4706-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4706-block  2
      54*: 4707:		       n = hv_iterinit(hv);
       54: 4707-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 54
    %%%%%: 4707-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4707-block  2
        -: 4708:		       Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
      210: 4709:		       i = 0;
      210: 4709-block  0
        -: 4710:		       while ((entry = hv_iternext(hv))) {
        -: 4711:		           unsigned int len;
        -: 4712:		           row_names[i] = savepv(hv_iterkey(entry, &len));
        -: 4713:		           row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
        -: 4714:		           i++;
        -: 4715:		       }
       54: 4716:		   } else croak("lm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
       54: 4716-block  0
       54: 4716-block  1
branch  0 taken 0
branch  1 taken 54 (fallthrough)
    #####: 4717:	  }
    %%%%%: 4717-block  0
      54*: 4718:	} else if (SvTYPE(ref) == SVt_PVAV) {
       54: 4718-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 54
    %%%%%: 4718-block  1
       54: 4719:	  AV *restrict av = (AV*)ref; n = av_len(av) + 1;
      54*: 4720:	  Newx(row_names, n, char*);
       54: 4720-block  0
branch  0 taken 54 (fallthrough)
branch  1 taken 0
       54: 4720-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 54
    %%%%%: 4720-block  2
        -: 4721:	  Newx(row_hashes, n, HV*);
        -: 4722:	  for (i = 0; i < n; i++) {
        -: 4723:		   SV **restrict val = av_fetch(av, i, 0);
       54: 4724:		   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
       54: 4725:		       row_hashes[i] = (HV*)SvRV(*val);
       54: 4726:		       char buf[32]; snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
       54: 4726-block  0
call    0 returned 54
      132: 4727:		       row_names[i] = savepv(buf);
      132: 4727-block  0
branch  0 taken 78
branch  1 taken 54 (fallthrough)
       78: 4728:		   } else {
       78: 4728-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 75
        3: 4729:		       for (k = 0; k < i; k++) Safefree(row_names[k]);
        3: 4729-block  0
call    0 returned 3
       12: 4730:		       Safefree(row_names); Safefree(row_hashes);
        9: 4730-block  0
       12: 4730-block  1
call    0 returned 12
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        9: 4731:		       croak("lm: Array values must be HashRefs (AoH)");
        9: 4731-block  0
call    0 returned 9
        9: 4732:		   }
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 4732-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 4733:	  }
        9: 4733-block  0
call    0 returned 9
        9: 4734:	} else croak("lm: Data must be an Array or Hash reference");
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        6: 4735:
        6: 4736:	// ========================================================================
        6: 4736-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 4737:	// PHASE 2: Formula Parsing & `.` Expansion
        6: 4737-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 4737-block  1
        6: 4738:	// ========================================================================
        6: 4739:	src = (char*)formula; dst = f_cpy;
        6: 4739-block  0
        -: 4740:	while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
        -: 4741:	*dst = '\0';
        -: 4742:
        -: 4743:	tilde = strchr(f_cpy, '~');
        3: 4744:	if (!tilde) {
        3: 4744-block  0
call    0 returned 3
        -: 4745:	  for (i = 0; i < n; i++) Safefree(row_names[i]);
       75: 4746:	  Safefree(row_names); if (row_hashes) Safefree(row_hashes);
       75: 4747:	  croak("lm: invalid formula, missing '~'");
       75: 4747-block  0
branch  0 taken 75 (fallthrough)
branch  1 taken 0
       75: 4748:	}
       75: 4748-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 51
       24: 4748-block  1
       75: 4749:	*tilde = '\0';
       75: 4750:	lhs = f_cpy;
       75: 4750-block  0
        -: 4751:	rhs = tilde + 1;
        -: 4752:
       78: 4753:	// Remove intercept-suppression markers from RHS.
       78: 4753-block  0
call    0 returned 78
        -: 4754:	// IMPORTANT: skip tokens that appear inside I(...) wrappers so that
        -: 4755:	// expressions like I(x^-1) are never mistakenly treated as "-1".
       54: 4756:	{
       54: 4756-block  0
call    0 returned 54
call    1 returned 54
       54: 4757:	  char *restrict p_idx = rhs;
call    0 returned 54
call    1 returned 54
       54: 4758:	  while (*p_idx) {
call    0 returned 54
call    1 returned 54
        -: 4759:		   // Skip over I(...) sub-expressions entirely
       54: 4760:		   if (p_idx[0] == 'I' && p_idx[1] == '(') {
branch  0 taken 51 (fallthrough)
branch  1 taken 3
       51: 4760-block  0
call    2 returned 51
        -: 4761:		       int depth = 0;
       54: 4762:		       while (*p_idx) { if (*p_idx == '(') depth++; else if (*p_idx == ')') { depth--; if (depth == 0) { p_idx++; break; } } p_idx++; }
       54: 4762-block  0
branch  0 taken 54 (fallthrough)
branch  1 taken 0
       54: 4763:		       continue;
       54: 4763-block  0
call    0 returned 54
      135: 4764:		   }
      135: 4764-block  0
branch  0 taken 81
branch  1 taken 54 (fallthrough)
       81: 4765:		   // Match bare -1
       81: 4765-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 81
    #####: 4766:		   if (p_idx[0] == '-' && p_idx[1] == '1' &&
    #####: 4767:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
    %%%%%: 4767-block  0
call    0 never executed
call    1 never executed
        -: 4768:		       has_intercept = false;
       81: 4769:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
       81: 4770:		       continue; // re-examine same position
       81: 4770-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 78
        3: 4771:		   }
        3: 4772:		   // Match +0
        3: 4773:		   if (p_idx[0] == '+' && p_idx[1] == '0' &&
        3: 4774:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
       3*: 4775:		       has_intercept = false;
        3: 4775-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 4775-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4775-block  2
        3: 4776:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
        3: 4777:		       continue;
        3: 4777-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 4777-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 4777-block  2
        3: 4778:		   }
        3: 4778-block  0
call    0 returned 3
        3: 4779:		   // Match leading 0+
call    0 returned 3
        3: 4780:		   if (p_idx == rhs && p_idx[0] == '0' && p_idx[1] == '+') {
        3: 4781:		       has_intercept = false;
call    0 returned 3
        3: 4782:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
        -: 4783:		       continue;
       78: 4784:		   }
      78*: 4785:		   // Match bare 0 (entire rhs)
       78: 4785-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 78
    %%%%%: 4785-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4785-block  2
       78: 4786:		   if (p_idx == rhs && p_idx[0] == '0' && p_idx[1] == '\0') {
       78: 4786-block  0
call    0 returned 78
        -: 4787:		       has_intercept = false; p_idx[0] = '\0'; break;
       81: 4788:		   }
       81: 4788-block  0
call    0 returned 81
        -: 4789:		   // Strip redundant +1 (keep intercept, just remove marker)
        -: 4790:		   if (p_idx[0] == '+' && p_idx[1] == '1' &&
        -: 4791:		       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
      192: 4792:		       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
       54: 4792-block  0
      138: 4792-block  1
      192: 4792-block  2
branch  0 taken 138
branch  1 taken 54 (fallthrough)
      138: 4793:		       continue;
     258*: 4794:		   }
      138: 4794-block  0
      120: 4794-block  1
branch  0 taken 0 (fallthrough)
branch  1 taken 120
    %%%%%: 4794-block  2
      120: 4794-block  3
      258: 4794-block  4
branch  2 taken 120
branch  3 taken 138 (fallthrough)
      138: 4795:		   // Strip leading bare 1 or 1+
      138: 4795-block  0
branch  0 taken 138 (fallthrough)
branch  1 taken 0
      138: 4795-block  1
call    2 returned 138
        -: 4796:		   if (p_idx == rhs) {
       54: 4797:		       if (p_idx[0] == '1' && p_idx[1] == '\0') { p_idx[0] = '\0'; break; }
        -: 4798:		       if (p_idx[0] == '1' && p_idx[1] == '+') { memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); continue; }
        -: 4799:		   }
        -: 4800:		   p_idx++;
        -: 4801:	  }
      192: 4802:	}
       54: 4802-block  0
      138: 4802-block  1
      192: 4802-block  2
branch  0 taken 138
branch  1 taken 54 (fallthrough)
      138: 4803:
      138: 4803-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 138
    #####: 4804:	// Clean up stray `++`, leading `+`, trailing `+`
    #####: 4805:	{
    %%%%%: 4805-block  0
call    0 never executed
call    1 never executed
    #####: 4806:	  char *restrict p_idx;
call    0 never executed
call    1 never executed
        -: 4807:	  while ((p_idx = strstr(rhs, "++")) != NULL)
      138: 4808:		   memmove(p_idx, p_idx + 1, strlen(p_idx + 1) + 1);
      138: 4808-block  0
branch  0 taken 51 (fallthrough)
branch  1 taken 87
       51: 4809:	  if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
       51: 4809-block  0
call    0 returned 51
        -: 4810:	  size_t len_rhs = strlen(rhs);
       87: 4811:	  if (len_rhs > 0 && rhs[len_rhs - 1] == '+') rhs[len_rhs - 1] = '\0';
       87: 4811-block  0
call    0 returned 87
branch  1 taken 15 (fallthrough)
branch  2 taken 72
       15: 4812:	}
       15: 4813:
       15: 4814:	// Expand `.` Operator
       15: 4814-block  0
call    0 returned 15
      141: 4815:	char rhs_expanded[2048] = "";
      126: 4815-block  0
      141: 4815-block  1
branch  0 taken 126
branch  1 taken 15 (fallthrough)
      126: 4816:	size_t rhs_len = 0;
      126: 4816-block  0
call    0 returned 126
      126: 4817:	chunk = strtok(rhs, "+");
branch  0 taken 126 (fallthrough)
branch  1 taken 0
      126: 4818:	while (chunk != NULL) {
      243: 4819:	  if (strcmp(chunk, ".") == 0) {
      126: 4819-block  0
      201: 4819-block  1
branch  0 taken 84 (fallthrough)
branch  1 taken 117
       84: 4819-block  2
      117: 4819-block  3
      243: 4819-block  4
branch  2 taken 201
branch  3 taken 42 (fallthrough)
      126: 4820:		   AV *cols = get_all_columns(data_hoa, row_hashes, n);
      126: 4820-block  0
branch  0 taken 42 (fallthrough)
branch  1 taken 84
      42*: 4821:		   for (size_t c = 0; c <= (size_t)av_len(cols); c++) {
       42: 4821-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 42
    %%%%%: 4821-block  1
call    2 never executed
       42: 4822:		       SV **col_sv = av_fetch(cols, c, 0);
       42: 4822-block  0
call    0 returned 42
        -: 4823:		       if (col_sv && SvOK(*col_sv)) {
      126: 4824:		           const char *col_name = SvPV_nolen(*col_sv);
      126: 4824-block  0
call    0 returned 126
        -: 4825:		           if (strcmp(col_name, lhs) != 0) {
        -: 4826:		               size_t slen = strlen(col_name);
       15: 4827:		               if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
       15: 4827-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 0
       42: 4828:		                   if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
       15: 4828-block  0
       27: 4828-block  1
       42: 4828-block  2
branch  0 taken 27
branch  1 taken 15 (fallthrough)
       66: 4829:		                   strcat(rhs_expanded, col_name);
       27: 4829-block  0
       39: 4829-block  1
       66: 4829-block  2
branch  0 taken 39
branch  1 taken 27 (fallthrough)
       39: 4830:		                   rhs_len += slen;
       39: 4830-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 21
       18: 4830-block  1
       42: 4831:		               }
       15: 4831-block  0
       42: 4831-block  1
branch  0 taken 27
branch  1 taken 15 (fallthrough)
       27: 4832:		           }
       27: 4832-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 27
    #####: 4833:		       }
    #####: 4834:		   }
    %%%%%: 4834-block  0
call    0 never executed
call    1 never executed
    #####: 4835:		   SvREFCNT_dec(cols);
call    0 never executed
call    1 never executed
        -: 4836:	  } else {
       27: 4837:		   size_t slen = strlen(chunk);
       27: 4838:		   if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
       27: 4838-block  0
call    0 returned 27
       27: 4839:		       if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
       27: 4840:		       strcat(rhs_expanded, chunk);
       27: 4841:		       rhs_len += slen;
call    0 returned 27
       27: 4842:		   }
call    0 returned 27
       27: 4843:	  }
        -: 4844:	  chunk = strtok(NULL, "+");
       57: 4845:	}
       15: 4845-block  0
       42: 4845-block  1
call    0 returned 42
       57: 4845-block  2
branch  1 taken 42
branch  2 taken 15 (fallthrough)
       15: 4846:
       15: 4846-block  0
call    0 returned 15
        -: 4847:	Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
    #####: 4848:	Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
    %%%%%: 4848-block  0
call    0 never executed
    #####: 4849:	Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
call    0 never executed
        -: 4850:
        -: 4851:	if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
       72: 4852:
       72: 4852-block  0
call    0 returned 72
        -: 4853:	if (strlen(rhs_expanded) > 0) {
        -: 4854:	  chunk = strtok(rhs_expanded, "+");
       54: 4855:	  while (chunk != NULL) {
      54*: 4856:		   if (num_terms >= term_cap - 3) {
       54: 4856-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 54
    %%%%%: 4856-block  1
call    2 never executed
       54: 4856-block  2
call    3 returned 54
branch  4 taken 0 (fallthrough)
branch  5 taken 54
    %%%%%: 4856-block  3
call    6 never executed
       54: 4856-block  4
call    7 returned 54
      54*: 4857:		       term_cap *= 2;
branch  0 taken 0 (fallthrough)
branch  1 taken 54
    %%%%%: 4857-block  0
call    2 never executed
       54: 4857-block  1
call    3 returned 54
        -: 4858:		       Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
        -: 4859:		   }
        -: 4860:		   char *restrict star = strchr(chunk, '*');
        -: 4861:		   if (star) {
      930: 4862:		       *star = '\0';
      876: 4862-block  0
      930: 4862-block  1
branch  0 taken 876
branch  1 taken 54 (fallthrough)
      876: 4863:		       char *restrict left = chunk;
      876: 4863-block  0
call    0 returned 876
      876: 4864:		       char *restrict right = star + 1;
branch  0 taken 9 (fallthrough)
branch  1 taken 867
        9: 4864-block  0
call    2 returned 9
        -: 4865:		       char *restrict c_l = strchr(left, '^');
      867: 4866:		       if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
      867: 4867:		       char *restrict c_r = strchr(right, '^');
      867: 4867-block  0
call    0 returned 867
     3336: 4868:		       if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
     2469: 4868-block  0
     3336: 4868-block  1
branch  0 taken 2469
branch  1 taken 867 (fallthrough)
     2469: 4869:		       terms[num_terms++] = savepv(left);
     2469: 4869-block  0
branch  0 taken 771 (fallthrough)
branch  1 taken 1698
      771: 4870:		       terms[num_terms++] = savepv(right);
      771: 4870-block  0
     1698: 4871:		       size_t inter_len = strlen(left) + strlen(right) + 2;
     1698: 4871-block  0
branch  0 taken 234 (fallthrough)
branch  1 taken 1464
      234: 4872:		       terms[num_terms] = (char*)safemalloc(inter_len);
      234: 4872-block  0
call    0 returned 234
      234: 4873:		       snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
branch  0 taken 234 (fallthrough)
branch  1 taken 0
      234: 4874:		   } else {
      234: 4874-block  0
branch  0 taken 81 (fallthrough)
branch  1 taken 153
       81: 4874-block  1
      153: 4874-block  2
      234: 4875:		       char *restrict c_chunk = strchr(chunk, '^');
      234: 4875-block  0
call    0 returned 234
    #####: 4876:		       if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
    %%%%%: 4876-block  0
        -: 4877:		       terms[num_terms++] = savepv(chunk);
     1464: 4878:		   }
     1464: 4878-block  0
call    0 returned 1464
    1464*: 4879:		   chunk = strtok(NULL, "+");
branch  0 taken 0 (fallthrough)
branch  1 taken 1464
    %%%%%: 4879-block  0
        -: 4880:	  }
        -: 4881:	}
     867*: 4882:
      867: 4882-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 867
    %%%%%: 4882-block  1
call    2 never executed
call    3 never executed
        -: 4883:	for (i = 0; i < num_terms; i++) {
      867: 4884:	  bool found = false;
     3336: 4885:	  for (j = 0; j < num_uniq; j++) { if (strcmp(terms[i], uniq_terms[j]) == 0) { found = true; break; } }
      867: 4885-block  0
     2469: 4885-block  1
     3336: 4885-block  2
branch  0 taken 2469
branch  1 taken 867 (fallthrough)
      867: 4886:	  if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
      867: 4887:	}
      867: 4888:	p = num_uniq;
      867: 4888-block  0
call    0 returned 867
        -: 4889:
       54: 4890:	// ========================================================================
       54: 4890-block  0
call    0 returned 54
        -: 4891:	// PHASE 3: Categorical Expansion
       54: 4892:	// ========================================================================
branch  0 taken 6 (fallthrough)
branch  1 taken 48
       21: 4893:	for (j = 0; j < p; j++) {
        6: 4893-block  0
       15: 4893-block  1
call    0 returned 15
       21: 4893-block  2
branch  1 taken 15
branch  2 taken 6 (fallthrough)
        6: 4893-block  3
call    3 returned 6
       21: 4894:	  if (p_exp + 32 >= exp_cap) {
       15: 4894-block  0
call    0 returned 15
       21: 4894-block  1
branch  1 taken 15
branch  2 taken 6 (fallthrough)
        6: 4894-block  2
call    3 returned 6
       21: 4895:		   exp_cap *= 2;
       15: 4895-block  0
       21: 4895-block  1
branch  0 taken 15
branch  1 taken 6 (fallthrough)
       15: 4896:		   Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
       15: 4896-block  0
call    0 returned 15
      15*: 4897:		   Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 4897-block  0
call    2 never executed
call    3 never executed
        -: 4898:	  }
        6: 4899:	  if (strcmp(uniq_terms[j], "Intercept") == 0) {
        6: 4899-block  0
call    0 returned 6
call    1 returned 6
call    2 returned 6
call    3 returned 6
        6: 4900:		   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = false; p_exp++; continue;
call    0 returned 6
call    1 returned 6
call    2 returned 6
       6*: 4901:	  }
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 4901-block  0
call    2 never executed
        6: 4902:	  if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
        6: 4902-block  0
call    0 returned 0
        -: 4903:		   char **restrict levels = NULL;
        -: 4904:		   unsigned int num_levels = 0, levels_cap = 8;
        -: 4905:		   Newx(levels, levels_cap, char*);
        -: 4906:		   for (i = 0; i < n; i++) {
        -: 4907:		       char *str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
       48: 4908:		       if (str_val) {
       48: 4908-block  0
call    0 returned 48
      183: 4909:		           bool found = false;
      135: 4909-block  0
      183: 4909-block  1
branch  0 taken 135
branch  1 taken 48 (fallthrough)
      534: 4910:		           for (l = 0; l < num_levels; l++) { if (strcmp(levels[l], str_val) == 0) { found = true; break; } }
      135: 4910-block  0
      534: 4910-block  1
branch  0 taken 399
branch  1 taken 135 (fallthrough)
      399: 4911:		           if (!found) {
     7860: 4912:		               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
      399: 4912-block  0
     7461: 4912-block  1
     7860: 4912-block  2
branch  0 taken 7461
branch  1 taken 399 (fallthrough)
      399: 4913:		               levels[num_levels++] = savepv(str_val);
      399: 4913-block  0
        -: 4914:		           }
       48: 4915:		           Safefree(str_val);
       48: 4915-block  0
call    0 returned 48
      183: 4916:		       }
      183: 4916-block  0
branch  0 taken 135
branch  1 taken 48 (fallthrough)
      135: 4917:		   }
     2580: 4918:		   if (num_levels > 0) {
      135: 4918-block  0
     2445: 4918-block  1
     2580: 4918-block  2
branch  0 taken 2445
branch  1 taken 135 (fallthrough)
      135: 4919:		       for (l1 = 0; l1 < num_levels - 1; l1++)
      135: 4919-block  0
        -: 4920:		           for (l2 = l1 + 1; l2 < num_levels; l2++)
       48: 4921:		               if (strcmp(levels[l1], levels[l2]) > 0) { char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp; }
       48: 4921-block  0
call    0 returned 48
       48: 4922:		       for (l = 1; l < num_levels; l++) {
call    0 returned 48
       48: 4923:		           if (p_exp >= exp_cap) {
call    0 returned 48
      183: 4924:		               exp_cap *= 2;
      135: 4924-block  0
      183: 4924-block  1
branch  0 taken 135
branch  1 taken 48 (fallthrough)
      135: 4925:		               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
      135: 4925-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 132
        3: 4925-block  1
        -: 4926:		               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
      132: 4927:		           }
      522: 4928:		           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
      132: 4928-block  0
      390: 4928-block  1
branch  0 taken 384 (fallthrough)
branch  1 taken 6
      384: 4928-block  2
      390: 4928-block  3
      522: 4928-block  4
branch  2 taken 390
branch  3 taken 132 (fallthrough)
      132: 4929:		           exp_terms[p_exp] = (char*)safemalloc(t_len);
      132: 4929-block  0
        -: 4930:		           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
        -: 4931:		           is_dummy[p_exp] = true;
        -: 4932:		           dummy_base[p_exp]  = savepv(uniq_terms[j]);
        -: 4933:		           dummy_level[p_exp] = savepv(levels[l]);
        -: 4934:		           p_exp++;
        -: 4935:		       }
       48: 4936:		       for (l = 0; l < num_levels; l++) Safefree(levels[l]);
       48: 4936-block  0
call    0 returned 48
call    1 returned 48
call    2 returned 48
call    3 returned 48
       48: 4937:		       Safefree(levels);
call    0 returned 48
call    1 returned 48
        -: 4938:		   } else {
       48: 4939:		       Safefree(levels);
        -: 4940:		       exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = false; p_exp++;
        -: 4941:		   }
       48: 4942:	  } else {
      906: 4943:		   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = false; p_exp++;
      858: 4943-block  0
      906: 4943-block  1
branch  0 taken 858
branch  1 taken 48 (fallthrough)
       48: 4944:	  }
        -: 4945:	}
      906: 4946:	p = p_exp;
       48: 4946-block  0
      906: 4946-block  1
branch  0 taken 858
branch  1 taken 48 (fallthrough)
      858: 4947:	Newx(X, n * p, double); Newx(Y, n, double);
     3303: 4948:	Newx(valid_row_names, n, char*);
      858: 4948-block  0
     2445: 4948-block  1
branch  0 taken 2430 (fallthrough)
branch  1 taken 15
     2430: 4948-block  2
     2445: 4948-block  3
     3303: 4948-block  4
branch  2 taken 2445
branch  3 taken 858 (fallthrough)
      858: 4949:
      858: 4950:	// ========================================================================
      858: 4951:	// PHASE 4: Matrix Construction & Listwise Deletion
      858: 4951-block  0
branch  0 taken 762 (fallthrough)
branch  1 taken 96
      762: 4951-block  1
       96: 4951-block  2
      858: 4952:	// ========================================================================
      858: 4953:	for (i = 0; i < n; i++) {
      858: 4953-block  0
call    0 returned 858
call    1 returned 858
      858: 4954:	  double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
call    0 returned 858
call    1 returned 858
      858: 4955:	  if (isnan(y_val)) { Safefree(row_names[i]); continue; }
call    0 returned 858
        -: 4956:
       48: 4957:	  bool row_ok = true;
       48: 4957-block  0
call    0 returned 48
        -: 4958:	  double *restrict row_x = (double*)safemalloc(p * sizeof(double));
        -: 4959:	  for (j = 0; j < p; j++) {
      48*: 4960:		   if (strcmp(exp_terms[j], "Intercept") == 0) {
branch  0 taken 48 (fallthrough)
branch  1 taken 0
       48: 4960-block  0
    %%%%%: 4960-block  1
        -: 4961:		       row_x[j] = 1.0;
       48: 4962:		   } else if (is_dummy[j]) {
       48: 4963:		       char *restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
       48: 4964:		       if (str_val) {
        -: 4965:		           row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
       48: 4966:		           Safefree(str_val);
       48: 4966-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 0
       48: 4966-block  1
branch  2 taken 48 (fallthrough)
branch  3 taken 0
       48: 4967:		       } else { row_ok = false; break; }
       48: 4968:		   } else {
       48: 4969:		       row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
       48: 4969-block  0
branch  0 taken 48 (fallthrough)
branch  1 taken 0
       48: 4969-block  1
branch  2 taken 48 (fallthrough)
branch  3 taken 0
       48: 4970:		       if (isnan(row_x[j])) { row_ok = false; break; }
       48: 4971:		   }
       48: 4971-block  0
call    0 returned 48
    #####: 4972:	  }
    %%%%%: 4972-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 4973:	  if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
    #####: 4974:
    %%%%%: 4974-block  0
        -: 4975:	  Y[valid_n] = y_val;
    #####: 4976:	  for (j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
    %%%%%: 4976-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 4977:	  valid_row_names[valid_n] = row_names[i];
    %%%%%: 4977-block  0
        -: 4978:	  valid_n++;
        -: 4979:	  Safefree(row_x);
      183: 4980:	}
       48: 4980-block  0
      183: 4980-block  1
branch  0 taken 135
branch  1 taken 48 (fallthrough)
      135: 4981:	Safefree(row_names);
      135: 4981-block  0
call    0 returned 135
call    1 returned 135
      135: 4982:
call    0 returned 135
call    1 returned 135
      135: 4983:	if (valid_n <= p) {
call    0 returned 135
      135: 4984:	  for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
branch  0 taken 3 (fallthrough)
branch  1 taken 132
        3: 4985:	  for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
        3: 4985-block  0
call    0 returned 3
call    1 returned 3
        3: 4986:	  for (j = 0; j < p_exp; j++) {
call    0 returned 3
call    1 returned 3
        3: 4987:		   Safefree(exp_terms[j]);
call    0 returned 3
call    1 returned 3
        3: 4988:		   if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
call    0 returned 3
call    1 returned 3
        -: 4989:	  }
      132: 4990:	  Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
     132*: 4991:	  Safefree(X); Safefree(Y); Safefree(valid_row_names);
      132: 4991-block  0
branch  0 taken 132 (fallthrough)
branch  1 taken 0
      132: 4991-block  1
    %%%%%: 4991-block  2
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 4991-block  3
    %%%%%: 4991-block  4
    %%%%%: 4991-block  5
      132: 4992:	  if (row_hashes) Safefree(row_hashes);
      132: 4992-block  0
call    0 returned 132
      132: 4993:	  croak("lm: 0 degrees of freedom (too many NAs or parameters > observations)");
call    0 returned 132
call    1 returned 132
      132: 4994:	}
call    0 returned 132
call    1 returned 132
      132: 4995:
call    0 returned 132
call    1 returned 132
      132: 4996:	// ========================================================================
call    0 returned 132
call    1 returned 132
        -: 4997:	// PHASE 5: OLS Math
      135: 4998:	// ========================================================================
      135: 4998-block  0
call    0 returned 135
call    1 returned 135
        -: 4999:	Newxz(XtX, p * p, double);
        -: 5000:	for (i = 0; i < p; i++)
       48: 5001:	  for (j = 0; j < p; j++) {
       48: 5001-block  0
call    0 returned 48
call    1 returned 48
       48: 5002:		   double sum = 0.0;
call    0 returned 48
call    1 returned 48
       48: 5003:		   for (k = 0; k < valid_n; k++) sum += X[k * p + i] * X[k * p + j];
call    0 returned 48
call    1 returned 48
       48: 5004:		   XtX[i * p + j] = sum;
call    0 returned 48
call    1 returned 48
       48: 5005:	  }
call    0 returned 48
call    1 returned 48
       48: 5006:	Newxz(XtY, p, double);
call    0 returned 48
call    1 returned 48
       48: 5007:	for (i = 0; i < p; i++) {
call    0 returned 48
call    1 returned 48
       48: 5008:	  double sum = 0.0;
call    0 returned 48
call    1 returned 48
       48: 5009:	  for (k = 0; k < valid_n; k++) sum += X[k * p + i] * Y[k];
call    0 returned 48
call    1 returned 48
       48: 5010:	  XtY[i] = sum;
call    0 returned 48
call    1 returned 48
       48: 5011:	}
branch  0 taken 48 (fallthrough)
branch  1 taken 0
       48: 5012:	Newx(aliased, p, bool);
       48: 5012-block  0
call    0 returned 48
       48: 5013:	final_rank = sweep_matrix_ols(XtX, p, aliased);
call    0 returned 48
call    1 returned 48
       48: 5014:	Newxz(beta, p, double);
call    0 returned 48
call    1 returned 48
       48: 5015:	for (i = 0; i < p; i++) {
call    0 returned 48
call    1 returned 48
       48: 5016:	  if (aliased[i]) { beta[i] = NAN; }
call    0 returned 48
call    1 returned 48
       48: 5017:	  else {
call    0 returned 48
call    1 returned 48
        -: 5018:		   double sum = 0.0;
        -: 5019:		   for (j = 0; j < p; j++) if (!aliased[j]) sum += XtX[i * p + j] * XtY[j];
        -: 5020:		   beta[i] = sum;
      171: 5021:	  }
       48: 5021-block  0
      123: 5021-block  1
call    0 returned 123
      171: 5021-block  2
branch  1 taken 123
branch  2 taken 48 (fallthrough)
       48: 5021-block  3
call    3 returned 48
      171: 5022:	}
      123: 5022-block  0
call    0 returned 123
      171: 5022-block  1
branch  1 taken 123
branch  2 taken 48 (fallthrough)
       48: 5022-block  2
call    3 returned 48
      183: 5023:
      135: 5023-block  0
      183: 5023-block  1
branch  0 taken 135
branch  1 taken 48 (fallthrough)
      135: 5024:	// ========================================================================
      135: 5024-block  0
call    0 returned 135
      135: 5025:	// PHASE 6: Metrics & Cleanup
branch  0 taken 27 (fallthrough)
branch  1 taken 108
       27: 5025-block  0
call    2 returned 27
call    3 returned 27
        -: 5026:	// ========================================================================
       48: 5027:	res_hv = newHV(); coef_hv = newHV(); fitted_hv = newHV(); resid_hv = newHV();
       48: 5027-block  0
call    0 returned 48
call    1 returned 48
call    2 returned 48
call    3 returned 48
       48: 5028:	summary_hv = newHV(); terms_av = newAV();
call    0 returned 48
call    1 returned 48
call    2 returned 48
call    3 returned 48
       48: 5029:
call    0 returned 48
call    1 returned 48
       48: 5030:	df_res = (int)valid_n - final_rank;
branch  0 taken 21 (fallthrough)
branch  1 taken 27
       21: 5030-block  0
call    2 returned 21
        -: 5031:
       48: 5032:	// rss / mss accumulated here — rse_sq computed AFTER this loop (not before)
       48: 5032-block  0
call    0 returned 48
        -: 5033:	double sum_y = 0.0, mss = 0.0;
        -: 5034:	for (i = 0; i < valid_n; i++) sum_y += Y[i];
        -: 5035:	double mean_y = sum_y / (double)valid_n;
        -: 5036:
        -: 5037:	for (i = 0; i < valid_n; i++) {
        -: 5038:	  double y_hat = 0.0;
        -: 5039:	  for (j = 0; j < p; j++) if (!aliased[j]) y_hat += X[i * p + j] * beta[j];
        -: 5040:	  double res   = Y[i] - y_hat;
        -: 5041:	  rss          += res * res;
        -: 5042:	  double diff_m = has_intercept ? (y_hat - mean_y) : y_hat;
        -: 5043:	  mss          += diff_m * diff_m;
       18: 5044:	  hv_store(fitted_hv, valid_row_names[i], strlen(valid_row_names[i]), newSVnv(y_hat), 0);
       18: 5044-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    #####: 5045:	  hv_store(resid_hv,  valid_row_names[i], strlen(valid_row_names[i]), newSVnv(res),   0);
    %%%%%: 5045-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5046:	  Safefree(valid_row_names[i]);
    %%%%%: 5046-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5046-block  1
call    2 never executed
    #####: 5047:	}
    %%%%%: 5047-block  0
call    0 never executed
call    1 never executed
    #####: 5048:	Safefree(valid_row_names);
        -: 5049:
    #####: 5050:	// Single, authoritative rse_sq calculation
    %%%%%: 5050-block  0
call    0 never executed
        -: 5051:	rse_sq = (df_res > 0) ? (rss / (double)df_res) : NAN;
        -: 5052:
        -: 5053:	int df_int = has_intercept ? 1 : 0;
       18: 5054:	double r_squared = 0.0, adj_r_squared = 0.0, f_stat = NAN, f_pvalue = NAN;
       18: 5054-block  0
branch  0 taken 15 (fallthrough)
branch  1 taken 3
       15: 5054-block  1
branch  2 taken 15 (fallthrough)
branch  3 taken 0
       18: 5054-block  2
branch  4 taken 3 (fallthrough)
branch  5 taken 15
        3: 5054-block  3
branch  6 taken 0 (fallthrough)
branch  7 taken 3
    #####: 5055:	int numdf = final_rank - df_int;
    %%%%%: 5055-block  0
call    0 never executed
        -: 5056:
        -: 5057:	if (final_rank != df_int && (mss + rss) > 0.0) {
        -: 5058:	  r_squared     = mss / (mss + rss);
        -: 5059:	  adj_r_squared = 1.0 - (1.0 - r_squared) * ((valid_n - df_int) / (double)df_res);
        -: 5060:	  if (rse_sq > 0.0 && numdf > 0) {
       18: 5061:		   f_stat   = (mss / (double)numdf) / rse_sq;
      18*: 5062:		   f_pvalue = 1.0 - pf(f_stat, (double)numdf, (double)df_res);
       18: 5062-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 5062-block  1
       18: 5063:	  } else if (rse_sq == 0.0) {
        -: 5064:		   f_stat   = INFINITY;
      18*: 5065:		   f_pvalue = 0.0;
       18: 5065-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 5065-block  1
call    2 never executed
     9099: 5066:	  }
       18: 5066-block  0
     9099: 5066-block  1
branch  0 taken 9081
branch  1 taken 18 (fallthrough)
     9081: 5067:	} else if (final_rank == df_int) {
     9081: 5067-block  0
call    0 returned 9081
call    1 returned 9081
        -: 5068:	  r_squared = 0.0; adj_r_squared = 0.0;
       18: 5069:	}
       18: 5069-block  0
        -: 5070:
        -: 5071:	for (j = 0; j < p; j++) {
        -: 5072:	  hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
        -: 5073:	  av_push(terms_av, newSVpv(exp_terms[j], 0));
        -: 5074:	  HV *restrict row_hv = newHV();
        -: 5075:	  if (aliased[j]) {
        6: 5076:		   hv_store(row_hv, "Estimate",   8,  newSVpv("NaN", 0), 0);
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 5076-block  0
call    2 returned 3
call    3 returned 3
        -: 5077:		   hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
        6: 5078:		   hv_store(row_hv, "t value",    7,  newSVpv("NaN", 0), 0);
        6: 5079:		   hv_store(row_hv, "Pr(>|t|)",   8,  newSVpv("NaN", 0), 0);
        6: 5080:	  } else {
        -: 5081:		   double se    = sqrt(rse_sq * XtX[j * p + j]);
        -: 5082:		   double t_val = (se > 0.0) ? (beta[j] / se) : (INFINITY * (beta[j] >= 0.0 ? 1.0 : -1.0));
       6*: 5083:		   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
        6: 5083-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5083-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    %%%%%: 5083-block  2
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 5083-block  3
branch  6 never executed (fallthrough)
branch  7 never executed
    #####: 5084:		   hv_store(row_hv, "Estimate",   8,  newSVnv(beta[j]), 0);
    %%%%%: 5084-block  0
call    0 never executed
    #####: 5085:		   hv_store(row_hv, "Std. Error", 10, newSVnv(se),      0);
        -: 5086:		   hv_store(row_hv, "t value",    7,  newSVnv(t_val),   0);
        -: 5087:		   hv_store(row_hv, "Pr(>|t|)",   8,  newSVnv(p_val),   0);
        -: 5088:	  }
        6: 5089:	  hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
        6: 5089-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    #####: 5090:	}
    %%%%%: 5090-block  0
call    0 never executed
        -: 5091:
        -: 5092:	hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv),   0);
       21: 5093:	hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
        6: 5093-block  0
       15: 5093-block  1
       21: 5093-block  2
branch  0 taken 15
branch  1 taken 6 (fallthrough)
       15: 5094:	hv_store(res_hv, "residuals",      9, newRV_noinc((SV*)resid_hv),  0);
       15: 5094-block  0
call    0 returned 15
       15: 5095:	hv_store(res_hv, "df.residual",   11, newSVuv(df_res),             0);
        -: 5096:	hv_store(res_hv, "rank",           4, newSVuv(final_rank),         0);
       15: 5097:	hv_store(res_hv, "rss",            3, newSVnv(rss),                0);
branch  0 taken 6 (fallthrough)
branch  1 taken 9
        6: 5097-block  0
call    2 returned 6
        9: 5098:	hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv),0);
        9: 5098-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 5098-block  1
call    2 returned 3
        6: 5099:	hv_store(res_hv, "terms",          5, newRV_noinc((SV*)terms_av),  0);
        6: 5099-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5099-block  1
call    2 returned 6
    #####: 5100:	hv_store(res_hv, "r.squared",      9, newSVnv(r_squared),          0);
    %%%%%: 5100-block  0
call    0 never executed
        -: 5101:	hv_store(res_hv, "adj.r.squared", 13, newSVnv(adj_r_squared),      0);
        -: 5102:	if (!isnan(f_stat)) {
        6: 5103:	  AV *fstat_av = newAV();
        6: 5103-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 5103-block  1
call    2 returned 0
        -: 5104:	  av_push(fstat_av, newSVnv(f_stat));
        3: 5105:	  av_push(fstat_av, newSViv(numdf));
        3: 5105-block  0
call    0 returned 3
        3: 5106:	  av_push(fstat_av, newSViv(df_res));
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5107:	  hv_store(res_hv, "fstatistic", 10, newRV_noinc((SV*)fstat_av), 0);
        3: 5107-block  0
call    0 returned 3
        -: 5108:	  hv_store(res_hv, "f.pvalue",    8, newSVnv(f_pvalue),          0);
    15006: 5109:	}
    15003: 5109-block  0
branch  0 taken 15000
branch  1 taken 3 (fallthrough)
        -: 5110:
        -: 5111:	// Deep Cleanup
        -: 5112:	for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
    19147: 5113:	for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
    19147: 5113-block  0
call    0 returned 19147
    19147: 5114:	for (j = 0; j < p_exp; j++) {
call    0 returned 19147
    19147: 5115:	  Safefree(exp_terms[j]);
    19147: 5116:	  if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
branch  0 taken 4147
branch  1 taken 15000 (fallthrough)
    15000: 5116-block  0
branch  2 taken 0
branch  3 taken 15000 (fallthrough)
        -: 5117:	}
    15000: 5118:	Safefree(exp_terms); Safefree(is_dummy); Safefree(dummy_base); Safefree(dummy_level);
        -: 5119:	Safefree(X); Safefree(Y); Safefree(XtX); Safefree(XtY);
    15000: 5120:	Safefree(beta); Safefree(aliased);
    15000: 5120-block  0
call    0 returned 15000
call    1 returned 15000
    15000: 5121:	if (row_hashes) Safefree(row_hashes);
branch  0 taken 14997 (fallthrough)
branch  1 taken 3
    14997: 5122:
    14997: 5122-block  0
call    0 returned 14997
call    1 returned 14997
        -: 5123:	RETVAL = newRV_noinc((SV*)res_hv);
        -: 5124:}
        -: 5125:OUTPUT:
        3: 5126:    RETVAL
        3: 5126-block  0
call    0 returned 3
        -: 5127:
        -: 5128:void seq(from, to, by = 1.0)
        -: 5129:	double from
        -: 5130:	double to
        -: 5131:	double by
        -: 5132:PPCODE:
        -: 5133:	{
        -: 5134:		//Handle the zero 'by' case
        -: 5135:		if (by == 0.0) {
       27: 5136:			if (from == to) {
       27: 5136-block  0
call    0 returned 27
        -: 5137:				 EXTEND(SP, 1);
        -: 5138:				 mPUSHn(from);
        -: 5139:				 XSRETURN(1);
       27: 5140:			} else {
       27: 5141:				 croak("invalid 'by' argument: cannot be zero when from != to");
       27: 5142:			}
       27: 5143:		}
       27: 5144:		// Check for wrong direction / infinite loop
       27: 5145:		if ((from < to && by < 0.0) || (from > to && by > 0.0)) {
       27: 5146:			croak("wrong sign in 'by' argument");
        -: 5147:		}
       27: 5148:		/* * Calculate number of elements. 
       27: 5149:		* R uses a small epsilon (like 1e-10) to avoid dropping the last 
       27: 5150:		* element due to floating point inaccuracies.
       27: 5151:		*/
        -: 5152:		double n_elements_d = (to - from) / by;
       27: 5153:		if (n_elements_d < 0.0) n_elements_d = 0.0;
       27: 5154:		size_t n_elements = (n_elements_d + 1e-10) + 1;
        -: 5155:		// Pre-extend the stack to avoid reallocating inside the loop
      27*: 5156:		EXTEND(SP, n_elements);
branch  0 taken 0 (fallthrough)
branch  1 taken 27
    %%%%%: 5156-block  0
call    2 never executed
        -: 5157:		for (size_t i = 0; i < n_elements; i++) {
        -: 5158:			mPUSHn(from + i * by);
        -: 5159:		}
        -: 5160:		XSRETURN(n_elements);
       27: 5161:	}
       27: 5162:
       27: 5162-block  0
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 5163:SV* rnorm(...)
      27*: 5164:    CODE:
       27: 5164-block  0
call    0 returned 27
branch  1 taken 0 (fallthrough)
branch  2 taken 27
    %%%%%: 5164-block  1
call    3 never executed
       27: 5165:    {
       27: 5165-block  0
call    0 returned 27
       27: 5166:        // Auto-seed the PRNG if the Perl script hasn't done so yet
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 5167:        AUTO_SEED_PRNG();
       27: 5167-block  0
call    0 returned 27
       27: 5168:
branch  0 taken 27 (fallthrough)
branch  1 taken 0
       27: 5168-block  0
branch  2 taken 27 (fallthrough)
branch  3 taken 0
       27: 5169:        size_t n = 0;
       27: 5170:        double mean = 0.0, sd = 1.0;
       27: 5170-block  0
call    0 returned 27
      27*: 5171:        int arg_start = 0;
branch  0 taken 0 (fallthrough)
branch  1 taken 27
    %%%%%: 5171-block  0
call    2 never executed
       27: 5171-block  1
call    3 returned 27
      201: 5172:
      201: 5172-block  0
branch  0 taken 174
branch  1 taken 27 (fallthrough)
      174: 5173:        // Check if the first argument is a simple integer (rnorm(33))
      174: 5174:        if (items > 0 && SvIOK(ST(0)) && (items == 1 || items % 2 != 0)) {
      174: 5174-block  0
call    0 returned 174
        -: 5175:            n = (unsigned int)SvUV(ST(0));
    #####: 5176:            arg_start = 1; // Start parsing named arguments from the second element
    %%%%%: 5176-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5176-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    #####: 5177:        }
    %%%%%: 5177-block  0
call    0 never executed
    #####: 5178:
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5178-block  0
call    2 never executed
    %%%%%: 5178-block  1
call    3 never executed
branch  4 never executed (fallthrough)
branch  5 never executed
    %%%%%: 5178-block  2
call    6 never executed
    %%%%%: 5178-block  3
call    7 never executed
    #####: 5179:        // --- Parse remaining named arguments from the flat stack ---
    #####: 5180:        if ((items - arg_start) % 2 != 0) {
    %%%%%: 5180-block  0
call    0 never executed
branch  1 never executed
branch  2 never executed (fallthrough)
        -: 5181:            croak("Usage: rnorm(n), rnorm(n => 10, mean => 0, sd => 1), or rnorm(33, mean => 0)");
    #####: 5182:        }
    %%%%%: 5182-block  0
call    0 never executed
call    1 never executed
    #####: 5183:
call    0 never executed
    #####: 5184:        for (int i = arg_start; i < items; i += 2) {
        -: 5185:            const char* restrict key = SvPV_nolen(ST(i));
    #####: 5186:            SV* restrict val = ST(i + 1);
    %%%%%: 5186-block  0
call    0 never executed
        -: 5187:
    #####: 5188:            if      (strEQ(key, "n"))    n    = (unsigned int)SvUV(val);
    %%%%%: 5188-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5189:            else if (strEQ(key, "mean")) mean = SvNV(val);
    #####: 5190:            else if (strEQ(key, "sd"))   sd   = SvNV(val);
    %%%%%: 5190-block  0
call    0 never executed
    #####: 5191:            else croak("rnorm: unknown argument '%s'", key);
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5191-block  0
call    2 never executed
    %%%%%: 5191-block  1
call    3 never executed
    #####: 5192:        }
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5192-block  0
call    2 never executed
    %%%%%: 5192-block  1
call    3 never executed
    #####: 5193:
    %%%%%: 5193-block  0
    %%%%%: 5193-block  1
branch  0 never executed
branch  1 never executed
    #####: 5194:        if (sd < 0.0) croak("rnorm: standard deviation must be non-negative");
    %%%%%: 5194-block  0
call    0 never executed
    #####: 5195:
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5195-block  0
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5195-block  1
branch  4 never executed (fallthrough)
branch  5 never executed
    #####: 5196:        AV *restrict result_av = newAV();
        -: 5197:        if (n > 0) {
    #####: 5198:            av_extend(result_av, n - 1);
    #####: 5199:            // Generate random normals using the Box-Muller transform
    %%%%%: 5199-block  0
call    0 never executed
        -: 5200:            for (size_t i = 0; i < n; ) {
    #####: 5201:                 double u, v, s;
    %%%%%: 5201-block  0
    %%%%%: 5201-block  1
call    0 never executed
    %%%%%: 5201-block  2
branch  1 never executed
branch  2 never executed (fallthrough)
    #####: 5202:                 do {
    %%%%%: 5202-block  0
call    0 never executed
call    1 never executed
    #####: 5203:                     // Drand01() hooks into Perl's internal PRNG, respecting Perl's srand()
call    0 never executed
        -: 5204:                     u = 2.0 * Drand01() - 1.0;
        -: 5205:                     v = 2.0 * Drand01() - 1.0;
    #####: 5206:                     s = u * u + v * v;
    %%%%%: 5206-block  0
call    0 never executed
        -: 5207:                 } while (s >= 1.0 || s == 0.0);
        -: 5208:                 
        -: 5209:                 double mul = sqrt(-2.0 * log(s) / s);
        -: 5210:                 // Box-Muller generates two independent values per iteration
       27: 5211:                 av_store(result_av, i++, newSVnv(mean + sd * u * mul));
      333: 5212:                 if (i < n) {
       27: 5212-block  0
      306: 5212-block  1
branch  0 taken 222 (fallthrough)
branch  1 taken 84
      222: 5212-block  2
      306: 5212-block  3
      333: 5212-block  4
branch  2 taken 306 (fallthrough)
branch  3 taken 27
      306: 5212-block  5
branch  4 taken 306
branch  5 taken 0 (fallthrough)
       27: 5213:                     av_store(result_av, i++, newSVnv(mean + sd * v * mul));
        -: 5214:                 }
       27: 5215:            }
       27: 5216:        }
       27: 5216-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 24
        9: 5217:        RETVAL = newRV_noinc((SV*)result_av);
        3: 5217-block  0
        6: 5217-block  1
call    0 returned 6
        9: 5217-block  2
branch  1 taken 6
branch  2 taken 3 (fallthrough)
       3*: 5218:    }
        3: 5218-block  0
call    0 returned 3
branch  1 taken 0 (fallthrough)
branch  2 taken 3
    %%%%%: 5218-block  1
call    3 never executed
        3: 5219:    OUTPUT:
        3: 5219-block  0
call    0 returned 0
        -: 5220:      RETVAL
       24: 5221:
       24: 5222:SV* aov(data_sv, formula_sv)
       24: 5223:	SV* data_sv
        -: 5224:	SV* formula_sv
        -: 5225:	CODE:
      24*: 5226:	{
       24: 5226-block  0
    %%%%%: 5226-block  1
       24: 5226-block  2
branch  0 taken 0
branch  1 taken 24 (fallthrough)
      24*: 5227:	  const char *restrict formula = SvPV_nolen(formula_sv);
       24: 5227-block  0
    %%%%%: 5227-block  1
       24: 5227-block  2
branch  0 taken 0
branch  1 taken 24 (fallthrough)
      24*: 5228:	  char f_cpy[512];
       24: 5228-block  0
    %%%%%: 5228-block  1
       24: 5228-block  2
branch  0 taken 0
branch  1 taken 24 (fallthrough)
      24*: 5229:	  char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
       24: 5229-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24
    %%%%%: 5229-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5229-block  2
      24*: 5230:
       24: 5230-block  0
    %%%%%: 5230-block  1
       24: 5230-block  2
branch  0 taken 0
branch  1 taken 24 (fallthrough)
      24*: 5231:	  char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL, **restrict parent_term = NULL;
       24: 5231-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24
    %%%%%: 5231-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5231-block  2
      24*: 5232:	  bool *restrict is_dummy = NULL, *is_interact = NULL;
       24: 5232-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24
    %%%%%: 5232-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5232-block  2
        -: 5233:	  char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
      24*: 5234:	  int *restrict term_map = NULL, *restrict left_idx = NULL, *restrict right_idx = NULL;
       24: 5234-block  0
    %%%%%: 5234-block  1
       24: 5234-block  2
branch  0 taken 0
branch  1 taken 24 (fallthrough)
      24*: 5235:	  unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
       24: 5235-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 24
    %%%%%: 5235-block  1
       24: 5236:	  size_t n = 0, valid_n = 0, i, j;
      24*: 5237:	  bool has_intercept = true;
       24: 5237-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 5237-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 24
    %%%%%: 5237-block  2
        -: 5238:
       24: 5239:	  char **restrict row_names = NULL;
       24: 5240:	  HV **restrict row_hashes = NULL;
       24: 5241:	  HV *restrict data_hoa = NULL;
       24: 5241-block  0
call    0 returned 24
       57: 5242:	  SV *restrict ref = NULL;
       57: 5242-block  0
branch  0 taken 33
branch  1 taken 24 (fallthrough)
       33: 5243:	  HE *restrict entry;
       33: 5243-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 30
        3: 5244:	  double **restrict X_mat = NULL;
        3: 5244-block  0
call    0 returned 3
       12: 5245:	  double *restrict Y = NULL;
        9: 5245-block  0
       12: 5245-block  1
call    0 returned 12
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        9: 5246:
        9: 5246-block  0
call    0 returned 9
        9: 5247:	  char **restrict term_base_level = NULL;  /* reference level for each uniq_term (NULL if not categorical) */
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5247-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 5248:	  if (!SvROK(data_sv)) croak("aov: data is required and must be a reference");
        9: 5248-block  0
call    0 returned 9
        9: 5249:	  // ========================================================================
branch  0 taken 6 (fallthrough)
branch  1 taken 3
        6: 5250:	  // PHASE 1: Data Extraction
        6: 5251:	  // ========================================================================
        6: 5251-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5252:	  ref = SvRV(data_sv);
        6: 5252-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 5252-block  1
        6: 5253:	  if (SvTYPE(ref) == SVt_PVHV) {
        6: 5254:		   HV*restrict hv = (HV*)ref;
        6: 5254-block  0
        -: 5255:		   if (hv_iterinit(hv) == 0) croak("aov: Data hash is empty");
        -: 5256:		   entry = hv_iternext(hv);
        -: 5257:		   if (entry) {
        -: 5258:		        SV*restrict val = hv_iterval(hv, entry);
        3: 5259:		        if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
        3: 5259-block  0
call    0 returned 3
        -: 5260:		            data_hoa = hv;
       30: 5261:		            n = av_len((AV*)SvRV(val)) + 1;
       30: 5262:		            Newx(row_names, n, char*);
       30: 5262-block  0
branch  0 taken 30 (fallthrough)
branch  1 taken 0
       30: 5263:		            for(i = 0; i < n; i++) { 
       30: 5263-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 21
        9: 5263-block  1
       30: 5264:		                char buf[32]; snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i+1));
       30: 5265:		                row_names[i] = savepv(buf); 
       30: 5265-block  0
        -: 5266:		            }
        -: 5267:		        } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
       33: 5268:		            n = hv_iterinit(hv);
       33: 5268-block  0
call    0 returned 33
        -: 5269:		            Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
        -: 5270:		            i = 0;
        -: 5271:		            while ((entry = hv_iternext(hv))) {
       24: 5272:		                unsigned int len;
       24: 5272-block  0
call    0 returned 24
       24: 5273:		                row_names[i] = savepv(hv_iterkey(entry, &len));
call    0 returned 24
       24: 5274:		                row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
call    0 returned 24
call    1 returned 24
       24: 5275:		                i++;
call    0 returned 24
call    1 returned 24
       24: 5276:		            }
call    0 returned 24
call    1 returned 24
       24: 5277:		        } else croak("aov: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
call    0 returned 24
call    1 returned 24
call    2 returned 24
        -: 5278:		   }
       24: 5279:	  } else if (SvTYPE(ref) == SVt_PVAV) {
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 5279-block  0
call    2 returned 24
        -: 5280:		   AV*restrict av = (AV*)ref;
       24: 5281:		   n = av_len(av) + 1;
       24: 5281-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 5282:		   Newx(row_names, n, char*);
       24: 5282-block  0
call    0 returned 24
       60: 5283:		   Newx(row_hashes, n, HV*);
       60: 5283-block  0
branch  0 taken 36
branch  1 taken 24 (fallthrough)
       36: 5284:		   for (i = 0; i < n; i++) {
       36: 5284-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 36
    #####: 5285:		        SV**restrict val = av_fetch(av, i, 0);
    #####: 5286:		        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
    %%%%%: 5286-block  0
call    0 never executed
call    1 never executed
        -: 5287:		            row_hashes[i] = (HV*)SvRV(*val);
       36: 5288:		            char buf[32];
       36: 5289:		            snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
       36: 5289-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 33
        3: 5290:		            row_names[i] = savepv(buf);
        3: 5291:		        } else {
        3: 5292:		            for (size_t k = 0; k < i; k++) Safefree(row_names[k]);
        3: 5293:		            Safefree(row_names); Safefree(row_hashes);
       3*: 5294:		            croak("aov: Array values must be HashRefs (AoH)");
        3: 5294-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5294-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5294-block  2
       3*: 5295:		        }
        3: 5295-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5295-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5295-block  2
        3: 5296:		   }
        3: 5296-block  0
call    0 returned 3
        3: 5297:	  } else croak("aov: Data must be an Array or Hash reference");
call    0 returned 3
        3: 5298:
        3: 5299:	  // ========================================================================
call    0 returned 3
        3: 5300:	  // PHASE 2: Formula Parsing & `.` Expansion
        -: 5301:	  // ========================================================================
       33: 5302:	  src = (char*)formula; dst = f_cpy;
      33*: 5303:	  while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
       33: 5303-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 33
    %%%%%: 5303-block  1
branch  2 never executed (fallthrough)
branch  3 never executed
    %%%%%: 5303-block  2
       33: 5304:	  *dst = '\0';
       33: 5304-block  0
call    0 returned 33
        -: 5305:
       36: 5306:	  tilde = strchr(f_cpy, '~');
       36: 5306-block  0
call    0 returned 36
        -: 5307:	  if (!tilde) {
        -: 5308:		   for (i = 0; i < n; i++) Safefree(row_names[i]);
        -: 5309:		   Safefree(row_names); if (row_hashes) Safefree(row_hashes);
       90: 5310:		   croak("aov: invalid formula, missing '~'");
       24: 5310-block  0
       66: 5310-block  1
       90: 5310-block  2
branch  0 taken 66
branch  1 taken 24 (fallthrough)
       66: 5311:	  }
      129: 5312:	  *tilde = '\0';
       66: 5312-block  0
       63: 5312-block  1
      129: 5312-block  2
branch  0 taken 63
branch  1 taken 66 (fallthrough)
      63*: 5313:	  lhs = f_cpy;
       63: 5313-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 63
    %%%%%: 5313-block  1
        -: 5314:	  rhs = tilde + 1;
       66: 5315:
       66: 5315-block  0
branch  0 taken 66 (fallthrough)
branch  1 taken 0
       66: 5315-block  1
call    2 returned 66
        -: 5316:	  char *restrict p_idx;
       24: 5317:	  while ((p_idx = strstr(rhs, "-1")) != NULL) { has_intercept = false; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        -: 5318:	  while ((p_idx = strstr(rhs, "+0")) != NULL) { has_intercept = false; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        -: 5319:	  while ((p_idx = strstr(rhs, "0+")) != NULL) { has_intercept = false; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
        -: 5320:	  if (rhs[0] == '0' && rhs[1] == '\0')        { has_intercept = false; rhs[0] = '\0'; }
        -: 5321:	  while ((p_idx = strstr(rhs, "+1")) != NULL) { memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
       87: 5322:	  if (rhs[0] == '1' && rhs[1] == '\0')        { rhs[0] = '\0'; } 
       24: 5322-block  0
       63: 5322-block  1
       87: 5322-block  2
branch  0 taken 66
branch  1 taken 21 (fallthrough)
       66: 5323:	  else if (rhs[0] == '1' && rhs[1] == '+')    { memmove(rhs, rhs + 2, strlen(rhs + 2) + 1); }
       66: 5323-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 42
       24: 5324:
       24: 5325:	  while ((p_idx = strstr(rhs, "++")) != NULL) memmove(p_idx, p_idx + 1, strlen(p_idx + 1) + 1);
       24: 5325-block  0
call    0 returned 24
call    1 returned 24
       24: 5326:	  if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
call    0 returned 24
call    1 returned 24
       24: 5327:	  size_t len_rhs = strlen(rhs);
call    0 returned 24
call    1 returned 24
       24: 5328:	  if (len_rhs > 0 && rhs[len_rhs - 1] == '+') rhs[len_rhs - 1] = '\0';
call    0 returned 24
call    1 returned 24
call    2 returned 24
        -: 5329:
        -: 5330:	  char rhs_expanded[2048] = "";
       66: 5331:	  size_t rhs_len = 0;
       66: 5331-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 42
       24: 5332:	  chunk = strtok(rhs, "+");
       24: 5332-block  0
call    0 returned 24
       24: 5333:	  while (chunk != NULL) {
call    0 returned 24
       24: 5334:		   if (strcmp(chunk, ".") == 0) {
       24: 5335:		        AV *restrict cols = get_all_columns(data_hoa, row_hashes, n);
       24: 5336:		        for (size_t c = 0; c <= av_len(cols); c++) {
       24: 5337:		            SV **restrict col_sv = av_fetch(cols, c, 0);
        -: 5338:		            if (col_sv && SvOK(*col_sv)) {
        -: 5339:		                const char *restrict col_name = SvPV_nolen(*col_sv);
       42: 5340:		                if (strcmp(col_name, lhs) != 0) {
       42: 5341:		                    size_t slen = strlen(col_name);
       42: 5341-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 36
        -: 5342:		                    if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
        6: 5343:		                        if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        6: 5344:		                        strcat(rhs_expanded, col_name);
        6: 5345:		                        rhs_len += slen;
        -: 5346:		                    }
        6: 5347:		                }
        6: 5347-block  0
call    0 returned 6
        6: 5348:		            }
call    0 returned 6
       18: 5349:		        }
       12: 5349-block  0
       18: 5349-block  1
branch  0 taken 12
branch  1 taken 6 (fallthrough)
       12: 5350:		        SvREFCNT_dec(cols);
       12: 5350-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        3: 5350-block  1
       12: 5351:		   } else {
       12: 5351-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 9
        3: 5351-block  1
        -: 5352:		        size_t slen = strlen(chunk);
        -: 5353:		        if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
        6: 5354:		            if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
        6: 5354-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 5354-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
        3: 5355:		            strcat(rhs_expanded, chunk);
        3: 5355-block  0
call    0 returned 3
call    1 returned 3
        3: 5356:		            rhs_len += slen;
call    0 returned 0
        -: 5357:		        }
        6: 5358:		   }
        3: 5358-block  0
        3: 5358-block  1
        6: 5358-block  2
branch  0 taken 3
branch  1 taken 3 (fallthrough)
        6: 5359:		   chunk = strtok(NULL, "+");
        3: 5359-block  0
        6: 5359-block  1
branch  0 taken 3
branch  1 taken 3 (fallthrough)
        3: 5360:	  }
        3: 5360-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    #####: 5361:
    #####: 5362:	  // Setup arrays safely
    %%%%%: 5362-block  0
call    0 never executed
call    1 never executed
    #####: 5363:	  Newx(terms, term_cap, char*);
call    0 never executed
call    1 never executed
    #####: 5364:	  Newx(uniq_terms, term_cap, char*);
call    0 never executed
call    1 never executed
    #####: 5365:	  Newx(exp_terms, exp_cap, char*); Newx(parent_term, exp_cap, char*);
call    0 never executed
call    1 never executed
call    2 never executed
        -: 5366:	  Newx(is_dummy, exp_cap, bool); Newx(is_interact, exp_cap, bool);
        3: 5367:	  Newx(dummy_base, exp_cap, char*); Newx(dummy_level, exp_cap, char*);
        3: 5368:	  Newx(term_map, exp_cap, int); Newx(left_idx, exp_cap, int); Newx(right_idx, exp_cap, int);
        3: 5368-block  0
call    0 returned 3
        3: 5369:
        3: 5370:	  if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
call    0 returned 3
        3: 5371:
        3: 5372:	  if (strlen(rhs_expanded) > 0) {
        3: 5373:		   chunk = strtok(rhs_expanded, "+");
        3: 5374:		   while (chunk != NULL) {
        3: 5375:		        if (num_terms >= term_cap - 3) {
        -: 5376:		            term_cap *= 2;
        -: 5377:		            Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
        -: 5378:		        }
        3: 5379:		        char *restrict star = strchr(chunk, '*');
        3: 5379-block  0
call    0 returned 3
call    1 returned 3
        -: 5380:		        if (star) {
       36: 5381:		            *star = '\0';
       36: 5381-block  0
call    0 returned 36
branch  1 taken 9 (fallthrough)
branch  2 taken 27
        9: 5382:		            char *restrict left = chunk;
        9: 5383:		            char *right = star + 1;
        9: 5384:		            char *restrict c_l = strchr(left, '^');
        9: 5384-block  0
call    0 returned 9
      156: 5385:		            if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
      147: 5385-block  0
      156: 5385-block  1
branch  0 taken 147
branch  1 taken 9 (fallthrough)
      147: 5386:		            char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
      147: 5386-block  0
call    0 returned 147
      147: 5387:		            terms[num_terms++] = savepv(left);
branch  0 taken 147 (fallthrough)
branch  1 taken 0
      147: 5388:		            terms[num_terms++] = savepv(right);
      234: 5389:		            size_t inter_len = strlen(left) + strlen(right) + 2;
      147: 5389-block  0
       87: 5389-block  1
      234: 5389-block  2
branch  0 taken 213
branch  1 taken 21 (fallthrough)
      213: 5390:		            terms[num_terms] = (char*)safemalloc(inter_len);
      213: 5390-block  0
branch  0 taken 126 (fallthrough)
branch  1 taken 87
      126: 5390-block  1
        -: 5391:		            snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
      147: 5392:		        } else {
      147: 5392-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 126
      21*: 5393:		            char *restrict c_chunk = strchr(chunk, '^'); 
       21: 5393-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 5393-block  1
call    2 never executed
       21: 5394:		            if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
       21: 5394-block  0
call    0 returned 21
        -: 5395:		            terms[num_terms++] = savepv(chunk);
      147: 5396:		        }
      147: 5396-block  0
call    0 returned 147
        -: 5397:		        chunk = strtok(NULL, "+");
        -: 5398:		   }
        -: 5399:	  }
        9: 5400:
        9: 5400-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
       21: 5401:	  for (i = 0; i < num_terms; i++) {
        9: 5401-block  0
       12: 5401-block  1
       21: 5401-block  2
branch  0 taken 12
branch  1 taken 9 (fallthrough)
       27: 5402:		   bool found = false;
       12: 5402-block  0
       15: 5402-block  1
       27: 5402-block  2
branch  0 taken 15
branch  1 taken 12 (fallthrough)
       15: 5403:		   for (size_t k = 0; k < num_uniq; k++) {
       15: 5403-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 12
        3: 5404:		         if (strcmp(terms[i], uniq_terms[k]) == 0) { found = true; break; }
        3: 5404-block  0
        -: 5405:		   }
        -: 5406:		   if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
        -: 5407:	  }
       21: 5408:	  p = num_uniq;
        9: 5408-block  0
       21: 5408-block  1
branch  0 taken 12
branch  1 taken 9 (fallthrough)
       12: 5409:
       12: 5409-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 12
    #####: 5410:	  /* ---- NEW: allocate one slot per unique term, zero-initialised so
    #####: 5411:		           non-categorical terms stay NULL                          ---- */
    %%%%%: 5411-block  0
call    0 never executed
call    1 never executed
    #####: 5412:	  Newxz(term_base_level, num_uniq, char*);
call    0 never executed
call    1 never executed
    #####: 5413:	  /* ----------------------------------------------------------------------- */
call    0 never executed
call    1 never executed
    #####: 5414:
call    0 never executed
call    1 never executed
call    2 never executed
        -: 5415:	  // ========================================================================
       12: 5416:	  // PHASE 3: Categorical & Interaction Expansion
       12: 5417:	  // ========================================================================
       12: 5417-block  0
call    0 returned 12
       12: 5418:	  for (j = 0; j < p; j++) {
       12: 5419:		   if (p_exp + 64 >= exp_cap) {
call    0 returned 12
       12: 5420:		         exp_cap *= 2;
       12: 5421:		         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
call    0 returned 12
       12: 5422:		         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
call    0 returned 12
       12: 5423:		         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
       12: 5424:		         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
        -: 5425:		   }
       30: 5426:
        9: 5426-block  0
       21: 5426-block  1
call    0 returned 21
       30: 5426-block  2
branch  1 taken 21
branch  2 taken 9 (fallthrough)
        9: 5427:		   if (strcmp(uniq_terms[j], "Intercept") == 0) {
        9: 5427-block  0
call    0 returned 9
        -: 5428:		         exp_terms[p_exp] = savepv("Intercept");
    #####: 5429:		         parent_term[p_exp] = savepv("Intercept");
    %%%%%: 5429-block  0
call    0 never executed
    #####: 5430:		         is_dummy[p_exp] = false; is_interact[p_exp] = false;
call    0 never executed
    #####: 5431:		         term_map[p_exp] = j;
call    0 never executed
    #####: 5432:		         p_exp++;
    #####: 5433:		         continue;
    #####: 5434:		   }
        -: 5435:		   
        -: 5436:		   char *restrict colon = strchr(uniq_terms[j], ':');
       27: 5437:		   if (colon) {
       27: 5437-block  0
call    0 returned 27
       27: 5438:		         char left[256], right[256];
call    0 returned 27
       27: 5439:		         strncpy(left, uniq_terms[j], colon - uniq_terms[j]);
       27: 5440:		         left[colon - uniq_terms[j]] = '\0';
       27: 5441:		         strcpy(right, colon + 1);
        -: 5442:
        -: 5443:		         int *restrict l_indices = (int*)safemalloc(p_exp * sizeof(int)); int l_count = 0;
        -: 5444:		         int *restrict r_indices = (int*)safemalloc(p_exp * sizeof(int)); int r_count = 0;
       21: 5445:		         for (size_t e = 0; e < p_exp; e++) {
       21: 5445-block  0
call    0 returned 21
      177: 5446:		             if (strcmp(parent_term[e], left) == 0) l_indices[l_count++] = e;
      156: 5446-block  0
call    0 returned 156
      177: 5446-block  1
branch  1 taken 156
branch  2 taken 21 (fallthrough)
      21*: 5447:		             if (strcmp(parent_term[e], right) == 0) r_indices[r_count++] = e;
       21: 5447-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 5447-block  1
call    2 never executed
       21: 5447-block  2
call    3 returned 21
        -: 5448:		         }
        -: 5449:
        -: 5450:		         if (l_count == 0 || r_count == 0) {
      177: 5451:		             Safefree(l_indices); Safefree(r_indices);
      156: 5451-block  0
      177: 5451-block  1
branch  0 taken 156
branch  1 taken 21 (fallthrough)
      156: 5452:		             croak("aov: Interaction term '%s' requires its main effects to be explicitly included in the formula", uniq_terms[j]);
      156: 5452-block  0
call    0 returned 156
     156*: 5453:		         } else {
branch  0 taken 0 (fallthrough)
branch  1 taken 156
    %%%%%: 5453-block  0
call    2 never executed
      156: 5454:		             for (int li = 0; li < l_count; li++) {
      156: 5455:		                 for (int ri = 0; ri < r_count; ri++) {
      156: 5455-block  0
call    0 returned 156
      666: 5456:		                     if (p_exp >= exp_cap) {
      510: 5456-block  0
      666: 5456-block  1
branch  0 taken 510
branch  1 taken 156 (fallthrough)
      510: 5457:		                         exp_cap *= 2;
      510: 5457-block  0
branch  0 taken 156 (fallthrough)
branch  1 taken 354
      156: 5458:		                         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
      156: 5458-block  0
      354: 5459:		                         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
      354: 5459-block  0
branch  0 taken 60 (fallthrough)
branch  1 taken 294
       60: 5460:		                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
       60: 5460-block  0
      294: 5461:		                         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
      294: 5461-block  0
branch  0 taken 174 (fallthrough)
branch  1 taken 120
      174: 5462:		                     }
      174: 5462-block  0
call    0 returned 174
      174: 5463:		                     size_t t_len = strlen(exp_terms[l_indices[li]]) + strlen(exp_terms[r_indices[ri]]) + 2;
branch  0 taken 174 (fallthrough)
branch  1 taken 0
      174: 5464:		                     exp_terms[p_exp] = (char*)safemalloc(t_len);
      174: 5464-block  0
branch  0 taken 78 (fallthrough)
branch  1 taken 96
       78: 5464-block  1
       96: 5464-block  2
      174: 5465:		                     snprintf(exp_terms[p_exp], t_len, "%s:%s", exp_terms[l_indices[li]], exp_terms[r_indices[ri]]);
      174: 5465-block  0
call    0 returned 174
    #####: 5466:		                     parent_term[p_exp] = savepv(uniq_terms[j]);
    %%%%%: 5466-block  0
        -: 5467:		                     is_dummy[p_exp] = false; is_interact[p_exp] = true;
      120: 5468:		                     left_idx[p_exp] = l_indices[li];
      120: 5468-block  0
call    0 returned 120
     120*: 5469:		                     right_idx[p_exp] = r_indices[ri];
branch  0 taken 0 (fallthrough)
branch  1 taken 120
    %%%%%: 5469-block  0
        -: 5470:		                     term_map[p_exp] = j;
        -: 5471:		                     p_exp++;
     156*: 5472:		                 }
      156: 5472-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 156
    %%%%%: 5472-block  1
call    2 never executed
call    3 never executed
        -: 5473:		             }
      156: 5474:		         }
      666: 5475:		         Safefree(l_indices); Safefree(r_indices);
      156: 5475-block  0
      510: 5475-block  1
      666: 5475-block  2
branch  0 taken 510
branch  1 taken 156 (fallthrough)
      156: 5476:		   } else {
      156: 5477:		         if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
      156: 5477-block  0
call    0 returned 156
      156: 5478:		             char **restrict levels = NULL;
call    0 returned 156
        -: 5479:		             unsigned int num_levels = 0, levels_cap = 8;
       21: 5480:		             Newx(levels, levels_cap, char*);
       21: 5480-block  0
call    0 returned 21
       21: 5481:		             for (i = 0; i < n; i++) {
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        -: 5482:		                 char* str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
       12: 5483:		                 if (str_val) {
        3: 5483-block  0
        9: 5483-block  1
call    0 returned 9
       12: 5483-block  2
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        3: 5483-block  3
call    3 returned 3
       12: 5484:		                     bool found = false;
        9: 5484-block  0
call    0 returned 9
       12: 5484-block  1
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        3: 5484-block  2
call    3 returned 3
       12: 5485:		                     for (size_t l = 0; l < num_levels; l++) {
        9: 5485-block  0
       12: 5485-block  1
branch  0 taken 9
branch  1 taken 3 (fallthrough)
        9: 5486:		                         if (strcmp(levels[l], str_val) == 0) { found = true; break; }
        9: 5486-block  0
call    0 returned 9
call    1 returned 9
       9*: 5487:		                     }
branch  0 taken 0 (fallthrough)
branch  1 taken 9
    %%%%%: 5487-block  0
call    2 never executed
call    3 never executed
        -: 5488:		                     if (!found) {
        3: 5489:		                         if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
        3: 5489-block  0
call    0 returned 3
call    1 returned 3
        3: 5490:		                         levels[num_levels++] = savepv(str_val);
call    0 returned 3
call    1 returned 3
        3: 5491:		                     }
call    0 returned 3
call    1 returned 3
        3: 5492:		                     Safefree(str_val);
call    0 returned 3
call    1 returned 3
call    2 returned 3
        9: 5493:		                 }
        6: 5493-block  0
call    0 returned 6
        9: 5493-block  1
branch  1 taken 6
branch  2 taken 3 (fallthrough)
        3: 5494:		             }
        3: 5494-block  0
call    0 returned 3
call    1 returned 3
       3*: 5495:		             
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5495-block  0
call    2 never executed
        3: 5496:		             if (num_levels > 0) {
        3: 5496-block  0
call    0 returned 0
        -: 5497:		                 for (size_t l1 = 0; l1 < num_levels - 1; l1++) {
        -: 5498:		                     for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
        -: 5499:		                         if (strcmp(levels[l1], levels[l2]) > 0) {
        -: 5500:		                             char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp;
       18: 5501:		                         }
       18: 5501-block  0
call    0 returned 18
       18: 5502:		                     }
call    0 returned 18
       18: 5503:		                 }
call    0 returned 18
        -: 5504:
        -: 5505:		                 /* ---- NEW: record the reference (base) level for this term ---- */
       18: 5506:		                 term_base_level[j] = savepv(levels[0]);
call    0 returned 18
       18: 5507:		                 /* --------------------------------------------------------------- */
call    0 returned 18
        -: 5508:
       72: 5509:		                 for (size_t l = 1; l < num_levels; l++) {
       54: 5509-block  0
       72: 5509-block  1
branch  0 taken 54
branch  1 taken 18 (fallthrough)
       54: 5510:		                     if (p_exp >= exp_cap) {
       54: 5510-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 36
       18: 5510-block  1
       36: 5511:		                         exp_cap *= 2;
       36: 5511-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 33
        3: 5511-block  1
       33: 5512:		                         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
       33: 5513:		                         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
       33: 5514:		                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
       33: 5515:		                         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
       33: 5515-block  0
        -: 5516:		                     }
       18: 5517:		                     size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
       72: 5518:		                     exp_terms[p_exp] = (char*)safemalloc(t_len);
       18: 5518-block  0
       54: 5518-block  1
       72: 5518-block  2
branch  0 taken 54
branch  1 taken 18 (fallthrough)
       54: 5519:		                     snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
       54: 5519-block  0
branch  0 taken 51 (fallthrough)
branch  1 taken 3
       51: 5519-block  1
        -: 5520:		                     parent_term[p_exp] = savepv(uniq_terms[j]);
       18: 5521:		                     is_dummy[p_exp] = true; is_interact[p_exp] = false;
      117: 5522:		                     dummy_base[p_exp] = savepv(uniq_terms[j]);
       18: 5522-block  0
      117: 5522-block  1
branch  0 taken 99
branch  1 taken 18 (fallthrough)
       99: 5523:		                     dummy_level[p_exp] = savepv(levels[l]);
       99: 5523-block  0
        -: 5524:		                     term_map[p_exp] = j;
       18: 5525:		                     p_exp++;
      18*: 5526:		                 }
       18: 5526-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 5526-block  1
    %%%%%: 5526-block  2
        -: 5527:		                 for (size_t l = 0; l < num_levels; l++) Safefree(levels[l]);
       18: 5528:		                 Safefree(levels);
       18: 5528-block  0
call    0 returned 18
       69: 5529:		             } else {
       51: 5529-block  0
       69: 5529-block  1
branch  0 taken 51
branch  1 taken 18 (fallthrough)
       51: 5530:		                 Safefree(levels);
       51: 5530-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 33
       18: 5530-block  1
       33: 5531:		                 exp_terms[p_exp] = savepv(uniq_terms[j]);
       33: 5531-block  0
call    0 returned 33
       33: 5532:		                 parent_term[p_exp] = savepv(uniq_terms[j]);
       33: 5533:		                 is_dummy[p_exp] = false; is_interact[p_exp] = false;
       33: 5534:		                 term_map[p_exp] = j;
branch  0 taken 30 (fallthrough)
branch  1 taken 3
       30: 5534-block  0
        3: 5534-block  1
        -: 5535:		                 p_exp++;
       33: 5536:		             }
       33: 5536-block  0
call    0 returned 33
call    1 returned 33
       33: 5537:		         } else {
call    0 returned 33
call    1 returned 33
       33: 5538:		             exp_terms[p_exp] = savepv(uniq_terms[j]);
call    0 returned 33
call    1 returned 33
       63: 5539:		             parent_term[p_exp] = savepv(uniq_terms[j]);
branch  0 taken 33 (fallthrough)
branch  1 taken 0
       33: 5539-block  0
branch  2 taken 30 (fallthrough)
branch  3 taken 3
       30: 5540:		             is_dummy[p_exp] = false; is_interact[p_exp] = false;
       30: 5541:		             term_map[p_exp] = j;
       30: 5541-block  0
call    0 returned 30
call    1 returned 30
       30: 5542:		             p_exp++;
call    0 returned 30
call    1 returned 30
call    2 returned 30
        -: 5543:		         }
        3: 5544:		   }
        3: 5544-block  0
call    0 returned 3
call    1 returned 3
        3: 5545:	  }
call    0 returned 3
call    1 returned 3
        -: 5546:	  X_mat = (double**)safemalloc(n * sizeof(double*));
       33: 5547:	  for(i = 0; i < n; i++) X_mat[i] = (double*)safemalloc(p_exp * sizeof(double));
       33: 5547-block  0
call    0 returned 33
call    1 returned 33
        -: 5548:	  Newx(Y, n, double);
        -: 5549:	  // ========================================================================
       18: 5550:	  // PHASE 4: Matrix Construction & Listwise Deletion
       18: 5550-block  0
call    0 returned 18
       18: 5551:	  // ========================================================================
call    0 returned 18
call    1 returned 18
       18: 5552:	  for (i = 0; i < n; i++) {
call    0 returned 18
call    1 returned 18
       18: 5553:		   double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
call    0 returned 18
call    1 returned 18
       18: 5554:		   if (isnan(y_val)) { Safefree(row_names[i]); continue; }
call    0 returned 18
call    1 returned 18
        -: 5555:		   bool row_ok = true;
        -: 5556:		   double *restrict row_x = (double*)safemalloc(p_exp * sizeof(double));
       69: 5557:		   for (j = 0; j < p_exp; j++) {
       51: 5557-block  0
call    0 returned 51
       69: 5557-block  1
branch  1 taken 51
branch  2 taken 18 (fallthrough)
       18: 5557-block  2
call    3 returned 18
       69: 5558:		         if (strcmp(exp_terms[j], "Intercept") == 0) {
       51: 5558-block  0
call    0 returned 51
       69: 5558-block  1
branch  1 taken 51
branch  2 taken 18 (fallthrough)
       18: 5558-block  2
call    3 returned 18
       72: 5559:		             row_x[j] = 1.0;
       54: 5559-block  0
       72: 5559-block  1
branch  0 taken 54
branch  1 taken 18 (fallthrough)
       54: 5560:		         } else if (is_interact[j]) {
       54: 5560-block  0
call    0 returned 54
call    1 returned 54
       54: 5561:		             row_x[j] = row_x[left_idx[j]] * row_x[right_idx[j]];
branch  0 taken 12 (fallthrough)
branch  1 taken 42
       12: 5561-block  0
call    2 returned 12
call    3 returned 12
        -: 5562:		         } else if (is_dummy[j]) {
       18: 5563:		             char*restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
       18: 5563-block  0
call    0 returned 18
call    1 returned 18
       18: 5564:		             if (str_val) {
call    0 returned 18
call    1 returned 18
       18: 5565:		                 row_x[j] = (strcmp(str_val, dummy_level[j]) == 0) ? 1.0 : 0.0;
call    0 returned 18
call    1 returned 18
       18: 5566:		                 Safefree(str_val);
call    0 returned 18
call    1 returned 18
call    2 returned 18
       18: 5567:		             } else { row_ok = false; break; }
call    0 returned 18
call    1 returned 18
      168: 5568:		         } else {
      150: 5568-block  0
call    0 returned 150
      168: 5568-block  1
branch  1 taken 150
branch  2 taken 18 (fallthrough)
       18: 5569:		             row_x[j] = evaluate_term(data_hoa, row_hashes, i, parent_term[j]);
       18: 5569-block  0
call    0 returned 18
call    1 returned 18
       18: 5570:		             if (isnan(row_x[j])) { row_ok = false; break; }
call    0 returned 18
call    1 returned 18
      18*: 5571:		         }
branch  0 taken 0 (fallthrough)
branch  1 taken 18
    %%%%%: 5571-block  0
call    2 never executed
       18: 5572:		   }
       18: 5572-block  0
call    0 returned 18
        -: 5573:		   if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
        -: 5574:		   
        -: 5575:		   Y[valid_n] = y_val;
        -: 5576:		   for (j = 0; j < p_exp; j++) X_mat[valid_n][j] = row_x[j];
        -: 5577:		   valid_n++;
        -: 5578:		   Safefree(row_x);
        -: 5579:		   Safefree(row_names[i]);
        -: 5580:	  }
        -: 5581:	  Safefree(row_names);
       18: 5582:	  if (valid_n <= p_exp) {
branch  0 taken 3 (fallthrough)
branch  1 taken 15
        3: 5582-block  0
call    2 returned 0
        -: 5583:		   // Full Clean Up 
       15: 5584:		   for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
       15: 5585:		   for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
       15: 5586:		   for (j = 0; j < p_exp; j++) {
        -: 5587:		        Safefree(exp_terms[j]); Safefree(parent_term[j]);
        -: 5588:		        if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
       21: 5589:		   }
       15: 5589-block  0
        6: 5589-block  1
       21: 5589-block  2
branch  0 taken 6
branch  1 taken 15 (fallthrough)
       6*: 5590:		   Safefree(exp_terms); Safefree(parent_term); 
        6: 5590-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 6
    %%%%%: 5590-block  1
call    2 never executed
        6: 5591:		   Safefree(is_dummy); Safefree(is_interact); 
        6: 5591-block  0
call    0 returned 6
        6: 5592:		   Safefree(dummy_base); Safefree(dummy_level);
        6: 5593:		   Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5593-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 6
    #####: 5594:		   for(i = 0; i < n; i++) Safefree(X_mat[i]);
    %%%%%: 5594-block  0
call    0 never executed
        6: 5595:		   Safefree(X_mat); Safefree(Y);
        6: 5595-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5596:		   if (row_hashes) Safefree(row_hashes);
        6: 5596-block  0
call    0 returned 6
        -: 5597:		   /* ---- NEW ---- */
        -: 5598:		   for (i = 0; i < num_uniq; i++) { if (term_base_level[i]) Safefree(term_base_level[i]); }
        -: 5599:		   Safefree(term_base_level);
      15*: 5600:		   /* ------------ */
       15: 5600-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 15
    %%%%%: 5600-block  1
call    2 never executed
       15: 5601:		   croak("aov: 0 degrees of freedom (too many NAs or parameters > observations)");
       15: 5602:	  }
        -: 5603:	// ========================================================================
       15: 5604:	  // PHASE 5: Math & Output Formatting
       15: 5604-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 9
        6: 5605:	  // ========================================================================
       6*: 5606:	  bool *restrict aliased_qr = (bool*)safemalloc(p_exp * sizeof(bool));
        6: 5606-block  0
call    0 returned 6
branch  1 taken 0 (fallthrough)
branch  2 taken 6
    %%%%%: 5606-block  1
call    3 never executed
        6: 5607:	  size_t *restrict rank_map = (size_t*)safemalloc(p_exp * sizeof(size_t));
        6: 5607-block  0
call    0 returned 6
        6: 5608:	  apply_householder_aov(X_mat, Y, valid_n, p_exp, aliased_qr, rank_map);
call    0 returned 6
        6: 5609:	  double *restrict term_ss;
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5609-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 5609-block  1
branch  4 taken 6 (fallthrough)
branch  5 taken 0
        6: 5609-block  2
branch  6 taken 6 (fallthrough)
branch  7 taken 0
        6: 5610:	  int *restrict term_df;
        6: 5611:	  Newxz(term_ss, num_uniq, double);
        6: 5612:	  Newxz(term_df, num_uniq, int);
        6: 5612-block  0
call    0 returned 6
        6: 5613:
call    0 returned 6
        6: 5614:	  for (i = 0; i < p_exp; i++) {
call    0 returned 6
        6: 5615:		   if (strcmp(exp_terms[i], "Intercept") == 0) continue; 
call    0 returned 6
       6*: 5616:		   if (aliased_qr[i]) continue;
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5616-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 5616-block  1
call    4 returned 6
    %%%%%: 5616-block  2
       6*: 5617:		   int t_idx = term_map[i];
        6: 5617-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5617-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 5617-block  2
call    4 returned 6
    %%%%%: 5617-block  3
       6*: 5618:		   size_t r_k = rank_map[i];
        6: 5618-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5618-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 5618-block  2
call    4 returned 6
    %%%%%: 5618-block  3
       6*: 5619:		   term_ss[t_idx] += Y[r_k] * Y[r_k];
        6: 5619-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5619-block  1
branch  2 taken 6 (fallthrough)
branch  3 taken 0
        6: 5619-block  2
call    4 returned 6
    %%%%%: 5619-block  3
        6: 5619-block  4
        -: 5620:		   term_df[t_idx] += 1;
    #####: 5621:	  }
    %%%%%: 5621-block  0
call    0 never executed
        -: 5622:	  int rank = 0;
        9: 5623:	  for (i = 0; i < p_exp; i++) {
        9: 5623-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        -: 5624:		   if (!aliased_qr[i]) rank++;
        9: 5625:	  }
       9*: 5626:	  double rss_prev = 0.0;
        9: 5626-block  0
call    0 returned 9
branch  1 taken 0 (fallthrough)
branch  2 taken 9
    %%%%%: 5626-block  1
call    3 never executed
        9: 5627:	  for (i = rank; i < valid_n; i++) {
        9: 5627-block  0
call    0 returned 9
        9: 5628:		   rss_prev += Y[i] * Y[i];
call    0 returned 9
       9*: 5629:	  }
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5629-block  0
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    %%%%%: 5629-block  1
call    4 never executed
        9: 5630:	  int res_df = valid_n - rank;
        9: 5630-block  0
call    0 returned 9
call    1 returned 9
        9: 5631:	  double ms_res = (res_df > 0) ? rss_prev / res_df : 0.0;
call    0 returned 9
call    1 returned 9
        9: 5632:
branch  0 taken 5 (fallthrough)
branch  1 taken 4
        5: 5632-block  0
        4: 5632-block  1
        9: 5633:	  HV*restrict ret_hash = newHV();
        9: 5633-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 4
        5: 5633-block  1
        4: 5633-block  2
        9: 5634:	  for (j = 0; j < num_uniq; j++) {
        9: 5634-block  0
call    0 returned 9
        9: 5635:		   if (strcmp(uniq_terms[j], "Intercept") == 0) continue;
call    0 returned 9
        9: 5636:		   HV*restrict term_stats = newHV();
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5636-block  0
branch  2 taken 9 (fallthrough)
branch  3 taken 0
        9: 5637:		   double ss = term_ss[j];
        9: 5637-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5637-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 5638:		   int df = term_df[j];
    %%%%%: 5638-block  0
call    0 never executed
        -: 5639:		   double ms = (df > 0) ? ss / df : 0.0;
        9: 5640:
        9: 5641:		   hv_stores(term_stats, "Df", newSViv(df));
       9*: 5642:		   hv_stores(term_stats, "Sum Sq", newSVnv(ss));
        9: 5642-block  0
call    0 returned 9
branch  1 taken 9 (fallthrough)
branch  2 taken 0
        9: 5642-block  1
call    3 returned 9
branch  4 taken 0 (fallthrough)
branch  5 taken 9
    %%%%%: 5642-block  2
call    6 never executed
        9: 5643:		   hv_stores(term_stats, "Mean Sq", newSVnv(ms));
        9: 5643-block  0
call    0 returned 9
        9: 5644:		   if (ms_res > 0.0 && df > 0) {
call    0 returned 9
        9: 5645:		         double f_val = ms / ms_res;
call    0 returned 9
call    1 returned 9
        9: 5646:		         hv_stores(term_stats, "F value", newSVnv(f_val));
call    0 returned 9
call    1 returned 9
        9: 5647:		         hv_stores(term_stats, "Pr(>F)", newSVnv(1.0 - pf(f_val, (double)df, (double)res_df)));
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 5647-block  0
        6: 5647-block  1
        9: 5648:		   } else {
        9: 5648-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 6
        3: 5648-block  1
        6: 5648-block  2
        9: 5649:		         hv_stores(term_stats, "F value", newSVnv(NAN));
        9: 5649-block  0
call    0 returned 9
        9: 5650:		         hv_stores(term_stats, "Pr(>F)", newSVnv(NAN));
call    0 returned 9
        9: 5651:		   }
call    0 returned 9
call    1 returned 9
        9: 5652:		   hv_store(ret_hash, uniq_terms[j], strlen(uniq_terms[j]), newRV_noinc((SV*)term_stats), 0);
call    0 returned 9
call    1 returned 9
        9: 5653:	  }
branch  0 taken 4 (fallthrough)
branch  1 taken 5
        4: 5653-block  0
        5: 5653-block  1
        9: 5654:
        9: 5654-block  0
branch  0 taken 4 (fallthrough)
branch  1 taken 5
        4: 5654-block  1
        5: 5654-block  2
       9*: 5655:	  HV*restrict res_stats = newHV();
        9: 5655-block  0
call    0 returned 9
branch  1 taken 9 (fallthrough)
branch  2 taken 0
        9: 5655-block  1
call    3 returned 9
branch  4 taken 9 (fallthrough)
branch  5 taken 0
        9: 5655-block  2
call    6 returned 9
call    7 returned 9
    %%%%%: 5655-block  3
       9*: 5656:	  hv_stores(res_stats, "Df", newSViv(res_df));
        9: 5656-block  0
call    0 returned 9
branch  1 taken 9 (fallthrough)
branch  2 taken 0
        9: 5656-block  1
call    3 returned 9
branch  4 taken 9 (fallthrough)
branch  5 taken 0
        9: 5656-block  2
call    6 returned 9
call    7 returned 9
    %%%%%: 5656-block  3
       9*: 5657:	  hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
        9: 5657-block  0
call    0 returned 9
branch  1 taken 9 (fallthrough)
branch  2 taken 0
        9: 5657-block  1
call    3 returned 9
branch  4 taken 9 (fallthrough)
branch  5 taken 0
        9: 5657-block  2
call    6 returned 9
call    7 returned 9
    %%%%%: 5657-block  3
       9*: 5658:	  hv_stores(res_stats, "Mean Sq", newSVnv(ms_res));
        9: 5658-block  0
call    0 returned 9
branch  1 taken 9 (fallthrough)
branch  2 taken 0
        9: 5658-block  1
call    3 returned 9
branch  4 taken 9 (fallthrough)
branch  5 taken 0
        9: 5658-block  2
call    6 returned 9
call    7 returned 9
    %%%%%: 5658-block  3
        9: 5658-block  4
        -: 5659:	  hv_stores(ret_hash, "Residuals", newRV_noinc((SV*)res_stats));
    #####: 5660:
    %%%%%: 5660-block  0
call    0 never executed
        -: 5661:	  /* ---- NEW: group_stats => { mean => {...}, size => {...} } ---- */
        -: 5662:	  {
        -: 5663:		   AV *restrict all_cols = get_all_columns(data_hoa, row_hashes, n);
       15: 5664:		   HV *restrict mean_hv  = newHV();
       15: 5664-block  0
call    0 returned 15
        -: 5665:		   HV *restrict size_hv  = newHV();
       15: 5666:		   for (size_t c = 0; c <= (size_t)av_len(all_cols); c++) {
call    0 returned 15
        -: 5667:		       SV **restrict col_sv = av_fetch(all_cols, c, 0);
        -: 5668:		       if (!col_sv || !SvOK(*col_sv)) continue;
       15: 5669:		       const char *restrict col_name = SvPV_nolen(*col_sv);
call    0 returned 15
       15: 5670:		       double col_sum = 0.0;
call    0 returned 15
call    1 returned 15
       15: 5671:		       IV     col_count = 0;
call    0 returned 15
call    1 returned 15
       15: 5672:		       for (i = 0; i < n; i++) {
call    0 returned 15
       15: 5673:		           double val = evaluate_term(data_hoa, row_hashes, i, col_name);
call    0 returned 15
call    1 returned 15
       15: 5674:		           if (!isnan(val)) { col_sum += val; col_count++; }
call    0 returned 15
call    1 returned 15
       15: 5675:		       }
call    0 returned 15
call    1 returned 15
       15: 5676:		       double col_mean = (col_count > 0) ? col_sum / col_count : NAN;
call    0 returned 15
       15: 5677:		       hv_store(mean_hv, col_name, strlen(col_name), newSVnv(col_mean), 0);
call    0 returned 15
call    1 returned 15
       15: 5678:		       hv_store(size_hv, col_name, strlen(col_name), newSViv(col_count), 0);
call    0 returned 15
call    1 returned 15
       15: 5679:		   }
call    0 returned 15
call    1 returned 15
        -: 5680:		   SvREFCNT_dec(all_cols);
       15: 5681:		   HV *restrict gs_hv = newHV();
call    0 returned 15
        -: 5682:		   hv_stores(gs_hv, "mean", newRV_noinc((SV*)mean_hv));
        -: 5683:		   hv_stores(gs_hv, "size", newRV_noinc((SV*)size_hv));
        -: 5684:		   hv_stores(ret_hash, "group_stats", newRV_noinc((SV*)gs_hv));
        -: 5685:	  }
        -: 5686:	  /* -------------------------------------------------------------- */
        -: 5687:
        -: 5688:	  // Deep Cleanup
       21: 5689:	  for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
       21: 5690:	  for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
       21: 5691:	  for (j = 0; j < p_exp; j++) {
       21: 5692:		   Safefree(exp_terms[j]); Safefree(parent_term[j]);
       21: 5693:		   if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
        -: 5694:	  }
       21: 5695:	  Safefree(exp_terms); Safefree(parent_term); 
       21: 5696:	  Safefree(is_dummy); Safefree(is_interact); 
       21: 5697:	  Safefree(dummy_base); Safefree(dummy_level);
       21: 5698:	  Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
        -: 5699:	  Safefree(term_ss); Safefree(term_df);
      21*: 5700:	  for (i = 0; i < n; i++) Safefree(X_mat[i]);
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 5700-block  0
call    2 never executed
      102: 5701:	  Safefree(X_mat); Safefree(Y);
       21: 5701-block  0
       81: 5701-block  1
      102: 5701-block  2
branch  0 taken 81
branch  1 taken 21 (fallthrough)
       81: 5702:	  Safefree(aliased_qr); Safefree(rank_map);
       81: 5702-block  0
call    0 returned 81
       81: 5703:	  if (row_hashes) Safefree(row_hashes);
        -: 5704:	  RETVAL = newRV_noinc((SV*)ret_hash);
       81: 5705:	}
branch  0 taken 3 (fallthrough)
branch  1 taken 78
        3: 5705-block  0
       78: 5706:OUTPUT:
       78: 5706-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 57
       21: 5706-block  1
       57: 5707:    RETVAL
       57: 5707-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 36
       21: 5707-block  1
       36: 5708:
       36: 5708-block  0
branch  0 taken 36 (fallthrough)
branch  1 taken 0
       36: 5708-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 33
        3: 5708-block  2
       33: 5709:PROTOTYPES: DISABLE
       33: 5709-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 15
       18: 5709-block  1
       15: 5710:
       15: 5710-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 6
        9: 5710-block  1
call    2 returned 9
        6: 5711:SV* fisher_test(...)
        6: 5711-block  0
branch  0 taken 6 (fallthrough)
branch  1 taken 0
        6: 5711-block  1
call    2 returned 6
    #####: 5712:CODE:
    %%%%%: 5712-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5712-block  1
call    2 never executed
    #####: 5713:{
    %%%%%: 5713-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5713-block  1
call    2 never executed
    #####: 5714:	if (items < 1) croak("fisher_test requires at least a data reference");
    %%%%%: 5714-block  0
call    0 never executed
        -: 5715:
        -: 5716:	SV*restrict data_ref = ST(0);
       21: 5717:	double conf_level = 0.95;
       21: 5717-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 5717-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
       18: 5717-block  2
        3: 5717-block  3
      21*: 5718:	const char*restrict alternative = "two.sided";
       21: 5718-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 5718-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 21
    %%%%%: 5718-block  2
       21: 5718-block  3
       21: 5719:
       21: 5719-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 5719-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 18
        3: 5719-block  2
       18: 5719-block  3
      21*: 5720:	// Parse named arguments
       21: 5720-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 5720-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 21
    %%%%%: 5720-block  2
       21: 5720-block  3
      21*: 5721:	for (unsigned short int i = 1; i < items; i += 2) {
       21: 5721-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 5721-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    %%%%%: 5721-block  2
       21: 5721-block  3
        -: 5722:		if (i + 1 >= items) croak("fisher_test: odd number of arguments");
       21: 5723:		const char*restrict key = SvPV_nolen(ST(i));
       21: 5724:		SV*restrict val = ST(i + 1);
       21: 5724-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 5724-block  1
      21*: 5725:		if (strEQ(key, "conf_level") || strEQ(key, "conf.level")) {
       21: 5725-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 5725-block  1
       21: 5726:			conf_level = SvNV(val);
       21: 5726-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 5726-block  1
      21*: 5727:		} else if (strEQ(key, "alternative")) {
       21: 5727-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 5727-block  1
      21*: 5728:			alternative = SvPV_nolen(val);
       21: 5728-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    %%%%%: 5728-block  1
        -: 5729:		}
       21: 5730:	}
       21: 5730-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 21
    #####: 5731:
    %%%%%: 5731-block  0
call    0 never executed
        -: 5732:	if (!SvROK(data_ref)) croak("fisher_test requires a reference to an Array or Hash");
        -: 5733:	SV*restrict deref = SvRV(data_ref);
       21: 5734:	size_t a = 0, b = 0, c = 0, d = 0;
       21: 5734-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 5734-block  1
call    2 returned 3
       18: 5734-block  2
      21*: 5735:	// Extract Data
       21: 5735-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 5735-block  1
call    2 returned 21
    %%%%%: 5735-block  2
      21*: 5736:	if (SvTYPE(deref) == SVt_PVAV) {
       21: 5736-block  0
branch  0 taken 21 (fallthrough)
branch  1 taken 0
       21: 5736-block  1
branch  2 taken 21 (fallthrough)
branch  3 taken 0
       21: 5736-block  2
call    4 returned 21
    %%%%%: 5736-block  3
       21: 5737:	  AV*restrict outer = (AV*)deref;
       21: 5737-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 5737-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 5737-block  2
call    4 returned 3
       18: 5737-block  3
       21: 5738:	  if (av_len(outer) != 1) croak("Outer array must have exactly 2 rows");
       21: 5738-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 5738-block  1
call    2 returned 18
        3: 5738-block  2
       21: 5739:	  SV**restrict row1_ptr = av_fetch(outer, 0, 0);
       21: 5739-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 5739-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 15
        6: 5739-block  2
       15: 5739-block  3
       21: 5740:	  SV**restrict row2_ptr = av_fetch(outer, 1, 0);
       21: 5740-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 5740-block  1
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 5740-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 18
        3: 5740-block  3
       18: 5740-block  4
       21: 5741:	  if (row1_ptr && row2_ptr && SvROK(*row1_ptr) && SvROK(*row2_ptr)) {
       21: 5741-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 5741-block  1
branch  2 taken 18 (fallthrough)
branch  3 taken 0
       18: 5741-block  2
       21: 5742:		   AV*restrict row1 = (AV*)SvRV(*row1_ptr);
       21: 5742-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 18
        3: 5743:		   AV*restrict row2 = (AV*)SvRV(*row2_ptr);
        3: 5743-block  0
call    0 returned 3
       18: 5744:		   SV**restrict a_ptr = av_fetch(row1, 0, 0);
       18: 5744-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 0
       18: 5745:		   SV**restrict b_ptr = av_fetch(row1, 1, 0);
      18*: 5746:		   SV**restrict c_ptr = av_fetch(row2, 0, 0);
       18: 5746-block  0
    %%%%%: 5746-block  1
       18: 5746-block  2
call    0 returned 18
branch  1 taken 0 (fallthrough)
branch  2 taken 18
    %%%%%: 5746-block  3
branch  3 never executed
branch  4 never executed (fallthrough)
      684: 5747:		   SV**restrict d_ptr = av_fetch(row2, 1, 0);
       18: 5747-block  0
      684: 5747-block  1
branch  0 taken 666
branch  1 taken 18 (fallthrough)
      666: 5748:		   a = (a_ptr && SvOK(*a_ptr)) ? SvIV(*a_ptr) : 0;
      666: 5749:		   b = (b_ptr && SvOK(*b_ptr)) ? SvIV(*b_ptr) : 0;
      666: 5749-block  0
call    0 returned 666
branch  1 taken 147 (fallthrough)
branch  2 taken 519
      147: 5749-block  1
      519: 5750:		   c = (c_ptr && SvOK(*c_ptr)) ? SvIV(*c_ptr) : 0;
      519: 5750-block  0
        -: 5751:		   d = (d_ptr && SvOK(*d_ptr)) ? SvIV(*d_ptr) : 0;
       18: 5752:	  } else {
       18: 5752-block  0
    #####: 5753:		  croak("Invalid 2D Array structure");
    %%%%%: 5753-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5754:	  }
    #####: 5755:	} else if (SvTYPE(deref) == SVt_PVHV) {
    %%%%%: 5755-block  0
    %%%%%: 5755-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 5756:		// Fixed 2D Hash Logic: Sort keys lexically to enforce structured rows/columns
    #####: 5757:		HV*restrict outer = (HV*)deref;
    %%%%%: 5757-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5757-block  1
    #####: 5758:		if (hv_iterinit(outer) != 2) croak("Outer hash must have exactly 2 keys");
    %%%%%: 5758-block  0
        -: 5759:		HE*restrict he1 = hv_iternext(outer);
    #####: 5760:		HE*restrict he2 = hv_iternext(outer);
    %%%%%: 5760-block  0
    #####: 5761:		if (!he1 || !he2) croak("Invalid outer hash");
    %%%%%: 5761-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5762:		const char*restrict k1 = SvPV_nolen(hv_iterkeysv(he1));
    #####: 5763:		const char*restrict k2 = SvPV_nolen(hv_iterkeysv(he2));
    %%%%%: 5763-block  0
    %%%%%: 5763-block  1
    %%%%%: 5763-block  2
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5763-block  3
branch  3 never executed
branch  4 never executed (fallthrough)
    #####: 5764:		HE*restrict row1_he = (strcmp(k1, k2) < 0) ? he1 : he2;
    %%%%%: 5764-block  0
    %%%%%: 5764-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 5765:		HE*restrict row2_he = (strcmp(k1, k2) < 0) ? he2 : he1;
    #####: 5766:		SV*restrict row1_sv = hv_iterval(outer, row1_he);
    %%%%%: 5766-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5766-block  1
    #####: 5767:		SV*restrict row2_sv = hv_iterval(outer, row2_he);
    %%%%%: 5767-block  0
        -: 5768:		if (!SvROK(row1_sv) || SvTYPE(SvRV(row1_sv)) != SVt_PVHV ||
    #####: 5769:			!SvROK(row2_sv) || SvTYPE(SvRV(row2_sv)) != SVt_PVHV) {
    %%%%%: 5769-block  0
    #####: 5770:			croak("Inner elements must be hashes");
    %%%%%: 5770-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    #####: 5771:		}
    #####: 5772:		HV*restrict in1 = (HV*)SvRV(row1_sv);
    %%%%%: 5772-block  0
    %%%%%: 5772-block  1
branch  0 never executed
branch  1 never executed (fallthrough)
    #####: 5773:		HV*restrict in2 = (HV*)SvRV(row2_sv);
    #####: 5774:		if (hv_iterinit(in1) != 2 || hv_iterinit(in2) != 2) croak("Inner hashes must have exactly 2 keys");
    %%%%%: 5774-block  0
call    0 never executed
branch  1 never executed (fallthrough)
branch  2 never executed
    %%%%%: 5774-block  1
    #####: 5775:		HE*restrict in1_he1 = hv_iternext(in1);
    %%%%%: 5775-block  0
        -: 5776:		HE*restrict in1_he2 = hv_iternext(in1);
    #####: 5777:		const char*restrict in1_k1 = SvPV_nolen(hv_iterkeysv(in1_he1));
    %%%%%: 5777-block  0
        -: 5778:		const char*restrict in1_k2 = SvPV_nolen(hv_iterkeysv(in1_he2));
       21: 5779:		HE*restrict in1_c1 = (strcmp(in1_k1, in1_k2) < 0) ? in1_he1 : in1_he2;
       21: 5779-block  0
call    0 returned 21
       21: 5780:		HE*restrict in1_c2 = (strcmp(in1_k1, in1_k2) < 0) ? in1_he2 : in1_he1;
call    0 returned 21
call    1 returned 21
       21: 5781:		HE*restrict in2_he1 = hv_iternext(in2);
call    0 returned 21
call    1 returned 21
       21: 5782:		HE*restrict in2_he2 = hv_iternext(in2);
call    0 returned 21
call    1 returned 21
       21: 5783:		const char*restrict in2_k1 = SvPV_nolen(hv_iterkeysv(in2_he1));
call    0 returned 21
call    1 returned 21
       21: 5784:		const char*restrict in2_k2 = SvPV_nolen(hv_iterkeysv(in2_he2));
call    0 returned 21
call    1 returned 21
       21: 5785:		HE*restrict in2_c1 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he1 : in2_he2;
call    0 returned 21
call    1 returned 21
       21: 5786:		HE*restrict in2_c2 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he2 : in2_he1;
branch  0 taken 6 (fallthrough)
branch  1 taken 15
        6: 5786-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 3
        3: 5786-block  1
        3: 5786-block  2
        6: 5786-block  3
       15: 5786-block  4
       21: 5787:		a = (hv_iterval(in1, in1_c1) && SvOK(hv_iterval(in1, in1_c1))) ? SvIV(hv_iterval(in1, in1_c1)) : 0;
       21: 5787-block  0
call    0 returned 21
call    1 returned 21
       21: 5788:		b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
branch  0 taken 6 (fallthrough)
branch  1 taken 15
        6: 5788-block  0
branch  2 taken 3 (fallthrough)
branch  3 taken 3
        3: 5788-block  1
        3: 5788-block  2
        6: 5788-block  3
       15: 5788-block  4
       21: 5789:		c = (hv_iterval(in2, in2_c1) && SvOK(hv_iterval(in2, in2_c1))) ? SvIV(hv_iterval(in2, in2_c1)) : 0;
       21: 5789-block  0
branch  0 taken 18 (fallthrough)
branch  1 taken 3
       18: 5789-block  1
call    2 returned 18
call    3 returned 18
       21: 5790:		d = (hv_iterval(in2, in2_c2) && SvOK(hv_iterval(in2, in2_c2))) ? SvIV(hv_iterval(in2, in2_c2)) : 0;
       21: 5790-block  0
call    0 returned 21
        -: 5791:	} else {
        -: 5792:	  croak("Input must be a 2D Array or 2D Hash");
        -: 5793:	}
        -: 5794:
        -: 5795:	// Perform Calculations via Helpers
        -: 5796:	double p_val = exact_p_value(a, b, c, d, alternative);
        -: 5797:	double mle_or, ci_low, ci_high;
        8: 5798:	calculate_exact_stats(a, b, c, d, conf_level, alternative, &mle_or, &ci_low, &ci_high);
        8: 5799:
        -: 5800:	// Construct the Return HashRef purely in C
        -: 5801:	HV*restrict ret_hash = newHV();
        -: 5802:	hv_stores(ret_hash, "method", newSVpv("Fisher's Exact Test for Count Data", 0));
        8: 5803:	hv_stores(ret_hash, "alternative", newSVpv(alternative, 0));
branch  0 taken 8 (fallthrough)
branch  1 taken 0
        8: 5803-block  0
branch  2 taken 6 (fallthrough)
branch  3 taken 2
        6: 5804:	AV*restrict ci_array = newAV();
        6: 5805:	av_push(ci_array, newSVnv(ci_low));
        6: 5805-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 3
        3: 5806:	av_push(ci_array, newSVnv(ci_high));
        3: 5806-block  0
        3: 5807:	hv_stores(ret_hash, "conf_int", newRV_noinc((SV*)ci_array));
        3: 5807-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5808:	HV*restrict est_hash = newHV();
        3: 5808-block  0
        -: 5809:	hv_stores(ret_hash, "estimate", newRV_noinc((SV*)est_hash));
        -: 5810:	hv_stores(est_hash, "odds ratio", newSVnv(mle_or));
        8: 5811:	hv_stores(ret_hash, "p_value", newSVnv(p_val));
        8: 5811-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 3
        5: 5811-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 5812:	// Return the HashRef
        5: 5812-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 2
        3: 5813:	RETVAL = newRV_noinc((SV*)ret_hash);
        3: 5813-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5814:}
        3: 5814-block  0
        -: 5815:OUTPUT:
        -: 5816:  RETVAL
       12: 5817:
        8: 5817-block  0
        4: 5817-block  1
       12: 5817-block  2
branch  0 taken 4
branch  1 taken 8 (fallthrough)
        4: 5818:SV* power_t_test(...)
        4: 5818-block  0
call    0 returned 4
        4: 5819:CODE:
        4: 5820:{
branch  0 taken 2 (fallthrough)
branch  1 taken 2
        2: 5820-block  0
        2: 5821:    SV* sv_n = NULL;
        2: 5821-block  0
branch  0 taken 2 (fallthrough)
branch  1 taken 0
        2: 5821-block  1
    #####: 5822:    SV* sv_delta = NULL;
    %%%%%: 5822-block  0
branch  0 never executed (fallthrough)
branch  1 never executed
    %%%%%: 5822-block  1
    #####: 5823:    SV* sv_sd = NULL;
    %%%%%: 5823-block  0
call    0 never executed
        -: 5824:    SV* sv_sig_level = NULL;
        -: 5825:    SV* sv_power = NULL;
        8: 5826:
        8: 5826-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 5
        3: 5826-block  1
branch  2 taken 3 (fallthrough)
branch  3 taken 0
        3: 5826-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 3
    #####: 5827:    const char* restrict type = "two.sample";
    %%%%%: 5827-block  0
call    0 never executed
        -: 5828:    const char* restrict alternative = "two.sided";
        -: 5829:    bool strict = false;
        -: 5830:    double tol = pow(2.2204460492503131e-16, 0.25); 
        8: 5831:
        8: 5832:    if (items % 2 != 0) croak("Usage: power_t_test(n => 30, delta => 0.5, sd => 1.0, ...)");
        8: 5833:    for (unsigned short int i = 0; i < items; i += 2) {
        -: 5834:        const char* restrict key = SvPV_nolen(ST(i));
        -: 5835:        SV* restrict val = ST(i+1);
        -: 5836:
        -: 5837:        if      (strEQ(key, "n"))           sv_n = val;
        8: 5838:        else if (strEQ(key, "delta"))       sv_delta = val;
        8: 5838-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 5
        3: 5839:        else if (strEQ(key, "sd"))          sv_sd = val;
        3: 5839-block  0
branch  0 taken 3 (fallthrough)
branch  1 taken 0
        3: 5839-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 3
    #####: 5840:        else if (strEQ(key, "sig.level") || strEQ(key, "sig_level")) sv_sig_level = val;
    %%%%%: 5840-block  0
call    0 never executed
        3: 5841:        else if (strEQ(key, "power"))       sv_power = val;
        -: 5842:        else if (strEQ(key, "type"))        type = SvPV_nolen(val);
        3: 5843:        else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        3: 5844:        else if (strEQ(key, "strict"))      strict = SvTRUE(val);
        3: 5844-block  0
call    0 returned 3
        -: 5845:        else if (strEQ(key, "tol"))         tol = SvNV(val);
       12: 5846:        else croak("power_t_test: unknown argument '%s'", key);
       12: 5846-block  0
call    0 returned 12
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        9: 5847:    }
        9: 5848:
        9: 5848-block  0
branch  0 taken 9 (fallthrough)
branch  1 taken 0
        9: 5848-block  1
branch  2 taken 0 (fallthrough)
branch  3 taken 9
    #####: 5849:    bool is_null_n = (!sv_n || !SvOK(sv_n));
    %%%%%: 5849-block  0
call    0 never executed
        9: 5850:    bool is_null_delta = (!sv_delta || !SvOK(sv_delta));
        9: 5850-block  0
call    0 returned 9
        -: 5851:    bool is_null_power = (!sv_power || !SvOK(sv_power));
       3*: 5852:    bool is_null_sd = (sv_sd && !SvOK(sv_sd)); 
        3: 5852-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 3
    %%%%%: 5852-block  1
call    2 never executed
        -: 5853:    bool is_null_sig_level = (sv_sig_level && !SvOK(sv_sig_level));
        3: 5854:
        3: 5854-block  0
call    0 returned 3
        -: 5855:    int missing_count = 0;
        -: 5856:    if (is_null_n) missing_count++;
        3: 5857:    if (is_null_delta) missing_count++;
        3: 5858:    if (is_null_power) missing_count++;
call    0 returned 3
       12: 5859:    if (is_null_sd) missing_count++;
       12: 5859-block  0
call    0 returned 12
branch  1 taken 9
branch  2 taken 3 (fallthrough)
        9: 5860:    if (is_null_sig_level) missing_count++;
        9: 5861:
        9: 5861-block  0
call    0 returned 9
       51: 5862:    if (missing_count != 1) {
       42: 5862-block  0
       51: 5862-block  1
branch  0 taken 42
branch  1 taken 9 (fallthrough)
       42: 5863:        croak("power_t_test: exactly one of 'n', 'delta', 'sd', 'power', and 'sig_level' must be undef/NULL");
       42: 5863-block  0
call    0 returned 42
       42: 5864:    }
branch  0 taken 42 (fallthrough)
branch  1 taken 0
       42: 5864-block  0
branch  2 taken 42 (fallthrough)
branch  3 taken 0
       42: 5864-block  1
call    4 returned 42
branch  5 taken 42 (fallthrough)
branch  6 taken 0
       42: 5865:
       42: 5865-block  0
call    0 returned 42
       42: 5866:    double n = is_null_n ? 0.0 : SvNV(sv_n);
       42: 5867:    double delta = is_null_delta ? 0.0 : SvNV(sv_delta);
        -: 5868:    double sd = (!sv_sd || is_null_sd) ? 1.0 : SvNV(sv_sd);
        -: 5869:    double sig_level = (!sv_sig_level || is_null_sig_level) ? 0.05 : SvNV(sv_sig_level);
        9: 5870:    double power = is_null_power ? 0.0 : SvNV(sv_power);
        9: 5870-block  0
        -: 5871:    short int tsample = (strEQ(type, "one.sample") || strEQ(type, "paired")) ? 1 : 2;
        3: 5872:    short int tside = (strEQ(alternative, "one.sided") || strEQ(alternative, "greater") || strEQ(alternative, "less")) ? 1 : 2;
        3: 5872-block  0
        -: 5873:    if (tside == 2 && !is_null_delta) delta = fabs(delta);
        -: 5874:    if (is_null_power) {
        -: 5875:        power = p_body(n, delta, sd, sig_level, tsample, tside, strict);
        -: 5876:    } else if (is_null_n) {
        -: 5877:        double low = 2.0, high = 1e7;
        5: 5878:        while (p_body(high, delta, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
        5: 5878-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 5878-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 5878-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 5
    #####: 5879:        while (high - low > tol) {
    %%%%%: 5879-block  0
call    0 never executed
        5: 5880:            double mid = low + (high - low) / 2.0;
        5: 5880-block  0
branch  0 taken 5 (fallthrough)
branch  1 taken 0
        5: 5880-block  1
branch  2 taken 5 (fallthrough)
branch  3 taken 0
        5: 5880-block  2
branch  4 taken 0 (fallthrough)
branch  5 taken 5
    #####: 5881:            if (p_body(mid, delta, sd, sig_level, tsample, tside, strict) < power) low = mid;
    %%%%%: 5881-block  0
call    0 never executed
        -: 5882:            else high = mid;
        5: 5883:        }
        5: 5884:        n = low + (high - low) / 2.0;
        5: 5885:    } else if (is_null_sd) {
        5: 5885-block  0
call    0 returned 5
        5: 5886:        double low = delta * 1e-7, high = delta * 1e7;
call    0 returned 5
       5*: 5887:        while (high - low > tol) {
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 5887-block  0
call    2 never executed
       5*: 5888:            double mid = low + (high - low) / 2.0;
        5: 5888-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 5
    %%%%%: 5888-block  1
call    2 never executed
        -: 5889:            if (p_body(n, delta, mid, sig_level, tsample, tside, strict) > power) low = mid;
        5: 5890:            else high = mid;
        5: 5890-block  0
call    0 returned 5
        -: 5891:        }
        -: 5892:        sd = low + (high - low) / 2.0;
        5: 5893:    } else if (is_null_delta) {
call    0 returned 5
        5: 5894:        double low = sd * 1e-7, high = sd * 1e7;
        -: 5895:        while (p_body(n, high, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
       75: 5896:        while (high - low > tol) {
       70: 5896-block  0
       75: 5896-block  1
branch  0 taken 70
branch  1 taken 5 (fallthrough)
       70: 5897:            double mid = low + (high - low) / 2.0;
       70: 5897-block  0
call    0 returned 70
       70: 5898:            if (p_body(n, mid, sd, sig_level, tsample, tside, strict) < power) low = mid;
call    0 returned 70
       70: 5899:            else high = mid;
branch  0 taken 70 (fallthrough)
branch  1 taken 0
       70: 5899-block  0
branch  2 taken 70 (fallthrough)
branch  3 taken 0
       70: 5899-block  1
call    4 returned 70
branch  5 taken 70 (fallthrough)
branch  6 taken 0
       70: 5900:        }
       70: 5900-block  0
branch  0 taken 70 (fallthrough)
branch  1 taken 0
       70: 5900-block  1
branch  2 taken 70 (fallthrough)
branch  3 taken 0
       70: 5901:        delta = low + (high - low) / 2.0;
       70: 5901-block  0
call    0 returned 70
       70: 5902:    } else if (is_null_sig_level) {
       70: 5903:        double low = 1e-10, high = 1.0 - 1e-10;
call    0 returned 70
        -: 5904:        while (high - low > tol) {
       70: 5905:            double mid = low + (high - low) / 2.0;
branch  0 taken 55 (fallthrough)
branch  1 taken 15
       55: 5906:            if (p_body(n, delta, sd, mid, tsample, tside, strict) < power) low = mid;
       55: 5906-block  0
call    0 returned 55
        -: 5907:            else high = mid;
       15: 5908:        }
       15: 5909:        sig_level = low + (high - low) / 2.0;
       15: 5909-block  0
call    0 returned 15
call    1 returned 15
        -: 5910:    }
       70: 5911:    HV*restrict ret = newHV();
       70: 5911-block  0
call    0 returned 70
       70: 5912:    hv_stores(ret, "n", newSVnv(n));
       70: 5913:    hv_stores(ret, "delta", newSVnv(delta));
        -: 5914:    hv_stores(ret, "sd", newSVnv(sd));
        -: 5915:    hv_stores(ret, "sig.level", newSVnv(sig_level));
        5: 5916:    hv_stores(ret, "power", newSVnv(power));
        5: 5917:    hv_stores(ret, "alternative", newSVpv(alternative, 0));
        5: 5917-block  0
call    0 returned 5
        -: 5918:    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";
        -: 5919:    hv_stores(ret, "method", newSVpv(m_str, 0));
        -: 5920:    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" : "");
        -: 5921:    if (n_str[0] != '\0') hv_stores(ret, "note", newSVpv(n_str, 0));
        -: 5922:    RETVAL = newRV_noinc((SV*)ret);
       8*: 5923:}
        8: 5923-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    %%%%%: 5923-block  1
call    2 never executed
call    3 never executed
       8*: 5924:OUTPUT:
        8: 5924-block  0
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    %%%%%: 5924-block  1
call    2 never executed
call    3 never executed
        -: 5925:    RETVAL
        -: 5926:
        8: 5927:SV* kruskal_test(...)
        8: 5928:CODE:
        8: 5928-block  0
call    0 returned 8
        -: 5929:{
        -: 5930:    SV *restrict x_sv = NULL, *restrict g_sv = NULL, *restrict h_sv = NULL;
        8: 5931:    unsigned int arg_idx = 0;
call    0 returned 8
        8: 5932:
call    0 returned 8
      120: 5933:    // 1. Shift positional arguments
      120: 5933-block  0
branch  0 taken 112
branch  1 taken 8 (fallthrough)
      112: 5934:    //    Accept either: (arrayref, arrayref) or (hashref)
      112: 5935:    if (arg_idx < items && SvROK(ST(arg_idx))) {
      112: 5936:        svtype t = SvTYPE(SvRV(ST(arg_idx)));
      112: 5936-block  0
        -: 5937:        if (t == SVt_PVAV) {
        -: 5938:            x_sv = ST(arg_idx++);
        -: 5939:        } else if (t == SVt_PVHV) {
        8: 5940:            h_sv = ST(arg_idx++);          /* hash-of-arrays shortcut */
       32: 5941:        }
        8: 5941-block  0
       24: 5941-block  1
       32: 5941-block  2
branch  0 taken 24
branch  1 taken 8 (fallthrough)
       24: 5942:    }
       24: 5942-block  0
branch  0 taken 24 (fallthrough)
branch  1 taken 0
       24: 5943:    if (!h_sv && arg_idx < items
       24: 5944:              && SvROK(ST(arg_idx))
       24: 5944-block  0
        -: 5945:              && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        8: 5946:        g_sv = ST(arg_idx++);
        8: 5946-block  0
call    0 returned 8
call    1 returned 8
call    2 returned 8
        -: 5947:    }
        8: 5948:    // 2. Parse named arguments (fallback)
        8: 5949:    for (; arg_idx < items; arg_idx += 2) {
        8: 5950:        const char *restrict key = SvPV_nolen(ST(arg_idx));
branch  0 taken 0 (fallthrough)
branch  1 taken 8
    #####: 5951:        SV         *restrict val = ST(arg_idx + 1);
    #####: 5952:        if      (strEQ(key, "x")) x_sv = val;
    %%%%%: 5952-block  0
        -: 5953:        else if (strEQ(key, "g")) g_sv = val;
        8: 5954:        else if (strEQ(key, "h")) h_sv = val;
        8: 5955:        else croak("kruskal_test: unknown argument '%s'", key);
        8: 5955-block  0
call    0 returned 8
        -: 5956:    }
        -: 5957:    // 3. Mutual-exclusion guard
        8: 5958:    if (h_sv && (x_sv || g_sv))
call    0 returned 8
        8: 5959:        croak("kruskal_test: cannot mix 'h' (hash-of-arrays) with 'x'/'g' inputs");
call    0 returned 8
call    1 returned 8
        8: 5960:
call    0 returned 8
call    1 returned 8
        8: 5961:    /* ------------------------------------------------------------------ */
call    0 returned 8
call    1 returned 8
        8: 5962:    /* Shared state filled by whichever input branch runs                 */
call    0 returned 8
call    1 returned 8
        8: 5963:    /* ------------------------------------------------------------------ */
call    0 returned 8
call    1 returned 8
        8: 5964:    RankInfo *restrict ri = NULL;
call    0 returned 8
        -: 5965:    char **restrict group_names = NULL; /* Track names to build group_stats */
        -: 5966:    size_t valid_n = 0;
        -: 5967:    size_t k       = 0;
        -: 5968:
        -: 5969:    /* ------------------------------------------------------------------ */
        -: 5970:    /* 4a. Hash-of-arrays input path                                      */
        -: 5971:    /*     my %x = ( group1 => [...], group2 => [...], ... )              */
        -: 5972:    /* ------------------------------------------------------------------ */
        -: 5973:    if (h_sv) {
        -: 5974:        if (!SvROK(h_sv) || SvTYPE(SvRV(h_sv)) != SVt_PVHV)
        -: 5975:            croak("kruskal_test: 'h' must be a HASH reference");
        -: 5976:        HV *restrict h_hv = (HV*)SvRV(h_sv);
        -: 5977:        
        -: 5978:        // First pass – validate values and tally total elements
        -: 5979:        size_t total = 0;
        -: 5980:        hv_iterinit(h_hv);
        -: 5981:        HE *restrict he;
        -: 5982:        while ((he = hv_iternext(h_hv))) {
        -: 5983:            SV *restrict val = HeVAL(he);
        -: 5984:            if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVAV)
        -: 5985:                croak("kruskal_test: every value in 'h' must be an ARRAY reference");
        -: 5986:            total += (size_t)(av_len((AV*)SvRV(val)) + 1);
        -: 5987:        }
        -: 5988:        if (total < 2) croak("not enough observations");
        -: 5989:
        -: 5990:        ri = (RankInfo *)safemalloc(total * sizeof(RankInfo));
        -: 5991:        
        -: 5992:        size_t num_keys = HvKEYS(h_hv);
        -: 5993:        group_names = (char **)safecalloc(num_keys, sizeof(char*));
        -: 5994:
        -: 5995:        /* Second pass – fill ri[], assigning one group_id per hash key */
        -: 5996:        size_t group_id = 0;
        -: 5997:        hv_iterinit(h_hv);
        -: 5998:        while ((he = hv_iternext(h_hv))) {
        -: 5999:            STRLEN klen;
        -: 6000:            const char *key_str = HePV(he, klen);
        -: 6001:            group_names[group_id] = savepvn(key_str, klen); // Save string key
        -: 6002:
        -: 6003:            AV    *restrict av  = (AV*)SvRV(HeVAL(he));
        -: 6004:            size_t          n_g = (size_t)(av_len(av) + 1);
        -: 6005:            for (size_t i = 0; i < n_g; i++) {
        -: 6006:                SV **restrict el = av_fetch(av, i, 0);
        -: 6007:                if (el && SvOK(*el) && looks_like_number(*el)) {
        -: 6008:                    ri[valid_n].val = SvNV(*el);
        -: 6009:                    ri[valid_n].idx = group_id;   /* group identity */
        -: 6010:                    valid_n++;
        -: 6011:                }
        -: 6012:            }
        -: 6013:            group_id++;
        -: 6014:        }
        -: 6015:        k = group_id;   /* number of unique groups = number of hash keys */
        -: 6016:
        -: 6017:    /* ------------------------------------------------------------------ */
        -: 6018:    /* 4b. Original x / g array-pair input path                           */
        -: 6019:    /* ------------------------------------------------------------------ */
        -: 6020:    } else {
        -: 6021:        if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 6022:            croak("kruskal_test: 'x' is a required argument and must be an ARRAY reference");
        -: 6023:        if (!g_sv || !SvROK(g_sv) || SvTYPE(SvRV(g_sv)) != SVt_PVAV)
        -: 6024:            croak("kruskal_test: 'g' is a required argument and must be an ARRAY reference");
        -: 6025:
        -: 6026:        AV *restrict x_av = (AV*)SvRV(x_sv);
        -: 6027:        AV *restrict g_av = (AV*)SvRV(g_sv);
        -: 6028:        size_t nx = (size_t)(av_len(x_av) + 1);
        -: 6029:        size_t ng = (size_t)(av_len(g_av) + 1);
        -: 6030:        if (nx != ng) croak("kruskal_test: 'x' and 'g' must have the same length");
        -: 6031:        if (nx < 2)   croak("not enough observations");
        -: 6032:
        -: 6033:        ri = (RankInfo *)safemalloc(nx * sizeof(RankInfo));
        -: 6034:        group_names = (char **)safecalloc(nx, sizeof(char*)); // Upper bound
        -: 6035:
        -: 6036:        // Map string group names → contiguous integer IDs
        -: 6037:        HV    *restrict group_map    = newHV();
        -: 6038:        size_t          next_group_id = 0;
        -: 6039:
        -: 6040:        for (size_t i = 0; i < nx; i++) {
        -: 6041:            SV **restrict x_el = av_fetch(x_av, i, 0);
        -: 6042:            SV **restrict g_el = av_fetch(g_av, i, 0);
        -: 6043:            if (x_el && SvOK(*x_el) && looks_like_number(*x_el)
        -: 6044:                     && g_el && SvOK(*g_el)) {
        -: 6045:                const char *restrict g_str = SvPV_nolen(*g_el);
        -: 6046:                STRLEN               glen  = strlen(g_str);
        -: 6047:                SV   **restrict id_sv = hv_fetch(group_map, g_str, glen, 0);
        -: 6048:                size_t group_id;
        -: 6049:                if (id_sv) {
        -: 6050:                    group_id = SvUV(*id_sv);
        -: 6051:                } else {
        -: 6052:                    group_id = next_group_id++;
        -: 6053:                    hv_store(group_map, g_str, glen, newSVuv(group_id), 0);
        -: 6054:                    group_names[group_id] = savepvn(g_str, glen); // Save string key
        -: 6055:                }
        -: 6056:                ri[valid_n].val = SvNV(*x_el);
        -: 6057:                ri[valid_n].idx = group_id;
        -: 6058:                valid_n++;
        -: 6059:            }
        -: 6060:        }
        -: 6061:        k = next_group_id;
        -: 6062:        SvREFCNT_dec(group_map);
        -: 6063:    }
        -: 6064:
        -: 6065:    /* ------------------------------------------------------------------ */
        -: 6066:    /* 5. Shared post-extraction validation                               */
        -: 6067:    /* ------------------------------------------------------------------ */
        -: 6068:    if (valid_n < 2 || k < 2) { 
        -: 6069:        Safefree(ri); 
        -: 6070:        if (group_names) {
        -: 6071:            for (size_t i = 0; i < k; i++) { if (group_names[i]) Safefree(group_names[i]); }
        -: 6072:            Safefree(group_names);
        -: 6073:        }
        -: 6074:        if (valid_n < 2) croak("not enough observations");
        -: 6075:        croak("all observations are in the same group");
        -: 6076:    }
        -: 6077:
        -: 6078:    // 6. Ranking and Tie Accumulation (Reusing LikeR Helper)
        -: 6079:    bool   has_ties = 0;
        -: 6080:    double tie_adj  = rank_and_count_ties(ri, valid_n, &has_ties);
        -: 6081:
        -: 6082:    // 7. Aggregate Sum of Ranks AND Actual Values by Group
        -: 6083:    double *restrict group_rank_sums = (double *)safecalloc(k, sizeof(double));
        -: 6084:    double *restrict group_val_sums  = (double *)safecalloc(k, sizeof(double)); // For Mean
        -: 6085:    size_t *restrict group_counts    = (size_t *)safecalloc(k, sizeof(size_t));
        -: 6086:    
        -: 6087:    for (size_t i = 0; i < valid_n; i++) {
        -: 6088:        size_t g_id = ri[i].idx;
        -: 6089:        group_rank_sums[g_id] += ri[i].rank;
        -: 6090:        group_val_sums[g_id]  += ri[i].val;
        -: 6091:        group_counts[g_id]++;
        -: 6092:    }
        -: 6093:
        -: 6094:    // 8. Calculate STATISTIC
        -: 6095:    double stat_base = 0.0;
        -: 6096:    for (size_t i = 0; i < k; i++) {
        -: 6097:        if (group_counts[i] > 0)
        -: 6098:            stat_base += (group_rank_sums[i] * group_rank_sums[i])
        -: 6099:                         / (double)group_counts[i];
        -: 6100:    }
        -: 6101:
        -: 6102:    double n_d  = (double)valid_n;
        -: 6103:    double stat = (12.0 * stat_base / (n_d * (n_d + 1.0))) - 3.0 * (n_d + 1.0);
        -: 6104:    if (tie_adj > 0.0) {
        -: 6105:        double tie_denom = 1.0 - (tie_adj / (n_d * n_d * n_d - n_d));
        -: 6106:        stat /= tie_denom;
        -: 6107:    }
        -: 6108:    int    df    = (int)k - 1;
        -: 6109:    double p_val = get_p_value(stat, df);
        -: 6110:
        -: 6111:    // 9. Return structured data exactly like R's htest
        -: 6112:    HV *restrict res = newHV();
        -: 6113:    hv_stores(res, "statistic", newSVnv(stat));
        -: 6114:    hv_stores(res, "parameter", newSViv(df));
        -: 6115:    hv_stores(res, "p_value",   newSVnv(p_val));
        -: 6116:    hv_stores(res, "p.value",   newSVnv(p_val));
        -: 6117:    hv_stores(res, "method",    newSVpv("Kruskal-Wallis rank sum test", 0));
        -: 6118:
        -: 6119:    // 10. Build the group_stats hash
        -: 6120:    HV *group_stats = newHV();
        -: 6121:    HV *stats_mean  = newHV();
        -: 6122:    HV *stats_size  = newHV();
        -: 6123:
        -: 6124:    for (size_t i = 0; i < k; i++) {
        -: 6125:        if (group_counts[i] > 0 && group_names[i]) {
        -: 6126:            double mean = group_val_sums[i] / (double)group_counts[i];
        -: 6127:            size_t nlen = strlen(group_names[i]);
        -: 6128:            
        -: 6129:            hv_store(stats_mean, group_names[i], nlen, newSVnv(mean), 0);
        -: 6130:            hv_store(stats_size, group_names[i], nlen, newSVuv(group_counts[i]), 0);
        -: 6131:        }
        -: 6132:        if (group_names[i]) Safefree(group_names[i]); // Clean up name copy
        -: 6133:    }
        -: 6134:
        -: 6135:    // Embed the nested hashes
        -: 6136:    hv_stores(group_stats, "mean", newRV_noinc((SV*)stats_mean));
        -: 6137:    hv_stores(group_stats, "size", newRV_noinc((SV*)stats_size));
        -: 6138:    hv_stores(res, "group_stats",  newRV_noinc((SV*)group_stats));
        -: 6139:
        -: 6140:    // Memory Cleanup
        -: 6141:    Safefree(group_names);
        -: 6142:    Safefree(group_rank_sums); 
        -: 6143:    Safefree(group_val_sums);
        -: 6144:    Safefree(group_counts); 
        -: 6145:    Safefree(ri);
        -: 6146:
        -: 6147:    RETVAL = newRV_noinc((SV*)res);
        -: 6148:}
        -: 6149:OUTPUT:
        -: 6150:    RETVAL
        -: 6151:
        -: 6152:SV* var_test(...)
        -: 6153:CODE:
        -: 6154:{
        -: 6155:	SV* restrict x_sv = NULL;
        -: 6156:	SV* restrict y_sv = NULL;
        -: 6157:	double ratio = 1.0, conf_level = 0.95;
        -: 6158:	const char* restrict alternative = "two.sided";
        -: 6159:	unsigned int arg_idx = 0;
        -: 6160:
        -: 6161:	// 1. Shift positional argument 'x' if it's an array reference
        -: 6162:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 6163:	  x_sv = ST(arg_idx);
        -: 6164:	  arg_idx++;
        -: 6165:	}
        -: 6166:
        -: 6167:	// 2. Shift positional argument 'y' if it's an array reference
        -: 6168:	if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
        -: 6169:	  y_sv = ST(arg_idx);
        -: 6170:	  arg_idx++;
        -: 6171:	}
        -: 6172:
        -: 6173:	// Ensure the remaining arguments form complete key-value pairs
        -: 6174:	if ((items - arg_idx) % 2 != 0) {
        -: 6175:	  croak("Usage: var_test(\\@x, \\@y, key => value, ...)");
        -: 6176:	}
        -: 6177:
        -: 6178:	// --- Parse named arguments from the remaining flat stack ---
        -: 6179:	for (; arg_idx < items; arg_idx += 2) {
        -: 6180:	  const char* restrict key = SvPV_nolen(ST(arg_idx));
        -: 6181:	  SV* restrict val = ST(arg_idx + 1);
        -: 6182:
        -: 6183:	  if      (strEQ(key, "x"))           x_sv        = val;
        -: 6184:	  else if (strEQ(key, "y"))           y_sv        = val;
        -: 6185:	  else if (strEQ(key, "ratio"))       ratio       = SvNV(val);
        -: 6186:	  else if (strEQ(key, "conf_level") || strEQ(key, "conf.level")) conf_level = SvNV(val);
        -: 6187:	  else if (strEQ(key, "alternative")) alternative = SvPV_nolen(val);
        -: 6188:	  else croak("var_test: unknown argument '%s'", key);
        -: 6189:	}
        -: 6190:
        -: 6191:	// --- Validate required inputs / types ---
        -: 6192:	if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
        -: 6193:	  croak("var_test: 'x' is a required argument and must be an ARRAY reference");
        -: 6194:	if (!y_sv || !SvROK(y_sv) || SvTYPE(SvRV(y_sv)) != SVt_PVAV)
        -: 6195:	  croak("var_test: 'y' is a required argument and must be an ARRAY reference");
        -: 6196:
        -: 6197:	if (ratio <= 0.0 || !isfinite(ratio)) 
        -: 6198:	  croak("var_test: 'ratio' must be a single positive number");
        -: 6199:	if (conf_level <= 0.0 || conf_level >= 1.0 || !isfinite(conf_level))
        -: 6200:	  croak("var_test: 'conf.level' must be a single number between 0 and 1");
        -: 6201:
        -: 6202:	AV* restrict x_av = (AV*)SvRV(x_sv);
        -: 6203:	AV* restrict y_av = (AV*)SvRV(y_sv);
        -: 6204:	size_t nx_raw = av_len(x_av) + 1;
        -: 6205:	size_t ny_raw = av_len(y_av) + 1;
        -: 6206:
        -: 6207:	// --- Computation via Welford's Algorithm (ignoring NaNs) ---
        -: 6208:	double mean_x = 0.0, M2_x = 0.0;
        -: 6209:	size_t nx = 0;
        -: 6210:	for (size_t i = 0; i < nx_raw; i++) {
        -: 6211:		SV** restrict tv = av_fetch(x_av, i, 0);
        -: 6212:		if (tv && SvOK(*tv) && looks_like_number(*tv)) {
        -: 6213:			double val = SvNV(*tv);
        -: 6214:			if (!isnan(val) && isfinite(val)) {
        -: 6215:				nx++;
        -: 6216:				double delta = val - mean_x;
        -: 6217:				mean_x += delta / nx;
        -: 6218:				M2_x += delta * (val - mean_x);
        -: 6219:			}
        -: 6220:		}
        -: 6221:	}
        -: 6222:
        -: 6223:	double mean_y = 0.0, M2_y = 0.0;
        -: 6224:	size_t ny = 0;
        -: 6225:	for (size_t i = 0; i < ny_raw; i++) {
        -: 6226:	  SV** restrict tv = av_fetch(y_av, i, 0);
        -: 6227:	  if (tv && SvOK(*tv) && looks_like_number(*tv)) {
        -: 6228:		   double val = SvNV(*tv);
        -: 6229:		   if (!isnan(val) && isfinite(val)) {
        -: 6230:		       ny++;
        -: 6231:		       double delta = val - mean_y;
        -: 6232:		       mean_y += delta / ny;
        -: 6233:		       M2_y += delta * (val - mean_y);
        -: 6234:		   }
        -: 6235:	  }
        -: 6236:	}
        -: 6237:
        -: 6238:	if (nx < 2) croak("not enough 'x' observations");
        -: 6239:	if (ny < 2) croak("not enough 'y' observations");
        -: 6240:
        -: 6241:	double df_x = (double)(nx - 1);
        -: 6242:	double df_y = (double)(ny - 1);
        -: 6243:	double var_x = M2_x / df_x;
        -: 6244:	double var_y = M2_y / df_y;
        -: 6245:
        -: 6246:	if (var_y == 0.0) croak("var_test: variance of 'y' is zero (cannot divide by zero)");
        -: 6247:
        -: 6248:	// --- Statistics Math ---
        -: 6249:	double estimate = var_x / var_y;
        -: 6250:	double statistic = estimate / ratio;
        -: 6251:
        -: 6252:	double p_val = pf(statistic, df_x, df_y);
        -: 6253:	double ci_lower = 0.0, ci_upper = INFINITY;
        -: 6254:
        -: 6255:	if (strcmp(alternative, "less") == 0) {
        -: 6256:	  ci_upper = estimate / qf_bisection(1.0 - conf_level, df_x, df_y);
        -: 6257:	} else if (strcmp(alternative, "greater") == 0) {
        -: 6258:	  p_val = 1.0 - p_val;
        -: 6259:	  ci_lower = estimate / qf_bisection(conf_level, df_x, df_y);
        -: 6260:	} else {
        -: 6261:	  // two.sided
        -: 6262:	  double p1 = p_val;
        -: 6263:	  double p2 = 1.0 - p_val;
        -: 6264:	  p_val = 2.0 * (p1 < p2 ? p1 : p2);
        -: 6265:	  
        -: 6266:	  double beta = (1.0 - conf_level) / 2.0;
        -: 6267:	  ci_lower = estimate / qf_bisection(1.0 - beta, df_x, df_y);
        -: 6268:	  ci_upper = estimate / qf_bisection(beta, df_x, df_y);
        -: 6269:	}
        -: 6270:
        -: 6271:	// --- Pack Results ---
        -: 6272:	HV* restrict results = newHV();
        -: 6273:	hv_store(results, "statistic", 9, newSVnv(statistic), 0);
        -: 6274:
        -: 6275:	AV* restrict param_av = newAV();
        -: 6276:	av_push(param_av, newSVnv(df_x));
        -: 6277:	av_push(param_av, newSVnv(df_y));
        -: 6278:	hv_store(results, "parameter", 9, newRV_noinc((SV*)param_av), 0);
        -: 6279:
        -: 6280:	hv_store(results, "p_value", 7, newSVnv(p_val), 0);
        -: 6281:
        -: 6282:	AV* restrict conf_int = newAV();
        -: 6283:	av_push(conf_int, newSVnv(ci_lower));
        -: 6284:	av_push(conf_int, newSVnv(ci_upper));
        -: 6285:	hv_store(results, "conf_int", 8, newRV_noinc((SV*)conf_int), 0);
        -: 6286:
        -: 6287:	hv_store(results, "estimate", 8, newSVnv(estimate), 0);
        -: 6288:	hv_store(results, "null_value", 10, newSVnv(ratio), 0);
        -: 6289:	hv_store(results, "alternative", 11, newSVpv(alternative, 0), 0);
        -: 6290:	hv_store(results, "method", 6, newSVpv("F test to compare two variances", 0), 0);
        -: 6291:
        -: 6292:	RETVAL = newRV_noinc((SV*)results);
        -: 6293:}
        -: 6294:OUTPUT:
        -: 6295:    RETVAL
