1use mech_core::*;
2use mech_core::value::ToUsize;
3#[cfg(feature = "matrix")]
4use mech_core::structures::matrix::Matrix;
5
6use std::fmt::Debug;
7use std::ops::{Add, AddAssign, Sub, Div};
8use num_traits::{Zero, One};
9use itertools::Itertools;
10use paste::paste;
11
12#[derive(Debug)]
15pub struct NChooseK<T> {
16 n: Ref<T>,
17 k: Ref<T>,
18 out: Ref<T>,
19}
20impl<T> MechFunctionFactory for NChooseK<T>
21where
22 T: Copy + Debug + Clone + Sync + Send + 'static +
23 Add<Output = T> + AddAssign +
24 Sub<Output = T> + Div<Output = T> +
25 Zero + One +
26 ConstElem + CompileConst + AsValueKind +
27 PartialEq + PartialOrd,
28 Ref<T>: ToValue,
29{
30 fn new(args: FunctionArgs) -> MResult<Box<dyn MechFunction>> {
31 match args {
32 FunctionArgs::Binary(out, arg1, arg2) => {
33 let n: Ref<T> = unsafe{ arg1.as_unchecked().clone() };
34 let k: Ref<T> = unsafe{ arg2.as_unchecked().clone() };
35 let out: Ref<T> = unsafe{ out.as_unchecked().clone() };
36 Ok(Box::new(Self{n, k, out}))
37 }
38 _ => Err(MechError2::new(
39 IncorrectNumberOfArguments { expected: 2, found: args.len() },
40 None
41 ).with_compiler_loc()
42 ),
43 }
44 }
45}
46impl<T> MechFunctionImpl for NChooseK<T>
47where
48 T: Copy + Debug + Clone + Sync + Send + 'static +
49 Add<Output = T> + AddAssign +
50 Sub<Output = T> + Div<Output = T> +
51 Zero + One +
52 PartialEq + PartialOrd,
53 Ref<T>: ToValue,
54{
55 fn solve(&self) {
56 let n_ptr = self.n.as_ptr();
57 let k_ptr = self.k.as_ptr();
58 let out_ptr = self.out.as_mut_ptr();
59 unsafe {
60 let n = *n_ptr;
61 let k = *k_ptr;
62 if k > n {
63 *out_ptr = T::zero(); return;
65 }
66 let mut result = T::one();
67 let mut i = T::zero();
68 while i < k {
69 let numerator = n - i;
70 let denominator = i + T::one();
71 result = result * numerator / denominator;
72 i = i + T::one();
73 }
74 *out_ptr = result;
75 }
76 }
77 fn out(&self) -> Value {self.out.to_value()}
78 fn to_string(&self) -> String {format!("{:#?}", self)}
79}
80#[cfg(feature = "compiler")]
81impl<T> MechFunctionCompiler for NChooseK<T>
82where
83 T: ConstElem + CompileConst + AsValueKind
84{
85 fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
86 let name = format!("NChooseK<{}>", T::as_value_kind());
87 compile_binop!(name, self.out, self.n, self.k, ctx, FeatureFlag::Custom(hash_str("combinatorics/n-choose-k")));
88 }
89}
90register_fxn_descriptor!(NChooseK, u8, "u8", i8, "i8", u16, "u16", i16, "i16", u32, "u32", i32, "i32", u64, "u64", i64, "i64", u128, "u128", i128, "i128", f32, "f32", f64, "f64", R64, "r64", C64, "c64");
91
92#[cfg(feature = "matrix")]
93#[derive(Debug)]
94pub struct NChooseKMatrix<T> {
95 n: Ref<Matrix<T>>,
96 k: Ref<T>,
97 out: Ref<Matrix<T>>,
98}
99#[cfg(feature = "matrix")]
100impl<T> MechFunctionFactory for NChooseKMatrix<T>
101where
102 T: Copy + Debug + Clone + Sync + Send + 'static +
103 ToUsize + std::fmt::Display +
104 Add<Output = T> + AddAssign +
105 Sub<Output = T> + Div<Output = T> +
106 Zero + One +
107 ConstElem + CompileConst + AsValueKind +
108 PartialEq + PartialOrd + ToMatrix,
109 Ref<T>: ToValue,
110 Matrix<T>: ToValue,
111{
112 fn new(args: FunctionArgs) -> MResult<Box<dyn MechFunction>> {
113 match args {
114 FunctionArgs::Binary(out, arg1, arg2) => {
115 let n: Ref<Matrix<T>> = unsafe{ arg1.as_unchecked().clone() };
116 let k: Ref<T> = unsafe{ arg2.as_unchecked().clone() };
117 let out: Ref<Matrix<T>> = unsafe{ out.as_unchecked().clone() };
118 Ok(Box::new(Self{n, k, out}))
119 }
120 _ => Err(MechError2::new(
121 IncorrectNumberOfArguments { expected: 2, found: args.len() },
122 None
123 ).with_compiler_loc()
124 ),
125 }
126 }
127}
128#[cfg(feature = "matrix")]
129impl<T> MechFunctionImpl for NChooseKMatrix<T>
130where
131 T: Copy + Debug + Clone + Sync + Send + 'static +
132 ToUsize + std::fmt::Display +
133 Add<Output = T> + AddAssign +
134 Sub<Output = T> + Div<Output = T> +
135 Zero + One +
136 PartialEq + PartialOrd + ToMatrix,
137 Ref<T>: ToValue,
138 Matrix<T>: ToValue,
139{
140 fn solve(&self) {
141 let n_matrix = self.n.borrow();
142 let k_scalar = *self.k.borrow();
143 let elements: Vec<T> = n_matrix.as_vec();
144 let k_usize: usize = k_scalar.to_usize();
145 if k_usize > elements.len() {
147 let empty = T::to_matrix(vec![], 0, k_usize);
148 *self.out.borrow_mut() = empty;
149 return;
150 }
151 let combinations: Vec<Vec<T>> = elements.iter().copied().combinations(k_usize).collect();
153
154 let rows = combinations.len();
156 let cols = k_usize;
157 let flat_data: Vec<T> = combinations.into_iter().flatten().collect();
158 let result = T::to_matrix(flat_data, cols, rows);
159 *self.out.borrow_mut() = result;
160 }
161 fn out(&self) -> Value { (*self.out.borrow()).to_value() }
162 fn to_string(&self) -> String { format!("{:#?}", self) }
163}
164#[cfg(all(feature = "matrix", feature = "compiler"))]
165impl<T> MechFunctionCompiler for NChooseKMatrix<T>
166where
167 T: ConstElem + CompileConst + AsValueKind,
168{
169 fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
170 let name = format!("NChooseKMatrix<{}>", T::as_value_kind());
171 compile_binop!(name, self.out, self.n, self.k, ctx, FeatureFlag::Custom(hash_str("combinatorics/n-choose-k")));
172 }
173}
174register_fxn_descriptor!(NChooseKMatrix, u8, "u8", i8, "i8", u16, "u16", i16, "i16", u32, "u32", i32, "i32", u64, "u64", i64, "i64", u128, "u128", i128, "i128", f32, "f32", f64, "f64", R64, "r64", C64, "c64");
175
176
177fn impl_combinatorics_n_choose_k_fxn(n: Value, k: Value) -> MResult<Box<dyn MechFunction>> {
178 match (n,k) {
179 #[cfg(feature = "u8")]
180 (Value::U8(n), Value::U8(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(u8::default())})),
181 #[cfg(feature = "u16")]
182 (Value::U16(n), Value::U16(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(u16::default())})),
183 #[cfg(feature = "u32")]
184 (Value::U32(n), Value::U32(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(u32::default())})),
185 #[cfg(feature = "u64")]
186 (Value::U64(n), Value::U64(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(u64::default())})),
187 #[cfg(feature = "u128")]
188 (Value::U128(n), Value::U128(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(u128::default())})),
189 #[cfg(feature = "i8")]
190 (Value::I8(n), Value::I8(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(i8::default())})),
191 #[cfg(feature = "i16")]
192 (Value::I16(n), Value::I16(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(i16::default())})),
193 #[cfg(feature = "i32")]
194 (Value::I32(n), Value::I32(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(i32::default())})),
195 #[cfg(feature = "i64")]
196 (Value::I64(n), Value::I64(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(i64::default())})),
197 #[cfg(feature = "i128")]
198 (Value::I128(n), Value::I128(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(i128::default())})),
199 #[cfg(feature = "f32")]
200 (Value::F32(n), Value::F32(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(f32::default())})),
201 #[cfg(feature = "f64")]
202 (Value::F64(n), Value::F64(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(f64::default())})),
203 #[cfg(feature = "rational")]
204 (Value::R64(n), Value::R64(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(R64::default())})),
205 #[cfg(feature = "complex")]
206 (Value::C64(n), Value::C64(k)) => Ok(Box::new(NChooseK{n: n, k: k, out: Ref::new(C64::default())})),
207 #[cfg(all(feature = "matrix", feature = "u8"))]
208 (Value::MatrixU8(n), Value::U8(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(u8::to_matrix(vec![], 0, 0))})),
209 #[cfg(all(feature = "matrix", feature = "u16"))]
210 (Value::MatrixU16(n), Value::U16(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(u16::to_matrix(vec![], 0, 0))})),
211 #[cfg(all(feature = "matrix", feature = "u32"))]
212 (Value::MatrixU32(n), Value::U32(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(u32::to_matrix(vec![], 0, 0))})),
213 #[cfg(all(feature = "matrix", feature = "u64"))]
214 (Value::MatrixU64(n), Value::U64(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(u64::to_matrix(vec![], 0, 0))})),
215 #[cfg(all(feature = "matrix", feature = "u128"))]
216 (Value::MatrixU128(n), Value::U128(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(u128::to_matrix(vec![], 0, 0))})),
217 #[cfg(all(feature = "matrix", feature = "i8"))]
218 (Value::MatrixI8(n), Value::I8(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(i8::to_matrix(vec![], 0, 0))})),
219 #[cfg(all(feature = "matrix", feature = "i16"))]
220 (Value::MatrixI16(n), Value::I16(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(i16::to_matrix(vec![], 0, 0))})),
221 #[cfg(all(feature = "matrix", feature = "i32"))]
222 (Value::MatrixI32(n), Value::I32(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(i32::to_matrix(vec![], 0, 0))})),
223 #[cfg(all(feature = "matrix", feature = "i64"))]
224 (Value::MatrixI64(n), Value::I64(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(i64::to_matrix(vec![], 0, 0))})),
225 #[cfg(all(feature = "matrix", feature = "i128"))]
226 (Value::MatrixI128(n), Value::I128(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(i128::to_matrix(vec![], 0, 0))})),
227 #[cfg(all(feature = "matrix", feature = "f32"))]
228 (Value::MatrixF32(n), Value::F32(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(f32::to_matrix(vec![], 0, 0))})),
229 #[cfg(all(feature = "matrix", feature = "f64"))]
230 (Value::MatrixF64(n), Value::F64(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(f64::to_matrix(vec![], 0, 0))})),
231 #[cfg(all(feature = "matrix", feature = "rational"))]
232 (Value::MatrixR64(n), Value::R64(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(R64::to_matrix(vec![], 0, 0))})),
233 #[cfg(all(feature = "matrix", feature = "complex"))]
234 (Value::MatrixC64(n), Value::C64(k)) => Ok(Box::new(NChooseKMatrix{n: Ref::new(n), k, out: Ref::new(C64::to_matrix(vec![], 0, 0))})),
235 (n,k) => Err(MechError2::new(
236 UnhandledFunctionArgumentKind2 { arg: (n.kind(), k.kind()), fxn_name: "combinatorics/n-choose-k".to_string() },
237 None
238 ).with_compiler_loc()
239 ),
240 }
241}
242
243pub struct CombinatoricsNChooseK {}
244impl NativeFunctionCompiler for CombinatoricsNChooseK {
245 fn compile(&self, arguments: &Vec<Value>) -> MResult<Box<dyn MechFunction>> {
246 if arguments.len() != 2 {
247 return Err(MechError2::new(IncorrectNumberOfArguments { expected: 2, found: arguments.len() }, None).with_compiler_loc());
248 }
249 let n = arguments[0].clone();
250 let k = arguments[1].clone();
251
252 match impl_combinatorics_n_choose_k_fxn(n.clone(),k.clone()) {
253 Ok(fxn) => Ok(fxn),
254 Err(_) => {
255 match (n,k) {
256 (Value::MutableReference(n),Value::MutableReference(k)) => {let n_brrw = n.borrow();let k_brrw = k.borrow();impl_combinatorics_n_choose_k_fxn(n_brrw.clone(),k_brrw.clone())}
257 (n,Value::MutableReference(k)) => {let k_brrw = k.borrow(); impl_combinatorics_n_choose_k_fxn(n.clone(),k_brrw.clone())}
258 (Value::MutableReference(n),k) => {let n_brrw = n.borrow();impl_combinatorics_n_choose_k_fxn(n_brrw.clone(),k.clone())}
259 (n,k) => Err(MechError2::new(
260 UnhandledFunctionArgumentKind2 { arg: (n.kind(), k.kind()), fxn_name: "combinatorics/n-choose-k".to_string() },
261 None
262 ).with_compiler_loc()
263 ),
264 }
265 }
266 }
267 }
268}
269
270register_descriptor! {
271 FunctionCompilerDescriptor {
272 name: "combinatorics/n-choose-k",
273 ptr: &CombinatoricsNChooseK{},
274 }
275}