use super::andon::AndonConfig;
#[derive(Debug, Clone, PartialEq)]
pub struct SyntheticConfig {
pub augmentation_ratio: f32,
pub quality_threshold: f32,
pub diversity_weight: f32,
pub max_attempts: usize,
pub seed: u64,
pub andon: AndonConfig,
}
impl Default for SyntheticConfig {
fn default() -> Self {
Self {
augmentation_ratio: 0.5,
quality_threshold: 0.7,
diversity_weight: 0.3,
max_attempts: 10,
seed: 42,
andon: AndonConfig::default(),
}
}
}
impl SyntheticConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_augmentation_ratio(mut self, ratio: f32) -> Self {
self.augmentation_ratio = ratio.clamp(0.0, 10.0);
self
}
#[must_use]
pub fn with_quality_threshold(mut self, threshold: f32) -> Self {
self.quality_threshold = threshold.clamp(0.0, 1.0);
self
}
#[must_use]
pub fn with_diversity_weight(mut self, weight: f32) -> Self {
self.diversity_weight = weight.clamp(0.0, 1.0);
self
}
#[must_use]
pub fn with_max_attempts(mut self, attempts: usize) -> Self {
self.max_attempts = attempts.max(1);
self
}
#[must_use]
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = seed;
self
}
#[must_use]
pub fn with_andon(mut self, andon: AndonConfig) -> Self {
self.andon = andon;
self
}
#[must_use]
pub fn with_andon_enabled(mut self, enabled: bool) -> Self {
self.andon.enabled = enabled;
self
}
#[must_use]
pub fn with_andon_rejection_threshold(mut self, threshold: f32) -> Self {
self.andon.rejection_threshold = threshold.clamp(0.0, 1.0);
self
}
#[must_use]
pub fn target_count(&self, seed_count: usize) -> usize {
(seed_count as f32 * self.augmentation_ratio) as usize
}
#[must_use]
pub fn meets_quality(&self, score: f32) -> bool {
score >= self.quality_threshold
}
#[must_use]
pub fn combined_score(&self, quality: f32, diversity: f32) -> f32 {
let quality_weight = 1.0 - self.diversity_weight;
quality_weight * quality + self.diversity_weight * diversity
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = SyntheticConfig::default();
assert!((config.augmentation_ratio - 0.5).abs() < f32::EPSILON);
assert!((config.quality_threshold - 0.7).abs() < f32::EPSILON);
assert!((config.diversity_weight - 0.3).abs() < f32::EPSILON);
assert_eq!(config.max_attempts, 10);
assert_eq!(config.seed, 42);
}
#[test]
fn test_new_equals_default() {
let config1 = SyntheticConfig::new();
let config2 = SyntheticConfig::default();
assert_eq!(config1, config2);
}
#[test]
fn test_builder_pattern() {
let config = SyntheticConfig::new()
.with_augmentation_ratio(1.5)
.with_quality_threshold(0.8)
.with_diversity_weight(0.4)
.with_max_attempts(20)
.with_seed(123);
assert!((config.augmentation_ratio - 1.5).abs() < f32::EPSILON);
assert!((config.quality_threshold - 0.8).abs() < f32::EPSILON);
assert!((config.diversity_weight - 0.4).abs() < f32::EPSILON);
assert_eq!(config.max_attempts, 20);
assert_eq!(config.seed, 123);
}
#[test]
fn test_augmentation_ratio_clamping() {
let config = SyntheticConfig::new().with_augmentation_ratio(-1.0);
assert!((config.augmentation_ratio - 0.0).abs() < f32::EPSILON);
let config = SyntheticConfig::new().with_augmentation_ratio(15.0);
assert!((config.augmentation_ratio - 10.0).abs() < f32::EPSILON);
}
#[test]
fn test_quality_threshold_clamping() {
let config = SyntheticConfig::new().with_quality_threshold(-0.5);
assert!((config.quality_threshold - 0.0).abs() < f32::EPSILON);
let config = SyntheticConfig::new().with_quality_threshold(1.5);
assert!((config.quality_threshold - 1.0).abs() < f32::EPSILON);
}
#[test]
fn test_diversity_weight_clamping() {
let config = SyntheticConfig::new().with_diversity_weight(-0.2);
assert!((config.diversity_weight - 0.0).abs() < f32::EPSILON);
let config = SyntheticConfig::new().with_diversity_weight(1.2);
assert!((config.diversity_weight - 1.0).abs() < f32::EPSILON);
}
#[test]
fn test_max_attempts_minimum() {
let config = SyntheticConfig::new().with_max_attempts(0);
assert_eq!(config.max_attempts, 1);
}
#[test]
fn test_target_count() {
let config = SyntheticConfig::new().with_augmentation_ratio(0.5);
assert_eq!(config.target_count(100), 50);
assert_eq!(config.target_count(0), 0);
let config = SyntheticConfig::new().with_augmentation_ratio(2.0);
assert_eq!(config.target_count(100), 200);
}
#[test]
fn test_meets_quality() {
let config = SyntheticConfig::new().with_quality_threshold(0.7);
assert!(config.meets_quality(0.7));
assert!(config.meets_quality(0.9));
assert!(!config.meets_quality(0.69));
assert!(!config.meets_quality(0.0));
}
#[test]
fn test_combined_score() {
let config = SyntheticConfig::new().with_diversity_weight(0.3);
let score = config.combined_score(1.0, 0.0);
assert!((score - 0.7).abs() < f32::EPSILON);
let score = config.combined_score(0.0, 1.0);
assert!((score - 0.3).abs() < f32::EPSILON);
let score = config.combined_score(0.5, 0.5);
assert!((score - 0.5).abs() < f32::EPSILON);
}
#[test]
fn test_combined_score_extreme_weights() {
let config = SyntheticConfig::new().with_diversity_weight(0.0);
let score = config.combined_score(0.8, 0.2);
assert!((score - 0.8).abs() < f32::EPSILON);
let config = SyntheticConfig::new().with_diversity_weight(1.0);
let score = config.combined_score(0.8, 0.2);
assert!((score - 0.2).abs() < f32::EPSILON);
}
#[test]
fn test_config_clone() {
let config1 = SyntheticConfig::new()
.with_augmentation_ratio(1.0)
.with_seed(999);
let config2 = config1.clone();
assert_eq!(config1, config2);
}
#[test]
fn test_config_debug() {
let config = SyntheticConfig::default();
let debug = format!("{config:?}");
assert!(debug.contains("SyntheticConfig"));
assert!(debug.contains("augmentation_ratio"));
}
#[test]
fn test_default_andon_config() {
let config = SyntheticConfig::default();
assert!(config.andon.enabled);
assert!((config.andon.rejection_threshold - 0.90).abs() < f32::EPSILON);
}
#[test]
fn test_with_andon() {
use crate::synthetic::AndonConfig;
let andon = AndonConfig::new()
.with_enabled(false)
.with_rejection_threshold(0.85);
let config = SyntheticConfig::new().with_andon(andon);
assert!(!config.andon.enabled);
assert!((config.andon.rejection_threshold - 0.85).abs() < f32::EPSILON);
}
#[test]
fn test_with_andon_enabled() {
let config = SyntheticConfig::new().with_andon_enabled(false);
assert!(!config.andon.enabled);
let config = SyntheticConfig::new().with_andon_enabled(true);
assert!(config.andon.enabled);
}
#[test]
fn test_with_andon_rejection_threshold() {
let config = SyntheticConfig::new().with_andon_rejection_threshold(0.80);
assert!((config.andon.rejection_threshold - 0.80).abs() < f32::EPSILON);
}
#[test]
fn test_with_andon_rejection_threshold_clamping() {
let config = SyntheticConfig::new().with_andon_rejection_threshold(1.5);
assert!((config.andon.rejection_threshold - 1.0).abs() < f32::EPSILON);
let config = SyntheticConfig::new().with_andon_rejection_threshold(-0.5);
assert!((config.andon.rejection_threshold - 0.0).abs() < f32::EPSILON);
}
#[test]
fn test_andon_config_in_clone() {
let config1 = SyntheticConfig::new()
.with_andon_enabled(false)
.with_andon_rejection_threshold(0.75);
let config2 = config1.clone();
assert!(!config2.andon.enabled);
assert!((config2.andon.rejection_threshold - 0.75).abs() < f32::EPSILON);
}
}