use nu_engine::command_prelude::*;
use nu_protocol::{ListStream, Signals};
#[derive(Clone)]
pub struct Permutations;
impl Command for Permutations {
fn name(&self) -> &str {
"permutations"
}
fn signature(&self) -> Signature {
Signature::build("permutations")
.input_output_types(vec![
(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::List(Box::new(Type::Any)))),
),
(Type::table(), Type::table()),
])
.category(Category::Filters)
}
fn description(&self) -> &str {
"Generates all permutations of the input list."
}
fn search_terms(&self) -> Vec<&str> {
vec!["arrangements", "orderings", "combinatorics", "factorial"]
}
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
mut input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let metadata = input.take_metadata();
let vals: Vec<Value> = input.into_iter().collect();
let signals = engine_state.signals().clone();
if vals.is_empty() {
return Ok(PipelineData::Value(Value::list(vec![], head), metadata));
}
let iter = PermutationsIter::new(vals, signals.clone(), head);
let stream = ListStream::new(iter, head, signals);
Ok(PipelineData::ListStream(stream, metadata))
}
fn examples(&self) -> Vec<Example<'static>> {
vec![
Example {
example: "[1 2 3] | permutations",
description: "Generate all permutations",
result: Some(Value::test_list(vec![
Value::test_list(vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
]),
Value::test_list(vec![
Value::test_int(2),
Value::test_int(1),
Value::test_int(3),
]),
Value::test_list(vec![
Value::test_int(3),
Value::test_int(1),
Value::test_int(2),
]),
Value::test_list(vec![
Value::test_int(1),
Value::test_int(3),
Value::test_int(2),
]),
Value::test_list(vec![
Value::test_int(2),
Value::test_int(3),
Value::test_int(1),
]),
Value::test_list(vec![
Value::test_int(3),
Value::test_int(2),
Value::test_int(1),
]),
])),
},
Example {
example: "[1 2] | permutations",
description: "Generate permutations of two elements",
result: Some(Value::test_list(vec![
Value::test_list(vec![Value::test_int(1), Value::test_int(2)]),
Value::test_list(vec![Value::test_int(2), Value::test_int(1)]),
])),
},
Example {
example: "[] | permutations",
description: "Empty list yields no permutations",
result: Some(Value::test_list(vec![])),
},
]
}
}
struct PermutationsIter {
values: Vec<Value>,
c: Vec<usize>,
i: usize,
n: usize,
first: bool,
done: bool,
signals: Signals,
span: Span,
}
impl PermutationsIter {
fn new(values: Vec<Value>, signals: Signals, span: Span) -> Self {
let n = values.len();
let done = n == 0;
Self {
c: vec![0; n],
i: 0,
n,
values,
first: true,
done,
signals,
span,
}
}
}
impl Iterator for PermutationsIter {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
if self.signals.interrupted() {
return None;
}
if self.first {
self.first = false;
return Some(Value::list(self.values.clone(), self.span));
}
while self.i < self.n {
if self.c[self.i] < self.i {
if self.i.is_multiple_of(2) {
self.values.swap(0, self.i);
} else {
self.values.swap(self.c[self.i], self.i);
}
self.c[self.i] += 1;
self.i = 0;
return Some(Value::list(self.values.clone(), self.span));
} else {
self.c[self.i] = 0;
self.i += 1;
}
}
self.done = true;
None
}
}
#[cfg(test)]
mod test {
use super::Permutations;
use nu_test_support::prelude::*;
#[test]
fn test_examples() -> nu_test_support::Result {
nu_test_support::test().examples(Permutations)
}
#[test]
fn permutations_n3() -> Result {
let result: Value = test().run("[1 2 3] | permutations")?;
assert_eq!(
result,
Value::test_list(vec![
Value::test_list(vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3)
]),
Value::test_list(vec![
Value::test_int(2),
Value::test_int(1),
Value::test_int(3)
]),
Value::test_list(vec![
Value::test_int(3),
Value::test_int(1),
Value::test_int(2)
]),
Value::test_list(vec![
Value::test_int(1),
Value::test_int(3),
Value::test_int(2)
]),
Value::test_list(vec![
Value::test_int(2),
Value::test_int(3),
Value::test_int(1)
]),
Value::test_list(vec![
Value::test_int(3),
Value::test_int(2),
Value::test_int(1)
]),
])
);
Ok(())
}
#[test]
fn permutations_n2() -> Result {
let result: Value = test().run("[1 2] | permutations")?;
assert_eq!(
result,
Value::test_list(vec![
Value::test_list(vec![Value::test_int(1), Value::test_int(2)]),
Value::test_list(vec![Value::test_int(2), Value::test_int(1)]),
])
);
Ok(())
}
#[test]
fn permutations_n1() -> Result {
let result: Value = test().run("[42] | permutations")?;
assert_eq!(
result,
Value::test_list(vec![Value::test_list(vec![Value::test_int(42)])])
);
Ok(())
}
#[test]
fn permutations_empty() -> Result {
test()
.run("[] | permutations")
.expect_value_eq(Value::test_list(vec![]))
}
#[test]
fn permutations_n4_count() -> Result {
let result: Value = test().run("[1 2 3 4] | permutations | length")?;
assert_eq!(result, Value::test_int(24));
Ok(())
}
#[test]
fn permutations_strings() -> Result {
let result: Value = test().run("[a b] | permutations | get 0 | str join '-'")?;
assert_eq!(result, Value::test_string("a-b"));
Ok(())
}
}