1use crate::{
4 algebra::{AddAssignByRef, AddByRef, NegByRef},
5 circuit::{
6 Circuit, OwnershipPreference, Scope, Stream,
7 operator_traits::{BinaryOperator, Operator},
8 },
9};
10use std::{borrow::Cow, marker::PhantomData, ops::Neg};
11
12impl<C, D> Stream<C, D>
13where
14 C: Circuit,
15 D: AddByRef + AddAssignByRef + Clone + 'static,
16{
17 #[track_caller]
56 pub fn plus(&self, other: &Stream<C, D>) -> Stream<C, D> {
57 if self.has_sharded_version() && other.has_sharded_version() {
60 self.circuit()
61 .add_binary_operator(
62 Plus::new(),
63 &self.try_sharded_version(),
64 &other.try_sharded_version(),
65 )
66 .mark_sharded()
67 } else {
68 self.circuit().add_binary_operator(Plus::new(), self, other)
69 }
70 }
71}
72
73impl<C, D> Stream<C, D>
74where
75 C: Circuit,
76 D: AddByRef + AddAssignByRef + Neg<Output = D> + NegByRef + Clone + 'static,
77{
78 #[track_caller]
82 pub fn minus(&self, other: &Stream<C, D>) -> Stream<C, D> {
83 if self.has_sharded_version() && other.has_sharded_version() {
86 self.circuit()
87 .add_binary_operator(
88 Minus::new(),
89 &self.try_sharded_version(),
90 &other.try_sharded_version(),
91 )
92 .mark_sharded()
93 } else {
94 self.circuit()
95 .add_binary_operator(Minus::new(), self, other)
96 }
97 }
98}
99
100pub struct Plus<D> {
105 phantom: PhantomData<D>,
106}
107
108impl<D> Default for Plus<D> {
109 fn default() -> Self {
110 Self {
111 phantom: PhantomData,
112 }
113 }
114}
115
116impl<D> Plus<D> {
117 pub const fn new() -> Self {
118 Self {
119 phantom: PhantomData,
120 }
121 }
122}
123
124impl<D> Operator for Plus<D>
125where
126 D: 'static,
127{
128 fn name(&self) -> Cow<'static, str> {
129 Cow::from("Plus")
130 }
131
132 fn fixedpoint(&self, _scope: Scope) -> bool {
133 true
134 }
135}
136
137impl<D> BinaryOperator<D, D, D> for Plus<D>
138where
139 D: AddByRef + AddAssignByRef + Clone + 'static,
140{
141 async fn eval(&mut self, i1: &D, i2: &D) -> D {
142 i1.add_by_ref(i2)
143 }
144
145 async fn eval_owned_and_ref(&mut self, mut i1: D, i2: &D) -> D {
146 i1.add_assign_by_ref(i2);
147 i1
148 }
149
150 async fn eval_ref_and_owned(&mut self, i1: &D, mut i2: D) -> D {
151 i2.add_assign_by_ref(i1);
152 i2
153 }
154
155 async fn eval_owned(&mut self, i1: D, i2: D) -> D {
156 i1.add_by_ref(&i2)
157 }
158
159 fn input_preference(&self) -> (OwnershipPreference, OwnershipPreference) {
160 (
161 OwnershipPreference::PREFER_OWNED,
162 OwnershipPreference::PREFER_OWNED,
163 )
164 }
165}
166
167pub struct Minus<D> {
170 phantom: PhantomData<D>,
171}
172
173impl<D> Default for Minus<D> {
174 fn default() -> Self {
175 Self {
176 phantom: PhantomData,
177 }
178 }
179}
180
181impl<D> Minus<D> {
182 pub const fn new() -> Self {
183 Self {
184 phantom: PhantomData,
185 }
186 }
187}
188
189impl<D> Operator for Minus<D>
190where
191 D: 'static,
192{
193 fn name(&self) -> Cow<'static, str> {
194 Cow::from("Minus")
195 }
196
197 fn fixedpoint(&self, _scope: Scope) -> bool {
198 true
199 }
200}
201
202impl<D> BinaryOperator<D, D, D> for Minus<D>
205where
206 D: AddByRef + AddAssignByRef + Neg<Output = D> + NegByRef + Clone + 'static,
207{
208 async fn eval(&mut self, i1: &D, i2: &D) -> D {
209 let mut i2neg = i2.neg_by_ref();
210 i2neg.add_assign_by_ref(i1);
211 i2neg
212 }
213
214 async fn eval_owned_and_ref(&mut self, i1: D, i2: &D) -> D {
215 i1.add_by_ref(&i2.neg_by_ref())
216 }
217
218 async fn eval_ref_and_owned(&mut self, i1: &D, i2: D) -> D {
219 i2.neg().add_by_ref(i1)
220 }
221
222 async fn eval_owned(&mut self, i1: D, i2: D) -> D {
223 i1.add_by_ref(&i2.neg())
224 }
225
226 fn input_preference(&self) -> (OwnershipPreference, OwnershipPreference) {
227 (
228 OwnershipPreference::PREFER_OWNED,
229 OwnershipPreference::PREFER_OWNED,
230 )
231 }
232}
233
234#[cfg(test)]
235mod test {
236 use crate::{
237 Circuit, RootCircuit,
238 algebra::HasZero,
239 circuit::OwnershipPreference,
240 operator::{Generator, Inspect},
241 typed_batch::OrdZSet,
242 zset,
243 };
244
245 #[test]
246 fn scalar_plus() {
247 let circuit = RootCircuit::build(move |circuit| {
248 let mut n = 0;
249 let source1 = circuit.add_source(Generator::new(move || {
250 let res = n;
251 n += 1;
252 res
253 }));
254 let mut n = 100;
255 let source2 = circuit.add_source(Generator::new(move || {
256 let res = n;
257 n -= 1;
258 res
259 }));
260 source1.plus(&source2).inspect(|n| assert_eq!(*n, 100));
261 Ok(())
262 })
263 .unwrap()
264 .0;
265
266 for _ in 0..100 {
267 circuit.transaction().unwrap();
268 }
269 }
270
271 #[test]
272 #[cfg_attr(miri, ignore)]
273 fn zset_plus() {
274 let build_plus_circuit = |circuit: &RootCircuit| {
275 let mut s = <OrdZSet<_>>::zero();
276 let delta = zset! { 5 => 1};
277 let source1 = circuit.add_source(Generator::new(move || {
278 s = s.merge(&delta);
279 s.clone()
280 }));
281 let mut s = <OrdZSet<_>>::zero();
282 let delta = zset! { 5 => -1};
283 let source2 = circuit.add_source(Generator::new(move || {
284 s = s.merge(&delta);
285 s.clone()
286 }));
287 source1
288 .plus(&source2)
289 .inspect(|s| assert_eq!(s, &<OrdZSet<u64>>::zero()));
290 (source1, source2)
291 };
292
293 let build_minus_circuit = |circuit: &RootCircuit| {
294 let mut s = <OrdZSet<_>>::zero();
295 let delta = zset! { 5 => 1};
296 let source1 = circuit.add_source(Generator::new(move || {
297 s = s.merge(&delta);
298 s.clone()
299 }));
300 let mut s = <OrdZSet<_>>::zero();
301 let delta = zset! { 5 => 1};
302 let source2 = circuit.add_source(Generator::new(move || {
303 s = s.merge(&delta);
304 s.clone()
305 }));
306 source1
307 .minus(&source2)
308 .inspect(|s| assert_eq!(s, &<OrdZSet<_>>::zero()));
309 (source1, source2)
310 };
311 let circuit = RootCircuit::build(move |circuit| {
313 build_plus_circuit(circuit);
314 build_minus_circuit(circuit);
315 Ok(())
316 })
317 .unwrap()
318 .0;
319
320 for _ in 0..100 {
321 circuit.transaction().unwrap();
322 }
323
324 let circuit = RootCircuit::build(move |circuit| {
326 let (source1, _source2) = build_plus_circuit(circuit);
327 circuit.add_unary_operator_with_preference(
328 Inspect::new(|_| {}),
329 &source1,
330 OwnershipPreference::STRONGLY_PREFER_OWNED,
331 );
332 let (source3, _source4) = build_minus_circuit(circuit);
333 circuit.add_unary_operator_with_preference(
334 Inspect::new(|_| {}),
335 &source3,
336 OwnershipPreference::STRONGLY_PREFER_OWNED,
337 );
338 Ok(())
339 })
340 .unwrap()
341 .0;
342
343 for _ in 0..100 {
344 circuit.transaction().unwrap();
345 }
346
347 let circuit = RootCircuit::build(move |circuit| {
349 let (_source1, source2) = build_plus_circuit(circuit);
350 circuit.add_unary_operator_with_preference(
351 Inspect::new(|_| {}),
352 &source2,
353 OwnershipPreference::STRONGLY_PREFER_OWNED,
354 );
355
356 let (_source3, source4) = build_minus_circuit(circuit);
357 circuit.add_unary_operator_with_preference(
358 Inspect::new(|_| {}),
359 &source4,
360 OwnershipPreference::STRONGLY_PREFER_OWNED,
361 );
362 Ok(())
363 })
364 .unwrap()
365 .0;
366
367 for _ in 0..100 {
368 circuit.transaction().unwrap();
369 }
370
371 let circuit = RootCircuit::build(move |circuit| {
373 let (source1, source2) = build_plus_circuit(circuit);
374 circuit.add_unary_operator_with_preference(
375 Inspect::new(|_| {}),
376 &source1,
377 OwnershipPreference::STRONGLY_PREFER_OWNED,
378 );
379 circuit.add_unary_operator_with_preference(
380 Inspect::new(|_| {}),
381 &source2,
382 OwnershipPreference::STRONGLY_PREFER_OWNED,
383 );
384
385 let (source3, source4) = build_minus_circuit(circuit);
386 circuit.add_unary_operator_with_preference(
387 Inspect::new(|_| {}),
388 &source3,
389 OwnershipPreference::STRONGLY_PREFER_OWNED,
390 );
391 circuit.add_unary_operator_with_preference(
392 Inspect::new(|_| {}),
393 &source4,
394 OwnershipPreference::STRONGLY_PREFER_OWNED,
395 );
396 Ok(())
397 })
398 .unwrap()
399 .0;
400
401 for _ in 0..100 {
402 circuit.transaction().unwrap();
403 }
404 }
405}