]> localhost Git - algorithms.git/commitdiff
completed gayle shapley a little better
authorJeff Hemminger <jeff@hemminger.haus>
Wed, 24 Dec 2025 03:52:31 +0000 (12:52 +0900)
committerJeff Hemminger <jeff@hemminger.haus>
Wed, 24 Dec 2025 03:52:31 +0000 (12:52 +0900)
Cargo.lock
Cargo.toml
src/algorithms/stable_matching/gale_shapley.rs
src/algorithms/stable_matching/mod.rs
src/algorithms/stable_matching/types.rs [deleted file]

index fb81a6325ce1879247fd17063862b44d9d4ae2c2..3186fc7a284e5ddf2754c6579b93be62504a424b 100644 (file)
@@ -2,11 +2,21 @@
 # It is not intended for manual editing.
 version = 4
 
+[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "algorithms"
 version = "0.1.0"
 dependencies = [
- "rand",
+ "quickcheck",
+ "quickcheck_macros",
 ]
 
 [[package]]
@@ -15,6 +25,16 @@ version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
 
+[[package]]
+name = "env_logger"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+dependencies = [
+ "log",
+ "regex",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.2.16"
@@ -33,13 +53,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
 
 [[package]]
-name = "ppv-lite86"
-version = "0.2.21"
+name = "log"
+version = "0.4.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
-dependencies = [
- "zerocopy",
-]
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
 
 [[package]]
 name = "proc-macro2"
@@ -50,6 +73,28 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "quickcheck"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "env_logger",
+ "log",
+ "rand",
+]
+
+[[package]]
+name = "quickcheck_macros"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f71ee38b42f8459a88d3362be6f9b841ad2d5421844f61eb1c59c11bff3ac14a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.40"
@@ -65,30 +110,47 @@ version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
- "libc",
- "rand_chacha",
  "rand_core",
 ]
 
 [[package]]
-name = "rand_chacha"
-version = "0.3.1"
+name = "rand_core"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "ppv-lite86",
- "rand_core",
+ "getrandom",
 ]
 
 [[package]]
-name = "rand_core"
-version = "0.6.4"
+name = "regex"
+version = "1.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
 dependencies = [
- "getrandom",
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
 ]
 
+[[package]]
+name = "regex-syntax"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+
 [[package]]
 name = "syn"
 version = "2.0.106"
@@ -111,23 +173,3 @@ name = "wasi"
 version = "0.11.1+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-
-[[package]]
-name = "zerocopy"
-version = "0.8.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
-dependencies = [
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.8.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
index 7ca51a74316d8268f29b57ac140107899192a5b1..393249c4c42c0008ad9152bd73351eae91b18bf0 100644 (file)
@@ -3,8 +3,7 @@ name = "algorithms"
 version = "0.1.0"
 edition = "2024"
 
-[dependencies]
-rand = "0.8"
 
-[lib]
-doctest = false
+[dev-dependencies]
+quickcheck = "1.0"
+quickcheck_macros = "1.0"
index 34c0840a4dbc651c47e7aae4687558daf5079a81..67c18a4bc85bb8a3e4e6067b2ef66db0e2912eb4 100644 (file)
-//! # Gale-Shapley Stable Matching Algorithm
-//!
-//! This crate implements the Gale-Shapley algorithm for solving the stable matching problem
-//! using functional programming principles inspired by category theory and type theory.
-//!
-//! The implementation features:
-//! - Type-safe preference handling with validation
-//! - Monadic state management for algorithm execution
-//! - Pure functional composition where possible
-//! - Comprehensive error handling with `Result` types
-//!
-//! ## Example
-//!
-//! ```
-//! 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 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.
-///
-/// This contains both the problem specification (people and their preferences)
-/// and the mutable algorithm state (current engagements, proposal history, etc.).
-///
-/// The structure follows functional programming principles by clearly separating
-/// immutable problem data from mutable algorithm state.
-///
-/// ## Algorithm State
-/// The Gale-Shapley algorithm maintains several pieces of state:
-/// - Current engagements between people
-/// - History of proposals made by each person
-/// - Set of currently unmatched people
-///
-/// ## Type Safety
-/// All operations on this structure return `Result` types to handle
-/// error conditions gracefully without panics.
-#[derive(Debug, Clone)]
-pub struct StableMatchingProblem {
-    /// All male participants in the matching    
-    pub men: Vec<Person>,
-    /// All female participants in the matching      
-    pub women: Vec<Person>,
-    /// Preference lists for all participants, indexed by person ID    
-    pub preferences: HashMap<u32, Preferences>,
-    // Mutable algorithm state
-    /// Current engagements: woman_id -> man_id    
-    pub engagements: HashMap<u32, u32>,
-    /// Proposal history: man_id -> Set<woman_id> of women proposed to    
-    pub proposal_history: HashMap<u32, HashSet<u32>>,
-    /// Set of currently unmatched men    
-    pub free_men: HashSet<u32>,
-}
+use std::collections::HashMap;
 
-impl StableMatchingProblem {
-    /// Creates a new stable matching problem instance with validation.
-    ///
-    /// # Arguments
-    /// * `men` - Vector of male participants
-    /// * `women` - Vector of female participants  
-    /// * `preferences` - HashMap of preference lists for all participants
-    ///
-    /// # Returns
-    /// * `Ok(StableMatchingProblem)` - Valid problem instance
-    /// * `Err(&'static str)` - Error message if validation fails
-    ///
-    /// # Errors
-    /// * Returns error if any man doesn't have Male gender
-    /// * Returns error if any woman doesn't have Female gender
-    /// * Returns error if the number of men and women are not equal
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use algorithms::stable_matching::*;    
-    /// # use std::collections::HashMap;
-    /// # use crate::{Person, Gender, Preferences, StableMatchingProblem};
-    /// let men = vec![Person { id: 1, gender: Gender::Male }];
-    /// let women = vec![Person { id: 2, gender: Gender::Female }];
-    /// 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>(())
-    /// ```    
-    pub fn new(
-        men: Vec<Person>,
-        women: Vec<Person>,
-        preferences: HashMap<u32, Preferences>,
-    ) -> Result<Self, &'static str> {
-        // Validation
-        if men.iter().any(|p| p.gender != Gender::Male) {
-            return Err("All men must have Male gender");
-        }
-        if women.iter().any(|p| p.gender != Gender::Female) {
-            return Err("All women must have Female gender");
-        }
-        if men.len() != women.len() {
-            return Err("Must have equal numbers of men and women");
-        }
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct AgentId(pub String);
 
-        // Initialize mutable state
-        let mut free_men = HashSet::new();
-        let mut proposal_history = HashMap::new();
+pub type PreferenceList = Vec<AgentId>;
 
-        for man in &men {
-            free_men.insert(man.id);
-            proposal_history.insert(man.id, HashSet::new());
-        }
-
-        Ok(StableMatchingProblem {
-            men,
-            women,
-            preferences,
-            engagements: HashMap::new(),
-            proposal_history,
-            free_men,
-        })
-    }
+#[derive(Clone, Debug)]
+pub struct Agent {
+    pub id: AgentId,
+}
 
-    /// Retrieves preference list for a given person.
-    ///
-    /// # Arguments
-    /// * `person_id` - The ID of the person whose preferences to retrieve
-    ///
-    /// # Returns
-    /// * `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)
-            .ok_or("No preferences found for person")
-    }
+#[derive(Clone, Debug)]
+pub struct Proposer {
+    pub agent: Agent,
+    preferences: PreferenceList,
+    next_to_propose: usize, // private index
+}
 
-    /// Checks if a woman is currently unengaged.
-    ///
-    /// # Arguments
-    /// * `woman_id` - The ID of the woman to check
-    ///
-    /// # Returns
-    /// `true` if the woman is free, `false` if she is engaged
-    pub fn is_woman_free(&self, woman_id: u32) -> bool {
-        !self.engagements.contains_key(&woman_id)
-    }
+#[derive(Clone, Debug)]
+pub struct Acceptor {
+    pub agent: Agent,
+    preferences: PreferenceList,
+    rankings: HashMap<AgentId, usize>, // preferred -> rank (lower = better)
+}
 
-    /// Checks if a man is currently unengaged.
-    ///
-    /// # Arguments  
-    /// * `man_id` - The ID of the man to check
-    ///
-    /// # Returns
-    /// `true` if the man is free, `false` if he is engaged    
-    pub fn is_man_free(&self, man_id: u32) -> bool {
-        self.free_men.contains(&man_id)
+impl Proposer {
+    pub fn new(id: AgentId, preferences: Vec<AgentId>) -> Self {
+        Self {
+            agent: Agent { id },
+            preferences,
+            next_to_propose: 0,
+        }
     }
 
-    /// Gets the ID of the man currently engaged to a woman.
-    ///
-    /// # Arguments
-    /// * `woman_id` - The ID of the woman
-    ///
-    /// # Returns
-    /// * `Some(man_id)` - If the woman is engaged
-    /// * `None` - If the woman is free    
-    pub fn get_engaged_man(&self, woman_id: u32) -> Option<u32> {
-        self.engagements.get(&woman_id).copied()
+    pub fn next_proposal(&mut self) -> Option<AgentId> {
+        let proposal = self.preferences.get(self.next_to_propose)?;
+        self.next_to_propose += 1;
+        Some(proposal.clone())
     }
+}
 
-    /// Creates an engagement between a man and woman, breaking any existing engagement.
-    ///
-    /// This operation maintains the invariant that each woman is engaged to at most
-    /// one man, and each man is engaged to at most one woman.
-    ///
-    /// # Arguments
-    /// * `man_id` - The ID of the man to engage
-    /// * `woman_id` - The ID of the woman to engage
-    ///
-    /// # Side Effects
-    /// * If the woman was previously engaged, her former partner becomes free  
-    /// * The man is removed from the free men set
-    /// * The engagement is recorded in the engagements map    
-    pub fn engage(&mut self, man_id: u32, woman_id: u32) {
-        // Break existing engagement if any
-        if let Some(current_man) = self.engagements.get(&woman_id) {
-            self.free_men.insert(*current_man);
+impl Acceptor {
+    pub fn new(id: AgentId, preferences: Vec<AgentId>) -> Self {
+        let rankings = preferences
+            .iter()
+            .enumerate()
+            .map(|(rank, agent)| (agent.clone(), rank))
+            .collect();
+        Self {
+            agent: Agent { id },
+            preferences,
+            rankings,
         }
-
-        // Create new engagement
-        self.engagements.insert(woman_id, man_id);
-        self.free_men.remove(&man_id);
-    }
-
-    /// Checks if a man has already proposed to a specific woman.
-    ///
-    /// # Arguments
-    /// * `man_id` - The ID of the man
-    /// * `woman_id` - The ID of the woman
-    ///
-    /// # Returns
-    /// `true` if the man has previously proposed to this woman    
-    pub fn has_proposed_to(&self, man_id: u32, woman_id: u32) -> bool {
-        self.proposal_history
-            .get(&man_id)
-            .map_or(false, |set| set.contains(&woman_id))
     }
 
-    /// Records that a man has proposed to a woman.
-    ///
-    /// # Arguments
-    /// * `man_id` - The ID of the man making the proposal
-    /// * `woman_id` - The ID of the woman receiving the proposal    
-    pub fn record_proposal(&mut self, man_id: u32, woman_id: u32) {
-        self.proposal_history
-            .entry(man_id)
-            .or_insert_with(HashSet::new)
-            .insert(woman_id);
+    pub fn prefers_over_current(&self, current: Option<&AgentId>, new: &AgentId) -> bool {
+        let new_rank = self.rankings.get(new);
+        match current {
+            None => true,
+            Some(current) => {
+                let current_rank = self.rankings.get(current);
+                new_rank < current_rank
+            }
+        }
     }
+}
 
-    /// Checks if a man has proposed to all possible partners.
-    ///
-    /// # Arguments
-    /// * `man_id` - The ID of the man to check
-    ///
-    /// # Returns
-    /// `true` if the man has exhausted all possible proposals    
-    pub fn has_proposed_to_all(&self, man_id: u32) -> bool {
-        self.proposal_history
-            .get(&man_id)
-            .map_or(false, |set| set.len() == self.women.len())
-    }
+#[derive(Clone, Debug)]
+pub struct StableMatchingInput {
+    pub proposers: Vec<Proposer>,
+    pub acceptors: Vec<Acceptor>,
+}
 
-    /// 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
-    /// has not yet proposed.
-    ///
-    /// # Arguments
-    /// * `man_id` - The ID of the man
-    ///
-    /// # Returns
-    /// * `Ok(Some(woman_id))` - The next woman to propose to
-    /// * `Ok(None)` - If he has proposed to everyone
-    /// * `Err(&'static str)` - If preferences not found for this man    
-    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
+impl StableMatchingInput {
+    pub fn new(
+        proposer_ids: Vec<AgentId>,
+        acceptor_ids: Vec<AgentId>,
+        proposer_prefs: Vec<Vec<usize>>, // indices into acceptor_ids
+        acceptor_prefs: Vec<Vec<usize>>, // indices into proposer_ids
+    ) -> Self {
+        let acceptors = acceptor_ids
+            .iter()
+            .zip(acceptor_prefs)
+            .map(|(id, prefs)| {
+                let pref_agents: Vec<AgentId> = prefs
+                    .iter()
+                    .map(|&idx| acceptor_ids[idx].clone())
+                    .collect();
+                Acceptor::new(id.clone(), pref_agents)
+            })
+            .collect();
+
+        let proposers = proposer_ids
             .iter()
-            .find(|&&woman_id| !proposed_set.contains(&woman_id))
-            .copied())
+            .zip(proposer_prefs)
+            .map(|(id, prefs)| {
+                let pref_agents: Vec<AgentId> = prefs
+                    .iter()
+                    .map(|&idx| acceptor_ids[idx].clone())
+                    .collect();
+                Proposer::new(id.clone(), pref_agents)
+            })
+            .collect();
+
+        Self { proposers, acceptors }
     }
 }
 
-/// A State monad implementation for managing algorithm state transformations.
-///
-/// This follows category theory principles by encapsulating stateful computations
-/// in a composable, purely functional way. The State monad allows us to thread
-/// mutable state through a series of computations while maintaining referential
-/// transparency at the functional level.
-///
-/// ## Category Theory Background
-/// The State monad is defined as `State s a = s -> (a, s)`, representing
-/// a function that takes an initial state and returns a value along with
-/// a new state. This forms a monad with the following operations:
-/// - `return`: Creates a state computation that returns a value without changing state
-/// - `bind` (>>=): Composes state computations sequentially
-///
-/// ## Type Parameters
-/// * `S` - The type of the state being threaded through computations
-/// * `A` - The type of the value produced by the computation
-pub struct State<S, A> {
-    run: Box<dyn FnOnce(S) -> (A, S)>,
-}
 
-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.
-    ///
-    /// # Arguments
-    /// * `f` - A function that takes initial state and returns (value, new_state)
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use algorithms::stable_matching::*;        
-    /// // A state computation that increments a counter and returns the old value
-    /// let increment = State::new(|count: i32| (count, count + 1));
-    /// let (old_value, new_count) = increment.run_state(5);
-    /// assert_eq!(old_value, 5);
-    /// assert_eq!(new_count, 6);
-    /// ```    
-    pub fn new<F>(f: F) -> Self
-    where
-        F: FnOnce(S) -> (A, S) + 'static,
-    {
-        State { run: Box::new(f) }
-    }
+pub fn gale_shapley(input: &StableMatchingInput) -> Vec<(AgentId, AgentId)> {
+    let mut proposers = input.proposers.clone();
+    let acceptors = input.acceptors.clone();
 
-    /// Maps a function over the value produced by a state computation.
-    ///
-    /// This implements the Functor instance for State, allowing transformation
-    /// of the computed value without affecting the state threading.
-    ///
-    /// # Type Parameters
-    /// * `B` - The type of the transformed value
-    /// * `F` - The transformation function type
-    ///
-    /// # Arguments
-    /// * `f` - Function to transform the computed value
-    ///
-    /// # Category Theory Note
-    /// This satisfies the functor laws:
-    /// - `fmap id = id`
-    /// - `fmap (g . f) = fmap g . fmap f`    
-    pub fn map<B: 'static, F: FnOnce(A) -> B + 'static>(self, f: F) -> State<S, B> {
-        State::new(move |s| {
-            let (a, s1) = (self.run)(s);
-            (f(a), s1)
-        })
-    }
+    let mut engagements: HashMap<AgentId, AgentId> = HashMap::new();
+    let mut free_proposers: Vec<usize> = (0..proposers.len()).collect();
 
-    /// Sequentially composes two state computations.
-    ///
-    /// This implements the monadic bind operation (>>=), enabling sequential
-    /// composition of stateful computations where the result of the first
-    /// computation can influence the second.
-    ///
-    /// # Type Parameters  
-    /// * `B` - The type of value produced by the second computation
-    /// * `F` - The function that creates the second computation
-    ///
-    /// # Arguments
-    /// * `f` - Function that takes the first result and produces a new state computation
-    ///
-    /// # Monad Laws
-    /// This satisfies the monad laws:
-    /// - Left identity: `return a >>= f ≡ f a`  
-    /// - Right identity: `m >>= return ≡ m`
-    /// - Associativity: `(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)`    
-    pub fn flat_map<B: 'static, F: FnOnce(A) -> State<S, B> + 'static>(self, f: F) -> State<S, B> {
-        State::new(move |s| {
-            let (a, s1) = (self.run)(s);
-            (f(a).run)(s1)
-        })
-    }
+    while let Some(proposer_idx) = free_proposers.pop() {
+        let proposer = &mut proposers[proposer_idx];
 
-    /// Executes the state computation with an initial state.
-    ///
-    /// This "runs" the state monad computation, providing the initial state
-    /// and extracting both the computed value and final state.
-    ///
-    /// # Arguments
-    /// * `initial_state` - The starting state for the computation
-    ///
-    /// # Returns
-    /// A tuple of (computed_value, final_state)    
-    pub fn run_state(self, initial_state: S) -> (A, S) {
-        (self.run)(initial_state)
+        if let Some(acceptor_id) = proposer.next_proposal() {
+            let acceptor_idx = acceptors
+                .iter()
+                .position(|a| a.agent.id == acceptor_id)
+                .expect("StableMatchingInput invariant violated");
+
+            let acceptor = &acceptors[acceptor_idx];
+            let current_partner = engagements.get(&acceptor_id);
+
+            if acceptor.prefers_over_current(current_partner, &proposer.agent.id) {
+                if let Some(old_partner_id) = engagements.insert(acceptor_id.clone(), proposer.agent.id.clone()) {
+                    let old_partner_idx = proposers
+                        .iter()
+                        .position(|p| p.agent.id == old_partner_id)
+                        .expect("Proposer not found");
+                    free_proposers.push(old_partner_idx);
+                }
+            } else {
+                free_proposers.push(proposer_idx);
+            }
+        }
     }
-}
 
-/// Pure function to get the next free man from the problem state.
-///
-/// Following functional programming principles, this function has no side effects
-/// and always returns the same output for the same input.
-///
-/// # Arguments
-/// * `state` - Current problem state
-///
-/// # Returns
-/// * `Some(man_id)` - ID of a free man, if any exist
-/// * `None` - If all men are engaged
-fn get_next_free_man(state: &StableMatchingProblem) -> Option<u32> {
-    state.free_men.iter().copied().next()
+    engagements
+        .into_iter()
+        .map(|(acceptor_id, proposer_id)| (proposer_id, acceptor_id))
+        .collect()
 }
 
-/// Pure function to find the next woman a man should propose to.
-///
-/// This implements the core logic of the Gale-Shapley algorithm: each man
-/// proposes to women in order of his preference, skipping those he has
-/// already proposed to.
-///
-/// # Arguments
-/// * `state` - Current problem state
-/// * `man_id` - ID of the man making proposals
-///
-/// # Returns
-/// * `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()
-    })
-}
 
-/// Pure function to determine if a woman prefers a new man over her current partner.
-///
-/// This implements the stability condition check: a woman will switch partners
-/// if she prefers the new proposer over her current partner.
-///
-/// # Arguments
-/// * `state` - Current problem state
-/// * `woman_id` - ID of the woman making the choice
-/// * `new_man_id` - ID of the new proposer  
-/// * `current_man_id` - ID of her current partner
-///
-/// # Returns
-/// `true` if the woman prefers the new man, `false` otherwise
-///
-/// # Stability Theory
-/// 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)
-        .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);
 
-            match (new_pos, current_pos) {
-                (Some(new_p), Some(current_p)) => new_p < current_p,
-                _ => false,
-            }
-        })
-        .unwrap_or(false)
-}
-
-/// Creates a single state transition for the Gale-Shapley algorithm.
-///
-/// This function encapsulates one iteration of the algorithm:
-/// 1. Find a free man
-/// 2. Find his next preferred woman to propose to
-/// 3. Handle the proposal (engage, reject, or switch partners)
-///
-/// The function is pure in the sense that it returns a state computation
-/// rather than directly mutating state.
-///
-/// # Returns
-/// A State monad computation that performs one algorithm step
-///
-/// # Algorithm Correctness
-/// Each step maintains the algorithm invariants:
-/// - Each woman is engaged to at most one man
-/// - Men propose in preference order
-/// - Women always accept better proposals
-fn update_state() -> State<StableMatchingProblem, ()> {
-    State::new(|mut 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);
-
-                match state.engagements.get(&woman_id) {
-                    None => {
-                        // Woman is free, engage
-                        state.engagements.insert(woman_id, man_id);
-                        state.free_men.remove(&man_id);
-                    }
-                    Some(&current_man_id) => {
-                        // Check if woman prefers new man
-                        if woman_prefers_new_man(&state, woman_id, man_id, current_man_id) {
-                            // Switch engagements
-                            state.engagements.insert(woman_id, man_id);
-                            state.free_men.remove(&man_id);
-                            state.free_men.insert(current_man_id);
-                        }
-                        // Otherwise, man remains free
-                    }
-                }
-            }
+#[cfg(test)]
+mod tests {
+    use quickcheck_macros::quickcheck;
+    use crate::{Acceptor, AgentId, Proposer, gale_shapley, StableMatchingInput};
+    use quickcheck::{Arbitrary, Gen};
+    impl Arbitrary for AgentId {
+        fn arbitrary(g: &mut Gen) -> Self {
+            // Generate a simple ID from a number
+            let num = u32::arbitrary(g);
+            AgentId(format!("agent_{}", num))
         }
-        ((), state)
-    })
-}
+    }
 
-/// Solves the stable matching problem using functional composition.
-///
-/// This function repeatedly applies the state transformation until no free men remain,
-/// implementing the complete Gale-Shapley algorithm through monadic composition.
-///
-/// # Arguments
-/// * `problem` - Initial problem instance with all men free
-///
-/// # Returns
-/// The solved problem with all participants matched
-///
-/// # Algorithm Properties
-/// The Gale-Shapley algorithm guarantees:
-/// - **Termination**: The algorithm always terminates in O(n²) proposals
-/// - **Stability**: The resulting matching is stable (no blocking pairs)
-/// - **Optimality**: The matching is man-optimal and woman-pessimal
-///
-/// # Examples
-///
-/// ```
-/// # 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);
-/// # Ok::<(), &'static str>(())
-/// ```
-//pub fn solve_stable_matching(mut problem: StableMatchingProblem) -> StableMatchingProblem {
-//    while !problem.free_men.is_empty() {
-//        let (_, new_state) = update_state().run_state(problem);
-//        problem = new_state;
-//    }
-//    problem
-//}
-
-/// 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
-/// the initial configuration until convergence to a stable matching.
-///
-/// # Arguments
-/// * `problem` - Initial problem instance with all men free
-///
-/// # Returns  
-/// The solved problem with all participants matched
-///
-/// # Algorithm Complexity
-/// - Time: O(n²) in worst case
-/// - Space: O(n) for state representation
-///
-/// # Functional Programming Note
-/// This demonstrates the unfold pattern - the categorical dual of fold.
-/// While fold consumes structure to produce values, unfold generates
-/// structure from an initial seed value.
-///
-/// # Examples
-///
-/// ```
-/// # 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>(())
-/// ```
-pub fn solve_stable_matching(problem: StableMatchingProblem) -> StableMatchingProblem {
-    std::iter::successors(Some(problem), |current_problem| {
-        if current_problem.free_men.is_empty() {
-            None // Algorithm has converged - no more states to generate
-        } else {
-            // Generate next state using monadic state transformation
-            let (_, next_state) = update_state().run_state(current_problem.clone());
-            Some(next_state)
-        }
-    })
-    .last() // Extract the final converged state
-    .expect("Iterator should always yield at least the initial state")
-}
+    impl Arbitrary for Proposer {
+        fn arbitrary(g: &mut Gen) -> Self {
+            let id = AgentId::arbitrary(g);
+            // Generate a preference list of 3-10 other agents
+            let pref_count = *g.choose(&[3, 4, 5, 6, 7, 8, 9, 10]).unwrap();
+            let preferences: Vec<AgentId> = (0..pref_count)
+                .map(|_| AgentId::arbitrary(g))
+                .collect();
 
-/// Generates a random stable matching problem instance for testing and experimentation.
-///
-/// This function creates a problem with the specified number of men and women,
-/// where each person has a random preference ordering over all potential partners.
-///
-/// # Arguments
-/// * `count` - Number of men and women to create (total 2×count participants)
-///
-/// # Returns
-/// * `Ok(StableMatchingProblem)` - Random problem instance ready to solve
-/// * `Err(&'static str)` - Error if problem creation fails
-///
-/// # Randomization
-/// Uses Fisher-Yates shuffle to create uniformly random preference orderings,
-/// ensuring each possible preference profile has equal probability.
-///
-/// # Examples
-///
-/// ```
-/// # 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
-/// # Ok::<(), &'static str>(())
-/// ```
-///
-/// # Use Cases
-/// - **Testing**: Generate test cases for algorithm verification
-/// - **Benchmarking**: Create instances for performance analysis  
-/// - **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,
-        })
-        .collect();
-
-    let women: Vec<Person> = (1..=count as u32)
-        .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);
+            Proposer::new(id, preferences)
+        }
     }
 
-    // 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);
+    impl Arbitrary for Acceptor {
+        fn arbitrary(g: &mut Gen) -> Self {
+            let id = AgentId::arbitrary(g);
+            // Generate a preference list of 3-10 other agents
+            let pref_count = *g.choose(&[3, 4, 5, 6, 7, 8, 9, 10]).unwrap();
+            let preferences: Vec<AgentId> = (0..pref_count)
+                .map(|_| AgentId::arbitrary(g))
+                .collect();
 
-        let prefs = Preferences::new(woman.id, men_ids)?;
-        preferences.insert(woman.id, prefs);
+            Acceptor::new(id, preferences)
+        }
     }
 
-    StableMatchingProblem::new(men, women, preferences)
-}
+    impl Arbitrary for StableMatchingInput {
+        fn arbitrary(g: &mut Gen) -> Self {
+            let num_agents = usize::arbitrary(g).min(10).max(2);
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_preferences_creation() {
-        let prefs = Preferences::new(1, vec![2, 3, 4]).unwrap();
-        assert_eq!(prefs.most_preferred(), 2);
-        assert!(prefs.prefers(2, 3).unwrap());
-        assert!(!prefs.prefers(4, 2).unwrap());
-    }
+            // Generate acceptor IDs first
+            let acceptor_ids: Vec<AgentId> = (0..num_agents)
+                .map(|i| AgentId(format!("A{}", i)))
+                .collect();
 
-    #[test]
-    fn test_problem_creation() {
-        let men = vec![
-            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,
-            },
-        ];
-
-        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);
-    }
+            // Generate proposer IDs
+            let proposer_ids: Vec<AgentId> = (0..num_agents)
+                .map(|i| AgentId(format!("P{}", i)))
+                .collect();
 
-    #[test]
-    fn test_generate_random_instance() {
-        // Generate a small test instance (e.g., 4 men and 4 women)
-        let n = 4;
-        let result = generate_random_instance(n);
-
-        assert!(
-            result.is_ok(),
-            "generate_random_instance returned an error: {:?}",
-            result.err()
-        );
-
-        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]"
-        );
-
-        // Check that preferences exist for each man and woman
-        for man in &instance.men {
-            assert!(
-                instance.preferences.contains_key(&man.id),
-                "Preference list missing for man {} [attached_file:1]",
-                man.id
-            );
-            let prefs = instance.preferences.get(&man.id).unwrap();
-            // Preferences should list all women without duplicates
-            assert_eq!(
-                prefs.ordered_ids.len(),
-                n,
-                "Man {} preferences should have length {} [attached_file:1]",
-                man.id,
-                n
-            );
-            let mut sorted = prefs.ordered_ids.clone();
-            sorted.sort();
-            sorted.dedup();
-            assert_eq!(
-                sorted.len(),
-                n,
-                "Man {} preferences contain duplicates [attached_file:1]",
-                man.id
-            );
-        }
+            // Generate random permutations for preferences
+            let acceptor_prefs: Vec<Vec<usize>> = (0..num_agents)
+                .map(|_| generate_permutation(g, num_agents))
+                .collect();
 
-        for woman in &instance.women {
-            assert!(
-                instance.preferences.contains_key(&woman.id),
-                "Preference list missing for woman {} [attached_file:1]",
-                woman.id
-            );
-            let prefs = instance.preferences.get(&woman.id).unwrap();
-            assert_eq!(
-                prefs.ordered_ids.len(),
-                n,
-                "Woman {} preferences should have length {} [attached_file:1]",
-                woman.id,
-                n
-            );
-            let mut sorted = prefs.ordered_ids.clone();
-            sorted.sort();
-            sorted.dedup();
-            assert_eq!(
-                sorted.len(),
-                n,
-                "Woman {} preferences contain duplicates [attached_file:1]",
-                woman.id
-            );
-        }
+            let proposer_prefs: Vec<Vec<usize>> = (0..num_agents)
+                .map(|_| generate_permutation(g, num_agents))  // Full permutation
+                .collect();
 
-        // Ensure free_men are all the men
-        for man in &instance.men {
-            assert!(
-                instance.free_men.contains(&man.id),
-                "Man {} should be free initially [attached_file:1]",
-                man.id
-            );
+            StableMatchingInput::new(proposer_ids, acceptor_ids, proposer_prefs, acceptor_prefs)
         }
+    }
 
-        // Ensure proposal_history is initialized for all men and is empty
-        for man in &instance.men {
-            let history = instance.proposal_history.get(&man.id).unwrap();
-            assert!(
-                history.is_empty(),
-                "Proposal history for man {} should be empty [attached_file:1]",
-                man.id
-            );
+    fn generate_permutation(g: &mut Gen, n: usize) -> Vec<usize> {
+        let mut perms: Vec<usize> = (0..n).collect();
+        for i in 0..n {
+            let j = i + usize::arbitrary(g) % (n.saturating_sub(i));
+            perms.swap(i, j);
         }
+        perms
+    }
+    #[quickcheck]
+    fn prop_proposer_can_propose(mut proposer: Proposer) -> bool {
+        // A proposer should always be able to make at least one proposal
+        proposer.next_proposal().is_some()
     }
 
-    /// Test that demonstrates the documented API usage
-    #[test]
-    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);
+    #[quickcheck]
+    fn prop_complete_matching(input: StableMatchingInput) -> bool {
+        let matches = gale_shapley(&input);  // Borrow with &
+        let max_possible = input.proposers.len().min(input.acceptors.len());
+        matches.len() == max_possible
+    }
 
-        Ok(())
+    #[quickcheck]
+    fn prop_proposers_satisfied(input: StableMatchingInput) -> bool {
+        let matches = gale_shapley(&input);
+        // In proposer-optimal matching, no proposer prefers an unmatched acceptor
+        // (simplified check - full stability requires more complex logic)
+        true // Placeholder for full stability property
     }
 }
index df5c9c9d93e3084fdaf3cd7f23a9e2ffbcb3a870..5f3ad2b90dc042a05f4db049d779f67f027a02cd 100644 (file)
@@ -2,13 +2,9 @@
 //!
 //! 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 types;
 
-// Re-export the public API from submodules
 pub use bipartite::*;
 pub use gale_shapley::*;
-// pub use types::*;
+pub use bipartite::Preferences;
\ No newline at end of file
diff --git a/src/algorithms/stable_matching/types.rs b/src/algorithms/stable_matching/types.rs
deleted file mode 100644 (file)
index fc59060..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/// 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.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Gender {
-    Male,
-    Female,
-}
-
-/// A person participating in the stable matching problem.
-///
-/// Each person has a unique identifier and belongs to one of two groups
-/// distinguished by gender. The algorithm ensures each person from one
-/// group is matched with exactly one person from the other group.
-///
-/// ## Type Theory Note
-/// This represents an element in one of two disjoint sets that form
-/// the domain of our matching function.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Person {
-    pub id: u32,
-    pub gender: Gender,
-}
-
-/// Represents a person's ordered preference list over potential partners.
-///
-/// This structure encapsulates the total ordering required by the Gale-Shapley
-/// algorithm. Each person must have a complete, strict preference ordering
-/// over all potential partners.
-///
-/// ## Category Theory Note
-/// This represents a morphism in the category of preferences, where objects
-/// are people and morphisms represent preference relations.
-///
-/// ## Examples
-///
-/// ```
-/// # use algorithms::stable_matching::*;
-/// // Person 1 prefers partners in order: 3, 1, 2
-/// let prefs = Preferences::new(1, vec!)?;[1][2][3]
-///
-/// // Check if person 3 is preferred over person 2
-/// assert!(prefs.prefers(3, 2)?);
-/// # Ok::<(), &'static str>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Preferences {
-    /// The ordered list of preferred partner IDs (most preferred first)
-    pub ordered_ids: Vec<u32>,
-    /// The ID of the person who holds these preferences
-    pub person_id: u32,
-}
-
-impl Preferences {
-    /// Creates a new preference list with validation.
-    ///
-    /// # Arguments
-    /// * `person_id` - The ID of the person who holds these preferences
-    /// * `ordered_ids` - List of preferred partner IDs in order of preference
-    ///
-    /// # Returns
-    /// * `Ok(Preferences)` - Valid preference list
-    /// * `Err(&'static str)` - Error message if validation fails
-    ///
-    /// # Errors
-    /// * Returns error if the preference list is empty
-    /// * Returns error if there are duplicate preferences
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use algorithms::stable_matching::*;
-    /// # use crate::Preferences;
-    /// // Valid preferences
-    /// let prefs = Preferences::new(1, vec!)?;[2][3][4]
-    ///
-    /// // Invalid - empty list
-    /// assert!(Preferences::new(1, vec![]).is_err());
-    ///
-    /// // Invalid - duplicates
-    /// assert!(Preferences::new(1, vec!).is_err());[3][2]
-    /// # Ok::<(), &'static str>(())
-    /// ```
-    pub fn new(person_id: u32, ordered_ids: Vec<u32>) -> Result<Self, &'static str> {
-        if ordered_ids.is_empty() {
-            return Err("Preference list cannot be empty");
-        }
-
-        let mut unique_ids = ordered_ids.clone();
-        unique_ids.sort();
-        unique_ids.dedup();
-        if unique_ids.len() != ordered_ids.len() {
-            return Err("No duplicate preferences allowed");
-        }
-
-        Ok(Preferences {
-            person_id,
-            ordered_ids,
-        })
-    }
-
-    /// Determines if person `a_id` is preferred over person `b_id`.
-    ///
-    /// This implements the strict preference relation required for stable matching.
-    /// Returns `true` if `a_id` appears earlier in the preference list than `b_id`.
-    ///
-    /// # Arguments
-    /// * `a_id` - ID of the first person to compare
-    /// * `b_id` - ID of the second person to compare
-    ///
-    /// # Returns
-    /// * `Ok(true)` - If `a_id` is preferred over `b_id`
-    /// * `Ok(false)` - If `b_id` is preferred over `a_id`
-    /// * `Err(&'static str)` - If either person is not in the preference list
-    ///
-    /// # Mathematical Note
-    /// This implements the relation `a_id ≻ b_id` in preference theory notation.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use algorithms::stable_matching::*;
-    /// # use crate::Preferences;
-    /// let prefs = Preferences::new(1, vec!)?;[2][3][5]
-    ///
-    /// assert!(prefs.prefers(3, 5)?);  // 3 is preferred over 5
-    /// assert!(!prefs.prefers(5, 3)?); // 5 is not preferred over 3
-    /// # 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)
-            .ok_or("Person A not found in preference list")?;
-        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)
-    }
-
-    /// Returns the most preferred person's ID.
-    ///
-    /// # Returns
-    /// The ID of the person at the top of this preference list.
-    ///
-    /// # Panics
-    /// Panics if the preference list is empty (which should be impossible
-    /// if constructed through `new()`).
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use algorithms::stable_matching::*;
-    /// # use crate::Preferences;
-    /// use algorithms::stable_matching::*;
-    /// let prefs = Preferences::new(1, vec!)?;[7][3][5]
-    /// assert_eq!(prefs.most_preferred(), 7);
-    /// # Ok::<(), &'static str>(())
-    /// ```
-    pub fn most_preferred(&self) -> u32 {
-        self.ordered_ids[0]
-    }
-}