Skip to main content

graphrecords_query/operations/logic/
exclusive_or.rs

1use super::{combine_masks_bare, combine_masks_indexed};
2use crate::{
3    Arity, Bare, ElementShape, ExclusiveOr, Explain, IndexDomain, Indexed, Labeled, Mask, Operand,
4    QueryResult,
5    execution::EvaluationCache,
6    operands::OperandHandle,
7    operations::{
8        Apply, ArgumentSource, ElementKernel, ElementPipeline, Keyed, Operation, OperationContext,
9        Prepare, Unaligned,
10    },
11    optimizer::{Estimate, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs, Stats},
12    registry::{describe::ArgumentRetention, operation_manifest},
13};
14use graphrecords_core::GraphRecord;
15use std::ops::BitXor;
16
17#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
18#[operation(scope = Element)]
19#[explain(label = "Xor")]
20#[plan(optimizer_hints(empty = if_all))]
21pub struct ExclusiveOrOperation<M> {
22    #[argument]
23    other: M,
24}
25
26impl<M: Prepare> Prepare for ExclusiveOrOperation<M> {
27    type Prepared<'a>
28        = M::Prepared<'a>
29    where
30        Self: 'a;
31
32    fn prepare<'a>(
33        &'a self,
34        graphrecord: &'a GraphRecord,
35        cache: &'a EvaluationCache<'a>,
36    ) -> QueryResult<Self::Prepared<'a>> {
37        self.other.prepare(graphrecord, cache)
38    }
39}
40
41impl<I, M> ElementKernel<Indexed<I, Mask>> for ExclusiveOrOperation<M>
42where
43    I: IndexDomain,
44    M: ArgumentSource<Keyed<I>, Mask>,
45{
46    type Emission = M::Retention;
47    type OutShape = Indexed<I, Mask>;
48
49    fn pipeline<'a>(
50        _graphrecord: &'a GraphRecord,
51        prepared: Self::Prepared<'a>,
52    ) -> QueryResult<ElementPipeline<'a, Indexed<I, Mask>, Self>> {
53        Ok(combine_masks_indexed::<_, M>(
54            prepared,
55            Self::LABEL,
56            |left, right| left ^ right,
57        ))
58    }
59
60    fn estimate(&self, input: Estimate, stats: &Stats) -> Estimate {
61        let selectivity = input
62            .selectivity
63            .zip(self.other.estimate(stats).selectivity)
64            .map(|(left, right)| (2.0 * left).mul_add(-right, left + right));
65
66        Estimate {
67            selectivity,
68            ..input
69        }
70    }
71}
72
73impl<M> ElementKernel<Bare<Mask>> for ExclusiveOrOperation<M>
74where
75    M: ArgumentSource<Unaligned, Mask>,
76{
77    type Emission = M::Retention;
78    type OutShape = Bare<Mask>;
79
80    fn pipeline<'a>(
81        _graphrecord: &'a GraphRecord,
82        prepared: Self::Prepared<'a>,
83    ) -> QueryResult<ElementPipeline<'a, Bare<Mask>, Self>> {
84        Ok(combine_masks_bare::<M>(
85            prepared,
86            Self::LABEL,
87            |left, right| left ^ right,
88        ))
89    }
90
91    fn estimate(&self, input: Estimate, stats: &Stats) -> Estimate {
92        let selectivity = input
93            .selectivity
94            .zip(self.other.estimate(stats).selectivity)
95            .map(|(left, right)| (2.0 * left).mul_add(-right, left + right));
96
97        Estimate {
98            selectivity,
99            ..input
100        }
101    }
102}
103
104impl<O, M> ExclusiveOr<M> for O
105where
106    ExclusiveOrOperation<M>: Operation,
107    O: Apply<ExclusiveOrOperation<M>>,
108{
109    type ReturnOperand = O::Output;
110
111    fn xor(&self, other: M) -> Self::ReturnOperand {
112        Self::ReturnOperand::new(OperationContext::new(
113            self.clone(),
114            ExclusiveOrOperation { other },
115        ))
116    }
117}
118
119impl<S, C, M> BitXor<M> for OperandHandle<S, C>
120where
121    S: ElementShape,
122    C: Arity,
123    Self: ExclusiveOr<M>,
124{
125    type Output = <Self as ExclusiveOr<M>>::ReturnOperand;
126
127    fn bitxor(self, rhs: M) -> Self::Output {
128        self.xor(rhs)
129    }
130}
131
132operation_manifest! {
133    ExclusiveOrOperation<M> {
134        method: ExclusiveOr<M>::xor;
135        scope: element;
136
137        kernel {
138            parameters: <I: IndexDomain>;
139            argument: M: ArgumentSource<Keyed<I>, Mask>;
140            input: Indexed<I, Mask>;
141            output: Indexed<I, Mask>;
142            emission: ArgumentRetention;
143        }
144
145        kernel {
146            parameters: <>;
147            argument: M: ArgumentSource<Unaligned, Mask>;
148            input: Bare<Mask>;
149            output: Bare<Mask>;
150            emission: ArgumentRetention;
151        }
152    }
153}