aboutsummaryrefslogtreecommitdiff
path: root/src/dk.biks.bachelorizer/dk/biks/bachelorizer/Combi.java
blob: 8bbe5efa402fe455671e663a6cc49c839dbbd185 (plain)
  1. // SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk>
  2. // SPDX-License-Identifier: GPL-3.0-or-later
  3. package dk.biks.bachelorizer;
  4. import java.util.ArrayList;
  5. import java.util.Collection;
  6. import java.util.Collections;
  7. import java.util.HashSet;
  8. import java.util.Iterator;
  9. import java.util.LinkedList;
  10. import java.util.List;
  11. import java.util.Set;
  12. import java.util.stream.Collectors;
  13. import com.example.portfolio3.AdjListGraph;
  14. import com.example.portfolio3.Graph;
  15. import com.example.portfolio3.GraphAlgorithms;
  16. import com.example.portfolio3.MatrixGraph;
  17. import com.example.portfolio3.Vertex;
  18. /// Combi - static sample dataset of course combinations
  19. ///
  20. /// Slurps and parses data from upstream-provided comma-separated file.
  21. ///
  22. /// @version 0.0.1
  23. /// @see <https://moodle.ruc.dk/mod/assign/view.php?id=523186>
  24. public final class Combi {
  25. /// data about combinations as a Graph
  26. private Graph g;
  27. /// Default constructor
  28. ///
  29. /// @param path path to data file
  30. private Combi(final String path) {
  31. // read into temporary graph to resolve vertice count
  32. //
  33. // use Adjacency List Representation:
  34. // * cheap to bootstrap (done once)
  35. // * simple to count vertices (done once): O(1)
  36. Graph _g = new AdjListGraph();
  37. GraphAlgorithms.readGraph(_g, path);
  38. Integer _vertice_count = _g.vertices().size();
  39. // read into final graph
  40. //
  41. // use Adjacency Matrix Representation:
  42. // * expensive to bootstrap (done once)
  43. // * simple to add edges but needs vertice count
  44. // * simple to get edges (done repeatedly): O(1)
  45. //
  46. // TODO: avoid reading and parsing file twice
  47. Graph g = new MatrixGraph(_vertice_count);
  48. GraphAlgorithms.readGraph(g, path);
  49. // ensure the graph is connected
  50. assertConnected(g);
  51. nonOverlapping(g);
  52. // GraphAlgorithms.printGraph(g);
  53. // release temporary variables for garbage collection
  54. _g = null;
  55. _vertice_count = null;
  56. }
  57. /// JVM entry point
  58. ///
  59. /// @param args command-line arguments
  60. public static void main(final String[] args) {
  61. // first argument, if provided, is the data file path;
  62. // else use upstream named file in current directory.
  63. String path = (args.length > 0)
  64. ? args[0]
  65. : "combi.txt";
  66. Combi combi = new Combi(path);
  67. }
  68. /// utility function to check that a graph is connected
  69. ///
  70. /// If check fails, throw an unchecked exception,
  71. /// since it occurs at runtime and is unrecoverable.
  72. ///
  73. /// Time complexity of the operation is O(n²)
  74. /// where n is the amount of vertices,
  75. /// since visitDepthFirst() recurses out-edges of all vertices.
  76. ///
  77. /// @param g Graph to inspect
  78. /// @throws IllegalArgumentException
  79. /// https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html
  80. public static void assertConnected(Graph g) {
  81. // collect all vertices in the graph
  82. Collection<Vertex> c = g.vertices();
  83. // pick a random vertice in the graph
  84. Vertex v = g.vertices().iterator().next();
  85. // collect the set of visitable vertices
  86. Set<Vertex> visited = GraphAlgorithms.visitDepthFirst(
  87. g, v);
  88. // throw exception if not all vertices were visited
  89. if (visited.size() != c.size()) {
  90. throw new IllegalArgumentException(
  91. "graph is not connected");
  92. }
  93. }
  94. /// groups of subject modules with no overlapping students
  95. ///
  96. /// @param g Graph to inspect
  97. /// @return list of non-overlapping sets
  98. public static ArrayList<Set<Vertex>> nonOverlapping(Graph g) {
  99. // get all subject modules
  100. //
  101. // TODO: optionally seed this, to enable ranking control
  102. List<Vertex> modules = new ArrayList<>(g.vertices());
  103. Collections.shuffle(modules);
  104. return nonOverlapping(g, modules);
  105. }
  106. /// groups of subject modules with no overlapping students
  107. ///
  108. /// @param g Graph to inspect
  109. /// @param vip Ordered list of subject modules to prioritize
  110. /// @return list of non-overlapping sets
  111. public static ArrayList<Set<Vertex>> nonOverlapping(Graph g, List<Vertex> vip) {
  112. ArrayList<Set<Vertex>> sets = new ArrayList<>();
  113. // track done subject modules as extendable set
  114. //
  115. // HashList used for efficient addition and lookup
  116. Set<Vertex> done = new HashSet<>();
  117. // convert module list to a set
  118. //
  119. // defined once outside loop as a slight optimization
  120. Set<Vertex> all_set = new HashSet<>(vip);
  121. // iterate over subject modules
  122. for (Vertex current : vip) {
  123. if (done.contains(current)) {
  124. continue;
  125. }
  126. // add set of current and any unconnected modules
  127. Set<Vertex> isolated = all_set.stream().filter(x -> {
  128. if (x == current)
  129. return false;
  130. Integer weight = g.getWeight(current, x);
  131. return weight == null && !done.contains(x);
  132. }).collect(Collectors.toSet());
  133. isolated.add(current);
  134. // add as set and omit from future iterations
  135. sets.add(isolated);
  136. done.addAll(isolated);
  137. }
  138. for(Set<Vertex> s: sets) {
  139. System.out.println("set: "+s.size());
  140. for (Vertex v: s) {
  141. System.out.println(" "+v.toString());
  142. }
  143. }
  144. return sets;
  145. }
  146. }