use ferray_core::error::FerrayResult;
use ferray_core::{Array, Element, Ix1};
fn sorted_unique<T: PartialOrd + Copy>(data: &[T]) -> Vec<T> {
let mut v: Vec<T> = data.to_vec();
v.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
v.dedup_by(|a, b| (*a).partial_cmp(b) == Some(std::cmp::Ordering::Equal));
v
}
fn to_vec<T: Element + Copy>(a: &Array<T, Ix1>) -> Vec<T> {
a.iter().copied().collect()
}
fn make_1d<T: Element>(data: Vec<T>) -> FerrayResult<Array<T, Ix1>> {
let n = data.len();
Array::from_vec(Ix1::new([n]), data)
}
pub fn union1d<T>(
a: &Array<T, Ix1>,
b: &Array<T, Ix1>,
assume_unique: bool,
) -> FerrayResult<Array<T, Ix1>>
where
T: Element + PartialOrd + Copy,
{
let av = if assume_unique {
to_vec(a)
} else {
sorted_unique(&to_vec(a))
};
let bv = if assume_unique {
to_vec(b)
} else {
sorted_unique(&to_vec(b))
};
let mut result = Vec::with_capacity(av.len() + bv.len());
let (mut i, mut j) = (0, 0);
while i < av.len() && j < bv.len() {
match av[i]
.partial_cmp(&bv[j])
.unwrap_or(std::cmp::Ordering::Equal)
{
std::cmp::Ordering::Less => {
result.push(av[i]);
i += 1;
}
std::cmp::Ordering::Greater => {
result.push(bv[j]);
j += 1;
}
std::cmp::Ordering::Equal => {
result.push(av[i]);
i += 1;
j += 1;
}
}
}
result.extend_from_slice(&av[i..]);
result.extend_from_slice(&bv[j..]);
make_1d(result)
}
pub fn intersect1d<T>(
a: &Array<T, Ix1>,
b: &Array<T, Ix1>,
assume_unique: bool,
) -> FerrayResult<Array<T, Ix1>>
where
T: Element + PartialOrd + Copy,
{
let av = if assume_unique {
to_vec(a)
} else {
sorted_unique(&to_vec(a))
};
let bv = if assume_unique {
to_vec(b)
} else {
sorted_unique(&to_vec(b))
};
let mut result = Vec::new();
let (mut i, mut j) = (0, 0);
while i < av.len() && j < bv.len() {
match av[i]
.partial_cmp(&bv[j])
.unwrap_or(std::cmp::Ordering::Equal)
{
std::cmp::Ordering::Less => i += 1,
std::cmp::Ordering::Greater => j += 1,
std::cmp::Ordering::Equal => {
result.push(av[i]);
i += 1;
j += 1;
}
}
}
make_1d(result)
}
pub fn setdiff1d<T>(
a: &Array<T, Ix1>,
b: &Array<T, Ix1>,
assume_unique: bool,
) -> FerrayResult<Array<T, Ix1>>
where
T: Element + PartialOrd + Copy,
{
let av = if assume_unique {
to_vec(a)
} else {
sorted_unique(&to_vec(a))
};
let bv = if assume_unique {
to_vec(b)
} else {
sorted_unique(&to_vec(b))
};
let mut result = Vec::new();
let (mut i, mut j) = (0, 0);
while i < av.len() {
if j >= bv.len() {
result.push(av[i]);
i += 1;
} else {
match av[i]
.partial_cmp(&bv[j])
.unwrap_or(std::cmp::Ordering::Equal)
{
std::cmp::Ordering::Less => {
result.push(av[i]);
i += 1;
}
std::cmp::Ordering::Greater => {
j += 1;
}
std::cmp::Ordering::Equal => {
i += 1;
j += 1;
}
}
}
}
make_1d(result)
}
pub fn setxor1d<T>(
a: &Array<T, Ix1>,
b: &Array<T, Ix1>,
assume_unique: bool,
) -> FerrayResult<Array<T, Ix1>>
where
T: Element + PartialOrd + Copy,
{
let av = if assume_unique {
to_vec(a)
} else {
sorted_unique(&to_vec(a))
};
let bv = if assume_unique {
to_vec(b)
} else {
sorted_unique(&to_vec(b))
};
let mut result = Vec::new();
let (mut i, mut j) = (0, 0);
while i < av.len() && j < bv.len() {
match av[i]
.partial_cmp(&bv[j])
.unwrap_or(std::cmp::Ordering::Equal)
{
std::cmp::Ordering::Less => {
result.push(av[i]);
i += 1;
}
std::cmp::Ordering::Greater => {
result.push(bv[j]);
j += 1;
}
std::cmp::Ordering::Equal => {
i += 1;
j += 1;
}
}
}
result.extend_from_slice(&av[i..]);
result.extend_from_slice(&bv[j..]);
make_1d(result)
}
pub fn in1d<T>(
a: &Array<T, Ix1>,
b: &Array<T, Ix1>,
assume_unique: bool,
) -> FerrayResult<Array<bool, Ix1>>
where
T: Element + PartialOrd + Copy,
{
let av = to_vec(a);
let bv = if assume_unique {
to_vec(b)
} else {
sorted_unique(&to_vec(b))
};
let result: Vec<bool> = av
.iter()
.map(|&val| {
bv.binary_search_by(|probe| {
probe.partial_cmp(&val).unwrap_or(std::cmp::Ordering::Equal)
})
.is_ok()
})
.collect();
let n = result.len();
Array::from_vec(Ix1::new([n]), result)
}
pub fn isin<T>(
element: &Array<T, Ix1>,
test_elements: &Array<T, Ix1>,
assume_unique: bool,
) -> FerrayResult<Array<bool, Ix1>>
where
T: Element + PartialOrd + Copy,
{
in1d(element, test_elements, assume_unique)
}
#[cfg(test)]
mod tests {
use super::*;
fn arr(data: Vec<i32>) -> Array<i32, Ix1> {
let n = data.len();
Array::from_vec(Ix1::new([n]), data).unwrap()
}
#[test]
fn test_union1d() {
let a = arr(vec![1, 2, 3]);
let b = arr(vec![2, 3, 4]);
let u = union1d(&a, &b, false).unwrap();
let data: Vec<i32> = u.iter().copied().collect();
assert_eq!(data, vec![1, 2, 3, 4]);
}
#[test]
fn test_intersect1d() {
let a = arr(vec![1, 2, 3, 4]);
let b = arr(vec![2, 4, 6]);
let i = intersect1d(&a, &b, false).unwrap();
let data: Vec<i32> = i.iter().copied().collect();
assert_eq!(data, vec![2, 4]);
}
#[test]
fn test_setdiff1d() {
let a = arr(vec![1, 2, 3, 4]);
let b = arr(vec![2, 4]);
let d = setdiff1d(&a, &b, false).unwrap();
let data: Vec<i32> = d.iter().copied().collect();
assert_eq!(data, vec![1, 3]);
}
#[test]
fn test_setxor1d() {
let a = arr(vec![1, 2, 3]);
let b = arr(vec![2, 3, 4]);
let x = setxor1d(&a, &b, false).unwrap();
let data: Vec<i32> = x.iter().copied().collect();
assert_eq!(data, vec![1, 4]);
}
#[test]
fn test_in1d() {
let a = arr(vec![1, 2, 3, 4, 5]);
let b = arr(vec![2, 4]);
let r = in1d(&a, &b, false).unwrap();
let data: Vec<bool> = r.iter().copied().collect();
assert_eq!(data, vec![false, true, false, true, false]);
}
#[test]
fn test_isin() {
let elem = arr(vec![1, 2, 3, 4, 5]);
let test = arr(vec![3, 5]);
let r = isin(&elem, &test, false).unwrap();
let data: Vec<bool> = r.iter().copied().collect();
assert_eq!(data, vec![false, false, true, false, true]);
}
#[test]
fn test_union1d_with_duplicates() {
let a = arr(vec![3, 1, 2, 1]);
let b = arr(vec![4, 2, 3, 2]);
let u = union1d(&a, &b, false).unwrap();
let data: Vec<i32> = u.iter().copied().collect();
assert_eq!(data, vec![1, 2, 3, 4]);
}
#[test]
fn test_union1d_assume_unique() {
let a = arr(vec![1, 2, 3]);
let b = arr(vec![2, 3, 4]);
let u = union1d(&a, &b, true).unwrap();
let data: Vec<i32> = u.iter().copied().collect();
assert_eq!(data, vec![1, 2, 3, 4]);
}
#[test]
fn test_setdiff1d_empty_result() {
let a = arr(vec![1, 2, 3]);
let b = arr(vec![1, 2, 3, 4]);
let d = setdiff1d(&a, &b, false).unwrap();
assert_eq!(d.size(), 0);
}
#[test]
fn test_intersect1d_empty_result() {
let a = arr(vec![1, 2, 3]);
let b = arr(vec![4, 5, 6]);
let i = intersect1d(&a, &b, false).unwrap();
assert_eq!(i.size(), 0);
}
}