Skip to content

Commit 1a694f7

Browse files
Merge pull request #13 from VariantSync/next-release
VEVOS Simulation v.1.1.2
2 parents 9c7c3a5 + 54346d6 commit 1a694f7

8 files changed

Lines changed: 200 additions & 25 deletions

File tree

src/main/java/org/variantsync/vevos/simulation/io/kernelhaven/ArtefactCSVExporter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ public CSV export() {
5959
private String[] toRow(final LineBasedAnnotation annotation) {
6060
final String[] row = makeRow();
6161
row[0] = currentFile.getFile().toString();
62-
row[1] = FormulaUtils.toString(currentFile.getPresenceCondition(), NodeWriter.javaSymbols);
63-
row[2] = FormulaUtils.toString(annotation.getFeatureMapping(), NodeWriter.javaSymbols);
64-
row[3] = FormulaUtils.toString(annotation.getPresenceCondition(), NodeWriter.javaSymbols);
62+
row[1] = FormulaUtils.toFormulaString(currentFile.getPresenceCondition(), NodeWriter.javaSymbols);
63+
row[2] = FormulaUtils.toFormulaString(annotation.getFeatureMapping(), NodeWriter.javaSymbols);
64+
row[3] = FormulaUtils.toFormulaString(annotation.getPresenceCondition(), NodeWriter.javaSymbols);
6565
row[4] = "" + annotation.getLineFrom();
6666
// -1 because Kernelhaven stores annotations as [#if, #endif) intervals, so we have to point one line before the annotation end (#endif).
6767
row[5] = "" + (annotation.getLineTo() - (annotation.isMacro() ? 1 : 0));

src/main/java/org/variantsync/vevos/simulation/repository/BusyboxRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,13 +275,13 @@ private static boolean doNotNormalizeDefUndef(@NonNull final String line) {
275275
temp = notNull(temp.replace("ENABLE_", "defined CONFIG_"));
276276
}
277277
if (temp.contains("&& ENABLE_")) {
278-
temp = notNull(temp.replace("ENABLE_", "'defined CONFIG_"));
278+
temp = notNull(temp.replace("ENABLE_", "defined CONFIG_"));
279279
}
280280
if (temp.contains("|| !ENABLE_")) {
281281
temp = notNull(temp.replace("!ENABLE_", "!defined CONFIG_"));
282282
}
283283
if (temp.contains("&& !ENABLE_")) {
284-
temp = notNull(temp.replace("!ENABLE_", "'!defined CONFIG_"));
284+
temp = notNull(temp.replace("!ENABLE_", "!defined CONFIG_"));
285285
}
286286

287287
return temp;

src/main/java/org/variantsync/vevos/simulation/util/fide/FormulaUtils.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,39 @@ public static Node replaceAllInplace(final Node root, final Predicate<Node> who,
103103
}
104104
}
105105

106-
public static String toString(Node formula, final String[] symbols) {
106+
/**
107+
* Wraps a literal name in a defined statement.
108+
* x -> defined(x)
109+
* !x -> !defined(x)
110+
*/
111+
private static Node ifdef(final Literal l) {
112+
final Function<Object, Literal> toDefinedLiteral = s -> new Literal("defined(" + s + ")");
113+
114+
if (l.positive) {
115+
return toDefinedLiteral.apply(l);
116+
} else {
117+
return new Not(toDefinedLiteral.apply(new Literal(l.var, true)));
118+
}
119+
}
120+
121+
/**
122+
* Serializes the given formula to a string usable in C preprocessor conditions.
123+
* True and false will be converted to 1 and 0, respectively.
124+
* Variables will be wrapped in a defined expression.
125+
* @see FixTrueFalse#TrueAs1
126+
* @see FixTrueFalse#FalseAs0
127+
*/
128+
public static String toCPPString(Node formula) {
107129
formula = replaceAll(formula, FixTrueFalse::isTrue, n -> FixTrueFalse.TrueAs1);
108130
formula = replaceAllInplace(formula, FixTrueFalse::isFalse, n -> FixTrueFalse.FalseAs0);
131+
formula = replaceAllInplace(formula, FixTrueFalse::isVariable,
132+
// we know that n is a literal because FixTureFalse::isVariable returned true
133+
n -> ifdef((Literal) n)
134+
);
135+
return toFormulaString(formula, NodeWriter.javaSymbols);
136+
}
109137

138+
public static String toFormulaString(final Node formula, final String[] symbols) {
110139
final NodeWriter writer = new NodeWriter(formula);
111140
writer.setNotation(NodeWriter.Notation.INFIX);
112141
writer.setEnquoteWhitespace(false);

src/main/java/org/variantsync/vevos/simulation/util/fide/bugfix/FixTrueFalse.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ public static boolean isFalse(final Node n) {
5050
return n instanceof Literal l && isFalseLiteral(l);
5151
}
5252

53+
/**
54+
* @return True iff the given formula is a literal that neither {@link #isTrue} nor {@link #isFalse}.
55+
*/
56+
public static boolean isVariable(final Node n) {
57+
return n instanceof Literal l && !isTrueLiteral(l) && !isFalseLiteral(l);
58+
}
59+
5360
/**
5461
* @return True iff the given name represents the atomic value true w.r.t. the constant TrueNames.
5562
*/

src/main/java/org/variantsync/vevos/simulation/variability/SPLCommit.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public Optional<SPLCommit[]> parents() {
138138
return Optional.ofNullable(parents);
139139
}
140140

141-
public void setParents(final SPLCommit[] parents) {
141+
public void setParents(final SPLCommit... parents) {
142142
this.parents = parents;
143143
}
144144

src/main/java/org/variantsync/vevos/simulation/variability/pc/variantlines/VariantAnnotation.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.variantsync.vevos.simulation.variability.pc.variantlines;
22

33
import org.prop4j.Node;
4-
import org.prop4j.NodeWriter;
54
import org.variantsync.vevos.simulation.util.fide.FormulaUtils;
5+
import org.variantsync.vevos.simulation.util.fide.bugfix.FixTrueFalse;
66
import org.variantsync.vevos.simulation.variability.pc.options.VariantGenerationOptions;
77

88
import java.util.ArrayList;
@@ -16,18 +16,24 @@ public record VariantAnnotation(
1616
public List<String> project(final VariantGenerationOptions projectionOptions, final List<String> splFileLines) {
1717
final List<String> result = new ArrayList<>();
1818

19-
if (projectionOptions.withMacros()) {
20-
result.add("#if " + FormulaUtils.toString(condition, NodeWriter.javaSymbols));
19+
final boolean genMacro = projectionOptions.withMacros() && !isTrue();
20+
21+
if (genMacro) {
22+
result.add("#if " + FormulaUtils.toCPPString(condition));
2123
}
2224

2325
for (final VariantLineChunk child : lines) {
2426
result.addAll(child.project(projectionOptions, splFileLines));
2527
}
2628

27-
if (projectionOptions.withMacros()) {
29+
if (genMacro) {
2830
result.add("#endif");
2931
}
3032

3133
return result;
3234
}
35+
36+
public boolean isTrue() {
37+
return FixTrueFalse.isTrue(condition);
38+
}
3339
}

src/main/java/org/variantsync/vevos/simulation/variability/sequenceextraction/LongestNonOverlappingSequences.java

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,29 +83,64 @@ public List<NonEmptyList<SPLCommit>> extract(final Collection<SPLCommit> commits
8383
}
8484
}
8585

86+
// Sort the sequences once more, after filtering them
87+
commitSequences.sort((o1, o2) -> Integer.compare(o2.size(), o1.size()));
8688
return commitSequences;
8789
}
8890

89-
// Recursively build sequences
9091
private static Set<LinkedList<SPLCommit>> retrieveSequencesForStart(final Map<SPLCommit, Set<SPLCommit>> parentChildMap, final SPLCommit start) {
91-
if (!parentChildMap.containsKey(start)) {
92-
// Create a new sequence that contains the start commit
93-
final Set<LinkedList<SPLCommit>> sequenceSet = new HashSet<>();
92+
// Set for holding all retrieved sequences
93+
Set<LinkedList<SPLCommit>> sequenceSet = new HashSet<>();
94+
{
95+
// We start with the 'start' commit
9496
final LinkedList<SPLCommit> sequence = new LinkedList<>();
9597
sequence.add(start);
9698
sequenceSet.add(sequence);
97-
return sequenceSet;
98-
} else {
99-
// Collect the sequences of the children and prepend the commit to each of them as parent
100-
final Set<LinkedList<SPLCommit>> sequences = new HashSet<>();
101-
for (final SPLCommit child : parentChildMap.get(start)) {
102-
final Set<LinkedList<SPLCommit>> childSequences = retrieveSequencesForStart(parentChildMap, child);
103-
for (final LinkedList<SPLCommit> childSequence : childSequences) {
104-
childSequence.addFirst(start);
105-
sequences.add(childSequence);
99+
}
100+
101+
// We continue to build sequences, going from parent to descendents, until the ends of all possible sequences have been reached
102+
boolean incomplete = true;
103+
while(incomplete) {
104+
// Why do we have to create a new HashSet every iteration?
105+
// (See comments https://github.com/VariantSync/VEVOS_Simulation/pull/13#discussion_r960891984)
106+
// It makes the code a lot simpler and runtime should not really be an issue here.
107+
// If we do not create a new set in each iteration, we have to create additional checks, because we can
108+
// only update the old sequence if there is only one child. If there is more than one child, we have to
109+
// first create copies of the sequence for each child and add these sequences to the set.
110+
// Finally, we could change the initial sequence in-place for the last child. However, because the children
111+
// are provided as a set, we either first have to convert the set to a list or array (which is the same as
112+
// creating a new set above), or we have to introduce additional counters and/or flags and checks.
113+
// It is not pretty. I was at least not able to think of something simpler, without introducing additional
114+
// cost elsewhere.
115+
Set<LinkedList<SPLCommit>> updatedSet = new HashSet<>();
116+
117+
// Set to false at the start of the iteration, it is set to true if at least one sequence has been extended
118+
incomplete = false;
119+
// For all found sequences, check whether their last commit has more children that can be added to the sequence
120+
// if multiple children exist, new sequences are created
121+
for (LinkedList<SPLCommit> sequence : sequenceSet) {
122+
final SPLCommit parent = sequence.getLast();
123+
if (parentChildMap.containsKey(parent)) {
124+
final Set<SPLCommit> children = parentChildMap.get(parent);
125+
for (SPLCommit child : children) {
126+
// There is at least one child, we are not done yet
127+
incomplete = true;
128+
// Create a new sequence from the old sequence for each child
129+
final LinkedList<SPLCommit> anotherSequence = new LinkedList<>(sequence);
130+
anotherSequence.add(child);
131+
// Add the new sequence to the new set
132+
updatedSet.add(anotherSequence);
133+
}
134+
} else {
135+
// There are no children, simply add the old sequence to the updated set
136+
updatedSet.add(sequence);
106137
}
107138
}
108-
return sequences;
139+
// Update the sequence set
140+
sequenceSet = updatedSet;
109141
}
142+
143+
return sequenceSet;
110144
}
111145
}
146+
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package org.variantsync.vevos.simulation.variability.sequenceextraction;
2+
3+
import org.junit.Assert;
4+
import org.junit.Test;
5+
import org.variantsync.functjonal.list.NonEmptyList;
6+
import org.variantsync.vevos.simulation.variability.SPLCommit;
7+
8+
import java.util.Arrays;
9+
import java.util.HashSet;
10+
import java.util.List;
11+
import java.util.Set;
12+
13+
public class LongestNonOverlappingSequencesTest {
14+
15+
@Test
16+
public void exampleExtraction() {
17+
// For example, if the commits comprise three partially overlapping sequences ([A-B-C-D-E], [X-Y-Z], [A-B-F-G]),
18+
// the function will return the sequences ([A-B-C-D-E], [X-Y-Z], [F-G]).
19+
final SPLCommit a = new SPLCommit("A");
20+
final SPLCommit b = new SPLCommit("B");
21+
final SPLCommit c = new SPLCommit("C");
22+
final SPLCommit d = new SPLCommit("D");
23+
final SPLCommit e = new SPLCommit("E");
24+
final SPLCommit f = new SPLCommit("F");
25+
final SPLCommit g = new SPLCommit("G");
26+
final SPLCommit x = new SPLCommit("X");
27+
final SPLCommit y = new SPLCommit("Y");
28+
final SPLCommit z = new SPLCommit("Z");
29+
// A has no parent
30+
a.setParents();
31+
// A-B
32+
b.setParents(a);
33+
// B-C
34+
c.setParents(b);
35+
// C-D
36+
d.setParents(c);
37+
// D-E
38+
e.setParents(d);
39+
// B-F
40+
f.setParents(b);
41+
// F-G
42+
g.setParents(f);
43+
// X has no parent
44+
x.setParents();
45+
// X-Y
46+
y.setParents(x);
47+
// Y-Z
48+
z.setParents(y);
49+
50+
List<SPLCommit> exampleCommits = Arrays.asList(a, b, c, d, e, f, g, x, y, z);
51+
NonEmptyList<SPLCommit> expectedOne = new NonEmptyList<>(Arrays.asList(a,b,c,d,e));
52+
NonEmptyList<SPLCommit> expectedTwo = new NonEmptyList<>(Arrays.asList(x, y, z));
53+
NonEmptyList<SPLCommit> expectedThree = new NonEmptyList<>(Arrays.asList(f, g));
54+
55+
LongestNonOverlappingSequences algorithm = new LongestNonOverlappingSequences();
56+
List<NonEmptyList<SPLCommit>> result = algorithm.extract(exampleCommits);
57+
58+
Assert.assertEquals(result.size(), 3);
59+
Assert.assertTrue(sequencesAreEqual(result.get(0), expectedOne));
60+
Assert.assertTrue(sequencesAreEqual(result.get(1), expectedTwo));
61+
Assert.assertTrue(sequencesAreEqual(result.get(2), expectedThree));
62+
}
63+
64+
@Test
65+
// This test caused a StackoverflowError for the previous implementation of LongestNonOverlappingSequences at
66+
// confirms that the Error is no longer thrown.
67+
public void stackOverflowPrevented() {
68+
int size = 10_000;
69+
Set<SPLCommit> commits = new HashSet<>();
70+
SPLCommit previousCommit = new SPLCommit("0");
71+
previousCommit.setParents();
72+
commits.add(previousCommit);
73+
for (int i = 1; i < size; i++) {
74+
SPLCommit commit = new SPLCommit(String.valueOf(i));
75+
commit.setParents(previousCommit);
76+
commits.add(commit);
77+
previousCommit = commit;
78+
}
79+
80+
LongestNonOverlappingSequences algo = new LongestNonOverlappingSequences();
81+
var result = algo.extract(commits);
82+
// Some sanity checks (i.e., is the sequence extracted correctly?)
83+
Assert.assertEquals(1, result.size());
84+
Assert.assertEquals(result.get(0).size(), size);
85+
}
86+
87+
private boolean sequencesAreEqual(final NonEmptyList<SPLCommit> a, final NonEmptyList<SPLCommit> b) {
88+
if (a.size() != b.size()) {
89+
return false;
90+
}
91+
for (int i = 0; i < a.size(); i++) {
92+
if (!a.get(i).id().equals(b.get(i).id())) {
93+
return false;
94+
}
95+
}
96+
return true;
97+
}
98+
}

0 commit comments

Comments
 (0)