#[test]
fn rotation_matrix_plan_basic() {
    let mut system = build_plane_system();
    let sel = system.select("name CA").unwrap();
    let mut plan = RotationMatrixPlan::new(sel, ReferenceMode::Topology, false);
    let mut frame = system.positions0.clone().unwrap();
    for p in frame.iter_mut() {
        p[0] += 10.0;
    }
    let frames = vec![frame];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 1);
            assert_eq!(cols, 9);
            assert!((data[0] - 1.0).abs() < 1e-6);
            assert!((data[4] - 1.0).abs() < 1e-6);
            assert!((data[8] - 1.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn bfactors_plan_basic() {
    let mut system = build_system();
    let sel = system.select("resid 1").unwrap();
    let mut plan = BfactorsPlan::new(sel);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[1.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 2);
            let expected = (8.0 * std::f64::consts::PI * std::f64::consts::PI / 3.0) * 0.25;
            assert!((vals[0] as f64 - expected).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn multidihedral_plan_basic() {
    let mut system = build_four_resid_system();
    let sel_a = system.select("resid 1").unwrap();
    let sel_b = system.select("resid 2").unwrap();
    let sel_c = system.select("resid 3").unwrap();
    let sel_d = system.select("resid 4").unwrap();
    let defs = vec![
        (sel_a.clone(), sel_b.clone(), sel_c.clone(), sel_d.clone()),
        (sel_a, sel_b, sel_c, sel_d),
    ];
    let mut plan = MultiDihedralPlan::new(defs, false, PbcMode::None, true, false);
    let frames = vec![system.positions0.clone().unwrap()];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 1);
            assert_eq!(cols, 2);
            assert!((data[0] + 90.0).abs() < 1e-4);
            assert!((data[1] + 90.0).abs() < 1e-4);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn closest_plan_basic() {
    let mut system = build_four_resid_system();
    let target = system.select("resid 1").unwrap();
    let probe = system.select("resid 2:4").unwrap();
    let mut plan = ClosestPlan::new(target, probe, 2, PbcMode::None);
    let frames = vec![system.positions0.clone().unwrap()];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 1);
            assert_eq!(cols, 2);
            assert!((data[0] - 1.0).abs() < 1e-6);
            assert!((data[1] - 2.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn replicate_cell_plan_basic() {
    let mut system = build_system();
    let sel = system.select("resid 1").unwrap();
    let mut plan = ReplicateCellPlan::new(sel, [2, 1, 1]);
    let chunk = traj_core::frame::FrameChunk {
        n_atoms: 2,
        n_frames: 1,
        coords: vec![[1.0, 2.0, 3.0, 1.0], [4.0, 5.0, 6.0, 1.0]],
        box_: vec![traj_core::frame::Box3::Orthorhombic { lx: 10.0, ly: 10.0, lz: 10.0 }],
        time_ps: None,
    };
    let device = Device::cpu();
    plan.init(&system, &device).unwrap();
    plan.process_chunk(&chunk, &system, &device).unwrap();
    let out = plan.finalize().unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 12);
            assert!((vals[0] - 1.0).abs() < 1e-6);
            assert!((vals[3] - 4.0).abs() < 1e-6);
            assert!((vals[6] - 11.0).abs() < 1e-6);
            assert!((vals[9] - 14.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn replicate_cell_plan_triclinic() {
    let mut system = build_single_atom_system();
    let sel = system.select("name CA").unwrap();
    let mut plan = ReplicateCellPlan::new(sel, [1, 2, 1]);
    let chunk = traj_core::frame::FrameChunk {
        n_atoms: 1,
        n_frames: 1,
        coords: vec![[0.0, 0.0, 0.0, 1.0]],
        box_: vec![traj_core::frame::Box3::Triclinic {
            m: [2.0, 0.0, 0.0, 0.5, 3.0, 0.0, 0.0, 0.0, 4.0],
        }],
        time_ps: None,
    };
    let device = Device::cpu();
    plan.init(&system, &device).unwrap();
    plan.process_chunk(&chunk, &system, &device).unwrap();
    let out = plan.finalize().unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 6);
            assert!((vals[0] - 0.0).abs() < 1e-6);
            assert!((vals[1] - 0.0).abs() < 1e-6);
            assert!((vals[2] - 0.0).abs() < 1e-6);
            assert!((vals[3] - 0.5).abs() < 1e-6);
            assert!((vals[4] - 3.0).abs() < 1e-6);
            assert!((vals[5] - 0.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn matrix_plan_distance_basic() {
    let mut system = build_two_resid_system();
    let sel = system.select("resid 1:2").unwrap();
    let mut plan = MatrixPlan::new(sel, MatrixMode::Distance, PbcMode::None);
    let frames = vec![vec![[0.0, 0.0, 0.0, 1.0], [3.0, 4.0, 0.0, 1.0]]];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 2);
            assert_eq!(cols, 2);
            assert!((data[1] - 5.0).abs() < 1e-6);
            assert!((data[2] - 5.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn pca_plan_basic() {
    let mut system = build_system();
    let sel = system.select("resid 1").unwrap();
    let mut plan = PcaPlan::new(sel, 2, false);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[1.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Pca(pca) => {
            assert_eq!(pca.n_components, 2);
            assert_eq!(pca.n_features, 6);
            assert_eq!(pca.eigenvalues.len(), 2);
            assert_eq!(pca.eigenvectors.len(), 12);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn vector_plan_basic() {
    let mut system = build_two_resid_system();
    let sel_a = system.select("resid 1").unwrap();
    let sel_b = system.select("resid 2").unwrap();
    let mut plan = VectorPlan::new(sel_a, sel_b, false, PbcMode::None);
    let frames = vec![vec![[0.0, 0.0, 0.0, 1.0], [1.0, 2.0, 3.0, 1.0]]];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 1);
            assert_eq!(cols, 3);
            assert!((data[0] - 1.0).abs() < 1e-6);
            assert!((data[1] - 2.0).abs() < 1e-6);
            assert!((data[2] - 3.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn get_velocity_plan_basic() {
    let mut system = build_two_resid_system();
    let sel = system.select("resid 1:2").unwrap();
    let mut plan = GetVelocityPlan::new(sel);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[1.0, 0.0, 0.0, 1.0], [1.0, 2.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 2);
            assert_eq!(cols, 6);
            assert_eq!(&data[0..6], &[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
            assert!((data[6] - 1.0).abs() < 1e-6);
            assert!((data[9] - 0.0).abs() < 1e-6);
            assert!((data[10] - 2.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn set_velocity_plan_deterministic() {
    let system = build_two_resid_system();
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[1.0, 1.0, 0.0, 1.0], [2.0, 1.0, 0.0, 1.0]],
    ];
    let mut traj_a = InMemoryTraj::new(frames.clone());
    let mut traj_b = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let sel = exec.system_mut().select("resid 1:2").unwrap();
    let mut plan_a = SetVelocityPlan::new(sel.clone(), 100.0, 7);
    let mut plan_b = SetVelocityPlan::new(sel, 100.0, 7);
    let out_a = exec.run_plan(&mut plan_a, &mut traj_a).unwrap();
    let out_b = exec.run_plan(&mut plan_b, &mut traj_b).unwrap();
    match (out_a, out_b) {
        (
            PlanOutput::Matrix { data: a, rows: ra, cols: ca },
            PlanOutput::Matrix { data: b, rows: rb, cols: cb },
        ) => {
            assert_eq!(ra, 2);
            assert_eq!(ca, 6);
            assert_eq!(ra, rb);
            assert_eq!(ca, cb);
            assert_eq!(a, b);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn mean_structure_plan_basic() {
    let mut system = build_system();
    let sel = system.select("resid 1").unwrap();
    let mut plan = MeanStructurePlan::new(sel);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[2.0, 0.0, 0.0, 1.0], [3.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 6);
            assert!((vals[0] - 1.0).abs() < 1e-6);
            assert!((vals[3] - 2.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn molsurf_plan_basic() {
    let mut system = build_two_resid_system();
    let sel = system.select("resid 1:2").unwrap();
    let mut plan = MolSurfPlan::new(sel);
    let frames = vec![vec![[0.0, 0.0, 0.0, 1.0], [2.0, 3.0, 4.0, 1.0]]];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 1);
            assert!((vals[0] - 52.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn xtalsymm_plan_basic() {
    let mut system = build_two_resid_system();
    let sel = system.select("resid 1:2").unwrap();
    let mut plan = XtalSymmPlan::new(sel, [2, 1, 1]);
    let frames = vec![vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]]];
    let mut traj = InMemoryTrajWithBox::new(
        frames,
        Box3::Orthorhombic {
            lx: 10.0,
            ly: 10.0,
            lz: 10.0,
        },
    );
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 12);
            assert!((vals[0] - 0.0).abs() < 1e-6);
            assert!((vals[3] - 1.0).abs() < 1e-6);
            assert!((vals[6] - 10.0).abs() < 1e-6);
            assert!((vals[9] - 11.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn make_structure_plan_basic() {
    let mut system = build_system();
    let sel = system.select("resid 1").unwrap();
    let mut plan = MakeStructurePlan::new(sel);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]],
        vec![[2.0, 0.0, 0.0, 1.0], [4.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 6);
            assert!((vals[0] - 1.0).abs() < 1e-6);
            assert!((vals[3] - 3.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn lowestcurve_plan_basic() {
    let mut system = build_two_resid_system();
    let sel_a = system.select("resid 1").unwrap();
    let sel_b = system.select("resid 2").unwrap();
    let mut plan = LowestCurvePlan::new(sel_a, sel_b, false, PbcMode::None);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]],
        vec![[0.0, 0.0, 0.0, 1.0], [0.5, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 1);
            assert!((vals[0] - 0.5).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn permute_dihedrals_plan_basic() {
    let mut system = build_four_resid_system();
    let sel_a = system.select("resid 1").unwrap();
    let sel_b = system.select("resid 2").unwrap();
    let sel_c = system.select("resid 3").unwrap();
    let sel_d = system.select("resid 4").unwrap();
    let defs = vec![(sel_a, sel_b, sel_c, sel_d)];
    let mut plan = PermuteDihedralsPlan::new(defs, false, PbcMode::None, true, false);
    let frames = vec![system.positions0.clone().unwrap()];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 1);
            assert_eq!(cols, 1);
            assert!((data[0] + 90.0).abs() < 1e-4);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn distance_rmsd_plan_basic() {
    let mut system = build_two_resid_system();
    let sel = system.select("resid 1:2").unwrap();
    let mut plan = DistanceRmsdPlan::new(sel, ReferenceMode::Frame0, PbcMode::None);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[0.0, 0.0, 0.0, 1.0], [3.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Series(vals) => {
            assert_eq!(vals.len(), 2);
            assert!((vals[0]).abs() < 1e-6);
            assert!((vals[1] - 2.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn pairwise_rmsd_plan_basic() {
    let mut system = build_two_resid_system();
    let sel = system.select("resid 1:2").unwrap();
    let mut plan = PairwiseRmsdPlan::new(sel, PairwiseMetric::Nofit, PbcMode::None);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[0.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 2);
            assert_eq!(cols, 2);
            let rmsd = (0.5f32).sqrt();
            assert!((data[1] - rmsd).abs() < 1e-6);
            assert!((data[2] - rmsd).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn pairwise_rmsd_plan_dme() {
    let mut system = build_two_resid_system();
    let sel = system.select("resid 1:2").unwrap();
    let mut plan = PairwiseRmsdPlan::new(sel, PairwiseMetric::Dme, PbcMode::None);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[0.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 2);
            assert_eq!(cols, 2);
            assert!((data[1] - 1.0).abs() < 1e-6);
            assert!((data[2] - 1.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn radgyr_tensor_plan_basic() {
    let mut system = build_system();
    let sel = system.select("resid 1").unwrap();
    let mut plan = RadgyrTensorPlan::new(sel, false);
    let frames = vec![vec![[0.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]]];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::Matrix { data, rows, cols } => {
            assert_eq!(rows, 1);
            assert_eq!(cols, 7);
            assert!((data[0] - 1.0).abs() < 1e-6);
            assert!((data[1] - 1.0).abs() < 1e-6);
            assert!(data[2].abs() < 1e-6);
            assert!(data[3].abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}


#[test]
fn msd_plan_simple() {
    let mut system = build_system();
    let sel = system.select("name CA").unwrap();
    let mut plan = MsdPlan::new(sel, GroupBy::Resid);
    let frames = vec![
        vec![[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]],
        vec![[1.0, 0.0, 0.0, 1.0], [2.0, 0.0, 0.0, 1.0]],
    ];
    let mut traj = InMemoryTraj::new(frames);
    let mut exec = Executor::new(system);
    let out = exec.run_plan(&mut plan, &mut traj).unwrap();
    match out {
        PlanOutput::TimeSeries { rows, cols, data, .. } => {
            assert_eq!(rows, 1);
            assert_eq!(cols, 8);
            assert!((data[0] - 1.0).abs() < 1e-6);
            assert!((data[1] - 1.0).abs() < 1e-6);
            assert!((data[6] - 1.0).abs() < 1e-6);
            assert!((data[7] - 1.0).abs() < 1e-6);
        }
        _ => panic!("unexpected output"),
    }
}
