Merge remote-tracking branch 'origin/main'
# Conflicts: # .idea/workspace.xml # graph/UndirectedGraph.java # out/production/ProjektGraph/OurApplication/OurApplication.class # out/production/ProjektGraph/graph/UndirectedGraph.class
This commit is contained in:
commit
3957fbba51
35
.idea/workspace.xml
generated
35
.idea/workspace.xml
generated
@ -7,22 +7,13 @@
|
||||
<list default="true" id="70f8ea87-9ffc-471a-8059-ebbfc323adcc" name="Changes" comment="LegendArea gestaltet">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/OurApplication/OurApplication.java" beforeDir="false" afterPath="$PROJECT_DIR$/OurApplication/OurApplication.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/Edge.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/Edge.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/EdgeMarking.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/EdgeMarking.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/Graph.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/Graph.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/MarkedEdge.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/MarkedEdge.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/MarkedVertex.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/MarkedVertex.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/Marking.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/Marking.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/NameDoesNotExistException.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/NameDoesNotExistException.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/DirectedGraph.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/DirectedGraph.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/UndirectedGraph.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/UndirectedGraph.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/Vertex.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/Vertex.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/graph/VertexMarking.java" beforeDir="false" afterPath="$PROJECT_DIR$/graph/VertexMarking.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/OurApplication/OurAlgorithm.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/OurApplication/OurAlgorithm.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/OurApplication/OurApplication.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/OurApplication/OurApplication.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/OurApplication/OurParameterArea.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/OurApplication/OurParameterArea.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/graph/DirectedGraph.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/graph/DirectedGraph.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/graph/ExampleGraphs.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/graph/ExampleGraphs.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/graph/Graph.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/graph/Graph.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/graph/MarkedEdge.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/graph/MarkedEdge.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/graph/MarkedVertex.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/graph/MarkedVertex.class" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/out/production/ProjektGraph/graph/UndirectedGraph.class" beforeDir="false" afterPath="$PROJECT_DIR$/out/production/ProjektGraph/graph/UndirectedGraph.class" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
@ -48,17 +39,17 @@
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"Application.Display.executor": "Run",
|
||||
"Application.OurApplication.executor": "Run",
|
||||
"Application.OurLegendArea.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"git-widget-placeholder": "main",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "C:/Git/ProjektGraphMain"
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"Application.Display.executor": "Run",
|
||||
"Application.OurApplication.executor": "Run",
|
||||
"Application.OurLegendArea.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"git-widget-placeholder": "main",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "C:/Git/ProjektGraphMain"
|
||||
}
|
||||
}</component>
|
||||
}]]></component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="C:\Git\ProjektGraph" />
|
||||
|
@ -60,7 +60,7 @@ public class OurApplication {
|
||||
OurTextArea textArea = new OurTextArea(logList);
|
||||
OurAlgorithm algorithm = new OurAlgorithm(parameterArea, methodButtons);
|
||||
OurLegendArea legendArea = new OurLegendArea();
|
||||
OurHybridWindow<OurDrawArea, OurTextArea, OurParameterArea, OurAlgorithm, OurLogElement, OurLegendArea, OurMethodButtons> applet = new OurHybridWindow<OurDrawArea, OurTextArea, OurParameterArea, OurAlgorithm, OurLogElement, OurLegendArea, OurMethodButtons>(drawArea, textArea, parameterArea, algorithm, logList, legendArea, methodButtons);
|
||||
OurHybridWindow<OurDrawArea, OurTextArea, OurParameterArea, OurAlgorithm, OurLogElement, OurLegendArea, OurMethodButtons> applet = new OurHybridWindow<>(drawArea, textArea, parameterArea, algorithm, logList, legendArea, methodButtons);
|
||||
|
||||
|
||||
|
||||
|
@ -433,6 +433,9 @@ public class DirectedGraph<T extends VertexMarking, U extends EdgeMarking> exten
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vorgänger aktualisieren
|
||||
predecessors.put(i, nextVertex.getElement());
|
||||
|
||||
// Aktualisiere Distanz von Start zu nächstem Knoten
|
||||
distance.put(i, dist);
|
||||
|
||||
|
@ -34,7 +34,7 @@ public class UndirectedGraph<T extends VertexMarking, U extends EdgeMarking> ext
|
||||
*/
|
||||
public UndirectedGraph() {
|
||||
super();
|
||||
this.screenGraph = new visualizationElements.Graph(new Vector<>(), new Vector<>(), false, EdgeStyle.Direct);
|
||||
this.screenGraph = new visualizationElements.Graph(new Vector<Vertex>(), new Vector<Edge>(), false, EdgeStyle.Direct);
|
||||
this.logList = new LogElementList<>();
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ public class UndirectedGraph<T extends VertexMarking, U extends EdgeMarking> ext
|
||||
*/
|
||||
public UndirectedGraph(String s) {
|
||||
super(s);
|
||||
this.screenGraph = new visualizationElements.Graph(new Vector<>(), new Vector<>(), false, EdgeStyle.Direct);
|
||||
this.screenGraph = new visualizationElements.Graph(new Vector<Vertex>(), new Vector<Edge>(), false, EdgeStyle.Direct);
|
||||
this.logList = new LogElementList<>();
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ public class UndirectedGraph<T extends VertexMarking, U extends EdgeMarking> ext
|
||||
* @return A deep copy of the visualization graph.
|
||||
*/
|
||||
public visualizationElements.Graph getScreenGraphCopy() {
|
||||
visualizationElements.Graph graphCopy = new visualizationElements.Graph(new Vector<>(), new Vector<>(), false, EdgeStyle.Direct);
|
||||
visualizationElements.Graph graphCopy = new visualizationElements.Graph(new Vector<Vertex>(), new Vector<Edge>(), false, EdgeStyle.Direct);
|
||||
Vector<visualizationElements.Vertex> copiedVertexes = new Vector<>();
|
||||
Vector<visualizationElements.Edge> copiedEdges = new Vector<>();
|
||||
for (visualizationElements.Vertex vertexCopy : this.screenGraph.getVertexes()) {
|
||||
@ -211,10 +211,130 @@ public class UndirectedGraph<T extends VertexMarking, U extends EdgeMarking> ext
|
||||
* @return The length of the shortest path between n1 and n2.
|
||||
*/
|
||||
public int getShortestPathDijkstra(MarkedVertex<T> n1, MarkedVertex<T> n2) {
|
||||
// Implementation of Dijkstra's algorithm
|
||||
// Details omitted for brevity in the comment section
|
||||
// Full implementation is provided in the actual code
|
||||
return 0; // Placeholder return value
|
||||
|
||||
// Erstellt Hashmap um Distanz von Startnoten zu jedem Knoten auf dem Graph zu tracken
|
||||
// Erstellt Hashmap um zu tracken welche Knoten schon besucht wurden
|
||||
// Erstelle Hashmap um Vorgängerknoten zu tracken
|
||||
// Initialisierung aller Distanzen auf UNENDLICH (= -1)
|
||||
// Initialisierung, dass kein Knoten besucht wurde
|
||||
// Initialisierung aller Vorgänger auf null
|
||||
HashMap<MarkedVertex<T>, Integer> distance = new HashMap<>();
|
||||
HashMap<MarkedVertex<T>, Boolean> visited = new HashMap<>();
|
||||
HashMap<MarkedVertex<T>, MarkedVertex<T>> predecessors = new HashMap<>();
|
||||
for (MarkedVertex<T> i: this.getAllVertexes()) {
|
||||
distance.put(i, -1);
|
||||
visited.put(i, false);
|
||||
predecessors.put(i, null);
|
||||
}
|
||||
|
||||
// Erstelle Schlange wo die nächsten Verbindungen drin sind
|
||||
PriorityQueue<WrapperElement<T>> queue = new PriorityQueue<>(new WrapperComparator<T>());
|
||||
|
||||
// Distanz zu Startknoten auf 0
|
||||
// Weg zu Startknoten in die Schlange aufnehmen
|
||||
distance.put(n1, 0);
|
||||
queue.add(new WrapperElement<>(n1, 0));
|
||||
|
||||
// Variable, die Distanz zwischen aktuellem Knoten und Nachfolger speichert
|
||||
int dist = 0;
|
||||
// Zähler für LogList
|
||||
int step = 0;
|
||||
// String für den Description Inhalt
|
||||
String textDescription;
|
||||
|
||||
// Färben der Start und Ziel Knoten für Visualisierung + hinzufügen zur LogList
|
||||
n1.getScreenVertex().setColor(Color.RED);
|
||||
n2.getScreenVertex().setColor(Color.RED);
|
||||
textDescription = "Startknoten: " + n1.getScreenVertex().getMarking()
|
||||
+ ", Endknoten: " + n2.getScreenVertex().getMarking();
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
// Den nächsten Knoten, der am wenigsten kostet, besuchen
|
||||
WrapperElement<T> nextVertex = queue.poll();
|
||||
|
||||
// Knoten als besucht makieren
|
||||
visited.put(nextVertex.getElement(), true);
|
||||
|
||||
|
||||
// Logging
|
||||
textDescription = "Visit " + nextVertex.getElement().getName();
|
||||
System.out.println(textDescription);
|
||||
nextVertex.getElement().getScreenVertex().setColor(Color.BLUE);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
|
||||
// Wenn Weg gefunden, brich ab
|
||||
if (nextVertex.getElement() == n2) {
|
||||
MarkedVertex<T> colorroute = n2;
|
||||
while (colorroute != null) {
|
||||
textDescription = colorroute.getName();
|
||||
System.out.println(textDescription);
|
||||
colorroute.getScreenVertex().setColor(Color.green);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
colorroute = predecessors.get(colorroute);
|
||||
}
|
||||
|
||||
|
||||
//zurücksetzten der Färbungen
|
||||
this.clearScreenGraphColor();
|
||||
return distance.get(n2);
|
||||
}
|
||||
|
||||
|
||||
// Gehe von diesem Knoten aus alle erreichbaren Knoten durch
|
||||
for (MarkedVertex<T> i: this.getNeighbours(nextVertex.getElement())) {
|
||||
|
||||
// Kante finde, die den jetzigen und nächsten Knoten verbindet
|
||||
for (MarkedEdge<U> j: this.getAllEdges()) {
|
||||
if (j.getSource() == nextVertex.getElement() && j.getDestination() == i) {
|
||||
|
||||
// Berechne Distanz zu nächstem Knoten
|
||||
EdgeWeightMarking marking = (EdgeWeightMarking) j.getMarking();
|
||||
dist = distance.get(nextVertex.getElement()) + marking.getWeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn es schon einen kürzeren Weg zum Knoten gibt, überspringen
|
||||
if ((distance.get(i) <= dist && distance.get(i) != -1) || visited.get(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Vorgänger aktualisieren
|
||||
predecessors.put(i, nextVertex.getElement());
|
||||
|
||||
// Aktualisiere Distanz von Start zu nächstem Knoten
|
||||
distance.put(i, dist);
|
||||
|
||||
// Logging
|
||||
textDescription = "Add " + i.getName() + " with " + dist + " weight to queue.";
|
||||
System.out.println(textDescription);
|
||||
i.getScreenVertex().setColor(Color.YELLOW);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
// Nehme nächsten Knoten in die Queue auf
|
||||
queue.add(new WrapperElement<>(i, dist));
|
||||
}
|
||||
}
|
||||
|
||||
//zurücksetzten der Färbungen
|
||||
this.clearScreenGraphColor();
|
||||
|
||||
MarkedVertex<T> colorroute = n2;
|
||||
while (colorroute != null) {
|
||||
textDescription = colorroute.getName();
|
||||
System.out.println(textDescription);
|
||||
colorroute.getScreenVertex().setColor(Color.green);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
colorroute = predecessors.get(colorroute);
|
||||
}
|
||||
|
||||
System.out.println("Done");
|
||||
// Gibt Distanz zu gefragtem Knoten zurück
|
||||
return distance.get(n2);
|
||||
}
|
||||
|
||||
|
||||
@ -225,12 +345,138 @@ public class UndirectedGraph<T extends VertexMarking, U extends EdgeMarking> ext
|
||||
* @param n2 The ending vertex of the shortest path.
|
||||
* @return The length of the shortest path between n1 and n2.
|
||||
*/
|
||||
|
||||
public int getShortestPathAStar(MarkedVertex<T> n1, MarkedVertex<T> n2) {
|
||||
// Implementation of A* algorithm
|
||||
// Details omitted for brevity in the comment section
|
||||
// Full implementation is provided in the actual code
|
||||
return 0; // Placeholder return value
|
||||
|
||||
// Erstellt Hashmap um Distanz von Startnoten zu jedem Knoten auf dem Graph zu tracken
|
||||
// Erstellt Hashmap um zu tracken welche Knoten schon besucht wurden
|
||||
// Erstelle Hashmap um Vorgängerknoten zu tracken
|
||||
// Initialisierung aller Distanzen auf UNENDLICH (= -1)
|
||||
// Initialisierung, dass kein Knoten besucht wurde
|
||||
// Initialisierung aller Vorgänger auf null
|
||||
HashMap<MarkedVertex<T>, Integer> distance = new HashMap<>();
|
||||
HashMap<MarkedVertex<T>, Boolean> visited = new HashMap<>();
|
||||
HashMap<MarkedVertex<T>, MarkedVertex<T>> predecessors = new HashMap<>();
|
||||
for (MarkedVertex<T> i: this.getAllVertexes()) {
|
||||
distance.put(i, -1);
|
||||
visited.put(i, false);
|
||||
predecessors.put(i, null);
|
||||
}
|
||||
|
||||
// Erstelle Schlange wo die nächsten Verbindungen drin sind
|
||||
PriorityQueue<WrapperElement<T>> queue = new PriorityQueue<>(new WrapperComparator<T>());
|
||||
|
||||
// Distanz zu Startknoten auf 0
|
||||
// Weg zu Startknoten in die Schlange aufnehmen
|
||||
distance.put(n1, 0);
|
||||
queue.add(new WrapperElement<>(n1, 0));
|
||||
|
||||
// Variable, die Distanz zwischen aktuellem Knoten und Nachfolger speichert
|
||||
int dist = 0;
|
||||
// Variable, die Distanz zwischen dem potenziell nächsten Knoten und dem Zielknoten speichert
|
||||
int airDist = 0;
|
||||
// Variable, die Distanz zwischen dem aktuellen Knoten bis zum Endknoten speichert
|
||||
int distToFinish = 0;
|
||||
// Zähler für LogList
|
||||
int step = 0;
|
||||
// String für den Description Inhalt
|
||||
String textDescription;
|
||||
|
||||
// Färben der Start und Ziel Knoten für Visualisierung + hinzufügen zur LogList
|
||||
n1.getScreenVertex().setColor(Color.RED);
|
||||
n2.getScreenVertex().setColor(Color.RED);
|
||||
textDescription = "Startknoten: " + n1.getScreenVertex().getMarking()
|
||||
+ ", Endknoten: " + n2.getScreenVertex().getMarking();
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
// Den nächsten Knoten, der am wenigsten kostet, besuchen
|
||||
WrapperElement<T> nextVertex = queue.poll();
|
||||
|
||||
// Knoten als besucht makieren
|
||||
visited.put(nextVertex.getElement(), true);
|
||||
|
||||
|
||||
// Logging
|
||||
textDescription = "Visit " + nextVertex.getElement().getName();
|
||||
System.out.println(textDescription);
|
||||
nextVertex.getElement().getScreenVertex().setColor(Color.BLUE);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
|
||||
// Wenn Weg gefunden, brich ab
|
||||
if (nextVertex.getElement() == n2) {
|
||||
MarkedVertex<T> colorroute = n2;
|
||||
while (colorroute != null) {
|
||||
textDescription = colorroute.getName();
|
||||
System.out.println(textDescription);
|
||||
colorroute.getScreenVertex().setColor(Color.green);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
colorroute = predecessors.get(colorroute);
|
||||
}
|
||||
|
||||
|
||||
//zurücksetzten der Färbungen
|
||||
this.clearScreenGraphColor();
|
||||
return distance.get(n2);
|
||||
}
|
||||
|
||||
|
||||
// Gehe von diesem Knoten aus alle erreichbaren Knoten durch
|
||||
for (MarkedVertex<T> i: this.getNeighbours(nextVertex.getElement())) {
|
||||
|
||||
// Kante finde, die den jetzigen und nächsten Knoten verbindet
|
||||
for (MarkedEdge<U> j: this.getAllEdges()) {
|
||||
if (j.getSource() == nextVertex.getElement() && j.getDestination() == i) {
|
||||
|
||||
//Berechnung der Heuristik über die Luftdistanz des nächsten Knoten zum Zielknoten
|
||||
airDist = (int) Math.sqrt(Math.pow((i.getCords()[0] - n2.getCords()[0]), 2)
|
||||
+ Math.pow((i.getCords()[1] - n2.getCords()[1]), 2));
|
||||
|
||||
// Berechne Distanz zu nächstem Knoten
|
||||
EdgeWeightMarking marking = (EdgeWeightMarking) j.getMarking();
|
||||
dist = distance.get(nextVertex.getElement()) + marking.getWeight();
|
||||
distToFinish = distance.get(nextVertex.getElement()) + marking.getWeight() + airDist;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn es schon einen kürzeren Weg zum Knoten gibt, überspringen
|
||||
if ((distance.get(i) <= dist && distance.get(i) != -1) || visited.get(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Aktualisiere Distanz von Start zu nächstem Knoten
|
||||
distance.put(i, dist);
|
||||
|
||||
// Logging
|
||||
textDescription = "Add " + i.getName() + " with " + dist + " weight to queue.";
|
||||
System.out.println(textDescription);
|
||||
i.getScreenVertex().setColor(Color.YELLOW);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
// Nehme nächsten Knoten in die Queue auf
|
||||
queue.add(new WrapperElement<>(i, distToFinish));
|
||||
}
|
||||
}
|
||||
|
||||
//zurücksetzten der Färbungen
|
||||
this.clearScreenGraphColor();
|
||||
|
||||
MarkedVertex<T> colorroute = n2;
|
||||
while (colorroute != null) {
|
||||
textDescription = colorroute.getName();
|
||||
System.out.println(textDescription);
|
||||
colorroute.getScreenVertex().setColor(Color.green);
|
||||
this.logList.add(new OurLogElement(step, textDescription, 0, this.getScreenGraphCopy()));
|
||||
|
||||
colorroute = predecessors.get(colorroute);
|
||||
}
|
||||
|
||||
System.out.println("Done");
|
||||
// Gibt Distanz zu gefragtem Knoten zurück
|
||||
return distance.get(n2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user