1use std::marker::PhantomData;
13
14use solverforge_core::score::Score;
15use solverforge_core::{ConstraintRef, ImpactType};
16
17use super::relational::operator::{ExistenceNode, Operator};
18use super::relational::Leaf;
19use super::weighting_support::ConstraintWeight;
20use crate::constraint::relational::OperatorTerminal;
21use crate::stream::joiner::plan::{CompileCondition, ExecutablePlan, IndexedPlan};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum ExistenceMode {
25 Exists,
26 NotExists,
27}
28
29pub struct ExistsConstraintStream<S, A, L, R, C, Sc>
33where
34 C: CompileCondition,
35 Sc: Score,
36{
37 pub(super) mode: ExistenceMode,
38 pub(super) left: L,
39 pub(super) right: R,
40 pub(super) condition: C,
41 pub(super) _phantom: PhantomData<(fn() -> S, fn() -> A, fn() -> Sc)>,
42}
43
44impl<S, A, L, R, C, Sc> ExistsConstraintStream<S, A, L, R, C, Sc>
45where
46 S: Send + Sync + 'static,
47 A: Clone + Send + Sync + 'static,
48 L: Operator<S>,
49 for<'a> L: Operator<S, View<'a> = Leaf<'a, A>>,
50 C: CompileCondition,
51 Sc: Score + 'static,
52{
53 pub(super) fn new(mode: ExistenceMode, left: L, right: R, condition: C) -> Self {
54 Self {
55 mode,
56 left,
57 right,
58 condition,
59 _phantom: PhantomData,
60 }
61 }
62
63 fn into_weighted_builder<W>(
64 self,
65 impact_type: ImpactType,
66 weight: W,
67 is_hard: bool,
68 ) -> ExistsConstraintBuilder<S, A, L, R, C, W, Sc>
69 where
70 W: for<'a> Fn(&S, &Leaf<'a, A>) -> Sc,
71 {
72 ExistsConstraintBuilder {
73 mode: self.mode,
74 left: self.left,
75 right: self.right,
76 condition: self.condition,
77 impact_type,
78 weight,
79 is_hard,
80 _phantom: PhantomData,
81 }
82 }
83
84 pub fn penalize<W>(
85 self,
86 weight: W,
87 ) -> ExistsConstraintBuilder<
88 S,
89 A,
90 L,
91 R,
92 C,
93 impl for<'a> Fn(&S, &Leaf<'a, A>) -> Sc + Send + Sync,
94 Sc,
95 >
96 where
97 W: for<'w> ConstraintWeight<(&'w A,), Sc> + Send + Sync,
98 {
99 let is_hard = weight.is_hard();
100 self.into_weighted_builder(
101 ImpactType::Penalty,
102 move |_s: &S, row: &Leaf<'_, A>| weight.score((row.entity,)),
103 is_hard,
104 )
105 }
106
107 pub fn reward<W>(
108 self,
109 weight: W,
110 ) -> ExistsConstraintBuilder<
111 S,
112 A,
113 L,
114 R,
115 C,
116 impl for<'a> Fn(&S, &Leaf<'a, A>) -> Sc + Send + Sync,
117 Sc,
118 >
119 where
120 W: for<'w> ConstraintWeight<(&'w A,), Sc> + Send + Sync,
121 {
122 let is_hard = weight.is_hard();
123 self.into_weighted_builder(
124 ImpactType::Reward,
125 move |_s: &S, row: &Leaf<'_, A>| weight.score((row.entity,)),
126 is_hard,
127 )
128 }
129}
130
131pub struct ExistsConstraintBuilder<S, A, L, R, C, W, Sc>
132where
133 C: CompileCondition,
134 Sc: Score,
135{
136 mode: ExistenceMode,
137 left: L,
138 right: R,
139 condition: C,
140 impact_type: ImpactType,
141 weight: W,
142 is_hard: bool,
143 _phantom: PhantomData<(fn() -> S, fn() -> A, fn() -> Sc)>,
144}
145
146impl<S, A, L, R, C, W, Sc> ExistsConstraintBuilder<S, A, L, R, C, W, Sc>
147where
148 S: Send + Sync + 'static,
149 A: 'static,
150 L: Operator<S>,
151 for<'a> L: Operator<S, View<'a> = Leaf<'a, A>>,
152 R: Operator<S>,
153 C: CompileCondition + 'static,
154 C::Plan: IndexedPlan + 'static,
155 for<'a> C::Plan: ExecutablePlan<L::View<'a>, R::View<'a>>,
156 W: for<'a> Fn(&S, &Leaf<'a, A>) -> Sc + Send + Sync,
157 Sc: Score + 'static,
158{
159 pub fn named(
160 self,
161 name: &str,
162 ) -> OperatorTerminal<
163 S,
164 ExistenceNode<S, L, R, C::Plan>,
165 impl for<'a> Fn(&S, &L::View<'a>) -> Sc + Send + Sync,
166 Sc,
167 > {
168 let exists = matches!(self.mode, ExistenceMode::Exists);
169 let node = ExistenceNode::new(self.left, self.right, self.condition, exists);
170 OperatorTerminal::new(
171 ConstraintRef::new("", name),
172 self.impact_type,
173 node,
174 self.weight,
175 self.is_hard,
176 )
177 }
178}
179
180impl<S, A, L, R, C, Sc: Score> std::fmt::Debug for ExistsConstraintStream<S, A, L, R, C, Sc>
181where
182 C: CompileCondition,
183{
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 f.debug_struct("ExistsConstraintStream")
186 .field("mode", &self.mode)
187 .finish()
188 }
189}
190
191impl<S, A, L, R, C, W, Sc: Score> std::fmt::Debug for ExistsConstraintBuilder<S, A, L, R, C, W, Sc>
192where
193 C: CompileCondition,
194{
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 f.debug_struct("ExistsConstraintBuilder")
197 .field("mode", &self.mode)
198 .field("impact_type", &self.impact_type)
199 .finish()
200 }
201}