use std::hash::Hash;
use std::marker::PhantomData;
use solverforge_core::score::Score;
use solverforge_core::{ConstraintRef, ImpactType};
use super::collection_extract::CollectionExtract;
use super::collector::{Accumulator, Collector};
use super::relational::leaf_collector::{leaf_key, LeafCollector, OptionalEntityKey};
use super::relational::operator::{
CollectionNode, ComplementNode, ComplementView, GroupNode, GroupView,
};
use super::relational::view_plan::{GroupKey, ViewEqualPlan};
use super::relational::Leaf;
use super::weighting_support::ConstraintWeight;
use crate::constraint::relational::OperatorTerminal;
pub struct ComplementedConstraintStream<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, Sc>
where
Sc: Score,
{
extractor_a: EA,
extractor_b: EB,
key_a: KA,
key_b: KB,
collector: C,
default_fn: D,
_phantom: PhantomData<(
fn() -> S,
fn() -> A,
fn() -> B,
fn() -> K,
fn() -> V,
fn() -> R,
fn() -> Acc,
fn() -> Sc,
)>,
}
impl<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, Sc>
ComplementedConstraintStream<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, Sc>
where
S: Send + Sync + 'static,
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
K: Clone + Eq + Hash + Send + Sync + 'static,
EA: CollectionExtract<S, Item = A>,
EB: CollectionExtract<S, Item = B>,
KA: Fn(&A) -> Option<K> + Send + Sync,
KB: Fn(&B) -> K + Send + Sync,
C: for<'i> Collector<&'i A, Value = V, Result = R, Accumulator = Acc> + Send + Sync + 'static,
V: Send + Sync + 'static,
R: Send + Sync + 'static,
Acc: Accumulator<V, R> + Send + Sync + 'static,
D: Fn(&B) -> R + Send + Sync,
Sc: Score + 'static,
{
fn into_weighted_builder<W>(
self,
impact_type: ImpactType,
weight_fn: W,
is_hard: bool,
) -> ComplementedConstraintBuilder<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, W, Sc>
where
W: Fn(&K, &R) -> Sc + Send + Sync,
{
ComplementedConstraintBuilder {
extractor_a: self.extractor_a,
extractor_b: self.extractor_b,
key_a: self.key_a,
key_b: self.key_b,
collector: self.collector,
default_fn: self.default_fn,
impact_type,
weight_fn,
is_hard,
_phantom: PhantomData,
}
}
pub(crate) fn new(
extractor_a: EA,
extractor_b: EB,
key_a: KA,
key_b: KB,
collector: C,
default_fn: D,
) -> Self {
Self {
extractor_a,
extractor_b,
key_a,
key_b,
collector,
default_fn,
_phantom: PhantomData,
}
}
pub fn penalize<W>(
self,
weight: W,
) -> ComplementedConstraintBuilder<
S,
A,
B,
K,
EA,
EB,
KA,
KB,
C,
V,
R,
Acc,
D,
impl Fn(&K, &R) -> Sc + Send + Sync,
Sc,
>
where
W: for<'w> ConstraintWeight<(&'w K, &'w R), Sc> + Send + Sync,
{
let is_hard = weight.is_hard();
self.into_weighted_builder(
ImpactType::Penalty,
move |key: &K, result: &R| weight.score((key, result)),
is_hard,
)
}
pub fn reward<W>(
self,
weight: W,
) -> ComplementedConstraintBuilder<
S,
A,
B,
K,
EA,
EB,
KA,
KB,
C,
V,
R,
Acc,
D,
impl Fn(&K, &R) -> Sc + Send + Sync,
Sc,
>
where
W: for<'w> ConstraintWeight<(&'w K, &'w R), Sc> + Send + Sync,
{
let is_hard = weight.is_hard();
self.into_weighted_builder(
ImpactType::Reward,
move |key: &K, result: &R| weight.score((key, result)),
is_hard,
)
}
}
impl<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, Sc: Score> std::fmt::Debug
for ComplementedConstraintStream<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, Sc>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComplementedConstraintStream").finish()
}
}
pub struct ComplementedConstraintBuilder<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, W, Sc>
where
Sc: Score,
{
extractor_a: EA,
extractor_b: EB,
key_a: KA,
key_b: KB,
collector: C,
default_fn: D,
impact_type: ImpactType,
weight_fn: W,
is_hard: bool,
_phantom: PhantomData<(
fn() -> S,
fn() -> A,
fn() -> B,
fn() -> K,
fn() -> V,
fn() -> R,
fn() -> Acc,
fn() -> Sc,
)>,
}
impl<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, W, Sc>
ComplementedConstraintBuilder<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, W, Sc>
where
S: Send + Sync + 'static,
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
K: Clone + Eq + Hash + Send + Sync + 'static,
EA: CollectionExtract<S, Item = A> + 'static,
EB: CollectionExtract<S, Item = B> + 'static,
KA: Fn(&A) -> Option<K> + Send + Sync + 'static,
KB: Fn(&B) -> K + Send + Sync + Clone + 'static,
C: for<'i> Collector<&'i A, Value = V, Result = R, Accumulator = Acc> + Send + Sync + 'static,
V: Send + Sync + 'static,
R: Send + Sync + 'static,
Acc: Accumulator<V, R> + Send + Sync + 'static,
D: Fn(&B) -> R + Send + Sync,
W: Fn(&K, &R) -> Sc + Send + Sync,
Sc: Score + 'static,
{
pub fn named(
self,
name: &str,
) -> OperatorTerminal<
S,
ComplementNode<
S,
CollectionNode<S, EB>,
GroupNode<
CollectionNode<S, EA>,
impl for<'l> Fn(&Leaf<'l, A>) -> Option<K> + Send + Sync,
LeafCollector<C, V, R, Acc>,
Option<K>,
Acc,
V,
R,
>,
ViewEqualPlan<Option<K>, OptionalEntityKey<KB>, GroupKey>,
impl for<'l> Fn(&S, &Leaf<'l, B>) -> R + Send + Sync,
R,
>,
impl for<'l> Fn(
&S,
&ComplementView<
'l,
Leaf<'l, B>,
GroupView<'l, S, CollectionNode<S, EA>, Option<K>, Acc, V, R>,
R,
>,
) -> Sc
+ Send
+ Sync,
Sc,
> {
let key_a = self.key_a;
let key_b = self.key_b;
let key_b_weight = key_b.clone();
let default_fn = self.default_fn;
let weight_fn = self.weight_fn;
let group = GroupNode::new(
CollectionNode::new(self.extractor_a, 0),
leaf_key(key_a),
LeafCollector::new(self.collector),
);
let targets = CollectionNode::new(self.extractor_b, 1);
let tree = ComplementNode::new(
targets,
group,
ViewEqualPlan::new(OptionalEntityKey::new(key_b.clone()), GroupKey),
move |_s: &S, row: &Leaf<'_, B>| default_fn(row.entity),
);
let weight = move |_s: &S,
row: &ComplementView<
'_,
Leaf<'_, B>,
GroupView<'_, S, CollectionNode<S, EA>, Option<K>, Acc, V, R>,
R,
>| match row {
ComplementView::Real(pair) => {
let key = key_b_weight(pair.left.entity);
pair.right.with_result(|r| weight_fn(&key, r))
}
ComplementView::Default(projected) => {
let key = key_b_weight(projected.input.entity);
weight_fn(&key, projected.value)
}
};
OperatorTerminal::new(
ConstraintRef::new("", name),
self.impact_type,
tree,
weight,
self.is_hard,
)
}
}
impl<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, W, Sc: Score> std::fmt::Debug
for ComplementedConstraintBuilder<S, A, B, K, EA, EB, KA, KB, C, V, R, Acc, D, W, Sc>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComplementedConstraintBuilder")
.field("impact_type", &self.impact_type)
.finish()
}
}