use std::collections::HashMap;
use cobre_core::{GenericConstraint, ResolvedGenericConstraintBounds};
use crate::constraints::GenericConstraintBoundsRow;
#[must_use]
pub fn resolve_generic_constraint_bounds(
constraints: &[GenericConstraint],
raw_bounds: &[GenericConstraintBoundsRow],
) -> ResolvedGenericConstraintBounds {
let id_to_idx: HashMap<i32, usize> = constraints
.iter()
.enumerate()
.map(|(idx, c)| (c.id.0, idx))
.collect();
ResolvedGenericConstraintBounds::new(
&id_to_idx,
raw_bounds
.iter()
.map(|r| (r.constraint_id, r.stage_id, r.block_id, r.bound)),
)
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::too_many_lines,
clippy::float_cmp
)]
mod tests {
use super::*;
use cobre_core::EntityId;
use cobre_core::generic_constraint::{ConstraintExpression, ConstraintSense};
fn make_constraint(id: i32) -> GenericConstraint {
use cobre_core::generic_constraint::SlackConfig;
GenericConstraint {
id: EntityId(id),
name: format!("c{id}"),
description: None,
sense: ConstraintSense::LessEqual,
expression: ConstraintExpression { terms: vec![] },
slack: SlackConfig {
enabled: false,
penalty: None,
},
}
}
fn make_row(
constraint_id: i32,
stage_id: i32,
block_id: Option<i32>,
bound: f64,
) -> GenericConstraintBoundsRow {
GenericConstraintBoundsRow {
constraint_id,
stage_id,
block_id,
bound,
}
}
#[test]
fn test_empty_constraints_empty_bounds() {
let table = resolve_generic_constraint_bounds(&[], &[]);
assert!(!table.is_active(0, 0));
assert!(table.bounds_for_stage(0, 0).is_empty());
}
#[test]
fn test_constraints_no_bounds() {
let constraints = vec![make_constraint(0), make_constraint(1)];
let table = resolve_generic_constraint_bounds(&constraints, &[]);
assert!(!table.is_active(0, 0));
assert!(!table.is_active(1, 0));
}
#[test]
fn test_two_constraints_sparse_bounds() {
let constraints = vec![make_constraint(0), make_constraint(1)];
let rows = vec![make_row(0, 0, None, 100.0), make_row(0, 1, None, 200.0)];
let table = resolve_generic_constraint_bounds(&constraints, &rows);
assert!(table.is_active(0, 0));
assert!(table.is_active(0, 1));
assert!(!table.is_active(1, 0));
assert!(!table.is_active(1, 1));
let s0 = table.bounds_for_stage(0, 0);
assert_eq!(s0.len(), 1);
assert!((s0[0].1 - 100.0).abs() < f64::EPSILON);
assert!(s0[0].0.is_none());
let s1 = table.bounds_for_stage(0, 1);
assert_eq!(s1.len(), 1);
assert!((s1[0].1 - 200.0).abs() < f64::EPSILON);
}
#[test]
fn test_block_specific_bounds() {
let constraints = vec![make_constraint(0)];
let rows = vec![
make_row(0, 0, None, 50.0),
make_row(0, 0, Some(0), 60.0),
make_row(0, 0, Some(1), 70.0),
];
let table = resolve_generic_constraint_bounds(&constraints, &rows);
assert!(table.is_active(0, 0));
let slice = table.bounds_for_stage(0, 0);
assert_eq!(slice.len(), 3);
assert_eq!(slice[0], (None, 50.0));
assert_eq!(slice[1], (Some(0), 60.0));
assert_eq!(slice[2], (Some(1), 70.0));
}
#[test]
fn test_unknown_constraint_id_skipped() {
let constraints = vec![make_constraint(0)];
let rows = vec![
make_row(0, 0, None, 100.0),
make_row(99, 0, None, 9999.0), ];
let table = resolve_generic_constraint_bounds(&constraints, &rows);
assert!(table.is_active(0, 0));
let slice = table.bounds_for_stage(0, 0);
assert_eq!(slice.len(), 1);
assert!((slice[0].1 - 100.0).abs() < f64::EPSILON);
}
#[test]
fn test_ac_is_active_true() {
let constraints = vec![make_constraint(0), make_constraint(1)];
let rows = vec![make_row(0, 0, None, 100.0), make_row(0, 1, None, 150.0)];
let table = resolve_generic_constraint_bounds(&constraints, &rows);
assert!(table.is_active(0, 0));
}
#[test]
fn test_ac_is_active_false() {
let constraints = vec![make_constraint(0), make_constraint(1)];
let rows = vec![make_row(0, 0, None, 100.0)];
let table = resolve_generic_constraint_bounds(&constraints, &rows);
assert!(!table.is_active(1, 0));
}
#[test]
fn test_ac_bounds_for_stage() {
let constraints = vec![make_constraint(0)];
let rows = vec![make_row(0, 0, None, 100.0)];
let table = resolve_generic_constraint_bounds(&constraints, &rows);
let slice = table.bounds_for_stage(0, 0);
assert_eq!(slice.len(), 1);
assert_eq!(slice[0], (None, 100.0));
}
}