Branch Coverage

File:LikeR.xs
Coverage:66.9%

line%coveragebranch
3050TF        if (df <= 0.0) return 0.0;
40100TF        for (unsigned short i = 1; i < n_steps; i++) {
52100TF                double weight = (i % 2 != 0) ? 4.0 : 2.0;
73100TF        for (size_t i = 0; i < n; i++) {
79100TF        for (size_t i = 0; i < n; ) {
81100TF                while (j < n && items[j].val == items[i].val) j++;
50TF                while (j < n && items[j].val == items[i].val) j++;
83100TF                for (size_t k = i; k < j; k++) items[k].rank = avg_rank;
87100TF        for (size_t i = 0; i < n; i++) ranks[i] = items[i].rank;
93100TF        if (prob <= 0.0) return 0;
94100TF        if (prob >= 1.0) return size;
97100TF        for (size_t i = 0; i < size; i++) {
98100TF                if (Drand01() <= prob) successes++;
111100TF        for(size_t k = 0; k <= max_x - min_x; ++k) {
113100TF          if (d_val > max_d) max_d = d_val;
117100TF        for(size_t k = 0; k <= max_x - min_x; ++k) {
124100TF        for(size_t k = 0; k <= max_x - min_x; ++k) {
126100TF          if (min_x + k <= a) *lower_tail += p_prob;
127100TF          if (min_x + k >= a) *upper_tail += p_prob;
135100TF    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
143100TF    for(size_t x = min_x; x <= max_x; ++x) {
14850TF    if (a == min_x && a == max_x) *mle_or = 1.0;
0TF    if (a == min_x && a == max_x) *mle_or = 1.0;
14950TF    else if (a == min_x) *mle_or = 0.0;
150100TF    else if (a == max_x) *mle_or = INFINITY;
15350TF        for (unsigned short int i = 0; i < 3000; i++) {
156100TF            for(size_t k = 0; k <= max_x - min_x; ++k) {
158100TF                if (d_val > max_d) max_d = d_val;
161100TF            for(size_t k = 0; k <= max_x - min_x; ++k) {
168100TF            if (exp_val > a) log_high = log_mid;
170100TF            if (log_high - log_low < 1e-15) break;
179100TF    if (!is_less) {
180100TF        double target_alpha = is_greater ? alpha : alpha / 2.0;
18150TF        if (a != min_x) {
18350TF            for (unsigned short int i = 0; i < 1000; i++) {
188100TF                if (err < best_err) { best_err = err; best = mid; }
189100TF                if (ut > target_alpha) log_high = log_mid;
191100TF                if (log_high - log_low < 1e-15) break;
198100TF    if (!is_greater) {
199100TF        double target_alpha = is_less ? alpha : alpha / 2.0;
200100TF        if (a != max_x) {
20250TF            for (unsigned short int i = 0; i < 1000; i++) {
207100TF                if (err < best_err) { best_err = err; best = mid; }
208100TF                if (lt > target_alpha) log_low = log_mid;
210100TF                if (log_high - log_low < 1e-15) break;
221100TF    size_t min_x = (r2 > c1) ? 0 : c1 - r2;
226100TF    for(size_t x = min_x; x <= max_x; ++x) {
232100TF    if (strcmp(alt, "less") == 0) {
233100TF        for(size_t x = min_x; x <= a; ++x) p_val += exp(logdc[x - min_x]);
234100TF    } else if (strcmp(alt, "greater") == 0) {
235100TF        for(size_t x = a; x <= max_x; ++x) p_val += exp(logdc[x - min_x]);
239100TF        for(size_t x = min_x; x <= max_x; ++x) {
241100TF            if (p_cur <= p_obs * relErr) p_val += p_cur;
24650TF    return (p_val > 1.0) ? 1.0 : p_val;
261100TF        for (size_t k = 0; k < n; k++) {
266100TF        for (size_t k = 0; k < n; k++) {
269100TF                if (fabs(A[k * n + k]) <= 1e-10 * orig_diag[k] || fabs(A[k * n + k]) < 1e-24) {
50TF                if (fabs(A[k * n + k]) <= 1e-10 * orig_diag[k] || fabs(A[k * n + k]) < 1e-24) {
272100TF                        for (size_t i = 0; i < n; i++) {
281100TF                for (size_t j = 0; j < n; j++) A[k * n + j] *= pivot;
282100TF                for (size_t i = 0; i < n; i++) {
283100TF                        if (i != k && A[i * n + k] != 0.0) {
100TF                        if (i != k && A[i * n + k] != 0.0) {
286100TF                                  for (size_t j = 0; j < n; j++) {
299100TF    if (row_hashes) {
30150TF        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
50TF        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
50TF        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
30550TF    } else if (data_hoa) {
30750TF        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
50TF        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
50TF        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
31250TF    if (val && SvOK(*val)) {
100TF    if (val && SvOK(*val)) {
31350TF        if (looks_like_number(*val)) return SvNV(*val);
32250TF    if (data_hoa) {
325100TF        while ((entry = hv_iternext(data_hoa))) {
3280TF    } else if (row_hashes && n > 0 && row_hashes[0]) {
0TF    } else if (row_hashes && n > 0 && row_hashes[0]) {
0TF    } else if (row_hashes && n > 0 && row_hashes[0]) {
3310TF        while ((entry = hv_iternext(row_hashes[0]))) {
34050TF    if (!term || term[0] == '\0') return NAN;
50TF    if (!term || term[0] == '\0') return NAN;
344100TF    if (colon) {
35050TF        if (isnan(left) || isnan(right)) return NAN;
50TF        if (isnan(left) || isnan(right)) return NAN;
35350TF    if (strncmp(term_cpy, "I(", 2) == 0) {
3550TF        if (end) *end = '\0';
3590TF        if (caret) {
3660TF        if (isnan(v)) return NAN;
3670TF        return power == 1 ? v : pow(v, power);
376100TF        for (size_t i = 0; i < n; i++) {
378100TF                if (row_hashes) {
380100TF                        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
50TF                        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
50TF                        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
38450TF                } else if (data_hoa) {
38650TF                        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
50TF                        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
50TF                        if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
391100TF                if (val && SvOK(*val)) {
50TF                if (val && SvOK(*val)) {
392100TF                        if (looks_like_number(*val)) return false; // First valid is number -> Numeric Column
40250TF        if (row_hashes) {
4040TF                if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
0TF                if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
0TF                if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
40850TF        } else if (data_hoa) {
41050TF                if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
50TF                if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
50TF                if (col && SvROK(*col) && SvTYPE(SvRV(*col)) == SVt_PVAV) {
41550TF        if (val && SvOK(*val)) {
50TF        if (val && SvOK(*val)) {
430100TF        if (diff < 0) return -1;
43150TF        if (diff > 0) return 1;
447100TF        if (diff < 0) return -1;
448100TF        if (diff > 0) return  1;
45650TF        Newx(ri, n, RankItem);
457100TF        for (size_t i = 0; i < n; i++) { ri[i].val = in[i]; ri[i].idx = i; }
461100TF        while (i < n) {
464100TF                while (j + 1 < n && ri[j + 1].val == ri[j].val) j++;
100TF                while (j + 1 < n && ri[j + 1].val == ri[j].val) j++;
467100TF                for (size_t k = i; k <= j; k++) out[ri[k].idx] = avg;
477100TF        for (size_t i = 0; i < n; i++) {
48350TF        if (den == 0.0) return NAN;
497100TF        for (size_t i = 0; i < n - 1; i++) {
498100TF          for (size_t j = i + 1; j < n; j++) {
501100TF                   if      (sx == 0 && sy == 0) { /* joint tie — not counted */ }
50TF                   if      (sx == 0 && sy == 0) { /* joint tie — not counted */ }
502100TF                   else if (sx == 0)            tie_x++;
50350TF                   else if (sy == 0)            tie_y++;
50450TF                   else if (sx == sy)           C++;
50950TF        if (denom == 0.0) return NAN;
517100TF        if (strcmp(method, "spearman") == 0) {
51950TF          Newx(rx, n, double); Newx(ry, n, double);
50TF          Newx(rx, n, double); Newx(ry, n, double);
526100TF        if (strcmp(method, "kendall") == 0)
54250TF        if (fabs(d) < FPMIN) d = FPMIN;
54450TF        for (m = 1; m <= MAX_ITER; m++) {
54850TF          if (fabs(d) < FPMIN) d = FPMIN;
55050TF          if (fabs(c) < FPMIN) c = FPMIN;
55450TF          if (fabs(d) < FPMIN) d = FPMIN;
55650TF          if (fabs(c) < FPMIN) c = FPMIN;
558100TF          if (fabs(del - 1.0) < EPS) break;
56450TF        if (x <= 0.0) return 0.0;
565100TF        if (x >= 1.0) return 1.0;
567100TF        if (x < (a + 1.0) / (a + b + 2.0)) return bt * _incbeta_cf(a, b, x) / a;
574100TF        if (strcmp(alt, "less") == 0) return (t < 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
100TF        if (strcmp(alt, "less") == 0) return (t < 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
575100TF        if (strcmp(alt, "greater") == 0) return (t > 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
50TF        if (strcmp(alt, "greater") == 0) return (t > 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
583100TF        while (get_t_pvalue(high, df, "greater") > p_tail) {
58650TF          if (high > 1000000.0) break; /* Fallback limit */
58950TF        for (unsigned short int i = 0; i < 100; i++) {
592100TF          if (p_mid > p_tail) {
597100TF          if (high - low < 1e-8) break;
6090TF        if (n == 0) return 1;
61850TF        double step = (n_bins > 0) ? (breaks[1] - breaks[0]) : 0.0;
620100TF        for (size_t i = 0; i < n_bins; i++) {
625100TF        if (step > 0.0) {
626100TF          for (size_t j = 0; j < n; j++) {
62950TF                   if (isnan(val) || isinf(val) || val < min_val) continue;
50TF                   if (isnan(val) || isinf(val) || val < min_val) continue;
50TF                   if (isnan(val) || isinf(val) || val < min_val) continue;
633100TF                   if (idx >= n_bins) {
639100TF                   while (idx > 0 && val <= breaks[idx]) {
100TF                   while (idx > 0 && val <= breaks[idx]) {
643100TF                   while (idx < n_bins - 1 && val > breaks[idx + 1]) {
50TF                   while (idx < n_bins - 1 && val > breaks[idx + 1]) {
64850TF        } else if (n_bins > 0) {
653100TF        for (size_t i = 0; i < n_bins; i++) {
655100TF          if (bin_width > 0) {
65850TF                   density[i] = (n_bins == 1) ? 1.0 : 0.0;
681100TF        if (fabs(y) < 0.42) {
687100TF          if (y > 0) r = 1.0 - p;
691100TF          if (y < 0) x = -x;
709100TF        for (size_t i = 0; i < n; i++) { perm[i] = i + 1; c[i] = 0; }
724100TF        TALLY_PERM();   /* initial permutation [1, 2, ..., n] */
50TF        TALLY_PERM();   /* initial permutation [1, 2, ..., n] */
50TF        TALLY_PERM();   /* initial permutation [1, 2, ..., n] */
727100TF        while (k < n) {
728100TF          if (c[k] < k) {
730100TF                   if (k % 2 == 0) {
735100TF                   TALLY_PERM();
100TF                   TALLY_PERM();
100TF                   TALLY_PERM();
75150TF        if (strcmp(alt, "greater") == 0) return p_le;
75250TF        if (strcmp(alt, "less")    == 0) return p_ge;
75450TF        double p = 2.0 * (p_le < p_ge ? p_le : p_ge);
75550TF        return (p > 1.0) ? 1.0 : p;
764100TF        for (long i = 0; i <= max_inv; i++) dp[i] = 0.0;
767100TF        for (size_t i = 2; i <= n; i++) {
769100TF          for (long k = 0; k <= max_inv; k++) next_dp[k] = 0.0;
771100TF          for (int k = 0; k <= current_max_inv; k++) {
773100TF                   for (int j = 0; j <= i - 1 && k - j >= 0; j++) {
100TF                   for (int j = 0; j <= i - 1 && k - j >= 0; j++) {
78450TF        if (i_obs < 0) i_obs = 0;
78550TF        if (i_obs > max_inv) i_obs = max_inv;
787100TF        for (long k = i_obs; k <= max_inv; k++) p_le += dp[k];
789100TF        for (long k = 0; k <= i_obs; k++) p_ge += dp[k];
79150TF        if (strcmp(alt, "greater") == 0) return p_ge;
792100TF        if (strcmp(alt, "less") == 0) return p_le;
79450TF        double p = 2.0 * (p_ge < p_le ? p_ge : p_le);
79550TF        return p > 1.0 ? 1.0 : p;
79950TF        if (f <= 0.0) return 0.0;
808100TF        for (size_t k = 0; k < p; k++) {
81050TF                if (r >= n) {
816100TF                for (size_t i = r; i < n; i++) {
817100TF                        if (fabs(X[i][k]) > max_val) max_val = fabs(X[i][k]);
819100TF                if (max_val < 1e-10) {
825100TF                for (size_t i = r; i < n; i++) {
830100TF                double s = (X[r][k] > 0) ? -norm : norm;
834100TF                for (size_t j = k + 1; j < p; j++) {
836100TF                        for (size_t i = r + 1; i < n; i++) dot += X[i][j] * X[i][k];
839100TF                        for (size_t i = r + 1; i < n; i++) X[i][j] += tau * X[i][k];
844100TF                for (size_t i = r + 1; i < n; i++) dot_y += y[i] * X[i][k];
847100TF                for (size_t i = r + 1; i < n; i++) y[i] += tau_y * X[i][k];
86350TF        if (!sv || !SvOK(sv)) return 0;
50TF        if (!sv || !SvOK(sv)) return 0;
866100TF        for (size_t i = 0; i < len; i++) {
86750TF          if (!isdigit(s[i])) return 1;
87450TF        if (!str) str = "";
876100TF        if (strstr(str, sep) != NULL || strchr(str, '"') != NULL || strchr(str, '\r') != NULL || strchr(str, '\n') != NULL) {
100TF        if (strstr(str, sep) != NULL || strchr(str, '"') != NULL || strchr(str, '\r') != NULL || strchr(str, '\n') != NULL) {
50TF        if (strstr(str, sep) != NULL || strchr(str, '"') != NULL || strchr(str, '\r') != NULL || strchr(str, '\n') != NULL) {
50TF        if (strstr(str, sep) != NULL || strchr(str, '"') != NULL || strchr(str, '\r') != NULL || strchr(str, '\n') != NULL) {
880100TF        if (needs_quotes) {
882100TF          for (const char *restrict p = str; *p; p++) {
883100TF                   if (*p == '"') {
899100TF        for (size_t i = 0; i < len; i++) {
900100TF          if (i > 0) PerlIO_write(fh, sep, sep_len);
90150TF          if (row[i]) {
91250TF        if (x < 0.0 || a <= 0.0) return 1.0;
50TF        if (x < 0.0 || a <= 0.0) return 1.0;
91350TF        if (x == 0.0) return 1.0;
916100TF        if (x < a + 1.0) {
920100TF                while (fabs(term) > 1e-15) {
93350TF        while (i < 10000) { // Safety bound
93750TF                if (fabs(d) < 1e-30) d = 1e-30;
93950TF                if (fabs(c) < 1e-30) c = 1e-30;
943100TF                if (fabs(del - 1.0) < 1e-15) break;
95150TF        if (df <= 0) return 1.0;
95250TF        if (stat <= 0.0) return 1.0;
96350TF        if (k < 0 || k > n) return 0.0L;
50TF        if (k < 0 || k > n) return 0.0L;
96450TF        if (k > n / 2) k = n - k;
966100TF        for (int i = 1; i <= k; i++) {
977100TF    if (k < 0) return 0.0;
97850TF    if (k >= max_u) return 1.0;
983100TF    for (int j = 1; j <= n; j++) {
984100TF        for (int i = j; i <= max_u; i++) w[i] += w[i - j];
985100TF        for (int i = max_u; i >= j + m; i--) w[i] -= w[i - j - m];
989100TF    for (int i = 0; i <= k; i++) cum_p += w[i];
100350TF        if (k < 0) return 0.0;
1004100TF        if (k >= max_v) return 1.0;
1009100TF        for (int i = 1; i <= n; i++) {
1010100TF          for (int j = max_v; j >= i; j--) w[j] += w[j - i];
1014100TF        for (int i = 0; i <= k; i++) cum_p += w[i];
103050TF        if (n == 0) return 0.0;
1035100TF        while (i < n) {
1037100TF                while (j < n && ri[j].val == ri[i].val) j++;
100TF                while (j < n && ri[j].val == ri[i].val) j++;
1039100TF                for (size_t k = i; k < j; k++) ri[k].rank = r;
1041100TF                if (t > 1) { *has_ties = 1; tie_adj += ((double)t * t * t - t); }
105950TF        if (n == 0) return 1.0;
106050TF        if (n < 0) return 1.0 / r_pow_di(x, -n);
1062100TF        for (int i = 0; i < n; i++) val *= x;
10700TF        if(x <= 0.) {
10710TF          if(lower) p = 0.;
10730TF        } else if(x < 1.) {
10780TF          for(k = 1; k < k_max; k += 2) {
10820TF          if(!lower) p = 1.0 - p;
10870TF          if(lower) {
10920TF          while(fabs(old_val - new_val) > tol) {
1105100TF        for(unsigned int i = 0; i < m; i++) {
1106100TF          for(unsigned int j = 0; j < m; j++) {
1108100TF                   for(unsigned int k = 0; k < m; k++) s += A[i * m + k] * B[k * m + j];
1115100TF    if(n == 1) {
1116100TF        for(int i = 0; i < m * m; i++) V[i] = A[i];
1124100TF    if((n % 2) == 0) {
1125100TF        for(int i = 0; i < m * m; i++) V[i] = B[i];
113150TF    if(V[(m / 2) * m + (m / 2)] > 1e140) {
11320TF        for(int i = 0; i < m * m; i++) V[i] = V[i] * 1e-140;
1146100TF        for(int i = 0; i < m; i++) {
1147100TF          for(int j = 0; j < m; j++) {
1148100TF                   if(i - j + 1 < 0) H[i * m + j] = 0;
1152100TF        for(int i = 0; i < m; i++) {
115650TF        H[(m - 1) * m] += ((2 * h - 1 > 0) ? r_pow_di(2 * h - 1, m) : 0);
1158100TF        for(int i = 0; i < m; i++) {
1159100TF          for(int j = 0; j < m; j++) {
1160100TF                   if(i - j + 1 > 0) {
1161100TF                       for(int g = 1; g <= i - j + 1; g++) H[i * m + j] /= g;
1170100TF        for(int i = 1; i <= n; i++) {
117250TF          if(s < 1e-140) {
1191100TF        while(i < nx || j < ny) {
50TF        while(i < nx || j < ny) {
119350TF          if (i < nx && j < ny) val = (x[i] < y[j]) ? x[i] : y[j];
100TF          if (i < nx && j < ny) val = (x[i] < y[j]) ? x[i] : y[j];
100TF          if (i < nx && j < ny) val = (x[i] < y[j]) ? x[i] : y[j];
119450TF          else if (i < nx) val = x[i];
1197100TF          while(i < nx && x[i] <= val) i++;
100TF          while(i < nx && x[i] <= val) i++;
1198100TF          while(j < ny && y[j] <= val) j++;
100TF          while(j < ny && y[j] <= val) j++;
1204100TF          if (diff > max_d_plus) max_d_plus = diff;
1205100TF          if (-diff > max_d_minus) max_d_minus = -diff;
1206100TF          if (fabs(diff) > max_d) max_d = fabs(diff);
1215100TF        if (two_sided) return (fabs(r - s) >= q);
1225100TF        for(unsigned int j = 1; j <= n; j++) {
1226100TF          if(psmirnov_exact_test(q, 0., j / nd, two_sided)) u[j] = 1.;
1229100TF        for(unsigned int i = 1; i <= m; i++) {
1230100TF          if(psmirnov_exact_test(q, i / md, 0., two_sided)) u[0] = 1.;
1231100TF          for(int j = 1; j <= n; j++) {
1232100TF                   if(psmirnov_exact_test(q, i / md, j / nd, two_sided)) u[j] = 1.;
124750TF        if (nu < 1e-7) nu = 1e-7;
125550TF        if (strict && tside == 2) {
0TF        if (strict && tside == 2) {
127650TF        }
50TF        }
50TF        }
128150TF                double p_mid = pf(mid, df1, df2);
1282100TF
50TF
128550TF                } else {
1292100TF// --- XS SECTION ---
129550TFSV* ks_test(...)
129650TFCODE:
129750TF{
12980TF        SV *restrict x_sv = NULL, *restrict y_sv = NULL;
130150TF        int arg_idx = 0;
130550TF                x_sv = ST(arg_idx);
50TF                x_sv = ST(arg_idx);
50TF                x_sv = ST(arg_idx);
1313100TF                } else if (SvPOK(ST(arg_idx))) {
100TF                } else if (SvPOK(ST(arg_idx))) {
50TF                } else if (SvPOK(ST(arg_idx))) {
131950TF        // Parse named arguments
1324100TF          else if (strEQ(key, "y"))           y_sv = val;
132650TF                   if (!SvOK(val)) exact = -1;
50TF                   if (!SvOK(val)) exact = -1;
50TF                   if (!SvOK(val)) exact = -1;
133550TF        }
100TF        }
50TF        }
1341100TF        if (!is_two_sided && !is_greater && !is_less) {
134350TF        }
50TF        }
50TF        }
134850TF
50TF
1357100TF        }
1358100TF
136350TF        if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
136450TF          AV *restrict y_av = (AV*)SvRV(y_sv);
1370100TF                   SV**restrict el = av_fetch(y_av, i, 0);
1371100TF                   if (el && SvOK(*el) && looks_like_number(*el)) {
1375100TF
137650TF          if (valid_nx < 1 || valid_ny < 1) {
137950TF          }
50TF          }
138350TF
13900TF          bool use_exact = false;
139950TF          for(size_t i=0; i<valid_ny; i++) comb[valid_nx+i] = y_data[i];
50TF          for(size_t i=0; i<valid_ny; i++) comb[valid_nx+i] = y_data[i];
140150TF
1404100TF                   if(comb[i] == comb[i-1]) { has_ties = true; break; }
141250TF                   method_desc = "Two-sample Kolmogorov-Smirnov exact test";
1413100TF                   double q = (0.5 + floor(statistic * valid_nx * valid_ny - 1e-7)) / ((double)valid_nx * valid_ny);
1414100TF                   p_value = psmirnov_exact_uniq_upper(q, valid_nx, valid_ny, is_two_sided);
141550TF          } else {
1417100TF                   double z = statistic * sqrt((double)(valid_nx * valid_ny) / (valid_nx + valid_ny));
141850TF                   if (is_two_sided) {
142050TF                   } else {
142150TF                       p_value = exp(-2.0 * z * z); // One-sided limit distribution
142350TF          }
142450TF          Safefree(y_data);
142650TF        // --- ONE SAMPLE (e.g. against pnorm) ---
14360TF                       
144850TF                   if (is_greater) statistic = max_d_plus;
144950TF                   else if (is_less) statistic = max_d_minus;
147050TF          }
100TF          }
50TF          }
147550TF        Safefree(x_data);
100TF        Safefree(x_data);
50TF        Safefree(x_data);
148050TF        hv_stores(res, "p_value", newSVnv(p_value));
1484100TF}
1487100TF
1488100TFSV* wilcox_test(...)
1489100TFCODE:
149050TF{
1491100TF        SV *restrict x_sv = NULL, *restrict y_sv = NULL;
149250TF        bool paired = false, correct = true;
14930TF        double mu = 0.0;
149650TF        int arg_idx = 0;
1500100TF                arg_idx++;
50TF                arg_idx++;
50TF                arg_idx++;
150450TF                y_sv = ST(arg_idx);
1508100TF        if ((items - arg_idx) % 2 != 0) {
50TF        if ((items - arg_idx) % 2 != 0) {
50TF        if ((items - arg_idx) % 2 != 0) {
1516100TF                else if (strEQ(key, "y"))           y_sv = val;
100TF                else if (strEQ(key, "y"))           y_sv = val;
1519100TF                else if (strEQ(key, "mu"))          mu = SvNV(val);
152150TF                        if (!SvOK(val)) exact = -1;
50TF                        if (!SvOK(val)) exact = -1;
50TF                        if (!SvOK(val)) exact = -1;
1527100TF        // --- Validate required / types ---
152950TF                croak("wilcox_test: 'x' is a required argument and must be an ARRAY reference");
50TF                croak("wilcox_test: 'x' is a required argument and must be an ARRAY reference");
50TF                croak("wilcox_test: 'x' is a required argument and must be an ARRAY reference");
153550TF        size_t ny = 0;
153650TF        if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
1541100TF        const char *restrict method_desc = "";
100TF        const char *restrict method_desc = "";
154450TF        if (ny > 0 && !paired) {
154550TF                RankInfo *restrict ri = (RankInfo *)safemalloc((nx + ny) * sizeof(RankInfo));
154650TF                size_t valid_nx = 0, valid_ny = 0;
50TF                size_t valid_nx = 0, valid_ny = 0;
100TF                size_t valid_nx = 0, valid_ny = 0;
1548100TF                        SV**restrict el = av_fetch(x_av, i, 0);
50TF                        SV**restrict el = av_fetch(x_av, i, 0);
1552100TF                                valid_nx++;
1557100TF                        if (el && SvOK(*el) && looks_like_number(*el)) {
155850TF                                ri[valid_nx + valid_ny].val = SvNV(*el);
15600TF                                valid_ny++;
156450TF                if (valid_ny == 0) { Safefree(ri); croak("not enough 'y' observations"); }
157050TF                statistic = w_rank_sum - (double)valid_nx * (valid_nx + 1.0) / 2.0;
157150TF                
100TF                
15720TF                if (exact == 1) use_exact = true;
15730TF                else if (exact == 0) use_exact = false;
157750TF                        warn("cannot compute exact p-value with ties; falling back to approximation");
157850TF                        use_exact = false;
1583100TF                        double p_greater = 1.0 - exact_pwilcox(statistic - 1.0, valid_nx, valid_ny);
50TF                        double p_greater = 1.0 - exact_pwilcox(statistic - 1.0, valid_nx, valid_ny);
100TF                        double p_greater = 1.0 - exact_pwilcox(statistic - 1.0, valid_nx, valid_ny);
1587100TF                        else {
158950TF                                p_value = 2.0 * p;
50TF                                p_value = 2.0 * p;
50TF                                p_value = 2.0 * p;
1592100TF                        method_desc = correct ? "Wilcoxon rank sum test with continuity correction" : "Wilcoxon rank sum test";
159450TF                        double var = ((double)valid_nx * valid_ny / 12.0) * ((total_n + 1.0) - tie_adj / (total_n * (total_n - 1.0)));
50TF                        double var = ((double)valid_nx * valid_ny / 12.0) * ((total_n + 1.0) - tie_adj / (total_n * (total_n - 1.0)));
50TF                        double var = ((double)valid_nx * valid_ny / 12.0) * ((total_n + 1.0) - tie_adj / (total_n * (total_n - 1.0)));
159750TF                        double CORRECTION = 0.0;
160150TF                                else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
160550TF                        if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
1610100TF        } else { // --- ONE SAMPLE / PAIRED ---
1617100TF                        if (!x_el || !SvOK(*x_el) || !looks_like_number(*x_el)) continue;
1618100TF                        double dx = SvNV(*x_el);
162050TF                        if (paired) {
162150TF                                SV**restrict y_el = av_fetch(y_av, i, 0);
162250TF                                if (!y_el || !SvOK(*y_el) || !looks_like_number(*y_el)) continue;
50TF                                if (!y_el || !SvOK(*y_el) || !looks_like_number(*y_el)) continue;
162350TF                                double dy = SvNV(*y_el);
50TF                                double dy = SvNV(*y_el);
162750TF                        } else {
50TF                        } else {
163150TF                        }
163650TF                }
163750TF                RankInfo *ri = (RankInfo *)safemalloc(n_nz * sizeof(RankInfo));
163950TF                        ri[i].val = fabs(diffs[i]);
16430TF                double tie_adj = rank_and_count_ties(ri, n_nz, &has_ties);
16480TF                if (exact == 1) use_exact = true;
16490TF                else if (exact == 0) use_exact = false;
0TF                else if (exact == 0) use_exact = false;
16500TF                else use_exact = (n_nz < 50 && !has_ties);
16510TF                if (use_exact && has_ties) {
16550TF                if (use_exact && has_zeroes) {
16560TF                        warn("cannot compute exact p-value with zeroes; falling back to approximation");
166150TF                        double p_less = exact_psignrank(statistic, n_nz);
167750TF                                if (strcmp(alternative, "two.sided") == 0) CORRECTION = (z > 0 ? 0.5 : -0.5);
168050TF                        }
100TF                        }
50TF                        }
168350TF                        if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
1691100TF        hv_stores(res, "statistic", newSVnv(statistic));
50TF        hv_stores(res, "statistic", newSVnv(statistic));
100TF        hv_stores(res, "statistic", newSVnv(statistic));
1694100TF        hv_stores(res, "alternative", newSVpv(alternative, 0));
1697100TFOUTPUT:
1698100TF        RETVAL
1699100TF
1702100TFCODE:
1710100TF                AV*restrict first_row = (AV*)SvRV(*first_elem);
1714100TF                r = 1;
1719100TF        int yates = (is_2d && r == 2 && c == 2) ? 1 : 0;
172250TF        if (is_2d) {
1734100TF                                 col_sum[j] += val;
1739100TF                        AV*restrict exp_row = newAV();
1753100TF                                } else {
1754100TF                                  stat += ((O - E) * (O - E)) / E;
177750TF        hv_store(results, "statistic", 9, newSVnv(stat), 0);
50TF        hv_store(results, "statistic", 9, newSVnv(stat), 0);
1779100TF        hv_store(results, "p_value", 7, newSVnv(p_val), 0);
50TF        hv_store(results, "p_value", 7, newSVnv(p_val), 0);
178450TF                } else {
1794100TF
179550TFPROTOTYPES: ENABLE
179850TFPPCODE:
1799100TF{
180050TF        SV *restrict data_sv = NULL;
1801100TF        SV *restrict file_sv = NULL;
180250TF        unsigned int arg_idx = 0;
180650TF          int type = SvTYPE(SvRV(ST(arg_idx)));
50TF          int type = SvTYPE(SvRV(ST(arg_idx)));
1810100TF          }
50TF          }
181450TF          arg_idx++;
50TF          arg_idx++;
1817100TF        const char *restrict sep = ",";
50TF        const char *restrict sep = ",";
1818100TF        SV *restrict row_names_sv = sv_2mortal(newSViv(1));
50TF        SV *restrict row_names_sv = sv_2mortal(newSViv(1));
1827100TF          else if (strEQ(key, "col.names")) col_names_sv = val;
182950TF          else if (strEQ(key, "row.names")) row_names_sv = val;
183350TF
50TF
1837100TF        SV *restrict data_ref = SvRV(data_sv);
50TF        SV *restrict data_ref = SvRV(data_sv);
1845100TF        if (col_names_sv && SvOK(col_names_sv)) {
184750TF                   croak("write_table: 'col.names' must be an ARRAY reference\n");
50TF                   croak("write_table: 'col.names' must be an ARRAY reference\n");
50TF                   croak("write_table: 'col.names' must be an ARRAY reference\n");
18480TF          }
1852100TF        AV *restrict rows_av = NULL;
1855100TF        if (SvTYPE(data_ref) == SVt_PVHV) {
186150TF                if (!first_val || !SvROK(first_val)) {
186350TF                }
50TF                }
50TF                }
50TF                }
1867100TF                }
186950TF                is_hoa = (first_type == SVt_PVAV);
50TF                is_hoa = (first_type == SVt_PVAV);
50TF                is_hoa = (first_type == SVt_PVAV);
50TF                is_hoa = (first_type == SVt_PVAV);
187750TF                if (is_hoh) {
188050TF                        while ((entry = hv_iternext(hv))) {
100TF                        while ((entry = hv_iternext(hv))) {
1884100TF        } else {
1885100TF                AV *restrict av = (AV*)data_ref;
50TF                AV *restrict av = (AV*)data_ref;
1887100TF                SV **restrict first_ptr = av_fetch(av, 0, 0);
188950TF                        croak("write_table: For ARRAY data, all elements must be HASH references (Array of Hashes)\n");
50TF                        croak("write_table: For ARRAY data, all elements must be HASH references (Array of Hashes)\n");
1895100TF                                 croak("write_table: Mixed data types detected in Array of Hashes. All elements must be HASH references.\n");
1899100TF        }
1905100TF        bool inc_rownames = (row_names_sv && SvTRUE(row_names_sv)) ? 1 : 0;
1910100TF                if (col_names_sv && SvOK(col_names_sv)) {
191850TF                        hv_iterinit((HV*)data_ref);
1919100TF                        HE *restrict entry;
192150TF                                HV *restrict inner = (HV*)SvRV(hv_iterval((HV*)data_ref, entry));
50TF                                HV *restrict inner = (HV*)SvRV(hv_iterval((HV*)data_ref, entry));
1928100TF                        unsigned num_cols = hv_iterinit(col_map);
1936100TF                        safefree(col_array);
193850TF          }
194150TF
1943100TF          if (inc_rownames) header_row[h_idx++] = "";
194550TF                   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
50TF                   SV**restrict h_ptr = av_fetch(headers_av, i, 0);
194650TF                   header_row[h_idx++] = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
1947100TF          }
100TF          }
1948100TF          print_string_row(fh, header_row, h_idx, sep);
195250TF          const char **restrict row_array = safemalloc(num_rows * sizeof(char*));
195350TF          for(size_t i=0; i<num_rows; i++) {
1965100TF                   SV **restrict inner_hv_ptr = hv_fetch(data_hv, row_array[i], strlen(row_array[i]), 0);
1970100TF                       const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
1973100TF                           if (SvROK(*cell_ptr)) {
1976100TF                     safefree(row_data);
50TF                     safefree(row_data);
1978100TF                       if (rows_av) SvREFCNT_dec(rows_av);
198050TF                           }
50TF                           }
1985100TF                   }
1990100TF        } else if (is_hoa) { // ----- Hash of Arrays -----
199350TF                hv_iterinit(data_hv);
1994100TF                HE *restrict entry;
50TF                HE *restrict entry;
19980TF                        if (len > max_rows) max_rows = len;
20000TF
0TF
20020TF                        AV *restrict c_av = (AV*)SvRV(col_names_sv);
2012100TF                                 col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
2013100TF                        }
201550TF                        for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
50TF                        for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
2020100TF                        rownames_col = SvPV_nolen(row_names_sv);
2022100TF
202350TF                        for(size_t i=0; i<=av_len(headers_av); i++) {
20250TF                                 if (!h_ptr || !*h_ptr) continue;
0TF                                 if (!h_ptr || !*h_ptr) continue;
20280TF                                     av_push(filtered_headers, newSVsv(h_sv));
0TF                                     av_push(filtered_headers, newSVsv(h_sv));
20290TF                                 }
20320TF                        headers_av = filtered_headers;
2048100TF                                 if (rownames_col) {
205050TF                                     if (rn_arr_ptr && SvROK(*rn_arr_ptr)) {
50TF                                     if (rn_arr_ptr && SvROK(*rn_arr_ptr)) {
205250TF                                         SV **restrict rn_val_ptr = av_fetch(rn_arr, i, 0);
50TF                                         SV **restrict rn_val_ptr = av_fetch(rn_arr, i, 0);
2055100TF                                                 PerlIO_close(fh);
100TF                                                 PerlIO_close(fh);
205650TF                                                 safefree(row_data);
20590TF                                             }
2071100TF                                 }
50TF                                 }
207450TF                                 SV**restrict h_ptr = av_fetch(headers_av, j, 0);
2077100TF                                 if (arr_ptr && SvROK(*arr_ptr)) {
50TF                                 if (arr_ptr && SvROK(*arr_ptr)) {
2079100TF                                     SV **restrict cell_ptr = av_fetch(arr, i, 0);
208150TF                                         if (SvROK(*cell_ptr)) {
50TF                                         if (SvROK(*cell_ptr)) {
2085100TF                                             croak("write_table: Cannot write nested reference types to table\n");
208750TF                                         row_data[d_idx++] = SvPV_nolen(*cell_ptr);
50TF                                         row_data[d_idx++] = SvPV_nolen(*cell_ptr);
2091100TF                                 } else {
2098100TF                safefree(row_data);
2103100TF                        AV *restrict c_av = (AV*)SvRV(col_names_sv);
2107100TF                        }
50TF                        }
21100TF                        for(size_t i=0; i<num_rows; i++) {
21120TF                                 if (row_ptr && SvROK(*row_ptr)) {
0TF                                 if (row_ptr && SvROK(*row_ptr)) {
21140TF                                     hv_iterinit(row_hv);
2124100TF                                 HE *restrict ce = hv_iternext(col_map);
2125100TF                                 col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
212750TF                        qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
50TF                        qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
2132100TF                if (inc_rownames && contains_nondigit(row_names_sv)) {
213550TF                        for(size_t i=0; i<=av_len(headers_av); i++) {
50TF                        for(size_t i=0; i<=av_len(headers_av); i++) {
2136100TF                                 SV**restrict h_ptr = av_fetch(headers_av, i, 0);
213750TF                                 if (!h_ptr || !*h_ptr) continue;
21380TF                                 SV *restrict h_sv = *h_ptr;
21390TF                                 if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
0TF                                 if (strcmp(SvPV_nolen(h_sv), rownames_col) != 0) {
21400TF                                     av_push(filtered_headers, newSVsv(h_sv));
21430TF                        SvREFCNT_dec(headers_av);
2157100TF                for(size_t i=0; i<num_rows; i++) {
215950TF                        SV **restrict row_ptr = av_fetch(data_av, i, 0);
50TF                        SV **restrict row_ptr = av_fetch(data_av, i, 0);
216050TF                        HV *restrict row_hv = (row_ptr && SvROK(*row_ptr)) ? (HV*)SvRV(*row_ptr) : NULL;
2161100TF                        if (inc_rownames) {
50TF                        if (inc_rownames) {
216250TF                                if (rownames_col) {
21650TF                                                if (SvROK(*rn_val_ptr)) {
2174100TF                                  }
50TF                                  }
217850TF                                  row_data[d_idx++] = savepv(buf);
2179100TF                                }
219650TF                                 }
50TF                                 }
50TF                                 }
220150TF                safefree(row_data);
220250TF        }
220550TF        PerlIO_close(fh);
2210100TF_parse_csv_file(char* file, const char* sep_str, const char* comment_str, SV* callback = &PL_sv_undef)
221450TF        AV *restrict current_row = newAV();
100TF        AV *restrict current_row = newAV();
221650TF        bool in_quotes = 0, post_quote = 0;
100TF        bool in_quotes = 0, post_quote = 0;
2220100TFCODE:
222350TF        } else {
222450TF                data = newAV();
100TF                data = newAV();
222650TF        sep_len = sep_str ? strlen(sep_str) : 0;
222950TF        fp = PerlIO_open(file, "r");
50TF        fp = PerlIO_open(file, "r");
50TF        fp = PerlIO_open(file, "r");
2234100TF        // Read line by line using PerlIO
2236100TF                char *restrict line = SvPV_nolen(line_sv);
2237100TF                size_t len = SvCUR(line_sv);
2238100TF                // chomp \r\n (Handles Windows invisible \r natively)
100TF                // chomp \r\n (Handles Windows invisible \r natively)
100TF                // chomp \r\n (Handles Windows invisible \r natively)
2241100TF                        if (len > 0 && line[len-1] == '\r') {
2244100TF                }
2247100TF                        bool is_empty = 1;
50TF                        bool is_empty = 1;
50TF                        bool is_empty = 1;
100TF                        bool is_empty = 1;
2256100TF                        }
226550TF                                        i++; // Skip the escaped second quote
226950TF                                } else if (!post_quote) {
227050TF                                        in_quotes = 1; // Open quotes (only when not in post-quote state)
227350TF                                av_push(current_row, newSVsv(field));
2285100TF                        post_quote = 0; // Reset post-quote state at row boundary
228750TF                        av_push(current_row, newSVsv(field));
229150TF                                dSP;
229250TF                                ENTER;
229550TF                                XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
230650TF        }
231850TF                        PUTBACK;
50TF                        PUTBACK;
232150TF                        LEAVE;
50TF                        LEAVE;
2326100TF                current_row = newAV();
2327100TF        }
232850TF        SvREFCNT_dec(field);
233750TF
2348100TF
235350TF                        croak("cov: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')", method);
50TF                        croak("cov: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')", method);
50TF                        croak("cov: unknown method '%s' (use 'pearson', 'spearman', or 'kendall')", method);
235450TF                }
50TF                }
50TF                }
235750TF                AV *restrict y_av = (AV*)SvRV(y_sv);
50TF                AV *restrict y_av = (AV*)SvRV(y_sv);
236550TF
2373100TF                        SV **restrict x_tv = av_fetch(x_av, i, 0);
2375100TF
2376100TF                        // Extract numeric values, defaulting to NAN for missing/invalid data
2385100TF                        }
2394100TF                        // 5. Algorithm routing
2406100TF                                 if (strcmp(method, "spearman") == 0) {
246250TF        size_t n = 0, valid_n = 0, i;
2464100TF        int iter = 0, max_iter = 25, final_rank = 0, df_res = 0;
2467100TF
2468100TF        char **restrict row_names = NULL;
246950TF        char **restrict valid_row_names = NULL;
247250TF        SV *restrict ref = NULL;
247350TF
50TF
2477100TF        double *restrict XtWX = NULL, *restrict XtWZ = NULL;
50TF        double *restrict XtWX = NULL, *restrict XtWZ = NULL;
2485100TF        for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
100TF        for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
50TF        for (unsigned short i_arg = 0; i_arg < items; i_arg += 2) {
248950TF          else if (strEQ(key, "data"))    data_sv = val;
249350TF        if (!formula) croak("glm: formula is required");
249450TF        if (!data_sv || !SvROK(data_sv)) croak("glm: data is required and must be a reference");
2497100TF        bool is_gaussian = (strcmp(family_str, "gaussian") == 0);
249850TF        if (!is_binomial && !is_gaussian) croak("glm: unsupported family '%s'", family_str);
250250TF        Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
50TF        Newx(exp_terms, exp_cap, char*); Newx(is_dummy, exp_cap, bool);
250750TF        *dst = '\0';
25100TF        if (!tilde) croak("glm: invalid formula, missing '~'");
0TF        if (!tilde) croak("glm: invalid formula, missing '~'");
25110TF        *tilde = '\0';
0TF        *tilde = '\0';
252050TF                   term_cap *= 2;
0TF                   term_cap *= 2;
2526100TF          }
2528100TF          if (star) {
252950TF                   *star = '\0';
253150TF                   char *restrict c_l = strchr(left, '^'); if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
253750TF                   terms[num_terms] = (char*)safemalloc(inter_len);
253950TF          } else {
254150TF                   if (c_chunk && strncmp(chunk, "I(", 2) != 0) *c_chunk = '\0';
254350TF          }
100TF          }
254650TF
2547100TF        for (i = 0; i < num_terms; i++) {
255150TF          }
50TF          }
255350TF        }
50TF        }
2555100TF
25630TF                        SV*restrict val = hv_iterval(hv, entry);
25660TF                                 n = av_len((AV*)SvRV(val)) + 1;
0TF                                 n = av_len((AV*)SvRV(val)) + 1;
25670TF                                 Newx(row_names, n, char*);
25690TF                                     char buf[32]; snprintf(buf, sizeof(buf), "%lu", i+1);
0TF                                     char buf[32]; snprintf(buf, sizeof(buf), "%lu", i+1);
0TF                                     char buf[32]; snprintf(buf, sizeof(buf), "%lu", i+1);
25740TF                                 Newx(row_names, n, char*); Newx(row_hashes, n, HV*);
2582100TF                        } else croak("glm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
258350TF                }
2588100TF          for (i = 0; i < n; i++) {
2591100TF                       row_hashes[i] = (HV*)SvRV(*val);
259350TF                       row_names[i] = savepv(buf);
2594100TF                   } else {
259650TF                       Safefree(row_names); Safefree(row_hashes);
2598100TF                   }
2599100TF          }
2601100TF
260250TF        // --- Categorical Expansion ---
0TF        // --- Categorical Expansion ---
260850TF          }
2609100TF          if (strcmp(uniq_terms[j], "Intercept") == 0) {
2610100TF                   exp_terms[p_exp] = savepv("Intercept"); is_dummy[p_exp] = false; p_exp++; continue;
261150TF          }
2616100TF                       char*restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, uniq_terms[j]);
261750TF                       if (str_val) {
2628100TF                   }
263950TF                               exp_cap *= 2;
50TF                               exp_cap *= 2;
264050TF                               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
2643100TF                           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
264550TF                           snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
2649100TF                       for (size_t l = 0; l < num_levels; l++) Safefree(levels[l]);
2650100TF                       Safefree(levels);
2652100TF                       Safefree(levels); exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = false; p_exp++;
265450TF          } else {
2655100TF                   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = false; p_exp++;
266050TF        Newx(X, n * p, double); Newx(Y, n, double);
266350TF        // --- Listwise Deletion ---
2665100TF                double y_val = evaluate_term(data_hoa, row_hashes, i, lhs);
267150TF                        if (strcmp(exp_terms[j], "Intercept") == 0) {
26720TF                                 row_x[j] = 1.0;
2681100TF                                 if (isnan(row_x[j])) { row_ok = false; break; }
2684100TF                if (!row_ok) { Safefree(row_names[i]); Safefree(row_x); continue; }
2686100TF                for (size_t j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
2687100TF                valid_row_names[valid_n] = row_names[i];
268850TF                valid_n++;
50TF                valid_n++;
2692100TF        if (valid_n <= p) {
269350TF          Safefree(X); Safefree(Y); Safefree(valid_row_names); if (row_hashes) Safefree(row_hashes);
270250TF        for (i = 0; i < p; i++) { beta[i] = 0.0; beta_old[i] = 0.0; }
2703100TF        // Initialize (mustart / etastart equivalent)
2704100TF        double sum_y = 0.0;
270750TF        for (i = 0; i < valid_n; i++) {
2716100TF                   deviance_old += dev;
100TF                   deviance_old += dev;
2717100TF          } else {
2719100TF                   eta[i] = mu[i];
2722100TF        // IRLS Loop
2726100TF                                 double varmu = mu[i] * (1.0 - mu[i]);
272750TF                                 double mu_eta = varmu; // Link derivative for logit
272950TF                                 Z[i] = eta[i] + (Y[i] - mu[i]) / mu_eta;
100TF                                 Z[i] = eta[i] + (Y[i] - mu[i]) / mu_eta;
2735100TF                }
2737100TF                for (i = 0; i < p; i++) { XtWZ[i] = 0.0; for (size_t j = 0; j < p; j++) XtWX[i * p + j] = 0.0; }
273950TF                        double w = W[k], z = Z[k];
100TF                        double w = W[k], z = Z[k];
2741100TF                                 XtWZ[i] += X[k * p + i] * w * z;
274450TF                        }
274550TF                }
2748100TF                        if (aliased[i]) { beta[i] = NAN; } else {
274950TF                                 double sum = 0.0;
2759100TF                                 double linear_pred = 0.0;
100TF                                 double linear_pred = 0.0;
50TF                                 double linear_pred = 0.0;
2764100TF                                     // Boundary enforcement
2767100TF                                     
2771100TF                                     else dev = 2.0 * (Y[i] * log(Y[i] / mu[i]) + (1.0 - Y[i]) * log((1.0 - Y[i]) / (1.0 - mu[i])));
2774100TF                                     mu[i] = eta[i];
100TF                                     mu[i] = eta[i];
2775100TF                                     double res = Y[i] - mu[i];
2776100TF                                     deviance_new += res * res;
277750TF                                 }
2778100TF                        }
2780100TF                        if (!is_binomial || deviance_new <= deviance_old + 1e-7 || !isfinite(deviance_new)) {
2786100TF                }
2787100TF                // Convergence Check
2788100TF                if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) {
278950TF                        converged = true; break;
2797100TF          double w = is_binomial ? (mu[k] * (1.0 - mu[k])) : 1.0;
280050TF                   double xw = X[k * p + i] * w;
2806100TF        double wtdmu = mean_y; // Since weights are 1.0 initially
50TF        double wtdmu = mean_y; // Since weights are 1.0 initially
2807100TF        for (i = 0; i < valid_n; i++) {
2809100TF                   if (Y[i] == 0.0)      null_dev += -2.0 * log(1.0 - wtdmu);
2812100TF          } else {
281350TF                   double diff = Y[i] - wtdmu;
2815100TF          }
2824100TF        // --- Return Structures ---
282950TF                double res = Y[i] - mu[i];
28320TF                        double d_res = 0.0;
28330TF                        if (Y[i] == 0.0)      d_res = sqrt(-2.0 * log(1.0 - mu[i]));
2837100TF                }
2841100TF        }
2842100TF        Safefree(valid_row_names);
2864100TF                }
2866100TF        }
2868100TF        hv_store(res_hv, "aic",            3, newSVnv(aic), 0);
2870100TF        hv_store(res_hv, "converged",      9, newSVuv(converged ? 1 : 0), 0);
2877100TF        hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
288750TF        for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]);
50TF        for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]);
2899100TF
2903100TF    RETVAL
2904100TF
290550TFSV* cor_test(...)
2906100TFCODE:
50TFCODE:
290750TF{
292050TF        for (unsigned short int i = 2; i < items; i += 2) {
50TF        for (unsigned short int i = 2; i < items; i += 2) {
50TF        for (unsigned short int i = 2; i < items; i += 2) {
292150TF          const char *restrict key = SvPV_nolen(ST(i));
50TF          const char *restrict key = SvPV_nolen(ST(i));
50TF          const char *restrict key = SvPV_nolen(ST(i));
292950TF          else croak("cor_test: unknown argument '%s'", key);
2935100TF
293950TF        HV *restrict rhv;
100TF        HV *restrict rhv;
50TF        HV *restrict rhv;
294050TF
100TF
50TF
2943100TF          croak("cor_test: x and y must be array references");
100TF          croak("cor_test: x and y must be array references");
295050TF        if (n_raw != av_len(y_av) + 1) croak("incompatible dimensions");
2956100TF        for (size_t i = 0; i < n_raw; i++) {
2959100TF          
296850TF          }
50TF          }
2982100TF                   mean_x += dx / (i + 1);
2984100TF                   mean_y += dy / (i + 1);
2985100TF                   M2_x += dx * (x[i] - mean_x);
298950TF          estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
0TF          estimate = (M2_x > 0.0 && M2_y > 0.0) ? cov / sqrt(M2_x * M2_y) : 0.0;
299050TF          df = n - 2;
299150TF          statistic = estimate * sqrt(df / (1.0 - estimate * estimate));
2992100TF
299750TF          double q = inverse_normal_cdf(1.0 - alpha/2.0);
299950TF          ci_upper = tanh(z + q * se);
50TF          ci_upper = tanh(z + q * se);
300350TF        } else if (is_kendall) {
0TF        } else if (is_kendall) {
300450TF          int c = 0, d = 0, tie_x = 0, tie_y = 0;
50TF          int c = 0, d = 0, tie_x = 0, tie_y = 0;
300950TF
50TF
301150TF                       else if (sign_x == 0) tie_x++;
30190TF
0TF
30220TF
30240TF          if (!exact_sv || !SvOK(exact_sv)) {
303050TF          if (do_exact && has_ties) do_exact = 0;
3038100TF                   double var_S = n * (n - 1) * (2.0 * n + 5.0) / 18.0;
304750TF                   } else {
50TF                   } else {
3051100TF        } else if (is_spearman) {
3058100TF          double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
305950TF          for (size_t i = 0; i < n; i++) {
50TF          for (size_t i = 0; i < n; i++) {
306450TF                   M2_x += dx * (rank_x[i] - mean_x);
0TF                   M2_x += dx * (rank_x[i] - mean_x);
306550TF                   M2_y += dy * (rank_y[i] - mean_y);
50TF                   M2_y += dy * (rank_y[i] - mean_y);
307050TF          // S = sum of squared rank differences (R's reported statistic)
30750TF          }
3092100TF                   statistic = S_stat;
311450TF          hv_stores(rhv, "parameter", newSVnv(df));
50TF          hv_stores(rhv, "parameter", newSVnv(df));
312150TF        RETVAL = newRV_noinc((SV*)rhv);
3124100TF    RETVAL
312650TFvoid shapiro_test(data)
50TFvoid shapiro_test(data)
312850TFPREINIT:
313650TF        }
50TF        }
3143100TF        // Extract variables and calculate mean (skipping undefined/NaN values)
314650TF          if (elem && SvOK(*elem)) {
315350TF          }
31570TF          Safefree(x);
316350TF        for (size_t i = 0; i < n; i++) {
316450TF          ssq += (x[i] - mean) * (x[i] - mean);
3165100TF        }
317350TF        if (n == 3) {
100TF        if (n == 3) {
3175100TF          double b_val = a_val * (x[2] - x[0]);
3183100TF          Newx(m, n, double);
3187100TF                   sum_m2 += m[i] * m[i];
3197100TF                   }
320350TF                   for (unsigned int i = 2; i < n-2; i++) {
322650TF                       // Horner-form polynomials in n for mu and log(sigma)
322750TF                       double mu     = 0.544  + nn * (-0.39978  + nn * ( 0.025054  - nn * 0.0006714));
323750TF                   double ln_n   = log((double)n);
3247100TF          if (p_val < 0.0) p_val = 0.0;
3249100TF          Safefree(m); m = NULL;  Safefree(a); a = NULL;
50TF          Safefree(m); m = NULL;  Safefree(a); a = NULL;
3252100TF        ret_hash = newHV();
325450TF        hv_stores(ret_hash, "W",         newSVnv(w));
50TF        hv_stores(ret_hash, "W",         newSVnv(w));
3256100TF        hv_stores(ret_hash, "p.value",   newSVnv(p_val));
50TF        hv_stores(ret_hash, "p.value",   newSVnv(p_val));
326350TF                double min_val = 0.0;
3265100TF                bool first = TRUE;
100TF                bool first = TRUE;
3272100TF                                for (size_t j = 0; j < len; j++) {
327350TF                                     SV** restrict tv = av_fetch(av, j, 0);
3284100TF                                 }
3286100TF                                 double val = SvNV(arg);
50TF                                 double val = SvNV(arg);
3289100TF                                     first = FALSE;
329150TF                                 count++;
50TF                                 count++;
3293100TF                                 croak("min: undefined value at argument index %d", (int)i);
100TF                                 croak("min: undefined value at argument index %d", (int)i);
330050TF
3302100TF        PROTOTYPE: @
100TF        PROTOTYPE: @
3309100TF                   SV* restrict arg = ST(i);
331050TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
332550TF                       }
3329100TF                           max_val = val;
3331100TF                       }
100TF                       }
3333100TF                   } else {
3338100TF          RETVAL = max_val;
334350TFCODE:
3352100TF
3355100TF        }
335850TF                // 1. Check if the current argument is a string key for a named parameter
336650TF                        } else if (strEQ(key, "min")) {
337050TF                                continue;
337250TF                                max = SvNV(ST(i+1));
3376100TF                        }
337850TF
339450TF        if (!n_set) {
3395100TF                croak("runif() requires at least the 'n' parameter");
3403100TF        const double range = max - min;
3407100TF                        r = NAN; // R behavior for inverted ranges
3408100TF                } else {
340950TF                        r = min + range * Drand01();
3414100TF}
100TF}
3415100TFOUTPUT:
100TFOUTPUT:
341850TFSV* rbinom(...)
3420100TF        {
3435100TF                   if      (strEQ(key, "n"))      n    = (unsigned int)SvUV(val);
100TF                   if      (strEQ(key, "n"))      n    = (unsigned int)SvUV(val);
3440100TF
344450TF
3448100TF                   for (unsigned int i = 0; i < n; i++) {
345050TF                   }
50TF                   }
3453100TF          RETVAL = newRV_noinc((SV*)result_av);
3454100TF        }
345750TF
346450TF                        croak("hist: first argument must be an array reference");
346750TF                size_t n_raw = av_len(x_av) + 1;
346950TF
347150TF                double *restrict x;
50TF                double *restrict x;
347750TF                        SV**restrict tv = av_fetch(x_av, i, 0);
0TF                        SV**restrict tv = av_fetch(x_av, i, 0);
348150TF                                 if (val < min_val) min_val = val;
348550TF                if (n == 0) {
348650TF                        Safefree(x);
348750TF                        croak("hist: input contains no valid numeric data");
348850TF                }
3492100TF                if (items == 2) {
3505100TF                        if (n_bins == 0 && looks_like_number(ST(1))) {
3507100TF                        }
353550TF                        if (i < n_bins) {
100TF                        if (i < n_bins) {
50TF                        if (i < n_bins) {
354150TF                hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
3544100TF                hv_stores(res_hv, "density", newRV_noinc((SV*)av_density));
3548100TF                Safefree(density); Safefree(counts);
354950TF
355250TF        OUTPUT:
50TF        OUTPUT:
50TF        OUTPUT:
355650TF        CODE:
356050TF                int arg_idx = 0;
3562100TF                /* --- 1. Consume first positional arg as 'x' if it's an array ref --- */
356450TF                         x_sv = ST(arg_idx);
50TF                         x_sv = ST(arg_idx);
356850TF                /* --- 2. Remaining args must be key-value pairs --- */
357950TF                }
50TF                }
50TF                }
3583100TF                size_t n_raw = av_len(x_av) + 1;
358550TF
50TF
358650TF                /* --- Extract valid numeric data & drop NAs --- */
50TF                /* --- Extract valid numeric data & drop NAs --- */
35930TF                                 x[n++] = SvNV(*tv);
3599100TF                }
3602100TF                // --- Parse Probabilities (Default matches R's c(0, .25, .5, .75, 1)) ---
3604100TF                unsigned int n_probs = 5;
3606100TF
362050TF                        Newx(probs, n_probs, double);
3644100TF                        /* Format hash key to exactly match R's naming convention ("25%", "33.3%") */
3646100TF                        double pct = p * 100.0;
50TF                        double pct = p * 100.0;
3649100TF                                 snprintf(key, sizeof(key), "%.0f%%", pct);
365150TF                                 snprintf(key, sizeof(key), "%.1f%%", pct);
50TF                                 snprintf(key, sizeof(key), "%.1f%%", pct);
365350TF
3657100TF                Safefree(x);
3658100TF                Safefree(probs);
3669100TF          double total = 0;
3671100TF        CODE:
50TF        CODE:
3674100TF                        if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
367650TF                                size_t len = av_len(av) + 1;
50TF                                size_t len = av_len(av) + 1;
368450TF                                     }
3692100TF                }
369350TF                if (count == 0) croak("mean needs >= 1 element");
3704100TF          for (size_t i = 0; i < items; i++) {
3706100TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
50TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
3709100TF                       for (size_t j = 0; j < len; j++) {
371150TF                           if (tv && SvOK(*tv)) {
50TF                           if (tv && SvOK(*tv)) {
371950TF                       total += SvNV(arg);
3727100TF        OUTPUT:
3728100TF          RETVAL
374450TF                    if (tv && SvOK(*tv)) {
100TF                    if (tv && SvOK(*tv)) {
50TF                    if (tv && SvOK(*tv)) {
375050TF                    } else {
100TF                    } else {
50TF                    } else {
375650TF                double val = SvNV(arg);
3761100TF                croak("sd: undefined value at argument index %zu", i);
3765100TF        RETVAL = sqrt(M2 / (count - 1));
3766100TF    OUTPUT:
3767100TF        RETVAL
3768100TF
3769100TF
3770100TFdouble var(...)
377150TF        PROTOTYPE: @
3776100TF          /* Single Pass Variance via Welford's Algorithm */
50TF          /* Single Pass Variance via Welford's Algorithm */
50TF          /* Single Pass Variance via Welford's Algorithm */
378050TF                       AV* restrict av = (AV*)SvRV(arg);
3782100TF                       for (size_t j = 0; j < len; j++) {
50TF                       for (size_t j = 0; j < len; j++) {
50TF                       for (size_t j = 0; j < len; j++) {
378550TF                               count++;
100TF                               count++;
3790100TF                           } else {
379250TF                           }
50TF                           }
3798100TF                       mean += delta / count;
50TF                       mean += delta / count;
3800100TF                   } else {
100TF                   } else {
3801100TF                       croak("var: undefined value at argument index %zu", i);
3803100TF          }
100TF          }
3805100TF          RETVAL = M2 / (count - 1);
380750TF          RETVAL
50TF          RETVAL
3813100TF                SV*restrict y_sv = NULL;
3815100TF                bool paired = FALSE, var_equal = FALSE;
381850TF                int arg_idx = 0;
50TF                int arg_idx = 0;
381950TF
50TF
382650TF                // 2. Shift second positional argument as 'y' if it's an array reference
3832100TF                // Ensure the remaining arguments form complete key-value pairs
383350TF                if ((items - arg_idx) % 2 != 0) {
0TF                if ((items - arg_idx) % 2 != 0) {
384250TF                        if      (strEQ(key, "x"))           x_sv        = val;
0TF                        if      (strEQ(key, "x"))           x_sv        = val;
3862100TF                if (conf_level <= 0.0 || conf_level >= 1.0)
3866100TF                HV*restrict results = newHV();
3889100TF                        var_y = M2_y / (ny - 1);
50TF                        var_y = M2_y / (ny - 1);
3895100TF                                     double dx = (dx_ptr && SvOK(*dx_ptr)) ? SvNV(*dx_ptr) : 0.0;
3901100TF                                 }
3903100TF                                 if (var_d == 0.0) croak("t_test: data are essentially constant");
100TF                                 if (var_d == 0.0) croak("t_test: data are essentially constant");
3904100TF                                 cint_est = mean_d;
50TF                                 cint_est = mean_d;
390550TF                                 std_err  = sqrt(var_d / nx);
390950TF                        } else if (var_equal) {
391050TF                                 if (var_x == 0.0 && var_y == 0.0) croak("t_test: data are essentially constant");
3912100TF                                 cint_est = mean_x - mean_y;
391450TF                                 t_stat   = (cint_est - mu) / std_err;
50TF                                 t_stat   = (cint_est - mu) / std_err;
3920100TF                                 cint_est         = mean_x - mean_y;
3921100TF                                 double stderr_x2 = var_x / nx;
3923100TF                                 std_err          = sqrt(stderr_x2 + stderr_y2);
3925100TF                                 df = pow(stderr_x2 + stderr_y2, 2) /
3927100TF                                 hv_store(results, "estimate_x", 10, newSVnv(mean_x), 0);
3929100TF                        }
3930100TF                } else {
3932100TF                        std_err  = sqrt(var_x / nx);
3934100TF                        df       = nx - 1;
3936100TF                }
393750TF                p_val = get_t_pvalue(t_stat, df, alternative);
3939100TF                if (strcmp(alternative, "less") == 0) {
3941100TF                        ci_lower = -INFINITY;
3943100TF                } else if (strcmp(alternative, "greater") == 0) {
394450TF                        t_crit   = qt_tail(df, alpha);
3946100TF                        ci_upper = INFINITY;
3948100TF                        t_crit   = qt_tail(df, alpha / 2.0);
3950100TF                        ci_upper = cint_est + t_crit * std_err;
3952100TF                AV*restrict conf_int = newAV();
3953100TF                av_push(conf_int, newSVnv(ci_lower));
3955100TF                hv_store(results, "statistic", 9, newSVnv(t_stat), 0);
395750TF                hv_store(results, "p_value",   7, newSVnv(p_val),  0);
395850TF                hv_store(results, "conf_int",  8, newRV_noinc((SV*)conf_int), 0);
3961100TF        OUTPUT:
396350TF
3968100TF                }
3972100TF                if (n == 0) {
3977100TF                strncpy(meth, method, 63); meth[63] = '\0';
3979100TF                // Resolve aliases
3984100TF                PVal *restrict arr;
3986100TF                Newx(arr, n, PVal);
3989100TF                for (size_t i = 0; i < n; i++) {
3993100TF                }
3994100TF                // Sort ascending (Stable sort using original index)
4000100TF                                adj[arr[i].orig_idx] = (v < 1.0) ? v : 1.0;
4001100TF                        }
400250TF                } else if (strcmp(meth, "holm") == 0) {
400650TF                                 if (v > cummax) cummax = v;
40070TF                                 adj[arr[i].orig_idx] = (cummax < 1.0) ? cummax : 1.0;
401550TF                        }
4016100TF                } else if (strcmp(meth, "bh") == 0) {
4030100TF                                adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
4032100TF                } else if (strcmp(meth, "hommel") == 0) {
50TF                } else if (strcmp(meth, "hommel") == 0) {
4035100TF                        Newx(q_arr, n, double);
403750TF                        double min_val = n * arr[0].p;
50TF                        double min_val = n * arr[0].p;
403950TF                                double temp = (n * arr[i].p) / (i + 1.0);
4043100TF                        }
404550TF                        for (size_t i = 0; i < n; i++) {
4047100TF                                 q_arr[i] = min_val;
4049100TF                        for (size_t j = n - 1; j >= 2; j--) {
50TF                        for (size_t j = n - 1; j >= 2; j--) {
4052100TF                                 // Calculate q1 = min(j * p[i2] / (2:j))
405450TF                                 for (size_t k = 1; k < i2_len; k++) {
50TF                                 for (size_t k = 1; k < i2_len; k++) {
405850TF                                     }
4064100TF                                 }
4070100TF                                for (size_t i = 0; i < n; i++) {
4077100TF                        for (size_t i = 0; i < n; i++) {
4078100TF                                double v = (pa[i] > arr[i].p) ? pa[i] : arr[i].p;
4079100TF                                if (v > 1.0) v = 1.0;
408450TF                        for (size_t i = 0; i < n; i++) {
50TF                        for (size_t i = 0; i < n; i++) {
408950TF                        croak("Unknown p-value adjustment method: %s", method);
409550TF                }
100TF                }
50TF                }
410050TF        PROTOTYPE: @
50TF        PROTOTYPE: @
410150TF        INIT:
410350TF          double* restrict nums;
410450TF          double median_val = 0.0;
410750TF          for (size_t i = 0; i < items; i++) {
50TF          for (size_t i = 0; i < items; i++) {
410950TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
100TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
50TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
4115100TF                               total_count++;
50TF                               total_count++;
411650TF                           } else {
4120100TF                   } else if (SvOK(arg)) {
412450TF                   }
412850TF          /* Allocate C array now that we know the exact size */
412950TF          Newx(nums, total_count, double);
4131100TF          /* Pass 2: Populate the C array — Safefree before any croak */
413350TF                   SV* restrict arg = ST(i);
50TF                   SV* restrict arg = ST(i);
50TF                   SV* restrict arg = ST(i);
4135100TF                       AV* restrict av = (AV*)SvRV(arg);
413750TF                       for (size_t j = 0; j < len; j++) {
50TF                       for (size_t j = 0; j < len; j++) {
50TF                       for (size_t j = 0; j < len; j++) {
414650TF                   } else if (SvOK(arg)) {
415150TF                   }
50TF                   }
50TF                   }
415550TF          qsort(nums, total_count, sizeof(double), compare_doubles);
4160100TF          }
416250TF          nums = NULL;
50TF          nums = NULL;
50TF          nums = NULL;
416650TF
50TF
416750TFSV* cor(SV* x_sv, SV* y_sv = &PL_sv_undef, const char* method = "pearson")
4168100TF        INIT:
417050TF          if (strcmp(method, "pearson")  != 0 &&
50TF          if (strcmp(method, "pearson")  != 0 &&
50TF          if (strcmp(method, "pearson")  != 0 &&
417750TF          if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV)
4179100TF
418050TF          AV*restrict x_av = (AV*)SvRV(x_sv);
4181100TF          size_t nx   = av_len(x_av) + 1;
418550TF          bool x_is_matrix = 0;
50TF          bool x_is_matrix = 0;
50TF          bool x_is_matrix = 0;
419550TF
50TF
419950TF          bool y_is_matrix = 0;
420150TF                   SV**restrict fp = av_fetch(y_av, 0, 0);
4202100TF                   if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
420350TF                       y_is_matrix = 1;
4204100TF          }
420850TF          if (!x_is_matrix && !y_is_matrix) {
50TF          if (!x_is_matrix && !y_is_matrix) {
50TF          if (!x_is_matrix && !y_is_matrix) {
421650TF
422350TF
4224100TF                       for (size_t i = 0; i < nx; i++) {
422850TF                       for (size_t i = 0; i < ny; i++) {
42310TF                       }
42320TF
42330TF                       double r = compute_cor(xd, yd, nx, method);
42350TF                       RETVAL = newSVnv(r);
42370TF          } else {//Branch 2: x is a matrix (or y is a matrix)  â†’  AoA result
42440TF                   if (!xr0 || !SvROK(*xr0) || SvTYPE(SvRV(*xr0)) != SVt_PVAV)
42450TF                       croak("cor: each row of x must be an ARRAY reference");
42480TF                   if (ncols_x == 0) croak("cor: x matrix has zero columns");
4252100TF                   // PRE-VALIDATION PASS: Ensure all rows are arrays to prevent memory leaks on croak
4253100TF                   for (size_t i = 0; i < nrows; i++) {
4257100TF                   }
4261100TF               for (size_t i = 0; i < nrows; i++) {
426350TF                           if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
4264100TF                               croak("cor: y row %lu is not an array ref", i);
428050TF                   }
4282100TF                   // -- resolve y: separate matrix or re-use x (symmetric)
100TF                   // -- resolve y: separate matrix or re-use x (symmetric)
428750TF                   // 1 = cor(X) — result is symmetric
428950TF                       // cross-correlation: X (nrows × p) vs Y (nrows × q)
429450TF                       Newx(col_y, ncols_y, double*);
50TF                       Newx(col_y, ncols_y, double*);
100TF                       Newx(col_y, ncols_y, double*);
429650TF                           Newx(col_y[j], nrows, double);
50TF                           Newx(col_y[j], nrows, double);
50TF                           Newx(col_y[j], nrows, double);
50TF                           Newx(col_y[j], nrows, double);
42980TF                               SV**restrict  rv = av_fetch(y_av, i, 0);
43000TF                               SV**restrict cv  = av_fetch(row, j, 0);
4309100TF                   if (nrows < 2)
431150TF                   // -- build cache for symmetric case: compute upper triangle, store results, mirror to lower triangle
431550TF                   AV **restrict rows_out;
50TF                   AV **restrict rows_out;
50TF                   AV **restrict rows_out;
431750TF                   for (size_t i = 0; i < ncols_x; i++) {
50TF                   for (size_t i = 0; i < ncols_x; i++) {
50TF                   for (size_t i = 0; i < ncols_x; i++) {
50TF                   for (size_t i = 0; i < ncols_x; i++) {
43190TF                       av_extend(rows_out[i], ncols_y - 1);
43210TF                   if (symmetric) {
43220TF                       /* Upper triangle + diagonal, then mirror. r_cache[i][j] (j >= i) holds the computed value. */
4333100TF                               r_cache[j][i] = r; // symmetry
4335100TF                       }
50TF                       }
433750TF                       for (size_t i = 0; i < ncols_x; i++)
433950TF                               av_store(rows_out[i], j, newSVnv(r_cache[i][j]));
50TF                               av_store(rows_out[i], j, newSVnv(r_cache[i][j]));
50TF                               av_store(rows_out[i], j, newSVnv(r_cache[i][j]));
4345100TF                       for (size_t i = 0; i < ncols_x; i++)
435550TF                   Safefree(col_x); col_x = NULL;
50TF                   Safefree(col_x); col_x = NULL;
4361100TF          }
4367100TF        PPCODE:
437050TF                double center_val = 0.0, scale_val = 1.0;
4372100TF                // 1. Parse Options Hash (if it exists as the last argument)
437450TF                        SV*restrict last_arg = ST(items - 1);
50TF                        SV*restrict last_arg = ST(items - 1);
437750TF                                 HV*restrict opt_hv = (HV*)SvRV(last_arg);
50TF                                 HV*restrict opt_hv = (HV*)SvRV(last_arg);
438450TF                                     } else {
438750TF                                         if (strcasecmp(str, "mean") == 0 || strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) {
438850TF                                             do_center_mean = true;
4394100TF                                             do_center_mean = true;
4401100TF                                 SV**restrict scale_sv = hv_fetch(opt_hv, "scale", 5, 0);
440350TF                                     SV*restrict val_sv = *scale_sv;
441050TF                                         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
4419100TF                                         }
442150TF                                 }
0TF                                 }
44240TF                // 2. Detect if the input is a Matrix (Array of Arrays)
44260TF                if (data_items == 1) {
0TF                if (data_items == 1) {
442850TF                        if (SvROK(first_arg) && SvTYPE(SvRV(first_arg)) == SVt_PVAV) {
443250TF                                     if (first_elem && SvROK(*first_elem) && SvTYPE(SvRV(*first_elem)) == SVt_PVAV) {
443350TF                                         is_matrix = true;
4434100TF                                     }
443650TF                        }
0TF                        }
44390TF                        //=========================================================
44410TF                        //=========================================================
0TF                        //=========================================================
444650TF                        ncol = av_len((AV*)SvRV(*first_row)) + 1;
4452100TF                        av_extend(result_av, nrow - 1);
4454100TF                        for (size_t r = 0; r < nrow; r++) {
4455100TF                                 row_ptrs[r] = newAV();
4460100TF                        for (size_t c = 0; c < ncol; c++) {
446650TF                                     SV**restrict row_sv = av_fetch(mat_av, r, 0);
4467100TF                                     if (row_sv && SvROK(*row_sv)) {
446950TF                                         SV**restrict cell_sv = av_fetch(row_av, c, 0);
447950TF                                 // Calculate Standard Deviation for this specific column if needed
4486100TF                                     double sum_sq = 0.0;
4489100TF                                         sum_sq += diff * diff;
4491100TF                                     col_scale = sqrt(sum_sq / (nrow - 1));
449450TF                                 for (size_t r = 0; r < nrow; r++) {
44970TF                                     av_store(row_ptrs[r], c, newSVnv(final_val));
450450TF                        PUSHs(sv_2mortal(newRV_noinc((SV*)result_av)));
100TF                        PUSHs(sv_2mortal(newRV_noinc((SV*)result_av)));
50TF                        PUSHs(sv_2mortal(newRV_noinc((SV*)result_av)));
450850TF                        // ======================================
4509100TF                        size_t total_count = 0, k = 0;
451350TF                                SV*restrict arg = ST(i);
0TF                                SV*restrict arg = ST(i);
451650TF                                        size_t len = av_len(av) + 1;
100TF                                        size_t len = av_len(av) + 1;
451850TF                                                SV**restrict tv = av_fetch(av, j, 0);
0TF                                                SV**restrict tv = av_fetch(av, j, 0);
4522100TF                                        total_count++;
50TF                                        total_count++;
4530100TF                                     AV*restrict av = (AV*)SvRV(arg);
4537100TF                                         }
454050TF                                     double val = SvNV(arg);
454150TF                                     nums[k++] = val; sum += val;
458450TF                   nrow_set = TRUE;
4586100TF                   ncol = (size_t)SvUV(val);
4589100TF                   byrow = SvTRUE(val);
459050TF          } else {
4593100TF        }
4594100TF        // Validate data input
100TF        // Validate data input
460050TF        if (data_len == 0) {
460250TF        }
460450TF        if (!nrow_set && !ncol_set) {
460650TF          ncol = 1;
100TF          ncol = 1;
460950TF        } else if (!nrow_set && ncol_set) {
4610100TF          nrow = (data_len + ncol - 1) / ncol;
461550TF        }
50TF        }
461750TF        AV*restrict result_av = newAV();
50TF        AV*restrict result_av = newAV();
4619100TF        size_t r, c;// Use unsigned types for counters to prevent negative indexing
46270TF        size_t total_cells = nrow * ncol;
46290TF          // Vector recycling logic
46300TF          SV**restrict fetched = av_fetch(data_av, i % data_len, 0);
46310TF          SV*restrict val = fetched ? newSVsv(*fetched) : newSV(0);
46330TF                   r = i / ncol;
0TF                   r = i / ncol;
0TF                   r = i / ncol;
46380TF          }
4649100TF        const char *restrict formula  = NULL;
100TF        const char *restrict formula  = NULL;
50TF        const char *restrict formula  = NULL;
4653100TF
4654100TF        char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
465550TF        bool *restrict is_dummy = NULL;
4667100TF        bool *restrict aliased = NULL;
466950TF        int final_rank = 0, df_res = 0;
0TF        int final_rank = 0, df_res = 0;
46710TF        AV *restrict terms_av;
0TF        AV *restrict terms_av;
0TF        AV *restrict terms_av;
0TF        AV *restrict terms_av;
4675100TF        if (items % 2 != 0) croak("Usage: lm(formula => 'mpg ~ wt * hp', data => \\%%mtcars)");
50TF        if (items % 2 != 0) croak("Usage: lm(formula => 'mpg ~ wt * hp', data => \\%%mtcars)");
467650TF
0TF
0TF
4682100TF          else croak("lm: unknown argument '%s'", key);
50TF          else croak("lm: unknown argument '%s'", key);
46830TF        }
0TF        }
0TF        }
4689100TF        // ========================================================================
50TF        // ========================================================================
0TF        // ========================================================================
4695100TF          if (entry) {
50TF          if (entry) {
0TF          if (entry) {
4699100TF                       n = av_len((AV*)SvRV(val)) + 1;
50TF                       n = av_len((AV*)SvRV(val)) + 1;
47000TF                       Newx(row_names, n, char*);
0TF                       Newx(row_names, n, char*);
0TF                       Newx(row_names, n, char*);
4705100TF                       }
470650TF                   } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
0TF                   } else if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVHV) {
470750TF                       n = hv_iterinit(hv);
0TF                       n = hv_iterinit(hv);
471650TF                   } else croak("lm: Hash values must be ArrayRefs (HoA) or HashRefs (HoH)");
471850TF        } else if (SvTYPE(ref) == SVt_PVAV) {
472050TF          Newx(row_names, n, char*);
50TF          Newx(row_names, n, char*);
4727100TF                       row_names[i] = savepv(buf);
4728100TF                   } else {
4730100TF                       Safefree(row_names); Safefree(row_hashes);
473250TF                   }
50TF                   }
4734100TF        } else croak("lm: Data must be an Array or Hash reference");
473650TF        // ========================================================================
4737100TF        // PHASE 2: Formula Parsing & `.` Expansion
474750TF          croak("lm: invalid formula, missing '~'");
4748100TF        }
4760100TF                   if (p_idx[0] == 'I' && p_idx[1] == '(') {
476250TF                       while (*p_idx) { if (*p_idx == '(') depth++; else if (*p_idx == ')') { depth--; if (depth == 0) { p_idx++; break; } } p_idx++; }
4764100TF                   }
476550TF                   // Match bare -1
4770100TF                       continue; // re-examine same position
477550TF                       has_intercept = false;
0TF                       has_intercept = false;
477750TF                       continue;
50TF                       continue;
478550TF                   // Match bare 0 (entire rhs)
0TF                   // Match bare 0 (entire rhs)
4792100TF                       memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1);
479450TF                   }
100TF                   }
479550TF                   // Strip leading bare 1 or 1+
4802100TF        }
480350TF
4808100TF                   memmove(p_idx, p_idx + 1, strlen(p_idx + 1) + 1);
4811100TF          if (len_rhs > 0 && rhs[len_rhs - 1] == '+') rhs[len_rhs - 1] = '\0';
4815100TF        char rhs_expanded[2048] = "";
481750TF        chunk = strtok(rhs, "+");
4819100TF          if (strcmp(chunk, ".") == 0) {
100TF          if (strcmp(chunk, ".") == 0) {
4820100TF                   AV *cols = get_all_columns(data_hoa, row_hashes, n);
482150TF                   for (size_t c = 0; c <= (size_t)av_len(cols); c++) {
482750TF                               if (rhs_len + slen + 2 < sizeof(rhs_expanded)) {
4828100TF                                   if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
4829100TF                                   strcat(rhs_expanded, col_name);
4830100TF                                   rhs_len += slen;
4831100TF                               }
483250TF                           }
4845100TF        }
485650TF                   if (num_terms >= term_cap - 3) {
50TF                   if (num_terms >= term_cap - 3) {
485750TF                       term_cap *= 2;
4862100TF                       *star = '\0';
4864100TF                       char *restrict right = star + 1;
4868100TF                       if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
4869100TF                       terms[num_terms++] = savepv(left);
4871100TF                       size_t inter_len = strlen(left) + strlen(right) + 2;
487350TF                       snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
4874100TF                   } else {
487950TF                   chunk = strtok(NULL, "+");
488250TF
4885100TF          for (j = 0; j < num_uniq; j++) { if (strcmp(terms[i], uniq_terms[j]) == 0) { found = true; break; } }
4892100TF        // ========================================================================
4893100TF        for (j = 0; j < p; j++) {
4894100TF          if (p_exp + 32 >= exp_cap) {
4895100TF                   exp_cap *= 2;
489750TF                   Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
490150TF          }
4909100TF                           bool found = false;
4910100TF                           for (l = 0; l < num_levels; l++) { if (strcmp(levels[l], str_val) == 0) { found = true; break; } }
4912100TF                               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
4916100TF                       }
4918100TF                   if (num_levels > 0) {
4924100TF                               exp_cap *= 2;
4925100TF                               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
4928100TF                           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
100TF                           size_t t_len = strlen(uniq_terms[j]) + strlen(levels[l]) + 1;
4943100TF                   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = false; p_exp++;
4946100TF        p = p_exp;
4948100TF        Newx(valid_row_names, n, char*);
100TF        Newx(valid_row_names, n, char*);
4951100TF        // PHASE 4: Matrix Construction & Listwise Deletion
496050TF                   if (strcmp(exp_terms[j], "Intercept") == 0) {
496650TF                           Safefree(str_val);
50TF                           Safefree(str_val);
496950TF                       row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
50TF                       row_x[j] = evaluate_term(data_hoa, row_hashes, i, exp_terms[j]);
49720TF          }
49760TF          for (j = 0; j < p; j++) X[valid_n * p + j] = row_x[j];
4980100TF        }
4984100TF          for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
499150TF          Safefree(X); Safefree(Y); Safefree(valid_row_names);
0TF          Safefree(X); Safefree(Y); Safefree(valid_row_names);
501150TF        }
5021100TF          }
5022100TF        }
5023100TF
5025100TF        // PHASE 6: Metrics & Cleanup
5030100TF        df_res = (int)valid_n - final_rank;
504450TF          hv_store(fitted_hv, valid_row_names[i], strlen(valid_row_names[i]), newSVnv(y_hat), 0);
50450TF          hv_store(resid_hv,  valid_row_names[i], strlen(valid_row_names[i]), newSVnv(res),   0);
50460TF          Safefree(valid_row_names[i]);
5054100TF        double r_squared = 0.0, adj_r_squared = 0.0, f_stat = NAN, f_pvalue = NAN;
50TF        double r_squared = 0.0, adj_r_squared = 0.0, f_stat = NAN, f_pvalue = NAN;
100TF        double r_squared = 0.0, adj_r_squared = 0.0, f_stat = NAN, f_pvalue = NAN;
50TF        double r_squared = 0.0, adj_r_squared = 0.0, f_stat = NAN, f_pvalue = NAN;
506250TF                   f_pvalue = 1.0 - pf(f_stat, (double)numdf, (double)df_res);
506550TF                   f_pvalue = 0.0;
5066100TF          }
5076100TF                   hv_store(row_hv, "Estimate",   8,  newSVpv("NaN", 0), 0);
508350TF                   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
50TF                   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
0TF                   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
0TF                   double p_val = get_t_pvalue(t_val, df_res, "two.sided");
508950TF          hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
5093100TF        hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
5097100TF        hv_store(res_hv, "rss",            3, newSVnv(rss),                0);
5098100TF        hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv),0);
509950TF        hv_store(res_hv, "terms",          5, newRV_noinc((SV*)terms_av),  0);
5103100TF          AV *fstat_av = newAV();
510650TF          av_push(fstat_av, newSViv(df_res));
5109100TF        }
5116100TF          if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
50TF          if (is_dummy[j]) { Safefree(dummy_base[j]); Safefree(dummy_level[j]); }
5121100TF        if (row_hashes) Safefree(row_hashes);
515650TF                EXTEND(SP, n_elements);
516250TF
516450TF    CODE:
516650TF        // Auto-seed the PRNG if the Perl script hasn't done so yet
516850TF
50TF
517150TF        int arg_start = 0;
5172100TF
51760TF            arg_start = 1; // Start parsing named arguments from the second element
0TF            arg_start = 1; // Start parsing named arguments from the second element
51780TF
0TF
51800TF        if ((items - arg_start) % 2 != 0) {
51880TF            if      (strEQ(key, "n"))    n    = (unsigned int)SvUV(val);
51910TF            else croak("rnorm: unknown argument '%s'", key);
51920TF        }
51930TF
51950TF
0TF
0TF
52010TF                 double u, v, s;
5212100TF                 if (i < n) {
100TF                 if (i < n) {
50TF                 if (i < n) {
5216100TF        }
5217100TF        RETVAL = newRV_noinc((SV*)result_av);
521850TF    }
522650TF        {
522750TF          const char *restrict formula = SvPV_nolen(formula_sv);
522850TF          char f_cpy[512];
522950TF          char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
0TF          char *restrict src, *restrict dst, *restrict tilde, *restrict lhs, *restrict rhs, *restrict chunk;
523050TF
523150TF          char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL, **restrict parent_term = NULL;
0TF          char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL, **restrict parent_term = NULL;
523250TF          bool *restrict is_dummy = NULL, *is_interact = NULL;
0TF          bool *restrict is_dummy = NULL, *is_interact = NULL;
523450TF          int *restrict term_map = NULL, *restrict left_idx = NULL, *restrict right_idx = NULL;
523550TF          unsigned int term_cap = 64, exp_cap = 64, num_terms = 0, num_uniq = 0, p = 0, p_exp = 0;
523750TF          bool has_intercept = true;
50TF          bool has_intercept = true;
5242100TF          SV *restrict ref = NULL;
5243100TF          HE *restrict entry;
5245100TF          double *restrict Y = NULL;
524750TF          char **restrict term_base_level = NULL;  /* reference level for each uniq_term (NULL if not categorical) */
50TF          char **restrict term_base_level = NULL;  /* reference level for each uniq_term (NULL if not categorical) */
5249100TF          // ========================================================================
525150TF          // ========================================================================
5252100TF          ref = SvRV(data_sv);
526250TF                            Newx(row_names, n, char*);
5263100TF                            for(i = 0; i < n; i++) {
527950TF          } else if (SvTYPE(ref) == SVt_PVAV) {
528150TF                   n = av_len(av) + 1;
5283100TF                   Newx(row_hashes, n, HV*);
528450TF                   for (i = 0; i < n; i++) {
5289100TF                            snprintf(buf, sizeof(buf), "%lu", (unsigned long)(i + 1));
529450TF                            croak("aov: Array values must be HashRefs (AoH)");
0TF                            croak("aov: Array values must be HashRefs (AoH)");
529550TF                        }
0TF                        }
530350TF          while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
0TF          while (*src && (dst - f_cpy < 511)) { if (!isspace(*src)) { *dst++ = *src; } src++; }
5310100TF                   croak("aov: invalid formula, missing '~'");
5312100TF          *tilde = '\0';
531350TF          lhs = f_cpy;
531550TF
5322100TF          if (rhs[0] == '1' && rhs[1] == '\0')        { rhs[0] = '\0'; }
5323100TF          else if (rhs[0] == '1' && rhs[1] == '+')    { memmove(rhs, rhs + 2, strlen(rhs + 2) + 1); }
5331100TF          size_t rhs_len = 0;
5341100TF                                    size_t slen = strlen(col_name);
5349100TF                        }
5350100TF                        SvREFCNT_dec(cols);
5351100TF                   } else {
5354100TF                            if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
50TF                            if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
5358100TF                   }
5359100TF                   chunk = strtok(NULL, "+");
536050TF          }
5381100TF                            *star = '\0';
5385100TF                            if (c_l && strncmp(left, "I(", 2) != 0) *c_l = '\0';
538750TF                            terms[num_terms++] = savepv(left);
5389100TF                            size_t inter_len = strlen(left) + strlen(right) + 2;
5390100TF                            terms[num_terms] = (char*)safemalloc(inter_len);
5392100TF                        } else {
539350TF                            char *restrict c_chunk = strchr(chunk, '^');
540050TF
5401100TF          for (i = 0; i < num_terms; i++) {
5402100TF                   bool found = false;
5403100TF                   for (size_t k = 0; k < num_uniq; k++) {
5408100TF          p = num_uniq;
540950TF
5426100TF
5446100TF                             if (strcmp(parent_term[e], left) == 0) l_indices[l_count++] = e;
544750TF                             if (strcmp(parent_term[e], right) == 0) r_indices[r_count++] = e;
5451100TF                             Safefree(l_indices); Safefree(r_indices);
545350TF                         } else {
5456100TF                                     if (p_exp >= exp_cap) {
5457100TF                                         exp_cap *= 2;
5459100TF                                         Renew(is_dummy, exp_cap, bool); Renew(is_interact, exp_cap, bool);
5461100TF                                         Renew(term_map, exp_cap, int); Renew(left_idx, exp_cap, int); Renew(right_idx, exp_cap, int);
546350TF                                     size_t t_len = strlen(exp_terms[l_indices[li]]) + strlen(exp_terms[r_indices[ri]]) + 2;
5464100TF                                     exp_terms[p_exp] = (char*)safemalloc(t_len);
546950TF                                     right_idx[p_exp] = r_indices[ri];
547250TF                                 }
5475100TF                         Safefree(l_indices); Safefree(r_indices);
5481100TF                             for (i = 0; i < n; i++) {
5483100TF                                 if (str_val) {
5484100TF                                     bool found = false;
5485100TF                                     for (size_t l = 0; l < num_levels; l++) {
548750TF                                     }
5493100TF                                 }
549550TF                             
5509100TF                                 for (size_t l = 1; l < num_levels; l++) {
5510100TF                                     if (p_exp >= exp_cap) {
5511100TF                                         exp_cap *= 2;
5518100TF                                     exp_terms[p_exp] = (char*)safemalloc(t_len);
5519100TF                                     snprintf(exp_terms[p_exp], t_len, "%s%s", uniq_terms[j], levels[l]);
5522100TF                                     dummy_base[p_exp] = savepv(uniq_terms[j]);
552650TF                                 }
5529100TF                             } else {
5530100TF                                 Safefree(levels);
5534100TF                                 term_map[p_exp] = j;
553950TF                             parent_term[p_exp] = savepv(uniq_terms[j]);
100TF                             parent_term[p_exp] = savepv(uniq_terms[j]);
5557100TF                   for (j = 0; j < p_exp; j++) {
5558100TF                         if (strcmp(exp_terms[j], "Intercept") == 0) {
5559100TF                             row_x[j] = 1.0;
5561100TF                             row_x[j] = row_x[left_idx[j]] * row_x[right_idx[j]];
5568100TF                         } else {
557150TF                         }
5582100TF          if (valid_n <= p_exp) {
5589100TF                   }
559050TF                   Safefree(exp_terms); Safefree(parent_term);
559350TF                   Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
50TF                   Safefree(term_map); Safefree(left_idx); Safefree(right_idx);
559550TF                   Safefree(X_mat); Safefree(Y);
560050TF                   /* ------------ */
5604100TF          // PHASE 5: Math & Output Formatting
560650TF          bool *restrict aliased_qr = (bool*)safemalloc(p_exp * sizeof(bool));
560950TF          double *restrict term_ss;
50TF          double *restrict term_ss;
50TF          double *restrict term_ss;
50TF          double *restrict term_ss;
561650TF                   if (aliased_qr[i]) continue;
50TF                   if (aliased_qr[i]) continue;
561750TF                   int t_idx = term_map[i];
50TF                   int t_idx = term_map[i];
561850TF                   size_t r_k = rank_map[i];
50TF                   size_t r_k = rank_map[i];
561950TF                   term_ss[t_idx] += Y[r_k] * Y[r_k];
50TF                   term_ss[t_idx] += Y[r_k] * Y[r_k];
562350TF          for (i = 0; i < p_exp; i++) {
562650TF          double rss_prev = 0.0;
562950TF          }
50TF          }
5632100TF
5633100TF          HV*restrict ret_hash = newHV();
563650TF                   HV*restrict term_stats = newHV();
50TF                   HV*restrict term_stats = newHV();
563750TF                   double ss = term_ss[j];
50TF                   double ss = term_ss[j];
564250TF                   hv_stores(term_stats, "Sum Sq", newSVnv(ss));
50TF                   hv_stores(term_stats, "Sum Sq", newSVnv(ss));
5647100TF                         hv_stores(term_stats, "Pr(>F)", newSVnv(1.0 - pf(f_val, (double)df, (double)res_df)));
5648100TF                   } else {
5653100TF          }
5654100TF
565550TF          HV*restrict res_stats = newHV();
50TF          HV*restrict res_stats = newHV();
565650TF          hv_stores(res_stats, "Df", newSViv(res_df));
50TF          hv_stores(res_stats, "Df", newSViv(res_df));
565750TF          hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
50TF          hv_stores(res_stats, "Sum Sq", newSVnv(rss_prev));
565850TF          hv_stores(res_stats, "Mean Sq", newSVnv(ms_res));
50TF          hv_stores(res_stats, "Mean Sq", newSVnv(ms_res));
570050TF          for (i = 0; i < n; i++) Safefree(X_mat[i]);
5701100TF          Safefree(X_mat); Safefree(Y);
5705100TF        }
5706100TFOUTPUT:
5707100TF    RETVAL
570850TF
100TF
5709100TFPROTOTYPES: DISABLE
5710100TF
571150TFSV* fisher_test(...)
57120TFCODE:
57130TF{
5717100TF        double conf_level = 0.95;
50TF        double conf_level = 0.95;
571850TF        const char*restrict alternative = "two.sided";
50TF        const char*restrict alternative = "two.sided";
5719100TF
50TF
572050TF        // Parse named arguments
50TF        // Parse named arguments
5721100TF        for (unsigned short int i = 1; i < items; i += 2) {
50TF        for (unsigned short int i = 1; i < items; i += 2) {
5724100TF                SV*restrict val = ST(i + 1);
572550TF                if (strEQ(key, "conf_level") || strEQ(key, "conf.level")) {
5726100TF                        conf_level = SvNV(val);
572750TF                } else if (strEQ(key, "alternative")) {
572850TF                        alternative = SvPV_nolen(val);
573050TF        }
5734100TF        size_t a = 0, b = 0, c = 0, d = 0;
573550TF        // Extract Data
573650TF        if (SvTYPE(deref) == SVt_PVAV) {
50TF        if (SvTYPE(deref) == SVt_PVAV) {
5737100TF          AV*restrict outer = (AV*)deref;
50TF          AV*restrict outer = (AV*)deref;
5738100TF          if (av_len(outer) != 1) croak("Outer array must have exactly 2 rows");
5739100TF          SV**restrict row1_ptr = av_fetch(outer, 0, 0);
100TF          SV**restrict row1_ptr = av_fetch(outer, 0, 0);
5740100TF          SV**restrict row2_ptr = av_fetch(outer, 1, 0);
50TF          SV**restrict row2_ptr = av_fetch(outer, 1, 0);
50TF          SV**restrict row2_ptr = av_fetch(outer, 1, 0);
5741100TF          if (row1_ptr && row2_ptr && SvROK(*row1_ptr) && SvROK(*row2_ptr)) {
50TF          if (row1_ptr && row2_ptr && SvROK(*row1_ptr) && SvROK(*row2_ptr)) {
5742100TF                   AV*restrict row1 = (AV*)SvRV(*row1_ptr);
574450TF                   SV**restrict a_ptr = av_fetch(row1, 0, 0);
574650TF                   SV**restrict c_ptr = av_fetch(row2, 0, 0);
0TF                   SV**restrict c_ptr = av_fetch(row2, 0, 0);
5747100TF                   SV**restrict d_ptr = av_fetch(row2, 1, 0);
5749100TF                   b = (b_ptr && SvOK(*b_ptr)) ? SvIV(*b_ptr) : 0;
57530TF                  croak("Invalid 2D Array structure");
57550TF        } else if (SvTYPE(deref) == SVt_PVHV) {
57570TF                HV*restrict outer = (HV*)deref;
57610TF                if (!he1 || !he2) croak("Invalid outer hash");
57630TF                const char*restrict k2 = SvPV_nolen(hv_iterkeysv(he2));
0TF                const char*restrict k2 = SvPV_nolen(hv_iterkeysv(he2));
57640TF                HE*restrict row1_he = (strcmp(k1, k2) < 0) ? he1 : he2;
57660TF                SV*restrict row1_sv = hv_iterval(outer, row1_he);
57700TF                        croak("Inner elements must be hashes");
57720TF                HV*restrict in1 = (HV*)SvRV(row1_sv);
57740TF                if (hv_iterinit(in1) != 2 || hv_iterinit(in2) != 2) croak("Inner hashes must have exactly 2 keys");
5786100TF                HE*restrict in2_c2 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he2 : in2_he1;
100TF                HE*restrict in2_c2 = (strcmp(in2_k1, in2_k2) < 0) ? in2_he2 : in2_he1;
5788100TF                b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
100TF                b = (hv_iterval(in1, in1_c2) && SvOK(hv_iterval(in1, in1_c2))) ? SvIV(hv_iterval(in1, in1_c2)) : 0;
5789100TF                c = (hv_iterval(in2, in2_c1) && SvOK(hv_iterval(in2, in2_c1))) ? SvIV(hv_iterval(in2, in2_c1)) : 0;
580350TF        hv_stores(ret_hash, "alternative", newSVpv(alternative, 0));
100TF        hv_stores(ret_hash, "alternative", newSVpv(alternative, 0));
5805100TF        av_push(ci_array, newSVnv(ci_low));
580750TF        hv_stores(ret_hash, "conf_int", newRV_noinc((SV*)ci_array));
5811100TF        hv_stores(ret_hash, "p_value", newSVnv(p_val));
50TF        hv_stores(ret_hash, "p_value", newSVnv(p_val));
5812100TF        // Return the HashRef
581350TF        RETVAL = newRV_noinc((SV*)ret_hash);
5817100TF
5820100TF{
582150TF    SV* sv_n = NULL;
58220TF    SV* sv_delta = NULL;
5826100TF
50TF
50TF
5838100TF        else if (strEQ(key, "delta"))       sv_delta = val;
583950TF        else if (strEQ(key, "sd"))          sv_sd = val;
50TF        else if (strEQ(key, "sd"))          sv_sd = val;
5846100TF        else croak("power_t_test: unknown argument '%s'", key);
584850TF
50TF
585250TF    bool is_null_sd = (sv_sd && !SvOK(sv_sd));
5859100TF    if (is_null_sd) missing_count++;
5862100TF    if (missing_count != 1) {
586450TF    }
50TF    }
50TF    }
587850TF        while (p_body(high, delta, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
50TF        while (p_body(high, delta, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
50TF        while (p_body(high, delta, sd, sig_level, tsample, tside, strict) < power && high < 1e12) high *= 2.0;
588050TF            double mid = low + (high - low) / 2.0;
50TF            double mid = low + (high - low) / 2.0;
50TF            double mid = low + (high - low) / 2.0;
588750TF        while (high - low > tol) {
588850TF            double mid = low + (high - low) / 2.0;
5896100TF        while (high - low > tol) {
589950TF            else high = mid;
50TF            else high = mid;
50TF            else high = mid;
590050TF        }
50TF        }
5905100TF            double mid = low + (high - low) / 2.0;
592350TF}
592450TFOUTPUT:
5933100TF    // 1. Shift positional arguments
5941100TF        }
594250TF    }
595050TF        const char *restrict key = SvPV_nolen(ST(arg_idx));