use super::*;
fn fwd(op: &Op, _ctx: &dyn Context, operands: &mut dyn CoordinateSet) -> usize {
let n = operands.len();
let Ok(order) = op.params.series("order") else {
return n;
};
let dimensionality = order.len();
let mut pos = [0_usize, 1, 2, 3];
let mut sgn = [1., 1., 1., 1.];
for (index, value) in order.iter().enumerate() {
pos[index] = (value.abs() - 1.) as usize;
sgn[index] = 1_f64.copysign(*value);
}
let mut successes = 0_usize;
for i in 0..n {
let inp = operands.get_coord(i);
let mut out = inp;
for index in 0..dimensionality {
out[index] = inp[pos[index]] * sgn[index];
}
operands.set_coord(i, &out);
successes += 1;
}
successes
}
fn inv(op: &Op, _ctx: &dyn Context, operands: &mut dyn CoordinateSet) -> usize {
let n = operands.len();
let Ok(order) = op.params.series("order") else {
return n;
};
let dimensionality = order.len();
let mut pos = [0_usize, 1, 2, 3];
let mut sgn = [1., 1., 1., 1.];
for (index, value) in order.iter().enumerate() {
pos[index] = (value.abs() - 1.) as usize;
sgn[index] = 1_f64.copysign(*value);
}
let mut successes = 0_usize;
for i in 0..n {
let inp = operands.get_coord(i);
let mut out = inp;
for index in 0..dimensionality {
out[pos[index]] = inp[index] * sgn[index];
}
operands.set_coord(i, &out);
successes += 1;
}
successes
}
#[rustfmt::skip]
pub const GAMUT: [OpParameter; 2] = [
OpParameter::Flag { key: "inv" },
OpParameter::Series { key: "order", default: Some("1,2,3,4") },
];
pub fn new(parameters: &RawParameters, _ctx: &dyn Context) -> Result<Op, Error> {
let op = Op::basic(parameters, InnerOp(fwd), Some(InnerOp(inv)), &GAMUT)?;
let Ok(order) = op.params.series("order") else {
return Ok(op);
};
if order.len() > 4 {
return Err(Error::BadParam(
"order".to_string(),
"More than 4 indices given".to_string(),
));
}
for &o in order {
let i = o as i64;
if (i as f64) != o || i == 0 || (i.unsigned_abs() as usize) > order.len() {
return Err(Error::BadParam("order".to_string(), o.to_string()));
}
}
for o in 1_u64..5 {
if order.iter().filter(|x| (x.abs() as u64) == o).count() > 1 {
return Err(Error::BadParam(
"order".to_string(),
"duplicate axis specified".to_string(),
));
}
}
Ok(op)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn four_dim() -> Result<(), Error> {
let mut ctx = Minimal::default();
let op = ctx.op("axisswap order=2,1,-3,-4")?;
let mut operands = [Coor4D([1., 2., 3., 4.])];
ctx.apply(op, Fwd, &mut operands)?;
assert_eq!(operands[0][0], 2.);
assert_eq!(operands[0][1], 1.);
assert_eq!(operands[0][2], -3.);
assert_eq!(operands[0][3], -4.);
ctx.apply(op, Inv, &mut operands)?;
assert_eq!(operands[0][0], 1.);
assert_eq!(operands[0][1], 2.);
assert_eq!(operands[0][2], 3.);
assert_eq!(operands[0][3], 4.);
Ok(())
}
#[test]
fn default_order() -> Result<(), Error> {
let mut ctx = Minimal::default();
let op = ctx.op("axisswap")?;
let mut operands = [Coor4D([1., 2., 3., 4.])];
ctx.apply(op, Fwd, &mut operands)?;
assert_eq!(operands[0][0], 1.);
assert_eq!(operands[0][1], 2.);
assert_eq!(operands[0][2], 3.);
assert_eq!(operands[0][3], 4.);
ctx.apply(op, Inv, &mut operands)?;
assert_eq!(operands[0][0], 1.);
assert_eq!(operands[0][1], 2.);
assert_eq!(operands[0][2], 3.);
assert_eq!(operands[0][3], 4.);
Ok(())
}
#[test]
fn two_dim() -> Result<(), Error> {
let mut ctx = Minimal::default();
let op = ctx.op("axisswap order=2,-1")?;
let mut operands = [Coor4D([1., 2., 3., 4.])];
ctx.apply(op, Fwd, &mut operands)?;
assert_eq!(operands[0][0], 2.);
assert_eq!(operands[0][1], -1.);
assert_eq!(operands[0][2], 3.);
assert_eq!(operands[0][3], 4.);
ctx.apply(op, Inv, &mut operands)?;
assert_eq!(operands[0][0], 1.);
assert_eq!(operands[0][1], 2.);
assert_eq!(operands[0][2], 3.);
assert_eq!(operands[0][3], 4.);
Ok(())
}
#[test]
fn bad_parameters() -> Result<(), Error> {
let mut ctx = Minimal::default();
let op = ctx.op("axisswap order=4,4,4,2,-1");
assert!(matches!(op, Err(Error::BadParam(_, _))));
let op = ctx.op("axisswap order=4,-4,2,-1");
assert!(matches!(op, Err(Error::BadParam(_, _))));
let op = ctx.op("axisswap order=2,3");
assert!(matches!(op, Err(Error::BadParam(_, _))));
let op = ctx.op("axisswap order");
assert!(matches!(op, Err(Error::BadParam(_, _))));
let op = ctx.op("axisswap");
assert!(op.is_ok());
Ok(())
}
}