use lapack::{cunmqr, dormqr, sormqr, zunmqr};
use crate::base_types::LapackResult;
use crate::base_types::{LapackError, c32, c64};
use crate::dense::linalg::lapack::interface::lapack_return;
use num::{Zero, complex::ComplexFloat};
#[derive(Clone, Copy)]
pub enum MqrTransMode {
NoTranspose,
ConjugateTranspose,
}
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum MqrSide {
Left = b'L',
Right = b'R',
}
trait TransposeToChar {
fn to_char(trans: MqrTransMode) -> u8;
}
macro_rules! implement_transpose_to_char {
($scalar:ty, $char:expr) => {
impl TransposeToChar for $scalar {
fn to_char(trans: MqrTransMode) -> u8 {
match trans {
MqrTransMode::NoTranspose => b'N',
MqrTransMode::ConjugateTranspose => $char,
}
}
}
};
}
implement_transpose_to_char!(f32, b'T');
implement_transpose_to_char!(f64, b'T');
implement_transpose_to_char!(c32, b'C');
implement_transpose_to_char!(c64, b'C');
pub trait Mqr: Sized {
#[allow(clippy::too_many_arguments)]
fn mqr(
side: MqrSide,
trans: MqrTransMode,
m: usize,
n: usize,
k: usize,
a: &[Self],
lda: usize,
tau: &[Self],
c: &mut [Self],
ldc: usize,
) -> LapackResult<()>;
}
macro_rules! implement_mqr {
($scalar:ty, $mqr:expr) => {
impl Mqr for $scalar {
fn mqr(
side: MqrSide,
trans: MqrTransMode,
m: usize,
n: usize,
k: usize,
a: &[Self],
lda: usize,
tau: &[Self],
c: &mut [Self],
ldc: usize,
) -> LapackResult<()> {
let mut info = 0;
match side {
MqrSide::Left => {
assert!(m >= k, "Require `m` {} >= `k` {}.", m, k);
assert!(
lda >= std::cmp::max(1, m),
"Require `lda` {} >= `max(1, m)` {}.",
lda,
std::cmp::max(1, m)
);
}
MqrSide::Right => {
assert!(n >= k, "Require `n` {} >= `k` {}.", m, k);
assert!(
lda >= std::cmp::max(1, n),
"Require `lda` {} >= `max(1, n)` {}.",
lda,
std::cmp::max(1, n)
);
}
}
assert_eq!(
a.len(),
lda * k,
"Require `a.len()` {} == `lda * k` {}.",
a.len(),
lda * k
);
assert_eq!(
tau.len(),
k,
"Require `tau.len()` {} == `k` {}.",
tau.len(),
k
);
assert!(
ldc >= std::cmp::max(1, m),
"Require `ldc` {} >= `max(1, m)` {}.",
ldc,
std::cmp::max(1, m)
);
assert_eq!(
c.len(),
ldc * n,
"Require `c.len()` {} == `ldc * n` {}.",
c.len(),
ldc * n
);
let trans = <$scalar as TransposeToChar>::to_char(trans);
let mut work = [<$scalar>::zero(); 1];
unsafe {
$mqr(
side as u8, trans, m as i32, n as i32, k as i32, a, lda as i32, tau, c,
ldc as i32, &mut work, -1, &mut info,
);
}
let lwork = work[0].re() as i32;
if info != 0 {
return Err(LapackError::LapackInfoCode(info));
}
let mut work = vec![<$scalar>::zero(); lwork as usize];
unsafe {
$mqr(
side as u8, trans, m as i32, n as i32, k as i32, a, lda as i32, tau, c,
ldc as i32, &mut work, lwork, &mut info,
);
}
lapack_return(info, ())
}
}
};
}
implement_mqr!(f32, sormqr);
implement_mqr!(f64, dormqr);
implement_mqr!(c32, cunmqr);
implement_mqr!(c64, zunmqr);