Skip to content

Commit bb6a9b1

Browse files
committed
domino sorting and automatic freeing of SPLCommits
1 parent 78d6cff commit bb6a9b1

10 files changed

Lines changed: 243 additions & 40 deletions

File tree

src/main/java/de/variantsync/evolution/Main.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,12 @@ public static void main(final String[] args) throws IOException, Resources.Resou
107107
final VariabilityDatasetLoader datasetLoader = new VariabilityDatasetLoader();
108108
assert datasetLoader.canLoad(variabilityDatasetDir);
109109
variabilityDataset = datasetLoader.load(variabilityDatasetDir).getSuccess();
110-
final Set<SPLCommitPair> commitPairs = variabilityDataset.getCommitPairsForEvolutionStudy();
110+
final Set<EvolutionStep<SPLCommit>> evolutionSteps = variabilityDataset.getCommitPairsForEvolutionStudy();
111111
Logger.info("The dataset contains " + variabilityDataset.getSuccessCommits().size() + " commits for which the variability extraction succeeded.");
112112
Logger.info("The dataset contains " + variabilityDataset.getErrorCommits().size() + " commits for which the variability extraction failed.");
113113
Logger.info("The dataset contains " + variabilityDataset.getPartialSuccessCommits().size() + " commits that for which the file presence conditions are missing.");
114-
Logger.info("The dataset contains " + commitPairs.size() + " usable pairs.");
115-
for (final SPLCommitPair pair : commitPairs) {
114+
Logger.info("The dataset contains " + evolutionSteps.size() + " usable pairs.");
115+
for (final EvolutionStep<SPLCommit> pair : evolutionSteps) {
116116
Logger.debug("<<CHILD> " + pair.child().id() + "> -- <<PARENT> " + pair.parent().id() + ">");
117117
Logger.debug("<<CHILD> " + pair.child().id() + "> -- <<SPL_COMMIT> " + pair.child().id() + ">");
118118
Logger.debug("<<PARENT> " + pair.parent().id() + "> -- <<SPL_COMMIT> " + pair.parent().id() + ">");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package de.variantsync.evolution.util.functional;
2+
3+
public interface CachedValue {
4+
void forget();
5+
}

src/main/java/de/variantsync/evolution/util/functional/Lazy.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* @param <A> The return type of this lazy computation.
2222
*/
2323
@SuppressWarnings("rawtypes")
24-
public class Lazy<A> implements Functor<Lazy, A> {
24+
public class Lazy<A> implements Functor<Lazy, A>, CachedValue {
2525
/**
2626
* Lazy is a monoid if the lazy values are monoidal.
2727
* Creates a Monoid for Lazy<A> from the monoid of the value type A.
@@ -99,12 +99,9 @@ public A take() {
9999

100100
/**
101101
* Clears the cached value, such that it has to be recomputed next time it is queried.
102-
* @return True if a value was cleared. False if there was no cached value to forget.
103102
*/
104-
public boolean forget() {
105-
final boolean aValueWasForgotten = val != null;
103+
public void forget() {
106104
val = null;
107-
return aValueWasForgotten;
108105
}
109106

110107
/**
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package de.variantsync.evolution.util.list;
2+
3+
import java.util.Stack;
4+
5+
public class StackUtil {
6+
public static <T> void pushAToB(final Stack<T> a, final Stack<T> b) {
7+
b.addAll(a);
8+
}
9+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package de.variantsync.evolution.variability;
2+
3+
import de.variantsync.evolution.repository.Commit;
4+
import de.variantsync.evolution.util.functional.CachedValue;
5+
import de.variantsync.evolution.util.list.StackUtil;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
import java.util.*;
9+
import java.util.stream.Stream;
10+
11+
public class DominoSortedEvolutionSteps<C extends Commit & CachedValue> implements Iterator<EvolutionStep<C>>, Iterable<EvolutionStep<C>> {
12+
private final Stack<Stack<C>> dominos;
13+
private final List<C> garbage;
14+
15+
public DominoSortedEvolutionSteps(final Stream<EvolutionStep<C>> steps) {
16+
this.dominos = DominoSort(steps);
17+
this.garbage = new ArrayList<>();
18+
}
19+
20+
public void clearCaches() {
21+
for (final C c : garbage) {
22+
c.forget();
23+
}
24+
garbage.clear();
25+
}
26+
27+
@Override
28+
public boolean hasNext() {
29+
return !dominos.isEmpty();
30+
}
31+
32+
@Override
33+
public EvolutionStep<C> next() {
34+
clearCaches();
35+
36+
final Stack<C> currentRow = dominos.peek();
37+
38+
assert !currentRow.isEmpty();
39+
final C child = currentRow.pop();
40+
garbage.add(child);
41+
42+
assert !currentRow.isEmpty();
43+
final C parent = currentRow.peek();
44+
45+
final EvolutionStep<C> result = new EvolutionStep<C>(parent, child);
46+
if (currentRow.size() == 1) {
47+
garbage.add(parent);
48+
dominos.pop();
49+
}
50+
return result;
51+
}
52+
53+
@NotNull
54+
@Override
55+
public Iterator<EvolutionStep<C>> iterator() {
56+
return this;
57+
}
58+
59+
private static <C extends Commit> Stack<Stack<C>> DominoSort(final Stream<EvolutionStep<C>> dominos) {
60+
final Stack<Stack<C>> chains = new Stack<>();
61+
62+
dominos.forEach(domino -> {
63+
// push the domino to an existing stack if possible
64+
for (final Stack<C> row : chains) {
65+
if (row.peek().equals(domino.parent())) {
66+
row.push(domino.child());
67+
return;
68+
}
69+
}
70+
71+
// if there was no suitable stack, create one
72+
final Stack<C> newRow = new Stack<>();
73+
newRow.add(domino.parent());
74+
newRow.add(domino.child());
75+
chains.push(newRow);
76+
});
77+
78+
// Merge all stacks until they cannot be reduced anymore.
79+
final Stack<Stack<C>> finalRows = new Stack<>();
80+
do {
81+
Stack<C> current = chains.pop();
82+
// System.out.println("Inspecting " + current);
83+
final Set<Stack<C>> mergedStacks = new HashSet<>();
84+
do {
85+
mergedStacks.clear();
86+
for (final Stack<C> other : chains) {
87+
if (current.firstElement().equals(other.lastElement())) {
88+
// System.out.println(" pushing it to " + other);
89+
other.pop();
90+
StackUtil.pushAToB(current, other);
91+
current = other;
92+
// System.out.println(" got " + current);
93+
mergedStacks.add(other);
94+
} else if (other.firstElement().equals(current.lastElement())) {
95+
// System.out.println(" pushed " + other + " onto it");
96+
current.pop();
97+
StackUtil.pushAToB(other, current);
98+
// System.out.println(" got " + current);
99+
mergedStacks.add(other);
100+
}
101+
}
102+
chains.removeAll(mergedStacks);
103+
} while (!mergedStacks.isEmpty());
104+
105+
finalRows.push(current);
106+
} while (!chains.isEmpty());
107+
108+
return finalRows;
109+
}
110+
111+
@Override
112+
public String toString() {
113+
return "DominoSortedEvolutionSteps{" + dominos + '}';
114+
}
115+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package de.variantsync.evolution.variability;
2+
3+
import de.variantsync.evolution.repository.Commit;
4+
5+
public record EvolutionStep<T extends Commit>(T parent, T child) {
6+
@Override
7+
public String toString() {
8+
return "(" + parent + ", " + child + ")";
9+
}
10+
}

src/main/java/de/variantsync/evolution/variability/SPLCommit.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import de.variantsync.evolution.io.Resources;
55
import de.variantsync.evolution.repository.Commit;
66
import de.variantsync.evolution.util.Logger;
7+
import de.variantsync.evolution.util.functional.CachedValue;
78
import de.variantsync.evolution.util.functional.Functional;
89
import de.variantsync.evolution.util.functional.Lazy;
910
import de.variantsync.evolution.util.functional.interfaces.FragileFunction;
@@ -19,7 +20,7 @@
1920
import java.util.Map;
2021
import java.util.Optional;
2122

22-
public class SPLCommit extends Commit {
23+
public class SPLCommit extends Commit implements CachedValue {
2324
private final Lazy<Optional<String>> kernelHavenLog;
2425
private final Lazy<Optional<IFeatureModel>> featureModel;
2526
private final Lazy<Optional<Artefact>> presenceConditions;
@@ -118,7 +119,8 @@ private static Path tryUnzip(final Path path) throws ZipException {
118119
/**
119120
* Clears all cached values by calling {@link Lazy#forget()} on all fields.
120121
*/
121-
public void clearCaches() {
122+
@Override
123+
public void forget() {
122124
kernelHavenLog.forget();
123125
featureModel.forget();
124126
presenceConditions.forget();

src/main/java/de/variantsync/evolution/variability/SPLCommitPair.java

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/main/java/de/variantsync/evolution/variability/VariabilityDataset.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import java.util.*;
88
import java.util.function.Function;
9-
import java.util.stream.Collectors;
109

1110
public class VariabilityDataset {
1211
private final Set<SPLCommit> allCommits;
@@ -96,27 +95,28 @@ public VariabilityHistory getVariabilityHistory(final Function<Collection<SPLCom
9695
*
9796
* @return Set of commit pairs that can be used in a variability evolution study
9897
*/
99-
public Set<SPLCommitPair> getCommitPairsForEvolutionStudy() {
100-
Logger.info("Retrieving commit pairs for study...");
101-
final var set = successCommits.stream()
102-
.map(c -> {
103-
if (c.parents().isPresent()) {
104-
final SPLCommit[] parents = c.parents().get();
105-
// We only consider commits that did not process a merge
106-
final boolean notAMerge = parents.length == 1;
107-
final SPLCommit p = parents[0];
108-
// We only consider commits that processed an SPL commit whose parent was also processed
109-
final boolean parentSuccess = successCommits.contains(p);
110-
if (notAMerge && parentSuccess) {
111-
return new SPLCommitPair(p, c);
112-
}
113-
}
114-
return null;
115-
})
116-
.filter(Objects::nonNull)
117-
.collect(Collectors.toSet());
118-
Logger.info("Done.");
119-
return set;
98+
public DominoSortedEvolutionSteps<SPLCommit> getCommitPairsForEvolutionStudy() {
99+
Logger.debug("Retrieving commit pairs for study...");
100+
final var steps = new DominoSortedEvolutionSteps<>(
101+
successCommits.stream()
102+
.map(c -> {
103+
if (c.parents().isPresent()) {
104+
final SPLCommit[] parents = c.parents().get();
105+
// We only consider commits that did not process a merge
106+
final boolean notAMerge = parents.length == 1;
107+
final SPLCommit p = parents[0];
108+
// We only consider commits that processed an SPL commit whose parent was also processed
109+
final boolean parentSuccess = successCommits.contains(p);
110+
if (notAMerge && parentSuccess) {
111+
return new EvolutionStep<>(p, c);
112+
}
113+
}
114+
return null;
115+
})
116+
.filter(Objects::nonNull)
117+
);
118+
Logger.debug("Done.");
119+
return steps;
120120
}
121121

122122
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package de.variantsync.evolution;
2+
3+
import de.variantsync.evolution.repository.Commit;
4+
import de.variantsync.evolution.util.functional.CachedValue;
5+
import de.variantsync.evolution.util.list.StackUtil;
6+
import de.variantsync.evolution.variability.DominoSortedEvolutionSteps;
7+
import de.variantsync.evolution.variability.EvolutionStep;
8+
import org.junit.Assert;
9+
import org.junit.Test;
10+
11+
import java.util.List;
12+
import java.util.Stack;
13+
14+
public class StackTest {
15+
private static class TestCommit extends Commit implements CachedValue {
16+
public TestCommit(final String commitId) {
17+
super(commitId);
18+
}
19+
@Override
20+
public void forget() {
21+
System.out.println("forget(" + super.id() + ")");
22+
}
23+
}
24+
25+
private static TestCommit commit(final String id) {
26+
return new TestCommit(id);
27+
}
28+
29+
private static EvolutionStep<TestCommit> pair(final String parent, final String child) {
30+
return new EvolutionStep<>(commit(parent), commit(child));
31+
}
32+
33+
@Test
34+
public void testMergeStacks() {
35+
final Stack<Integer> b = new Stack<>();
36+
b.push(1);
37+
b.push(2);
38+
final Stack<Integer> a = new Stack<>();
39+
a.push(3);
40+
a.push(4);
41+
final Stack<Integer> result = new Stack<>();
42+
result.push(1);
43+
result.push(2);
44+
result.push(3);
45+
result.push(4);
46+
47+
StackUtil.pushAToB(a, b);
48+
Assert.assertEquals(b, result);
49+
}
50+
51+
@Test
52+
public void evolSteps() {
53+
final List<EvolutionStep<TestCommit>> input = List.of(
54+
pair("1", "2"),
55+
pair("4", "5"),
56+
pair("2", "3"),
57+
pair("3", "4"),
58+
59+
pair("12", "13"),
60+
pair("10", "11"),
61+
pair("11", "12")
62+
);
63+
64+
final DominoSortedEvolutionSteps<TestCommit> steps = new DominoSortedEvolutionSteps<>(input.stream());
65+
System.out.println("Sorted steps: " + steps);
66+
67+
for (final EvolutionStep<TestCommit> step : steps) {
68+
System.out.println("Processing " + step);
69+
}
70+
System.out.println("Manual cleanup");
71+
steps.clearCaches();
72+
}
73+
}

0 commit comments

Comments
 (0)