1use greeners_core::error::GreenersError;
2use greeners_core::linalg::LinalgInverse as _;
3use greeners_core::types::InferenceType;
4use indexmap::IndexMap;
5use ndarray::{Array1, Array2};
6use statrs::distribution::{ContinuousCDF, Normal};
7use std::fmt;
8
9#[derive(Debug)]
11pub struct ConditionalResult {
12 pub model_name: String,
13 pub params: Array1<f64>,
15 pub std_errors: Array1<f64>,
16 pub z_values: Array1<f64>,
17 pub p_values: Array1<f64>,
18 pub log_likelihood: f64,
19 pub aic: f64,
20 pub bic: f64,
21 pub n_obs: usize,
22 pub n_groups: usize,
23 pub iterations: usize,
24 pub converged: bool,
25 pub inference_type: InferenceType,
26 pub variable_names: Option<Vec<String>>,
27}
28
29impl fmt::Display for ConditionalResult {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 writeln!(f, "\n{:=^78}", format!(" {} Results ", self.model_name))?;
32 writeln!(
33 f,
34 "{:<20} {:>15} || {:<20} {:>15.4}",
35 "No. Observations:", self.n_obs, "Log-Likelihood:", self.log_likelihood
36 )?;
37 writeln!(
38 f,
39 "{:<20} {:>15} || {:<20} {:>15.4}",
40 "No. Groups:", self.n_groups, "AIC:", self.aic
41 )?;
42
43 writeln!(f, "\n{:-^78}", "")?;
44 writeln!(
45 f,
46 "{:<12} {:>10} {:>10} {:>8} {:>8}",
47 "", "coef", "std err", "z", "P>|z|"
48 )?;
49 writeln!(f, "{:-^78}", "")?;
50
51 for i in 0..self.params.len() {
52 let name = self
53 .variable_names
54 .as_ref()
55 .and_then(|n| n.get(i).cloned())
56 .unwrap_or_else(|| format!("x{}", i));
57 writeln!(
58 f,
59 "{:<12} {:>10.4} {:>10.4} {:>8.3} {:>8.3}",
60 name, self.params[i], self.std_errors[i], self.z_values[i], self.p_values[i]
61 )?;
62 }
63
64 writeln!(f, "{:=^78}", "")
65 }
66}
67
68impl ConditionalResult {
69 pub fn model_stats(&self) -> (f64, f64, f64) {
71 (self.aic, self.bic, self.log_likelihood)
72 }
73}
74
75pub struct ConditionalLogit;
80
81impl ConditionalLogit {
82 pub fn fit(
85 y: &Array1<f64>,
86 x: &Array2<f64>,
87 groups: &[usize],
88 ) -> Result<ConditionalResult, GreenersError> {
89 Self::fit_with_names(y, x, groups, None)
90 }
91
92 pub fn fit_with_names(
93 y: &Array1<f64>,
94 x: &Array2<f64>,
95 groups: &[usize],
96 variable_names: Option<Vec<String>>,
97 ) -> Result<ConditionalResult, GreenersError> {
98 let n = y.len();
99 let k = x.ncols();
100
101 if n != groups.len() {
102 return Err(GreenersError::ShapeMismatch(
103 "y and groups must have same length".into(),
104 ));
105 }
106
107 let mut group_map: IndexMap<usize, Vec<usize>> = IndexMap::new();
109 for (i, &g) in groups.iter().enumerate() {
110 group_map.entry(g).or_default().push(i);
111 }
112
113 let valid_groups: Vec<Vec<usize>> = group_map
115 .values()
116 .filter(|indices| {
117 let sum: f64 = indices.iter().map(|&i| y[i]).sum();
118 let n_g = indices.len() as f64;
119 sum > 0.5 && sum < n_g - 0.5
120 })
121 .cloned()
122 .collect();
123
124 if valid_groups.is_empty() {
125 return Err(GreenersError::InvalidOperation(
126 "No groups with variation in y".into(),
127 ));
128 }
129
130 let n_groups = valid_groups.len();
131
132 let mut beta = Array1::<f64>::zeros(k);
134 let max_iter = 100;
135 let tol = 1e-6;
136 let mut converged = false;
137 let mut iter = 0;
138 let mut log_likelihood = 0.0;
139
140 for iteration in 0..max_iter {
141 iter = iteration + 1;
142
143 let mut gradient = Array1::<f64>::zeros(k);
144 let mut hessian = Array2::<f64>::zeros((k, k));
145 log_likelihood = 0.0;
146
147 for group_indices in &valid_groups {
148 let n_g = group_indices.len();
149 let s_g: usize = group_indices.iter().map(|&i| y[i] as usize).sum();
150
151 if n_g <= 20 && s_g <= 10 {
154 let xb: Vec<f64> = group_indices.iter().map(|&i| x.row(i).dot(&beta)).collect();
156
157 let mut x_obs = Array1::<f64>::zeros(k);
159 for &i in group_indices {
160 if y[i] > 0.5 {
161 x_obs = &x_obs + &x.row(i).to_owned();
162 }
163 }
164
165 let combos = combinations(n_g, s_g);
167 let mut e_x = Array1::<f64>::zeros(k);
168 let mut e_xx = Array2::<f64>::zeros((k, k));
169
170 let combo_sums: Vec<f64> = combos
172 .iter()
173 .map(|combo| combo.iter().map(|&j| xb[j]).sum::<f64>())
174 .collect();
175
176 let max_sum = combo_sums.iter().copied().fold(f64::NEG_INFINITY, f64::max);
177
178 let mut total_w = 0.0;
179 let mut weighted_x = vec![Array1::<f64>::zeros(k); combos.len()];
180
181 for (ci, combo) in combos.iter().enumerate() {
182 let w = (combo_sums[ci] - max_sum).exp();
183 total_w += w;
184
185 let mut x_combo = Array1::<f64>::zeros(k);
186 for &j in combo {
187 x_combo = &x_combo + &x.row(group_indices[j]).to_owned();
188 }
189 weighted_x[ci] = x_combo;
190 }
191
192 let log_denom = max_sum + total_w.ln();
193 let obs_sum: f64 = group_indices
194 .iter()
195 .filter(|&&i| y[i] > 0.5)
196 .map(|&i| x.row(i).dot(&beta))
197 .sum();
198
199 log_likelihood += obs_sum - log_denom;
200
201 for (ci, _combo) in combos.iter().enumerate() {
203 let w = (combo_sums[ci] - max_sum).exp() / total_w;
204 let x_c = &weighted_x[ci];
205 e_x = &e_x + &(x_c * w);
206
207 for a in 0..k {
208 for b in 0..k {
209 e_xx[[a, b]] += w * x_c[a] * x_c[b];
210 }
211 }
212 }
213
214 gradient = &gradient + &(&x_obs - &e_x);
216
217 for a in 0..k {
219 for b in 0..k {
220 hessian[[a, b]] -= e_xx[[a, b]] - e_x[a] * e_x[b];
221 }
222 }
223 } else {
224 let xb: Vec<f64> = group_indices.iter().map(|&i| x.row(i).dot(&beta)).collect();
227 let exp_xb: Vec<f64> = xb.iter().map(|v| v.exp()).collect();
228 let sum_exp: f64 = exp_xb.iter().sum();
229
230 for (j_idx, &i) in group_indices.iter().enumerate() {
231 let p_j = exp_xb[j_idx] / sum_exp;
232 let diff = y[i] - s_g as f64 * p_j;
233 for kk in 0..k {
234 gradient[kk] += diff * x[[i, kk]];
235 }
236
237 for kk in 0..k {
238 for ll in 0..k {
239 hessian[[kk, ll]] -=
240 s_g as f64 * p_j * (1.0 - p_j) * x[[i, kk]] * x[[i, ll]];
241 }
242 }
243 }
244
245 for (j_idx, &i) in group_indices.iter().enumerate() {
247 if y[i] > 0.5 {
248 log_likelihood += (exp_xb[j_idx] / sum_exp).max(1e-15).ln();
249 }
250 }
251 }
252 }
253
254 let neg_hessian = -&hessian;
256 let inv_neg_hessian = match neg_hessian.inv() {
257 Ok(m) => m,
258 Err(_) => return Err(GreenersError::OptimizationFailed),
259 };
260
261 let change = inv_neg_hessian.dot(&gradient);
262 beta = &beta + &change;
263
264 if change.mapv(|v| v.powi(2)).sum().sqrt() < tol {
265 converged = true;
266 break;
267 }
268 }
269
270 let mut final_hessian = Array2::<f64>::zeros((k, k));
272 for group_indices in &valid_groups {
273 let n_g = group_indices.len();
274 let s_g: usize = group_indices.iter().map(|&i| y[i] as usize).sum();
275
276 if n_g <= 20 && s_g <= 10 {
277 let xb: Vec<f64> = group_indices.iter().map(|&i| x.row(i).dot(&beta)).collect();
278 let combos = combinations(n_g, s_g);
279 let combo_sums: Vec<f64> = combos
280 .iter()
281 .map(|combo| combo.iter().map(|&j| xb[j]).sum::<f64>())
282 .collect();
283 let max_sum = combo_sums.iter().copied().fold(f64::NEG_INFINITY, f64::max);
284
285 let mut total_w = 0.0;
286 let mut weighted_x = vec![Array1::<f64>::zeros(k); combos.len()];
287 for (ci, combo) in combos.iter().enumerate() {
288 let w = (combo_sums[ci] - max_sum).exp();
289 total_w += w;
290 let mut x_combo = Array1::<f64>::zeros(k);
291 for &j in combo {
292 x_combo = &x_combo + &x.row(group_indices[j]).to_owned();
293 }
294 weighted_x[ci] = x_combo;
295 }
296
297 let mut e_x = Array1::<f64>::zeros(k);
298 let mut e_xx = Array2::<f64>::zeros((k, k));
299 for (ci, _) in combos.iter().enumerate() {
300 let w = (combo_sums[ci] - max_sum).exp() / total_w;
301 let x_c = &weighted_x[ci];
302 e_x = &e_x + &(x_c * w);
303 for a in 0..k {
304 for b in 0..k {
305 e_xx[[a, b]] += w * x_c[a] * x_c[b];
306 }
307 }
308 }
309
310 for a in 0..k {
311 for b in 0..k {
312 final_hessian[[a, b]] -= e_xx[[a, b]] - e_x[a] * e_x[b];
313 }
314 }
315 } else {
316 let exp_xb: Vec<f64> = group_indices
317 .iter()
318 .map(|&i| x.row(i).dot(&beta).exp())
319 .collect();
320 let sum_exp: f64 = exp_xb.iter().sum();
321
322 for (j_idx, &i) in group_indices.iter().enumerate() {
323 let p_j = exp_xb[j_idx] / sum_exp;
324 for kk in 0..k {
325 for ll in 0..k {
326 final_hessian[[kk, ll]] -=
327 s_g as f64 * p_j * (1.0 - p_j) * x[[i, kk]] * x[[i, ll]];
328 }
329 }
330 }
331 }
332 }
333
334 let cov_matrix = (-&final_hessian).inv().unwrap_or(Array2::eye(k) * 1e-4);
335 let std_errors: Array1<f64> = (0..k).map(|i| cov_matrix[[i, i]].max(0.0).sqrt()).collect();
336
337 let normal_dist = Normal::standard();
338 let z_values = &beta / std_errors.mapv(|s| if s > 1e-15 { s } else { 1.0 });
339 let p_values = z_values.mapv(|z| 2.0 * (1.0 - normal_dist.cdf(z.abs())));
340
341 let k_f = k as f64;
342 let aic = -2.0 * log_likelihood + 2.0 * k_f;
343 let bic = -2.0 * log_likelihood + k_f * (n as f64).ln();
344
345 Ok(ConditionalResult {
346 model_name: "Conditional Logit".to_string(),
347 params: beta,
348 std_errors,
349 z_values,
350 p_values,
351 log_likelihood,
352 aic,
353 bic,
354 n_obs: n,
355 n_groups,
356 iterations: iter,
357 converged,
358 inference_type: InferenceType::Normal,
359 variable_names,
360 })
361 }
362}
363
364pub struct ConditionalPoisson;
369
370impl ConditionalPoisson {
371 pub fn fit(
372 y: &Array1<f64>,
373 x: &Array2<f64>,
374 groups: &[usize],
375 ) -> Result<ConditionalResult, GreenersError> {
376 Self::fit_with_names(y, x, groups, None)
377 }
378
379 pub fn fit_with_names(
380 y: &Array1<f64>,
381 x: &Array2<f64>,
382 groups: &[usize],
383 variable_names: Option<Vec<String>>,
384 ) -> Result<ConditionalResult, GreenersError> {
385 let n = y.len();
386 let k = x.ncols();
387
388 if n != groups.len() {
389 return Err(GreenersError::ShapeMismatch(
390 "y and groups must have same length".into(),
391 ));
392 }
393
394 let mut group_map: IndexMap<usize, Vec<usize>> = IndexMap::new();
395 for (i, &g) in groups.iter().enumerate() {
396 group_map.entry(g).or_default().push(i);
397 }
398
399 let valid_groups: Vec<Vec<usize>> = group_map
401 .values()
402 .filter(|indices| {
403 let sum: f64 = indices.iter().map(|&i| y[i]).sum();
404 sum > 0.5
405 })
406 .cloned()
407 .collect();
408
409 if valid_groups.is_empty() {
410 return Err(GreenersError::InvalidOperation(
411 "No groups with positive counts".into(),
412 ));
413 }
414
415 let n_groups = valid_groups.len();
416
417 let mut beta = Array1::<f64>::zeros(k);
420 let max_iter = 100;
421 let tol = 1e-6;
422 let mut converged = false;
423 let mut iter = 0;
424 let mut log_likelihood = 0.0;
425
426 for iteration in 0..max_iter {
427 iter = iteration + 1;
428
429 let mut gradient = Array1::<f64>::zeros(k);
430 let mut hessian = Array2::<f64>::zeros((k, k));
431 log_likelihood = 0.0;
432
433 for group_indices in &valid_groups {
434 let s_g: f64 = group_indices.iter().map(|&i| y[i]).sum();
435
436 let exp_xb: Vec<f64> = group_indices
437 .iter()
438 .map(|&i| x.row(i).dot(&beta).exp())
439 .collect();
440 let sum_exp: f64 = exp_xb.iter().sum();
441
442 for (j_idx, &i) in group_indices.iter().enumerate() {
444 if y[i] > 0.0 {
445 log_likelihood += y[i] * (exp_xb[j_idx] / sum_exp).max(1e-15).ln();
446 }
447 }
448
449 let mut e_x = Array1::<f64>::zeros(k);
451 for (j_idx, &i) in group_indices.iter().enumerate() {
452 let p_j = exp_xb[j_idx] / sum_exp;
453 for kk in 0..k {
454 e_x[kk] += p_j * x[[i, kk]];
455 }
456 }
457
458 for &i in group_indices {
460 for kk in 0..k {
461 gradient[kk] += y[i] * (x[[i, kk]] - e_x[kk]);
462 }
463 }
464
465 let mut e_xx = Array2::<f64>::zeros((k, k));
467 for (j_idx, &i) in group_indices.iter().enumerate() {
468 let p_j = exp_xb[j_idx] / sum_exp;
469 for a in 0..k {
470 for b in 0..k {
471 e_xx[[a, b]] += p_j * x[[i, a]] * x[[i, b]];
472 }
473 }
474 }
475
476 for a in 0..k {
477 for b in 0..k {
478 hessian[[a, b]] -= s_g * (e_xx[[a, b]] - e_x[a] * e_x[b]);
479 }
480 }
481 }
482
483 let neg_hessian = -&hessian;
484 let inv_neg_hessian = match neg_hessian.inv() {
485 Ok(m) => m,
486 Err(_) => return Err(GreenersError::OptimizationFailed),
487 };
488
489 let change = inv_neg_hessian.dot(&gradient);
490 beta = &beta + &change;
491
492 if change.mapv(|v| v.powi(2)).sum().sqrt() < tol {
493 converged = true;
494 break;
495 }
496 }
497
498 let mut final_hessian = Array2::<f64>::zeros((k, k));
500 for group_indices in &valid_groups {
501 let s_g: f64 = group_indices.iter().map(|&i| y[i]).sum();
502 let exp_xb: Vec<f64> = group_indices
503 .iter()
504 .map(|&i| x.row(i).dot(&beta).exp())
505 .collect();
506 let sum_exp: f64 = exp_xb.iter().sum();
507
508 let mut e_x = Array1::<f64>::zeros(k);
509 let mut e_xx = Array2::<f64>::zeros((k, k));
510 for (j_idx, &i) in group_indices.iter().enumerate() {
511 let p_j = exp_xb[j_idx] / sum_exp;
512 for kk in 0..k {
513 e_x[kk] += p_j * x[[i, kk]];
514 }
515 for a in 0..k {
516 for b in 0..k {
517 e_xx[[a, b]] += p_j * x[[i, a]] * x[[i, b]];
518 }
519 }
520 }
521
522 for a in 0..k {
523 for b in 0..k {
524 final_hessian[[a, b]] -= s_g * (e_xx[[a, b]] - e_x[a] * e_x[b]);
525 }
526 }
527 }
528
529 let cov_matrix = (-&final_hessian).inv().unwrap_or(Array2::eye(k) * 1e-4);
530 let std_errors: Array1<f64> = (0..k).map(|i| cov_matrix[[i, i]].max(0.0).sqrt()).collect();
531
532 let normal_dist = Normal::standard();
533 let z_values = &beta / std_errors.mapv(|s| if s > 1e-15 { s } else { 1.0 });
534 let p_values = z_values.mapv(|z| 2.0 * (1.0 - normal_dist.cdf(z.abs())));
535
536 let k_f = k as f64;
537 let aic = -2.0 * log_likelihood + 2.0 * k_f;
538 let bic = -2.0 * log_likelihood + k_f * (n as f64).ln();
539
540 Ok(ConditionalResult {
541 model_name: "Conditional Poisson".to_string(),
542 params: beta,
543 std_errors,
544 z_values,
545 p_values,
546 log_likelihood,
547 aic,
548 bic,
549 n_obs: n,
550 n_groups,
551 iterations: iter,
552 converged,
553 inference_type: InferenceType::Normal,
554 variable_names,
555 })
556 }
557}
558
559pub struct ConditionalMNLogit;
564
565impl ConditionalMNLogit {
566 pub fn fit(
573 y: &Array1<f64>,
574 x: &Array2<f64>,
575 groups: &[usize],
576 _n_alts: usize,
577 ) -> Result<ConditionalResult, GreenersError> {
578 Self::fit_with_names(y, x, groups, _n_alts, None)
579 }
580
581 pub fn fit_with_names(
582 y: &Array1<f64>,
583 x: &Array2<f64>,
584 groups: &[usize],
585 _n_alts: usize,
586 variable_names: Option<Vec<String>>,
587 ) -> Result<ConditionalResult, GreenersError> {
588 let n_rows = x.nrows();
589 let k = x.ncols();
590
591 if n_rows != groups.len() {
592 return Err(GreenersError::ShapeMismatch(
593 "x rows and groups must have same length".into(),
594 ));
595 }
596
597 let mut group_map: IndexMap<usize, Vec<usize>> = IndexMap::new();
599 for (i, &g) in groups.iter().enumerate() {
600 group_map.entry(g).or_default().push(i);
601 }
602
603 let mut choice_sets: Vec<Vec<usize>> = group_map.values().cloned().collect();
604 choice_sets.sort_by_key(|v| v[0]);
605
606 let n_occasions = y.len();
607 if choice_sets.len() != n_occasions {
608 return Err(GreenersError::ShapeMismatch(
609 "Number of groups must equal length of y".into(),
610 ));
611 }
612
613 let mut beta = Array1::<f64>::zeros(k);
615 let max_iter = 100;
616 let tol = 1e-6;
617 let mut converged = false;
618 let mut iter = 0;
619 let mut log_likelihood = 0.0;
620
621 for iteration in 0..max_iter {
622 iter = iteration + 1;
623 let mut gradient = Array1::<f64>::zeros(k);
624 let mut hessian = Array2::<f64>::zeros((k, k));
625 log_likelihood = 0.0;
626
627 for (occ, indices) in choice_sets.iter().enumerate() {
628 let chosen = y[occ] as usize;
629
630 let xb: Vec<f64> = indices.iter().map(|&i| x.row(i).dot(&beta)).collect();
632 let max_xb = xb.iter().copied().fold(f64::NEG_INFINITY, f64::max);
633 let exp_xb: Vec<f64> = xb.iter().map(|&v| (v - max_xb).exp()).collect();
634 let sum_exp: f64 = exp_xb.iter().sum();
635
636 if chosen < indices.len() {
638 log_likelihood += xb[chosen] - max_xb - sum_exp.ln();
639 }
640
641 let probs: Vec<f64> = exp_xb.iter().map(|&e| e / sum_exp).collect();
643
644 let mut e_x = Array1::<f64>::zeros(k);
646 for (j, &idx) in indices.iter().enumerate() {
647 let xj = x.row(idx);
648 for kk in 0..k {
649 e_x[kk] += probs[j] * xj[kk];
650 }
651 }
652
653 if chosen < indices.len() {
655 let x_chosen = x.row(indices[chosen]);
656 for kk in 0..k {
657 gradient[kk] += x_chosen[kk] - e_x[kk];
658 }
659 }
660
661 for (j, &idx) in indices.iter().enumerate() {
663 let xj = x.row(idx);
664 for a in 0..k {
665 for b in 0..k {
666 hessian[[a, b]] -= probs[j] * xj[a] * xj[b];
667 }
668 }
669 }
670 for a in 0..k {
671 for b in 0..k {
672 hessian[[a, b]] += e_x[a] * e_x[b];
673 }
674 }
675 }
676
677 let neg_hessian = -&hessian;
678 let inv_neg_hessian = match neg_hessian.inv() {
679 Ok(m) => m,
680 Err(_) => return Err(GreenersError::OptimizationFailed),
681 };
682
683 let change = inv_neg_hessian.dot(&gradient);
684 beta = &beta + &change;
685
686 if change.mapv(|v| v.powi(2)).sum().sqrt() < tol {
687 converged = true;
688 break;
689 }
690 }
691
692 let mut final_hessian = Array2::<f64>::zeros((k, k));
694 for indices in &choice_sets {
695 let xb: Vec<f64> = indices.iter().map(|&i| x.row(i).dot(&beta)).collect();
696 let max_xb = xb.iter().copied().fold(f64::NEG_INFINITY, f64::max);
697 let exp_xb: Vec<f64> = xb.iter().map(|&v| (v - max_xb).exp()).collect();
698 let sum_exp: f64 = exp_xb.iter().sum();
699 let probs: Vec<f64> = exp_xb.iter().map(|&e| e / sum_exp).collect();
700
701 let mut e_x = Array1::<f64>::zeros(k);
702 for (j, &idx) in indices.iter().enumerate() {
703 let xj = x.row(idx);
704 for kk in 0..k {
705 e_x[kk] += probs[j] * xj[kk];
706 }
707 }
708
709 for (j, &idx) in indices.iter().enumerate() {
710 let xj = x.row(idx);
711 for a in 0..k {
712 for b in 0..k {
713 final_hessian[[a, b]] -= probs[j] * xj[a] * xj[b];
714 }
715 }
716 }
717 for a in 0..k {
718 for b in 0..k {
719 final_hessian[[a, b]] += e_x[a] * e_x[b];
720 }
721 }
722 }
723
724 let cov_matrix = (-&final_hessian).inv().unwrap_or(Array2::eye(k) * 1e-4);
725 let std_errors: Array1<f64> = (0..k).map(|i| cov_matrix[[i, i]].max(0.0).sqrt()).collect();
726
727 let normal_dist = Normal::standard();
728 let z_values = &beta / std_errors.mapv(|s| if s > 1e-15 { s } else { 1.0 });
729 let p_values = z_values.mapv(|z| 2.0 * (1.0 - normal_dist.cdf(z.abs())));
730
731 let k_f = k as f64;
732 let n = n_rows;
733 let aic = -2.0 * log_likelihood + 2.0 * k_f;
734 let bic = -2.0 * log_likelihood + k_f * (n as f64).ln();
735
736 Ok(ConditionalResult {
737 model_name: "Conditional MNLogit".to_string(),
738 params: beta,
739 std_errors,
740 z_values,
741 p_values,
742 log_likelihood,
743 aic,
744 bic,
745 n_obs: n_rows,
746 n_groups: choice_sets.len(),
747 iterations: iter,
748 converged,
749 inference_type: greeners_core::types::InferenceType::Normal,
750 variable_names,
751 })
752 }
753}
754
755fn combinations(n: usize, r: usize) -> Vec<Vec<usize>> {
757 if r == 0 {
758 return vec![vec![]];
759 }
760 if r > n {
761 return vec![];
762 }
763
764 let mut result = Vec::new();
765 let mut combo = vec![0usize; r];
766 for (i, item) in combo.iter_mut().enumerate().take(r) {
768 *item = i;
769 }
770
771 loop {
772 result.push(combo.clone());
773
774 let mut i = r;
776 loop {
777 if i == 0 {
778 return result;
779 }
780 i -= 1;
781 if combo[i] < n - r + i {
782 break;
783 }
784 if i == 0 {
785 return result;
786 }
787 }
788
789 combo[i] += 1;
790 for j in (i + 1)..r {
791 combo[j] = combo[j - 1] + 1;
792 }
793 }
794}