Skip to content

Commit

Permalink
Improved architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
julian committed Dec 3, 2024
1 parent 883c4c4 commit 7e0a720
Show file tree
Hide file tree
Showing 21 changed files with 443 additions and 708 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import com.example.algorithm.Explanation.Explanation;
import com.example.algorithm.Graph.GraphResponse;
import com.example.algorithm.Graph.PathFinding.BreadthFirstSearch.BreadthFirstSearchService;
import com.example.algorithm.Graph.PathFinding.DepthFirstSearch.DepthFirstSearchService;
import com.example.algorithm.Graph.PathFinding.DijkstraAlgorithm.DijkstraService;
import com.example.algorithm.Graph.PathFinding.BreadthFirstSearchService;
import com.example.algorithm.Graph.PathFinding.DepthFirstSearchService;
import com.example.algorithm.Graph.PathFinding.DijkstraService;
import com.example.algorithm.Graph.PathFinding.PathFindingService;
import org.json.JSONException;
import org.slf4j.Logger;
Expand Down Expand Up @@ -34,12 +34,12 @@ public PathFindingController(BreadthFirstSearchService bfService, DepthFirstSear
@PostMapping(
path = "/{searchType}/step"
)
public ResponseEntity<GraphResponse> step(@PathVariable("searchType") String searchType, RequestEntity<String> graph) {
public ResponseEntity<GraphResponse[]> step(@PathVariable("searchType") String searchType, RequestEntity<String> graph) {
try {
PathFindingService service = stringToService(searchType);

logger.info("New PathFinding nextstep-request: " + graph.getBody());
GraphResponse temp = service.step(graph.getBody());
GraphResponse[] temp = service.execute(graph.getBody());
return new ResponseEntity<>(temp, HttpStatus.OK);
} catch (JSONException e) {
logger.error("PathFinding step JSON failed: " + graph);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.example.algorithm.Graph.GraphEdge;
import com.example.algorithm.Graph.GraphNode;
import com.example.algorithm.Graph.GraphResponse;
import lombok.Getter;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
Expand All @@ -12,166 +13,13 @@
import java.util.Arrays;
import static com.example.algorithm.Graph.GraphEdge.UNVISITED;

@Getter
public class AllShortestPathGraph extends Graph {
GraphNode[] vertices;
int start;
double[] oldReachingCosts;
private int start;

public AllShortestPathGraph(String graphJSON) throws JSONException {
super(graphJSON);
JSONObject graph = new JSONObject(graphJSON);
convNodesFromJSON(graph);
start = graph.getInt("start");
if (vertices[start].getWeight() > 0) {
vertices[start].setWeight(0.0);
oldReachingCosts[start] = 0.0;
}
}

private void convNodesFromJSON(JSONObject graphJSON) throws JSONException {
JSONArray vertices = graphJSON.getJSONArray("vertices");
this.vertices = new GraphNode[vertices.length()];
this.oldReachingCosts = new double[vertices.length()];

for (int i = 0; i < vertices.length(); i++) {
String vertexValue = vertices.getJSONObject(i).getString("value");
Double vertexWeight = vertices.getJSONObject(i).optDouble("weight", Double.POSITIVE_INFINITY);
this.vertices[i] = new GraphNode(vertexValue, vertexWeight);
this.oldReachingCosts[i] = vertexWeight;
}
}

public GraphResponse dijkstra() {
vertices[start].setWeight(0.0);
ArrayDeque<Integer> pathToStart = new ArrayDeque<>();
pathToStart.add(start);
ArrayDeque<Integer>[] pathsAlreadyKnown = new ArrayDeque[adjacencyMatrix.length];
Arrays.fill(pathsAlreadyKnown, null);
pathsAlreadyKnown[start] = pathToStart;
dijkstraRecursive(pathsAlreadyKnown);

return new GraphResponse(adjacencyMatrix, vertices);
}

private void dijkstraRecursive(ArrayDeque<Integer>[] pathToVertices) {
double cheapestCost = Double.POSITIVE_INFINITY;
int indexFrom = -1;
int indexTo = -1;
for (int i = 0; i < adjacencyMatrix.length; i++) {
int cheapestEdgeIndex = findCheapestEdge(i);
if (cheapestEdgeIndex >= 0 && adjacencyMatrix[i][cheapestEdgeIndex].getWeight() < cheapestCost && vertices[i].getWeight() < Double.POSITIVE_INFINITY) {
indexFrom = i;
indexTo = cheapestEdgeIndex;
cheapestCost = vertices[indexFrom].getWeight() + adjacencyMatrix[indexFrom][indexTo].getWeight();
}
}

if (indexFrom == -1) {
colorAllVisitedEdges();
return;
}

vertices[indexTo].setWeight(vertices[indexFrom].getWeight() + adjacencyMatrix[indexFrom][indexTo].getWeight());

ArrayDeque<Integer> currentUpdated = pathToVertices[indexFrom].clone();
currentUpdated.add(indexTo);
pathToVertices[indexTo] = currentUpdated;

if (adjacencyMatrix[indexFrom][indexTo].tryToVisit()) {
dijkstraRecursive(pathToVertices);
}
}

public GraphResponse bellmanFord() {
boolean finished = false;
boolean changes = true;

for (int k = 0; k < vertices.length - 1 && changes; k++) {
changes = false;
for (int i = 0; i < adjacencyMatrix.length; i++) {
for (int j = 0; j < adjacencyMatrix.length; j++) {
if (adjacencyMatrix[i][j] != null && vertices[j].getWeight() > vertices[i].getWeight() + adjacencyMatrix[i][j].getWeight()) {
if (vertices[i].getWeight() < 0 && adjacencyMatrix[i][j].getWeight() < 0) {
vertices[j].setWeight(Double.NEGATIVE_INFINITY);
} else {
vertices[j].setWeight(vertices[i].getWeight() + adjacencyMatrix[i][j].getWeight());
}
colorEddgesFromNode(i);
if (vertices[j].getWeight() < oldReachingCosts[j]) {
finished = true;
}
changes = true;
}
}
if (finished) {
return new GraphResponse(adjacencyMatrix, vertices);
}

}
}

changes = true;
resetEdgeColoring();
for (int k = 0; k < vertices.length - 1 && changes; k++) {
changes = false;
for (int i = 0; i < adjacencyMatrix.length; i++) {
for (int j = 0; j < adjacencyMatrix.length; j++) {
if (adjacencyMatrix[i][j] != null && vertices[j].getWeight() > vertices[i].getWeight() + adjacencyMatrix[i][j].getWeight()) {
vertices[j].setWeight(Double.NEGATIVE_INFINITY);
colorEddgesFromNode(i);
if (oldReachingCosts[j] > Double.NEGATIVE_INFINITY) {
finished = true;
}
changes = true;
}
}
if (finished) {
return new GraphResponse(adjacencyMatrix, vertices);
}
}
}

colorEddgesFromNode(vertices.length);
return new GraphResponse(adjacencyMatrix, vertices);
}

private void colorEddgesFromNode(int nodeIndex) {
for (int i = 0; i < adjacencyMatrix.length; i++) {
for (int j = 0; j < adjacencyMatrix.length; j++) {
if (i == nodeIndex && adjacencyMatrix[i][j] != null) adjacencyMatrix[i][j].tryToVisit();
else if (adjacencyMatrix[i][j] != null) adjacencyMatrix[i][j].tryToProcess();
}
}
}
private int findCheapestEdge(int node) {
int index = -1;
Double cheapest = Double.POSITIVE_INFINITY;
for (int i = 0; i < adjacencyMatrix.length; i++) {
if (i != node && adjacencyMatrix[node][i] != null && adjacencyMatrix[node][i].getWeight() < cheapest && vertices[i].getWeight() == Double.POSITIVE_INFINITY) {
index = i;
cheapest = adjacencyMatrix[node][i].getWeight();
}
}
return index;
}

private void resetEdgeColoring() {
for (int i = 0; i < adjacencyMatrix.length; i++) {
for (int j = 0; j < adjacencyMatrix.length; j++) {
if (adjacencyMatrix[i][j] != null) {
adjacencyMatrix[i][j].setMarking(UNVISITED);
}
}
}
}

private void colorAllVisitedEdges() {
for (com.example.algorithm.Graph.GraphEdge[] matrix : adjacencyMatrix) {
for (int j = 0; j < adjacencyMatrix.length; j++) {
if (matrix[j] != null && matrix[j].getMarking() == GraphEdge.VISITED) {
matrix[j].finish();
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class BellmanFordService extends AllShortestPathService {
@Override
public GraphResponse step(String graphString) throws JSONException {
AllShortestPathGraph graph = new AllShortestPathGraph(graphString);
return graph.bellmanFord();
return new GraphResponse(graph);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class AllShortestPathDijkstraService extends AllShortestPathService {
@Override
public GraphResponse step(String graphString) throws JSONException {
AllShortestPathGraph graph = new AllShortestPathGraph(graphString);
return graph.dijkstra();
return new GraphResponse(graph);
}

@Override
Expand Down
55 changes: 41 additions & 14 deletions algorithm/src/main/java/com/example/algorithm/Graph/Graph.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,58 @@
package com.example.algorithm.Graph;

import lombok.Getter;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

@Getter
public class Graph {
protected GraphEdge[][] adjacencyMatrix;
protected GraphNode[] vertexList;

public Graph(String graphJSON) throws JSONException {
JSONObject graph = new JSONObject(graphJSON);
convJSONtoAdjacencyMatrix(graph.getJSONArray("edges"));
constructFromJSONVertexList(graph.getJSONArray("vertices"));
constructFromJSONEdgeList(graph.getJSONArray("edges"));
}

private void constructFromJSONVertexList(JSONArray jsonVertexList) throws JSONException {
vertexList = new GraphNode[jsonVertexList.length()];

for (int i = 0; i < jsonVertexList.length(); i++) {
JSONObject vertex = jsonVertexList.getJSONObject(i);

vertexList[i] = new GraphNode(vertex);
}
}

private void constructFromJSONEdgeList(JSONArray jsonEdgeList) throws JSONException {
adjacencyMatrix = new GraphEdge[vertexList.length][vertexList.length];

for (int i=0; i < jsonEdgeList.length(); i++) {
JSONObject edge = jsonEdgeList.getJSONObject(i);
int from = getVertexId(edge.getString("from"));
int to = getVertexId(edge.getString("to"));
String id = edge.getString("id");

Double weight = null;
if (edge.has("weight") && !edge.isNull("weight")) {
weight = edge.getDouble("weight");
}

adjacencyMatrix[from][to] = new GraphEdge(GraphEdge.UNVISITED, weight, id);
}
}

private void convJSONtoAdjacencyMatrix(JSONArray adjMatrixJSON) throws JSONException {
adjacencyMatrix = new GraphEdge[adjMatrixJSON.length()][adjMatrixJSON.length()];
for (int i = 0; i < adjMatrixJSON.length(); i++) {
for (int j = 0; j < adjMatrixJSON.getJSONArray(i).length(); j++) {
try {
if (adjMatrixJSON.getJSONArray(i).getJSONObject(j).getString("weight").equals("null")) {
adjacencyMatrix[i][j] = new GraphEdge(adjMatrixJSON.getJSONArray(i).getJSONObject(j).getInt("marking"));
} else {
adjacencyMatrix[i][j] = new GraphEdge(adjMatrixJSON.getJSONArray(i).getJSONObject(j).getInt("marking"), adjMatrixJSON.getJSONArray(i).getJSONObject(j).getDouble("weight"));
}
} catch (JSONException e) {
adjacencyMatrix[i][j] = null;
}
public int getVertexId(String name) {
for (int i = 0; i < vertexList.length; i++) {
if (vertexList[i].getValue().equals(name)) {
return i;
}
}

throw new IndexOutOfBoundsException("Vertex not found");
}
}
39 changes: 11 additions & 28 deletions algorithm/src/main/java/com/example/algorithm/Graph/GraphEdge.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.algorithm.Graph;

import lombok.Getter;
import lombok.Setter;

public class GraphEdge {
Expand All @@ -8,45 +9,27 @@ public class GraphEdge {
public static final int ON_FINAL_PATH = 2;
public static final int PROCESSED = 3;

@Setter
@Getter
private int marking;
@Getter
private Double weight;
@Getter
private String id;

public GraphEdge(int marking) {
public GraphEdge(int marking, Double weight, String id) {
this.marking = marking;
this.weight = null;
this.weight = weight;
}

public GraphEdge(int marking, double weight) {
this.marking = marking;
this.weight = Double.valueOf(weight);
public void visit() {
marking = VISITED;
}

public boolean tryToVisit() {
if (marking != VISITED) {
marking = VISITED;
return false;
} else {
return true;
}
public void process() {
marking = PROCESSED;
}

public boolean tryToProcess() {
if (marking == VISITED) {
marking = PROCESSED;
return true;
}
return false;
}
public void finish() {
marking = ON_FINAL_PATH;
}

public int getMarking() {
return marking;
}

public Double getWeight() {
return weight;
}
}
21 changes: 18 additions & 3 deletions algorithm/src/main/java/com/example/algorithm/Graph/GraphNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import lombok.Getter;
import lombok.Setter;
import org.json.JSONException;
import org.json.JSONObject;

public class GraphNode {
@Getter
Expand All @@ -12,8 +14,21 @@ public class GraphNode {
@Setter
String value;

public GraphNode(String value, Double weight) {
this.value = value;
this.weight = weight;
@Getter
String id;

public GraphNode(JSONObject object) throws JSONException {
this.value = object.getString("value");
this.id = object.getString("id");

if (object.has("weight") && !object.isNull("weight")) {
this.weight = object.getDouble("weight");
}
}

public GraphNode(GraphNode graphNode) {
this.weight = graphNode.weight;
this.value = graphNode.value;
this.id = graphNode.id;
}
}
Loading

0 comments on commit 7e0a720

Please sign in to comment.