Branch Coverage

File:Stats-LikeR-0.07/LikeR.xs
Coverage:66.8%

line%coveragebranch
3050TFsample__mix64(void)
40100TF{
52100TF/* Uniform integer in [0, upper) — rejection loop, no modulo bias */
73100TF//   Helpers for Random Number Generation
79100TF// This perfectly replicates R's pt(..., ncp) exactness without requiring complex Beta functions.
81100TF        if (df <= 0.0) return 0.0;
50TF        if (df <= 0.0) return 0.0;
83100TF        double step = 1.0 / n_steps;
87100TF        double root_half = 0.70710678118654752440; // 1 / sqrt(2)
93100TF                double log_M = log_coef + (df - 1.0) * log(w) - half_df * w * w;
94100TF                double M = exp(log_M);
97100TF                double pnorm_val = 0.5 * erfc(-z * root_half);
98100TF                double weight = (i % 2 != 0) ? 4.0 : 2.0;
111100TF
113100TF        return ((RankInfo*)a)->idx - ((RankInfo*)b)->idx;
117100TF        RankInfo *restrict items = safemalloc(n * sizeof(RankInfo));
124100TF        for (size_t i = 0; i < n; ) {
126100TF                while (j < n && items[j].val == items[i].val) j++;
127100TF                double avg_rank = (i + 1 + j) / 2.0;
135100TF// Generates a single binomial random variate.
143100TF                if (Drand01() <= prob) successes++;
14850TFstatic double log_choose(size_t n, size_t k) {
0TFstatic double log_choose(size_t n, size_t k) {
14950TF        return lgamma((double)n + 1.0) - lgamma((double)k + 1.0) - lgamma((double)(n - k) + 1.0);
150100TF}
15350TFstatic 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) {
156100TF        for(size_t k = 0; k <= max_x - min_x; ++k) {
158100TF          if (d_val > max_d) max_d = d_val;
161100TF        double sum_d = 0.0;
168100TF
170100TF          double p_prob = exp(logdc[k] + log_omega * (min_x + k) - max_d) / sum_d;
179100TF    size_t r1 = a + b, r2 = c + d, c1 = a + c;
180100TF    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
18150TF    size_t max_x = (r1 < c1) ? r1 : c1;
18350TF    bool is_less = (strcmp(alt, "less") == 0);
188100TF    for(size_t x = min_x; x <= max_x; ++x) {
189100TF        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
191100TF
198100TF        for (unsigned short int i = 0; i < 3000; i++) {
199100TF            double log_mid = 0.5 * (log_low + log_high);
200100TF            double max_d = -1e300;
20250TF                double d_val = logdc[k] + log_mid * (min_x + k);
207100TF                double p_prob = exp(logdc[k] + log_mid * (min_x + k) - max_d);
208100TF                sum_d += p_prob;
210100TF            }
221100TF    *ci_high = INFINITY;
226100TF        if (a != min_x) {
232100TF                double err = fabs(ut - target_alpha);
233100TF                if (err < best_err) { best_err = err; best = mid; }
234100TF                if (ut > target_alpha) log_high = log_mid;
235100TF                else log_low = log_mid;
239100TF        }
241100TF
24650TF            double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
261100TF}
266100TF    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
269100TF    double *logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
50TF    double *logdc = (double*)safemalloc((max_x - min_x + 1) * sizeof(double));
272100TF        logdc[x - min_x] = log_choose(r1, x) + log_choose(r2, c1 - x) - denom;
281100TF    } else {
282100TF        double p_obs = exp(logdc[a - min_x]);
283100TF        double relErr = 1.0 + 1e-7;
100TF        double relErr = 1.0 + 1e-7;
286100TF            if (p_cur <= p_obs * relErr) p_val += p_cur;
299100TF * Utilizes a relative tolerance check to prevent dropping micro-variance features.
30150TFstatic int sweep_matrix_ols(double *restrict A, size_t n, bool *restrict aliased) {
50TFstatic int sweep_matrix_ols(double *restrict A, size_t n, bool *restrict aliased) {
50TFstatic int sweep_matrix_ols(double *restrict A, size_t n, bool *restrict aliased) {
30550TF        // Save the original diagonal values to use as a baseline for relative variance
30750TF                aliased[k] = FALSE;
50TF                aliased[k] = FALSE;
50TF                aliased[k] = FALSE;
31250TF                // Check pivot for collinearity using a RELATIVE tolerance
100TF                // Check pivot for collinearity using a RELATIVE tolerance
313100TF                // (Fallback to a tiny absolute tolerance of 1e-24 to catch literal zero vectors)
32250TF                }
325100TF                A[k * n + k] = 1.0;
3280TF                        if (i != k && A[i * n + k] != 0.0) {
0TF                        if (i != k && A[i * n + k] != 0.0) {
0TF                        if (i != k && A[i * n + k] != 0.0) {
3310TF                                  for (size_t j = 0; j < n; j++) {
34050TF
50TF
344100TF    if (row_hashes) {
35050TF    } else if (data_hoa) {
50TF    } else if (data_hoa) {
35350TF            AV*restrict av = (AV*)SvRV(*col);
3550TF        }
3590TF        return NAN; // Catch strings like "blue"
3660TF    AV *cols = newAV();
3670TF    if (data_hoa) {
376100TF        while ((entry = hv_iternext(row_hashes[0]))) {
378100TF        }
380100TF    return cols;
50TF    return cols;
50TF    return cols;
38450TFstatic double evaluate_term(HV *restrict data_hoa, HV **restrict row_hashes, unsigned int i, const char *restrict term) {
38650TF
50TF
50TF
391100TF        double left = evaluate_term(data_hoa, row_hashes, i, term_cpy);
50TF        double left = evaluate_term(data_hoa, row_hashes, i, term_cpy);
392100TF        double right = evaluate_term(data_hoa, row_hashes, i, colon + 1);
40250TF        char *restrict caret = strchr(inner, '^');
4040TF        if (caret) {
0TF        if (caret) {
0TF        if (caret) {
40850TF        double v = get_data_value(data_hoa, row_hashes, i, inner);
41050TF        
50TF        
50TF        
41550TF    Safefree(term_cpy);
50TF    Safefree(term_cpy);
430100TF                        SV **restrict col = hv_fetch(data_hoa, var, strlen(var), 0);
43150TF                        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
447100TF        if (row_hashes) {
448100TF                val = hv_fetch(row_hashes[i], var, strlen(var), 0);
45650TF                        AV*restrict av = (AV*)SvRV(*col);
457100TF                        val = av_fetch(av, i, 0);
461100TF          return savepv(SvPV_nolen(*val)); /* Allocates and returns string */
464100TF}
100TF}
467100TFtypedef struct {
477100TF        /* Stabilize sort by falling back to original index */
48350TF/* Item used to sort values while remembering their original index,
497100TF/* Compute 1-based average ranks with tie-breaking into out[].
498100TF * in[] is not modified.                                                 */
501100TF        Newx(ri, n, RankItem);
50TF        Newx(ri, n, RankItem);
502100TF        for (size_t i = 0; i < n; i++) { ri[i].val = in[i]; ri[i].idx = i; }
50350TF        qsort(ri, n, sizeof(RankItem), cmp_rank_item);
50450TF
50950TF                while (j + 1 < n && ri[j + 1].val == ri[j].val) j++;
517100TF
51950TF * Returns NAN when either variable has zero variance (matches R).       */
50TF * Returns NAN when either variable has zero variance (matches R).       */
526100TF        double num = (double)n * sxy - sx * sy;
54250TF        for (size_t i = 0; i < n - 1; i++) {
54450TF                   int sx = (x[i] > x[j]) - (x[i] < x[j]);   /* sign of x[i]-x[j] */
54850TF                   else if (sy == 0)            tie_y++;
55050TF                   else                         D++;
55450TF        if (denom == 0.0) return NAN;
55650TF}
558100TF/* Single dispatch: compute correlation according to method string.
56450TF          Newx(rx, n, double); Newx(ry, n, double);
565100TF          rank_data(x, rx, n);
567100TF          double r = pearson_corr(rx, ry, n);
574100TF        return pearson_corr(x, y, n);
100TF        return pearson_corr(x, y, n);
575100TF}
50TF}
583100TF        int m;
58650TF        c = 1.0; d = 1.0 - qab * x / qap;
58950TF        for (m = 1; m <= MAX_ITER; m++) {
592100TF          d = 1.0 + aa * d;
597100TF          aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
6090TF        if (x <= 0.0) return 0.0;
61850TF        double prob_2tail = incbeta(df / 2.0, 0.5, x);
620100TF        if (strcmp(alt, "greater") == 0) return (t > 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
625100TFstatic double qt_tail(double df, double p_tail) {
626100TF        double low = 0.0, high = 1.0;
62950TF          low = high;
50TF          low = high;
50TF          low = high;
633100TF        // Bisect to find the root
639100TF          } else {
100TF          } else {
643100TF        }
50TF        }
64850TF        double da = *(const double*restrict)a;
653100TFstatic size_t calculate_sturges_bins(size_t n) {
655100TF        return (size_t)(log((double)n) / log(2.0) + 1.0);
65850TF// Logic for distributing data into bins (Optimized to O(N))
681100TF                   /* Adjust for exact boundaries (R's right-inclusive default: (a, b]) */
687100TF                   // Conversely, if floating-point truncation placed it too low, push it up
691100TF                   counts[idx]++;
709100TFdouble approx_pnorm(double x) {
724100TF        double x, r, y;
50TF        double x, r, y;
50TF        double x, r, y;
727100TF          r = y * y;
728100TF          x = y * (((a[3]*r + a[2])*r + a[1])*r + a[0]) /
730100TF        } else {
735100TF                   r * (c[5] + r * (c[6] + r * (c[7] + r * c[8])))))));
100TF                   r * (c[5] + r * (c[6] + r * (c[7] + r * c[8])))))));
100TF                   r * (c[5] + r * (c[6] + r * (c[7] + r * c[8])))))));
75150TFstatic double spearman_exact_pvalue(double s_obs, size_t n, const char *restrict alt) {
75250TF        int *restrict perm = (int*)safemalloc(n * sizeof(int));
75450TF        for (size_t i = 0; i < n; i++) { perm[i] = i + 1; c[i] = 0; }
75550TF
764100TF          if (s_ <= s_obs + 1e-9) count_le++;                 \
767100TF        } while (0)
769100TF        TALLY_PERM();   /* initial permutation [1, 2, ..., n] */
771100TF        unsigned int k = 1;
773100TF          if (c[k] < k) {
100TF          if (c[k] < k) {
78450TF                   c[k] = 0;
78550TF                   k++;
787100TF        }
789100TF
79150TF        /* p_le = P(S ≤ s_obs) ≡ P(rho ≥ rho_obs)  â€” upper rho tail
792100TF        * p_ge = P(S ≥ s_obs) ≡ P(rho ≤ rho_obs)  â€” lower rho tail  */
79450TF        double p_ge = (double)count_ge / (double)total;
79550TF
79950TF        double p = 2.0 * (p_le < p_ge ? p_le : p_ge);
808100TF        double *restrict dp = (double*)safemalloc((max_inv + 1) * sizeof(double));
81050TF        dp[0] = 1.0;
816100TF          for (int k = 0; k <= current_max_inv; k++) {
817100TF                   double sum = 0;
819100TF                       sum += dp[k - j];
825100TF          dp = next_dp;
830100TF        if (i_obs > max_inv) i_obs = max_inv;
834100TF        for (long k = 0; k <= i_obs; k++) p_ge += dp[k];
836100TF        if (strcmp(alt, "greater") == 0) return p_ge;
839100TF        double p = 2.0 * (p_ge < p_le ? p_ge : p_le);
844100TF        if (f <= 0.0) return 0.0;
847100TF}
86350TF                }
50TF                }
866100TF                        continue;
86750TF                } // Collinear or zero column
87450TF                norm = sqrt(norm);
876100TF                double u1 = X[r][k] - s;
100TF                double u1 = X[r][k] - s;
50TF                double u1 = X[r][k] - s;
50TF                double u1 = X[r][k] - s;
880100TF                        double dot = u1 * X[r][j];
882100TF                        double tau = dot / (s * u1);
883100TF                        X[r][j] += tau * u1;
899100TF// --- write_table Helpers ---
900100TF
90150TF// Sorts string arrays alphabetically
91250TF          if (!isdigit(s[i])) return 1;
50TF          if (!isdigit(s[i])) return 1;
91350TF        }
916100TF
920100TF        bool needs_quotes = 0;
93350TF                   }
93750TF          PerlIO_puts(fh, str);
93950TF}
943100TF        size_t sep_len = strlen(sep);
95150TF        }
95250TF        PerlIO_putc(fh, '\n');
96350TF                double term = 1.0 / a;
50TF                double term = 1.0 / a;
96450TF                double n = 1.0;
966100TF                        term *= x / (a + n);
977100TF        double h = d, i = 1.0;
97850TF        while (i < 10000) { // Safety bound
983100TF                c = b + an / c;
984100TF                if (fabs(c) < 1e-30) c = 1e-30;
985100TF                d = 1.0 / d;
989100TF                i += 1.0;
100350TF#define M_SQRT1_2 0.70710678118654752440
1004100TF#endif
1009100TF        if (k > n / 2) k = n - k;
1010100TF        long double res = 1.0L;
1014100TF        return res;
103050TF        for (int i = max_u; i >= j + m; i--) w[i] -= w[i - j - m];
1035100TF    
1037100TF    double result = (double)(cum_p / total);
100TF    double result = (double)(cum_p / total);
1039100TF    Safefree(w);
1041100TF}
105950TF        for (int i = 0; i <= k; i++) cum_p += w[i];
106050TF
1062100TF        double result = (double)(cum_p / total);
10700TF        double db = ((const RankInfo*)b)->val;
10710TF        return (da > db) - (da < db);
10730TF
10780TF        double tie_adj = 0.0;
10820TF                while (j < n && ri[j].val == ri[i].val) j++;
10870TF                i = j;
10920TF#ifndef M_PI_2
1105100TF        if (n < 0) return 1.0 / r_pow_di(x, -n);
1106100TF        double val = 1.0;
1108100TF        return val;
1115100TF        if(x <= 0.) {
1116100TF          if(lower) p = 0.;
1124100TF                   s += exp(k * k * z - w);
1125100TF          }
113150TF          s = -1.0;
11320TF          if(lower) {
1146100TF}
1147100TF
1148100TF// Auxiliary routines used by K2x() for matrix operations
1152100TF                   double s = 0.;
115650TF        }
1158100TF
1159100TFstatic void m_power(double *A, int eA, double *V, int *eV, int m, int n) {
1160100TF    if(n == 1) {
1161100TF        for(int i = 0; i < m * m; i++) V[i] = A[i];
1170100TF        for(int i = 0; i < m * m; i++) V[i] = B[i];
117250TF    } else {
1191100TF        for(int i = 0; i < m; i++) {
50TF        for(int i = 0; i < m; i++) {
119350TF                   if(i - j + 1 < 0) H[i * m + j] = 0;
100TF                   if(i - j + 1 < 0) H[i * m + j] = 0;
100TF                   if(i - j + 1 < 0) H[i * m + j] = 0;
119450TF                   else H[i * m + j] = 1;
1197100TF        for(int i = 0; i < m; i++) {
100TF        for(int i = 0; i < m; i++) {
1198100TF          H[i * m] -= r_pow_di(h, i + 1);
100TF          H[i * m] -= r_pow_di(h, i + 1);
1204100TF          for(int j = 0; j < m; j++) {
1205100TF                   if(i - j + 1 > 0) {
1206100TF                       for(int g = 1; g <= i - j + 1; g++) H[i * m + j] /= g;
1215100TF        for(int i = 1; i <= n; i++) {
1225100TF        return s;
1226100TF}
1229100TFstatic void calc_2sample_stats(double *x, size_t nx, double *y, size_t ny,
1230100TF                               double *d, double *d_plus, double *d_minus) {
1231100TF        qsort(x, nx, sizeof(double), compare_doubles);
1232100TF        qsort(y, ny, sizeof(double), compare_doubles);
124750TF          double diff = cdf1 - cdf2;
125550TF        *d_minus = max_d_minus;
0TF        *d_minus = max_d_minus;
126850TF        u[0] = 0.;
126950TF
1272100TF          else u[j] = u[j - 1];
127550TF          if(psmirnov_exact_test(q, i / md, 0., two_sided)) u[0] = 1.;
127950TF                       double v = (double)(i) / (double)(i + j);
1283100TF          }
1288100TF}
130450TF          // Default: 1 - P(T < qu)
50TF          // Default: 1 - P(T < qu)
50TF          // Default: 1 - P(T < qu)
130950TF
1310100TF// Bisection algorithm to find the inverse F-distribution (Quantile function)
50TF// Bisection algorithm to find the inverse F-distribution (Quantile function)
131350TF        if (p <= 0.0) return 0.0;
1320100TF          if (high > 1e100) break; /* Fallback limit */
132350TF        // Bisect to find the root
132450TF        for (unsigned short int i = 0; i < 150; i++) {
132550TF                double mid = low + (high - low) / 2.0;
13260TF                double p_mid = pf(mid, df1, df2);
132950TF                        low = mid;
133350TF                if (high - low < 1e-12) break;
50TF                if (high - low < 1e-12) break;
50TF                if (high - low < 1e-12) break;
1341100TFCODE:
100TFCODE:
50TFCODE:
134750TF
1352100TF        }
135450TF        if (arg_idx < items) {
50TF        if (arg_idx < items) {
50TF        if (arg_idx < items) {
136350TF
100TF
50TF
1369100TF          else if (strEQ(key, "y"))           y_sv = val;
137150TF                   if (!SvOK(val)) exact = -1;
50TF                   if (!SvOK(val)) exact = -1;
50TF                   if (!SvOK(val)) exact = -1;
137650TF        }
50TF        }
1385100TF
1386100TF        if (!is_two_sided && !is_greater && !is_less) {
139150TF        size_t nx = av_len(x_av) + 1;
139250TF        if (nx == 0) croak("Not enough 'x' observations");
1398100TF          SV**restrict el = av_fetch(x_av, i, 0);
1399100TF          if (el && SvOK(*el) && looks_like_number(*el)) {
1403100TF
140450TF        double statistic = 0.0, p_value = 0.0;
140750TF        // --- TWO SAMPLE ---
50TF        // --- TWO SAMPLE ---
141150TF          
14180TF                   }
142750TF          calc_2sample_stats(x_data, valid_nx, y_data, valid_ny, &d, &d_plus, &d_minus);
50TF          calc_2sample_stats(x_data, valid_nx, y_data, valid_ny, &d, &d_plus, &d_minus);
142950TF          // Map alternative to the correct statistic
1432100TF          else statistic = d;
144050TF          // Check for ties in combined set
1441100TF          size_t total_n = valid_nx + valid_ny;
1442100TF          double *restrict comb = (double *)safemalloc(total_n * sizeof(double));
144350TF          for(size_t i=0; i<valid_nx; i++) comb[i] = x_data[i];
1445100TF          qsort(comb, total_n, sizeof(double), compare_doubles);
144650TF
144850TF          for(size_t i = 1; i < total_n; i++) {
144950TF                   if(comb[i] == comb[i-1]) { has_ties = TRUE; break; }
145150TF          Safefree(comb);
145250TF          if (use_exact && has_ties) {
145450TF                   use_exact = FALSE;
14640TF                       p_value = K2l(z, 0, 1e-9);
147650TF                       double cdf_obs_low  = (double)i / valid_nx;
147750TF                       double cdf_obs_high = (double)(i + 1) / valid_nx;
149850TF                           p_value = 1.0 - K2x(valid_nx, statistic);
100TF                           p_value = 1.0 - K2x(valid_nx, statistic);
50TF                           p_value = 1.0 - K2x(valid_nx, statistic);
150350TF                       }
100TF                       }
50TF                       }
150850TF                       else p_value = exp(-2.0 * z * z);
1512100TF                    croak("ks_test: Unsupported 1-sample distribution '%s'. Use arrays for 2-sample.", dist);
1515100TF          Safefree(x_data);
1516100TF          croak("ks_test: Invalid arguments for 'y'.");
1517100TF        }
151850TF        Safefree(x_data);
1519100TF        if (p_value > 1.0) p_value = 1.0;
152050TF        if (p_value < 0.0) p_value = 0.0;
15210TF        HV *restrict res = newHV();
152450TF        hv_stores(res, "method", newSVpv(method_desc, 0));
1528100TFOUTPUT:
50TFOUTPUT:
50TFOUTPUT:
153250TFCODE:
1536100TF        double mu = 0.0;
50TF        double mu = 0.0;
50TF        double mu = 0.0;
1544100TF        }
100TF        }
1547100TF                y_sv = ST(arg_idx);
154950TF        }
50TF        }
50TF        }
1555100TF        for (; arg_idx < items; arg_idx += 2) {
155750TF                SV *restrict val = ST(arg_idx + 1);
50TF                SV *restrict val = ST(arg_idx + 1);
50TF                SV *restrict val = ST(arg_idx + 1);
156350TF                else if (strEQ(key, "exact"))       {
156450TF                        if (!SvOK(val)) exact = -1;
1569100TF        }
100TF        }
157250TF                croak("wilcox_test: 'x' is a required argument and must be an ARRAY reference");
157350TF        AV *restrict x_av = (AV*)SvRV(x_sv);
157450TF        size_t nx = av_len(x_av) + 1;
50TF        size_t nx = av_len(x_av) + 1;
100TF        size_t nx = av_len(x_av) + 1;
1576100TF
50TF
1580100TF                y_av = (AV*)SvRV(y_sv);
1585100TF        bool use_exact = FALSE;
158650TF        // --- TWO SAMPLE (Mann-Whitney) ---
15880TF                RankInfo *restrict ri = (RankInfo *)safemalloc((nx + ny) * sizeof(RankInfo));
159250TF                        if (el && SvOK(*el) && looks_like_number(*el)) {
159850TF                for (size_t i = 0; i < ny; i++) {
159950TF                        SV**restrict el = av_fetch(y_av, i, 0);
100TF                        SV**restrict el = av_fetch(y_av, i, 0);
16000TF                        if (el && SvOK(*el) && looks_like_number(*el)) {
16010TF                                ri[valid_nx + valid_ny].val = SvNV(*el);
160550TF                }
160650TF                if (valid_nx == 0) { Safefree(ri); croak("not enough (non-missing) 'x' observations"); }
1611100TF                double w_rank_sum = 0.0;
50TF                double w_rank_sum = 0.0;
100TF                double w_rank_sum = 0.0;
1615100TF                if (exact == 1) use_exact = TRUE;
161750TF                else use_exact = (valid_nx < 50 && valid_ny < 50 && !has_ties);
50TF                else use_exact = (valid_nx < 50 && valid_ny < 50 && !has_ties);
50TF                else use_exact = (valid_nx < 50 && valid_ny < 50 && !has_ties);
1620100TF                        warn("cannot compute exact p-value with ties; falling back to approximation");
162250TF                }
50TF                }
50TF                }
162550TF                        double p_less = exact_pwilcox(statistic, valid_nx, valid_ny);
162950TF                        else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
163350TF                        }
1638100TF                        double z = statistic - exp;
1645100TF                        }
1646100TF                        z = (z - CORRECTION) / sqrt(var);
164850TF                        if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
164950TF                        else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
165050TF                        else p_value = 2.0 * approx_pnorm(-fabs(z));
50TF                        else p_value = 2.0 * approx_pnorm(-fabs(z));
165150TF                }
50TF                }
165550TF                double *restrict diffs = (double *)safemalloc(nx * sizeof(double));
50TF                double *restrict diffs = (double *)safemalloc(nx * sizeof(double));
165950TF                        SV**restrict x_el = av_fetch(x_av, i, 0);
166450TF                                SV**restrict y_el = av_fetch(y_av, i, 0);
166550TF                                if (!y_el || !SvOK(*y_el) || !looks_like_number(*y_el)) continue;
166750TF                                double d = dx - dy - mu;
16710TF                                double d = dx - mu;
16760TF                if (n_nz == 0) {
16770TF                        Safefree(diffs);
0TF                        Safefree(diffs);
16780TF                        croak("not enough (non-missing) observations");
16790TF                }
16830TF                        ri[i].idx = (diffs[i] > 0);
16840TF                }
168950TF                        if (ri[i].idx) statistic += ri[i].rank;
170550TF                        double p_greater = 1.0 - exact_psignrank(statistic - 1.0, n_nz);
170850TF                        else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
100TF                        else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
50TF                        else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
171150TF                                p_value = 2.0 * p;
1719100TF                        if (correct) {
50TF                        if (correct) {
100TF                        if (correct) {
1722100TF                                else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
1725100TF
1726100TF                        if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
1727100TF                        else if (strcmp(alternative, "greater") == 0) p_value = 1.0 - approx_pnorm(z);
1730100TF                Safefree(ri); Safefree(diffs);
1738100TF        RETVAL = newRV_noinc((SV*)res);
1742100TF
1747100TF        AV*restrict obs_av = (AV*)SvRV(data_ref);
175050TF        SV**restrict first_elem = av_fetch(obs_av, 0, 0);
1762100TF        int yates = (is_2d && r == 2 && c == 2) ? 1 : 0;
1767100TF                double *restrict col_sum = (double*)safemalloc(c * sizeof(double));
1781100TF                for (unsigned int i = 0; i < r; i++) {
1782100TF                        AV*restrict exp_row = newAV();
180550TF          for (unsigned int j = 0; j < c; j++) {
50TF          for (unsigned int j = 0; j < c; j++) {
1807100TF                   grand_total += SvNV(*val_sv);
50TF                   grand_total += SvNV(*val_sv);
181250TF                   double O = SvNV(*val_sv);
1822100TF        hv_store(results, "p_value", 7, newSVnv(p_val), 0);
182350TF        hv_store(results, "expected", 8, newRV_noinc((SV*)expected_av), 0);
182650TF                        hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test with Yates' continuity correction", 0), 0);
1827100TF                } else {
182850TF                        hv_store(results, "method", 6, newSVpv("Pearson's Chi-squared test", 0), 0);
1829100TF                }
183050TF        } else {
183450TF}
50TF}
1838100TFPROTOTYPES: ENABLE
50TFPROTOTYPES: ENABLE
184250TF{
50TF{
1845100TF        unsigned int arg_idx = 0;
50TF        unsigned int arg_idx = 0;
1846100TF
50TF
1855100TF        if (arg_idx < items) {
185750TF        arg_idx++;
186150TF        const char *restrict undef_val = "NA";
50TF        const char *restrict undef_val = "NA";
1865100TF        // Read the remaining Hash-style arguments
50TF        // Read the remaining Hash-style arguments
1871100TF        else if (strEQ(key, "col.names")) col_names_sv = val;
187350TF        else if (strEQ(key, "row.names")) row_names_sv = val;
50TF        else if (strEQ(key, "row.names")) row_names_sv = val;
50TF        else if (strEQ(key, "row.names")) row_names_sv = val;
18740TF        else if (strEQ(key, "sep")) sep = SvPV_nolen(val);
1877100TF        }
1880100TF        croak("write_table: 'data' must be a HASH or ARRAY reference\n");
188650TF
188850TF        const char *restrict file = SvPV_nolen(file_sv);
50TF        const char *restrict file = SvPV_nolen(file_sv);
50TF        const char *restrict file = SvPV_nolen(file_sv);
50TF        const char *restrict file = SvPV_nolen(file_sv);
1892100TF                  croak("write_table: 'col.names' must be an ARRAY reference\n");
189450TF        }
50TF        }
50TF        }
50TF        }
190250TF          if (hv_iterinit(hv) == 0) XSRETURN_EMPTY;
190550TF          SV *restrict first_val = hv_iterval(hv, entry);
100TF          SV *restrict first_val = hv_iterval(hv, entry);
1909100TF          int first_type = SvTYPE(SvRV(first_val));
1910100TF          if (first_type != SVt_PVHV && first_type != SVt_PVAV) {
50TF          if (first_type != SVt_PVHV && first_type != SVt_PVAV) {
1912100TF          }
191450TF          is_hoa = (first_type == SVt_PVAV);
50TF          is_hoa = (first_type == SVt_PVAV);
1920100TF                   }
1924100TF                   hv_iterinit(hv);
1930100TF          AV *restrict av = (AV*)data_ref;
1935100TF          }
194350TF          is_aoh = 1;
1944100TF        }
194650TF        PerlIO *restrict fh = PerlIO_open(file, "w");
50TF        PerlIO *restrict fh = PerlIO_open(file, "w");
1953100TF        // ----- Hash of Hashes -----
1961100TF                } else {
196350TF                        hv_iterinit((HV*)data_ref);
196650TF                                 HV *restrict inner = (HV*)SvRV(hv_iterval((HV*)data_ref, entry));
1968100TF                                 HE *restrict inner_entry;
197050TF                                     hv_store_ent(col_map, hv_iterkeysv(inner_entry), newSViv(1), 0);
50TF                                     hv_store_ent(col_map, hv_iterkeysv(inner_entry), newSViv(1), 0);
197150TF                                 }
1972100TF                        }
100TF                        }
1973100TF                        unsigned num_cols = hv_iterinit(col_map);
197750TF                                 col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
197850TF                        }
1990100TF                  SV**restrict h_ptr = av_fetch(headers_av, i, 0);
1995100TF
1998100TF        for(size_t i=0; i<num_rows; i++) {
2001100TF        qsort(row_array, num_rows, sizeof(char*), cmp_string_wt);
50TF        qsort(row_array, num_rows, sizeof(char*), cmp_string_wt);
2003100TF        HV *restrict data_hv = (HV*)data_ref;
200550TF
50TF
2010100TF                  SV **restrict inner_hv_ptr = hv_fetch(data_hv, row_array[i], strlen(row_array[i]), 0);
2015100TF                      const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
201850TF                          if (SvROK(*cell_ptr)) {
2019100TF                              PerlIO_close(fh);
50TF                              PerlIO_close(fh);
20230TF                              if (rows_av) SvREFCNT_dec(rows_av);
20250TF                          }
0TF                          }
20270TF                      } else {
2037100TF          size_t max_rows = 0;
2038100TF          hv_iterinit(data_hv);
204050TF          while((entry = hv_iternext(data_hv))) {
50TF          while((entry = hv_iternext(data_hv))) {
2045100TF
2047100TF                   AV *restrict c_av = (AV*)SvRV(col_names_sv);
204850TF                   for(size_t i=0; i<=av_len(c_av); i++) {
20500TF                        if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
0TF                        if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
20530TF                   unsigned int num_cols = hv_iterinit(data_hv);
0TF                   unsigned int num_cols = hv_iterinit(data_hv);
20540TF                   const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
20570TF                        col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
2073100TF                            av_push(filtered_headers, newSVsv(h_sv));
207550TF                   }
50TF                   }
207750TF                   headers_av = filtered_headers;
50TF                   headers_av = filtered_headers;
2080100TF          const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
100TF          const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
208150TF          size_t h_idx = 0;
20840TF                   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
2096100TF                                AV *restrict rn_arr = (AV*)SvRV(*rn_arr_ptr);
50TF                                AV *restrict rn_arr = (AV*)SvRV(*rn_arr_ptr);
209950TF                                    if (SvROK(*rn_val_ptr)) {
2102100TF                                        if (headers_av) SvREFCNT_dec(headers_av);
50TF                                        if (headers_av) SvREFCNT_dec(headers_av);
2104100TF                                    }
210650TF                                } else {
50TF                                } else {
2110100TF                                row_data[d_idx++] = undef_val;
211250TF                        } else {
50TF                        } else {
2116100TF                        }
2123100TF                            AV *restrict arr = (AV*)SvRV(*arr_ptr);
2128100TF                                    safefree(row_data);
2132100TF                                row_data[d_idx++] = SvPV_nolen(*cell_ptr);
50TF                                row_data[d_idx++] = SvPV_nolen(*cell_ptr);
21350TF                            }
21370TF                            row_data[d_idx++] = undef_val;
0TF                            row_data[d_idx++] = undef_val;
21390TF                   }
2149100TF                   for(size_t i=0; i<=av_len(c_av); i++) {
2150100TF                        SV **restrict c = av_fetch(c_av, i, 0);
215250TF                   }
50TF                   }
2157100TF                        if (row_ptr && SvROK(*row_ptr)) {
216050TF                            HE *restrict entry;
50TF                            HE *restrict entry;
2161100TF                            while((entry = hv_iternext(row_hv))) {
216250TF                                hv_store_ent(col_map, hv_iterkeysv(entry), newSViv(1), 0);
21630TF                            }
21640TF                        }
0TF                        }
21650TF                   }
21680TF                   for(unsigned int i=0; i<num_cols; i++) {
2182100TF                        if (!h_ptr || !*h_ptr) continue;
218450TF                        if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
50TF                        if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
218550TF                            av_push(filtered_headers, newSVsv(h_sv));
2186100TF                        }
50TF                        }
218750TF                   }
21900TF          }
2199100TF          print_string_row(fh, header_row, h_idx, sep);
50TF          print_string_row(fh, header_row, h_idx, sep);
220350TF                   size_t d_idx = 0;
2204100TF                   SV **restrict row_ptr = av_fetch(data_av, i, 0);
222150TF                         char buf[32];
50TF                         char buf[32];
50TF                         char buf[32];
222650TF
222750TF                   for(size_t j=0; j<num_headers; j++) {
223050TF                        SV **restrict cell_ptr = row_hv ? hv_fetch(row_hv, col_name, strlen(col_name), 0) : NULL;
2235100TF                                if (headers_av) SvREFCNT_dec(headers_av);
223950TF                        } else {
100TF                        } else {
224150TF                        }
100TF                        }
2245100TF          }
224850TF        if (headers_av) SvREFCNT_dec(headers_av);
224950TF        if (rows_av) SvREFCNT_dec(rows_av);
100TF        if (rows_av) SvREFCNT_dec(rows_av);
225150TF        XSRETURN_EMPTY;
225450TFSV*
50TFSV*
50TFSV*
2259100TF        AV *restrict current_row = newAV();
2261100TF        bool in_quotes = 0, post_quote = 0;
2262100TF        size_t sep_len, comment_len;
2263100TF        SV *restrict line_sv;
100TF        SV *restrict line_sv;
100TF        SV *restrict line_sv;
2266100TF        if (SvOK(callback) && SvROK(callback) && SvTYPE(SvRV(callback)) == SVt_PVCV) {
2269100TF                data = newAV();
2272100TF        comment_len = comment_str ? strlen(comment_str) : 0;
50TF        comment_len = comment_str ? strlen(comment_str) : 0;
50TF        comment_len = comment_str ? strlen(comment_str) : 0;
100TF        comment_len = comment_str ? strlen(comment_str) : 0;
2281100TF                char *restrict line = SvPV_nolen(line_sv);
229050TF                if (!in_quotes) {
229450TF                                if (line[i] != ' ' && line[i] != '\t') { is_empty = 0; break; }
229550TF                        }
229850TF                        // Skip comments
2310100TF                                        i++; // Skip the escaped second quote
231250TF                                        in_quotes = 0;  // Close quotes
231650TF                                }
231750TF                        } else if (!in_quotes && sep_len > 0 && (len - i) >= sep_len && strncmp(line + i, sep_str, sep_len) == 0) {
232050TF                                i += sep_len - 1;     // Advance past multi-char separators
233050TF                        post_quote = 0; // Reset post-quote state at row boundary
234250TF                                call_sv(callback, G_DISCARD);
50TF                                call_sv(callback, G_DISCARD);
234550TF                                SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
50TF                                SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
2350100TF                }
2351100TF        }
235250TF        PerlIO_close(fp);
236150TF                        PUSHMARK(SP);
2372100TF        }
237750TF        } else {
50TF        } else {
50TF        } else {
237850TF                RETVAL = newRV_noinc((SV*)data);
50TF                RETVAL = newRV_noinc((SV*)data);
50TF                RETVAL = newRV_noinc((SV*)data);
238150TF        RETVAL
50TF        RETVAL
238950TF                }
2395100TF                if (strcmp(method, "pearson") != 0 &&
2397100TF                        strcmp(method, "kendall") != 0) {
2398100TF                        croak("cov: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')", method);
2406100TF                if (nx != ny) {
2415100TF                size_t n = 0;
2427100TF                                 x_val[n] = xv;
248350TF        }
2485100TF          RETVAL
2488100TFCODE:
2489100TF{
249050TF        const char *restrict formula  = NULL;
249350TF        char f_cpy[512];
249450TF        char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
50TF        char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
2498100TF        bool *restrict is_dummy = NULL;
50TF        bool *restrict is_dummy = NULL;
2506100TF
100TF
50TF
251050TF        HV *restrict data_hoa = NULL;
251450TF        double *restrict W = NULL, *restrict Z = NULL, *restrict beta = NULL, *restrict beta_old = NULL;
251550TF        bool *restrict aliased = NULL;
2518100TF        HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
251950TF        AV *restrict terms_av;
252350TF
50TF
252850TF          else if (strEQ(key, "data"))    data_sv = val;
25310TF        }        
0TF        }        
25320TF        if (!formula) croak("glm: formula is required");
0TF        if (!formula) croak("glm: formula is required");
254150TF        Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
0TF        Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
2547100TF
2549100TF        if (!tilde) croak("glm: invalid formula, missing '~'");
255050TF        *tilde = '\0';
255250TF
255850TF          if (num_terms >= term_cap - 3) {
256050TF                   Renew(terms, term_cap, char*); Renew(uniq_terms, term_cap, char*);
256250TF          if (strcmp(chunk, "1") == 0 || strcmp(chunk, "-1") == 0) {
256450TF                   continue;
100TF                   continue;
256750TF          if (star) {
2568100TF                   *star = '\0';
257250TF                   
50TF                   
257450TF                   terms[num_terms++] = savepv(right);
50TF                   terms[num_terms++] = savepv(right);
2576100TF                   terms[num_terms] = (char*)safemalloc(inter_len);
25840TF        }
25870TF          bool found = FALSE;
0TF          bool found = FALSE;
25880TF          for (size_t j = 0; j < num_uniq; j++) {
25900TF          }
0TF          }
0TF          }
25950TF        // --- Data Extraction ---
2603100TF                        if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
260450TF                                 data_hoa = hv;
2609100TF                                     row_names[i] = savepv(buf);
2612100TF                                 n = hv_iterinit(hv);
261450TF                                 i = 0;
2615100TF                                 while ((entry = hv_iternext(hv))) {
261750TF                                     row_names[i] = savepv(hv_iterkey(entry, &len));
2619100TF                                     i++;
2620100TF                                 }
2622100TF                }
262350TF        } else if (SvTYPE(ref) == SVt_PVAV) {
0TF        } else if (SvTYPE(ref) == SVt_PVAV) {
262950TF                   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
2630100TF                       row_hashes[i] = (HV*)SvRV(*val);
2631100TF                       char buf[32]; snprintf(buf, sizeof(buf), "%lu", i + 1);
263250TF                       row_names[i] = savepv(buf);
2637100TF                   }
263850TF          }
2649100TF                   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = FALSE; p_exp++; continue;
266050TF                           }
50TF                           }
266150TF                           if (!found) {
2664100TF                           }
266650TF                       }
2670100TF                           for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
2671100TF                               if (strcmp(levels[l1], levels[l2]) > 0) {
2673100TF                               }
267550TF                       }
2676100TF                       for (size_t l = 1; l < num_levels; l++) {
268150TF                           }
268450TF                           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
2686100TF                           p_exp++;
269250TF                   }
26930TF          } else {
2702100TF        // --- Listwise Deletion ---
2705100TF                if (isnan(y_val)) { Safefree(row_names[i]); continue; }
2707100TF                bool row_ok = TRUE;
2708100TF                double *restrict row_x = (double*)safemalloc(p * sizeof(double));
270950TF                for (size_t j = 0; j < p; j++) {
50TF                for (size_t j = 0; j < p; j++) {
2713100TF                                 char* str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
271450TF                                 if (str_val) {
272350TF                if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
2724100TF                Y[valid_n] = y_val;
2725100TF                for (size_t j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
272850TF                Safefree(row_x);
2737100TF        W = (double*)safemalloc(valid_n * sizeof(double)); Z = (double*)safemalloc(valid_n * sizeof(double));
100TF        W = (double*)safemalloc(valid_n * sizeof(double)); Z = (double*)safemalloc(valid_n * sizeof(double));
2738100TF        beta = (double*)safemalloc(p * sizeof(double)); beta_old = (double*)safemalloc(p * sizeof(double));
2740100TF        XtWX = (double*)safemalloc(p * p * sizeof(double)); XtWZ = (double*)safemalloc(p * sizeof(double));
2743100TF        double sum_y = 0.0;
2747100TF          if (is_binomial) {
274850TF                   if (Y[i] < 0.0 || Y[i] > 1.0) croak("glm: binomial family requires response between 0 and 1");
275050TF                   eta[i] = log(mu[i] / (1.0 - mu[i]));
100TF                   eta[i] = log(mu[i] / (1.0 - mu[i]));
2756100TF          } else {
2758100TF                   eta[i] = mu[i];
276050TF        }
100TF        }
2762100TF        for (iter = 1; iter <= max_iter; iter++) {
276550TF                                 double varmu = mu[i] * (1.0 - mu[i]);
276650TF                                 double mu_eta = varmu; // Link derivative for logit
2769100TF                                 W[i] = (mu_eta * mu_eta) / varmu;
277050TF                        } else {
2780100TF                                 XtWZ[i] += X[k * p + i] * w * z;
100TF                                 XtWZ[i] += X[k * p + i] * w * z;
50TF                                 XtWZ[i] += X[k * p + i] * w * z;
2785100TF                final_rank = sweep_matrix_ols(XtWX, p, aliased);
2788100TF                                 double sum = 0.0;
2792100TF                }
2795100TF                for (unsigned short int half = 0; half < 10; half++) {
100TF                for (unsigned short int half = 0; half < 10; half++) {
2796100TF                        deviance_new = 0.0;
2797100TF                        for (i = 0; i < valid_n; i++) {
279850TF                                 double linear_pred = 0.0;
2799100TF                                 for (size_t j = 0; j < p; j++) if (!aliased[j]) linear_pred += X[i * p + j] * beta[j];
2801100TF                                 if (is_binomial) {
2807100TF                                     double dev = 0.0;
2808100TF                                     if (Y[i] == 0.0)      dev = -2.0 * log(1.0 - mu[i]);
2809100TF                                     else if (Y[i] == 1.0) dev = -2.0 * log(mu[i]);
281050TF                                     else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
2818100TF                        // Step halving divergence check
282150TF                        }
2827100TF                if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) {
50TF                if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) {
2828100TF                        converged = TRUE; break;
2830100TF                deviance_old = deviance_new;
2833100TF        // Final accurate calculation of W for standard errors
283450TF        for (i = 0; i < p; i++) { for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
2836100TF          double w = is_binomial ? (mu[k] * (1.0 - mu[k])) : 1.0;
2845100TF        double wtdmu = mean_y; // Since weights are 1.0 initially
285050TF                   else null_dev += 2.0 * (Y[i] * log(Y[i] / wtdmu) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - wtdmu)));
28530TF                   null_dev += diff * diff;
28540TF          }
2858100TF          double n_f = (double)valid_n;
2862100TF        }
2863100TF        // --- Return Structures ---
2885100TF                hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
2887100TF
2889100TF                if (aliased[j]) {
2891100TF                        hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
2898100TF                        
290850TF        hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv), 0);
50TF        hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv), 0);
2920100TF        hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv), 0);
2924100TF        for (i = 0; i < num_terms; i++) Safefree(terms[i]);
2925100TF        Safefree(terms);
292650TF        for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]);
2927100TF        Safefree(uniq_terms);
50TF        Safefree(uniq_terms);
292850TF        for (size_t j = 0; j < p_exp; j++) {
294150TFOUTPUT:
50TFOUTPUT:
50TFOUTPUT:
294250TF    RETVAL
50TF    RETVAL
50TF    RETVAL
295050TF        SV *restrict x_ref = ST(0), *restrict y_ref = ST(1);
2956100TF        bool continuity = 0;
296050TF          const char *restrict key = SvPV_nolen(ST(i));
100TF          const char *restrict key = SvPV_nolen(ST(i));
50TF          const char *restrict key = SvPV_nolen(ST(i));
296150TF          SV *restrict val = ST(i + 1);
100TF          SV *restrict val = ST(i + 1);
50TF          SV *restrict val = ST(i + 1);
2964100TF          else if (strEQ(key, "method"))      method = SvPV_nolen(val);
100TF          else if (strEQ(key, "method"))      method = SvPV_nolen(val);
297150TF        AV *restrict x_av, *restrict y_av;
2977100TF        bool is_spearman = (strcmp(method, "spearman") == 0);
2980100TF        if (!SvOK(x_ref) || !SvROK(x_ref) || SvTYPE(SvRV(x_ref)) != SVt_PVAV ||
298950TF        if (n_raw != av_len(y_av) + 1) croak("incompatible dimensions");
50TF        if (n_raw != av_len(y_av) + 1) croak("incompatible dimensions");
3003100TF          if (!isnan(xv) && !isnan(yv)) {
3005100TF              y[n] = yv;
3006100TF              n++;
301050TF        if (n < 3) {
0TF        if (n < 3) {
301150TF          Safefree(x);
301250TF          Safefree(y);
3013100TF          croak("not enough finite observations");
301850TF          double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
302050TF                   double dx = x[i] - mean_x;
50TF                   double dx = x[i] - mean_x;
302450TF                   M2_x += dx * (x[i] - mean_x);
0TF                   M2_x += dx * (x[i] - mean_x);
302550TF                   M2_y += dy * (y[i] - mean_y);
50TF                   M2_y += dy * (y[i] - mean_y);
303050TF          statistic = estimate * sqrt(df / (1.0 - estimate * estimate));
50TF          statistic = estimate * sqrt(df / (1.0 - estimate * estimate));
303250TF          // Confidence interval using Fisher's Z transform
30400TF          // HIGH-PRECISION P-VALUE USING INCOMPLETE BETA
0TF          // HIGH-PRECISION P-VALUE USING INCOMPLETE BETA
30430TF          int c = 0, d = 0, tie_x = 0, tie_y = 0;
30450TF                   for (size_t j = i + 1; j < n; j++) {
305150TF                       else if (sign_y == 0) tie_y++;
3059100TF          bool has_ties = (tie_x > 0 || tie_y > 0);
306850TF          // If forced exact but ties exist, R overrides and falls back to approximation anyway
50TF          // If forced exact but ties exist, R overrides and falls back to approximation anyway
3072100TF                   double S_stat = c - d;
3079100TF                   if (continuity) S -= (S > 0 ? 1 : -1);
308050TF                   statistic = S / sqrt(var_S);
50TF                   statistic = S / sqrt(var_S);
308550TF                       p_value = approx_pnorm(statistic);
0TF                       p_value = approx_pnorm(statistic);
308650TF                   } else {
50TF                   } else {
309150TF          double *restrict rank_x = safemalloc(n * sizeof(double));
30960TF          // Spearman rho = Pearson r of the ranks (Welford's Method)
3113100TF                   S_stat += diff * diff;
313450TF                   double r = estimate;
50TF                   double r = estimate;
314150TF        } else {
3144100TF        }
314650TF        rhv = newHV();
50TF        rhv = newHV();
314850TF        hv_stores(rhv, "p.value", newSVnv(p_value));
315650TF          av_push(ci_av, newSVnv(ci_upper));
50TF          av_push(ci_av, newSVnv(ci_upper));
3163100TF    RETVAL
316650TF        SV *data
317350TF        if (!SvROK(data) || SvTYPE(SvRV(data)) != SVt_PVAV) {
31770TF        av = (AV *)SvRV(data);
318350TF        for (size_t i = 0; i < n_raw; i++) {
318450TF          SV **restrict elem = av_fetch(av, i, 0);
3185100TF          if (elem && SvOK(*elem)) {
319350TF        }
100TF        }
3195100TF        if (n < 3 || n > 5000) {
3203100TF          ssq += (x[i] - mean) * (x[i] - mean);
3207100TF          croak("Data is perfectly constant; cannot compute Shapiro-Wilk test");
3217100TF          // Exact P-value for n=3
322350TF          Newx(a, n, double);
324650TF          for (size_t i = 0; i < n; i++) {
324750TF                   b_val += a[i] * x[i];
325750TF                   // Royston's branch for 4 <= n <= 11 (AS R94, small-sample path).
3267100TF                       double sig_val= 1.3822 + nn * (-0.77857  + nn * ( 0.062767  - nn * 0.0020322));
3269100TF                       z = (-log(gamma - y) - mu) / sigma;
50TF                       z = (-log(gamma - y) - mu) / sigma;
3272100TF                       p_val = 0.5 * erfc(z * M_SQRT1_2);
327450TF          } else {
100TF          } else {
3276100TF                   double ln_n   = log((double)n);
50TF                   double ln_n   = log((double)n);
3285100TF          if (p_val > 1.0) p_val = 1.0;
3287100TF          
100TF          
3296100TF        EXTEND(SP, 1);
329750TF        PUSHs(sv_2mortal(newRV_noinc((SV *)ret_hash)));
3308100TF                        if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
3310100TF                                size_t len = av_len(av) + 1;
50TF                                size_t len = av_len(av) + 1;
3313100TF                                     if (tv && SvOK(*tv)) {
331550TF                                         if (first || val < min_val) {
100TF                                         if (first || val < min_val) {
3317100TF                                             first = FALSE;
100TF                                             first = FALSE;
3326100TF                                 if (first || val < min_val) {
3328100TF                                     first = FALSE;
100TF                                     first = FALSE;
3337100TF        OUTPUT:
333850TF          RETVAL
335350TF                           SV** restrict tv = av_fetch(av, j, 0);
3357100TF                                   max_val = val;
3359100TF                               }
100TF                               }
3361100TF                           } else {
3366100TF                       double val = SvNV(arg);
337150TF                       count++;
3380100TF
3383100TF{
338650TF
339450TF        }
339850TF                if (i + 1 < items && SvPOK(ST(i))) {
340050TF                        if (strEQ(key, "n")) {
3404100TF                                continue;
340650TF                                min = SvNV(ST(i+1));
342250TF                } else if (!min_set) {
3423100TF                        min = SvNV(ST(i));
3431100TF                i++;
3435100TF        }
3436100TF        // Ensure PRNG is seeded
343750TF        AUTO_SEED_PRNG();
3442100TF        const double range = max - min;
100TF        const double range = max - min;
3443100TF        for (size_t j = 0; j < n; j++) {
100TF        for (size_t j = 0; j < n; j++) {
344650TF                        r = NAN; // R behavior for inverted ranges
3448100TF                        r = min + range * Drand01();
3463100TF                   croak("Usage: rbinom(n => 10, size => 100, prob => 0.5)");
100TF                   croak("Usage: rbinom(n => 10, size => 100, prob => 0.5)");
3468100TF          bool size_set = FALSE, prob_set = FALSE;
347250TF                   SV* restrict val = ST(i + 1);
3476100TF                   else if (strEQ(key, "prob")) { prob = SvNV(val); prob_set = TRUE; }
347850TF          }
50TF          }
3481100TF          if (!size_set || !prob_set) croak("rbinom: 'size' and 'prob' are required arguments");
3482100TF          if (prob < 0.0 || prob > 1.0) croak("rbinom: prob must be between 0 and 1");
348550TF          if (n > 0) {
349250TF          RETVAL = newRV_noinc((SV*)result_av);
349550TF                RETVAL
349750TFSV*
349950TF        CODE:
50TF        CODE:
350550TF                AV*restrict x_av = (AV*)SvRV(x_sv);
0TF                AV*restrict x_av = (AV*)SvRV(x_sv);
350950TF                // 2. Extract Data & Find Range
351350TF                double min_val = DBL_MAX, max_val = -DBL_MAX;
351450TF
351550TF                for (size_t i = 0; i < n_raw; i++) {
351650TF                        SV**restrict tv = av_fetch(x_av, i, 0);
3520100TF                                 if (val < min_val) min_val = val;
3533100TF                        n_bins = (size_t)SvIV(ST(1));
3535100TF                        /* Support named parameters even if mixed with positional arguments */
356350TF                // 5. Compute Statistics
100TF                // 5. Compute Statistics
50TF                // 5. Compute Statistics
356950TF                AV*restrict av_counts  = newAV();
3572100TF                for (size_t i = 0; i <= n_bins; i++) {
3576100TF                                 av_push(av_mids,    newSVnv(mids[i]));
357750TF                                 av_push(av_density, newSVnv(density[i]));
358050TF                hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
50TF                hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
50TF                hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
358450TF
358850TF
3590100TF        }
359250TF          RETVAL
50TF          RETVAL
359650TF        {
360750TF                /* --- 2. Remaining args must be key-value pairs --- */
50TF                /* --- 2. Remaining args must be key-value pairs --- */
50TF                /* --- 2. Remaining args must be key-value pairs --- */
3611100TF                for (; arg_idx < items; arg_idx += 2) {
361350TF                         SV *restrict val = ST(arg_idx + 1);
50TF                         SV *restrict val = ST(arg_idx + 1);
361450TF
50TF
36210TF                AV *restrict x_av = (AV*)SvRV(x_sv);
3627100TF                Newx(x, n_raw, double);
3630100TF                        SV **restrict tv = av_fetch(x_av, i, 0);
3632100TF                                 x[n++] = SvNV(*tv);
3634100TF                }
364850TF                        n_probs = av_len(p_av) + 1;
3672100TF                                 q = x[n - 1]; /* Prevent out-of-bounds mapping */
3674100TF                                 q = x[0];
50TF                                 q = x[0];
3677100TF                                 double h = (n - 1) * p;
367950TF                                 double gamma = h - j;
100TF                                 double gamma = h - j;
3686100TF                        
3693100TF                        hv_store(res_hv, key, strlen(key), newSVnv(q), 0);
3694100TF                }
3704100TF
3706100TF        PROTOTYPE: @
50TF        PROTOTYPE: @
3709100TF          size_t count = 0;
371150TF                for (size_t i = 0; i < items; i++) {
100TF                for (size_t i = 0; i < items; i++) {
3718100TF                                     if (tv && SvOK(*tv)) {
372550TF                        } else if (SvOK(arg)) {
3726100TF                                 total += SvNV(arg);
3737100TFdouble sum(...)
3739100TF        INIT:
50TF        INIT:
3742100TF        CODE:
374450TF                   SV* restrict arg = ST(i);
100TF                   SV* restrict arg = ST(i);
3754100TF                               croak("sum: undefined value at array ref index %zu (argument %zu)", j, i);
3764100TF          if (count == 0) croak("sum needs >= 1 element");
376550TF          RETVAL = total;
3777100TF            SV* restrict arg = ST(i);
3779100TF                AV* restrict av = (AV*)SvRV(arg);
50TF                AV* restrict av = (AV*)SvRV(arg);
3782100TF                    SV** restrict tv = av_fetch(av, j, 0);
378450TF                        count++;
100TF                        count++;
3794100TF                count++;
3804100TF        RETVAL = sqrt(M2 / (count - 1));
3805100TF    OUTPUT:
382150TF                       for (size_t j = 0; j < len; j++) {
100TF                       for (size_t j = 0; j < len; j++) {
50TF                       for (size_t j = 0; j < len; j++) {
382750TF                               mean += delta / count;
100TF                               mean += delta / count;
50TF                               mean += delta / count;
383350TF                   } else if (SvOK(arg)) {
3838100TF                       M2 += delta * (val - mean);
3842100TF          }
3843100TF          if (count < 2) croak("var needs >= 2 elements");
3844100TF          RETVAL = M2 / (count - 1);
3845100TF        OUTPUT:
3846100TF          RETVAL
3847100TF
384850TFSV* t_test(...)
3853100TF                double mu = 0.0, conf_level = 0.95;
50TF                double mu = 0.0, conf_level = 0.95;
50TF                double mu = 0.0, conf_level = 0.95;
385750TF                int arg_idx = 0;
3859100TF                // 1. Shift first positional argument as 'x' if it's an array reference
50TF                // 1. Shift first positional argument as 'x' if it's an array reference
50TF                // 1. Shift first positional argument as 'x' if it's an array reference
386250TF                  arg_idx++;
100TF                  arg_idx++;
3867100TF                  y_sv = ST(arg_idx);
386950TF                }
50TF                }
3875100TF
50TF
3877100TF                for (; arg_idx < items; arg_idx += 2) {
100TF                for (; arg_idx < items; arg_idx += 2) {
3878100TF                        const char*restrict key = SvPV_nolen(ST(arg_idx));
3880100TF
100TF
3882100TF                        else if (strEQ(key, "y"))           y_sv        = val;
388450TF                        else if (strEQ(key, "paired"))      paired      = SvTRUE(val);
50TF                        else if (strEQ(key, "paired"))      paired      = SvTRUE(val);
3890100TF
3892100TF                if (!x_sv || !SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
389550TF                size_t nx = av_len(x_av) + 1;
50TF                size_t nx = av_len(x_av) + 1;
389650TF                if (nx < 2) croak("t_test: 'x' needs at least 2 elements");
50TF                if (nx < 2) croak("t_test: 'x' needs at least 2 elements");
390350TF                // --- Computation via Welford's Algorithm --- */
3909100TF                        double delta = val - mean_x;
391050TF                        mean_x += delta / (i + 1);
0TF                        mean_x += delta / (i + 1);
391950TF                        if (paired && ny != nx) croak("t_test: Paired arrays must be same length");
0TF                        if (paired && ny != nx) croak("t_test: Paired arrays must be same length");
3939100TF                                     M2_d += delta * (val - mean_d);
3943100TF                                 cint_est = mean_d;
3966100TF                                 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
50TF                                 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
3972100TF                        t_stat   = (cint_est - mu) / std_err;
3978100TF                if (strcmp(alternative, "less") == 0) {
3980100TF                        ci_lower = -INFINITY;
100TF                        ci_lower = -INFINITY;
3981100TF                        ci_upper = cint_est + t_crit * std_err;
50TF                        ci_upper = cint_est + t_crit * std_err;
398250TF                } else if (strcmp(alternative, "greater") == 0) {
398650TF                } else {
398750TF                        t_crit   = qt_tail(df, alpha / 2.0);
3989100TF                        ci_upper = cint_est + t_crit * std_err;
399150TF                AV*restrict conf_int = newAV();
50TF                AV*restrict conf_int = newAV();
3997100TF                hv_store(results, "conf_int",  8, newRV_noinc((SV*)conf_int), 0);
3998100TF                RETVAL = newRV_noinc((SV*)results);
4000100TF        OUTPUT:
4002100TF
4004100TF        INIT:
4006100TF                        croak("p_adjust: first argument must be an ARRAY reference of p-values");
4007100TF                }
4009100TF                size_t n = av_len(p_av) + 1;
4011100TF                if (n == 0) {
4013100TF                }
401450TF                // Normalize method string
4016100TF                strncpy(meth, method, 63); meth[63] = '\0';
4018100TF                // Resolve aliases
4020100TF                if (strstr(meth, "benjamini") && strstr(meth, "yekutieli")) strcpy(meth, "by");
402150TF                if (strcmp(meth, "fdr") == 0) strcpy(meth, "bh");
4023100TF                PVal *restrict arr;
4025100TF                Newx(arr, n, PVal);
4027100TF
4029100TF                        SV**restrict tv = av_fetch(p_av, i, 0);
4030100TF                        arr[i].p = (tv && SvOK(*tv)) ? SvNV(*tv) : 1.0;
4032100TF                }
403450TF                qsort(arr, n, sizeof(PVal), cmp_pval);
403550TF        PPCODE:
4038100TF                                double v = arr[i].p * n;
404050TF                        }
4045100TF                                 if (v > cummax) cummax = v;
4049100TF                        double cummin = 1.0;
4054100TF                        }
4056100TF                        double cummin = 1.0;
4061100TF                        }
4063100TF                        double q = 0.0;
4066100TF                        for (ssize_t i = n - 1; i >= 0; i--) {
4070100TF                        }
4071100TF                } else if (strcmp(meth, "hommel") == 0) {
4077100TF                        for (size_t i = 1; i < n; i++) {
4078100TF                                double temp = (n * arr[i].p) / (i + 1.0);
407950TF                                if (temp < min_val) {
408350TF                        // pa <- q <- rep(min, n)
40840TF                        for (size_t i = 0; i < n; i++) {
409250TF                                 double q1 = (j * arr[n_mj + 1].p) / 2.0;
4093100TF                                 for (size_t k = 1; k < i2_len; k++) {
4107100TF                                }
4109100TF                                for (size_t i = 0; i < n; i++) {
50TF                                for (size_t i = 0; i < n; i++) {
4112100TF                                    }
411450TF                        }
100TF                        }
4120100TF                        }
4126100TF                } else {
412950TF                }
4132100TF                for (size_t i = 0; i < n; i++) {
4134100TF                }
50TF                }
4137100TF
413950TF        PROTOTYPE: @
50TF        PROTOTYPE: @
414650TF          for (size_t i = 0; i < items; i++) {
4156100TF                               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
4163100TF                   }
4170100TF          /* Pass 2: Populate the C array — Safefree before any croak */
4171100TF          for (size_t i = 0; i < items; i++) {
4172100TF                   SV* restrict arg = ST(i);
417750TF                           SV** restrict tv = av_fetch(av, j, 0);
50TF                           SV** restrict tv = av_fetch(av, j, 0);
418250TF                               croak("median: undefined value at array ref index %zu (argument %zu)", j, i);
418850TF                       Safefree(nums);
100TF                       Safefree(nums);
50TF                       Safefree(nums);
419350TF          /* Sort and calculate median */
50TF          /* Sort and calculate median */
419450TF          qsort(nums, total_count, sizeof(double), compare_doubles);
419650TF                   median_val = (nums[total_count / 2 - 1] + nums[total_count / 2]) / 2.0;
419750TF          } else {
420050TF          Safefree(nums);
50TF          Safefree(nums);
420250TF          RETVAL = median_val;
100TF          RETVAL = median_val;
50TF          RETVAL = median_val;
4208100TF        // --- validate method -------------------------------------------
50TF        // --- validate method -------------------------------------------
420950TF        if (strcmp(method, "pearson")  != 0 &&
4213100TF                        method);
421750TF                  croak("cor: x must be an ARRAY reference");
422150TF        if (nx == 0) croak("cor: x is empty");
422250TF
4224100TF        bool x_is_matrix = 0;
422650TF                  SV**restrict fp = av_fetch(x_av, 0, 0);
50TF                  SV**restrict fp = av_fetch(x_av, 0, 0);
50TF                  SV**restrict fp = av_fetch(x_av, 0, 0);
4228100TF                      x_is_matrix = 1;
423050TF
50TF
50TF
423950TF        if (has_y && ny > 0) {
424450TF
50TF
50TF
424850TF                  if (!has_y) {
4253100TF                          croak("cor: x and y must have the same length (%lu vs %lu)",
425550TF
50TF
50TF
425950TF                      double *restrict xd, *restrict yd;
50TF                      double *restrict xd, *restrict yd;
426050TF                      Newx(xd, nx, double);
4261100TF                      Newx(yd, ny, double);
426350TF                      bool x_sd0 = 1, y_sd0 = 1;
50TF                      bool x_sd0 = 1, y_sd0 = 1;
50TF                      bool x_sd0 = 1, y_sd0 = 1;
427050TF                          if (!isnan(val)) {
4272100TF                              else if (val != x_first) x_sd0 = 0;
427350TF                          }
4274100TF                      }
427850TF                          yd[i] = val;
50TF                          yd[i] = val;
50TF                          yd[i] = val;
428850TF                          croak("cor: standard deviation of y is 0");
50TF                          croak("cor: standard deviation of y is 0");
429250TF                      Safefree(xd); Safefree(yd);
429450TF                  }
4295100TF        } else {//Branch 2: x is a matrix (or y is a matrix)  â†’  AoA result
429650TF                  // -- resolve x matrix dimensions
4297100TF                  if (!x_is_matrix)
430150TF                  SV**restrict xr0 = av_fetch(x_av, 0, 0);
50TF                  SV**restrict xr0 = av_fetch(x_av, 0, 0);
50TF                  SV**restrict xr0 = av_fetch(x_av, 0, 0);
430950TF
431650TF                  
4317100TF                  if (has_y && y_is_matrix) {
432150TF                          if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
43240TF                  }
43250TF
43260TF                  // -- extract x columns
43280TF                  Newx(col_x, ncols_x, double*);
43300TF                  for (size_t j = 0; j < ncols_x; j++) {
43370TF                          SV**restrict cv  = av_fetch(row, j, 0);
43380TF                          double val = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
43410TF                              if (isnan(first)) first = val;
4345100TF                      if (sd0) {
4346100TF                          for (size_t k = 0; k <= j; k++) Safefree(col_x[k]);
4350100TF                  }
4354100TF                  double **restrict col_y   = NULL;
435650TF
4357100TF                  // 1 = cor(X) — result is symmetric
437350TF                              double val = (cv && SvOK(*cv) && looks_like_number(*cv)) ? SvNV(*cv) : NAN;
4375100TF                              if (!isnan(val)) {
100TF                              if (!isnan(val)) {
438050TF                          if (sd0) {
438250TF                              Safefree(col_x);
438750TF                      }
50TF                      }
100TF                      }
438950TF                      ncols_y  = ncols_x;
50TF                      ncols_y  = ncols_x;
50TF                      ncols_y  = ncols_x;
0TF                      ncols_y  = ncols_x;
43910TF                      symmetric = 1;
43930TF                  if (nrows < 2)
4402100TF                      rows_out[i] = newAV();
440450TF                  }
440850TF                      Newx(r_cache, ncols_x, double*);
50TF                      Newx(r_cache, ncols_x, double*);
50TF                      Newx(r_cache, ncols_x, double*);
441050TF                          Newx(r_cache[i], ncols_x, double);
50TF                          Newx(r_cache[i], ncols_x, double);
50TF                          Newx(r_cache[i], ncols_x, double);
0TF                          Newx(r_cache[i], ncols_x, double);
44120TF                      for (size_t i = 0; i < ncols_x; i++) {
44140TF                          for (size_t j = i + 1; j < ncols_x; j++) {
44150TF                              double r = compute_cor(col_x[i], col_x[j], nrows, method);
4426100TF                      Safefree(r_cache); r_cache = NULL;
4428100TF                      // cross-correlation: every (i,j) pair is independent
50TF                      // cross-correlation: every (i,j) pair is independent
443050TF                          for (size_t j = 0; j < ncols_y; j++)
443250TF                  }
50TF                  }
50TF                  }
4438100TF                  for (size_t j = 0; j < ncols_x; j++) Safefree(col_x[j]);
444850TF
50TF
4454100TF                double center_val = 0.0, scale_val = 1.0;
4460100TF                                 data_items = items - 1; // Exclude hash from data processing
446350TF                                 SV**restrict center_sv = hv_fetch(opt_hv, "center", 6, 0);
4465100TF                                     SV*restrict val_sv = *center_sv;
446750TF                                         do_center_mean = FALSE; center_val = 0.0;
50TF                                         do_center_mean = FALSE; center_val = 0.0;
447050TF                                         /* Trap booleans and empty strings before numeric checks */
50TF                                         /* Trap booleans and empty strings before numeric checks */
447750TF                                         } else if (SvTRUE(val_sv)) {
448050TF                                             do_center_mean = FALSE; center_val = 0.0;
448150TF                                         }
4487100TF                                     SV*restrict val_sv = *scale_sv;
4494100TF                                         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
449650TF                                         } else if (looks_like_number(val_sv)) {
450350TF                                         }
4512100TF                        if (SvROK(first_arg) && SvTYPE(SvRV(first_arg)) == SVt_PVAV) {
451450TF                                 if (av_len(av) >= 0) {
0TF                                 if (av_len(av) >= 0) {
45170TF                                         is_matrix = TRUE;
45190TF                                 }
0TF                                 }
452150TF                }
452550TF                        //=========================================================
452650TF                        AV*restrict mat_av = (AV*)SvRV(ST(0));
4527100TF                        size_t nrow = av_len(mat_av) + 1, ncol = 0;
452950TF                        SV**restrict first_row = av_fetch(mat_av, 0, 0);
0TF                        SV**restrict first_row = av_fetch(mat_av, 0, 0);
45320TF                        if (nrow == 0 || ncol == 0) croak("scale requires non-empty matrix");
45340TF                        // Create a new matrix for the scaled output
0TF                        // Create a new matrix for the scaled output
453950TF                                 row_ptrs[r] = newAV();
4544100TF                        for (size_t c = 0; c < ncol; c++) {
4545100TF                                 double col_sum = 0.0;
4546100TF                                 double *restrict col_data;
4551100TF                                     if (row_sv && SvROK(*row_sv)) {
455750TF                                     }
4558100TF                                     col_sum += col_data[r];
456050TF
457050TF                                     double sum_sq = 0.0;
4577100TF                                 // Store scaled values back into the new matrix rows
4580100TF                                     double final_val = (col_scale == 0.0) ? (0.0 / 0.0) : (centered / col_scale);
4582100TF                                 }
458550TF                        safefree(row_ptrs);
45880TF                        PUSHs(sv_2mortal(newRV_noinc((SV*)result_av)));
459550TF                        double sum = 0.0;
100TF                        double sum = 0.0;
50TF                        double sum = 0.0;
459950TF                                        AV*restrict av = (AV*)SvRV(arg);
4600100TF                                        size_t len = av_len(av) + 1;
460450TF                                        }
0TF                                        }
460750TF                                }
100TF                                }
460950TF                        if (total_count == 0) croak("scale requires at least 1 numeric element");
0TF                        if (total_count == 0) croak("scale requires at least 1 numeric element");
4613100TF                                 if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
50TF                                 if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
4621100TF                                         }
4628100TF                        if (do_center_mean) center_val = sum / total_count;
463150TF                                     Safefree(nums);
463250TF                                     croak("scale needs >= 2 elements to calculate SD");
467550TF                   croak("Unknown option: %s", key);
4677100TF        }
4680100TF          croak("The 'data' option must be an array reference (e.g. data => [1..6])");
468150TF        }
4684100TF        if (data_len == 0) {
4685100TF          croak("Data array cannot be empty");
100TF          croak("Data array cannot be empty");
469150TF        } else if (nrow_set && !ncol_set) {
469350TF        } else if (!nrow_set && ncol_set) {
469550TF        }
469750TF        if (nrow == 0 || ncol == 0) {
100TF        if (nrow == 0 || ncol == 0) {
470050TF        // Create the matrix (Array of Arrays)
4701100TF        AV*restrict result_av = newAV();
470650TF          row_ptrs[r] = newAV();
50TF          row_ptrs[r] = newAV();
470850TF          av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
50TF          av_push(result_av, newRV_noinc((SV*)row_ptrs[r]));
4710100TF        // Fill the matrix
47180TF                   c = i % ncol;
47200TF                   r = i % nrow;
47210TF                   c = i / nrow;
47220TF          }
47240TF        }
0TF        }
0TF        }
47290TF
4740100TF        char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
100TF        char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
50TF        char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
4744100TF
4745100TF        char **restrict row_names = NULL, **restrict valid_row_names = NULL;
474650TF        HV **restrict row_hashes = NULL;
4758100TF
476050TF
0TF
47620TF          const char *restrict key = SvPV_nolen(ST(i_arg));
0TF          const char *restrict key = SvPV_nolen(ST(i_arg));
0TF          const char *restrict key = SvPV_nolen(ST(i_arg));
0TF          const char *restrict key = SvPV_nolen(ST(i_arg));
4766100TF          else croak("lm: unknown argument '%s'", key);
50TF          else croak("lm: unknown argument '%s'", key);
476750TF        }
0TF        }
0TF        }
4773100TF        // ========================================================================
50TF        // ========================================================================
47740TF        ref = SvRV(data_sv);
0TF        ref = SvRV(data_sv);
0TF        ref = SvRV(data_sv);
4780100TF                   SV *restrict val = hv_iterval(hv, entry);
50TF                   SV *restrict val = hv_iterval(hv, entry);
0TF                   SV *restrict val = hv_iterval(hv, entry);
4786100TF                           char buf[32];
50TF                           char buf[32];
0TF                           char buf[32];
4790100TF                   } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
50TF                   } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
47910TF                       n = hv_iterinit(hv);
0TF                       n = hv_iterinit(hv);
0TF                       n = hv_iterinit(hv);
4796100TF                           row_names[i] = savepv(hv_iterkey(entry, &len));
479750TF                           row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
0TF                           row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
479850TF                           i++;
0TF                           i++;
480750TF                   SV **restrict val = av_fetch(av, i, 0);
480950TF                       row_hashes[i] = (HV*)SvRV(*val);
481150TF                       row_names[i] = savepv(buf);
50TF                       row_names[i] = savepv(buf);
4818100TF        } else croak("lm: Data must be an Array or Hash reference");
4819100TF
4821100TF        // PHASE 2: Formula Parsing & `.` Expansion
482350TF        src = (char*)formula; dst = f_cpy;
50TF        src = (char*)formula; dst = f_cpy;
4825100TF        *dst = '\0';
482750TF        tilde = strchr(f_cpy, '~');
4828100TF        if (!tilde) {
483850TF        // IMPORTANT: skip tokens that appear inside I(...) wrappers so that
4839100TF        // expressions like I(x^-1) are never mistakenly treated as "-1".
4851100TF                       (p_idx[2] == '\0' || p_idx[2] == '+' || p_idx[2] == '-')) {
485350TF                       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
4855100TF                   }
485650TF                   // Match +0
4861100TF                       continue;
486650TF                       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
0TF                       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
486850TF                   }
50TF                   }
487650TF                       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
0TF                       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
4883100TF                   }
488550TF          }
100TF          }
488650TF        }
4893100TF          if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
489450TF          size_t len_rhs = strlen(rhs);
4899100TF        char rhs_expanded[2048] = "";
4902100TF        while (chunk != NULL) {
4906100TF                       SV **col_sv = av_fetch(cols, c, 0);
490850TF                           const char *col_name = SvPV_nolen(*col_sv);
4910100TF                               size_t slen = strlen(col_name);
100TF                               size_t slen = strlen(col_name);
4911100TF                               if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
491250TF                                   if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
491850TF                   }
4919100TF                   SvREFCNT_dec(cols);
4920100TF          } else {
4921100TF                   size_t slen = strlen(chunk);
4922100TF                   if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
492350TF                       if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
4936100TF
494750TF                       char *restrict left = chunk;
50TF                       char *restrict left = chunk;
494850TF                       char *restrict right = star + 1;
4953100TF                       terms[num_terms++] = savepv(left);
4955100TF                       size_t inter_len = strlen(left) + strlen(right) + 2;
4959100TF                       char *restrict c_chunk = strchr(chunk, '^');
4960100TF                       if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
4962100TF                   }
496450TF          }
4965100TF        }
497050TF          if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
497350TF
4976100TF        // ========================================================================
4983100TF          if (strcmp(uniq_terms[j], "Intercept") == 0) {
4984100TF                   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = FALSE; p_exp++; continue;
4985100TF          }
4986100TF          if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
498850TF                   unsigned int num_levels = 0, levels_cap = 8;
499250TF                       if (str_val) {
5000100TF                       }
5001100TF                   }
5003100TF                       for (l1 = 0; l1 < num_levels - 1; l1++)
5007100TF                           if (p_exp >= exp_cap) {
5009100TF                               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
5015100TF                           is_dummy[p_exp] = TRUE;
5016100TF                           dummy_base[p_exp]  = savepv(uniq_terms[j]);
5019100TF                       }
100TF                       }
5034100TF        // ========================================================================
5037100TF        for (i = 0; i < n; i++) {
5039100TF          if (isnan(y_val)) { Safefree(row_names[i]); continue; }
100TF          if (isnan(y_val)) { Safefree(row_names[i]); continue; }
5042100TF          double *restrict row_x = (double*)safemalloc(p * sizeof(double));
505150TF                       } else { row_ok = FALSE; break; }
505750TF          if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
50TF          if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
506050TF          for (j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
50TF          for (j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
50630TF          Safefree(row_x);
50670TF        if (valid_n <= p) {
5071100TF                   Safefree(exp_terms[j]);
5075100TF          Safefree(X); Safefree(Y); Safefree(valid_row_names);
508250TF        // ========================================================================
0TF        // ========================================================================
510250TF                   double sum = 0.0;
5112100TF        summary_hv = newHV(); terms_av = newAV();
5113100TF
5114100TF        df_res = (int)valid_n - final_rank;
5116100TF        // rss / mss accumulated here — rse_sq computed AFTER this loop (not before)
5121100TF        for (i = 0; i < valid_n; i++) {
513550TF        rse_sq = (df_res > 0) ? (rss / (double)df_res) : NAN;
51360TF
51370TF        int df_int = has_intercept ? 1 : 0;
5145100TF                   f_stat   = (mss / (double)numdf) / rse_sq;
50TF                   f_stat   = (mss / (double)numdf) / rse_sq;
100TF                   f_stat   = (mss / (double)numdf) / rse_sq;
50TF                   f_stat   = (mss / (double)numdf) / rse_sq;
515350TF        }
515650TF          hv_store(coef_hv, exp_terms[j], strlen(exp_terms[j]), newSVnv(beta[j]), 0);
5157100TF          av_push(terms_av, newSVpv(exp_terms[j], 0));
5167100TF                   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
517450TF        }
50TF        }
0TF        }
0TF        }
518050TF        hv_store(res_hv, "rank",           4, newSVuv(final_rank),         0);
5184100TF        hv_store(res_hv, "r.squared",      9, newSVnv(r_squared),          0);
5188100TF          av_push(fstat_av, newSVnv(f_stat));
5189100TF          av_push(fstat_av, newSViv(numdf));
519050TF          av_push(fstat_av, newSViv(df_res));
5194100TF
519750TF        for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
5200100TF          if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
5207100TF        RETVAL = newRV_noinc((SV*)res_hv);
50TF        RETVAL = newRV_noinc((SV*)res_hv);
5212100TFvoid seq(from, to, by = 1.0)
524850TF        CODE:
525350TF          size_t n = 0;
525550TF          int arg_start = 0;
525750TF          // Check if the first argument is a simple integer (rnorm(33))
525950TF                   n = (unsigned int)SvUV(ST(0));
50TF                   n = (unsigned int)SvUV(ST(0));
526250TF
5263100TF          // --- Parse remaining named arguments from the flat stack ---
52670TF
0TF
52690TF                   const char* restrict key = SvPV_nolen(ST(i));
0TF                   const char* restrict key = SvPV_nolen(ST(i));
52710TF
52790TF
52820TF                   av_extend(result_av, n - 1);
52830TF                   // Generate random normals using the Box-Muller transform
52840TF                   for (size_t i = 0; i < n; ) {
52860TF                        do {
0TF                        do {
0TF                        do {
52920TF                        
5303100TF        OUTPUT:
100TF        OUTPUT:
50TF        OUTPUT:
5307100TF        SV* data_sv
5308100TF        SV* formula_sv
530950TF        CODE:
531750TF          char **restrict dummy_base = NULL, **restrict dummy_level = NULL;
531850TF          int *restrict term_map = NULL, *restrict left_idx = NULL, *restrict right_idx = NULL;
531950TF          unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
532050TF          size_t n = 0, valid_n = 0, i, j;
0TF          size_t n = 0, valid_n = 0, i, j;
532150TF          bool has_intercept = TRUE;
532250TF
0TF
532350TF          char **restrict row_names = NULL;
0TF          char **restrict row_names = NULL;
532550TF          HV *restrict data_hoa = NULL;
532650TF          SV *restrict ref = NULL;
532850TF          double **restrict X_mat = NULL;
50TF          double **restrict X_mat = NULL;
5333100TF          // ========================================================================
5334100TF          // PHASE 1: Data Extraction
5336100TF          ref = SvRV(data_sv);
533850TF                   HV*restrict hv = (HV*)ref;
50TF                   HV*restrict hv = (HV*)ref;
5340100TF                   entry = hv_iternext(hv);
534250TF                        SV*restrict val = hv_iterval(hv, entry);
5343100TF                        if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
535350TF                            Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
5354100TF                            i = 0;
537050TF                        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
537250TF                            char buf[32];
5374100TF                            row_names[i] = savepv(buf);
537550TF                        } else {
5380100TF                   }
538550TF          // ========================================================================
0TF          // ========================================================================
538650TF          src = (char*)formula; dst = f_cpy;
0TF          src = (char*)formula; dst = f_cpy;
539450TF                   croak("aov: invalid formula, missing '~'");
0TF                   croak("aov: invalid formula, missing '~'");
5401100TF          while ((p_idx = strstr(rhs, "-1")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
5403100TF          while ((p_idx = strstr(rhs, "0+")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
540450TF          if (rhs[0] == '0' && rhs[1] == '\0')        { has_intercept = FALSE; rhs[0] = '\0'; }
540650TF          if (rhs[0] == '1' && rhs[1] == '\0')        { rhs[0] = '\0'; }
5418100TF                   if (strcmp(chunk, ".") == 0) {
5419100TF                        AV *restrict cols = get_all_columns(data_hoa, row_hashes, n);
5427100TF                                        if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
5437100TF                        if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
5445100TF
5446100TF          // Setup arrays safely
5447100TF          Newx(terms, term_cap, char*);
5450100TF          Newx(is_dummy, exp_cap, bool); Newx(is_interact, exp_cap, bool);
50TF          Newx(is_dummy, exp_cap, bool); Newx(is_interact, exp_cap, bool);
5454100TF          if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
5455100TF
545650TF          if (strlen(rhs_expanded) > 0) {
5477100TF                            char *restrict c_chunk = strchr(chunk, '^');
5481100TF                        chunk = strtok(NULL, "+");
548350TF          }
5485100TF          for (i = 0; i < num_terms; i++) {
5486100TF                   bool found = FALSE;
5488100TF                         if (strcmp(terms[i], uniq_terms[k]) == 0) { found = TRUE; break; }
548950TF                   }
549650TF          Newxz(term_base_level, num_uniq, char*);
5497100TF          /* ----------------------------------------------------------------------- */
5498100TF
5499100TF          // ========================================================================
5509100TF                   }
551050TF
5527100TF                         int *restrict l_indices = (int*)safemalloc(p_exp * sizeof(int)); int l_count = 0;
5547100TF                                     size_t t_len = strlen(exp_terms[l_indices[li]]) + strlen(exp_terms[r_indices[ri]]) + 2;
554850TF                                     exp_terms[p_exp] = (char*)safemalloc(t_len);
5552100TF                                     left_idx[p_exp] = l_indices[li];
555450TF                                     term_map[p_exp] = j;
5557100TF                             }
5558100TF                         }
5560100TF                   } else {
5562100TF                             char **restrict levels = NULL;
556450TF                             Newx(levels, levels_cap, char*);
5565100TF                             for (i = 0; i < n; i++) {
557050TF                                         if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; }
557350TF                                         if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
5576100TF                                     Safefree(str_val);
5582100TF                                     for (size_t l2 = l1 + 1; l2 < num_levels; l2++) {
5584100TF                                             char *tmp = levels[l1]; levels[l1] = levels[l2]; levels[l2] = tmp;
5585100TF                                         }
5586100TF                                     }
558850TF
5594100TF                                     if (p_exp >= exp_cap) {
559650TF                                         Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
559850TF                                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
100TF                                         Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
5614100TF                                 Safefree(levels);
5615100TF                                 exp_terms[p_exp] = savepv(uniq_terms[j]);
5616100TF                                 parent_term[p_exp] = savepv(uniq_terms[j]);
5623100TF                             parent_term[p_exp] = savepv(uniq_terms[j]);
5624100TF                             is_dummy[p_exp] = FALSE; is_interact[p_exp] = FALSE;
5627100TF                         }
563150TF          for(i = 0; i < n; i++) X_mat[i] = (double*)safemalloc(p_exp * sizeof(double));
5634100TF          // PHASE 4: Matrix Construction & Listwise Deletion
5635100TF          // ========================================================================
5639100TF                   bool row_ok = TRUE;
564450TF                         } else if (is_interact[j]) {
100TF                         } else if (is_interact[j]) {
5666100TF          if (valid_n <= p_exp) {
566850TF                   for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
50TF                   for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
5672100TF                        if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
5674100TF                   Safefree(exp_terms); Safefree(parent_term);
5676100TF                   Safefree(dummy_base); Safefree(dummy_level);
5689100TF          // ========================================================================
5690100TF          bool *restrict aliased_qr = (bool*)safemalloc(p_exp * sizeof(bool));
5691100TF          size_t *restrict rank_map = (size_t*)safemalloc(p_exp * sizeof(size_t));
5693100TF          double *restrict term_ss;
5700100TF                   if (aliased_qr[i]) continue;
570350TF                   term_ss[t_idx] += Y[r_k] * Y[r_k];
5714100TF          int res_df = valid_n - rank;
5721100TF                   double ss = term_ss[j];
572250TF                   int df = term_df[j];
572550TF                   hv_stores(term_stats, "Df", newSViv(df));
50TF                   hv_stores(term_stats, "Df", newSViv(df));
572750TF                   hv_stores(term_stats, "Mean Sq", newSVnv(ms));
573250TF                   } else {
5736100TF                   hv_store(ret_hash, uniq_terms[j], strlen(uniq_terms[j]), newRV_noinc((SV*)term_stats), 0);
573850TF
574150TF          hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
50TF          hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
50TF          hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
50TF          hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
574850TF                   HV *restrict mean_hv  = newHV();
50TF                   HV *restrict mean_hv  = newHV();
574950TF                   HV *restrict size_hv  = newHV();
50TF                   HV *restrict size_hv  = newHV();
575050TF                   for (size_t c = 0; c <= (size_t)av_len(all_cols); c++) {
50TF                   for (size_t c = 0; c <= (size_t)av_len(all_cols); c++) {
575150TF                       SV **restrict col_sv = av_fetch(all_cols, c, 0);
50TF                       SV **restrict col_sv = av_fetch(all_cols, c, 0);
575550TF                       IV     col_count = 0;
575850TF                           if (!isnan(val)) { col_sum += val; col_count++; }
576150TF                       hv_store(mean_hv, col_name, strlen(col_name), newSVnv(col_mean), 0);
50TF                       hv_store(mean_hv, col_name, strlen(col_name), newSVnv(col_mean), 0);
5764100TF                   SvREFCNT_dec(all_cols);
5765100TF                   HV *restrict gs_hv = newHV();
576850TF                   hv_stores(ret_hash, "group_stats", newRV_noinc((SV*)gs_hv));
50TF                   hv_stores(ret_hash, "group_stats", newRV_noinc((SV*)gs_hv));
576950TF          }
50TF          }
577450TF          for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
50TF          for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
577950TF          Safefree(exp_terms); Safefree(parent_term);
578050TF          Safefree(is_dummy); Safefree(is_interact);
578550TF          Safefree(X_mat); Safefree(Y);
578650TF          Safefree(aliased_qr); Safefree(rank_map);
578750TF          if (row_hashes) Safefree(row_hashes);
50TF          if (row_hashes) Safefree(row_hashes);
578850TF          RETVAL = newRV_noinc((SV*)ret_hash);
50TF          RETVAL = newRV_noinc((SV*)ret_hash);
578950TF        }
50TF        }
579050TFOUTPUT:
50TFOUTPUT:
583250TF                   a = (a_ptr && SvOK(*a_ptr)) ? SvIV(*a_ptr) : 0;
5833100TF                   b = (b_ptr && SvOK(*b_ptr)) ? SvIV(*b_ptr) : 0;
5837100TF                  croak("Invalid 2D Array structure");
5838100TF          }
5839100TF        } else if (SvTYPE(deref) == SVt_PVHV) {
584050TF                // Fixed 2D Hash Logic: Sort keys lexically to enforce structured rows/columns
100TF                // Fixed 2D Hash Logic: Sort keys lexically to enforce structured rows/columns
5841100TF                HV*restrict outer = (HV*)deref;
5842100TF                if (hv_iterinit(outer) != 2) croak("Outer hash must have exactly 2 keys");
584350TF                HE*restrict he1 = hv_iternext(outer);
58440TF                HE*restrict he2 = hv_iternext(outer);
58450TF                if (!he1 || !he2) croak("Invalid outer hash");
5849100TF                HE*restrict row2_he = (strcmp(k1, k2) < 0) ? he2 : he1;
50TF                HE*restrict row2_he = (strcmp(k1, k2) < 0) ? he2 : he1;
585050TF                SV*restrict row1_sv = hv_iterval(outer, row1_he);
50TF                SV*restrict row1_sv = hv_iterval(outer, row1_he);
5851100TF                SV*restrict row2_sv = hv_iterval(outer, row2_he);
50TF                SV*restrict row2_sv = hv_iterval(outer, row2_he);
585250TF                if (!SvROK(row1_sv) || SvTYPE(SvRV(row1_sv)) != SVt_PVHV ||
50TF                if (!SvROK(row1_sv) || SvTYPE(SvRV(row1_sv)) != SVt_PVHV ||
5853100TF                        !SvROK(row2_sv) || SvTYPE(SvRV(row2_sv)) != SVt_PVHV) {
50TF                        !SvROK(row2_sv) || SvTYPE(SvRV(row2_sv)) != SVt_PVHV) {
5856100TF                HV*restrict in1 = (HV*)SvRV(row1_sv);
585750TF                HV*restrict in2 = (HV*)SvRV(row2_sv);
5858100TF                if (hv_iterinit(in1) != 2 || hv_iterinit(in2) != 2) croak("Inner hashes must have exactly 2 keys");
585950TF                HE*restrict in1_he1 = hv_iternext(in1);
586050TF                HE*restrict in1_he2 = hv_iternext(in1);
586250TF                const char*restrict in1_k2 = SvPV_nolen(hv_iterkeysv(in1_he2));
5866100TF                HE*restrict in2_he2 = hv_iternext(in2);
586750TF                const char*restrict in2_k1 = SvPV_nolen(hv_iterkeysv(in2_he1));
586850TF                const char*restrict in2_k2 = SvPV_nolen(hv_iterkeysv(in2_he2));
50TF                const char*restrict in2_k2 = SvPV_nolen(hv_iterkeysv(in2_he2));
5869100TF                HE*restrict in2_c1 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he1 : in2_he2;
50TF                HE*restrict in2_c1 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he1 : in2_he2;
5870100TF                HE*restrict in2_c2 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he2 : in2_he1;
5871100TF                a = (hv_iterval(in1, in1_c1) && SvOK(hv_iterval(in1, in1_c1))) ? SvIV(hv_iterval(in1, in1_c1)) : 0;
100TF                a = (hv_iterval(in1, in1_c1) && SvOK(hv_iterval(in1, in1_c1))) ? SvIV(hv_iterval(in1, in1_c1)) : 0;
5872100TF                b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
50TF                b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
50TF                b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
5873100TF                c = (hv_iterval(in2, in2_c1) && SvOK(hv_iterval(in2, in2_c1))) ? SvIV(hv_iterval(in2, in2_c1)) : 0;
50TF                c = (hv_iterval(in2, in2_c1) && SvOK(hv_iterval(in2, in2_c1))) ? SvIV(hv_iterval(in2, in2_c1)) : 0;
5874100TF                d = (hv_iterval(in2, in2_c2) && SvOK(hv_iterval(in2, in2_c2))) ? SvIV(hv_iterval(in2, in2_c2)) : 0;
587650TF          croak("Input must be a 2D Array or 2D Hash");
587850TF
0TF
5879100TF        // Perform Calculations via Helpers
5881100TF        double mle_or, ci_low, ci_high;
58850TF        HV*restrict ret_hash = newHV();
58870TF        hv_stores(ret_hash, "alternative", newSVpv(alternative, 0));
58890TF        av_push(ci_array, newSVnv(ci_low));
58930TF        hv_stores(ret_hash, "estimate", newRV_noinc((SV*)est_hash));
58950TF        hv_stores(ret_hash, "p_value", newSVnv(p_val));
0TF        hv_stores(ret_hash, "p_value", newSVnv(p_val));
58960TF        // Return the HashRef
58980TF}
59020TFSV* power_t_test(...)
59040TF{
59060TF        SV*restrict sv_delta = NULL;
5918100TF          const char* restrict key = SvPV_nolen(ST(i));
100TF          const char* restrict key = SvPV_nolen(ST(i));
5920100TF
100TF
5921100TF          if      (strEQ(key, "n"))           sv_n = val;
593550TF        bool is_null_power = (!sv_power || !SvOK(sv_power));
100TF        bool is_null_power = (!sv_power || !SvOK(sv_power));
5937100TF        bool is_null_sig_level = (sv_sig_level && !SvOK(sv_sig_level));
593950TF        unsigned int missing_count = 0;
5943100TF        if (is_null_sd) missing_count++;
50TF        if (is_null_sd) missing_count++;
5944100TF        if (is_null_sig_level) missing_count++;
594550TF
5949100TF
5952100TF        double sd = (!sv_sd || is_null_sd) ? 1.0 : SvNV(sv_sd);
595350TF        double sig_level = (!sv_sig_level || is_null_sig_level) ? 0.05 : SvNV(sv_sig_level);
59540TF        double power = is_null_power ? 0.0 : SvNV(sv_power);
5958100TF        if (is_null_power) {
50TF        if (is_null_power) {
50TF        if (is_null_power) {
5973100TF                   if (p_body(n, delta, mid, sig_level, tsample, tside, strict) > power) low = mid;
597450TF                   else high = mid;
50TF                   else high = mid;
5982100TF                   if (p_body(n, mid, sd, sig_level, tsample, tside, strict) < power) low = mid;
598450TF          }
50TF          }
598850TF          while (high - low > tol) {
599250TF          }
5998100TF        hv_stores(ret, "sd", newSVnv(sd));
600050TF        hv_stores(ret, "power", newSVnv(power));
6005100TF        if (n_str[0] != '\0') hv_stores(ret, "note", newSVpv(n_str, 0));
600750TF}
50TF}
50TF}
602150TF          if (t == SVt_PVAV) {
50TF          if (t == SVt_PVAV) {
50TF          if (t == SVt_PVAV) {
602350TF          } else if (t == SVt_PVHV) {
50TF          } else if (t == SVt_PVHV) {
50TF          } else if (t == SVt_PVHV) {
603050TF          g_sv = ST(arg_idx++);
603150TF        }
6040100TF        }
604350TF          croak("kruskal_test: cannot mix 'h' (hash-of-arrays) with 'x'/'g' inputs");
50TF          croak("kruskal_test: cannot mix 'h' (hash-of-arrays) with 'x'/'g' inputs");
50TF          croak("kruskal_test: cannot mix 'h' (hash-of-arrays) with 'x'/'g' inputs");
604450TF
50TF
6049100TF        char **restrict group_names = NULL; /* Track names to build group_stats */
606850TF                       croak("kruskal_test: every value in 'h' must be an ARRAY reference");
50TF                       croak("kruskal_test: every value in 'h' must be an ARRAY reference");
60700TF          }
60710TF          if (total < 2) croak("not enough observations");
0TF          if (total < 2) croak("not enough observations");
60740TF          size_t num_keys = HvKEYS(h_hv);
6087100TF                       SV **restrict el = av_fetch(av, i, 0);
6096100TF          k = group_id;   /* number of unique groups = number of hash keys */
609750TF
610450TF          if (!g_sv || !SvROK(g_sv) || SvTYPE(SvRV(g_sv)) != SVt_PVAV)
6124100TF                   if (x_el && SvOK(*x_el) && looks_like_number(*x_el)
612550TF                            && g_el && SvOK(*g_el)) {
50TF                            && g_el && SvOK(*g_el)) {
613250TF                       } else {
616250TF
50TF
50TF
616850TF        for (size_t i = 0; i < valid_n; i++) {
50TF        for (size_t i = 0; i < valid_n; i++) {
50TF        for (size_t i = 0; i < valid_n; i++) {
617450TF
6179100TF                   stat_base += (group_rank_sums[i] * group_rank_sums[i])
618350TF        double n_d  = (double)valid_n;
618450TF        double stat = (12.0 * stat_base / (n_d * (n_d + 1.0))) - 3.0 * (n_d + 1.0);
6185100TF        if (tie_adj > 0.0) {
618650TF          double tie_denom = 1.0 - (tie_adj / (n_d * n_d * n_d - n_d));
0TF          double tie_denom = 1.0 - (tie_adj / (n_d * n_d * n_d - n_d));
61870TF          stat /= tie_denom;
619250TF        // 9. Return structured data exactly like R's htest
50TF        // 9. Return structured data exactly like R's htest
50TF        // 9. Return structured data exactly like R's htest
619450TF        hv_stores(res, "statistic", newSVnv(stat));
50TF        hv_stores(res, "statistic", newSVnv(stat));
50TF        hv_stores(res, "statistic", newSVnv(stat));
619750TF        hv_stores(res, "p.value",   newSVnv(p_val));
50TF        hv_stores(res, "p.value",   newSVnv(p_val));
619950TF
50TF
50TF
6210100TF                   hv_store(stats_mean, group_names[i], nlen, newSVnv(mean), 0);
621250TF          }
50TF          }
50TF          }
621450TF        }
50TF        }
6225100TF
622750TF}
50TF}
50TF}
622950TF    RETVAL
50TF    RETVAL
6238100TF        unsigned int arg_idx = 0;
6239100TF
6246100TF        // 2. Shift positional argument 'y' if it's an array reference
625550TF        }
625750TF        // --- Parse named arguments from the remaining flat stack ---
626450TF          else if (strEQ(key, "ratio"))       ratio       = SvNV(val);