use extendr_api::prelude::*;
use faer::{Mat, MatRef};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use std::collections::{BTreeMap, HashMap};
use std::ops::{Add, Mul};
use crate::prelude::*;
use crate::utils::traits::FaerRType;
pub type NestedHashMap = FxHashMap<String, FxHashMap<String, FxHashSet<String>>>;
pub type NestedBtreeMap = BTreeMap<String, BTreeMap<String, FxHashSet<String>>>;
#[derive(Debug)]
pub enum NamedVecError {
NotNumeric,
NoNames,
MissingValues,
}
impl std::fmt::Display for NamedVecError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
NamedVecError::NotNumeric => write!(f, "Input is not a numeric vector"),
NamedVecError::NoNames => write!(f, "Vector has no names attribute"),
NamedVecError::MissingValues => write!(f, "Vector contains missing values"),
}
}
}
impl std::error::Error for NamedVecError {}
impl From<NamedVecError> for extendr_api::Error {
fn from(err: NamedVecError) -> Self {
extendr_api::Error::Other(err.to_string())
}
}
pub fn r_list_to_hashmap(r_list: List) -> extendr_api::Result<FxHashMap<String, Vec<String>>> {
let mut result = FxHashMap::with_capacity_and_hasher(r_list.len(), FxBuildHasher);
for (n, s) in r_list {
let s_vec = s.as_string_vector().ok_or_else(|| {
Error::Other(format!(
"Failed to convert value for key '{}' to string vector",
n
))
})?;
result.insert(n.to_string(), s_vec);
}
Ok(result)
}
pub fn r_list_to_hashmap_set(
r_list: List,
) -> extendr_api::Result<FxHashMap<String, FxHashSet<String>>> {
let mut result = FxHashMap::with_capacity_and_hasher(r_list.len(), FxBuildHasher);
for (n, s) in r_list {
let s_vec = s.as_string_vector().ok_or_else(|| {
Error::Other(format!(
"Failed to convert value for key '{}' to string vector",
n
))
})?;
let mut s_hash = FxHashSet::with_capacity_and_hasher(s_vec.len(), FxBuildHasher);
for item in s_vec {
s_hash.insert(item);
}
result.insert(n.to_string(), s_hash);
}
Ok(result)
}
#[allow(dead_code)]
pub fn r_nested_list_to_nested_hashmap(r_nested_list: List) -> extendr_api::Result<NestedHashMap> {
let mut result = FxHashMap::with_capacity_and_hasher(r_nested_list.len(), FxBuildHasher);
for (n, obj) in r_nested_list {
let inner_list = obj.as_list().ok_or_else(|| {
Error::Other(format!("Failed to convert value for key '{}' to list", n))
})?;
let inner_hashmap = r_list_to_hashmap_set(inner_list)?;
result.insert(n.to_string(), inner_hashmap);
}
Ok(result)
}
pub fn r_list_to_hash_vec(r_list: List) -> extendr_api::Result<Vec<FxHashSet<String>>> {
let mut res = Vec::with_capacity(r_list.len());
for (n, s) in r_list {
let s_vec = s.as_string_vector().ok_or_else(|| {
Error::Other(format!(
"Failed to convert value for key '{}' to string vector",
n
))
})?;
let mut s_hash = FxHashSet::with_capacity_and_hasher(s_vec.len(), FxBuildHasher);
for item in s_vec {
s_hash.insert(item);
}
res.push(s_hash)
}
Ok(res)
}
pub fn r_list_to_btree_set(
r_list: List,
) -> extendr_api::Result<BTreeMap<String, FxHashSet<String>>> {
let mut result = BTreeMap::new();
for (n, s) in r_list {
let s_vec = s.as_string_vector().ok_or_else(|| {
Error::Other(format!(
"Failed to convert value for key '{}' to string vector",
n
))
})?;
let mut s_hash = FxHashSet::with_capacity_and_hasher(s_vec.len(), FxBuildHasher);
for item in s_vec {
s_hash.insert(item);
}
result.insert(n.to_string(), s_hash);
}
Ok(result)
}
pub fn r_nested_list_to_btree_nest(r_nested_list: List) -> extendr_api::Result<NestedBtreeMap> {
let mut result = BTreeMap::new();
for (n, obj) in r_nested_list {
let inner_list = obj.as_list().ok_or_else(|| {
Error::Other(format!("Failed to convert value for key '{}' to list", n))
})?;
let inner_tree = r_list_to_btree_set(inner_list)?;
result.insert(n.to_string(), inner_tree);
}
Ok(result)
}
pub type NamedNumericVec = (Vec<String>, Vec<f64>);
pub fn r_list_to_str_vec(r_list: List) -> extendr_api::Result<Vec<Vec<String>>> {
let mut result = Vec::with_capacity(r_list.len());
for (n, s) in r_list.into_iter() {
let s_vec = s.as_string_vector().ok_or_else(|| {
Error::Other(format!(
"Failed to convert value to string vector at key '{}'",
n
))
})?;
result.push(s_vec);
}
Ok(result)
}
pub fn r_named_vec_data(named_vec: Robj) -> extendr_api::Result<NamedNumericVec> {
let values = named_vec
.as_real_vector()
.ok_or(NamedVecError::NotNumeric)?;
let names_attr = named_vec.names().ok_or(NamedVecError::NoNames)?;
let names: Vec<String> = names_attr.into_iter().map(|s| s.to_string()).collect();
Ok((names, values))
}
pub fn r_matrix_to_faer<T>(x: &RMatrix<T>) -> MatRef<'_, T>
where
T: Copy + Clone,
extendr_api::Robj: for<'a> extendr_api::AsTypedSlice<'a, T>,
{
let ncol = x.ncols();
let nrow = x.nrows();
let data = x.data();
MatRef::from_column_major_slice(data, nrow, ncol)
}
pub fn r_matrix_to_vec_bool(x: &RMatrix<Rbool>) -> Vec<Vec<bool>> {
let ncol = x.ncols();
let nrow = x.nrows();
let data = x.data();
(0..ncol)
.map(|j| (0..nrow).map(|i| data[i + j * nrow].to_bool()).collect())
.collect()
}
pub fn faer_to_r_matrix<T>(x: MatRef<T>) -> extendr_api::RArray<T::RType, 2>
where
T: FaerRType,
{
T::to_r_matrix(x)
}
pub fn r_matrix_to_faer_fp32(x: &RMatrix<f64>) -> Mat<f32> {
let ncol = x.ncols();
let nrow = x.nrows();
let data = x.data();
let data_fp32 = data.iter().map(|x| *x as f32).collect::<Vec<f32>>();
Mat::from_fn(nrow, ncol, |i, j| data_fp32[i + j * nrow])
}
pub fn sparse_data_to_list<T>(sparse: CompressedSparseData2<T>) -> List
where
T: Into<Robj> + Clone + Default + Into<f64> + Sync + Add + PartialEq + Mul,
{
let data: Vec<f64> = sparse
.data
.into_iter()
.map(|x| {
let robj: Robj = x.into();
robj.as_real().unwrap()
})
.collect();
let indptr = sparse
.indptr
.iter()
.map(|x| *x as i32)
.collect::<Vec<i32>>();
let indices = sparse
.indices
.iter()
.map(|x| *x as i32)
.collect::<Vec<i32>>();
let nrow = sparse.shape.0;
let ncol = sparse.shape.1;
let cs_type = match sparse.cs_type {
CompressedSparseFormat::Csr => "csr",
CompressedSparseFormat::Csc => "csc",
};
list!(
data = data,
indptr = indptr,
indices = indices,
nrow = nrow,
ncol = ncol,
cs_type = cs_type.to_string()
)
}
pub fn list_to_sparse_matrix<T>(
r_list: List,
populate_data_2: bool,
) -> Result<CompressedSparseData2<T>, BixverseErrors>
where
T: Clone + Default + TryFrom<Robj>,
{
let r_data: HashMap<&str, Robj> = r_list
.try_into()
.map_err(|_| BixverseErrors::RListParse("not a named list"))?;
let indptr: Vec<usize> = r_data
.get("indptr")
.and_then(|v| v.as_integer_slice())
.unwrap()
.iter()
.map(|&x| x as usize)
.collect();
let indices: Vec<usize> = r_data
.get("indices")
.and_then(|v| v.as_integer_slice())
.unwrap()
.iter()
.map(|&x| x as usize)
.collect();
let data: Vec<T> = r_data
.get("data")
.unwrap()
.as_real_slice()
.unwrap()
.iter()
.map(|&x| T::try_from(Robj::from(x)).ok().unwrap())
.collect();
let nrow = r_data.get("nrow").and_then(|v| v.as_integer()).unwrap() as usize;
let ncol = r_data.get("ncol").and_then(|v| v.as_integer()).unwrap() as usize;
let format_str = r_data.get("format").and_then(|v| v.as_str()).unwrap();
let cs_type = match format_str {
"csr" => CompressedSparseFormat::Csr,
"csc" => CompressedSparseFormat::Csc,
_ => panic!("Unknown format"),
};
let data_2 = if populate_data_2 {
Some(data.clone())
} else {
None
};
Ok(CompressedSparseData2 {
data,
indices,
indptr,
cs_type,
data_2,
shape: (nrow, ncol),
})
}