use serde::{Deserialize, Serialize};
use std::error::Error;
use strum_macros::Display;
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
pub enum AggrOp {
Key(Option<Vec<usize>>),
Add,
Min,
Max,
BoolMapOr,
}
#[derive(Debug, Clone, Display)]
pub enum AggregationError {
FlatSetIsNotData,
FlatFlowInfoNFv9NotSupported,
OperationNotSupported,
}
impl Error for AggregationError {}
#[inline]
pub fn reduce_num<T: Copy + std::ops::AddAssign + Ord>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
op: &AggrOp,
) -> Result<(), AggregationError> {
match op {
AggrOp::Key(indices) => set_field(lhs, rhs, indices),
AggrOp::Add => aggr_add(lhs, rhs),
AggrOp::Min => aggr_min(lhs, rhs),
AggrOp::Max => aggr_max(lhs, rhs),
AggrOp::BoolMapOr => Err(AggregationError::OperationNotSupported),
}
}
#[inline]
pub fn reduce_boolmap<T: Copy + std::ops::BitOrAssign>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
op: &AggrOp,
) -> Result<(), AggregationError> {
match op {
AggrOp::Key(indices) => set_field(lhs, rhs, indices),
AggrOp::Add => Err(AggregationError::OperationNotSupported),
AggrOp::Min => Err(AggregationError::OperationNotSupported),
AggrOp::Max => Err(AggregationError::OperationNotSupported),
AggrOp::BoolMapOr => aggr_bitor(lhs, rhs),
}
}
#[inline]
pub fn reduce_misc<T: Copy>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
op: &AggrOp,
) -> Result<(), AggregationError> {
match op {
AggrOp::Key(indices) => set_field(lhs, rhs, indices),
AggrOp::Add => Err(AggregationError::OperationNotSupported),
AggrOp::Min => Err(AggregationError::OperationNotSupported),
AggrOp::Max => Err(AggregationError::OperationNotSupported),
AggrOp::BoolMapOr => Err(AggregationError::OperationNotSupported),
}
}
#[inline]
pub fn reduce_misc_clone<T: Clone>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
op: &AggrOp,
) -> Result<(), AggregationError> {
match op {
AggrOp::Key(indices) => set_field_clone(lhs, rhs, indices),
AggrOp::Add => Err(AggregationError::OperationNotSupported),
AggrOp::Min => Err(AggregationError::OperationNotSupported),
AggrOp::Max => Err(AggregationError::OperationNotSupported),
AggrOp::BoolMapOr => Err(AggregationError::OperationNotSupported),
}
}
#[inline]
fn set_field<T: Copy>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
indices: &Option<Vec<usize>>,
) -> Result<(), AggregationError> {
if let Some(idxs) = indices {
if let Some(rhs_vec) = rhs {
let res: Vec<T> = idxs
.iter()
.filter_map(|&idx| rhs_vec.get(idx).copied())
.collect();
*lhs = if res.is_empty() { None } else { Some(res) };
}
} else if let Some(rhs_vec) = rhs {
*lhs = Some(rhs_vec.to_vec());
} else {
*lhs = None;
}
Ok(())
}
#[inline]
fn set_field_clone<T: Clone>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
indices: &Option<Vec<usize>>,
) -> Result<(), AggregationError> {
if let Some(idxs) = indices {
if let Some(rhs_vec) = rhs {
let res: Vec<T> = idxs
.iter()
.filter_map(|&idx| rhs_vec.get(idx).cloned())
.collect();
*lhs = if res.is_empty() { None } else { Some(res) };
}
} else {
*lhs = rhs.clone();
}
Ok(())
}
#[inline]
fn aggr_add<T: Copy + std::ops::AddAssign>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
) -> Result<(), AggregationError> {
match (lhs.as_mut(), rhs) {
(None, _) => *lhs = rhs.clone(),
(Some(_), None) => {
}
(Some(lhs), Some(rhs)) => {
lhs.iter_mut().zip(rhs.iter()).for_each(|(a, b)| *a += *b);
if rhs.len() > lhs.len() {
for i in &rhs[lhs.len()..] {
lhs.push(*i);
}
}
}
}
Ok(())
}
#[inline]
fn aggr_min<T: Copy + std::cmp::Ord>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
) -> Result<(), AggregationError> {
match (lhs.as_mut(), rhs) {
(None, _) => *lhs = rhs.clone(),
(Some(_), None) => {
}
(Some(lhs), Some(rhs)) => {
lhs.iter_mut()
.zip(rhs.iter())
.for_each(|(a, b)| *a = std::cmp::min(*a, *b));
if rhs.len() > lhs.len() {
for i in &rhs[lhs.len()..] {
lhs.push(*i);
}
}
}
}
Ok(())
}
#[inline]
fn aggr_max<T: Copy + std::cmp::Ord>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
) -> Result<(), AggregationError> {
match (lhs.as_mut(), rhs) {
(None, _) => *lhs = rhs.clone(),
(Some(_), None) => {
}
(Some(lhs), Some(rhs)) => {
lhs.iter_mut()
.zip(rhs.iter())
.for_each(|(a, b)| *a = std::cmp::max(*a, *b));
if rhs.len() > lhs.len() {
for i in &rhs[lhs.len()..] {
lhs.push(*i);
}
}
}
}
Ok(())
}
#[inline]
fn aggr_bitor<T: Copy + std::ops::BitOrAssign>(
lhs: &mut Option<Vec<T>>,
rhs: &Option<Vec<T>>,
) -> Result<(), AggregationError> {
match (lhs.as_mut(), rhs) {
(None, _) => *lhs = rhs.clone(),
(Some(_), None) => {
}
(Some(lhs), Some(rhs)) => {
lhs.iter_mut().zip(rhs.iter()).for_each(|(a, b)| *a |= *b);
if rhs.len() > lhs.len() {
for i in &rhs[lhs.len()..] {
lhs.push(*i);
}
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reduce_num_add_lhs_longer() {
let mut lhs = Some(vec![1, 1, 1]);
let rhs = Some(vec![1, 1]);
let op = AggrOp::Add;
reduce_num(&mut lhs, &rhs, &op).unwrap();
assert_eq!(lhs, Some(vec![2, 2, 1]));
}
#[test]
fn test_reduce_num_add_rhs_longer() {
let mut lhs = Some(vec![1, 1]);
let rhs = Some(vec![1, 1, 1]);
let op = AggrOp::Add;
reduce_num(&mut lhs, &rhs, &op).unwrap();
assert_eq!(lhs, Some(vec![2, 2, 1]));
}
#[test]
fn test_reduce_num_min() {
let mut lhs = Some(vec![5, 6, 7]);
let rhs = Some(vec![4, 7, 6]);
let op = AggrOp::Min;
reduce_num(&mut lhs, &rhs, &op).unwrap();
assert_eq!(lhs, Some(vec![4, 6, 6]));
}
#[test]
fn test_reduce_num_max() {
let mut lhs = Some(vec![1, 2, 3]);
let rhs = Some(vec![4, 1, 5]);
let op = AggrOp::Max;
reduce_num(&mut lhs, &rhs, &op).unwrap();
assert_eq!(lhs, Some(vec![4, 2, 5]));
}
#[test]
fn test_reduce_boolmap_bitor() {
let mut lhs = Some(vec![0b001, 0b001, 0b010]);
let rhs = Some(vec![0b001, 0b0100, 0b100]);
let op = AggrOp::BoolMapOr;
reduce_boolmap(&mut lhs, &rhs, &op).unwrap();
assert_eq!(lhs, Some(vec![0b001, 0b101, 0b110]));
}
#[test]
fn test_reduce_misc() {
let mut lhs = None;
let rhs = Some(vec![1, 2, 3, 4, 5]);
let op = AggrOp::Key(Some(vec![0, 2, 4]));
reduce_misc(&mut lhs, &rhs, &op).unwrap();
assert_eq!(lhs, Some(vec![1, 3, 5]));
}
#[test]
fn test_reduce_misc_clone() {
let mut lhs = None;
let rhs = Some(vec![
String::from("a"),
String::from("b"),
String::from("c"),
]);
let op = AggrOp::Key(Some(vec![0, 2]));
reduce_misc_clone(&mut lhs, &rhs, &op).unwrap();
assert_eq!(lhs, Some(vec![String::from("a"), String::from("c")]));
}
}