From 1106c10490958ec6c66037d3cdf31924c9d96d9d Mon Sep 17 00:00:00 2001
From: Marc-Antoine Manningham <marc-antoine.m@outlook.com>
Date: Sat, 14 Sep 2024 22:59:41 -0400
Subject: [PATCH] refactor: continue working on item variants

---
 combinatoric-solver/src/collection.rs |  9 +++--
 combinatoric-solver/src/lib.rs        |  1 +
 combinatoric-solver/src/parameters.rs |  8 +++++
 combinatoric-solver/src/solver.rs     | 47 +++++++++++++++++++++++----
 4 files changed, 56 insertions(+), 9 deletions(-)
 create mode 100644 combinatoric-solver/src/parameters.rs

diff --git a/combinatoric-solver/src/collection.rs b/combinatoric-solver/src/collection.rs
index d21d0f6..831e7a8 100644
--- a/combinatoric-solver/src/collection.rs
+++ b/combinatoric-solver/src/collection.rs
@@ -6,17 +6,22 @@ where
     S: Score,
     V: ItemVariant,
 {
-    pub score: S,
-    pub item_variant: [V; MAX_ITEMS],
+    score: S,
+    item_variant: [V; MAX_ITEMS],
 }
 
 impl<const MAX_ITEMS: usize, S: Score, V: ItemVariant> Collection<MAX_ITEMS, S, V> {
     #[inline]
     pub fn push(&self, iteration: usize, item_variant: V) -> Self {
         let mut new = self.clone();
+
         new.item_variant[iteration] = item_variant;
         new
     }
+    #[inline]
+    pub fn item_variant(&self, iteration: usize) -> &[V] {
+        &self.item_variant[0..iteration]
+    }
 }
 
 impl<const MAX_ITEMS: usize, S: Score, V: ItemVariant> Default for Collection<MAX_ITEMS, S, V> {
diff --git a/combinatoric-solver/src/lib.rs b/combinatoric-solver/src/lib.rs
index 8d79d25..4d7930b 100644
--- a/combinatoric-solver/src/lib.rs
+++ b/combinatoric-solver/src/lib.rs
@@ -1,5 +1,6 @@
 pub mod collection;
 pub mod item;
+pub mod parameters;
 pub mod score;
 pub mod solver;
 
diff --git a/combinatoric-solver/src/parameters.rs b/combinatoric-solver/src/parameters.rs
new file mode 100644
index 0000000..2974100
--- /dev/null
+++ b/combinatoric-solver/src/parameters.rs
@@ -0,0 +1,8 @@
+use crate::item::ItemVariant;
+
+pub trait Parameters<V>
+where
+    V: ItemVariant,
+{
+    fn is_valid(&self, previous_variants: &[V], new_variant: V) -> bool;
+}
diff --git a/combinatoric-solver/src/solver.rs b/combinatoric-solver/src/solver.rs
index eb27c47..a8f47fa 100644
--- a/combinatoric-solver/src/solver.rs
+++ b/combinatoric-solver/src/solver.rs
@@ -1,8 +1,9 @@
-use std::{cmp::Reverse, collections::BinaryHeap};
+use std::{cmp::Reverse, collections::BinaryHeap, num::NonZeroUsize};
 
 use crate::{
     collection::Collection,
     item::{CombinatoricItem, ItemVariant},
+    parameters::Parameters,
     score::Score,
 };
 
@@ -12,20 +13,22 @@ where
     V: ItemVariant,
     I: CombinatoricItem,
 {
+    /// Min-heap to keep the best schedules and fetch the worst of the best in O(1)
     top_items: BinaryHeap<Reverse<Collection<MAX_ITEMS, S, V>>>,
-    max_capacity: usize,
+    max_capacity: NonZeroUsize,
     parameters: Parameters,
     items: Vec<I>,
 }
 
-impl<'a, const MAX_ITEMS: usize, S, V, I, Parameters> Solver<MAX_ITEMS, S, V, I, Parameters>
+impl<'a, const MAX_ITEMS: usize, S, V, I, P> Solver<MAX_ITEMS, S, V, I, P>
 where
     S: Score,
     V: ItemVariant,
     I: CombinatoricItem<ItemVariant = V>,
+    P: Parameters<V>,
 {
-    pub fn new(max_capacity: usize, items: Vec<I>, parameters: Parameters) -> Self {
-        let top_items = BinaryHeap::with_capacity(max_capacity);
+    pub fn new(max_capacity: NonZeroUsize, items: Vec<I>, parameters: P) -> Self {
+        let top_items = BinaryHeap::with_capacity(max_capacity.into());
         Self {
             top_items,
             max_capacity,
@@ -35,22 +38,52 @@ where
     }
     pub fn solve(mut self) -> Vec<I> {
         let item = &self.items;
-        Self::rec_iter(0, Collection::default(), item, &mut self.top_items);
+        Self::rec_iter(
+            0,
+            Collection::default(),
+            self.max_capacity,
+            &self.parameters,
+            item,
+            &mut self.top_items,
+        );
         vec![]
     }
     fn rec_iter(
         i: usize,
         collection: Collection<MAX_ITEMS, S, V>,
+        max_capacity: NonZeroUsize,
+        parameters: &P,
         items: &'a [I],
         top_items: &mut BinaryHeap<Reverse<Collection<MAX_ITEMS, S, V>>>,
     ) {
         if i >= items.len() {
+            // If it reach this branch it is already valid and better than the worst top schedule, so we can push
+            if top_items.len() >= max_capacity.into() {
+                top_items.pop();
+            }
+            top_items.push(Reverse(collection));
             return;
         }
 
         for variant in items[i].variants() {
+            if !parameters.is_valid(collection.item_variant(i), variant) {
+                return;
+            }
             let collections = collection.push(i, variant);
-            Self::rec_iter(i + 1, collections, items, top_items);
+            // SAFETY: if the len is greater than the max_capacity, it is greater than 0 enforced by the NonZeroUsize, which allows us to unwrap without checking if it is None
+            if top_items.len() >= max_capacity.into()
+                && unsafe { top_items.peek().unwrap_unchecked().0 >= collections }
+            {
+                return;
+            }
+            Self::rec_iter(
+                i + 1,
+                collections,
+                max_capacity,
+                parameters,
+                items,
+                top_items,
+            );
         }
     }
 }
-- 
GitLab