Branch Coverage

File:LikeR.xs
Coverage:66.8%

line%coveragebranch
3050TF * PERL_NO_GET_CONTEXT is therefore not a concern here.
40100TF        z = (z ^ (z >> 27)) * UINT64_C(0x94d049bb133111eb);
52100TF          s = (uint64_t)time(NULL) ^ ((uint64_t)getpid() << 32);
73100TF                        (void)seedDrand01((Rand_seed_t)Perl_seed(aTHX)); \
79100TF//   Helpers for Random Number Generation
81100TF#ifndef M_PI
50TF#ifndef M_PI
83100TF#endif
87100TF        if (df <= 0.0) return 0.0;
93100TF        double root_half = 0.70710678118654752440; // 1 / sqrt(2)
94100TF
97100TF                double w = u / (1.0 - u);
98100TF                // Scaled Chi-distribution log-density
111100TF// Ranking helper with tie adjustment (matches R's tie handling)
113100TFstatic int compare_rank(const void *restrict a, const void *restrict b) {
117100TF
124100TF        for (size_t i = 0; i < n; i++) {
126100TF                items[i].idx = i;
127100TF        }
135100TF                i = j;
143100TFstatic size_t generate_binomial(const size_t size, const double prob) {
14850TF        for (size_t i = 0; i < size; i++) {
0TF        for (size_t i = 0; i < size; i++) {
14950TF                if (Drand01() <= prob) successes++;
150100TF        }
15350TF// Helper: log combination
156100TF}
158100TF// Log-space tails for non-central hypergeometric
161100TF
168100TF        for(size_t k = 0; k <= max_x - min_x; ++k) {
170100TF        }
179100TF        }
180100TF}
18150TF
18350TFstatic void calculate_exact_stats(size_t a, size_t b, size_t c, size_t d, double conf_level, const char*restrict alt, double *restrict mle_or, double *restrict ci_low, double *restrict ci_high) {
188100TF
189100TF        bool is_less = (strcmp(alt, "less") == 0);
191100TF
198100TF        // MLE
199100TF        if (a == min_x && a == max_x) *mle_or = 1.0;
200100TF        else if (a == min_x) *mle_or = 0.0;
20250TF        else {
207100TF                   for(size_t k = 0; k <= max_x - min_x; ++k) {
208100TF                       double d_val = logdc[k] + log_mid * (min_x + k);
210100TF                   }
221100TF          }
226100TF        *ci_high = INFINITY;
232100TF                   double log_low = -100.0, log_high = 100.0, best = 1.0, best_err = 1e9, lt, ut;
233100TF                   for (unsigned short int i = 0; i < 1000; i++) {
234100TF                       double log_mid = 0.5 * (log_low + log_high);
235100TF                       double mid = exp(log_mid);
239100TF                       if (ut > target_alpha) log_high = log_mid;
241100TF                       if (log_high - log_low < 1e-15) break;
24650TF
261100TF                        }
266100TF}
269100TFstatic double exact_p_value(size_t a, size_t b, size_t c, size_t d, const char* alt) {
50TFstatic double exact_p_value(size_t a, size_t b, size_t c, size_t d, const char* alt) {
272100TF        size_t max_x = (r1 < c1) ? r1 : c1;
281100TF
282100TF        if (strcmp(alt, "less") == 0) {
283100TF          for(size_t x = min_x; x <= a; ++x) p_val += exp(logdc[x - min_x]);
100TF          for(size_t x = min_x; x <= a; ++x) p_val += exp(logdc[x - min_x]);
286100TF        } else {
299100TF * Helpers for lm Linear Regression: OLS Matrix Math & Formula Parsing
30150TF
50TF
50TF
30550TF */
30750TF        int rank = 0;
50TF        int rank = 0;
50TF        int rank = 0;
31250TF                aliased[k] = FALSE;
100TF                aliased[k] = FALSE;
313100TF                orig_diag[k] = A[k * n + k];
32250TF                        for (size_t i = 0; i < n; i++) {
325100TF                        }
3280TF                rank++;
0TF                rank++;
0TF                rank++;
3310TF                for (size_t j = 0; j < n; j++) A[k * n + j] *= pivot;
34050TF                }
50TF                }
344100TF}
35050TF          val = hv_fetch(row_hashes[i], var, strlen(var), 0);
50TF          val = hv_fetch(row_hashes[i], var, strlen(var), 0);
35350TF                   val = av_fetch(av, 0, 0);
3550TF        } else if (data_hoa) {
3590TF                   val = av_fetch(av, i, 0);
3660TF        return NAN; // Catch undef/missing keys
3670TF}
376100TF                   av_push(cols, newSVsv(hv_iterkeysv(entry)));
378100TF        } else if (row_hashes && n > 0 && row_hashes[0]) {
380100TF          HE *restrict entry;
50TF          HE *restrict entry;
50TF          HE *restrict entry;
38450TF        }
38650TF}
50TF}
50TF}
391100TF
50TF
392100TF        char *restrict term_cpy = savepv(term);
40250TF        }
4040TF          char *restrict end = strrchr(term_cpy, ')');
0TF          char *restrict end = strrchr(term_cpy, ')');
0TF          char *restrict end = strrchr(term_cpy, ')');
40850TF          int power = 1;
41050TF                   *caret = '\0';
50TF                   *caret = '\0';
50TF                   *caret = '\0';
41550TF          
50TF          
430100TF                        if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVAV) {
43150TF                                 AV*restrict av = (AV*)SvRV(*val);
447100TF}
448100TF
45650TF                        val = av_fetch(av, 0, 0);
457100TF                }
461100TF                        AV*restrict av = (AV*)SvRV(*col);
464100TF        }
100TF        }
467100TF        }
477100TF// Comparator for qsort
48350TF        return ((PVal*)a)->orig_idx - ((PVal*)b)->orig_idx;
497100TF        if (diff < 0) return -1;
498100TF        if (diff > 0) return  1;
501100TF
50TF
502100TF/* Compute 1-based average ranks with tie-breaking into out[].
50350TF * in[] is not modified.                                                 */
50450TFstatic void rank_data(const double *restrict in, double *restrict out, size_t n) {
50950TF
517100TF                for (size_t k = i; k <= j; k++) out[ri[k].idx] = avg;
51950TF        }
50TF        }
526100TF        double sx = 0, sy = 0, sxy = 0, sx2 = 0, sy2 = 0;
54250TF * x, T_y = pairs tied only on y.  Joint ties (both zero) are excluded
54450TF * Returns NAN when the denominator is zero.                             */
54850TF          for (size_t j = i + 1; j < n; j++) {
55050TF                   int sy = (y[i] > y[j]) - (y[i] < y[j]);
55450TF                   else if (sx == sy)           C++;
55650TF          }
558100TF        double denom = sqrt((double)(C + D + tie_x) * (double)(C + D + tie_y));
56450TF * Allocates and frees temporary rank arrays internally for Spearman.   */
565100TFstatic double compute_cor(const double *restrict x, const double *restrict y,
567100TF        if (strcmp(method, "spearman") == 0) {
574100TF          return r;
100TF          return r;
575100TF        }
50TF        }
583100TF#define MAX_ITER 500
58650TF
58950TF        double aa, c, d, del, h, qab, qam, qap;
592100TF        if (fabs(d) < FPMIN) d = FPMIN;
597100TF          d = 1.0 + aa * d;
6090TF        }
61850TF        return 1.0 - bt * _incbeta_cf(b, a, 1.0 - x) / b;
620100TF
625100TF        if (strcmp(alt, "greater") == 0) return (t > 0) ? 0.5 * prob_2tail : 1.0 - 0.5 * prob_2tail;
626100TF        return prob_2tail;
62950TF// Bisection algorithm to find the inverse t-distribution (Critical t-value)
50TF// Bisection algorithm to find the inverse t-distribution (Critical t-value)
50TF// Bisection algorithm to find the inverse t-distribution (Critical t-value)
633100TF        while (get_t_pvalue(high, df, "greater") > p_tail) {
639100TF        for (unsigned short int i = 0; i < 100; i++) {
100TF        for (unsigned short int i = 0; i < 100; i++) {
643100TF                   low = mid;
50TF                   low = mid;
64850TF        }
653100TF        double da = *(const double*restrict)a;
655100TF        return (da > db) - (da < db);
65850TFstatic size_t calculate_sturges_bins(size_t n) {
681100TF                   size_t idx = (size_t)((val - min_val) / step);
687100TF                   /* If value is exactly on or slightly below the lower boundary of the assigned bin,
691100TF                   }
709100TF          }
724100TF        double a[4] = {2.50662823884, -18.61500062529, 41.39119773534, -25.44106049637};
50TF        double a[4] = {2.50662823884, -18.61500062529, 41.39119773534, -25.44106049637};
50TF        double a[4] = {2.50662823884, -18.61500062529, 41.39119773534, -25.44106049637};
727100TF                          0.0276438810333863, 0.0038405729373609, 0.0003951896511919,
728100TF                          0.0000321767881768, 0.0000002888167364, 0.0000003960315187};
730100TF        y = p - 0.5;
735100TF        } else {
100TF        } else {
100TF        } else {
75150TF * i.e. rho ≥ rho_obs) and how many yield S ≥ s_obs ("upper tail").
75250TF *
75450TF * Valid up to n = 9 (362 880 iterations — negligible cost).
75550TF * ----------------------------------------------------------------------- */
764100TF          double s_ = 0.0;                                     \
767100TF                   s_ += d_ * d_;                                   \
769100TF          if (s_ <= s_obs + 1e-9) count_le++;                 \
771100TF          total++;                                             \
773100TF
100TF
78450TF                   }
78550TF                   TALLY_PERM();
787100TF                   k = 1;
789100TF                   c[k] = 0;
79150TF          }
792100TF        }
79450TF
79550TF        Safefree(perm); Safefree(c);
79950TF        double p_ge = (double)count_ge / (double)total;
808100TF * Exact Kendall p-value via Mahonian Numbers (Inversions distribution)
81050TF * ----------------------------------------------------------------------- */
816100TF        /* Build the distribution of inversions via DP */
817100TF        for (size_t i = 2; i <= n; i++) {
819100TF          for (long k = 0; k <= max_inv; k++) next_dp[k] = 0.0;
825100TF                   }
830100TF          dp = next_dp;
834100TF        if (i_obs < 0) i_obs = 0;
836100TF        double p_le = 0.0; /* P(S <= S_obs) */
839100TF        for (long k = 0; k <= i_obs; k++) p_ge += dp[k];
844100TF        double p = 2.0 * (p_ge < p_le ? p_ge : p_le);
847100TF// F-distribution Cumulative Distribution Function P(F <= f)
86350TF                }
50TF                }
866100TF                for (size_t i = r; i < n; i++) {
86750TF                        if (fabs(X[i][k]) > max_val) max_val = fabs(X[i][k]);
87450TF                double norm = 0;
876100TF                        X[i][k] /= max_val;
100TF                        X[i][k] /= max_val;
50TF                        X[i][k] /= max_val;
50TF                        X[i][k] /= max_val;
880100TF                double s = (X[r][k] > 0) ? -norm : norm;
882100TF                X[r][k] = s * max_val;
883100TF
899100TF                rank_map[k] = r; // Map original column index to orthogonal row index
900100TF                r++;
90150TF        }
91250TFstatic bool contains_nondigit(SV *restrict sv) {
50TFstatic bool contains_nondigit(SV *restrict sv) {
91350TF        if (!sv || !SvOK(sv)) return 0;
916100TF        for (size_t i = 0; i < len; i++) {
920100TF}
93350TF                   if (*p == '"') {
93750TF                       PerlIO_putc(fh, *p);
93950TF          }
943100TF        }
95150TF          if (row[i]) {
95250TF                   print_str_quoted(fh, row[i], sep);
96350TF        if (x == 0.0) return 1.0;
50TF        if (x == 0.0) return 1.0;
96450TF
966100TF        if (x < a + 1.0) {
977100TF
97850TF        // Continued fraction for x >= a + 1
983100TF        while (i < 10000) { // Safety bound
984100TF                double an = -i * (i - a);
985100TF                b += 2.0;
989100TF                if (fabs(c) < 1e-30) c = 1e-30;
100350TF        return igamc((double)df / 2.0, stat / 2.0);
1004100TF}
1009100TF#endif
1010100TF
1014100TF        if (k > n / 2) k = n - k;
103050TF        long double *restrict w = (long double *)safecalloc(max_u + 1, sizeof(long double));
1035100TF          for (int i = max_u; i >= j + m; i--) w[i] -= w[i - j - m];
1037100TF
100TF
1039100TF        for (int i = 0; i <= k; i++) cum_p += w[i];
1041100TF        long double total = choose_comb(m + n, n);
105950TF        for (int i = 1; i <= n; i++) {
106050TF          for (int j = max_v; j >= i; j--) w[j] += w[j - i];
1062100TF
10700TF        return result;
10710TF}
10730TFstatic int cmp_rank_info(const void *a, const void *b) {
10780TF
10820TF        size_t i = 0;
10870TF                while (j < n && ri[j].val == ri[i].val) j++;
10920TF                i = j;
1105100TF#endif
1106100TF
1108100TFstatic double r_pow_di(double x, int n) {
1115100TF
1116100TF// Two-sample two-sided asymptotic distribution
1124100TF          int k_max = (int) sqrt(2.0 - log(tol));
1125100TF          double w = log(x);
113150TF          p = s / M_1_SQRT_2PI;
11320TF          if(!lower) p = 1.0 - p;
1146100TF                   k++;
1147100TF          }
1148100TF          p = new_val;
1152100TF
115650TF          for(unsigned int j = 0; j < m; j++) {
1158100TF                   for(unsigned int k = 0; k < m; k++) s += A[i * m + k] * B[k * m + j];
1159100TF                   C[i * m + j] = s;
1160100TF          }
1161100TF        }
1170100TF        m_power(A, eA, V, eV, m, n / 2);
117250TF        m_multiply(V, V, B, m);
1191100TF        int m = 2 * k - 1;
50TF        int m = 2 * k - 1;
119350TF        double *restrict H = (double*) safecalloc(m * m, sizeof(double));
100TF        double *restrict H = (double*) safecalloc(m * m, sizeof(double));
100TF        double *restrict H = (double*) safecalloc(m * m, sizeof(double));
119450TF        double *restrict Q = (double*) safecalloc(m * m, sizeof(double));
1197100TF          for(int j = 0; j < m; j++) {
100TF          for(int j = 0; j < m; j++) {
1198100TF                   if(i - j + 1 < 0) H[i * m + j] = 0;
100TF                   if(i - j + 1 < 0) H[i * m + j] = 0;
1204100TF          H[(m - 1) * m + i] -= r_pow_di(h, (m - i));
1205100TF        }
1206100TF        H[(m - 1) * m] += ((2 * h - 1 > 0) ? r_pow_di(2 * h - 1, m) : 0);
1215100TF
1225100TF          }
1226100TF        }
1229100TF        Safefree(Q);
1230100TF        return s;
1231100TF}
1232100TF
124750TF          while(i < nx && x[i] <= val) i++;
125550TF          if (-diff > max_d_minus) max_d_minus = -diff;
0TF          if (-diff > max_d_minus) max_d_minus = -diff;
126850TF
126950TF// Evaluate the exact 2-sample probability
1272100TF        double *restrict u = (double *) safecalloc(n + 1, sizeof(double));
127550TF        for(unsigned int j = 1; j <= n; j++) {
127950TF        for(unsigned int i = 1; i <= m; i++) {
1283100TF                        else {
1288100TF                }
130450TF
50TF
50TF
130950TF          // Default: 1 - P(T < qu)
1310100TF          // Ensure exact_pnt is using a convergence tolerance of at least 1e-15
50TF          // Ensure exact_pnt is using a convergence tolerance of at least 1e-15
131350TF}
1320100TF        double low = 0.0, high = 1.0;
132350TF          low = high;
132450TF          high *= 2.0;
132550TF          if (high > 1e100) break; /* Fallback limit */
13260TF        }
132950TF        for (unsigned short int i = 0; i < 150; i++) {
133350TF                if (p_mid < p) {
50TF                if (p_mid < p) {
50TF                if (p_mid < p) {
1341100TF}
100TF}
50TF}
134750TF *   my $res = oneway_test(\%groups, var_equal => 1);
1352100TF * ── Mode 2: formula – response ~ factor ───────────────────────────────────
135450TF *   my $res = oneway_test(\%data, formula => "yield ~ ctrl");
50TF *   my $res = oneway_test(\%data, formula => "yield ~ ctrl");
50TF *   my $res = oneway_test(\%data, formula => "yield ~ ctrl");
136350TF *     oneway.test(Value ~ Group, data = my_data)
100TF *     oneway.test(Value ~ Group, data = my_data)
50TF *     oneway.test(Value ~ Group, data = my_data)
1369100TF *   Hash ref with keys:
137150TF *     num_df    => numerator degrees of freedom   (k − 1)
50TF *     num_df    => numerator degrees of freedom   (k − 1)
50TF *     num_df    => numerator degrees of freedom   (k − 1)
137650TF *     n         => total observations
50TF *     n         => total observations
1385100TF/* -----------------------------------------------------------------------
1386100TF * C HELPERS  (place above "--- XS SECTION ---")
139150TF        double  statistic;
139250TF        double  num_df;
1398100TF        double  ms_within;   /* ss_within  / denom_df         */
1399100TF        int     k;           /* number of groups              */
1403100TF
140450TF/* ── c_oneway_test ───────────────────────────────────────────────────────
140750TF *  sizes     â€“ n_i for each group (length k)
50TF *  sizes     â€“ n_i for each group (length k)
141150TF *  Mirrors R's oneway.test() arithmetic exactly.
14180TF              int var_equal)
142750TF
50TF
142950TF        IV total_n = 0;
1432100TF          n_i[g]     = (double)ng;
144050TF          double ss = 0.0;
1441100TF          for (size_t i = 0; i < ng; i++) {
1442100TF                   double d = data[offset + i] - mean;
144350TF                   ss += d * d;
1445100TF          v_i[g] = ss / (double)(ng - 1);   /* ng >= 2 guaranteed by caller */
144650TF          offset += ng;
144850TF
144950TF        res.n = total_n;
145150TF        /* grand mean (simple average over all obs; used only by classic branch) */
145250TF        double grand_mean = 0.0;
145450TF        grand_mean /= (double)total_n;
14640TF                        double dm = m_i[g] - grand_mean;
147650TF        } else {
147750TF                /* ── Welch one-way (heteroscedastic) ───────────────────────────── *
149850TF                }
100TF                }
50TF                }
150350TF                        num += w_i[g] * dm * dm;
100TF                        num += w_i[g] * dm * dm;
50TF                        num += w_i[g] * dm * dm;
150850TF                /* unweighted SS for the output table */
1512100TF                        ssbg += n_i[g] * dm * dm;
1515100TF                res.ss_between = ssbg;
1516100TF                res.ss_within  = sswg;
1517100TF                res.ms_between = (df1  > 0.0) ? ssbg / df1          : 0.0;
151850TF                res.ms_within  = (res.denom_df > 0.0) ? sswg / res.denom_df : 0.0;
1519100TF                Safefree(w_i);
152050TF        }
15210TF        /* upper-tail p-value  P(F ≥ statistic) */
152450TF        return res;
1528100TF *
50TF *
50TF *
153250TF *  Caller must Safefree() both *lhs and *rhs on success.
1536100TF{
50TF{
50TF{
1544100TF        if (l_end < l_start) return 0;             /* empty LHS */
100TF        if (l_end < l_start) return 0;             /* empty LHS */
1547100TF        const char *restrict r_start = tilde + 1;
154950TF        const char *restrict r_end = r_start + strlen(r_start) - 1;
50TF        const char *restrict r_end = r_start + strlen(r_start) - 1;
50TF        const char *restrict r_end = r_start + strlen(r_start) - 1;
1555100TF
155750TF        *rhs = (char *)safemalloc(rlen + 1);
50TF        *rhs = (char *)safemalloc(rlen + 1);
50TF        *rhs = (char *)safemalloc(rlen + 1);
156350TF/* ── build_groups_from_formula ───────────────────────────────────────────
156450TF *
1569100TF *                  slots for both; actual group count returned via *out_k)
100TF *                  slots for both; actual group count returned via *out_k)
157250TF *
157350TF *  Group identity is the string representation of each label element
157450TF *  (SvPV_nolen), so integer 0 and string "0" are the same group.
50TF *  (SvPV_nolen), so integer 0 and string "0" are the same group.
100TF *  (SvPV_nolen), so integer 0 and string "0" are the same group.
1576100TF *  factor level ordering from stack().
50TF *  factor level ordering from stack().
1580100TF#define OWT_MAX_GROUPS 1024   /* sane ceiling; ANOVA with >1024 groups is absurd */
1585100TF        AV *restrict label_av,
158650TF        double *restrict out_flat,
15880TF        size_t *restrict out_k,
159250TF{
159850TF                   "formula: response length (%"IVdf") != factor length (%"IVdf")",
159950TF                   n, nl);
100TF                   n, nl);
16000TF          return 0;
16010TF        }
160550TF        }
160650TF
1611100TF        IV     *restrict obs_group    = (IV *)safemalloc((size_t)n * sizeof(IV));
50TF        IV     *restrict obs_group    = (IV *)safemalloc((size_t)n * sizeof(IV));
100TF        IV     *restrict obs_group    = (IV *)safemalloc((size_t)n * sizeof(IV));
1615100TF          SV **lsv = av_fetch(label_av, i, 0);
161750TF
50TF
50TF
1620100TF          for (size_t g = 0; g < ngroups; g++) {
162250TF          }
50TF          }
50TF          }
162550TF                       snprintf(errbuf, errbuf_len,
162950TF                       return 0;
163350TF                   group_names[ngroups] = (char *)safemalloc(lablen + 1);
1638100TF        }
1645100TF          return 0;
1646100TF        }
164850TF        /* count per-group sizes */
164950TF        memset(out_sizes, 0, ngroups * sizeof(size_t));
165050TF        for (unsigned i = 0; i < n; i++) out_sizes[obs_group[i]]++;
50TF        for (unsigned i = 0; i < n; i++) out_sizes[obs_group[i]]++;
165150TF
50TF
165550TF                   snprintf(errbuf, errbuf_len,
50TF                   snprintf(errbuf, errbuf_len,
165950TF                   Safefree(group_names);  Safefree(obs_group);
166450TF        /* ── fill flat output array in group order ─────────────────────── *
166550TF        *  We compute a running write-offset per group, then scatter.      *
166750TF        size_t *restrict write_pos = (size_t *)safemalloc(ngroups * sizeof(size_t));
16710TF
16760TF          out_flat[write_pos[g]++] = val;
16770TF        }
0TF        }
16780TF
16790TF        *out_k = ngroups;
16830TF        if (out_names) {
16840TF          *out_names = group_names;   /* caller takes ownership */
168950TF        return 1;
170550TF    char        *lhs = NULL, *rhs = NULL;
170850TF    char       ** gnames = NULL;
100TF    char       ** gnames = NULL;
50TF    char       ** gnames = NULL;
171150TF    IV           total_n = 0;
1719100TF                SV *restrict val = ST(ai + 1);
50TF                SV *restrict val = ST(ai + 1);
100TF                SV *restrict val = ST(ai + 1);
1722100TF                else if (strEQ(key, "formula"))
1725100TF        /* validate data_ref and determine if it's an Array or Hash */
1726100TF        if (!SvROK(data_ref))
1727100TF          croak("oneway_test: first argument must be a hash or array reference");
1730100TF        if (SvTYPE(rv) == SVt_PVHV) {
1738100TF            /* MODE 3 – Array of Arrays (AoA) */
1742100TF            k = (size_t)av_len(in_av) + 1;
1747100TF            /* first pass: sizes, total_n, and generate index names */
175050TF                if (!val || !*val || !SvROK(*val) || SvTYPE(SvRV(*val)) != SVt_PVAV)
1762100TF                memcpy(gnames[g], buf, klen + 1);
1767100TF            for (size_t g = 0; g < k; g++) {
1781100TF          factor_name = rhs;   /* use the actual factor variable name */
1782100TF          SV **restrict resp_svp = hv_fetch(in_hv, lhs, (I32)strlen(lhs), 0);
180550TF        } else {
50TF        } else {
1807100TF                k = (size_t)hv_iterinit(in_hv);
50TF                k = (size_t)hv_iterinit(in_hv);
181250TF                /* first pass: sizes, total_n, and group name strings */
1822100TF                                     croak("oneway_test: group '%s' has fewer than 2 observations",
182350TF                                           HePV(he, PL_na));
182650TF                                /* save a copy of the key string */
1827100TF                                STRLEN klen;
182850TF                                const char *kstr = HePV(he, klen);
1829100TF                                gnames[g] = (char *)safemalloc(klen + 1);
183050TF                                memcpy(gnames[g], kstr, klen + 1);
183450TF                /* second pass: fill flat in the same iteration order */
50TF                /* second pass: fill flat in the same iteration order */
1838100TF                        hv_iterinit(in_hv);
50TF                        hv_iterinit(in_hv);
184250TF                                 for (IV i = 0; i < len; i++) {
50TF                                 for (IV i = 0; i < len; i++) {
1845100TF                                 }
50TF                                 }
1846100TF                        }
50TF                        }
1855100TF                        for (size_t i = 0; i < sizes[g]; i++) sum += flat[offset + i];
185750TF                        offset   += sizes[g];
186150TF        res = c_oneway_test(flat, sizes, k, var_equal);
50TF        res = c_oneway_test(flat, sizes, k, var_equal);
1865100TF        /* ── build return hash ref
50TF        /* ── build return hash ref
1871100TF        */
187350TF        /* Group (factor) sub-hash */
50TF        /* Group (factor) sub-hash */
50TF        /* Group (factor) sub-hash */
18740TF        {
1877100TF                hv_stores(g_hv, "Sum Sq",  newSVnv(res.ss_between));
1880100TF                hv_stores(g_hv, "Pr(>F)",  newSVnv(res.p_value));
188650TF                HV *restrict r_hv = newHV();
188850TF                hv_stores(r_hv, "Sum Sq",  newSVnv(res.ss_within));
50TF                hv_stores(r_hv, "Sum Sq",  newSVnv(res.ss_within));
50TF                hv_stores(r_hv, "Sum Sq",  newSVnv(res.ss_within));
50TF                hv_stores(r_hv, "Sum Sq",  newSVnv(res.ss_within));
1892100TF        /* group_stats sub-hash */
189450TF                HV *restrict gs_hv   = newHV();
50TF                HV *restrict gs_hv   = newHV();
50TF                HV *restrict gs_hv   = newHV();
50TF                HV *restrict gs_hv   = newHV();
190250TF                }
190550TF                hv_stores(ret_hv, "group_stats", newRV_noinc((SV *)gs_hv));
100TF                hv_stores(ret_hv, "group_stats", newRV_noinc((SV *)gs_hv));
1909100TF        for (size_t g = 0; g < k; g++) Safefree(gnames[g]);
1910100TF        Safefree(gnames);
50TF        Safefree(gnames);
1912100TF        /* freed here, after factor_name is no longer needed */
191450TF  OUTPUT:
50TF  OUTPUT:
1920100TF        SV *restrict x_sv = NULL, *restrict y_sv = NULL;
1924100TF
1930100TF        // Check if second argument is an array (2-sample) or a string representing a CDF (1-sample)
1935100TF                } else if (SvPOK(ST(arg_idx))) {
194350TF          SV *restrict val = ST(arg_idx + 1);
1944100TF          if      (strEQ(key, "x"))           x_sv = val;
194650TF          else if (strEQ(key, "exact"))       {
50TF          else if (strEQ(key, "exact"))       {
1953100TF
1961100TF
196350TF          croak("ks_test: alternative must be 'two.sided', 'less', or 'greater'");
196650TF        AV *restrict x_av = (AV*)SvRV(x_sv);
1968100TF        if (nx == 0) croak("Not enough 'x' observations");
197050TF        // Extract 'x' array to C-array
50TF        // Extract 'x' array to C-array
197150TF        double *restrict x_data = (double *)safemalloc(nx * sizeof(double));
1972100TF        size_t valid_nx = 0;
100TF        size_t valid_nx = 0;
1973100TF        for (size_t i = 0; i < nx; i++) {
197750TF          }
197850TF        }
1990100TF          for (size_t i = 0; i < ny; i++) {
1995100TF          }
1998100TF                   Safefree(x_data); Safefree(y_data);
2001100TF
50TF
2003100TF          calc_2sample_stats(x_data, valid_nx, y_data, valid_ny, &d, &d_plus, &d_minus);
200550TF          // Map alternative to the correct statistic
50TF          // Map alternative to the correct statistic
2010100TF          // Determine if exact or asymptotic
2015100TF
201850TF          double *restrict comb = (double *)safemalloc(total_n * sizeof(double));
2019100TF          for(size_t i=0; i<valid_nx; i++) comb[i] = x_data[i];
50TF          for(size_t i=0; i<valid_nx; i++) comb[i] = x_data[i];
20230TF          bool has_ties = FALSE;
20250TF                   if(comb[i] == comb[i-1]) { has_ties = TRUE; break; }
0TF                   if(comb[i] == comb[i-1]) { has_ties = TRUE; break; }
20270TF          Safefree(comb);
2037100TF                   method_desc = "Two-sample Kolmogorov-Smirnov test (asymptotic)";
2038100TF                   double z = statistic * sqrt((double)(valid_nx * valid_ny) / (valid_nx + valid_ny));
204050TF                       p_value = K2l(z, 0, 1e-9);
50TF                       p_value = K2l(z, 0, 1e-9);
2045100TF          Safefree(y_data);
2047100TF                const char *restrict dist = SvPV_nolen(y_sv);
204850TF                if (strEQ(dist, "pnorm")) {
20500TF                        double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
0TF                        double max_d = 0.0, max_d_plus = 0.0, max_d_minus = 0.0;
20530TF                                double cdf_obs_high = (double)(i + 1) / valid_nx;
0TF                                double cdf_obs_high = (double)(i + 1) / valid_nx;
20540TF                                double cdf_theor    = approx_pnorm(x_data[i]);
20570TF                                if (diff1 > max_d_plus) max_d_plus = diff1;
2073100TF                                        warn("exact 1-sample 1-sided KS test not implemented; using asymptotic");
207550TF                                        p_value = exp(-2.0 * z * z);
50TF                                        p_value = exp(-2.0 * z * z);
207750TF                        } else {
50TF                        } else {
2080100TF                                 if (is_two_sided) p_value = K2l(z, 0, 1e-6);
100TF                                 if (is_two_sided) p_value = K2l(z, 0, 1e-6);
208150TF                                 else p_value = exp(-2.0 * z * z);
20840TF                         Safefree(x_data);
2096100TF        hv_stores(res, "p_value", newSVnv(p_value));
50TF        hv_stores(res, "p_value", newSVnv(p_value));
209950TF        RETVAL = newRV_noinc((SV*)res);
2102100TF        RETVAL
50TF        RETVAL
2104100TFSV* wilcox_test(...)
210650TF{
50TF{
2110100TF        short int exact = -1;
211250TF        int arg_idx = 0;
50TF        int arg_idx = 0;
2116100TF                arg_idx++;
2123100TF        // Ensure the remaining arguments form complete key-value pairs
2128100TF        for (; arg_idx < items; arg_idx += 2) {
2132100TF                else if (strEQ(key, "y"))          y_sv = val;
50TF                else if (strEQ(key, "y"))          y_sv = val;
21350TF                else if (strEQ(key, "mu"))          mu = SvNV(val);
21370TF                        if (!SvOK(val)) exact = -1;
0TF                        if (!SvOK(val)) exact = -1;
21390TF                }
2149100TF
2150100TF        AV *restrict y_av = NULL;
215250TF        if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
50TF        if (y_sv && SvROK(y_sv) && SvTYPE(SvRV(y_sv)) == SVt_PVAV) {
2157100TF        const char *restrict method_desc = "";
216050TF        if (ny > 0 && !paired) {
50TF        if (ny > 0 && !paired) {
2161100TF                RankInfo *restrict ri = (RankInfo *)safemalloc((nx + ny) * sizeof(RankInfo));
216250TF                size_t valid_nx = 0, valid_ny = 0;
21630TF                for (size_t i = 0; i < nx; i++) {
21640TF                        SV**restrict el = av_fetch(x_av, i, 0);
0TF                        SV**restrict el = av_fetch(x_av, i, 0);
21650TF                        if (el && SvOK(*el) && looks_like_number(*el)) {
21680TF                                valid_nx++;
2182100TF                bool has_ties = 0;
218450TF                double w_rank_sum = 0.0;
50TF                double w_rank_sum = 0.0;
218550TF                for (size_t i = 0; i < total_n; i++) if (ri[i].idx == 1) w_rank_sum += ri[i].rank;
2186100TF                statistic = w_rank_sum - (double)valid_nx * (valid_nx + 1.0) / 2.0;
50TF                statistic = w_rank_sum - (double)valid_nx * (valid_nx + 1.0) / 2.0;
218750TF                
21900TF                else use_exact = (valid_nx < 50 && valid_ny < 50 && !has_ties);
2199100TF                        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);
220350TF                        else {
2204100TF                                double p = (p_less < p_greater) ? p_less : p_greater;
222150TF                        if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
50TF                        if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
50TF                        if (strcmp(alternative, "less") == 0) p_value = approx_pnorm(z);
222650TF        } else { // --- ONE SAMPLE / PAIRED ---
222750TF                if (paired && (!y_av || nx != ny)) croak("'x' and 'y' must have the same length for paired test");
223050TF                bool has_zeroes = FALSE;
2235100TF
223950TF                                double dy = SvNV(*y_el);
100TF                                double dy = SvNV(*y_el);
224150TF                                if (d == 0.0) has_zeroes = TRUE; // Drop exact zeroes
100TF                                if (d == 0.0) has_zeroes = TRUE; // Drop exact zeroes
2245100TF                                if (d == 0.0) has_zeroes = TRUE;
224850TF                }
224950TF                if (n_nz == 0) {
100TF                if (n_nz == 0) {
225150TF                        croak("not enough (non-missing) observations");
225450TF                for (size_t i = 0; i < n_nz; i++) {
50TF                for (size_t i = 0; i < n_nz; i++) {
50TF                for (size_t i = 0; i < n_nz; i++) {
2259100TF                double tie_adj = rank_and_count_ties(ri, n_nz, &has_ties);
2261100TF                for (size_t i = 0; i < n_nz; i++) {
2262100TF                        if (ri[i].idx) statistic += ri[i].rank;
2263100TF                }
100TF                }
100TF                }
2266100TF                else use_exact = (n_nz < 50 && !has_ties);
2269100TF                        use_exact = FALSE;
2272100TF                        warn("cannot compute exact p-value with zeroes; falling back to approximation");
50TF                        warn("cannot compute exact p-value with zeroes; falling back to approximation");
50TF                        warn("cannot compute exact p-value with zeroes; falling back to approximation");
100TF                        warn("cannot compute exact p-value with zeroes; falling back to approximation");
2281100TF                        else if (strcmp(alternative, "greater") == 0) p_value = p_greater;
229050TF                        double z = statistic - exp;
229450TF                                else if (strcmp(alternative, "greater") == 0) CORRECTION = 0.5;
229550TF                                else if (strcmp(alternative, "less") == 0) CORRECTION = -0.5;
229850TF
2310100TF        hv_stores(res, "alternative", newSVpv(alternative, 0));
231250TF}
231650TFSV* chisq_test(data_ref)
231750TF    SV* data_ref;
232050TF    // 1. Input Validation (mimics: die 'Input must be an array reference')
233050TF        is_2d = 1;
234250TF        double *restrict row_sum = (double*)safemalloc(r * sizeof(double));
50TF        double *restrict row_sum = (double*)safemalloc(r * sizeof(double));
234550TF        for(unsigned int j=0; j<c; j++) col_sum[j] = 0.0;
50TF        for(unsigned int j=0; j<c; j++) col_sum[j] = 0.0;
2350100TF                 SV**restrict val_sv = av_fetch(row, j, 0);
2351100TF                 double val = SvNV(*val_sv);
235250TF                 row_sum[i] += val;
236150TF            for (unsigned int j = 0; j < c; j++) {
2372100TF                } else {
237750TF        }
50TF        }
50TF        }
237850TF        safefree(row_sum); safefree(col_sum);
50TF        safefree(row_sum); safefree(col_sum);
50TF        safefree(row_sum); safefree(col_sum);
238150TF      for (unsigned int j = 0; j < c; j++) {
50TF      for (unsigned int j = 0; j < c; j++) {
238950TF           av_push(expected_av, newSVnv(E));
2395100TF
2397100TF    HV*restrict results = newHV();
2398100TF
2406100TF    hv_store(parameter_hv, "df", 2, newSViv(df), 0);
2415100TF    // 'observed' => data_ref (Increment ref count since hv_store consumes ownership)
2427100TF        }
248350TF    if (!data_sv || !SvROK(data_sv)) {
2485100TF    }
2488100TF        croak("write_table: 'data' must be a HASH or ARRAY reference\n");
2489100TF    }
249050TF
249350TF
249450TF    // NEW: Auto-detect separator from file extension if not overridden
50TF    // NEW: Auto-detect separator from file extension if not overridden
2498100TF            const char *restrict ext = file + file_len - 4;
50TF            const char *restrict ext = file + file_len - 4;
2506100TF
100TF
50TF
251050TF        }
251450TF    AV *restrict rows_av = NULL;
251550TF
2518100TF        HV *restrict hv = (HV*)data_ref;
251950TF        if (hv_iterinit(hv) == 0) XSRETURN_EMPTY;
252350TF        if (!first_val || !SvROK(first_val)) {
50TF        if (!first_val || !SvROK(first_val)) {
252850TF            croak("write_table: Data values must be either all HASHes or all ARRAYs\n");
25310TF        is_hoa = (first_type == SVt_PVAV);
0TF        is_hoa = (first_type == SVt_PVAV);
25320TF        hv_iterinit(hv);
0TF        hv_iterinit(hv);
254150TF            hv_iterinit(hv);
0TF            hv_iterinit(hv);
2547100TF        AV *restrict av = (AV*)data_ref;
2549100TF        SV **restrict first_ptr = av_fetch(av, 0, 0);
255050TF        if (!first_ptr || !*first_ptr || !SvROK(*first_ptr) || SvTYPE(SvRV(*first_ptr)) != SVt_PVHV) {
255250TF        }
255850TF            }
256050TF        is_aoh = 1;
256250TF
256450TF    if (!fh) croak("write_table: Could not open '%s' for writing", file);
100TF    if (!fh) croak("write_table: Could not open '%s' for writing", file);
256750TF    bool inc_rownames = (row_names_sv && SvTRUE(row_names_sv)) ? 1 : 0;
2568100TF    const char *restrict rownames_col = NULL;
257250TF        if (col_names_sv && SvOK(col_names_sv)) {
50TF        if (col_names_sv && SvOK(col_names_sv)) {
257450TF            for(size_t i=0; i<=av_len(c_av); i++) {
50TF            for(size_t i=0; i<=av_len(c_av); i++) {
2576100TF                if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
25840TF                hv_iterinit(inner);
25870TF                    hv_store_ent(col_map, hv_iterkeysv(inner_entry), newSViv(1), 0);
0TF                    hv_store_ent(col_map, hv_iterkeysv(inner_entry), newSViv(1), 0);
25880TF                }
25900TF            unsigned num_cols = hv_iterinit(col_map);
0TF            unsigned num_cols = hv_iterinit(col_map);
0TF            unsigned num_cols = hv_iterinit(col_map);
25950TF            }
2603100TF
260450TF    size_t h_idx = 0;
2609100TF    }
2612100TF
261450TF    const char **restrict row_array = safemalloc(num_rows * sizeof(char*));
2615100TF    for(size_t i=0; i<num_rows; i++) {
261750TF    }
2619100TF
2620100TF    HV *restrict data_hv = (HV*)data_ref;
2622100TF
262350TF    for(size_t i=0; i<num_rows; i++) {
0TF    for(size_t i=0; i<num_rows; i++) {
262950TF
2630100TF        for(size_t j=0; j<num_headers; j++) {
2631100TF            SV**restrict h_ptr = av_fetch(headers_av, j, 0);
263250TF            const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
2637100TF              safefree(row_array); safefree(row_data);
263850TF              if (headers_av) SvREFCNT_dec(headers_av);
2649100TF    safefree(row_array); safefree(row_data);
266050TF        }
50TF        }
266150TF
2664100TF            for(size_t i=0; i<=av_len(c_av); i++) {
266650TF                if(c && SvOK(*c)) av_push(headers_av, newSVsv(*c));
2670100TF            const char **restrict col_array = safemalloc(num_cols * sizeof(char*));
2671100TF            for(unsigned int i=0; i<num_cols; i++) {
2673100TF                col_array[i] = SvPV_nolen(hv_iterkeysv(ce));
267550TF            qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
2676100TF            for(unsigned i=0; i<num_cols; i++) av_push(headers_av, newSVpv(col_array[i], 0));
268150TF            rownames_col = SvPV_nolen(row_names_sv);
268450TF            for(size_t i=0; i<=av_len(headers_av); i++) {
2686100TF                if (!h_ptr || !*h_ptr) continue;
269250TF            SvREFCNT_dec(headers_av);
26930TF            headers_av = filtered_headers;
2702100TF        }
2705100TF        const char **restrict row_data = safemalloc((num_headers + 1) * sizeof(char*));
2707100TF            size_t d_idx = 0;
2708100TF            if (inc_rownames) {
270950TF                if (rownames_col) {
50TF                if (rownames_col) {
2713100TF                        SV **restrict rn_val_ptr = av_fetch(rn_arr, i, 0);
271450TF                        if (rn_val_ptr && SvOK(*rn_val_ptr)) {
272350TF                            row_data[d_idx++] = undef_val;
2724100TF                         }
2725100TF                    } else {
272850TF                } else {
2737100TF                SV **restrict arr_ptr = hv_fetch(data_hv, col_name, strlen(col_name), 0);
100TF                SV **restrict arr_ptr = hv_fetch(data_hv, col_name, strlen(col_name), 0);
2738100TF                if (arr_ptr && SvROK(*arr_ptr)) {
2740100TF                    SV **restrict cell_ptr = av_fetch(arr, i, 0);
2743100TF                              PerlIO_close(fh);
2747100TF                         }
274850TF                         row_data[d_idx++] = SvPV_nolen(*cell_ptr);
275050TF                         row_data[d_idx++] = undef_val;
100TF                         row_data[d_idx++] = undef_val;
2756100TF            print_string_row(fh, row_data, d_idx, sep);
2758100TF        }
276050TF    } else if (is_aoh) {// ----- Array of Hashes -----
100TF    } else if (is_aoh) {// ----- Array of Hashes -----
2762100TF      size_t num_rows = av_len(data_av) + 1;
276550TF           for(size_t i=0; i<=av_len(c_av); i++) {
276650TF                SV **restrict c = av_fetch(c_av, i, 0);
2769100TF      } else {
277050TF           HV *restrict col_map = newHV();
2780100TF                }
100TF                }
50TF                }
2785100TF                HE *restrict ce = hv_iternext(col_map);
2788100TF           qsort(col_array, num_cols, sizeof(char*), cmp_string_wt);
2792100TF      }
2795100TF           AV *restrict filtered_headers = newAV();
100TF           AV *restrict filtered_headers = newAV();
2796100TF           for(size_t i=0; i<=av_len(headers_av); i++) {
2797100TF                SV**restrict h_ptr = av_fetch(headers_av, i, 0);
279850TF                if (!h_ptr || !*h_ptr) continue;
2799100TF                SV *restrict h_sv = *h_ptr;
2801100TF                    av_push(filtered_headers, newSVsv(h_sv));
2807100TF      size_t num_headers = av_len(headers_av) + 1;
2808100TF      const char **restrict header_row = safemalloc((num_headers + 1) * sizeof(char*));
2809100TF      size_t h_idx = 0;
281050TF      if (inc_rownames) header_row[h_idx++] = "";
2818100TF      for(size_t i=0; i<num_rows; i++) {
282150TF           HV *restrict row_hv = (row_ptr && SvROK(*row_ptr)) ? (HV*)SvRV(*row_ptr) : NULL;
2827100TF                            PerlIO_close(fh);
50TF                            PerlIO_close(fh);
2828100TF                                 safefree(row_data);
2830100TF                            croak("write_table: Cannot write nested reference types to table\n");
2833100TF                 } else {
283450TF                       row_data[d_idx++] = undef_val;
2836100TF               } else {
2845100TF                const char *restrict col_name = (h_ptr && SvOK(*h_ptr)) ? SvPV_nolen(*h_ptr) : "";
285050TF                        safefree(row_data);
28530TF                    }
28540TF                    row_data[d_idx++] = SvPV_nolen(*cell_ptr);
2858100TF           }
2862100TF      safefree(row_data);
2863100TF    }
2885100TF                data = newAV();
2887100TF        sep_len = sep_str ? strlen(sep_str) : 0;
2889100TF
2891100TF        if (!fp) {
2898100TF                size_t len = SvCUR(line_sv);
290850TF                        bool is_empty = 1;
50TF                        bool is_empty = 1;
2920100TF                for (size_t i = 0; i < len; i++) {
2924100TF                                if (in_quotes && (i + 1 < len) && line[i+1] == '"') {
2925100TF                                        sv_catpvn(field, "\"", 1);
292650TF                                        i++; // Skip the escaped second quote
2927100TF                                } else if (in_quotes) {
50TF                                } else if (in_quotes) {
292850TF                                        in_quotes = 0;  // Close quotes
294150TF                }
50TF                }
50TF                }
294250TF                if (in_quotes) {
50TF                if (in_quotes) {
50TF                if (in_quotes) {
295050TF                        // If a callback is provided, invoke it in a streaming fashion
2956100TF                                XPUSHs(sv_2mortal(newRV_inc((SV*)current_row)));
296050TF                                LEAVE;
100TF                                LEAVE;
50TF                                LEAVE;
296150TF                                SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
100TF                                SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
50TF                                SvREFCNT_dec(current_row); // Frees the row from C memory if Perl didn't keep it
2964100TF                        }
100TF                        }
297150TF        if (in_quotes) {
2977100TF                        PUSHMARK(SP);
2980100TF                        call_sv(callback, G_DISCARD);
298950TF        SvREFCNT_dec(field);
50TF        SvREFCNT_dec(field);
3003100TF                if (!SvROK(x_sv) || SvTYPE(SvRV(x_sv)) != SVt_PVAV) {
3005100TF                }
3006100TF                if (!SvROK(y_sv) || SvTYPE(SvRV(y_sv)) != SVt_PVAV) {
301050TF                // 2. Validate method argument
0TF                // 2. Validate method argument
301150TF                if (strcmp(method, "pearson") != 0 &&
301250TF                        strcmp(method, "spearman") != 0 &&
3013100TF                        strcmp(method, "kendall") != 0) {
301850TF                AV *restrict y_av = (AV*)SvRV(y_sv);
302050TF                size_t ny = av_len(y_av) + 1;
50TF                size_t ny = av_len(y_av) + 1;
302450TF                                   (unsigned long)nx, (unsigned long)ny);
0TF                                   (unsigned long)nx, (unsigned long)ny);
302550TF                }
50TF                }
303050TF                double *restrict y_val = (double*)safemalloc(nx * sizeof(double));
50TF                double *restrict y_val = (double*)safemalloc(nx * sizeof(double));
303250TF
30400TF
0TF
30430TF                                 x_val[n] = xv;
30450TF                                 n++;
305150TF                        Safefree(x_val);        Safefree(y_val);
3059100TF                                     for (size_t j = 0; j < n; j++) {
306850TF                                     // Spearman: Rank the data first, then run standard covariance
50TF                                     // Spearman: Rank the data first, then run standard covariance
3072100TF                                     rank_data(x_val, rx, n);
3079100TF                                         cov_sum += dx * (ry[i] - mean_y);
308050TF                                     }
50TF                                     }
308550TF                                         double dx = x_val[i] - mean_x;
0TF                                         double dx = x_val[i] - mean_x;
308650TF                                         mean_x += dx / (i + 1);
50TF                                         mean_x += dx / (i + 1);
309150TF                                 }
30960TF                        Safefree(x_val); Safefree(y_val);
3113100TF        char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
313450TF        HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
50TF        HV *restrict res_hv, *restrict coef_hv, *restrict fitted_hv, *restrict resid_hv, *restrict summary_hv;
314150TF          const char *restrict key = SvPV_nolen(ST(i_arg));
3144100TF          else if (strEQ(key, "data"))    data_sv = val;
314650TF          else croak("glm: unknown argument '%s'", key);
50TF          else croak("glm: unknown argument '%s'", key);
314850TF        if (!formula) croak("glm: formula is required");
315650TF        Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
50TF        Newx(terms, term_cap, char*); Newx(uniq_terms, term_cap, char*);
3163100TF
316650TF        *tilde = '\0';
317350TF        while (chunk != NULL) {
31770TF          }
318350TF          if (star) {
318450TF                   *star = '\0';
3185100TF                   char *restrict left = chunk; char *restrict right = star + 1;
319350TF                   snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
100TF                   snprintf(terms[num_terms++], inter_len, "%s:%s", left, right);
3195100TF                   char *restrict c_chunk = strchr(chunk, '^');
3203100TF          bool found = FALSE;
3207100TF          if (!found) uniq_terms[num_uniq++] = savepv(terms[i]);
3217100TF                if (entry) {
322350TF                                 for(i = 0; i < n; i++) {
324650TF                       row_hashes[i] = (HV*)SvRV(*val);
324750TF                       char buf[32]; snprintf(buf, sizeof(buf), "%lu", i + 1);
325750TF        // --- Categorical Expansion ---
3267100TF          if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
3269100TF                   Newx(levels, levels_cap, char*);
50TF                   Newx(levels, levels_cap, char*);
3272100TF                       if (str_val) {
327450TF                           for (size_t l = 0; l < num_levels; l++) {
100TF                           for (size_t l = 0; l < num_levels; l++) {
3276100TF                           }
50TF                           }
3285100TF                       for (size_t l1 = 0; l1 < num_levels - 1; l1++) {
3287100TF                               if (strcmp(levels[l1], levels[l2]) > 0) {
100TF                               if (strcmp(levels[l1], levels[l2]) > 0) {
3296100TF                               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
329750TF                           }
3308100TF                   }
3310100TF                   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
50TF                   exp_terms[p_exp] = savepv(uniq_terms[j]); is_dummy[p_exp] = FALSE; p_exp++;
3313100TF        p = p_exp;
331550TF        Newx(X, n * p, double); Newx(Y, n, double);
100TF        Newx(X, n * p, double); Newx(Y, n, double);
3317100TF
100TF
3326100TF                        if (strcmp(exp_terms[j], "Intercept") == 0) {
3328100TF                        } else if (is_dummy[j]) {
100TF                        } else if (is_dummy[j]) {
3337100TF                        }
333850TF                }
335350TF        W = (double*)safemalloc(valid_n * sizeof(double)); Z = (double*)safemalloc(valid_n * sizeof(double));
3357100TF        for (i = 0; i < p; i++) { beta[i] = 0.0; beta_old[i] = 0.0; }
3359100TF        double sum_y = 0.0;
100TF        double sum_y = 0.0;
3361100TF        double mean_y = sum_y / valid_n;
3366100TF                   eta[i] = log(mu[i] / (1.0 - mu[i]));
337150TF                   deviance_old += dev;
3380100TF                        if (is_binomial) {
3383100TF                                 if (varmu < 1e-10) varmu = 1e-10;
338650TF                        } else {
339450TF                        double w = W[k], z = Z[k];
339850TF                                 for (size_t j = 0; j < p; j++) XtWX[i * p + j] += xw * X[k * p + j];
340050TF                }
3404100TF                                 double sum = 0.0;
340650TF                                 beta[i] = sum;
342250TF                                     
3423100TF                                     double dev = 0.0;
3431100TF                                     deviance_new += res * res;
3435100TF                        if (!is_binomial || deviance_new <= deviance_old + 1e-7 || !isfinite(deviance_new)) {
3436100TF                                 continue;
343750TF                        }
3442100TF                // Convergence Check
100TF                // Convergence Check
3443100TF                if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) {
100TF                if (fabs(deviance_new - deviance_old) / (0.1 + fabs(deviance_new)) < epsilon) {
344650TF                deviance_old = deviance_new;
3448100TF        }
3463100TF          if (is_binomial) {
100TF          if (is_binomial) {
3468100TF                   double diff = Y[i] - wtdmu;
347250TF        // --- AIC Calculation ---
3476100TF        } else if (is_binomial) {
347850TF        }
50TF        }
3481100TF        df_res = valid_n - final_rank;
3482100TF        dispersion = is_binomial ? 1.0 : ((df_res > 0) ? (deviance_new / df_res) : NAN);
348550TF                if (is_binomial) {
349250TF                }
349550TF                Safefree(valid_row_names[i]);
349750TF        Safefree(valid_row_names);
349950TF        summary_hv = newHV(); terms_av = newAV();
50TF        summary_hv = newHV(); terms_av = newAV();
350550TF                if (aliased[j]) {
0TF                if (aliased[j]) {
350950TF                        hv_store(row_hv, is_binomial ? "Pr(>|z|)" : "Pr(>|t|)", 8, newSVpv("NaN", 0), 0);
351350TF                        double p_val = is_binomial ? 2.0 * (1.0 - approx_pnorm(fabs(val_stat))) : get_t_pvalue(val_stat, df_res, "two.sided");
351450TF                        
351550TF                        hv_store(row_hv, "Estimate",   8, newSVnv(beta[j]), 0);
351650TF                        hv_store(row_hv, "Std. Error", 10, newSVnv(se), 0);
3520100TF                hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
3533100TF        hv_store(res_hv, "iter",           4, newSVuv(iter > max_iter ? max_iter : iter), 0);
3535100TF        hv_store(res_hv, "rank",           4, newSVuv(final_rank), 0);
356350TF        if (items < 2 || items % 2 != 0)
100TF        if (items < 2 || items % 2 != 0)
50TF        if (items < 2 || items % 2 != 0)
356950TF        const char *restrict method = "pearson";
3572100TF        bool continuity = 0;
3576100TF          const char *restrict key = SvPV_nolen(ST(i));
357750TF          SV *restrict val = ST(i + 1);
358050TF          else if (strEQ(key, "method"))      method = SvPV_nolen(val);
50TF          else if (strEQ(key, "method"))      method = SvPV_nolen(val);
50TF          else if (strEQ(key, "method"))      method = SvPV_nolen(val);
358450TF          else croak("cor_test: unknown argument '%s'", key);
358850TF        double *restrict x, *restrict y;
3590100TF
359250TF        bool is_kendall  = (strcmp(method, "kendall") == 0);
50TF        bool is_kendall  = (strcmp(method, "kendall") == 0);
359650TF        if (!SvOK(x_ref) || !SvROK(x_ref) || SvTYPE(SvRV(x_ref)) != SVt_PVAV ||
360750TF        x = safemalloc(n_raw * sizeof(double));
50TF        x = safemalloc(n_raw * sizeof(double));
50TF        x = safemalloc(n_raw * sizeof(double));
3611100TF        for (size_t i = 0; i < n_raw; i++) {
361350TF          SV **restrict y_val = av_fetch(y_av, i, 0);
50TF          SV **restrict y_val = av_fetch(y_av, i, 0);
361450TF          
50TF          
36210TF              y[n] = yv;
3627100TF          Safefree(x);
3630100TF        }
3632100TF        if (is_pearson) {
3634100TF          double mean_x = 0.0, mean_y = 0.0, M2_x = 0.0, M2_y = 0.0, cov = 0.0;
364850TF          // Confidence interval using Fisher's Z transform
3672100TF          double denom = sqrt((double)(c + d + tie_x) * (double)(c + d + tie_y));
3674100TF
50TF
3677100TF
367950TF          if (!exact_sv || !SvOK(exact_sv)) {
100TF          if (!exact_sv || !SvOK(exact_sv)) {
3686100TF
3693100TF                   double var_S = n * (n - 1) * (2.0 * n + 5.0) / 18.0;
3694100TF                   double S = c - d;
3704100TF                   }
3706100TF        } else if (is_spearman) {
50TF        } else if (is_spearman) {
3709100TF          compute_ranks(x, rank_x, n);
371150TF
100TF
3718100TF                   mean_y += dy / (i + 1);
372550TF          // S = sum of squared rank differences (R's reported statistic)
3726100TF          double S_stat = 0.0;
3737100TF                       break;
3739100TF          }
50TF          }
3742100TF          } else {
374450TF          }
100TF          }
3754100TF                   p_value = get_t_pvalue(statistic, (double)(n - 2), alternative);
3764100TF        hv_stores(rhv, "p.value", newSVnv(p_value));
376550TF        hv_stores(rhv, "statistic", newSVnv(statistic));
3777100TF}
3779100TF    RETVAL
50TF    RETVAL
3782100TF        SV *data
378450TF        AV *restrict av;
100TF        AV *restrict av;
3794100TF        n_raw = av_len(av) + 1;
3804100TF                       x[n] = val;
3805100TF                       mean += val;
382150TF        if (ssq == 0.0) {
100TF        if (ssq == 0.0) {
50TF        if (ssq == 0.0) {
382750TF        // --- Core AS R94 Algorithm: Weights and Statistic W ---
100TF        // --- Core AS R94 Algorithm: Weights and Statistic W ---
50TF        // --- Core AS R94 Algorithm: Weights and Statistic W ---
383350TF          // Exact P-value for n=3
3838100TF          Newx(m, n, double);
3842100TF                   sum_m2 += m[i] * m[i];
3843100TF          }
3844100TF          double u = 1.0 / sqrt((double)n);
3845100TF          double a_n = -2.706056*pow(u,5) + 4.434685*pow(u,4) - 2.071190*pow(u,3) - 0.147981*pow(u,2) + 0.221157*u + m[n-1]/sqrt(sum_m2);
3846100TF          a[n-1] = a_n;
3847100TF          a[0]   = -a_n;
384850TF          if (n == 4 || n == 5) {
3853100TF          } else {
50TF          } else {
50TF          } else {
385750TF                   double eps = (sum_m2 - 2.0 * m[n-1]*m[n-1] - 2.0 * m[n-2]*m[n-2]) / (1.0 - 2.0 * a_n*a_n - 2.0 * a_n1*a_n1);
3859100TF                       a[i] = m[i] / sqrt(eps);
50TF                       a[i] = m[i] / sqrt(eps);
50TF                       a[i] = m[i] / sqrt(eps);
386250TF          for (size_t i = 0; i < n; i++) {
100TF          for (size_t i = 0; i < n; i++) {
3867100TF          /* NOTE: p_val is declared in PREINIT above;
386950TF                */
50TF                */
3875100TF                   // if y reaches gamma the p-value is essentially zero
50TF                   // if y reaches gamma the p-value is essentially zero
3877100TF                   double gamma = 0.459 * nn - 2.273;
100TF                   double gamma = 0.459 * nn - 2.273;
3878100TF                   if (y >= gamma) {
3880100TF                   } else {
100TF                   } else {
3882100TF                       double mu     = 0.544  + nn * (-0.39978  + nn * ( 0.025054  - nn * 0.0006714));
388450TF                       double sigma  = exp(sig_val);
50TF                       double sigma  = exp(sig_val);
3890100TF          } else {
3892100TF                   double ln_n   = log((double)n);
389550TF                   double sig_val= -0.4803 + ln_n * (-0.082676 + ln_n * 0.0030302);
50TF                   double sig_val= -0.4803 + ln_n * (-0.082676 + ln_n * 0.0030302);
389650TF                   double sigma  = exp(sig_val);
50TF                   double sigma  = exp(sig_val);
390350TF          
3909100TF        hv_stores(ret_hash, "W",         newSVnv(w));
391050TF        hv_stores(ret_hash, "p_value",   newSVnv(p_val));
0TF        hv_stores(ret_hash, "p_value",   newSVnv(p_val));
391950TF                size_t count = 0;
0TF                size_t count = 0;
3939100TF                                 }
3943100TF                                     min_val = val;
3966100TF                       AV* restrict av = (AV*)SvRV(arg);
50TF                       AV* restrict av = (AV*)SvRV(arg);
3972100TF                               if (first || val > max_val) {
3978100TF                               croak("max: undefined value at array ref index %zu (argument %zu)", j, i);
3980100TF                       }
100TF                       }
3981100TF                   } else if (SvOK(arg)) {
50TF                   } else if (SvOK(arg)) {
398250TF                       double val = SvNV(arg);
398650TF                       }
398750TF                       count++;
3989100TF                       croak("max: undefined value at argument index %zu", i);
399150TF          }
50TF          }
3997100TFSV* runif(...)
3998100TFCODE:
4000100TF        size_t n = 0;
4002100TF
4004100TF        bool n_set = 0, min_set = 0, max_set = 0;
4006100TF        unsigned int i = 0;
4007100TF
4009100TF          croak("Usage: runif(n, [min=0], [max=1]) or runif(n => $n, ...)");
4011100TF
4013100TF                // 1. Check if the current argument is a string key for a named parameter
401450TF                if (i + 1 < items && SvPOK(ST(i))) {
4016100TF                        if (strEQ(key, "n")) {
4018100TF                                n_set = 1;
4020100TF                                continue;
402150TF                        } else if (strEQ(key, "min")) {
4023100TF                                min_set = 1;
4025100TF                                continue;
4027100TF                                max = SvNV(ST(i+1));
4029100TF                                i += 2;
4030100TF                                continue;
4032100TF                }
403450TF                // 2. Fallback to positional parsing if it's not a recognized key
403550TF                if (!n_set) {
4038100TF                } else if (!min_set) {
404050TF                        min_set = 1;
4045100TF                        croak("Too many arguments or unrecognized parameter passed to runif()");
4049100TF        if (!n_set) {
4054100TF        AV *restrict results = newAV();
4056100TF                av_extend(results, n - 1);
4061100TF                if (max < min) {
4063100TF                } else {
4066100TF                av_push(results, newSVnv(r));
4070100TFOUTPUT:
4071100TF    RETVAL
4077100TF          AUTO_SEED_PRNG();
4078100TF          if (items % 2 != 0)
407950TF                   croak("Usage: rbinom(n => 10, size => 100, prob => 0.5)");
408350TF          
40840TF          bool size_set = FALSE, prob_set = FALSE;
409250TF                   else if (strEQ(key, "prob")) { prob = SvNV(val); prob_set = TRUE; }
4093100TF                   else croak("rbinom: unknown argument '%s'", key);
4107100TF
4109100TF        }
50TF        }
4112100TF
411450TFhist(SV* x_sv, ...)
100TFhist(SV* x_sv, ...)
4120100TF
4126100TF                double *restrict x;
412950TF                double min_val = DBL_MAX, max_val = -DBL_MAX;
4132100TF                        SV**restrict tv = av_fetch(x_av, i, 0);
4134100TF                                 double val = SvNV(*tv);
50TF                                 double val = SvNV(*tv);
4137100TF                                 if (val > max_val) max_val = val;
413950TF                }
50TF                }
414650TF
4156100TF                                     break;
4163100TF                }
4170100TF                Newx(density, n_bins,     double);
4171100TF                Newx(counts,  n_bins,     size_t);
4172100TF
417750TF                }
50TF                }
418250TF                // 6. Build Return HashRef
418850TF                for (size_t i = 0; i <= n_bins; i++) {
100TF                for (size_t i = 0; i <= n_bins; i++) {
50TF                for (size_t i = 0; i <= n_bins; i++) {
419350TF                                 av_push(av_density, newSVnv(density[i]));
50TF                                 av_push(av_density, newSVnv(density[i]));
419450TF                        }
419650TF                hv_stores(res_hv, "breaks",  newRV_noinc((SV*)av_breaks));
419750TF                hv_stores(res_hv, "counts",  newRV_noinc((SV*)av_counts));
420050TF
50TF
420250TF                Safefree(x); Safefree(breaks); Safefree(mids);
100TF                Safefree(x); Safefree(breaks); Safefree(mids);
50TF                Safefree(x); Safefree(breaks); Safefree(mids);
4208100TF          RETVAL
50TF          RETVAL
420950TF
4213100TF                SV *restrict x_sv = NULL;
421750TF                /* --- 1. Consume first positional arg as 'x' if it's an array ref --- */
422150TF                }
422250TF
4224100TF                if ((items - arg_idx) % 2 != 0)
422650TF
50TF
50TF
4228100TF                         const char *restrict key = SvPV_nolen(ST(arg_idx));
423050TF
50TF
50TF
423950TF                if (n_raw == 0) croak("quantile: 'x' is empty");
424450TF                size_t n = 0;
50TF                size_t n = 0;
50TF                size_t n = 0;
424850TF                                 x[n++] = SvNV(*tv);
4253100TF                        croak("quantile: 'x' contains no valid numbers");
425550TF                // --- Sort Data for Quantile Math ---
50TF                // --- Sort Data for Quantile Math ---
50TF                // --- Sort Data for Quantile Math ---
425950TF                unsigned int n_probs = 5;
50TF                unsigned int n_probs = 5;
426050TF                double *restrict probs;
4261100TF
426350TF                        AV *restrict p_av = (AV*)SvRV(probs_sv);
50TF                        AV *restrict p_av = (AV*)SvRV(probs_sv);
50TF                        AV *restrict p_av = (AV*)SvRV(probs_sv);
427050TF                                     Safefree(x); Safefree(probs);
4272100TF                                 }
427350TF                        }
4274100TF                } else {
427850TF
50TF
50TF
428850TF                                 q = x[n - 1]; /* Prevent out-of-bounds mapping */
50TF                                 q = x[n - 1]; /* Prevent out-of-bounds mapping */
429250TF                                 /* Continuous sample quantile interpolation (Type 7) */
429450TF                                 unsigned int j = (unsigned int)h; /* floor via cast */
4295100TF                                 double gamma = h - j;
429650TF                                 q = (1.0 - gamma) * x[j] + gamma * x[j + 1];
4297100TF                        }
430150TF                        double pct = p * 100.0;
50TF                        double pct = p * 100.0;
50TF                        double pct = p * 100.0;
430950TF                        hv_store(res_hv, key, strlen(key), newSVnv(q), 0);
431650TF        }
4317100TF        OUTPUT:
432150TFdouble mean(...)
43240TF          double total = 0;
43250TF          size_t count = 0;
43260TF        CODE:
43280TF                        SV* restrict arg = ST(i);
43300TF                                AV* restrict av = (AV*)SvRV(arg);
43370TF                                     } else {
43380TF                                         croak("mean: undefined value at array ref index %zu (argument %zu)", j, i);
43410TF                        } else if (SvOK(arg)) {
4345100TF                                 croak("mean: undefined value at argument index %zu", i);
4346100TF                        }
4350100TF        OUTPUT:
4354100TF        PROTOTYPE: @
435650TF        HV *restrict counts;
4357100TF        HV *restrict originals;
437350TF                                if (tv && SvOK(*tv)) {
4375100TF                                        const char *restrict key = SvPV(*tv, klen);
100TF                                        const char *restrict key = SvPV(*tv, klen);
438050TF                                        if (cnt > max_count) max_count = cnt;
438250TF                                                 hv_store(originals, key, klen, newSVsv(*tv), 0);
438750TF                        }
50TF                        }
100TF                        }
438950TF                        STRLEN klen;
50TF                        STRLEN klen;
50TF                        STRLEN klen;
0TF                        STRLEN klen;
43910TF                        SV **restrict slot = hv_fetch(counts, key, klen, 1);
43930TF                        size_t cnt = SvOK(*slot) ? SvIV(*slot) + 1 : 1;
4402100TF        }
440450TF        if (arg_count == 0)
440850TF        while ((he = hv_iternext(counts))) {
50TF        while ((he = hv_iternext(counts))) {
50TF        while ((he = hv_iternext(counts))) {
441050TF                        STRLEN klen;
50TF                        STRLEN klen;
50TF                        STRLEN klen;
0TF                        STRLEN klen;
44120TF                        SV **restrict orig = hv_fetch(originals, key, klen, 0);
44140TF                }
44150TF        }
4426100TF                                 AV* restrict av = (AV*)SvRV(arg);
4428100TF                                 for (size_t j = 0; j < len; j++) {
50TF                                 for (size_t j = 0; j < len; j++) {
443050TF                                     if (tv && SvOK(*tv)) {
443250TF                                         count++;
50TF                                         count++;
50TF                                         count++;
4438100TF                                 total += SvNV(arg);
444850TF
50TF
4454100TF        CODE:
4460100TF                                size_t len = av_len(av) + 1;
446350TF                                  if (tv && SvOK(*tv)) {
4465100TF                                                double val = SvNV(*tv);
446750TF                                                mean += delta / count;
50TF                                                mean += delta / count;
447050TF                                                croak("sd: undefined value at array ref index %zu (argument %zu)", j, i);
50TF                                                croak("sd: undefined value at array ref index %zu (argument %zu)", j, i);
447750TF                                 mean += delta / count;
448050TF                                 croak("sd: undefined value at argument index %zu", i);
448150TF                        }
4487100TF
4494100TF        CODE:
449650TF                for (size_t i = 0; i < items; i++) {
450350TF                                          if (tv && SvOK(*tv)) {
4512100TF                                 }
451450TF                                 count++;
0TF                                 count++;
45170TF                                 mean += delta / count;
45190TF                        } else {
0TF                        } else {
452150TF                        }
452550TF        OUTPUT:
452650TF                RETVAL
4527100TF
452950TF        CODE:
0TF        CODE:
45320TF                SV*restrict y_sv = NULL;
45340TF                bool paired = FALSE, var_equal = FALSE;
0TF                bool paired = FALSE, var_equal = FALSE;
453950TF                // 1. Shift first positional argument as 'x' if it's an array reference
4544100TF
4545100TF                // 2. Shift second positional argument as 'y' if it's an array reference
4546100TF                if (arg_idx < items && SvROK(ST(arg_idx)) && SvTYPE(SvRV(ST(arg_idx))) == SVt_PVAV) {
4551100TF                // Ensure the remaining arguments form complete key-value pairs
455750TF                for (; arg_idx < items; arg_idx += 2) {
4558100TF                        const char*restrict key = SvPV_nolen(ST(arg_idx));
456050TF
457050TF
4577100TF                AV*restrict y_av = NULL;
4580100TF                        
4582100TF                        croak("t_test: 'conf_level' must be between 0 and 1");
458550TF                HV*restrict results = newHV();
45880TF                        double val = (tv && SvOK(*tv)) ? SvNV(*tv) : 0;
459550TF
100TF
50TF
459950TF                        if (paired && ny != nx) croak("t_test: Paired arrays must be same length");
4600100TF                        double mean_y = 0.0, M2_y = 0.0, var_y;
460450TF                                 double delta = val - mean_y;
0TF                                 double delta = val - mean_y;
460750TF                        }
100TF                        }
460950TF                        if (paired) {
0TF                        if (paired) {
4613100TF                                          SV**restrict dy_ptr = av_fetch(y_av, i, 0);
50TF                                          SV**restrict dy_ptr = av_fetch(y_av, i, 0);
4621100TF                                 double var_d = M2_d / (nx - 1);
4628100TF                        } else if (var_equal) {
463150TF                                 cint_est = mean_x - mean_y;
463250TF                                 std_err  = sqrt(pooled_var * (1.0 / nx + 1.0 / ny));
467550TF                hv_store(results, "df",        2, newSVnv(df),     0);
4677100TF                hv_store(results, "conf_int",  8, newRV_noinc((SV*)conf_int), 0);
4680100TF        OUTPUT:
468150TF                RETVAL
4684100TF        INIT:
4685100TF                if (!SvROK(p_sv) || SvTYPE(SvRV(p_sv)) != SVt_PVAV) {
100TF                if (!SvROK(p_sv) || SvTYPE(SvRV(p_sv)) != SVt_PVAV) {
469150TF                if (n == 0) {
469350TF                }
469550TF                char meth[64];
469750TF                for(unsigned short int i = 0; meth[i]; i++) meth[i] = tolower(meth[i]);
100TF                for(unsigned short int i = 0; meth[i]; i++) meth[i] = tolower(meth[i]);
470050TF                if (strstr(meth, "benjamini") && strstr(meth, "yekutieli")) strcpy(meth, "by");
4701100TF                if (strcmp(meth, "fdr") == 0) strcpy(meth, "bh");
470650TF                Newx(adj, n, double);
50TF                Newx(adj, n, double);
470850TF                for (size_t i = 0; i < n; i++) {
50TF                for (size_t i = 0; i < n; i++) {
4710100TF                        arr[i].p = (tv && SvOK(*tv)) ? SvNV(*tv) : 1.0;
47180TF                                double v = arr[i].p * n;
47200TF                        }
47210TF                } else if (strcmp(meth, "holm") == 0) {
47220TF                        double cummax = 0.0;
47240TF                                 double v = arr[i].p * (n - i);
0TF                                 double v = arr[i].p * (n - i);
0TF                                 double v = arr[i].p * (n - i);
47290TF                        double cummin = 1.0;
4740100TF                                adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
100TF                                adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
50TF                                adj[arr[i].orig_idx] = (cummin < 1.0) ? cummin : 1.0;
4744100TF                        for (size_t i = 1; i <= n; i++) q += 1.0 / i;
4745100TF                        double cummin = 1.0;
474650TF                        for (ssize_t i = n - 1; i >= 0; i--) {
4758100TF                                double temp = (n * arr[i].p) / (i + 1.0);
476050TF                                   min_val = temp;
0TF                                   min_val = temp;
47620TF                        }
0TF                        }
0TF                        }
0TF                        }
4766100TF                                 q_arr[i] = min_val;
50TF                                 q_arr[i] = min_val;
476750TF                        }
0TF                        }
0TF                        }
4773100TF                                 for (size_t k = 1; k < i2_len; k++) {
50TF                                 for (size_t k = 1; k < i2_len; k++) {
47740TF                                     double temp_q1 = (j * arr[n_mj + 1 + k].p) / (2.0 + k);
0TF                                     double temp_q1 = (j * arr[n_mj + 1 + k].p) / (2.0 + k);
0TF                                     double temp_q1 = (j * arr[n_mj + 1 + k].p) / (2.0 + k);
4780100TF                                 for (size_t i = 0; i <= n_mj; i++) {
50TF                                 for (size_t i = 0; i <= n_mj; i++) {
0TF                                 for (size_t i = 0; i <= n_mj; i++) {
4786100TF                                     q_arr[n_mj + 1 + i] = q_arr[n_mj];
50TF                                     q_arr[n_mj + 1 + i] = q_arr[n_mj];
0TF                                     q_arr[n_mj + 1 + i] = q_arr[n_mj];
4790100TF                                    if (pa[i] < q_arr[i]) {
50TF                                    if (pa[i] < q_arr[i]) {
47910TF                                       pa[i] = q_arr[i];
0TF                                       pa[i] = q_arr[i];
0TF                                       pa[i] = q_arr[i];
4796100TF                        for (size_t i = 0; i < n; i++) {
479750TF                                double v = (pa[i] > arr[i].p) ? pa[i] : arr[i].p;
0TF                                double v = (pa[i] > arr[i].p) ? pa[i] : arr[i].p;
479850TF                                if (v > 1.0) v = 1.0;
0TF                                if (v > 1.0) v = 1.0;
480750TF                        Safefree(arr); Safefree(adj);
480950TF                }
481150TF                EXTEND(SP, n);
50TF                EXTEND(SP, n);
4818100TFdouble median(...)
4819100TF        PROTOTYPE: @
4821100TF          size_t total_count = 0, k = 0;
482350TF          double median_val = 0.0;
50TF          double median_val = 0.0;
4825100TF          /* Pass 1: Count valid elements — die immediately on any undef */
482750TF                   SV* restrict arg = ST(i);
4828100TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
483850TF                       }
4839100TF                   } else if (SvOK(arg)) {
4851100TF          for (size_t i = 0; i < items; i++) {
485350TF                   if (SvROK(arg) && SvTYPE(SvRV(arg)) == SVt_PVAV) {
4855100TF                       size_t len = av_len(av) + 1;
485650TF                       for (size_t j = 0; j < len; j++) {
4861100TF                               Safefree(nums);
486650TF                       nums[k++] = SvNV(arg);
0TF                       nums[k++] = SvNV(arg);
486850TF                       Safefree(nums);
50TF                       Safefree(nums);
487650TF                   median_val = (nums[total_count / 2 - 1] + nums[total_count / 2]) / 2.0;
0TF                   median_val = (nums[total_count / 2 - 1] + nums[total_count / 2]) / 2.0;
4883100TF        OUTPUT:
488550TF
100TF
488650TFSV* cor(SV* x_sv, SV* y_sv = &PL_sv_undef, const char* method = "pearson")
4893100TF                        method);
489450TF
4899100TF        AV*restrict x_av = (AV*)SvRV(x_sv);
4902100TF
4906100TF                  SV**restrict fp = av_fetch(x_av, 0, 0);
490850TF                      x_is_matrix = 1;
4910100TF
100TF
4911100TF        // --- detect y ----------------------------
491250TF        bool has_y = (SvOK(y_sv) && SvROK(y_sv) &&
491850TF        bool y_is_matrix = 0;
4919100TF        if (has_y && ny > 0) {
4920100TF                SV**restrict fp = av_fetch(y_av, 0, 0);
4921100TF                if (fp && SvROK(*fp) && SvTYPE(SvRV(*fp)) == SVt_PVAV)
4922100TF                        y_is_matrix = 1;
492350TF        }
4936100TF                      if (nx < 2)
494750TF                          SV**restrict tv = av_fetch(x_av, i, 0);
50TF                          SV**restrict tv = av_fetch(x_av, i, 0);
494850TF                          double val = (tv && SvOK(*tv) && looks_like_number(*tv)) ? SvNV(*tv) : NAN;
4953100TF                          }
4955100TF                      for (size_t i = 0; i < ny; i++) {
4959100TF                          if (!isnan(val)) {
4960100TF                              if (isnan(y_first)) y_first = val;
4962100TF                          }
496450TF
4965100TF                      if (x_sd0 || y_sd0) {
497050TF
497350TF                      RETVAL = newSVnv(r);
4976100TF                  // -- resolve x matrix dimensions
4983100TF                      croak("cor: each row of x must be an ARRAY reference");
4984100TF
4985100TF                  size_t ncols_x = av_len((AV*)SvRV(*xr0)) + 1;
4986100TF                  if (ncols_x == 0) croak("cor: x matrix has zero columns");
498850TF                  size_t nrows   = nx;    /* observations */
499250TF                      SV**restrict rv = av_fetch(x_av, i, 0);
5000100TF                          SV**restrict rv = av_fetch(y_av, i, 0);
5001100TF                          if (!rv || !SvROK(*rv) || SvTYPE(SvRV(*rv)) != SVt_PVAV)
5003100TF                      }
5007100TF                  double **restrict col_x;
5009100TF
5015100TF                          SV**restrict rv = av_fetch(x_av, i, 0);
5016100TF                          AV*restrict  row = (AV*)SvRV(*rv);
5019100TF                          col_x[j][i] = val;
100TF                          col_x[j][i] = val;
5034100TF                  double **restrict col_y   = NULL;
5037100TF                  // 1 = cor(X) — result is symmetric
5039100TF                      // cross-correlation: X (nrows × p) vs Y (nrows × q)
100TF                      // cross-correlation: X (nrows × p) vs Y (nrows × q)
5042100TF                      if (ncols_y == 0) croak("cor: y matrix has zero columns");
505150TF                              AV*restrict  row = (AV*)SvRV(*rv);
505750TF                                  else if (val != first) sd0 = 0;
50TF                                  else if (val != first) sd0 = 0;
506050TF                          if (sd0) {
50TF                          if (sd0) {
50630TF                              for (size_t k = 0; k <= j; k++) Safefree(col_y[k]);
50670TF                      }
5071100TF                      symmetric = 1;
5075100TF                  // -- build cache for symmetric case: compute upper triangle, store results, mirror to lower triangle
508250TF                      rows_out[i] = newAV();
0TF                      rows_out[i] = newAV();
510250TF                          for (size_t j = 0; j < ncols_x; j++)
5112100TF                  }
5113100TF                  // push row AVs into result
5114100TF                  for (size_t i = 0; i < ncols_x; i++)
5116100TF                  Safefree(rows_out); rows_out = NULL;
5121100TF                      for (size_t j = 0; j < ncols_y; j++) Safefree(col_y[j]);
513550TF                size_t data_items = items;
51360TF                // 1. Parse Options Hash (if it exists as the last argument)
51370TF                if (items > 0) {
5145100TF                                     SV*restrict val_sv = *center_sv;
50TF                                     SV*restrict val_sv = *center_sv;
100TF                                     SV*restrict val_sv = *center_sv;
50TF                                     SV*restrict val_sv = *center_sv;
515350TF                                         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
515650TF                                             do_center_mean = FALSE; center_val = SvNV(val_sv);
5157100TF                                         } else if (SvTRUE(val_sv)) {
5167100TF                                     SV*restrict val_sv = *scale_sv;
517450TF                                         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
50TF                                         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
0TF                                         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
0TF                                         } else if (strcasecmp(str, "none") == 0 || strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0 || strcmp(str, "") == 0) {
518050TF                                             do_scale_sd = TRUE;
5184100TF                                     }
5188100TF                // 2. Detect if the input is a Matrix (Array of Arrays)
5189100TF                bool is_matrix = FALSE;
519050TF                if (data_items == 1) {
5194100TF                                 if (av_len(av) >= 0) {
519750TF                                         is_matrix = TRUE;
5200100TF                        }
5207100TF                        size_t nrow = av_len(mat_av) + 1, ncol = 0;
50TF                        size_t nrow = av_len(mat_av) + 1, ncol = 0;
5212100TF                        if (nrow == 0 || ncol == 0) croak("scale requires non-empty matrix");
524850TF                                         croak("scale needs >= 2 rows to calculate standard deviation for a matrix column");
525350TF                                         sum_sq += diff * diff;
525550TF                                     col_scale = sqrt(sum_sq / (nrow - 1));
525750TF                                 // Store scaled values back into the new matrix rows
525950TF                                     double centered = col_data[r] - col_center;
50TF                                     double centered = col_data[r] - col_center;
526250TF                                 }
5263100TF                                 Safefree(col_data);
52670TF                        EXTEND(SP, 1);
0TF                        EXTEND(SP, 1);
52690TF                } else {
0TF                } else {
52710TF                        // FLAT LIST MODE: Original functionality
52790TF                                        AV*restrict av = (AV*)SvRV(arg);
52820TF                                                SV**restrict tv = av_fetch(av, j, 0);
52830TF                                                if (tv && SvOK(*tv)) { total_count++; }
52840TF                                        }
52860TF                                        total_count++;
0TF                                        total_count++;
0TF                                        total_count++;
52920TF                                 SV*restrict arg = ST(i);
5303100TF                                 } else if (SvOK(arg)) {
100TF                                 } else if (SvOK(arg)) {
50TF                                 } else if (SvOK(arg)) {
5307100TF                        }
5308100TF                        if (do_center_mean) center_val = sum / total_count;
530950TF                        if (do_scale_sd) {
531750TF                                     sum_sq += diff * diff;
531850TF                                 }
531950TF                                 scale_val = sqrt(sum_sq / (total_count - 1));
532050TF                        }
0TF                        }
532150TF                        EXTEND(SP, total_count);
532250TF                        for (size_t i = 0; i < total_count; i++) {
0TF                        for (size_t i = 0; i < total_count; i++) {
532350TF                                double centered = nums[i] - center_val;
0TF                                double centered = nums[i] - center_val;
532550TF                                PUSHs(sv_2mortal(newSVnv(final_val)));
532650TF                        }
532850TF                }
50TF                }
5333100TF        // Basic check: must have an even number of arguments for key => value
5334100TF        if (items % 2 != 0) {
5336100TF        }
533850TF        size_t nrow = 0, ncol = 0;
50TF        size_t nrow = 0, ncol = 0;
5340100TF        // Parse named arguments
534250TF          char*restrict key = SvPV_nolen(ST(i));
5343100TF          SV*restrict val   = ST(i + 1);
535350TF                   byrow = SvTRUE(val);
5354100TF          } else {
537050TF          ncol = 1;
537250TF          ncol = (data_len + nrow - 1) / nrow;
5374100TF          nrow = (data_len + ncol - 1) / ncol;
537550TF        }
5380100TF        // Create the matrix (Array of Arrays)
538550TF        for (r = 0; r < nrow; r++) {
0TF        for (r = 0; r < nrow; r++) {
538650TF          row_ptrs[r] = newAV();
0TF          row_ptrs[r] = newAV();
539450TF          SV**restrict fetched = av_fetch(data_av, i % data_len, 0);
0TF          SV**restrict fetched = av_fetch(data_av, i % data_len, 0);
5401100TF                   c = i / nrow;
5403100TF          av_store(row_ptrs[r], c, val);
540450TF        }
540650TF        RETVAL = newRV_noinc((SV*)result_av);
5418100TF        char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL;
5419100TF        bool *restrict is_dummy = NULL;
5427100TF        HV *restrict data_hoa = NULL;
5437100TF        HE *restrict entry;
5445100TF          else if (strEQ(key, "data"))    data_sv = val;
5446100TF          else croak("lm: unknown argument '%s'", key);
5447100TF        }
5450100TF
50TF
5454100TF        ref = SvRV(data_sv);
5455100TF        if (SvTYPE(ref) == SVt_PVHV) {
545650TF          HV *restrict hv = (HV*)ref;
5477100TF                           row_hashes[i] = (HV*)SvRV(hv_iterval(hv, entry));
5481100TF          }
548350TF          AV *restrict av = (AV*)ref; n = av_len(av) + 1;
5485100TF          Newx(row_hashes, n, HV*);
5486100TF          for (i = 0; i < n; i++) {
5488100TF                   if (val && SvROK(*val) && SvTYPE(SvRV(*val)) == SVt_PVHV) {
548950TF                       row_hashes[i] = (HV*)SvRV(*val);
549650TF                   }
5497100TF          }
5498100TF        } else croak("lm: Data must be an Array or Hash reference");
5499100TF
5509100TF          for (i = 0; i < n; i++) Safefree(row_names[i]);
551050TF          Safefree(row_names); if (row_hashes) Safefree(row_hashes);
5527100TF                       continue;
5547100TF                       continue;
554850TF                   }
5552100TF                   }
555450TF                   if (p_idx[0] == '+' && p_idx[1] == '1' &&
5557100TF                       continue;
5558100TF                   }
5560100TF                   if (p_idx == rhs) {
5562100TF                       if (p_idx[0] == '1' && p_idx[1] == '+') { memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); continue; }
556450TF                   p_idx++;
5565100TF          }
557050TF          char *restrict p_idx;
557350TF          if (rhs[0] == '+') memmove(rhs, rhs + 1, strlen(rhs + 1) + 1);
5576100TF        }
5582100TF        while (chunk != NULL) {
5584100TF                   AV *cols = get_all_columns(data_hoa, row_hashes, n);
5585100TF                   for (size_t c = 0; c <= (size_t)av_len(cols); c++) {
5586100TF                       SV **col_sv = av_fetch(cols, c, 0);
558850TF                           const char *col_name = SvPV_nolen(*col_sv);
5594100TF                                   rhs_len += slen;
559650TF                           }
559850TF                   }
100TF                   }
5614100TF
5615100TF        if (has_intercept) { terms[num_terms++] = savepv("Intercept"); }
5616100TF
5623100TF                   }
5624100TF                   char *restrict star = strchr(chunk, '*');
5627100TF                       char *restrict left = chunk;
563150TF                       char *restrict c_r = strchr(right, '^');
5634100TF                       terms[num_terms++] = savepv(right);
5635100TF                       size_t inter_len = strlen(left) + strlen(right) + 2;
5639100TF                       char *restrict c_chunk = strchr(chunk, '^');
564450TF          }
100TF          }
5666100TF          if (is_column_categorical(data_hoa, row_hashes, n, uniq_terms[j])) {
566850TF                   unsigned int num_levels = 0, levels_cap = 8;
50TF                   unsigned int num_levels = 0, levels_cap = 8;
5672100TF                       if (str_val) {
5674100TF                           for (l = 0; l < num_levels; l++) { if (strcmp(levels[l], str_val) == 0) { found = TRUE; break; } }
5676100TF                               if (num_levels >= levels_cap) { levels_cap *= 2; Renew(levels, levels_cap, char*); }
5689100TF                               Renew(exp_terms, exp_cap, char*); Renew(is_dummy, exp_cap, bool);
5690100TF                               Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
5691100TF                           }
5693100TF                           exp_terms[p_exp] = (char*)safemalloc(t_len);
5700100TF                       for (l = 0; l < num_levels; l++) Safefree(levels[l]);
570350TF                       Safefree(levels);
5714100TF        // ========================================================================
5721100TF          bool row_ok = TRUE;
572250TF          double *restrict row_x = (double*)safemalloc(p * sizeof(double));
572550TF                       row_x[j] = 1.0;
50TF                       row_x[j] = 1.0;
572750TF                       char *restrict str_val = get_data_string_alloc(data_hoa, row_hashes, i, dummy_base[j]);
573250TF                   } else {
5736100TF          }
573850TF
574150TF          valid_row_names[valid_n] = row_names[i];
50TF          valid_row_names[valid_n] = row_names[i];
50TF          valid_row_names[valid_n] = row_names[i];
50TF          valid_row_names[valid_n] = row_names[i];
574850TF          for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
50TF          for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
574950TF          for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
50TF          for (i = 0; i < num_uniq; i++) Safefree(uniq_terms[i]); Safefree(uniq_terms);
575050TF          for (j = 0; j < p_exp; j++) {
50TF          for (j = 0; j < p_exp; j++) {
575150TF                   Safefree(exp_terms[j]);
50TF                   Safefree(exp_terms[j]);
575550TF          Safefree(X); Safefree(Y); Safefree(valid_row_names);
575850TF        }
576150TF        // PHASE 5: OLS Math
50TF        // PHASE 5: OLS Math
5764100TF        for (i = 0; i < p; i++)
5765100TF          for (j = 0; j < p; j++) {
576850TF                   XtX[i * p + j] = sum;
50TF                   XtX[i * p + j] = sum;
576950TF          }
50TF          }
577450TF          XtY[i] = sum;
50TF          XtY[i] = sum;
577950TF        for (i = 0; i < p; i++) {
578050TF          if (aliased[i]) { beta[i] = NAN; }
578550TF          }
578650TF        }
578750TF
50TF
578850TF        // ========================================================================
50TF        // ========================================================================
578950TF        // PHASE 6: Metrics & Cleanup
50TF        // PHASE 6: Metrics & Cleanup
579050TF        // ========================================================================
50TF        // ========================================================================
583250TF          r_squared = 0.0; adj_r_squared = 0.0;
5833100TF        }
5837100TF          av_push(terms_av, newSVpv(exp_terms[j], 0));
5838100TF          HV *restrict row_hv = newHV();
5839100TF          if (aliased[j]) {
584050TF                   hv_store(row_hv, "Estimate",   8,  newSVpv("NaN", 0), 0);
100TF                   hv_store(row_hv, "Estimate",   8,  newSVpv("NaN", 0), 0);
5841100TF                   hv_store(row_hv, "Std. Error", 10, newSVpv("NaN", 0), 0);
5842100TF                   hv_store(row_hv, "t value",    7,  newSVpv("NaN", 0), 0);
584350TF                   hv_store(row_hv, "Pr(>|t|)",   8,  newSVpv("NaN", 0), 0);
58440TF          } else {
58450TF                   double se    = sqrt(rse_sq * XtX[j * p + j]);
5849100TF                   hv_store(row_hv, "Std. Error", 10, newSVnv(se),      0);
50TF                   hv_store(row_hv, "Std. Error", 10, newSVnv(se),      0);
585050TF                   hv_store(row_hv, "t value",    7,  newSVnv(t_val),   0);
50TF                   hv_store(row_hv, "t value",    7,  newSVnv(t_val),   0);
5851100TF                   hv_store(row_hv, "Pr(>|t|)",   8,  newSVnv(p_val),   0);
50TF                   hv_store(row_hv, "Pr(>|t|)",   8,  newSVnv(p_val),   0);
585250TF          }
50TF          }
5853100TF          hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
50TF          hv_store(summary_hv, exp_terms[j], strlen(exp_terms[j]), newRV_noinc((SV*)row_hv), 0);
5856100TF        hv_store(res_hv, "coefficients",  12, newRV_noinc((SV*)coef_hv),   0);
585750TF        hv_store(res_hv, "fitted.values", 13, newRV_noinc((SV*)fitted_hv), 0);
5858100TF        hv_store(res_hv, "residuals",      9, newRV_noinc((SV*)resid_hv),  0);
585950TF        hv_store(res_hv, "df.residual",   11, newSVuv(df_res),             0);
586050TF        hv_store(res_hv, "rank",           4, newSVuv(final_rank),         0);
586250TF        hv_store(res_hv, "summary",        7, newRV_noinc((SV*)summary_hv),0);
5866100TF        if (!isnan(f_stat)) {
586750TF          AV *fstat_av = newAV();
586850TF          av_push(fstat_av, newSVnv(f_stat));
50TF          av_push(fstat_av, newSVnv(f_stat));
5869100TF          av_push(fstat_av, newSViv(numdf));
50TF          av_push(fstat_av, newSViv(numdf));
5870100TF          av_push(fstat_av, newSViv(df_res));
5871100TF          hv_store(res_hv, "fstatistic", 10, newRV_noinc((SV*)fstat_av), 0);
100TF          hv_store(res_hv, "fstatistic", 10, newRV_noinc((SV*)fstat_av), 0);
5872100TF          hv_store(res_hv, "f.pvalue",    8, newSVnv(f_pvalue),          0);
50TF          hv_store(res_hv, "f.pvalue",    8, newSVnv(f_pvalue),          0);
50TF          hv_store(res_hv, "f.pvalue",    8, newSVnv(f_pvalue),          0);
5873100TF        }
50TF        }
5874100TF
587650TF        for (i = 0; i < num_terms; i++) Safefree(terms[i]); Safefree(terms);
587850TF        for (j = 0; j < p_exp; j++) {
0TF        for (j = 0; j < p_exp; j++) {
5879100TF          Safefree(exp_terms[j]);
5881100TF        }
58850TF        if (row_hashes) Safefree(row_hashes);
58870TF        RETVAL = newRV_noinc((SV*)res_hv);
58890TFOUTPUT:
58930TF        double from
58950TF        double by
0TF        double by
58960TFPPCODE:
58980TF                //Handle the zero 'by' case
59020TF                                 mPUSHn(from);
59040TF                        } else {
59060TF                        }
5918100TF                size_t n_elements = (n_elements_d + 1e-10) + 1;
100TF                size_t n_elements = (n_elements_d + 1e-10) + 1;
5920100TF                EXTEND(SP, n_elements);
100TF                EXTEND(SP, n_elements);
5921100TF                for (size_t i = 0; i < n_elements; i++) {
593550TF          int arg_start = 0;
100TF          int arg_start = 0;
5937100TF          // Check if the first argument is a simple integer (rnorm(33))
593950TF                   n = (unsigned int)SvUV(ST(0));
5943100TF          // --- Parse remaining named arguments from the flat stack ---
50TF          // --- Parse remaining named arguments from the flat stack ---
5944100TF          if ((items - arg_start) % 2 != 0) {
594550TF                   croak("Usage: rnorm(n), rnorm(n => 10, mean => 0, sd => 1), or rnorm(33, mean => 0)");
5949100TF                   const char* restrict key = SvPV_nolen(ST(i));
5952100TF                   if      (strEQ(key, "n"))    n    = (unsigned int)SvUV(val);
595350TF                   else if (strEQ(key, "mean")) mean = SvNV(val);
59540TF                   else if (strEQ(key, "sd"))   sd   = SvNV(val);
5958100TF          if (sd < 0.0) croak("rnorm: standard deviation must be non-negative");
50TF          if (sd < 0.0) croak("rnorm: standard deviation must be non-negative");
50TF          if (sd < 0.0) croak("rnorm: standard deviation must be non-negative");
5973100TF                        double mul = sqrt(-2.0 * log(s) / s);
597450TF                        // Box-Muller generates two independent values per iteration
50TF                        // Box-Muller generates two independent values per iteration
5982100TF        }
598450TF        RETVAL
50TF        RETVAL
598850TF        SV* formula_sv
599250TF        SV *restrict orig_data_sv = data_sv;
5998100TF        if (!formula_sv || !SvOK(formula_sv) || SvCUR(formula_sv) == 0) {
600050TF                     croak("aov: Without a formula, data must be a HashRef of ArrayRefs (mimicking R's named list)");
6005100TF                 HV *restrict stacked_hv = newHV();
600750TF                 AV *restrict grp_av = newAV();
50TF                 AV *restrict grp_av = newAV();
50TF                 AV *restrict grp_av = newAV();
602150TF                                 av_push(val_av, newSVsv(*v));
50TF                                 av_push(val_av, newSVsv(*v));
50TF                                 av_push(val_av, newSVsv(*v));
602350TF                             }
50TF                             }
50TF                             }
603050TF                 
603150TF                 hv_stores(stacked_hv, "Value", newRV_noinc((SV*)val_av));
6040100TF
604350TF
50TF
50TF
604450TF        char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL, **restrict parent_term = NULL;
50TF        char **restrict terms = NULL, **restrict uniq_terms = NULL, **restrict exp_terms = NULL, **restrict parent_term = NULL;
6049100TF        size_t n = 0, valid_n = 0, i, j;
606850TF                HV*restrict hv = (HV*)ref;
50TF                HV*restrict hv = (HV*)ref;
60700TF                entry = hv_iternext(hv);
60710TF                if (entry) {
0TF                if (entry) {
60740TF                                  data_hoa = hv;
6087100TF                                      row_names[i] = savepv(hv_iterkey(entry, &len));
6096100TF                Newx(row_names, n, char*);
609750TF                Newx(row_hashes, n, HV*);
610450TF                                  row_names[i] = savepv(buf);
6124100TF                  croak("aov: invalid formula, missing '~'");
612550TF        }
50TF        }
613250TF        while ((p_idx = strstr(rhs, "+0")) != NULL) { has_intercept = FALSE; memmove(p_idx, p_idx + 2, strlen(p_idx + 2) + 1); }
616250TF                                  }
50TF                                  }
50TF                                  }
616850TF                                  if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
50TF                                  if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
50TF                                  if (rhs_len > 0) { strcat(rhs_expanded, "+"); rhs_len++; }
617450TF        }
6179100TF        Newx(is_dummy, exp_cap, bool); Newx(is_interact, exp_cap, bool);
618350TF        if (strlen(rhs_expanded) > 0) {
618450TF                chunk = strtok(rhs_expanded, "+");
6185100TF                while (chunk != NULL) {
618650TF                         if (num_terms >= term_cap - 3) {
0TF                         if (num_terms >= term_cap - 3) {
61870TF                                  term_cap *= 2;
619250TF                                  *star = '\0';
50TF                                  *star = '\0';
50TF                                  *star = '\0';
619450TF                                  char *right = star + 1;
50TF                                  char *right = star + 1;
50TF                                  char *right = star + 1;
619750TF                                  char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
50TF                                  char *restrict c_r = strchr(right, '^'); if (c_r && strncmp(right, "I(", 2) != 0) *c_r = '\0';
619950TF                                  terms[num_terms++] = savepv(right);
50TF                                  terms[num_terms++] = savepv(right);
50TF                                  terms[num_terms++] = savepv(right);
6210100TF        }
621250TF        for (i = 0; i < num_terms; i++) {
50TF        for (i = 0; i < num_terms; i++) {
50TF        for (i = 0; i < num_terms; i++) {
621450TF                  for (size_t k = 0; k < num_uniq; k++) {
50TF                  for (size_t k = 0; k < num_uniq; k++) {
6225100TF                if (p_exp + 64 >= exp_cap) {
622750TF                        Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
50TF                        Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
50TF                        Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);
622950TF                        Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
50TF                        Renew(dummy_base, exp_cap, char*); Renew(dummy_level, exp_cap, char*);
6238100TF                        p_exp++;
6239100TF                        continue;
6246100TF                        left[colon - uniq_terms[j]] = '\0';
625550TF
625750TF                                Safefree(l_indices); Safefree(r_indices);
626450TF                                                      Renew(exp_terms, exp_cap, char*); Renew(parent_term, exp_cap, char*);