use std::collections::HashSet;
pub struct EquationHandler {
neq: usize,
is_prescribed: Vec<bool>,
e_to_iu: Vec<usize>,
e_to_ip: Vec<usize>,
u_sorted: Vec<usize>,
p_sorted: Vec<usize>,
}
impl EquationHandler {
pub fn new(neq: usize) -> Self {
let all: Vec<_> = (0..neq).collect(); EquationHandler {
neq,
is_prescribed: vec![false; neq],
e_to_iu: all.clone(),
e_to_ip: vec![usize::MAX; neq],
u_sorted: all,
p_sorted: Vec::new(),
}
}
pub fn recompute(&mut self, p_list: &[usize]) {
let mut p_set = HashSet::new();
for p in p_list {
if *p >= self.neq {
panic!("prescribed equation index is out of bounds");
}
p_set.insert(*p);
}
self.u_sorted.clear();
self.p_sorted.clear();
let mut iu = 0;
let mut ip = 0;
for e in 0..self.neq {
if p_set.contains(&e) {
self.is_prescribed[e] = true;
self.e_to_iu[e] = usize::MAX;
self.e_to_ip[e] = ip;
self.p_sorted.push(e);
ip += 1;
} else {
self.is_prescribed[e] = false;
self.e_to_iu[e] = iu;
self.e_to_ip[e] = usize::MAX;
self.u_sorted.push(e);
iu += 1;
}
}
}
pub fn neq(&self) -> usize {
self.neq
}
pub fn nu(&self) -> usize {
self.u_sorted.len()
}
pub fn np(&self) -> usize {
self.p_sorted.len()
}
pub fn is_unknown(&self, e: usize) -> bool {
!self.is_prescribed[e]
}
pub fn is_prescribed(&self, e: usize) -> bool {
self.is_prescribed[e]
}
pub fn iu(&self, e: usize) -> usize {
if self.e_to_iu[e] == usize::MAX {
panic!("global equation ID does not correspond to an unknown equation");
}
self.e_to_iu[e]
}
pub fn ip(&self, e: usize) -> usize {
if self.e_to_ip[e] == usize::MAX {
panic!("global equation ID does not correspond to a prescribed equation");
}
self.e_to_ip[e]
}
pub fn unknown(&self) -> &Vec<usize> {
&self.u_sorted
}
pub fn prescribed(&self) -> &Vec<usize> {
&self.p_sorted
}
}
#[cfg(test)]
mod tests {
use super::EquationHandler;
#[test]
fn new_creates_correct_initial_state() {
let neq = 6;
let handler = EquationHandler::new(neq);
assert_eq!(handler.neq(), 6);
assert_eq!(handler.nu(), 6); assert_eq!(handler.np(), 0);
for e in 0..neq {
assert!(!handler.is_prescribed(e));
assert_eq!(handler.iu(e), e);
}
assert_eq!(handler.unknown(), &vec![0, 1, 2, 3, 4, 5]);
assert_eq!(handler.prescribed(), &Vec::<usize>::new());
}
#[test]
fn new_handles_edge_cases() {
let handler = EquationHandler::new(1);
assert_eq!(handler.neq(), 1);
assert_eq!(handler.nu(), 1);
assert_eq!(handler.np(), 0);
assert_eq!(handler.unknown(), &vec![0]);
let handler = EquationHandler::new(0);
assert_eq!(handler.neq(), 0);
assert_eq!(handler.nu(), 0);
assert_eq!(handler.np(), 0);
assert!(handler.unknown().is_empty());
assert!(handler.prescribed().is_empty());
}
#[test]
fn recompute_works_with_prescribed_equations() {
let mut handler = EquationHandler::new(6);
let p_list = &[0, 3];
handler.recompute(p_list);
assert_eq!(handler.neq(), 6);
assert_eq!(handler.nu(), 4); assert_eq!(handler.np(), 2);
assert!(handler.is_prescribed(0));
assert!(!handler.is_prescribed(1));
assert!(!handler.is_prescribed(2));
assert!(handler.is_prescribed(3));
assert!(!handler.is_prescribed(4));
assert!(!handler.is_prescribed(5));
assert_eq!(handler.iu(1), 0); assert_eq!(handler.iu(2), 1); assert_eq!(handler.iu(4), 2); assert_eq!(handler.iu(5), 3);
assert_eq!(handler.ip(0), 0); assert_eq!(handler.ip(3), 1);
assert_eq!(handler.unknown(), &vec![1, 2, 4, 5]);
assert_eq!(handler.prescribed(), &vec![0, 3]);
}
#[test]
fn recompute_handles_all_prescribed() {
let mut handler = EquationHandler::new(3);
let p_list = &[0, 1, 2];
handler.recompute(p_list);
assert_eq!(handler.nu(), 0);
assert_eq!(handler.np(), 3);
for e in 0..3 {
assert!(handler.is_prescribed(e));
assert_eq!(handler.ip(e), e);
}
assert!(handler.unknown().is_empty());
assert_eq!(handler.prescribed(), &vec![0, 1, 2]);
}
#[test]
fn recompute_handles_all_unknown() {
let mut handler = EquationHandler::new(4);
let p_list = &[1, 3];
handler.recompute(p_list);
assert_eq!(handler.np(), 2);
handler.recompute(&[]);
assert_eq!(handler.nu(), 4);
assert_eq!(handler.np(), 0);
for e in 0..4 {
assert!(!handler.is_prescribed(e));
assert_eq!(handler.iu(e), e);
}
assert_eq!(handler.unknown(), &vec![0, 1, 2, 3]);
assert!(handler.prescribed().is_empty());
}
#[test]
fn recompute_handles_duplicate_prescriptions() {
let mut handler = EquationHandler::new(4);
let p_list = &[1, 1, 3, 1];
handler.recompute(p_list);
assert_eq!(handler.nu(), 2); assert_eq!(handler.np(), 2);
assert_eq!(handler.unknown(), &vec![0, 2]);
assert_eq!(handler.prescribed(), &vec![1, 3]);
}
#[test]
#[should_panic(expected = "prescribed equation index is out of bounds")]
fn recompute_panics_on_invalid_indices() {
let mut handler = EquationHandler::new(3);
let p_list = &[0, 5]; handler.recompute(p_list);
}
#[test]
#[should_panic(expected = "prescribed equation index is out of bounds")]
fn recompute_panics_on_max_usize() {
let mut handler = EquationHandler::new(3);
let p_list = &[usize::MAX];
handler.recompute(p_list);
}
#[test]
#[should_panic]
fn is_prescribed_panics_on_out_of_bounds() {
let handler = EquationHandler::new(3);
let _ = handler.is_prescribed(3); }
#[test]
#[should_panic]
fn is_prescribed_panics_on_large_index() {
let handler = EquationHandler::new(3);
let _ = handler.is_prescribed(100); }
#[test]
#[should_panic(expected = "global equation ID does not correspond to an unknown equation")]
fn iu_panics_on_prescribed_equation() {
let mut handler = EquationHandler::new(4);
handler.recompute(&[1]);
assert_eq!(handler.iu(0), 0);
assert_eq!(handler.iu(2), 1);
assert_eq!(handler.iu(3), 2);
let _ = handler.iu(1);
}
#[test]
#[should_panic(expected = "global equation ID does not correspond to a prescribed equation")]
fn ip_panics_on_unknown_equation() {
let mut handler = EquationHandler::new(4);
handler.recompute(&[0, 2]);
assert_eq!(handler.ip(0), 0);
assert_eq!(handler.ip(2), 1);
let _ = handler.ip(1);
}
#[test]
#[should_panic(expected = "global equation ID does not correspond to a prescribed equation")]
fn ip_panics_on_another_unknown_equation() {
let mut handler = EquationHandler::new(4);
handler.recompute(&[0, 2]);
let _ = handler.ip(3);
}
#[test]
fn documentation_example_works() {
let mut handler = EquationHandler::new(6);
let p_list = &[0, 3];
handler.recompute(p_list);
assert_eq!(handler.neq(), 6);
assert_eq!(handler.nu(), 4);
assert_eq!(handler.np(), 2);
assert!(handler.is_prescribed(0));
assert!(!handler.is_prescribed(1));
assert!(!handler.is_prescribed(2));
assert!(handler.is_prescribed(3));
assert!(!handler.is_prescribed(4));
assert!(!handler.is_prescribed(5));
assert_eq!(handler.iu(1), 0);
assert_eq!(handler.iu(2), 1);
assert_eq!(handler.iu(4), 2);
assert_eq!(handler.iu(5), 3);
assert_eq!(handler.ip(0), 0);
assert_eq!(handler.ip(3), 1);
assert_eq!(handler.unknown(), &vec![1, 2, 4, 5]);
assert_eq!(handler.prescribed(), &vec![0, 3]);
}
#[test]
fn complex_recompute_sequence() {
let mut handler = EquationHandler::new(8);
handler.recompute(&[1, 4, 7]);
assert_eq!(handler.nu(), 5);
assert_eq!(handler.np(), 3);
assert_eq!(handler.unknown(), &vec![0, 2, 3, 5, 6]);
assert_eq!(handler.prescribed(), &vec![1, 4, 7]);
handler.recompute(&[0, 2, 3, 6]);
assert_eq!(handler.nu(), 4);
assert_eq!(handler.np(), 4);
assert_eq!(handler.unknown(), &vec![1, 4, 5, 7]);
assert_eq!(handler.prescribed(), &vec![0, 2, 3, 6]);
handler.recompute(&[]);
assert_eq!(handler.nu(), 8);
assert_eq!(handler.np(), 0);
assert_eq!(handler.unknown(), &vec![0, 1, 2, 3, 4, 5, 6, 7]);
assert!(handler.prescribed().is_empty());
for e in 0..8 {
assert!(!handler.is_prescribed(e));
assert_eq!(handler.iu(e), e);
}
}
#[test]
fn consistency_checks() {
let mut handler = EquationHandler::new(10);
handler.recompute(&[2, 5, 8]);
assert_eq!(handler.nu() + handler.np(), handler.neq());
let mut all_equations = handler.unknown().clone();
all_equations.extend(handler.prescribed());
all_equations.sort();
let expected: Vec<_> = (0..10).collect();
assert_eq!(all_equations, expected);
for &u in handler.unknown() {
assert!(!handler.prescribed().contains(&u));
}
for &p in handler.prescribed() {
assert!(!handler.unknown().contains(&p));
}
for &u in handler.unknown() {
assert!(!handler.is_prescribed(u));
handler.iu(u);
}
for &p in handler.prescribed() {
assert!(handler.is_prescribed(p));
handler.ip(p);
}
}
#[test]
fn sorted_lists_are_actually_sorted() {
let mut handler = EquationHandler::new(10);
handler.recompute(&[7, 2, 9, 1, 5]);
let unknown = handler.unknown();
assert_eq!(unknown, &vec![0, 3, 4, 6, 8]);
assert!(unknown.windows(2).all(|w| w[0] < w[1]));
let prescribed = handler.prescribed();
assert_eq!(prescribed, &vec![1, 2, 5, 7, 9]);
assert!(prescribed.windows(2).all(|w| w[0] < w[1]));
}
#[test]
fn prescribed_set_handles_duplicates() {
let mut handler = EquationHandler::new(4);
let p_list = &[1, 1, 3, 1, 3];
handler.recompute(p_list);
assert_eq!(handler.nu(), 2); assert_eq!(handler.np(), 2); assert_eq!(handler.unknown(), &vec![0, 2]);
assert_eq!(handler.prescribed(), &vec![1, 3]);
}
#[test]
fn comprehensive_boundary_condition_example() {
let mut handler = EquationHandler::new(8);
let boundary_nodes = &[0, 1, 6, 7, 2, 5]; handler.recompute(boundary_nodes);
for &node in boundary_nodes {
assert!(handler.is_prescribed(node));
}
for &interior in &[3, 4] {
assert!(!handler.is_prescribed(interior));
}
assert_eq!(handler.np(), 6); assert_eq!(handler.nu(), 2);
assert_eq!(handler.prescribed(), &vec![0, 1, 2, 5, 6, 7]);
assert_eq!(handler.unknown(), &vec![3, 4]);
}
#[test]
fn empty_system_edge_case() {
let mut handler = EquationHandler::new(0);
handler.recompute(&[]);
assert_eq!(handler.np(), 0);
assert_eq!(handler.nu(), 0);
}
#[test]
fn single_equation_system() {
let mut handler = EquationHandler::new(1);
handler.recompute(&[0]);
assert!(handler.is_prescribed(0));
assert_eq!(handler.np(), 1);
assert_eq!(handler.nu(), 0);
handler.recompute(&[]);
assert!(!handler.is_prescribed(0));
assert_eq!(handler.np(), 0);
assert_eq!(handler.nu(), 1);
}
#[test]
fn recompute_clears_previous_state() {
let mut handler = EquationHandler::new(6);
handler.recompute(&[0, 2, 4]);
assert_eq!(handler.np(), 3);
handler.recompute(&[1, 3]);
assert_eq!(handler.np(), 2);
assert!(!handler.is_prescribed(0));
assert!(!handler.is_prescribed(2));
assert!(!handler.is_prescribed(4));
assert!(handler.is_prescribed(1));
assert!(handler.is_prescribed(3));
}
#[test]
fn is_unknown_works() {
let mut handler = EquationHandler::new(4);
handler.recompute(&[1]);
assert!(handler.is_unknown(0));
assert!(!handler.is_unknown(1));
assert!(handler.is_unknown(2));
assert!(handler.is_unknown(3));
}
#[test]
#[should_panic]
fn is_unknown_panics_on_out_of_bounds() {
let handler = EquationHandler::new(3);
let _ = handler.is_unknown(3);
}
}