Compare commits
No commits in common. "d474b93c1056c3eb8cf87d57a319b5f280992581" and "d5a79e77cea8b668010a83891a03c5d64deb7e6d" have entirely different histories.
d474b93c10
...
d5a79e77ce
1
code/python/.gitignore
vendored
1
code/python/.gitignore
vendored
@ -10,7 +10,6 @@
|
|||||||
!/README.md
|
!/README.md
|
||||||
!/LICENSE
|
!/LICENSE
|
||||||
!/requirements
|
!/requirements
|
||||||
!/pyproject.toml
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# PROJECT FILES
|
# PROJECT FILES
|
||||||
|
@ -21,12 +21,12 @@ endif
|
|||||||
# Macros
|
# Macros
|
||||||
################################
|
################################
|
||||||
|
|
||||||
define create_file_if_not_exists
|
define create_folder_if_not_exists
|
||||||
@touch "$(1)";
|
@if ! [ -d "$(1)" ]; then mkdir "$(1)"; fi
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define create_folder_if_not_exists
|
define create_folder_if_not_exists
|
||||||
if ! [ -d "$(1)" ]; then mkdir "$(1)"; fi
|
@touch "$(1)";
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define delete_if_file_exists
|
define delete_if_file_exists
|
||||||
@ -63,15 +63,13 @@ all: setup run
|
|||||||
################################
|
################################
|
||||||
# TARGETS: testing
|
# TARGETS: testing
|
||||||
################################
|
################################
|
||||||
tests: unit-tests
|
unit-test: unit-tests
|
||||||
unit-tests:
|
unit-tests:
|
||||||
@# For logging purposes (since stdout is rechanneled):
|
@cd tests && \
|
||||||
@$(call delete_if_file_exists,logs/debug.log)
|
${PYTHON} -m unittest discover -v \
|
||||||
@$(call create_folder_if_not_exists,logs)
|
--start-directory "." \
|
||||||
@$(call create_file_if_not_exists,logs/debug.log)
|
--top-level-directory ".." \
|
||||||
@# for python unit tests:
|
--pattern "test_*.py"
|
||||||
@${PYTHON} -m pytest tests --cache-clear --verbose -k test_
|
|
||||||
@cat logs/debug.log
|
|
||||||
################################
|
################################
|
||||||
# AUXILIARY (INTERNAL TARGETS)
|
# AUXILIARY (INTERNAL TARGETS)
|
||||||
################################
|
################################
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
[tool.pytest.ini_options]
|
|
||||||
minversion = "7.1.1"
|
|
||||||
testpaths = [
|
|
||||||
"tests",
|
|
||||||
]
|
|
||||||
python_files = [
|
|
||||||
"**/test_*.py",
|
|
||||||
]
|
|
@ -1,6 +1,4 @@
|
|||||||
pip>=22.0.4
|
pip>=22.0.4
|
||||||
pytest>=7.1.1
|
|
||||||
pytest-lazy-fixture>=0.6.3
|
|
||||||
typing>=3.7.4.3
|
typing>=3.7.4.3
|
||||||
numpy>=1.22.3
|
numpy>=1.22.3
|
||||||
pandas>=1.4.1
|
pandas>=1.4.1
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# EXPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'log_info',
|
|
||||||
'log_debug',
|
|
||||||
'log_warn',
|
|
||||||
'log_error',
|
|
||||||
'log_fatal',
|
|
||||||
];
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# METHODS logging
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def log_info(*text: str):
|
|
||||||
'''
|
|
||||||
Prints an info message
|
|
||||||
'''
|
|
||||||
for line in text:
|
|
||||||
print("[\x1b[94;1mINFO\x1b[0m] {}".format(line));
|
|
||||||
return;
|
|
||||||
|
|
||||||
def log_debug(*text: str):
|
|
||||||
'''
|
|
||||||
Prints a debug message
|
|
||||||
'''
|
|
||||||
for line in text:
|
|
||||||
print("[\x1b[96;1mDEBUG\x1b[95;0m] {}".format(line));
|
|
||||||
return;
|
|
||||||
|
|
||||||
def log_warn(*text: str):
|
|
||||||
'''
|
|
||||||
Prints a warning message
|
|
||||||
'''
|
|
||||||
for line in text:
|
|
||||||
print("[\x1b[93;1mWARNING\x1b[0m] {}".format(line));
|
|
||||||
return;
|
|
||||||
|
|
||||||
def log_error(*text: str):
|
|
||||||
'''
|
|
||||||
Prints an error message
|
|
||||||
'''
|
|
||||||
for line in text:
|
|
||||||
print("[\x1b[91;1mERROR\x1b[0m] {}".format(line));
|
|
||||||
return;
|
|
||||||
|
|
||||||
def log_fatal(*text: str):
|
|
||||||
'''
|
|
||||||
Prints a fatal error message + crashes
|
|
||||||
'''
|
|
||||||
for line in text:
|
|
||||||
print("[\x1b[91;1mFATAL\x1b[0m] {}".format(line));
|
|
||||||
exit(1);
|
|
@ -5,9 +5,7 @@
|
|||||||
# IMPORTS
|
# IMPORTS
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
from __future__ import annotations;
|
#
|
||||||
|
|
||||||
from src.local.typing import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# EXPORTS
|
# EXPORTS
|
||||||
@ -25,34 +23,22 @@ class Graph(object):
|
|||||||
'''
|
'''
|
||||||
a data structure for graphs
|
a data structure for graphs
|
||||||
'''
|
'''
|
||||||
nodes: list[Any];
|
nodes: list[str];
|
||||||
edges: list[tuple[Any,Any]]
|
edges: list[tuple[str,str]]
|
||||||
|
|
||||||
def __init__(self, nodes: list[Any], edges: list[tuple[Any,Any]]):
|
def __init__(self, nodes: list[str], edges: list[tuple[str,str]]):
|
||||||
self.nodes = nodes;
|
self.nodes = nodes;
|
||||||
self.edges = edges;
|
self.edges = edges;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def successor(self, u: str):
|
||||||
return len(self.nodes);
|
|
||||||
|
|
||||||
def subgraph(self, nodes: list[Any]) -> Graph:
|
|
||||||
'''
|
|
||||||
@returns graph induced by subset of nodes
|
|
||||||
'''
|
|
||||||
return Graph(
|
|
||||||
nodes = [ u for u in self.nodes if u in nodes ],
|
|
||||||
edges = [ (u, v) for u, v in self.edges if u in nodes and v in nodes ],
|
|
||||||
);
|
|
||||||
|
|
||||||
def successors(self, u: str):
|
|
||||||
'''
|
'''
|
||||||
@returns
|
@returns
|
||||||
list of successor nodes
|
list of successor nodes
|
||||||
'''
|
'''
|
||||||
return [ v for (u_, v) in self.edges if u == u_ ];
|
return [ v for (u_, v) in self.edges if u == u_ ];
|
||||||
|
|
||||||
def predecessors(self, v: str):
|
def predecessor(self, v: str):
|
||||||
'''
|
'''
|
||||||
@returns
|
@returns
|
||||||
list of predecessor nodes
|
list of predecessor nodes
|
||||||
|
@ -5,17 +5,12 @@
|
|||||||
# IMPORTS
|
# IMPORTS
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
from __future__ import annotations;
|
|
||||||
from enum import Enum;
|
from enum import Enum;
|
||||||
from dataclasses import dataclass;
|
|
||||||
from dataclasses import field
|
|
||||||
from platform import node;
|
|
||||||
|
|
||||||
from src.local.typing import *;
|
from src.local.typing import *;
|
||||||
|
|
||||||
from src.core.log import *;
|
|
||||||
from src.stacks.stack import *;
|
from src.stacks.stack import *;
|
||||||
from src.graphs.graph import *;
|
from src.graphs.graph import *
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# EXPORTS
|
# EXPORTS
|
||||||
@ -38,134 +33,59 @@ class State(Enum):
|
|||||||
# Tarjan Algorithm
|
# Tarjan Algorithm
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
def tarjan_algorithm(G: Graph, debug: bool = False) -> List[Any]:
|
class NodeInformation:
|
||||||
'''
|
root: int;
|
||||||
# Tarjan Algorithm #
|
index: int;
|
||||||
Runs the Tarjan-Algorithm to compute the strongly connected components.
|
state: State;
|
||||||
'''
|
|
||||||
# initialise state - mark all nodes as UNTOUCHED:
|
|
||||||
ctx = Context(G, debug=debug);
|
|
||||||
# loop through all nodes and carry out Tarjan-Algorithm, provided node not already visitted.
|
|
||||||
for u in G.nodes:
|
|
||||||
if ctx.get_state(u) == State.UNTOUCHED:
|
|
||||||
tarjan_visit(G, u, ctx);
|
|
||||||
|
|
||||||
return ctx.components;
|
def __init__(self):
|
||||||
|
self.root = self.index = 0;
|
||||||
|
self.state = State.UNTOUCHED;
|
||||||
|
|
||||||
def tarjan_visit(G: Graph, u: Any, ctx: Context):
|
def tarjan_algorithm(G: Graph) -> List[Any]:
|
||||||
'''
|
'''
|
||||||
Recursive depth-first search algorithm to compute strongly components of a graph.
|
runs the Tarjan-Algorithm to compute the strongly connected components
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Place node on stack + initialise visit-index + component-index.
|
# sonst Stack erstellen und Bearbeitungszustand der Knoten im Speicher notieren:
|
||||||
ctx.max_index += 1;
|
S = Stack();
|
||||||
ctx.push(u);
|
index = 0;
|
||||||
ctx.set_least_index(u, ctx.max_index);
|
infos = { u: NodeInformation() for u in G.nodes };
|
||||||
ctx.set_index(u, ctx.max_index);
|
components = [];
|
||||||
ctx.set_state(u, State.PENDING);
|
|
||||||
|
|
||||||
|
def tarjan_visit(v: Any):
|
||||||
'''
|
'''
|
||||||
Compute strongly connected components of each child node.
|
recursive depth-first search algorithm to compute components
|
||||||
NOTE: Child nodes remain on stack, if and only if parent is in same component.
|
|
||||||
'''
|
'''
|
||||||
for v in G.successors(u):
|
nonlocal G, S, index, components;
|
||||||
# Visit child node only if untouched:
|
|
||||||
if ctx.get_state(v) == State.UNTOUCHED:
|
|
||||||
tarjan_visit(G, v, ctx);
|
|
||||||
ctx.set_least_index(u, min(ctx.get_least_index(u), ctx.get_least_index(v)));
|
|
||||||
# Otherwise update associated component-index of parent node, if in same component as child:
|
|
||||||
elif ctx.stack_contains(v):
|
|
||||||
ctx.set_least_index(u, min(ctx.get_least_index(u), ctx.get_index(v)));
|
|
||||||
|
|
||||||
ctx.set_state(u, State.FINISHED);
|
if not(infos[v].state == State.UNTOUCHED):
|
||||||
ctx.log_info(u);
|
return;
|
||||||
|
|
||||||
# If at least-index of component pop everything from stack up to least index and add component:
|
index += 1;
|
||||||
if ctx.get_index(u) == ctx.get_least_index(u):
|
infos[v].index = infos[v].root = index;
|
||||||
|
S.push(v);
|
||||||
|
|
||||||
|
infos[v].state = State.PENDING;
|
||||||
|
# depth first search:
|
||||||
|
for u in G.successor(v):
|
||||||
|
tarjan_visit(u);
|
||||||
|
# remains relevant for v, provided u still in Stack:
|
||||||
|
if u in S:
|
||||||
|
infos[v].root = min(infos[v].root, infos[u].root);
|
||||||
|
infos[v].state = State.FINISHED;
|
||||||
|
|
||||||
|
# if at root of component pop everything from stack up to root and add component:
|
||||||
|
if infos[v].index == infos[v].root:
|
||||||
component = [];
|
component = [];
|
||||||
while True:
|
condition_reached_root = False;
|
||||||
v = ctx.top();
|
while not condition_reached_root:
|
||||||
ctx.pop();
|
u = S.pop();
|
||||||
component.append(v);
|
component.append(u);
|
||||||
if u == v:
|
condition_reached_root = (u == v);
|
||||||
break;
|
components.append(component);
|
||||||
ctx.components.append(component);
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
for v in G.nodes:
|
||||||
# AUXILIARY context variables for algorithm
|
tarjan_visit(v);
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
return components;
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class NodeInformationDefault:
|
|
||||||
node: Any = field(default=None);
|
|
||||||
least_index: int = field(default=0);
|
|
||||||
index: int = field(default=0);
|
|
||||||
state: State = field(default=State.UNTOUCHED, repr=False);
|
|
||||||
|
|
||||||
class NodeInformation(NodeInformationDefault):
|
|
||||||
def __init__(self, u: Any):
|
|
||||||
super().__init__();
|
|
||||||
self.node = u;
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ContextDefault:
|
|
||||||
max_index: int = field(default=0);
|
|
||||||
debug: bool = field(default=False);
|
|
||||||
stack: Stack = field(default_factory=lambda: Stack());
|
|
||||||
components: list[list[Any]] = field(default_factory=lambda: []);
|
|
||||||
infos: dict[Any, NodeInformation] = field(default_factory=lambda: dict());
|
|
||||||
|
|
||||||
class Context(ContextDefault):
|
|
||||||
def __init__(self, G: Graph, debug: bool):
|
|
||||||
super().__init__();
|
|
||||||
self.debug = debug;
|
|
||||||
self.infos = { u: NodeInformation(u) for u in G.nodes };
|
|
||||||
|
|
||||||
def push(self, u: Any):
|
|
||||||
self.stack.push(u);
|
|
||||||
|
|
||||||
def top(self) -> Any:
|
|
||||||
return self.stack.top();
|
|
||||||
|
|
||||||
def pop(self) -> Any:
|
|
||||||
return self.stack.pop();
|
|
||||||
|
|
||||||
def update_infos(self, u: Any, info: NodeInformation):
|
|
||||||
self.infos[u] = info;
|
|
||||||
|
|
||||||
def set_state(self, u: Any, state: State):
|
|
||||||
info = self.infos[u];
|
|
||||||
info.state = state;
|
|
||||||
self.update_infos(u, info);
|
|
||||||
|
|
||||||
def set_least_index(self, u: Any, least_index: int):
|
|
||||||
info = self.infos[u];
|
|
||||||
info.least_index = least_index;
|
|
||||||
self.update_infos(u, info);
|
|
||||||
|
|
||||||
def set_index(self, u: Any, index: int):
|
|
||||||
info = self.infos[u];
|
|
||||||
info.index = index;
|
|
||||||
self.update_infos(u, info);
|
|
||||||
|
|
||||||
def stack_contains(self, u: Any) -> bool:
|
|
||||||
return self.stack.contains(u);
|
|
||||||
|
|
||||||
def get_info(self, u: Any) -> NodeInformation:
|
|
||||||
return self.infos[u];
|
|
||||||
|
|
||||||
def get_state(self, u: Any) -> State:
|
|
||||||
return self.get_info(u).state;
|
|
||||||
|
|
||||||
def get_least_index(self, u: Any) -> int:
|
|
||||||
return self.get_info(u).least_index;
|
|
||||||
|
|
||||||
def get_index(self, u: Any) -> int:
|
|
||||||
return self.get_info(u).index;
|
|
||||||
|
|
||||||
def log_info(self, u: Any):
|
|
||||||
if not self.debug:
|
|
||||||
return;
|
|
||||||
info = self.get_info(u);
|
|
||||||
log_debug(info);
|
|
||||||
|
@ -11,7 +11,6 @@ import sys;
|
|||||||
os.chdir(os.path.join(os.path.dirname(__file__), '..'));
|
os.chdir(os.path.join(os.path.dirname(__file__), '..'));
|
||||||
sys.path.insert(0, os.getcwd());
|
sys.path.insert(0, os.getcwd());
|
||||||
|
|
||||||
from src.core.log import *;
|
|
||||||
from src.graphs.graph import *;
|
from src.graphs.graph import *;
|
||||||
from src.graphs.tarjan import *;
|
from src.graphs.tarjan import *;
|
||||||
|
|
||||||
@ -27,27 +26,14 @@ from src.graphs.tarjan import *;
|
|||||||
|
|
||||||
def enter():
|
def enter():
|
||||||
## Beispiel aus Seminarblatt 1
|
## Beispiel aus Seminarblatt 1
|
||||||
nodes = [1,2,3,4,5,6,7,8];
|
## TODO -> schieben in config datei
|
||||||
edges = [
|
G = Graph(
|
||||||
(1,2),
|
nodes=[1,2,3,4,5,6,7],
|
||||||
(1,3),
|
edges=[(1,2), (1,3), (2,3), (3,4), (4,5), (5,2), (5,6), (5,7), (6,7)],
|
||||||
(2,4),
|
);
|
||||||
(2,5),
|
components = tarjan_algorithm(G);
|
||||||
(3,5),
|
|
||||||
(3,6),
|
|
||||||
(3,8),
|
|
||||||
(4,5),
|
|
||||||
(4,7),
|
|
||||||
(5,1),
|
|
||||||
(5,8),
|
|
||||||
(6,8),
|
|
||||||
(7,8),
|
|
||||||
(8,6),
|
|
||||||
];
|
|
||||||
G = Graph(nodes=nodes, edges=edges);
|
|
||||||
components = tarjan_algorithm(G, debug=True);
|
|
||||||
for component in components:
|
for component in components:
|
||||||
log_debug(component);
|
print(component);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -23,10 +23,10 @@ class Stack:
|
|||||||
'''
|
'''
|
||||||
A data structure for stacks
|
A data structure for stacks
|
||||||
'''
|
'''
|
||||||
elements: list[Any];
|
values: list[Any];
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.elements = [];
|
self.values = [];
|
||||||
return;
|
return;
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
@ -34,25 +34,16 @@ class Stack:
|
|||||||
@returns
|
@returns
|
||||||
number of elements in stack
|
number of elements in stack
|
||||||
'''
|
'''
|
||||||
return len(self.elements);
|
return len(self.values);
|
||||||
|
|
||||||
def __contains__(self, value: Any) -> bool:
|
def __contains__(self, value: Any) -> bool:
|
||||||
return value in self.elements;
|
return value in self.values;
|
||||||
|
|
||||||
def push(self, value: Any):
|
def push(self, value: Any):
|
||||||
'''
|
'''
|
||||||
add element to stack
|
add element to stack
|
||||||
'''
|
'''
|
||||||
self.elements.append(value);
|
self.values.append(value);
|
||||||
|
|
||||||
def top(self) -> Any:
|
|
||||||
'''
|
|
||||||
@returns
|
|
||||||
top element from stack without removal
|
|
||||||
'''
|
|
||||||
if len(self.elements) == 0:
|
|
||||||
raise Exception('Stack is empty!');
|
|
||||||
return self.elements[-1];
|
|
||||||
|
|
||||||
def pop(self) -> Any:
|
def pop(self) -> Any:
|
||||||
'''
|
'''
|
||||||
@ -60,11 +51,14 @@ class Stack:
|
|||||||
top element from stack and removes it
|
top element from stack and removes it
|
||||||
'''
|
'''
|
||||||
value = self.top();
|
value = self.top();
|
||||||
self.elements = self.elements[:-1];
|
self.values = self.values[:-1];
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
def contains(self, element: Any) -> bool:
|
def top(self) -> Any:
|
||||||
'''
|
'''
|
||||||
checks if element in stack:
|
@returns
|
||||||
|
top element from stack without removal
|
||||||
'''
|
'''
|
||||||
return element in self.elements;
|
if len(self.values) == 0:
|
||||||
|
raise Exception('Stack is empty!');
|
||||||
|
return self.values[-1];
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from unittest import TestCase;
|
|
||||||
from pytest import mark;
|
|
||||||
from pytest import fixture;
|
|
||||||
|
|
||||||
from tests.core.log import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# CONSTANTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# FIXTURES
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@fixture(scope='module')
|
|
||||||
def test():
|
|
||||||
return TestCase();
|
|
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# METHODS - logging
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def log(*lines: str):
|
|
||||||
with open('logs/debug.log', 'a') as fp:
|
|
||||||
for line in lines:
|
|
||||||
print(line, end='\n', file=fp);
|
|
@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from pytest import mark;
|
|
||||||
from pytest import fixture;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# CONSTANTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# FIXTURES
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from pytest import mark;
|
|
||||||
from pytest import fixture;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# FIXTURES
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Test logging
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from unittest import TestCase;
|
|
||||||
from pytest import mark;
|
|
||||||
from pytest import fixture;
|
|
||||||
|
|
||||||
from tests.core.log import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# CONSTANTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# FIXTURES
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
@ -1,60 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from contextlib import nullcontext as does_not_raise
|
|
||||||
from collections import Counter;
|
|
||||||
from pytest import mark;
|
|
||||||
from pytest import fixture;
|
|
||||||
from unittest import TestCase;
|
|
||||||
|
|
||||||
from src.graphs.graph import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# FIXTURES
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@fixture(scope='module')
|
|
||||||
def graph1() -> Graph:
|
|
||||||
return Graph(
|
|
||||||
nodes=[1,2,3,4,5,6,7,8],
|
|
||||||
edges=[(1,2), (1, 3), (2,3), (3,4), (4, 5), (5, 6), (6, 2), (6, 7), (6, 8)],
|
|
||||||
);
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Test Graph
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@mark.usefixtures('test')
|
|
||||||
def test_graph_creation(test: TestCase):
|
|
||||||
with does_not_raise():
|
|
||||||
G = Graph(
|
|
||||||
[5, 7, 8],
|
|
||||||
[(5,7), (7, 8)]
|
|
||||||
);
|
|
||||||
test.assertEqual(len(G), 3);
|
|
||||||
G = Graph(
|
|
||||||
["5", "7", "8", "10"],
|
|
||||||
[("5", "7"), ("7", "8")]
|
|
||||||
);
|
|
||||||
test.assertEqual(len(G), 4);
|
|
||||||
G = Graph(nodes=[], edges=[]);
|
|
||||||
test.assertEqual(len(G), 0);
|
|
||||||
|
|
||||||
@mark.usefixtures('test', 'graph1')
|
|
||||||
def test_graph_subgraph(test: TestCase, graph1: Graph):
|
|
||||||
sub_gph = graph1.subgraph([2,4,5,6,8]);
|
|
||||||
test.assertCountEqual(sub_gph.edges, [(6,2), (4,5), (5,6), (6,8)]);
|
|
||||||
|
|
||||||
@mark.usefixtures('test', 'graph1')
|
|
||||||
def test_graph_successors_and_predecessors(test: TestCase, graph1: Graph):
|
|
||||||
test.assertCountEqual(graph1.successors(1), [2, 3]);
|
|
||||||
test.assertEqual(len(graph1.successors(8)), 0);
|
|
||||||
test.assertCountEqual(graph1.successors(6), [2, 7, 8]);
|
|
||||||
|
|
||||||
test.assertEqual(len(graph1.predecessors(1)), 0);
|
|
||||||
test.assertCountEqual(graph1.predecessors(8), [6]);
|
|
||||||
test.assertCountEqual(graph1.predecessors(6), [5]);
|
|
@ -1,119 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from contextlib import nullcontext as does_not_raise
|
|
||||||
from collections import Counter;
|
|
||||||
from pytest import mark;
|
|
||||||
from pytest import fixture;
|
|
||||||
from pytest import lazy_fixture;
|
|
||||||
from unittest import TestCase;
|
|
||||||
|
|
||||||
from src.local.typing import *;
|
|
||||||
from src.graphs.graph import *;
|
|
||||||
from src.graphs.tarjan import *;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# FIXTURES
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# TESTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
# @mark.usefixtures('my_data', 'first_entry', 'second_entry')
|
|
||||||
# def test_format_data_for_display(my_data):
|
|
||||||
# assert format_data_for_display(my_data) == [
|
|
||||||
# "Alfonsa Ruiz: Senior Software Engineer",
|
|
||||||
# "Sayid Khan: Project Manager",
|
|
||||||
# ];
|
|
||||||
|
|
||||||
# @mark.usefixtures('my_data', 'first_entry', 'second_entry')
|
|
||||||
# def test_something(my_data):
|
|
||||||
# assert 1+1 == 2;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Fixtures
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@fixture(scope='module')
|
|
||||||
def graph1() -> Graph:
|
|
||||||
return Graph(
|
|
||||||
nodes=[1,2,3,4],
|
|
||||||
edges=[(1,2), (2,4), (4,2)],
|
|
||||||
);
|
|
||||||
|
|
||||||
@fixture(scope='module')
|
|
||||||
def graph2() -> Graph:
|
|
||||||
return Graph(
|
|
||||||
nodes=[1,2,3,4,5,6,7],
|
|
||||||
edges=[(1,2), (1,3), (2,3), (3,4), (4,5), (5,2), (5,6), (5,7), (6,7)],
|
|
||||||
);
|
|
||||||
|
|
||||||
@fixture(scope='module')
|
|
||||||
def graph3() -> Graph:
|
|
||||||
return Graph(
|
|
||||||
nodes=[1,2,3,4,5,6,7,8],
|
|
||||||
edges=[
|
|
||||||
(1,2),
|
|
||||||
(1,3),
|
|
||||||
(2,4),
|
|
||||||
(2,5),
|
|
||||||
(3,5),
|
|
||||||
(3,6),
|
|
||||||
(3,8),
|
|
||||||
(4,5),
|
|
||||||
(4,7),
|
|
||||||
(5,1),
|
|
||||||
(5,8),
|
|
||||||
(6,8),
|
|
||||||
(7,8),
|
|
||||||
(8,6),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Test Tarjan-Algorithm
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@mark.parametrize(
|
|
||||||
('G', 'expected'),
|
|
||||||
[
|
|
||||||
(lazy_fixture('graph1'), [[1], [3], [2,4]]),
|
|
||||||
(lazy_fixture('graph2'), [[1], [6], [7], [2,3,4,5]]),
|
|
||||||
(lazy_fixture('graph3'), [[1,2,3,4,5], [7], [6,8]]),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@mark.usefixtures('test')
|
|
||||||
def test_tarjan(test, G, expected):
|
|
||||||
components = tarjan_algorithm(G, False);
|
|
||||||
assert_components_eq(test, components, expected);
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# AUXILIARY METHODS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def assert_components_eq(test: TestCase, components1: list[list[Any]], components2: list[list[Any]]):
|
|
||||||
result = check_components_eq(test, components1, components2);
|
|
||||||
test.assertTrue(result);
|
|
||||||
|
|
||||||
def check_components_eq(test: TestCase, components1: list[list[Any]], components2: list[list[Any]]) -> bool:
|
|
||||||
if len(components1) != len(components2):
|
|
||||||
return False;
|
|
||||||
for component1 in components1:
|
|
||||||
found = False;
|
|
||||||
for component2 in components2:
|
|
||||||
try:
|
|
||||||
test.assertCountEqual(component1, component2);
|
|
||||||
found = True;
|
|
||||||
break;
|
|
||||||
except:
|
|
||||||
continue;
|
|
||||||
if not found:
|
|
||||||
return False;
|
|
||||||
return True;
|
|
@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from pytest import mark;
|
|
||||||
from pytest import fixture;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# FIXTURES
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
#
|
|
@ -1,63 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# IMPORTS
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
from contextlib import nullcontext as does_not_raise
|
|
||||||
import pytest;
|
|
||||||
from pytest import mark;
|
|
||||||
from unittest import TestCase;
|
|
||||||
|
|
||||||
from src.stacks.stack import Stack;
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Fixtures
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
def format_data_for_display(data):
|
|
||||||
return [ '{given_name} {family_name}: {title}'.format(**row) for row in data ];
|
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# Test stack
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
@mark.usefixtures('test')
|
|
||||||
def test_stack_initialisation(test: TestCase):
|
|
||||||
stack = Stack();
|
|
||||||
test.assertEqual(len(stack), 0);
|
|
||||||
|
|
||||||
@mark.usefixtures('test')
|
|
||||||
def test_stack_push(test: TestCase):
|
|
||||||
stack = Stack();
|
|
||||||
test.assertEqual(len(stack), 0);
|
|
||||||
stack.push('hallo');
|
|
||||||
test.assertEqual(len(stack), 1);
|
|
||||||
stack.push('welt');
|
|
||||||
test.assertEqual(len(stack), 2);
|
|
||||||
stack.push('!');
|
|
||||||
test.assertEqual(len(stack), 3);
|
|
||||||
|
|
||||||
@mark.usefixtures('test')
|
|
||||||
def test_stack_no_error(test: TestCase):
|
|
||||||
stack = Stack();
|
|
||||||
stack.push('hallo');
|
|
||||||
stack.push('welt');
|
|
||||||
stack.push('!');
|
|
||||||
with does_not_raise():
|
|
||||||
stack.pop();
|
|
||||||
stack.pop();
|
|
||||||
stack.pop();
|
|
||||||
|
|
||||||
@mark.usefixtures('test')
|
|
||||||
def test_stack_error_po(test: TestCase):
|
|
||||||
stack = Stack();
|
|
||||||
stack.push('hallo');
|
|
||||||
stack.push('welt');
|
|
||||||
stack.push('!');
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
stack.pop();
|
|
||||||
stack.pop();
|
|
||||||
stack.pop();
|
|
||||||
stack.pop();
|
|
@ -19,12 +19,12 @@ endif
|
|||||||
# Macros
|
# Macros
|
||||||
################################
|
################################
|
||||||
|
|
||||||
define create_file_if_not_exists
|
define create_folder_if_not_exists
|
||||||
@touch "$(1)";
|
@if ! [ -d "$(1)" ]; then mkdir "$(1)"; fi
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define create_folder_if_not_exists
|
define create_folder_if_not_exists
|
||||||
@if ! [ -d "$(1)" ]; then mkdir "$(1)"; fi
|
@touch "$(1)";
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define delete_if_file_exists
|
define delete_if_file_exists
|
||||||
|
@ -48,7 +48,7 @@ fn tarjan_visit<T>(gph: &Graph<T>, &u: &T, ctx: &mut Context<T>)
|
|||||||
{
|
{
|
||||||
// Place node on stack + initialise visit-index + component-index.
|
// Place node on stack + initialise visit-index + component-index.
|
||||||
ctx.max_index += 1;
|
ctx.max_index += 1;
|
||||||
ctx.stack_push(&u);
|
ctx.push(&u);
|
||||||
ctx.set_root(&u, ctx.max_index);
|
ctx.set_root(&u, ctx.max_index);
|
||||||
ctx.set_index(&u, ctx.max_index);
|
ctx.set_index(&u, ctx.max_index);
|
||||||
ctx.set_state(&u, State::PENDING);
|
ctx.set_state(&u, State::PENDING);
|
||||||
@ -63,19 +63,18 @@ fn tarjan_visit<T>(gph: &Graph<T>, &u: &T, ctx: &mut Context<T>)
|
|||||||
tarjan_visit(gph, &v, ctx);
|
tarjan_visit(gph, &v, ctx);
|
||||||
}
|
}
|
||||||
// Update associated component-index of parent node, if in same component as child:
|
// Update associated component-index of parent node, if in same component as child:
|
||||||
if ctx.stack_contains(&v) {
|
if ctx.stack.contains(&v) {
|
||||||
ctx.set_root(&u, value_min!(ctx.get_root(&u), ctx.get_root(&v)));
|
ctx.set_root(&u, value_min!(ctx.get_root(&u), ctx.get_root(&v)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.set_state(&u, State::FINISHED);
|
ctx.set_state(&u, State::FINISHED);
|
||||||
ctx.log_info(&u);
|
ctx.log_info(&u);
|
||||||
|
|
||||||
// If at root of component pop everything from stack up to root and add component:
|
|
||||||
if ctx.get_index(&u) == ctx.get_root(&u) {
|
if ctx.get_index(&u) == ctx.get_root(&u) {
|
||||||
let mut component: Vec<T> = Vec::new();
|
let mut component: Vec<T> = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let v = ctx.stack_top();
|
let v = ctx.top();
|
||||||
ctx.stack_pop();
|
ctx.pop();
|
||||||
component.push(v.clone());
|
component.push(v.clone());
|
||||||
if u == v { break; }
|
if u == v { break; }
|
||||||
}
|
}
|
||||||
@ -120,15 +119,15 @@ impl<T> Context<T>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stack_push(self: &mut Self, u: &T) {
|
fn push(self: &mut Self, u: &T) {
|
||||||
self.stack.push(u.clone());
|
self.stack.push(u.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stack_top(self: &mut Self) -> T {
|
fn top(self: &mut Self) -> T {
|
||||||
return self.stack.top();
|
return self.stack.top();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stack_pop(self: &mut Self) -> T {
|
fn pop(self: &mut Self) -> T {
|
||||||
return self.stack.pop();
|
return self.stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,10 +153,6 @@ impl<T> Context<T>
|
|||||||
self.update_infos(u, info);
|
self.update_infos(u, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stack_contains(self: &Self, u: &T) -> bool {
|
|
||||||
return self.stack.contains(u);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_info(self: &Self, u: &T) -> NodeInformation<T> {
|
fn get_info(self: &Self, u: &T) -> NodeInformation<T> {
|
||||||
return *self.infos.get(u).unwrap();
|
return *self.infos.get(u).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ fn fixture_graph3() -> graph::Graph<i32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Test Tarjan-Algorithm
|
// Test Graph
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
@ -7,6 +7,10 @@ extern crate closure;
|
|||||||
|
|
||||||
use ads2::stacks::stack;
|
use ads2::stacks::stack;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Test regex
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Test stack
|
// Test stack
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user