]> localhost Git - algorithms.git/commitdiff
fixed some tests master
authorjeff <jeff@home.168.40.253>
Mon, 5 Oct 2026 10:25:58 +0000 (19:25 +0900)
committerjeff <jeff@home.168.40.253>
Mon, 5 Oct 2026 10:25:58 +0000 (19:25 +0900)
src/algorithms/heap/leftist_priority_heap.rs
src/algorithms/interview_scheduling/weighted_interval_scheduling.rs
src/algorithms/stable_matching/bipartite.rs
src/algorithms/stable_matching/gale_shapley.rs
tests/test_weighted_interval_scheduling.rs

index 478a616d6a71284d17d341bcbb398d9e85ce160c..2cbb1d79e70ae7fe54dfecc15e19e70c40293c43 100644 (file)
@@ -1,5 +1,5 @@
 use std::fmt::Debug;
-use std::rc::Rc;
+use std::sync::Arc;
 
 /// A semigroup: a type with an associative combine operation
 pub trait Semigroup {
@@ -25,8 +25,8 @@ pub enum Heap<T> {
     Node {
         rank: usize,
         elem: T,
-        left: Rc<Heap<T>>,
-        right: Rc<Heap<T>>,
+        left: Arc<Heap<T>>,
+        right: Arc<Heap<T>>,
     },
 }
 
@@ -70,15 +70,15 @@ impl<T: Ord + Clone> Heap<T> {
         Heap::Node {
             rank,
             elem,
-            left: Rc::new(left),
-            right: Rc::new(right),
+            left: Arc::new(left),
+            right: Arc::new(right),
         }
     }
 
-    /// Try to unwrap an Rc: if we have the only reference, extract the heap for free.
+    /// Try to unwrap an Arc: if we have the only reference, extract the heap for free.
     /// If the heap is shared, clone it (necessary for persistence).
-    fn unwrap_or_clone(rc: Rc<Self>) -> Self {
-        Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone())
+    fn unwrap_or_clone(rc: Arc<Self>) -> Self {
+        Arc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone())
     }
 
     /// Merge two heaps into one, maintaining heap and leftist properties.
@@ -147,8 +147,8 @@ impl<T: Ord + Clone> Heap<T> {
         let singleton = Heap::Node {
             rank: 1,
             elem,
-            left: Rc::new(Heap::Empty),
-            right: Rc::new(Heap::Empty),
+            left: Arc::new(Heap::Empty),
+            right: Arc::new(Heap::Empty),
         };
         self.merge(singleton)
     }
index 714b60bae36b0d0b1018d45292fac45c5d77e53f..2b6bd0fb44683d518fc6db6edabfa684a51130f1 100644 (file)
@@ -122,7 +122,7 @@ impl WeightedScheduler {
 
     /// Reconstructs the actual optimal solution (which jobs to select)
     /// Returns a vector of job indices in the optimal solution
-    pub fn find_optimal_jobs(&self) -> Vec<usize> {
+    pub fn find_optimal_indices(&self) -> Vec<usize> {
         if self.n == 0 {
             return Vec::new();
         }
@@ -140,7 +140,7 @@ impl WeightedScheduler {
 
             if include_value > self.dp[i - 1] {
                 // Job i was included
-                solution.push(self.jobs[job_idx].id);
+                solution.push(job_idx);
 
                 // Move to the predecessor
                 match self.predecessors[job_idx] {
@@ -158,39 +158,53 @@ impl WeightedScheduler {
         solution
     }
 
+    /// Convenience: map indices (from `find_optimal_indices`) back to original job IDs.
+    pub fn ids_for(&self, indices: &[usize]) -> Vec<usize> {
+        indices.iter().map(|&i| self.jobs[i].id).collect()
+    }
+
+    /// Read-only access to the (finish-time-sorted) jobs.
+    pub fn jobs(&self) -> &[Job] {
+        &self.jobs
+    }
+    
     /// Solves the weighted interval scheduling problem
     /// Returns (maximum_weight, selected_job_ids)
     pub fn solve(&mut self) -> (i32, Vec<usize>) {
         let max_weight = self.find_max_weight();
-        let selected_jobs = self.find_optimal_jobs();
-        (max_weight, selected_jobs)
+        let indices = self.find_optimal_indices();
+        (max_weight, indices)
     }
 
-    /// Pretty prints the solution
-    pub fn print_solution(&self, max_weight: i32, selected_jobs: &[usize]) {
+    /// Pretty prints the solution.
+    ///
+    /// `indices` are positions in the sorted `jobs` vector (as returned by
+    /// [`Self::find_optimal_indices`] / [`Self::solve`]), not original job IDs.
+    pub fn print_solution(&self, max_weight: i32, indices: &[usize]) {
         println!("\n=== Weighted Interval Scheduling Solution ===");
         println!("Maximum total weight: {}", max_weight);
         println!("Selected jobs:");
 
-        for &job_id in selected_jobs {
-            // Find the job with this ID
-            if let Some(job) = self.jobs.iter().find(|j| j.id == job_id) {
-                println!(
-                    "  Job {}: [{}→{}, weight={}]",
-                    job.id, job.start, job.finish, job.weight
-                );
-            }
+        let selected_ids: std::collections::HashSet<usize> =
+            indices.iter().map(|&i| self.jobs[i].id).collect();
+
+        for &i in indices {
+            let job = &self.jobs[i];
+            println!(
+                "  Job {} (pos {}): [{}→{}, weight={}]",
+                job.id, i, job.start, job.finish, job.weight
+            );
         }
 
         println!("\nAll jobs (sorted by finish time):");
-        for (_i, job) in self.jobs.iter().enumerate() {
-            let selected = selected_jobs.contains(&job.id);
-            let marker = if selected { "✓" } else { " " };
+        for (i, job) in self.jobs.iter().enumerate() {
+            let marker = if selected_ids.contains(&job.id) { "✓" } else { " " };
             println!(
-                "  {} Job {}: [{}→{}, weight={}]",
-                marker, job.id, job.start, job.finish, job.weight
+                "  {} [{}] Job {}: [{}→{}, weight={}]",
+                marker, i, job.id, job.start, job.finish, job.weight
             );
         }
     }
+
 }
 
index 05bfd9221e24270e7941477e6d8444d35e612645..ce6bc9d0e026ea8be9150e2678e9dcc97eeb3c49 100644 (file)
@@ -141,10 +141,6 @@ pub fn stable_match(
 
                 // Proposer is now engaged
                 free_left.remove(&proposer_id);
-            } else {
-                // Proposal rejected; proposer remains free for next iteration
-                free_left.remove(&proposer_id);
-                free_left.insert(proposer_id.clone());
             }
         } else {
             // Proposer exhausted their preference list
index 2964447deedbe84f86ec58ed83f0ef97d0f31348..8fb938311240bcf3e08d750691e2681fda2ded8d 100644 (file)
@@ -1,5 +1,5 @@
-use crate::StableMatchingGraph;
 use std::collections::HashMap;
+use crate::bipartite::StableMatchingGraph;
 
 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub struct AgentId(pub String);
index 50c13a986dad389f7ffa889bbabc14756e30e4fe..4a0198fdc5de31b2bbc59015dba2ca5fdbf0b7e0 100644 (file)
@@ -43,85 +43,79 @@ fn is_valid_solution(jobs: &[Job], solution: &[usize]) -> bool {
 }
 
 // Pure function to calculate total weight
-fn calculate_total_weight(jobs: &[Job], solution: &[usize]) -> i32 {
-    solution
-        .iter()
-        .map(|&idx| jobs[idx].weight)
-        .map(|w| i32::try_from(w).expect("Weight overflow"))
-        .sum()
+fn calculate_total_weight(jobs: &[Job], indices: &[usize]) -> i32 {
+    indices.iter().map(|&i| jobs[i].weight).sum()
 }
 
 // Example 1: Basic test case
-// #[test]
-// fn test_basic_case_example() {
-//     let jobs = create_jobs_basic();
-//     let mut scheduler = WeightedScheduler::new(jobs.clone());
-//     let (max_weight, solution) = scheduler.solve();
-//
-//     // Verify solution is valid
-//     assert!(is_valid_solution(&jobs, &solution));
-//
-//     // Verify calculated weight matches
-//     assert_eq!(max_weight, calculate_total_weight(&jobs, &solution));
-//
-//     // For this specific case, optimal weight should be 9
-//     // (jobs with weights 5 and 4, indices 1 and 3)
-//     assert_eq!(max_weight, 9);
-// }
+#[test]
+fn test_basic_case_example() {
+    let jobs = create_jobs_basic();
+    let mut scheduler = WeightedScheduler::new(jobs.clone());
+    let (max_weight, solution) = scheduler.solve();
+    // Verify solution is valid
+    assert!(is_valid_solution(scheduler.jobs(), &solution));
+    // Verify calculated weight matches
+    assert_eq!(max_weight, calculate_total_weight(scheduler.jobs(), &solution));
+
+    // For this specific case, optimal weight should be 9
+    // (jobs with weights 5 and 4, indices 1 and 3)
+    assert_eq!(max_weight, 9);
+}
 
 // Example 2: Case where greedy by weight fails
-// #[test]
-// fn test_greedy_by_weight_fails() {
-//     let jobs = create_jobs_greedy_fails();
-//     let mut scheduler = WeightedScheduler::new(jobs.clone());
-//     let (max_weight, solution) = scheduler.solve();
-//
-//     // Verify solution validity
-//     assert!(is_valid_solution(&jobs, &solution));
-//     assert_eq!(max_weight, calculate_total_weight(&jobs, &solution));
-//
-//     // The optimal solution should choose the four short jobs (weight 12)
-//     // rather than the single long job (weight 10)
-//     assert_eq!(max_weight, 12);
-//     assert_eq!(solution.len(), 4);
-// }
+#[test]
+fn test_greedy_by_weight_fails() {
+    let jobs = create_jobs_greedy_fails();
+    let mut scheduler = WeightedScheduler::new(jobs.clone());
+    let (max_weight, solution) = scheduler.solve();
+
+    // Verify solution validity
+    assert!(is_valid_solution(scheduler.jobs(), &solution));
+    assert_eq!(max_weight, calculate_total_weight(scheduler.jobs(), &solution));
+
+    // The optimal solution should choose the four short jobs (weight 12)
+    // rather than the single long job (weight 10)
+    assert_eq!(max_weight, 12);
+    assert_eq!(solution.len(), 4);
+}
 
 // Example 3: Larger test case
-// #[test]
-// fn test_larger_case_example() {
-//     let jobs = create_jobs_large_case();
-//     let mut scheduler = WeightedScheduler::new(jobs.clone());
-//     let (max_weight, solution) = scheduler.solve();
-//
-//     // Verify solution validity
-//     assert!(is_valid_solution(&jobs, &solution));
-//     assert_eq!(max_weight, calculate_total_weight(&jobs, &solution));
-//
-//     // For this case, we just ensure we get a reasonable result
-//     assert!(max_weight > 0);
-//     assert!(!solution.is_empty());
-// }
+#[test]
+fn test_larger_case_example() {
+    let jobs = create_jobs_large_case();
+    let mut scheduler = WeightedScheduler::new(jobs.clone());
+    let (max_weight, solution) = scheduler.solve();
+
+    // Verify solution validity
+    assert!(is_valid_solution(scheduler.jobs(), &solution));
+    assert_eq!(max_weight, calculate_total_weight(scheduler.jobs(), &solution));
+
+    // For this case, we just ensure we get a reasonable result
+    assert!(max_weight > 0);
+    assert!(!solution.is_empty());
+}
 
 // Property-based test: solution should always be valid
-// #[test]
-// fn test_solution_validity_property() {
-//     let test_cases = vec![
-//         create_jobs_basic(),
-//         create_jobs_greedy_fails(),
-//         create_jobs_large_case(),
-//     ];
-//
-//     for jobs in test_cases {
-//         let mut scheduler = WeightedScheduler::new(jobs.clone());
-//         let (max_weight, solution) = scheduler.solve();
-//
-//         // Property: solution must be valid (no overlapping jobs)
-//         assert!(is_valid_solution(&jobs, &solution));
-//
-//         // Property: calculated weight must match returned weight
-//         assert_eq!(max_weight, calculate_total_weight(&jobs, &solution));
-//     }
-// }
+#[test]
+fn test_solution_validity_property() {
+    let test_cases = vec![
+        create_jobs_basic(),
+        create_jobs_greedy_fails(),
+        create_jobs_large_case(),
+    ];
+
+    for jobs in test_cases {
+        let mut scheduler = WeightedScheduler::new(jobs.clone());
+        let (max_weight, solution) = scheduler.solve();
+
+        // Property: solution must be valid (no overlapping jobs)
+        assert!(is_valid_solution(scheduler.jobs(), &solution));
+
+        // Property: calculated weight must match returned weight
+        assert_eq!(max_weight, calculate_total_weight(scheduler.jobs(), &solution));
+    }
+}
 
 // Functional testing pattern: test with transformations
 #[test]
@@ -138,7 +132,6 @@ fn test_job_order_independence() {
     assert_eq!(weight1, weight2);
 }
 
-// Test edge case: all jobs overlap
 #[test]
 fn test_all_jobs_overlap() {
     let jobs = vec![
@@ -151,18 +144,13 @@ fn test_all_jobs_overlap() {
     let mut scheduler = WeightedScheduler::new(jobs.clone());
     let (max_weight, solution) = scheduler.solve();
 
-    // Should pick the highest weight job (12)
     assert_eq!(max_weight, 12);
     assert_eq!(solution.len(), 1);
-    assert_eq!(solution[0], 2); // Job with weight 12
-}
 
-#[test]
-fn test_empty_jobs() {
-    let mut scheduler = WeightedScheduler::new(vec![]);
-    let (max_weight, solution) = scheduler.solve();
-    assert_eq!(max_weight, 0);
-    assert!(solution.is_empty());
+    // The selected index should point to the highest-weight job
+    let selected = &jobs[solution[0]];
+    assert_eq!(selected.weight, 12);
+    assert_eq!(selected.id, 2);
 }
 
 #[test]