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