aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java
blob: bfd4c049f207001fd10cc1eb1105242c77b8e278 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package tools.refinery.store.query.viatra.internal.cardinality;

import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator;
import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
import tools.refinery.store.representation.cardinality.FiniteUpperCardinality;
import tools.refinery.store.representation.cardinality.UnboundedUpperCardinality;
import tools.refinery.store.representation.cardinality.UpperCardinalities;
import tools.refinery.store.representation.cardinality.UpperCardinality;

import java.util.stream.Stream;

public class UpperCardinalitySumAggregationOperator implements IMultisetAggregationOperator<UpperCardinality,
		UpperCardinalitySumAggregationOperator.Accumulator, UpperCardinality> {
	public static final UpperCardinalitySumAggregationOperator INSTANCE = new UpperCardinalitySumAggregationOperator();

	public static final BoundAggregator BOUND_AGGREGATOR = new BoundAggregator(INSTANCE, UpperCardinality.class,
			UpperCardinality.class);

	private UpperCardinalitySumAggregationOperator() {
		// Singleton constructor.
	}

	@Override
	public String getName() {
		return "sum<UpperCardinality>";
	}

	@Override
	public String getShortDescription() {
		return "%s computes the sum of finite or unbounded upper cardinalities".formatted(getName());
	}

	@Override
	public Accumulator createNeutral() {
		return new Accumulator();
	}

	@Override
	public boolean isNeutral(Accumulator result) {
		return result.sumFiniteUpperBounds == 0 && result.countUnbounded == 0;
	}

	@Override
	public Accumulator update(Accumulator oldResult, UpperCardinality updateValue, boolean isInsertion) {
		if (updateValue instanceof FiniteUpperCardinality finiteUpperCardinality) {
			int finiteUpperBound = finiteUpperCardinality.finiteUpperBound();
			if (isInsertion) {
				oldResult.sumFiniteUpperBounds += finiteUpperBound;
			} else {
				oldResult.sumFiniteUpperBounds -= finiteUpperBound;
			}
		} else if (updateValue instanceof UnboundedUpperCardinality) {
			if (isInsertion) {
				oldResult.countUnbounded += 1;
			} else {
				oldResult.countUnbounded -= 1;
			}
		} else {
			throw new IllegalArgumentException("Unknown UpperCardinality: " + updateValue);
		}
		return oldResult;
	}

	@Override
	public UpperCardinality getAggregate(Accumulator result) {
		return result.countUnbounded > 0 ? UpperCardinalities.UNBOUNDED :
				UpperCardinalities.valueOf(result.sumFiniteUpperBounds);
	}

	@Override
	public UpperCardinality aggregateStream(Stream<UpperCardinality> stream) {
		var result = stream.collect(this::createNeutral, (accumulator, value) -> update(accumulator, value, true),
				(left, right) -> new Accumulator(left.sumFiniteUpperBounds + right.sumFiniteUpperBounds,
						left.countUnbounded + right.countUnbounded));
		return getAggregate(result);
	}

	@Override
	public Accumulator clone(Accumulator original) {
		return new Accumulator(original.sumFiniteUpperBounds, original.countUnbounded);
	}

	public static class Accumulator {
		private int sumFiniteUpperBounds;

		private int countUnbounded;

		private Accumulator(int sumFiniteUpperBounds, int countUnbounded) {
			this.sumFiniteUpperBounds = sumFiniteUpperBounds;
			this.countUnbounded = countUnbounded;
		}

		private Accumulator() {
			this(0, 0);
		}
	}
}