mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-02-01 17:39:13 +01:00
Added 'perms' (permutations) and 'sort' functions
This commit is contained in:
parent
ba2a840a28
commit
c93afbf3a6
@ -1,5 +1,6 @@
|
||||
#[cfg(feature = "rug")]
|
||||
pub mod with_rug;
|
||||
|
||||
#[cfg(feature = "rug")]
|
||||
use rug::Float;
|
||||
#[cfg(feature = "rug")]
|
||||
|
@ -78,6 +78,7 @@ lazy_static! {
|
||||
m.insert("Re", (UnaryFuncInfo(re, Other), ""));
|
||||
m.insert("round", (UnaryFuncInfo(round, Other), ""));
|
||||
m.insert("sgn", (UnaryFuncInfo(sgn, Other), ""));
|
||||
m.insert("sort", (UnaryFuncInfo(sort, Other), ""));
|
||||
m.insert("sqrt", (UnaryFuncInfo(sqrt, Other), ""));
|
||||
m.insert("√", (UnaryFuncInfo(sqrt, Other), ""));
|
||||
m.insert("transpose", (UnaryFuncInfo(transpose, Other), ""));
|
||||
@ -107,6 +108,7 @@ lazy_static! {
|
||||
m.insert("diag", VectorFuncInfo(diag, Other));
|
||||
m.insert("max", VectorFuncInfo(max, Other));
|
||||
m.insert("min", VectorFuncInfo(min, Other));
|
||||
m.insert("perms", VectorFuncInfo(perms, Other));
|
||||
m
|
||||
};
|
||||
}
|
||||
@ -246,6 +248,8 @@ fn from_angle_unit(
|
||||
}
|
||||
|
||||
pub mod funcs {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[cfg(not(feature = "rug"))]
|
||||
pub use super::regular::funcs::*;
|
||||
use super::special_funcs::factorial;
|
||||
@ -748,6 +752,71 @@ pub mod funcs {
|
||||
x.pow_without_unit(&KalkValue::from(1f64).div_without_unit(&n))
|
||||
}
|
||||
|
||||
pub fn perms(x: KalkValue) -> KalkValue {
|
||||
if let KalkValue::Vector(values) = sort(x) {
|
||||
let mut result: Vec<Vec<KalkValue>> = vec![values];
|
||||
|
||||
// Permutations in lexographic order: https://www.baeldung.com/cs/array-generate-all-permutations
|
||||
loop {
|
||||
let prev_values = result.last().unwrap();
|
||||
let mut i = prev_values.len() - 1;
|
||||
for _ in (1..prev_values.len()).rev() {
|
||||
if let (KalkValue::Number(real, _, _), KalkValue::Number(real_2, _, _)) =
|
||||
(&prev_values[i - 1], &prev_values[i])
|
||||
{
|
||||
if real >= real_2 {
|
||||
i -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
// Needs to be checked inside the loop as well
|
||||
// since the counter is of type usize, which
|
||||
// can't be negative.
|
||||
if i == 0 {
|
||||
return KalkValue::Matrix(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
return KalkValue::Matrix(result);
|
||||
}
|
||||
|
||||
let pivot = if let KalkValue::Number(real, _, _) = &prev_values[i - 1] {
|
||||
real
|
||||
} else {
|
||||
return KalkValue::nan();
|
||||
};
|
||||
|
||||
let mut j = prev_values.len() - 1;
|
||||
while let KalkValue::Number(real, _, _) = &prev_values[j] {
|
||||
if real <= pivot {
|
||||
j -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_values = prev_values.clone();
|
||||
new_values.swap(i - 1, j);
|
||||
|
||||
// Reverse the part after new_values[i]
|
||||
i += 1;
|
||||
j = new_values.len();
|
||||
while i < j {
|
||||
new_values.swap(i - 1, j - 1);
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
result.push(new_values);
|
||||
}
|
||||
} else {
|
||||
KalkValue::nan()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn re(x: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
KalkValue::Number(real, float!(0), unit)
|
||||
@ -794,6 +863,24 @@ pub mod funcs {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort(x: KalkValue) -> KalkValue {
|
||||
if let KalkValue::Vector(mut values) = x {
|
||||
values.sort_by(|a, b| {
|
||||
if let KalkValue::Boolean(true) = a.eq_without_unit(b) {
|
||||
Ordering::Equal
|
||||
} else if let KalkValue::Boolean(true) = a.greater_than_without_unit(b) {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
});
|
||||
|
||||
KalkValue::Vector(values)
|
||||
} else {
|
||||
KalkValue::nan()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sqrt(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if x.has_imaginary() {
|
||||
@ -896,6 +983,10 @@ mod tests {
|
||||
use crate::prelude::KalkValue;
|
||||
use crate::test_helpers::cmp;
|
||||
|
||||
fn val(x: f64) -> KalkValue {
|
||||
KalkValue::from(x)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unary_funcs() {
|
||||
let in_out = vec![
|
||||
@ -969,6 +1060,35 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_perms() {
|
||||
let vecs = vec![
|
||||
(KalkValue::Vector(vec![val(1f64)]), 1),
|
||||
(KalkValue::Vector(vec![val(1f64), val(2f64)]), 2),
|
||||
(KalkValue::Vector(vec![val(1f64), val(2f64), val(3f64)]), 6),
|
||||
(KalkValue::Vector(vec![val(2f64), val(3f64), val(1f64)]), 6),
|
||||
(
|
||||
KalkValue::Vector(vec![val(2f64), val(3f64), val(1f64), val(3f64)]),
|
||||
12,
|
||||
),
|
||||
];
|
||||
for (vec, expected_len) in vecs {
|
||||
let permutations = if let KalkValue::Matrix(permutations) = perms(vec) {
|
||||
permutations
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
assert_eq!(permutations.len(), expected_len);
|
||||
|
||||
let mut results = std::collections::HashSet::with_capacity(permutations.len());
|
||||
for permutation in permutations {
|
||||
let as_str = format!("{:?}", permutation);
|
||||
assert!(!results.contains(&as_str));
|
||||
results.insert(as_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transpose() {
|
||||
fn to_matrix(rows: Vec<Vec<i32>>) -> KalkValue {
|
||||
|
Loading…
Reference in New Issue
Block a user