From: Jeff Hemminger Date: Wed, 24 Dec 2025 01:22:17 +0000 (+0900) Subject: formatting X-Git-Url: http://gitweb.hemminger.haus/?a=commitdiff_plain;h=63efbcf8c52ab845d100723e6a21ff365de094b0;p=algorithms.git formatting --- diff --git a/src/algorithms/interview_scheduling/earliest_completion.rs b/src/algorithms/interview_scheduling/earliest_completion.rs index 5397386..1fd7656 100644 --- a/src/algorithms/interview_scheduling/earliest_completion.rs +++ b/src/algorithms/interview_scheduling/earliest_completion.rs @@ -4,11 +4,10 @@ pub struct Time(pub T) where T: Ord + Copy; - #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Interview -where - T: Ord + Copy, // ← Add this constraint +pub struct Interview +where + T: Ord + Copy, // ← Add this constraint { pub start: Time, pub end: Time, @@ -17,7 +16,7 @@ where impl Interview where - T: Ord + Copy, // ← Also add here for impl blocks + T: Ord + Copy, // ← Also add here for impl blocks { pub fn new(id: usize, start: T, end: T) -> Self { Self { @@ -26,7 +25,7 @@ where end: Time(end), } } - + pub fn duration(&self) -> T { // Implementation depends on what operations T supports // This is a placeholder @@ -38,7 +37,7 @@ where pub fn sort_by_earliest_completion(interviews: &mut [Interview]) { interviews.sort_by(|a, b| a.end.cmp(&b.end)); } -} +} /// Optimal interval scheduling using earliest completion time /// Returns maximum set of non-overlapping interviews @@ -49,22 +48,22 @@ where if interviews.is_empty() { return vec![]; } - + // Step 1: Sort by earliest completion time Interview::sort_by_earliest_completion(&mut interviews); - + // Step 2: Greedily select non-overlapping intervals - let mut selected = vec![interviews[0].clone()]; - + let mut selected = vec![interviews[0].clone()]; + for interview in interviews.into_iter().skip(1) { let last_selected = selected.last().unwrap(); - + // If this interview starts after the last one ends, select it if interview.start >= last_selected.end { selected.push(interview); } } - + selected } @@ -72,7 +71,7 @@ where mod tests { use crate::interview_scheduling::test_data::*; - + #[test] fn test_with_generated_data() { let config = InterviewGenConfig { @@ -83,10 +82,10 @@ mod tests { max_duration: 15, seed: Some(42), }; - + let interviews = generate_random_interviews(&config); assert_eq!(interviews.len(), 5); - + // Test your algorithm with this data // let result = your_scheduling_algorithm(&interviews); } diff --git a/src/algorithms/interview_scheduling/mod.rs b/src/algorithms/interview_scheduling/mod.rs index 4dd6544..43d52f1 100644 --- a/src/algorithms/interview_scheduling/mod.rs +++ b/src/algorithms/interview_scheduling/mod.rs @@ -1,8 +1,7 @@ - pub mod earliest_completion; -pub mod weighted_interval_scheduling; pub mod test_data; +pub mod weighted_interval_scheduling; pub use earliest_completion::*; -pub use weighted_interval_scheduling::*; pub use test_data::*; +pub use weighted_interval_scheduling::*; diff --git a/src/algorithms/interview_scheduling/test_data.rs b/src/algorithms/interview_scheduling/test_data.rs index 09fe2f0..ce93450 100644 --- a/src/algorithms/interview_scheduling/test_data.rs +++ b/src/algorithms/interview_scheduling/test_data.rs @@ -35,13 +35,13 @@ impl Default for InterviewGenConfig { /// Generate a set of random interviews for testing pub fn generate_random_interviews(config: &InterviewGenConfig) -> Vec> { let mut rng = SimpleRng::new(config.seed.unwrap_or(42)); - + (0..config.count) .map(|id| { let start = rng.gen_range(config.min_start, config.max_start); let duration = rng.gen_range(config.min_duration, config.max_duration); let end = start + duration; - + Interview::new(id, start, end) }) .collect() @@ -51,22 +51,20 @@ pub fn generate_random_interviews(config: &InterviewGenConfig) -> Vec (Vec>, usize) { let interviews = vec![ Interview::new(0, 0, 5), // Non-overlapping - should be selected - Interview::new(1, 6, 10), // Non-overlapping - should be selected + Interview::new(1, 6, 10), // Non-overlapping - should be selected Interview::new(2, 11, 15), // Non-overlapping - should be selected Interview::new(3, 16, 20), // Non-overlapping - should be selected Interview::new(4, 2, 8), // Overlaps with 0 and 1 Interview::new(5, 12, 18), // Overlaps with 2 and 3 Interview::new(6, 1, 19), // Overlaps with all ]; - + (interviews, 4) // Optimal solution has 4 interviews } /// Generate worst-case scenario: all interviews overlap pub fn generate_all_overlapping_interviews(count: usize) -> Vec> { - (0..count) - .map(|id| Interview::new(id, 10, 20)) - .collect() + (0..count).map(|id| Interview::new(id, 10, 20)).collect() } /// Generate best-case scenario: no interviews overlap @@ -89,12 +87,12 @@ impl SimpleRng { fn new(seed: u64) -> Self { Self { state: seed } } - + fn next(&mut self) -> u64 { self.state = self.state.wrapping_mul(1103515245).wrapping_add(12345); self.state } - + fn gen_range(&mut self, min: u64, max: u64) -> u64 { if min >= max { return min; diff --git a/src/algorithms/mod.rs b/src/algorithms/mod.rs index bffaca0..e92be22 100644 --- a/src/algorithms/mod.rs +++ b/src/algorithms/mod.rs @@ -1,9 +1,9 @@ //! Core algorithm implementations organized by problem type. -pub mod stable_matching; +mod heap; pub mod interview_scheduling; +pub mod stable_matching; -// Re-export all algorithm modules for easier access -pub use stable_matching::*; +// Re-export all algorithm modules for easier access pub use interview_scheduling::*; - +pub use stable_matching::*; diff --git a/src/algorithms/stable_matching/gale_shapley.rs b/src/algorithms/stable_matching/gale_shapley.rs index 11c25e0..34c0840 100644 --- a/src/algorithms/stable_matching/gale_shapley.rs +++ b/src/algorithms/stable_matching/gale_shapley.rs @@ -14,20 +14,19 @@ //! ``` //! use std::collections::HashMap; //! use algorithms::stable_matching::*; -//! +//! //! // Generate a random instance and solve it //! let problem = generate_random_instance(3)?; //! let solution = solve_stable_matching(problem); -//! +//! //! // All men should be matched //! assert_eq!(solution.free_men.len(), 0); //! # Ok::<(), &'static str>(()) //! ``` -use std::collections::{HashMap, HashSet}; -use rand::seq::SliceRandom; use crate::algorithms::stable_matching::types::{Gender, Person, Preferences}; - +use rand::seq::SliceRandom; +use std::collections::{HashMap, HashSet}; /// The main structure representing a stable matching problem instance. /// @@ -46,7 +45,7 @@ use crate::algorithms::stable_matching::types::{Gender, Person, Preferences}; /// ## Type Safety /// All operations on this structure return `Result` types to handle /// error conditions gracefully without panics. -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] pub struct StableMatchingProblem { /// All male participants in the matching pub men: Vec, @@ -91,7 +90,7 @@ impl StableMatchingProblem { /// let mut prefs = HashMap::new(); /// prefs.insert(1, Preferences::new(1, vec!)?);[11] /// prefs.insert(2, Preferences::new(2, vec!)?);[12] - /// + /// /// let problem = StableMatchingProblem::new(men, women, prefs)?; /// assert_eq!(problem.free_men.len(), 1); /// # Ok::<(), &'static str>(()) @@ -111,16 +110,16 @@ impl StableMatchingProblem { if men.len() != women.len() { return Err("Must have equal numbers of men and women"); } - + // Initialize mutable state let mut free_men = HashSet::new(); let mut proposal_history = HashMap::new(); - + for man in &men { free_men.insert(man.id); proposal_history.insert(man.id, HashSet::new()); } - + Ok(StableMatchingProblem { men, women, @@ -140,10 +139,11 @@ impl StableMatchingProblem { /// * `Ok(&Preferences)` - Reference to the person's preferences /// * `Err(&'static str)` - Error if no preferences found for this person pub fn get_preferences(&self, person_id: u32) -> Result<&Preferences, &'static str> { - self.preferences.get(&person_id) + self.preferences + .get(&person_id) .ok_or("No preferences found for person") } - + /// Checks if a woman is currently unengaged. /// /// # Arguments @@ -196,7 +196,7 @@ impl StableMatchingProblem { if let Some(current_man) = self.engagements.get(&woman_id) { self.free_men.insert(*current_man); } - + // Create new engagement self.engagements.insert(woman_id, man_id); self.free_men.remove(&man_id); @@ -240,7 +240,7 @@ impl StableMatchingProblem { .get(&man_id) .map_or(false, |set| set.len() == self.women.len()) } - + /// Finds the next woman this man should propose to according to his preferences. /// /// Returns the highest-ranked woman in his preference list to whom he @@ -256,8 +256,9 @@ impl StableMatchingProblem { pub fn next_woman_to_propose(&self, man_id: u32) -> Result, &'static str> { let prefs = self.get_preferences(man_id)?; let proposed_set = self.proposal_history.get(&man_id).unwrap(); - - Ok(prefs.ordered_ids + + Ok(prefs + .ordered_ids .iter() .find(|&&woman_id| !proposed_set.contains(&woman_id)) .copied()) @@ -286,7 +287,6 @@ pub struct State { } impl State { - /// Creates a new state computation from a function. /// /// This is the fundamental constructor for the State monad. @@ -305,7 +305,9 @@ impl State { /// assert_eq!(new_count, 6); /// ``` pub fn new(f: F) -> Self - where F: FnOnce(S) -> (A, S) + 'static { + where + F: FnOnce(S) -> (A, S) + 'static, + { State { run: Box::new(f) } } @@ -356,7 +358,7 @@ impl State { (f(a).run)(s1) }) } - + /// Executes the state computation with an initial state. /// /// This "runs" the state monad computation, providing the initial state @@ -401,14 +403,14 @@ fn get_next_free_man(state: &StableMatchingProblem) -> Option { /// * `Some(woman_id)` - Next woman to propose to /// * `None` - If he has proposed to all women fn find_next_proposal_target(state: &StableMatchingProblem, man_id: u32) -> Option { - state.preferences.get(&man_id) - .and_then(|prefs| { - let proposed_set = state.proposal_history.get(&man_id)?; - prefs.ordered_ids - .iter() - .find(|&&woman_id| !proposed_set.contains(&woman_id)) - .copied() - }) + state.preferences.get(&man_id).and_then(|prefs| { + let proposed_set = state.proposal_history.get(&man_id)?; + prefs + .ordered_ids + .iter() + .find(|&&woman_id| !proposed_set.contains(&woman_id)) + .copied() + }) } /// Pure function to determine if a woman prefers a new man over her current partner. @@ -429,12 +431,25 @@ fn find_next_proposal_target(state: &StableMatchingProblem, man_id: u32) -> Opti /// This function implements the core stability check. A matching is stable /// if no woman would prefer to switch from her current partner to any man /// who would also prefer to switch to her. -fn woman_prefers_new_man(state: &StableMatchingProblem, woman_id: u32, new_man_id: u32, current_man_id: u32) -> bool { - state.preferences.get(&woman_id) +fn woman_prefers_new_man( + state: &StableMatchingProblem, + woman_id: u32, + new_man_id: u32, + current_man_id: u32, +) -> bool { + state + .preferences + .get(&woman_id) .map(|woman_prefs| { - let new_pos = woman_prefs.ordered_ids.iter().position(|&id| id == new_man_id); - let current_pos = woman_prefs.ordered_ids.iter().position(|&id| id == current_man_id); - + let new_pos = woman_prefs + .ordered_ids + .iter() + .position(|&id| id == new_man_id); + let current_pos = woman_prefs + .ordered_ids + .iter() + .position(|&id| id == current_man_id); + match (new_pos, current_pos) { (Some(new_p), Some(current_p)) => new_p < current_p, _ => false, @@ -466,8 +481,12 @@ fn update_state() -> State { if let Some(man_id) = get_next_free_man(&state) { if let Some(woman_id) = find_next_proposal_target(&state, man_id) { // Record the proposal - state.proposal_history.get_mut(&man_id).unwrap().insert(woman_id); - + state + .proposal_history + .get_mut(&man_id) + .unwrap() + .insert(woman_id); + match state.engagements.get(&woman_id) { None => { // Woman is free, engage @@ -514,7 +533,7 @@ fn update_state() -> State { /// # use algorithms::stable_matching::*; /// let problem = generate_random_instance(4)?; /// let solution = solve_stable_matching(problem); -/// +/// /// // Verify all men are matched /// assert_eq!(solution.free_men.len(), 0); /// assert_eq!(solution.engagements.len(), 4); @@ -531,7 +550,7 @@ fn update_state() -> State { /// Solves the stable matching problem using functional unfold composition. /// /// This implementation uses `std::iter::successors` to express the algorithm -/// as an unfold (anamorphism) - generating successive problem states from +/// as an unfold (anamorphism) - generating successive problem states from /// the initial configuration until convergence to a stable matching. /// /// # Arguments @@ -546,7 +565,7 @@ fn update_state() -> State { /// /// # Functional Programming Note /// This demonstrates the unfold pattern - the categorical dual of fold. -/// While fold consumes structure to produce values, unfold generates +/// While fold consumes structure to produce values, unfold generates /// structure from an initial seed value. /// /// # Examples @@ -555,7 +574,7 @@ fn update_state() -> State { /// # use algorithms::stable_matching::*; /// let problem = generate_random_instance(4)?; /// let solution = solve_stable_matching(problem); -/// +/// /// assert_eq!(solution.free_men.len(), 0); /// assert_eq!(solution.engagements.len(), 4); /// # Ok::<(), &'static str>(()) @@ -565,7 +584,7 @@ pub fn solve_stable_matching(problem: StableMatchingProblem) -> StableMatchingPr if current_problem.free_men.is_empty() { None // Algorithm has converged - no more states to generate } else { - // Generate next state using monadic state transformation + // Generate next state using monadic state transformation let (_, next_state) = update_state().run_state(current_problem.clone()); Some(next_state) } @@ -596,7 +615,7 @@ pub fn solve_stable_matching(problem: StableMatchingProblem) -> StableMatchingPr /// # use algorithms::stable_matching::*; /// // Create a problem with 5 men and 5 women /// let problem = generate_random_instance(5)?; -/// +/// /// assert_eq!(problem.men.len(), 5); /// assert_eq!(problem.women.len(), 5); /// assert_eq!(problem.preferences.len(), 10); // 5 + 5 @@ -609,43 +628,49 @@ pub fn solve_stable_matching(problem: StableMatchingProblem) -> StableMatchingPr /// - **Research**: Study algorithm behavior on random instances pub fn generate_random_instance(count: usize) -> Result { let mut rng = rand::thread_rng(); - + // Create people using functional combinators let men: Vec = (1..=count as u32) - .map(|id| Person { id, gender: Gender::Male }) + .map(|id| Person { + id, + gender: Gender::Male, + }) .collect(); - + let women: Vec = (1..=count as u32) - .map(|id| Person { id, gender: Gender::Female }) + .map(|id| Person { + id, + gender: Gender::Female, + }) .collect(); - + let mut preferences = HashMap::new(); - + // Generate men's preferences functionally for man in &men { let mut women_ids: Vec = (1..=count as u32).collect(); women_ids.shuffle(&mut rng); - + let prefs = Preferences::new(man.id, women_ids)?; preferences.insert(man.id, prefs); } - - // Generate women's preferences functionally + + // Generate women's preferences functionally for woman in &women { let mut men_ids: Vec = (1..=count as u32).collect(); men_ids.shuffle(&mut rng); - + let prefs = Preferences::new(woman.id, men_ids)?; preferences.insert(woman.id, prefs); } - + StableMatchingProblem::new(men, women, preferences) -} - +} + #[cfg(test)] mod tests { use super::*; - + #[test] fn test_preferences_creation() { let prefs = Preferences::new(1, vec![2, 3, 4]).unwrap(); @@ -653,22 +678,34 @@ mod tests { assert!(prefs.prefers(2, 3).unwrap()); assert!(!prefs.prefers(4, 2).unwrap()); } - + #[test] fn test_problem_creation() { let men = vec![ - Person { id: 1, gender: Gender::Male }, - Person { id: 2, gender: Gender::Male }, + Person { + id: 1, + gender: Gender::Male, + }, + Person { + id: 2, + gender: Gender::Male, + }, ]; let women = vec![ - Person { id: 1, gender: Gender::Female }, - Person { id: 2, gender: Gender::Female }, + Person { + id: 1, + gender: Gender::Female, + }, + Person { + id: 2, + gender: Gender::Female, + }, ]; - + let mut preferences = HashMap::new(); preferences.insert(1, Preferences::new(1, vec![1, 2]).unwrap()); // Man 1 prefs preferences.insert(2, Preferences::new(2, vec![2, 1]).unwrap()); // Man 2 prefs - + let problem = StableMatchingProblem::new(men, women, preferences).unwrap(); assert_eq!(problem.free_men.len(), 2); } @@ -688,8 +725,16 @@ mod tests { let instance = result.unwrap(); // Check men and women count - assert_eq!(instance.men.len(), n, "Incorrect number of men generated [attached_file:1]"); - assert_eq!(instance.women.len(), n, "Incorrect number of women generated [attached_file:1]"); + assert_eq!( + instance.men.len(), + n, + "Incorrect number of men generated [attached_file:1]" + ); + assert_eq!( + instance.women.len(), + n, + "Incorrect number of women generated [attached_file:1]" + ); // Check that preferences exist for each man and woman for man in &instance.men { @@ -768,11 +813,11 @@ mod tests { fn test_basic_functionality() -> Result<(), &'static str> { let problem = generate_random_instance(3)?; let solution = solve_stable_matching(problem); - + // All men should be matched assert_eq!(solution.free_men.len(), 0); assert_eq!(solution.engagements.len(), 3); - + Ok(()) } } diff --git a/src/algorithms/stable_matching/mod.rs b/src/algorithms/stable_matching/mod.rs index 98d710e..df5c9c9 100644 --- a/src/algorithms/stable_matching/mod.rs +++ b/src/algorithms/stable_matching/mod.rs @@ -3,12 +3,12 @@ //! Implementation of the Gale-Shapley algorithm for the Stable Matching Problem. // Declare submodules if you split them +pub mod bipartite; pub mod gale_shapley; pub mod types; -pub mod bipartite; //pub mod types; // Re-export the public API from submodules -pub use gale_shapley::*; pub use bipartite::*; -pub use types::*; +pub use gale_shapley::*; +// pub use types::*; diff --git a/src/algorithms/stable_matching/types.rs b/src/algorithms/stable_matching/types.rs index 0480f8e..fc59060 100644 --- a/src/algorithms/stable_matching/types.rs +++ b/src/algorithms/stable_matching/types.rs @@ -1,6 +1,5 @@ - /// Represents the gender of a person in the matching problem. -/// +/// /// This enum is used to distinguish between the two sides of the bipartite matching. /// In the classical formulation, these are typically "men" and "women", but the /// algorithm applies to any two-sided matching scenario. @@ -55,7 +54,6 @@ pub struct Preferences { } impl Preferences { - /// Creates a new preference list with validation. /// /// # Arguments @@ -132,9 +130,15 @@ impl Preferences { /// # Ok::<(), &'static str>(()) /// ``` pub fn prefers(&self, a_id: u32, b_id: u32) -> Result { - let pos_a = self.ordered_ids.iter().position(|&id| id == a_id) + let pos_a = self + .ordered_ids + .iter() + .position(|&id| id == a_id) .ok_or("Person A not found in preference list")?; - let pos_b = self.ordered_ids.iter().position(|&id| id == b_id) + let pos_b = self + .ordered_ids + .iter() + .position(|&id| id == b_id) .ok_or("Person B not found in preference list")?; Ok(pos_a < pos_b)