ConceptGraphService.java

1
package edu.ucsb.cs.scaffold.services;
2
3
import edu.ucsb.cs.scaffold.entity.Concept;
4
import edu.ucsb.cs.scaffold.entity.ConceptEdge;
5
import java.util.ArrayDeque;
6
import java.util.ArrayList;
7
import java.util.Comparator;
8
import java.util.Deque;
9
import java.util.HashMap;
10
import java.util.HashSet;
11
import java.util.List;
12
import java.util.Map;
13
import java.util.Set;
14
import java.util.stream.Collectors;
15
import org.springframework.stereotype.Service;
16
17
/**
18
 * Graph algorithms for the prerequisite structure of a course's top-level concepts (subconcepts
19
 * have no position in this graph). Prerequisite edges only ever connect top-level concepts, so
20
 * every method here operates on that subgraph.
21
 *
22
 * <p>{@link #reset} is the analysis run by {@code POST /api/course/scaffold/reset}: it detects
23
 * cycles (flagging their edges rather than processing them further), removes edges that are
24
 * redundant given the graph's transitive structure, ranks concepts by longest path from a root, and
25
 * lays out each level's x,y position. It is a pure function of its inputs — no repository access —
26
 * so the controller owns loading input and persisting the result.
27
 */
28
@Service
29
public class ConceptGraphService {
30
31
  // Index i holds the color for level i+1; levels beyond the palette reuse the last color.
32
  public static final List<String> LEVEL_COLORS =
33
      List.of("#c99ffe", "#feaef2", "#93ebff", "#fe9a71", "#2bcd9c");
34
35
  public static final String CYCLE_EDGE_COLOR = "#FF0000";
36
37
  // Initial guesses; revisit if the resulting layout looks too cramped or too sparse. These
38
  // are also the default Course.xSpacing/ySpacing values (see the Course entity and its
39
  // migration), which let each course override the spacing used by POST
40
  // /api/course/scaffold/reset via PUT /api/course/scaffold/spacing.
41
  public static final int MIN_HORIZONTAL_SEPARATION = 350;
42
  public static final int VERTICAL_LEVEL_SEPARATION = 300;
43
44
  /** The color assigned to a top-level concept at the given longest-path level (1-based). */
45
  public String colorForLevel(int level) {
46 1 1. colorForLevel : Replaced integer subtraction with addition → KILLED
    int index = Math.min(Math.max(level, 1), LEVEL_COLORS.size()) - 1;
47 1 1. colorForLevel : replaced return value with "" for edu/ucsb/cs/scaffold/services/ConceptGraphService::colorForLevel → KILLED
    return LEVEL_COLORS.get(index);
48
  }
49
50
  /**
51
   * True if adding an edge sourceId -&gt; targetId would create a cycle, i.e. targetId can already
52
   * reach sourceId via existingEdges. Used to reject new prerequisite edges at creation time,
53
   * before a cycle can ever be persisted.
54
   */
55
  public boolean wouldCreateCycle(List<ConceptEdge> existingEdges, Long sourceId, Long targetId) {
56
    Map<Long, List<Long>> adjacency = buildAdjacency(existingEdges);
57
    Set<Long> visited = new HashSet<>();
58
    Deque<Long> queue = new ArrayDeque<>();
59
    visited.add(targetId);
60
    queue.add(targetId);
61 1 1. wouldCreateCycle : negated conditional → KILLED
    while (!queue.isEmpty()) {
62
      Long current = queue.poll();
63 1 1. wouldCreateCycle : negated conditional → KILLED
      if (current.equals(sourceId)) {
64 1 1. wouldCreateCycle : replaced boolean return with false for edu/ucsb/cs/scaffold/services/ConceptGraphService::wouldCreateCycle → KILLED
        return true;
65
      }
66
      for (Long next : adjacency.getOrDefault(current, List.of())) {
67 1 1. wouldCreateCycle : negated conditional → KILLED
        if (visited.add(next)) {
68
          queue.add(next);
69
        }
70
      }
71
    }
72 1 1. wouldCreateCycle : replaced boolean return with true for edu/ucsb/cs/scaffold/services/ConceptGraphService::wouldCreateCycle → KILLED
    return false;
73
  }
74
75
  public record Position(int x, int y) {}
76
77
  public record ResetResult(
78
      Set<Long> cycleEdgeIds,
79
      Set<Long> removedEdgeIds,
80
      Map<Long, Integer> levelByConceptId,
81
      Map<Long, Position> positionByConceptId) {}
82
83
  /**
84
   * Runs the full scaffold reset analysis: cycle detection, transitive reduction, longest-path
85
   * leveling, and layout. Does not mutate concepts or edges or read/write any repository; the
86
   * caller applies {@link ResetResult} to persistent entities.
87
   *
88
   * @param topLevelConcepts every top-level concept in the course (used for their id/x, to sort and
89
   *     lay out concepts with no edges at all, and as the node set for the graph algorithms)
90
   * @param edges every prerequisite edge in the course
91
   */
92
  public ResetResult reset(List<Concept> topLevelConcepts, List<ConceptEdge> edges) {
93
    Map<Long, Integer> priorXByConceptId =
94
        topLevelConcepts.stream().collect(Collectors.toMap(Concept::getId, Concept::getX));
95 1 1. reset : replaced return value with null for edu/ucsb/cs/scaffold/services/ConceptGraphService::reset → KILLED
    return reset(topLevelConcepts, edges, priorXByConceptId);
96
  }
97
98
  /**
99
   * Like {@link #reset(List, List)}, but sorts each level by the given prior x values instead of
100
   * each concept's own x column. Used by the controller to sort by the requesting user's private,
101
   * unsaved drag positions where they exist, falling back to the concept's persisted x otherwise —
102
   * see {@code POST /api/course/scaffold/reset}. Lays out using the default {@link
103
   * #MIN_HORIZONTAL_SEPARATION}/{@link #VERTICAL_LEVEL_SEPARATION}; see {@link #reset(List, List,
104
   * Map, int, int)} to override the spacing.
105
   */
106
  public ResetResult reset(
107
      List<Concept> topLevelConcepts,
108
      List<ConceptEdge> edges,
109
      Map<Long, Integer> priorXByConceptId) {
110 1 1. reset : replaced return value with null for edu/ucsb/cs/scaffold/services/ConceptGraphService::reset → KILLED
    return reset(
111
        topLevelConcepts,
112
        edges,
113
        priorXByConceptId,
114
        MIN_HORIZONTAL_SEPARATION,
115
        VERTICAL_LEVEL_SEPARATION);
116
  }
117
118
  /**
119
   * Like {@link #reset(List, List, Map)}, but lays out using the given xSpacing/ySpacing instead of
120
   * the default {@link #MIN_HORIZONTAL_SEPARATION}/{@link #VERTICAL_LEVEL_SEPARATION}. Used by the
121
   * controller to honor a course's {@code xSpacing}/{@code ySpacing} settings — see {@code POST
122
   * /api/course/scaffold/reset}.
123
   */
124
  public ResetResult reset(
125
      List<Concept> topLevelConcepts,
126
      List<ConceptEdge> edges,
127
      Map<Long, Integer> priorXByConceptId,
128
      int xSpacing,
129
      int ySpacing) {
130
    Set<Long> nodeIds = topLevelConcepts.stream().map(Concept::getId).collect(Collectors.toSet());
131
132
    Map<Long, Long> sccId = computeStronglyConnectedComponents(buildAdjacency(edges), nodeIds);
133
    Map<Long, Long> sccSize =
134 1 1. lambda$reset$0 : replaced Long return value with 0L for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$reset$0 → KILLED
        sccId.values().stream().collect(Collectors.groupingBy(id -> id, Collectors.counting()));
135
136
    Set<Long> cycleEdgeIds = new HashSet<>();
137
    List<ConceptEdge> acyclicEdges = new ArrayList<>();
138
    for (ConceptEdge edge : edges) {
139
      Long sourceId = edge.getSource().getId();
140
      Long targetId = edge.getTarget().getId();
141
      boolean inCycle =
142 3 1. reset : negated conditional → KILLED
2. reset : changed conditional boundary → KILLED
3. reset : negated conditional → KILLED
          sccId.get(sourceId).equals(sccId.get(targetId)) && sccSize.get(sccId.get(sourceId)) > 1;
143 1 1. reset : negated conditional → KILLED
      if (inCycle) {
144
        cycleEdgeIds.add(edge.getId());
145
      } else {
146
        acyclicEdges.add(edge);
147
      }
148
    }
149
150
    Set<Long> removedEdgeIds = computeTransitiveReductionRemovals(nodeIds, acyclicEdges);
151
152
    // The transitively redundant edges are only reported for deletion, not filtered out
153
    // before leveling: a removed edge u->v is by definition a shortcut for a longer path
154
    // u -> ... -> v, whose constraint on v's level always dominates, so including or
155
    // excluding these edges cannot change any longest-path level.
156
    Map<Long, Integer> levelByConceptId = computeLongestPathLevels(nodeIds, acyclicEdges);
157
    Map<Long, Position> positionByConceptId =
158
        computeLayout(topLevelConcepts, levelByConceptId, priorXByConceptId, xSpacing, ySpacing);
159
160 1 1. reset : replaced return value with null for edu/ucsb/cs/scaffold/services/ConceptGraphService::reset → KILLED
    return new ResetResult(cycleEdgeIds, removedEdgeIds, levelByConceptId, positionByConceptId);
161
  }
162
163
  /**
164
   * Lays each level out left to right, sorted by each concept's prior x (then id to break ties),
165
   * centered horizontally at x=0. Each level sits {@code ySpacing} above the previous one, with
166
   * level 1 at y=0.
167
   */
168
  private Map<Long, Position> computeLayout(
169
      List<Concept> topLevelConcepts,
170
      Map<Long, Integer> levelByConceptId,
171
      Map<Long, Integer> priorXByConceptId,
172
      int xSpacing,
173
      int ySpacing) {
174
    Map<Integer, List<Concept>> byLevel =
175
        topLevelConcepts.stream()
176 1 1. lambda$computeLayout$1 : replaced Integer return value with 0 for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$computeLayout$1 → KILLED
            .collect(Collectors.groupingBy(c -> levelByConceptId.get(c.getId())));
177
178
    Map<Long, Position> positions = new HashMap<>();
179
    for (Map.Entry<Integer, List<Concept>> entry : byLevel.entrySet()) {
180
      int level = entry.getKey();
181
      List<Concept> sorted =
182
          entry.getValue().stream()
183
              .sorted(
184 1 1. lambda$computeLayout$2 : replaced Integer return value with 0 for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$computeLayout$2 → KILLED
                  Comparator.comparing((Concept c) -> priorXByConceptId.get(c.getId()))
185
                      .thenComparing(Concept::getId))
186
              .toList();
187
      int n = sorted.size();
188 3 1. computeLayout : Replaced integer subtraction with addition → KILLED
2. computeLayout : removed negation → KILLED
3. computeLayout : Replaced integer multiplication with division → KILLED
      int y = -(level - 1) * ySpacing;
189 2 1. computeLayout : changed conditional boundary → KILLED
2. computeLayout : negated conditional → KILLED
      for (int i = 0; i < n; i++) {
190 4 1. computeLayout : Replaced double multiplication with division → KILLED
2. computeLayout : Replaced double division with multiplication → KILLED
3. computeLayout : Replaced double subtraction with addition → KILLED
4. computeLayout : Replaced integer subtraction with addition → KILLED
        int x = (int) Math.round((i - (n - 1) / 2.0) * xSpacing);
191
        positions.put(sorted.get(i).getId(), new Position(x, y));
192
      }
193
    }
194 1 1. computeLayout : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeLayout → KILLED
    return positions;
195
  }
196
197
  /**
198
   * Tarjan's algorithm: maps each node id to an id shared by its strongly connected component (a
199
   * nontrivial cycle iff more than one node shares it). The component id is the id of the
200
   * component's root node — any value unique per component would do, and using the root avoids
201
   * maintaining a separate counter.
202
   */
203
  private Map<Long, Long> computeStronglyConnectedComponents(
204
      Map<Long, List<Long>> adjacency, Set<Long> nodeIds) {
205
    TarjanState state = new TarjanState(adjacency);
206
    for (Long node : nodeIds) {
207 1 1. computeStronglyConnectedComponents : negated conditional → KILLED
      if (!state.index.containsKey(node)) {
208 1 1. computeStronglyConnectedComponents : removed call to edu/ucsb/cs/scaffold/services/ConceptGraphService$TarjanState::strongConnect → KILLED
        state.strongConnect(node);
209
      }
210
    }
211 1 1. computeStronglyConnectedComponents : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeStronglyConnectedComponents → KILLED
    return state.sccId;
212
  }
213
214
  private static final class TarjanState {
215
    private final Map<Long, List<Long>> adjacency;
216
    private final Map<Long, Integer> index = new HashMap<>();
217
    private final Map<Long, Integer> lowlink = new HashMap<>();
218
    private final Set<Long> onStack = new HashSet<>();
219
    private final Deque<Long> stack = new ArrayDeque<>();
220
    private final Map<Long, Long> sccId = new HashMap<>();
221
    private int counter = 0;
222
223
    TarjanState(Map<Long, List<Long>> adjacency) {
224
      this.adjacency = adjacency;
225
    }
226
227
    void strongConnect(Long v) {
228
      index.put(v, counter);
229
      lowlink.put(v, counter);
230 1 1. strongConnect : Replaced integer addition with subtraction → KILLED
      counter++;
231 1 1. strongConnect : removed call to java/util/Deque::push → KILLED
      stack.push(v);
232
      onStack.add(v);
233
234
      for (Long w : adjacency.getOrDefault(v, List.of())) {
235 1 1. strongConnect : negated conditional → KILLED
        if (!index.containsKey(w)) {
236 1 1. strongConnect : removed call to edu/ucsb/cs/scaffold/services/ConceptGraphService$TarjanState::strongConnect → KILLED
          strongConnect(w);
237
          lowlink.put(v, Math.min(lowlink.get(v), lowlink.get(w)));
238 1 1. strongConnect : negated conditional → KILLED
        } else if (onStack.contains(w)) {
239
          lowlink.put(v, Math.min(lowlink.get(v), index.get(w)));
240
        }
241
      }
242
243 1 1. strongConnect : negated conditional → KILLED
      if (lowlink.get(v).equals(index.get(v))) {
244
        Long w;
245
        do {
246
          w = stack.pop();
247
          onStack.remove(w);
248
          // v is this component's root: every member gets tagged with its id.
249
          sccId.put(w, v);
250 1 1. strongConnect : negated conditional → KILLED
        } while (!w.equals(v));
251
      }
252
    }
253
  }
254
255
  /**
256
   * An edge u-&gt;v is redundant if some other direct successor w of u can also reach v; such an
257
   * edge is a "shortcut" whose removal does not change reachability. Requires an acyclic edge set.
258
   */
259
  private Set<Long> computeTransitiveReductionRemovals(
260
      Set<Long> nodeIds, List<ConceptEdge> acyclicEdges) {
261
    Map<Long, List<Long>> adjacency = buildAdjacency(acyclicEdges);
262
    Map<Long, Set<Long>> reachable = new HashMap<>();
263
    for (Long node : nodeIds) {
264
      reachable.put(node, bfsReachable(node, adjacency));
265
    }
266
267
    Set<Long> removed = new HashSet<>();
268
    for (ConceptEdge edge : acyclicEdges) {
269
      Long u = edge.getSource().getId();
270
      Long v = edge.getTarget().getId();
271
      boolean redundant =
272
          adjacency.getOrDefault(u, List.of()).stream()
273 3 1. lambda$computeTransitiveReductionRemovals$3 : replaced boolean return with true for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$computeTransitiveReductionRemovals$3 → KILLED
2. lambda$computeTransitiveReductionRemovals$3 : negated conditional → KILLED
3. lambda$computeTransitiveReductionRemovals$3 : negated conditional → KILLED
              .anyMatch(w -> !w.equals(v) && reachable.getOrDefault(w, Set.of()).contains(v));
274 1 1. computeTransitiveReductionRemovals : negated conditional → KILLED
      if (redundant) {
275
        removed.add(edge.getId());
276
      }
277
    }
278 1 1. computeTransitiveReductionRemovals : replaced return value with Collections.emptySet for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeTransitiveReductionRemovals → KILLED
    return removed;
279
  }
280
281
  /**
282
   * Longest path from any root (no incoming edge) to each node; roots are level 1. Requires an
283
   * acyclic edge set.
284
   */
285
  private Map<Long, Integer> computeLongestPathLevels(Set<Long> nodeIds, List<ConceptEdge> edges) {
286
    Map<Long, List<Long>> outNeighbors = buildAdjacency(edges);
287
    Map<Long, Integer> inDegree = new HashMap<>();
288
    for (Long node : nodeIds) {
289
      inDegree.put(node, 0);
290
    }
291
    for (ConceptEdge edge : edges) {
292
      inDegree.merge(edge.getTarget().getId(), 1, Integer::sum);
293
    }
294
295
    Map<Long, Integer> level = new HashMap<>();
296
    Deque<Long> queue = new ArrayDeque<>();
297
    for (Long node : nodeIds) {
298
      level.put(node, 1);
299 1 1. computeLongestPathLevels : negated conditional → KILLED
      if (inDegree.get(node) == 0) {
300
        queue.add(node);
301
      }
302
    }
303
304 1 1. computeLongestPathLevels : negated conditional → TIMED_OUT
    while (!queue.isEmpty()) {
305
      Long u = queue.poll();
306
      for (Long v : outNeighbors.getOrDefault(u, List.of())) {
307 1 1. computeLongestPathLevels : Replaced integer addition with subtraction → KILLED
        level.put(v, Math.max(level.get(v), level.get(u) + 1));
308 1 1. computeLongestPathLevels : negated conditional → KILLED
        if (inDegree.merge(v, -1, Integer::sum) == 0) {
309
          queue.add(v);
310
        }
311
      }
312
    }
313 1 1. computeLongestPathLevels : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeLongestPathLevels → KILLED
    return level;
314
  }
315
316
  private Set<Long> bfsReachable(Long start, Map<Long, List<Long>> adjacency) {
317
    Set<Long> visited = new HashSet<>(adjacency.getOrDefault(start, List.of()));
318
    Deque<Long> queue = new ArrayDeque<>(visited);
319 1 1. bfsReachable : negated conditional → TIMED_OUT
    while (!queue.isEmpty()) {
320
      Long current = queue.poll();
321
      for (Long next : adjacency.getOrDefault(current, List.of())) {
322 1 1. bfsReachable : negated conditional → TIMED_OUT
        if (visited.add(next)) {
323
          queue.add(next);
324
        }
325
      }
326
    }
327 1 1. bfsReachable : replaced return value with Collections.emptySet for edu/ucsb/cs/scaffold/services/ConceptGraphService::bfsReachable → KILLED
    return visited;
328
  }
329
330
  private Map<Long, List<Long>> buildAdjacency(List<ConceptEdge> edges) {
331
    Map<Long, List<Long>> adjacency = new HashMap<>();
332
    for (ConceptEdge edge : edges) {
333
      adjacency
334 1 1. lambda$buildAdjacency$4 : replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$buildAdjacency$4 → KILLED
          .computeIfAbsent(edge.getSource().getId(), k -> new ArrayList<>())
335
          .add(edge.getTarget().getId());
336
    }
337 1 1. buildAdjacency : replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::buildAdjacency → KILLED
    return adjacency;
338
  }
339
}

Mutations

46

1.1
Location : colorForLevel
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:colorForLevel_clamps_a_non_positive_level_to_level_one()]
Replaced integer subtraction with addition → KILLED

47

1.1
Location : colorForLevel
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:colorForLevel_clamps_a_non_positive_level_to_level_one()]
replaced return value with "" for edu/ucsb/cs/scaffold/services/ConceptGraphService::colorForLevel → KILLED

61

1.1
Location : wouldCreateCycle
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:wouldCreateCycle_is_true_when_the_target_can_reach_the_source_transitively()]
negated conditional → KILLED

63

1.1
Location : wouldCreateCycle
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:wouldCreateCycle_is_false_when_there_are_no_existing_edges()]
negated conditional → KILLED

64

1.1
Location : wouldCreateCycle
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:wouldCreateCycle_is_true_when_the_target_can_reach_the_source_transitively()]
replaced boolean return with false for edu/ucsb/cs/scaffold/services/ConceptGraphService::wouldCreateCycle → KILLED

67

1.1
Location : wouldCreateCycle
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:wouldCreateCycle_is_true_when_the_target_can_reach_the_source_transitively()]
negated conditional → KILLED

72

1.1
Location : wouldCreateCycle
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:wouldCreateCycle_is_false_when_there_are_no_existing_edges()]
replaced boolean return with true for edu/ucsb/cs/scaffold/services/ConceptGraphService::wouldCreateCycle → KILLED

95

1.1
Location : reset
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
replaced return value with null for edu/ucsb/cs/scaffold/services/ConceptGraphService::reset → KILLED

110

1.1
Location : reset
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
replaced return value with null for edu/ucsb/cs/scaffold/services/ConceptGraphService::reset → KILLED

134

1.1
Location : lambda$reset$0
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
replaced Long return value with 0L for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$reset$0 → KILLED

142

1.1
Location : reset
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
negated conditional → KILLED

2.2
Location : reset
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_does_not_treat_a_self_loop_as_a_cycle()]
changed conditional boundary → KILLED

3.3
Location : reset
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
negated conditional → KILLED

143

1.1
Location : reset
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
negated conditional → KILLED

160

1.1
Location : reset
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
replaced return value with null for edu/ucsb/cs/scaffold/services/ConceptGraphService::reset → KILLED

176

1.1
Location : lambda$computeLayout$1
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
replaced Integer return value with 0 for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$computeLayout$1 → KILLED

184

1.1
Location : lambda$computeLayout$2
Killed by : edu.ucsb.cs.scaffold.controller.ConceptsControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.controller.ConceptsControllerTests]/[method:reset_falls_back_to_saved_x_when_the_override_has_no_x_value()]
replaced Integer return value with 0 for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$computeLayout$2 → KILLED

188

1.1
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
Replaced integer subtraction with addition → KILLED

2.2
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_uses_the_given_xSpacing_and_ySpacing_instead_of_the_defaults()]
removed negation → KILLED

3.3
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_uses_the_given_xSpacing_and_ySpacing_instead_of_the_defaults()]
Replaced integer multiplication with division → KILLED

189

1.1
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
changed conditional boundary → KILLED

2.2
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
negated conditional → KILLED

190

1.1
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_orders_concepts_within_a_level_by_prior_x_then_by_id_to_break_ties()]
Replaced double multiplication with division → KILLED

2.2
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_orders_concepts_within_a_level_by_prior_x_then_by_id_to_break_ties()]
Replaced double division with multiplication → KILLED

3.3
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_orders_concepts_within_a_level_by_prior_x_then_by_id_to_break_ties()]
Replaced double subtraction with addition → KILLED

4.4
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
Replaced integer subtraction with addition → KILLED

194

1.1
Location : computeLayout
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeLayout → KILLED

207

1.1
Location : computeStronglyConnectedComponents
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
negated conditional → KILLED

208

1.1
Location : computeStronglyConnectedComponents
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
removed call to edu/ucsb/cs/scaffold/services/ConceptGraphService$TarjanState::strongConnect → KILLED

211

1.1
Location : computeStronglyConnectedComponents
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeStronglyConnectedComponents → KILLED

230

1.1
Location : strongConnect
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
Replaced integer addition with subtraction → KILLED

231

1.1
Location : strongConnect
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
removed call to java/util/Deque::push → KILLED

235

1.1
Location : strongConnect
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
negated conditional → KILLED

236

1.1
Location : strongConnect
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
removed call to edu/ucsb/cs/scaffold/services/ConceptGraphService$TarjanState::strongConnect → KILLED

238

1.1
Location : strongConnect
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
negated conditional → KILLED

243

1.1
Location : strongConnect
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_flags_edges_in_two_separate_disjoint_cycles_independently()]
negated conditional → KILLED

250

1.1
Location : strongConnect
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
negated conditional → KILLED

273

1.1
Location : lambda$computeTransitiveReductionRemovals$3
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_removes_a_direct_edge_made_redundant_by_a_longer_path()]
replaced boolean return with true for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$computeTransitiveReductionRemovals$3 → KILLED

2.2
Location : lambda$computeTransitiveReductionRemovals$3
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_removes_a_direct_edge_made_redundant_by_a_longer_path()]
negated conditional → KILLED

3.3
Location : lambda$computeTransitiveReductionRemovals$3
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_removes_a_direct_edge_made_redundant_by_a_longer_path()]
negated conditional → KILLED

274

1.1
Location : computeTransitiveReductionRemovals
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_removes_a_direct_edge_made_redundant_by_a_longer_path()]
negated conditional → KILLED

278

1.1
Location : computeTransitiveReductionRemovals
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_removes_a_direct_edge_made_redundant_by_a_longer_path()]
replaced return value with Collections.emptySet for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeTransitiveReductionRemovals → KILLED

299

1.1
Location : computeLongestPathLevels
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_uses_the_given_xSpacing_and_ySpacing_instead_of_the_defaults()]
negated conditional → KILLED

304

1.1
Location : computeLongestPathLevels
Killed by : none
negated conditional → TIMED_OUT

307

1.1
Location : computeLongestPathLevels
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_uses_the_given_xSpacing_and_ySpacing_instead_of_the_defaults()]
Replaced integer addition with subtraction → KILLED

308

1.1
Location : computeLongestPathLevels
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_removes_a_direct_edge_made_redundant_by_a_longer_path()]
negated conditional → KILLED

313

1.1
Location : computeLongestPathLevels
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_centers_a_single_concept_at_a_level_on_x_zero()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::computeLongestPathLevels → KILLED

319

1.1
Location : bfsReachable
Killed by : none
negated conditional → TIMED_OUT

322

1.1
Location : bfsReachable
Killed by : none
negated conditional → TIMED_OUT

327

1.1
Location : bfsReachable
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:reset_removes_a_direct_edge_made_redundant_by_a_longer_path()]
replaced return value with Collections.emptySet for edu/ucsb/cs/scaffold/services/ConceptGraphService::bfsReachable → KILLED

334

1.1
Location : lambda$buildAdjacency$4
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:wouldCreateCycle_is_false_when_the_target_cannot_reach_the_source()]
replaced return value with Collections.emptyList for edu/ucsb/cs/scaffold/services/ConceptGraphService::lambda$buildAdjacency$4 → KILLED

337

1.1
Location : buildAdjacency
Killed by : edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.services.ConceptGraphServiceTests]/[method:wouldCreateCycle_is_true_when_the_target_can_reach_the_source_transitively()]
replaced return value with Collections.emptyMap for edu/ucsb/cs/scaffold/services/ConceptGraphService::buildAdjacency → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0