use crate::error::{Error, Result};
use crate::ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum GroupComparison {
Identical = 0,
Similar = 1,
Unequal = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RankRange {
pub first: i32,
pub last: i32,
pub stride: i32,
}
pub struct Group {
pub(crate) handle: i32,
}
unsafe impl Send for Group {}
unsafe impl Sync for Group {}
impl Group {
pub fn size(&self) -> Result<i32> {
let mut size: i32 = 0;
let ret = unsafe { ffi::ferrompi_group_size(self.handle, &mut size) };
Error::check_with_op(ret, "group_size")?;
Ok(size)
}
pub fn rank(&self) -> Result<i32> {
let mut rank: i32 = 0;
let ret = unsafe { ffi::ferrompi_group_rank(self.handle, &mut rank) };
Error::check_with_op(ret, "group_rank")?;
Ok(rank)
}
pub fn raw_handle(&self) -> i32 {
self.handle
}
pub fn undefined() -> i32 {
unsafe { ffi::ferrompi_mpi_undefined() }
}
pub fn include(&self, ranks: &[i32]) -> Result<Group> {
let mut new_handle: i32 = 0;
let ret = unsafe {
ffi::ferrompi_group_incl(
self.handle,
ranks.len() as i32,
ranks.as_ptr(),
&mut new_handle,
)
};
Error::check_with_op(ret, "group_incl")?;
Ok(Group { handle: new_handle })
}
pub fn union(&self, other: &Group) -> Result<Group> {
let mut h: i32 = -1;
let ret = unsafe { ffi::ferrompi_group_union(self.handle, other.handle, &mut h) };
Error::check_with_op(ret, "group_union")?;
Ok(Group { handle: h })
}
pub fn intersection(&self, other: &Group) -> Result<Group> {
let mut h: i32 = -1;
let ret = unsafe { ffi::ferrompi_group_intersection(self.handle, other.handle, &mut h) };
Error::check_with_op(ret, "group_intersection")?;
Ok(Group { handle: h })
}
pub fn difference(&self, other: &Group) -> Result<Group> {
let mut h: i32 = -1;
let ret = unsafe { ffi::ferrompi_group_difference(self.handle, other.handle, &mut h) };
Error::check_with_op(ret, "group_difference")?;
Ok(Group { handle: h })
}
pub fn compare(&self, other: &Group) -> Result<GroupComparison> {
let mut result: i32 = -1;
let ret = unsafe { ffi::ferrompi_group_compare(self.handle, other.handle, &mut result) };
Error::check_with_op(ret, "group_compare")?;
match result {
0 => Ok(GroupComparison::Identical),
1 => Ok(GroupComparison::Similar),
2 => Ok(GroupComparison::Unequal),
other => Err(Error::Internal(format!(
"ferrompi_group_compare returned unexpected result {other}"
))),
}
}
pub fn exclude(&self, ranks: &[i32]) -> Result<Group> {
let mut new_handle: i32 = 0;
let ret = unsafe {
ffi::ferrompi_group_excl(
self.handle,
ranks.len() as i32,
ranks.as_ptr(),
&mut new_handle,
)
};
Error::check_with_op(ret, "group_excl")?;
Ok(Group { handle: new_handle })
}
pub fn range_include(&self, ranges: &[RankRange]) -> Result<Group> {
let mut flat: Vec<i32> = Vec::with_capacity(3 * ranges.len());
for r in ranges {
flat.push(r.first);
flat.push(r.last);
flat.push(r.stride);
}
let mut h: i32 = -1;
let ret = unsafe {
ffi::ferrompi_group_range_incl(self.handle, ranges.len() as i32, flat.as_ptr(), &mut h)
};
Error::check_with_op(ret, "group_range_incl")?;
Ok(Group { handle: h })
}
pub fn range_exclude(&self, ranges: &[RankRange]) -> Result<Group> {
let mut flat: Vec<i32> = Vec::with_capacity(3 * ranges.len());
for r in ranges {
flat.push(r.first);
flat.push(r.last);
flat.push(r.stride);
}
let mut h: i32 = -1;
let ret = unsafe {
ffi::ferrompi_group_range_excl(self.handle, ranges.len() as i32, flat.as_ptr(), &mut h)
};
Error::check_with_op(ret, "group_range_excl")?;
Ok(Group { handle: h })
}
pub fn translate_ranks(&self, ranks: &[i32], other: &Group) -> Result<Vec<Option<i32>>> {
if ranks.is_empty() {
return Ok(vec![]);
}
let mut out: Vec<i32> = vec![-1; ranks.len()];
let ret = unsafe {
ffi::ferrompi_group_translate_ranks(
self.handle,
ranks.len() as i32,
ranks.as_ptr(),
other.handle,
out.as_mut_ptr(),
)
};
Error::check_with_op(ret, "group_translate_ranks")?;
Ok(out
.into_iter()
.map(|r| if r == -1 { None } else { Some(r) })
.collect())
}
}
impl Drop for Group {
fn drop(&mut self) {
if self.handle > 0 {
unsafe { ffi::ferrompi_group_free(self.handle) };
}
}
}
#[cfg(test)]
mod tests {
use super::{Group, GroupComparison, RankRange};
use crate::error::Result;
const _: () = {
#[allow(dead_code)]
fn check<T: Send + Sync>() {}
#[allow(dead_code)]
fn group_send_sync_compile_time_assertion() {
check::<Group>();
}
};
#[test]
fn group_raw_handle_returns_stored_value() {
let g = Group { handle: 7 };
assert_eq!(g.raw_handle(), 7);
std::mem::forget(g);
}
#[test]
fn group_drop_with_zero_handle_is_no_op() {
let g = Group { handle: 0 };
drop(g); }
#[allow(dead_code)]
fn group_union_signature_compiles(a: &Group, b: &Group) -> Result<Group> {
a.union(b)
}
#[allow(dead_code)]
fn group_intersection_signature_compiles(a: &Group, b: &Group) -> Result<Group> {
a.intersection(b)
}
#[allow(dead_code)]
fn group_difference_signature_compiles(a: &Group, b: &Group) -> Result<Group> {
a.difference(b)
}
#[allow(dead_code)]
fn group_range_include_signature_compiles(g: &Group, r: &[RankRange]) -> Result<Group> {
g.range_include(r)
}
#[allow(dead_code)]
fn group_range_exclude_signature_compiles(g: &Group, r: &[RankRange]) -> Result<Group> {
g.range_exclude(r)
}
#[allow(dead_code)]
fn group_translate_ranks_signature_compiles(
a: &Group,
ranks: &[i32],
b: &Group,
) -> Result<Vec<Option<i32>>> {
a.translate_ranks(ranks, b)
}
#[test]
fn translate_ranks_empty_input_returns_empty_vec() {
let g = Group { handle: 0 };
let result = g
.translate_ranks(&[], &Group { handle: 0 })
.expect("translate_ranks with empty slice must succeed");
assert!(result.is_empty(), "expected empty Vec, got {result:?}");
std::mem::forget(g);
}
#[test]
fn group_comparison_repr_values() {
assert_eq!(GroupComparison::Identical as i32, 0);
assert_eq!(GroupComparison::Similar as i32, 1);
assert_eq!(GroupComparison::Unequal as i32, 2);
}
#[test]
fn group_comparison_debug_format() {
assert_eq!(format!("{:?}", GroupComparison::Identical), "Identical");
assert_eq!(format!("{:?}", GroupComparison::Similar), "Similar");
assert_eq!(format!("{:?}", GroupComparison::Unequal), "Unequal");
}
#[test]
fn group_comparison_equality_and_hash() {
use std::collections::HashSet;
let mut s = HashSet::new();
s.insert(GroupComparison::Identical);
s.insert(GroupComparison::Similar);
s.insert(GroupComparison::Unequal);
assert_eq!(s.len(), 3);
assert!(s.contains(&GroupComparison::Identical));
assert!(s.contains(&GroupComparison::Similar));
assert!(s.contains(&GroupComparison::Unequal));
}
#[allow(dead_code)]
fn group_compare_signature_compiles(a: &Group, b: &Group) -> Result<GroupComparison> {
a.compare(b)
}
#[test]
fn rank_range_repr_and_size() {
assert_eq!(std::mem::size_of::<RankRange>(), 12);
}
#[test]
fn rank_range_debug_format() {
let r = RankRange {
first: 1,
last: 5,
stride: 2,
};
let s = format!("{r:?}");
assert!(s.contains("first: 1"), "expected 'first: 1' in {s:?}");
assert!(s.contains("last: 5"), "expected 'last: 5' in {s:?}");
assert!(s.contains("stride: 2"), "expected 'stride: 2' in {s:?}");
}
#[test]
fn rank_range_equality() {
let a = RankRange {
first: 0,
last: 3,
stride: 1,
};
let b = RankRange {
first: 0,
last: 3,
stride: 1,
};
let c = RankRange {
first: 0,
last: 3,
stride: 2,
};
assert_eq!(a, b);
assert_ne!(a, c);
}
}