1use nu_engine::command_prelude::*;
2use nu_protocol::{ListStream, Signals};
3
4#[derive(Clone)]
5pub struct Combinations;
6
7impl Command for Combinations {
8 fn name(&self) -> &str {
9 "combinations"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("combinations")
14 .input_output_types(vec![
15 (
16 Type::List(Box::new(Type::Any)),
17 Type::List(Box::new(Type::List(Box::new(Type::Any)))),
18 ),
19 (Type::table(), Type::table()),
20 ])
21 .required("k", SyntaxShape::Int, "The size of each combination (k).")
22 .category(Category::Filters)
23 }
24
25 fn description(&self) -> &str {
26 "Generates all combinations of size k from the input list."
27 }
28
29 fn search_terms(&self) -> Vec<&str> {
30 vec!["choose", "subset", "combinatorics"]
31 }
32
33 fn run(
34 &self,
35 engine_state: &EngineState,
36 stack: &mut Stack,
37 call: &Call,
38 mut input: PipelineData,
39 ) -> Result<PipelineData, ShellError> {
40 let head = call.head;
41 let k: usize = call.req(engine_state, stack, 0)?;
42 let metadata = input.take_metadata();
43
44 let vals: Vec<Value> = input.into_iter().collect();
45 let signals = engine_state.signals().clone();
46
47 if k == 0 {
48 return Ok(PipelineData::Value(
50 Value::list(vec![Value::list(vec![], head)], head),
51 metadata,
52 ));
53 }
54
55 let iter = CombinationsIter::new(vals, k, signals.clone(), head);
56 let stream = ListStream::new(iter, head, signals);
57 Ok(PipelineData::ListStream(stream, metadata))
58 }
59
60 fn examples(&self) -> Vec<Example<'static>> {
61 vec![
62 Example {
63 example: "[1 2 3] | combinations 2",
64 description: "Generate all 2-combinations",
65 result: Some(Value::test_list(vec![
66 Value::test_list(vec![Value::test_int(1), Value::test_int(2)]),
67 Value::test_list(vec![Value::test_int(1), Value::test_int(3)]),
68 Value::test_list(vec![Value::test_int(2), Value::test_int(3)]),
69 ])),
70 },
71 Example {
72 example: "[[a] [b] [c]] | combinations 2",
73 description: "Generate combinations of lists",
74 result: Some(Value::test_list(vec![
75 Value::test_list(vec![
76 Value::test_list(vec![Value::test_string("a")]),
77 Value::test_list(vec![Value::test_string("b")]),
78 ]),
79 Value::test_list(vec![
80 Value::test_list(vec![Value::test_string("a")]),
81 Value::test_list(vec![Value::test_string("c")]),
82 ]),
83 Value::test_list(vec![
84 Value::test_list(vec![Value::test_string("b")]),
85 Value::test_list(vec![Value::test_string("c")]),
86 ]),
87 ])),
88 },
89 Example {
90 example: "[1 2] | combinations 3",
91 description: "k > n yields an empty list",
92 result: Some(Value::test_list(vec![])),
93 },
94 ]
95 }
96}
97
98struct CombinationsIter {
105 n: usize,
106 k: usize,
107 indices: Vec<usize>,
108 values: Vec<Value>,
109 done: bool,
110 signals: Signals,
111 span: Span,
112}
113
114impl CombinationsIter {
115 fn new(values: Vec<Value>, k: usize, signals: Signals, span: Span) -> Self {
116 let n = values.len();
117 let done = k == 0 || k > n;
118 let indices: Vec<usize> = if done { vec![] } else { (0..k).collect() };
119 Self {
120 n,
121 k,
122 indices,
123 values,
124 done,
125 signals,
126 span,
127 }
128 }
129}
130
131impl Iterator for CombinationsIter {
132 type Item = Value;
133
134 fn next(&mut self) -> Option<Self::Item> {
135 if self.done {
136 return None;
137 }
138
139 if self.signals.interrupted() {
140 return None;
141 }
142
143 let combination: Vec<Value> = self
144 .indices
145 .iter()
146 .map(|&i| self.values[i].clone())
147 .collect();
148 let result = Some(Value::list(combination, self.span));
149
150 let mut i = self.k.wrapping_sub(1);
152 while i < self.k && self.indices[i] == self.n - self.k + i {
153 if i == 0 {
154 self.done = true;
155 return result;
156 }
157 i -= 1;
158 }
159
160 self.indices[i] += 1;
161 for j in (i + 1)..self.k {
162 self.indices[j] = self.indices[j - 1] + 1;
163 }
164
165 result
166 }
167}
168
169#[cfg(test)]
170mod test {
171 use super::Combinations;
172 use nu_test_support::prelude::*;
173
174 #[test]
175 fn test_examples() -> nu_test_support::Result {
176 nu_test_support::test().examples(Combinations)
177 }
178
179 #[test]
180 fn combinations_k2() -> Result {
181 let result: Value = test().run("[1 2 3] | combinations 2")?;
182 assert_eq!(
183 result,
184 Value::test_list(vec![
185 Value::test_list(vec![Value::test_int(1), Value::test_int(2)]),
186 Value::test_list(vec![Value::test_int(1), Value::test_int(3)]),
187 Value::test_list(vec![Value::test_int(2), Value::test_int(3)]),
188 ])
189 );
190 Ok(())
191 }
192
193 #[test]
194 fn combinations_k0() -> Result {
195 let result: Value = test().run("[1 2 3] | combinations 0")?;
196 assert_eq!(result, Value::test_list(vec![Value::test_list(vec![])]));
197 Ok(())
198 }
199
200 #[test]
201 fn combinations_k1() -> Result {
202 let result: Value = test().run("[1 2 3] | combinations 1")?;
203 assert_eq!(
204 result,
205 Value::test_list(vec![
206 Value::test_list(vec![Value::test_int(1)]),
207 Value::test_list(vec![Value::test_int(2)]),
208 Value::test_list(vec![Value::test_int(3)]),
209 ])
210 );
211 Ok(())
212 }
213
214 #[test]
215 fn combinations_k_equals_n() -> Result {
216 let result: Value = test().run("[1 2 3] | combinations 3")?;
217 assert_eq!(
218 result,
219 Value::test_list(vec![Value::test_list(vec![
220 Value::test_int(1),
221 Value::test_int(2),
222 Value::test_int(3),
223 ])])
224 );
225 Ok(())
226 }
227
228 #[test]
229 fn combinations_k_greater_than_n() -> Result {
230 test()
231 .run("[1 2] | combinations 3")
232 .expect_value_eq(Value::test_list(vec![]))
233 }
234
235 #[test]
236 fn combinations_empty_list() -> Result {
237 test()
238 .run("[] | combinations 2")
239 .expect_value_eq(Value::test_list(vec![]))
240 }
241
242 #[test]
243 fn combinations_strings() -> Result {
244 let result: Value = test().run("[a b c] | combinations 2")?;
245 assert_eq!(
246 result,
247 Value::test_list(vec![
248 Value::test_list(vec![Value::test_string("a"), Value::test_string("b")]),
249 Value::test_list(vec![Value::test_string("a"), Value::test_string("c")]),
250 Value::test_list(vec![Value::test_string("b"), Value::test_string("c")]),
251 ])
252 );
253 Ok(())
254 }
255
256 #[test]
257 fn combinations_k0_empty_input() -> Result {
258 let result: Value = test().run("[] | combinations 0")?;
260 assert_eq!(result, Value::test_list(vec![Value::test_list(vec![])]));
261 Ok(())
262 }
263}