]> localhost Git - algorithms.git/commitdiff
formatting
authorJeff Hemminger <jeff@hemminger.haus>
Wed, 24 Dec 2025 01:22:17 +0000 (10:22 +0900)
committerJeff Hemminger <jeff@hemminger.haus>
Wed, 24 Dec 2025 01:22:17 +0000 (10:22 +0900)
src/algorithms/interview_scheduling/earliest_completion.rs
src/algorithms/interview_scheduling/mod.rs
src/algorithms/interview_scheduling/test_data.rs
src/algorithms/mod.rs
src/algorithms/stable_matching/gale_shapley.rs
src/algorithms/stable_matching/mod.rs
src/algorithms/stable_matching/types.rs

index 53973869145a8ca71bfca6644c2924aad3c4ce35..1fd76568069297c16cd1d2b2b3888483e90fb520 100644 (file)
@@ -4,11 +4,10 @@ pub struct Time<T>(pub T)
 where
     T: Ord + Copy;
 
-
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Interview<T> 
-where 
-    T: Ord + Copy,  // ← Add this constraint
+pub struct Interview<T>
+where
+    T: Ord + Copy, // ← Add this constraint
 {
     pub start: Time<T>,
     pub end: Time<T>,
@@ -17,7 +16,7 @@ where
 
 impl<T> Interview<T>
 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<T>]) {
         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);
     }
index 4dd6544a1fa95f88f3bdd3ca1fe35bea33745b72..43d52f1967c4d3404efda6295eab64caf8ac6a64 100644 (file)
@@ -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::*;
index 09fe2f0b179a4c14df5ccaba688478484a1cd2a5..ce93450bcb09e356afbb558c6d73e4b6ccf57e5d 100644 (file)
@@ -35,13 +35,13 @@ impl Default for InterviewGenConfig {
 /// Generate a set of random interviews for testing
 pub fn generate_random_interviews(config: &InterviewGenConfig) -> Vec<Interview<u64>> {
     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<Interview<
 pub fn generate_known_optimal_interviews() -> (Vec<Interview<u64>>, 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<Interview<u64>> {
-    (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;
index bffaca0a4e254586ed7decceed2219d712204400..e92be2275dba1c009839ad0523a15355b669bd1c 100644 (file)
@@ -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::*;
index 11c25e0845d6ccd327de6b508aceccbcf3d36f66..34c0840a4dbc651c47e7aae4687558daf5079a81 100644 (file)
 //! ```
 //! 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<Person>,
@@ -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<Option<u32>, &'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<S, A> {
 }
 
 impl<S: 'static, A: 'static> State<S, A> {
-
     /// Creates a new state computation from a function.
     ///
     /// This is the fundamental constructor for the State monad.
@@ -305,7 +305,9 @@ impl<S: 'static, A: 'static> State<S, A> {
     /// assert_eq!(new_count, 6);
     /// ```    
     pub fn new<F>(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<S: 'static, A: 'static> State<S, A> {
             (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<u32> {
 /// * `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<u32> {
-    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<StableMatchingProblem, ()> {
         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<StableMatchingProblem, ()> {
 /// # 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<StableMatchingProblem, ()> {
 /// 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<StableMatchingProblem, ()> {
 ///
 /// # 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<StableMatchingProblem, ()> {
 /// # 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<StableMatchingProblem, &'static str> {
     let mut rng = rand::thread_rng();
-    
+
     // Create people using functional combinators
     let men: Vec<Person> = (1..=count as u32)
-        .map(|id| Person { id, gender: Gender::Male })
+        .map(|id| Person {
+            id,
+            gender: Gender::Male,
+        })
         .collect();
-    
+
     let women: Vec<Person> = (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<u32> = (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<u32> = (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(())
     }
 }
index 98d710eba8bf8d8e2ca84daf4f15ac9ab479fe59..df5c9c9d93e3084fdaf3cd7f23a9e2ffbcb3a870 100644 (file)
@@ -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::*;
index 0480f8ef9a8fb70fcb65d3b831b5b471500c27db..fc590605b56a816511e37df61376ffc5146469fe 100644 (file)
@@ -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<bool, &'static str> {
-        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)