Compare commits

..

No commits in common. "master" and "v0.5" have entirely different histories.
master ... v0.5

53 changed files with 744 additions and 6192 deletions

27
.gitignore vendored
View File

@ -44,30 +44,3 @@ app.*.map.json
*/android/app/debug */android/app/debug
*/android/app/profile */android/app/profile
*/android/app/release */android/app/release
*.aux
*.glo
*.idx
*.log
*.toc
*.ist
*.acn
*.acr
*.alg
*.bbl
*.blg
*.dvi
*.glg
*.gls
*.ilg
*.ind
*.lof
*.lot
*.maf
*.mtc
*.mtc1
*.out
*.synctex.gz
*/_minted-example-work/
*/tex/example-work.pdf
tex/example-work.pdf

View File

@ -13,7 +13,7 @@ void printGraphsName(List<Graphs> inp) {
} }
void main(List<String> arguments) { void main(List<String> arguments) {
/* Map<int, int> x = {2: 10}; /*Map<int, int> x = {2: 10};
var p = Dot.fromMap("Т1", x); var p = Dot.fromMap("Т1", x);
p.printD(); p.printD();
x = <int, int>{}; x = <int, int>{};
@ -31,10 +31,10 @@ void main(List<String> arguments) {
x1.printToFile("o.txt"); x1.printToFile("o.txt");
var x2 = Graphs.fromFile("o.txt"); var x2 = Graphs.fromFile("o.txt");
x2.printG(); x2.printG();
print(x2.bfsHasPath(1, 4));
print(x2.bfsPath(1, 3)); print(x2.bfsPath(1, 3));
*/
/*var x1 = Graphs("Gt", false, true); Graphs x1 = Graphs("Gt", false, true);
x1.addIsolated("T1"); x1.addIsolated("T1");
x1.addIsolated("T2"); x1.addIsolated("T2");
x1.addIsolated("T3"); x1.addIsolated("T3");
@ -42,27 +42,19 @@ void main(List<String> arguments) {
x1.addPath(1, 3, 1); x1.addPath(1, 3, 1);
x1.addPath(3, 2, 5); x1.addPath(3, 2, 5);
x1.addPath(2, 4, 10); x1.addPath(2, 4, 10);
x1.printG();*/ x1.printG();
var x1 = Graphs.fromFile("star.txt"); print(x1.bfsPath(1, 4));
for (var i in x1.getSortedPathList()) { print(x1.bfsHasPath(1, 4));
print("${i.l} ${i.d} ${i.p}"); print(x1.dfsIterative(1));
} */
x1.kruskal()!.printG();
//print(x1.getSortedPathList());
//print(x1.bfsPath(1, 4));
//print(x1.dfsIterative(1));
//print(x1.dijkstra(2));
/* var x1 = Graphs.fromFile("star.txt");
print(x1.prim());
int deistvie = 1; int deistvie = 1;
List<Graphs> graphs = <Graphs>[]; List<Graphs> graphs = <Graphs>[];
String name; String name;
String str = ""; String str = "";
Separators sep = Separators();
while (deistvie != 0) { while (deistvie != 0) {
stdout.write( stdout.write(
"1 - создать граф, 2 - удалить граф, 3 - добавить в граф вершину,\n4 - удалить вершину, 5 - добавить ребро/дугу, 6 - удалить ребро/дугу,\n7 - вывести граф на экран, 8 - вывести граф в файл, 9 - алгоритмы,\n0 - выход\nDeistvie: "); "1 - создать граф, 2 - удалить граф, 3 - добавить в граф вершину,\n4 - удалить вершину, 5 - добавить ребро/дугу, 6 - удалить ребро/дугу,\n7 - вывести граф на экран, 8 - вывести граф в файл, 0 - выход\nDeistvie: ");
deistvie = getIntLine(); deistvie = getIntLine();
switch (deistvie) { switch (deistvie) {
case 1: case 1:
@ -120,9 +112,9 @@ void main(List<String> arguments) {
List<int> inn = <int>[]; List<int> inn = <int>[];
List<int> len = <int>[]; List<int> len = <int>[];
List<String> a = str.split(Separators.space); List<String> a = str.split(sep.space);
for (var splitted in a) { for (var splitted in a) {
var dt = splitted.split(Separators.dotToLength); var dt = splitted.split(sep.dotToLength);
if (dt.length == 2) { if (dt.length == 2) {
inn.add(int.parse(dt[0])); inn.add(int.parse(dt[0]));
len.add(int.parse(dt[1])); len.add(int.parse(dt[1]));
@ -165,13 +157,13 @@ void main(List<String> arguments) {
stdout.write("Имя вершины: "); stdout.write("Имя вершины: ");
String dotName = getStrLine(); String dotName = getStrLine();
/*stdout.write( stdout.write(
"Ввод: *куда*|*вес* через пробел. Если граф не взвешенный, то *вес* можно не указывать.\nВершина cмежна с: "); "Ввод: *куда*|*вес* через пробел. Если граф не взвешенный, то *вес* можно не указывать.\nВершина cмежна с: ");
str = getStrLine(); str = getStrLine();
List<String> a = str.split(Separators.space); List<String> a = str.split(sep.space);
for (var splitted in a) { for (var splitted in a) {
var dt = splitted.split(Separators.dotToLength); var dt = splitted.split(sep.dotToLength);
if (dt.length == 2) { if (dt.length == 2) {
inn.add(int.parse(dt[0])); inn.add(int.parse(dt[0]));
len.add(int.parse(dt[1])); len.add(int.parse(dt[1]));
@ -179,12 +171,12 @@ void main(List<String> arguments) {
inn.add(int.parse(splitted)); inn.add(int.parse(splitted));
len.add(0); len.add(0);
} }
}*/ }
printGraphsName(graphs); printGraphsName(graphs);
stdout.write("Вставка в графа с номером: "); stdout.write("Вставка в графа с номером: ");
int y = getIntLine(); int y = getIntLine();
if (y >= 0 && y < graphs.length) { if (y >= 0 && y < graphs.length) {
graphs[y].addIsolated(dotName); graphs[y].addDotFromToLists(dotName, inn, len);
} else { } else {
print("Не найден граф с таким номером"); print("Не найден граф с таким номером");
} }
@ -252,7 +244,7 @@ void main(List<String> arguments) {
num = graphs[y].getDotAmount(); num = graphs[y].getDotAmount();
stdout.write( stdout.write(
"Количество вершин: $num. Введите через пробел 2 вершины, между которыми удалить ребро: "); "Количество вершин: $num. Введите через пробел 2 вершины, между которыми удалить ребро: ");
List<String> x = getStrLine().split(Separators.space); List<String> x = getStrLine().split(sep.space);
x1 = int.parse(x[0]); x1 = int.parse(x[0]);
x2 = int.parse(x[1]); x2 = int.parse(x[1]);
if (x1 >= 0 && x1 < num && x2 >= 0 && x2 < num) { if (x1 >= 0 && x1 < num && x2 >= 0 && x2 < num) {
@ -294,67 +286,6 @@ void main(List<String> arguments) {
} }
break; break;
} }
case 9:
{
print("1 - BFS, 2 - DFS, 3 - Дейкстра. Действие:");
int d = getIntLine();
printGraphsName(graphs);
int num = graphs.length;
stdout.write("Работаем в графе с номером: ");
int y = getIntLine();
graphs[y].printG();
if (y >= 0 && y < num) {
num = graphs[y].getDotAmount();
stdout.write("Вершина, из которой выходят: ");
int x1 = getIntLine();
switch (d) {
case 1:
{
stdout.write("Вершина, в которую идут: ");
int x2 = getIntLine();
if (x1 >= 0 && x1 <= num && x2 >= 0 && x2 <= num) {
print("BFS: ${graphs[y].bfsPath(x1, x2)}");
} else {
print("Не найдены введеные вершины в графе\n");
}
break;
}
case 2:
{
if (x1 >= 0 && x1 <= num) {
print("DFS: ${graphs[y].dfsIterative(x1)}");
} else {
print("Не найдена введеныая вершина в графе\n");
}
break;
}
case 3:
{
if (x1 >= 0 && x1 <= num) {
print("Dijkstra: ${graphs[y].dijkstra(x1)}");
if (!graphs[y].getUseLengthBool()) {
print("*Т.к. граф не взвешенный, то вес пути = 1.\n");
}
} else {
print("Не найдена введеная вершина в графе\n");
}
break;
}
default:
{
break;
}
}
} else {
print("Не найден граф с таким номером");
}
break;
}
case 10:
{
print(graphs[0].prim());
break;
}
case 0: case 0:
getStrLine(); getStrLine();
exit(0); exit(0);
@ -362,5 +293,5 @@ void main(List<String> arguments) {
print("Действие не распознано"); print("Действие не распознано");
break; break;
} }
}*/ }
} }

View File

@ -1,15 +1,15 @@
import 'dart:io'; import 'dart:io';
class Separators { class Separators {
static const String dotToConnections = ": "; final String dotToConnections = ": ";
static const String dotToLength = "|"; final String dotToLength = "|";
static const String space = " "; final String space = " ";
static const String hasLength = "Взвешенный"; final String hasLength = "Взвешенный\n";
static const String hasNoLength = "НеВзвешенный"; final String hasNoLength = "НеВзвешенный\n";
static const String isOriented = "Ориентированный"; final String isOriented = "Ориентированный\n";
static const String isNotOriented = "НеОриентированный"; final String isNotOriented = "НеОриентированный\n";
static const String nL = "\n"; final String nL = "\n";
static const String end = "END"; final String end = "END";
Separators(); Separators();
} }
@ -25,7 +25,6 @@ class Dot {
String getName() => _name; String getName() => _name;
bool hasConnection(int n) => _ln.containsKey(n); bool hasConnection(int n) => _ln.containsKey(n);
Map<int, int> getL() => _ln; Map<int, int> getL() => _ln;
int getLength(int x) { int getLength(int x) {
if (hasConnection(x)) { if (hasConnection(x)) {
return _ln[x]!; return _ln[x]!;
@ -54,9 +53,9 @@ class Dot {
} }
//******Constructor****** //******Constructor******
Dot([String name = "Undefined", int n = -1]) { Dot() {
_name = name; _name = "Undefined";
num = n; num = -1;
_ln = <int, int>{}; _ln = <int, int>{};
} }
Dot.fromTwoLists(String name, List<int> num0, List<int> length, Dot.fromTwoLists(String name, List<int> num0, List<int> length,
@ -89,7 +88,6 @@ class Dot {
} }
class Graphs { class Graphs {
static const int intMax = 0x7fffffffffffffff;
//Data //Data
String _name = "Undefined"; //Имя String _name = "Undefined"; //Имя
int _amount = 0; //Количество вершин int _amount = 0; //Количество вершин
@ -99,9 +97,11 @@ class Graphs {
bool _oriented = false; //Ориентированность bool _oriented = false; //Ориентированность
//*********************Add************************ //*********************Add************************
String? addDot(Dot a) { bool addDot(Dot a) {
if (getNumByName(a.getName()) != null) { if (getNumByName(a.getName()) != null) {
return ("Dot name \"${a.getName()}\" already in use. Change name or use addPath"); print(
"Dot name ${a.getName()} already in use. Change name or use addPath");
return false;
} }
_amount++; _amount++;
a.num = _amount; a.num = _amount;
@ -109,7 +109,7 @@ class Graphs {
_syncNameTable(); _syncNameTable();
checkDots(false); checkDots(false);
if (!_oriented) _fullFix(); if (!_oriented) _fullFix();
return null; return true;
} }
bool addDotFromToLists(String name, List<int> num0, List<int> length, bool addDotFromToLists(String name, List<int> num0, List<int> length,
@ -129,43 +129,37 @@ class Graphs {
return true; return true;
} }
String? addIsolated(String name) { bool addIsolated(String name) {
var res = addDot(Dot.fromTwoLists(name, [], [])); var o = addDot(Dot.fromTwoLists(name, [], []));
_syncNameTable(); _syncNameTable();
return res; return o;
} }
String? addPath(int from, int to, [int len = 0]) { bool addPath(int from, int to, [int len = 0]) {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) { if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return "Index out of range. Have dots 1..$_amount"; return false;
} }
_dots[from - 1].addPath(to, len); _dots[from - 1].addPath(to, len);
if (!_oriented) { if (!_oriented) {
_dots[to - 1].addPath(from, len); _dots[to - 1].addPath(from, len);
} }
return null; return true;
} }
//*********************Add************************ //*********************Add************************
//*********Delete********* //*********Delete*********
String? delPath(int from, int to) { bool delPath(int from, int to) {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) { if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return "Can't find specified path"; return false;
}
if (!_dots[from - 1].hasConnection(to)) {
return "Already no connection between $from and $to";
} }
_dots[from - 1].delPath(to); _dots[from - 1].delPath(to);
if (!_oriented) { if (!_oriented) {
_dots[to - 1].delPath(from); _dots[to - 1].delPath(from);
} }
return null; return true;
} }
String? delDot(int inn) { bool delDot(int inn) {
if (inn > _amount || inn < 1) {
return "Index out of range. Allowed 1..$_amount";
}
List<int> toDel = <int>[]; List<int> toDel = <int>[];
for (int i in _dots[inn - 1].getL().keys) { for (int i in _dots[inn - 1].getL().keys) {
toDel.add(i); toDel.add(i);
@ -177,13 +171,7 @@ class Graphs {
_syncNum(); _syncNum();
_syncNameTable(); _syncNameTable();
_fixAfterDel(inn); _fixAfterDel(inn);
return null; return true;
}
void flushData() {
_dots = <Dot>[];
_amount = 0;
_nameTable = <int, String>{};
} }
//*********Delete********* //*********Delete*********
@ -253,121 +241,12 @@ class Graphs {
} }
//******Helper******* //******Helper*******
//*****Setters*******
void setName(String name) => _name = name;
String? flipUseOrientation() {
if (_amount != 0) {
return "Can change use of orientation only in empty graph";
}
_oriented = !_oriented;
return null;
}
String? flipUseLength() {
if (_amount != 0) {
return "Can change use of length only in empty graph";
}
_useLength = !_useLength;
return null;
}
String? replaceDataFromFile(String path) {
File file = File(path);
List<String> lines = file.readAsLinesSync();
if (lines.length < 3) {
return "Not enough lines in file";
}
String name = lines.removeAt(0);
bool oriented;
switch (lines.removeAt(0)) {
case Separators.isOriented:
oriented = true;
break;
case Separators.isNotOriented:
oriented = false;
break;
default:
return "Error on parsing \"IsOriented\"";
}
bool useLength;
switch (lines.removeAt(0).trim()) {
case Separators.hasLength:
useLength = true;
break;
case Separators.hasNoLength:
useLength = false;
break;
default:
return "Error on parsing \"HasLength\"";
}
List<Dot> dots = <Dot>[];
for (var l in lines) {
l = l.trimRight();
if (l != Separators.end) {
var spl = l.split(Separators.space);
List<int> dot = <int>[];
List<int> len = <int>[];
String name = spl.removeAt(0);
name = name.substring(0, name.length - 1);
for (var splitted in spl) {
if (splitted != "") {
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
int? parsed = int.tryParse(dt[0]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[0]}\"";
}
dot.add(parsed);
if (useLength) {
parsed = int.tryParse(dt[1]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[1]}\"";
}
len.add(parsed);
} else {
len.add(0);
}
} else if (dt.length == 1) {
int? parsed = int.tryParse(splitted);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"$splitted\"";
}
dot.add(parsed);
len.add(0);
}
}
}
dots.add(Dot.fromTwoLists(name, dot, len));
}
}
_name = name;
_oriented = oriented;
_useLength = useLength;
_dots = dots;
_syncNum();
_syncNameTable();
if (!_oriented) _fullFix();
return null;
}
//*****Setters*******
//*****Getters******* //*****Getters*******
bool getDoubleSidedBool() => _oriented; bool getDoubleSided() => _oriented;
String getDoubleSidedStr() { bool getUseLength() => _useLength;
if (_oriented) return Separators.isOriented;
return Separators.isNotOriented;
}
bool getUseLengthBool() => _useLength;
String getUseLengthStr() {
if (_useLength) return Separators.hasLength;
return Separators.hasNoLength;
}
List<Dot> getDots() => _dots; List<Dot> getDots() => _dots;
String getName() => _name; String getName() => _name;
String? getNameByNum(int n) => _nameTable[n]; String? getNameByNum(int n) => _nameTable[n];
Map<int, String> getNameTable() => _nameTable;
int getDotAmount() => _dots.length; int getDotAmount() => _dots.length;
int? getNumByName(String n) { int? getNumByName(String n) {
for (var i in _nameTable.keys) { for (var i in _nameTable.keys) {
@ -403,69 +282,6 @@ class Graphs {
} }
return out; return out;
} }
List<int>? getLongestPath([int start = 1]) {
start--;
if (start < 0 || start >= _amount) {
return null;
}
int max = -1;
int inD = -1;
int out = -1;
List<int>? res = <int>[];
for (int i = start; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
if (lens[d]! > max) {
max = lens[d]!;
inD = i + 1;
out = d;
}
}
}
if (inD == -1) {
return null;
}
res.add(inD);
res.add(out);
res.add(max);
return res;
}
List<LenDotPath> getSortedPathList() {
int max = -1;
int inD = -1;
int out = -1;
List<LenDotPath> result = <LenDotPath>[];
for (int i = 0; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
max = lens[d]!;
inD = i + 1;
out = d;
result.add(LenDotPath(max, inD, out));
}
}
result.sort((a, b) => a.l.compareTo(b.l));
return result;
}
/*List<Dot> getNoRepeatDots() {
List<Dot> ret = <Dot>[];
for (int i = 0; i < _amount; i++) {
ret.add(Dot(_dots[i].getName(), _dots[i].num));
}
for (int i = 0; i < _amount; i++) {
for (int j in _dots[i].getL().keys) {
if (!ret[j - 1].hasConnection(i + 1) && !ret[i].hasConnection(j) ||
i == j) {
var len = _dots[i].getLength(j);
ret[i].addPath(j, len);
}
}
}
return ret;
}*/
//*****Getters******* //*****Getters*******
//******Print****** //******Print******
@ -487,34 +303,31 @@ class Graphs {
} }
void printToFile(String name) { void printToFile(String name) {
Separators sep = Separators();
var file = File(name); var file = File(name);
file.writeAsStringSync("$_name\n"); file.writeAsStringSync("$_name\n");
if (_oriented) { if (_oriented) {
file.writeAsStringSync("${Separators.isOriented}\n", file.writeAsStringSync(sep.isOriented, mode: FileMode.append);
mode: FileMode.append);
} else { } else {
file.writeAsStringSync("${Separators.isNotOriented}\n", file.writeAsStringSync(sep.isNotOriented, mode: FileMode.append);
mode: FileMode.append);
} }
if (_useLength) { if (_useLength) {
file.writeAsStringSync("${Separators.hasLength}\n", file.writeAsStringSync(sep.hasLength, mode: FileMode.append);
mode: FileMode.append);
} else { } else {
file.writeAsStringSync("${Separators.hasNoLength}\n", file.writeAsStringSync(sep.hasNoLength, mode: FileMode.append);
mode: FileMode.append);
} }
for (int i = 0; i < _amount; i++) { for (int i = 0; i < _amount; i++) {
file.writeAsStringSync(_dots[i].getName() + Separators.dotToConnections, file.writeAsStringSync((i + 1).toString() + sep.dotToConnections,
mode: FileMode.append); mode: FileMode.append);
var d = _dots[i].getL(); var d = _dots[i].getL();
for (var j in d.keys) { for (var j in d.keys) {
file.writeAsStringSync( file.writeAsStringSync(
j.toString() + Separators.dotToLength + d[j].toString() + " ", j.toString() + sep.dotToLength + d[j].toString() + " ",
mode: FileMode.append); mode: FileMode.append);
} }
file.writeAsStringSync(Separators.nL, mode: FileMode.append); file.writeAsStringSync(sep.nL, mode: FileMode.append);
} }
file.writeAsStringSync(Separators.end, mode: FileMode.append); file.writeAsStringSync(sep.end, mode: FileMode.append);
} }
//******Print****** //******Print******
@ -540,22 +353,22 @@ class Graphs {
if (!_oriented) _fullFix(); if (!_oriented) _fullFix();
} }
Graphs.fromFile(String path) { Graphs.fromFile(String path) {
replaceDataFromFile(path); Separators sep = Separators();
/*File file = File(path); File file = File(path);
List<String> lines = file.readAsLinesSync(); List<String> lines = file.readAsLinesSync();
_name = lines.removeAt(0); _name = lines.removeAt(0);
_oriented = lines.removeAt(0) == Separators.isOriented.trim(); _oriented = lines.removeAt(0) == sep.isOriented.trim();
_useLength = lines.removeAt(0) == Separators.hasLength.trim(); _useLength = lines.removeAt(0) == sep.hasLength.trim();
_dots = <Dot>[]; _dots = <Dot>[];
for (var l in lines) { for (var l in lines) {
if (l != Separators.end) { if (l != sep.end) {
var spl = l.split(Separators.space); var spl = l.split(sep.space);
List<int> dot = <int>[]; List<int> dot = <int>[];
List<int> len = <int>[]; List<int> len = <int>[];
String name = spl.removeAt(0); String name = spl.removeAt(0);
name = name.substring(0, name.length - 1); name = name.substring(0, name.length - 1);
for (var splitted in spl) { for (var splitted in spl) {
var dt = splitted.split(Separators.dotToLength); var dt = splitted.split(sep.dotToLength);
if (dt.length == 2) { if (dt.length == 2) {
dot.add(int.parse(dt[0])); dot.add(int.parse(dt[0]));
if (_useLength) { if (_useLength) {
@ -573,7 +386,7 @@ class Graphs {
} }
_syncNum(); _syncNum();
_syncNameTable(); _syncNameTable();
if (!_oriented) _fullFix();*/ if (!_oriented) _fullFix();
} }
//*******Constructor******** //*******Constructor********
@ -581,14 +394,14 @@ class Graphs {
Graphs.clone(Graphs a) { Graphs.clone(Graphs a) {
_name = a.getName(); _name = a.getName();
_dots = a.getDots(); _dots = a.getDots();
_oriented = a.getDoubleSidedBool(); _oriented = a.getDoubleSided();
_useLength = a.getUseLengthBool(); _useLength = a.getUseLength();
_amount = _dots.length; _amount = _dots.length;
_syncNameTable(); _syncNameTable();
} }
//************Алгоритмы************ //************Алгоритмы************
/* bool bfsHasPath(int startDot, int goalDot) { bool bfsHasPath(int startDot, int goalDot) {
// обход в ширину // обход в ширину
startDot--; startDot--;
goalDot--; goalDot--;
@ -615,10 +428,9 @@ class Graphs {
} }
} }
return false; // Целевой узел недостижим return false; // Целевой узел недостижим
}*/ }
List<int>? bfsPath(int startDot, int goalDot) { List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
//if (!bfsHasPath(startDot, goalDot)) return null; //if (!bfsHasPath(startDot, goalDot)) return null;
startDot--; startDot--;
goalDot--; goalDot--;
@ -658,6 +470,7 @@ class Graphs {
//Восстановим кратчайший путь //Восстановим кратчайший путь
//Для восстановления пути пройдём его в обратном порядке, и развернём. //Для восстановления пути пройдём его в обратном порядке, и развернём.
List<int> path = <int>[]; List<int> path = <int>[];
int cur = goalDot; //текущая вершина пути int cur = goalDot; //текущая вершина пути
@ -680,128 +493,46 @@ class Graphs {
List<bool>? dfsIterative(int v) { List<bool>? dfsIterative(int v) {
v--; v--;
//List<int>? pos = <int>[];
List<bool> label = <bool>[]; List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) { for (int i = 0; i < _amount; i++) {
label.add(false); label.add(false);
} }
List<int> stack = <int>[]; List<int> stack = <int>[];
stack.add(v); stack.add(v);
//pos.add(v);
while (stack.isNotEmpty) { while (stack.isNotEmpty) {
v = stack.removeLast(); v = stack.removeLast();
if (!label[v]) { if (!label[v]) {
label[v] = true; label[v] = true;
for (int i in _dots[v].getL().keys) { for (int i in _dots[v].getL().keys) {
stack.add(i - 1); stack.add(i - 1);
//pos.add(i);
} }
} }
} }
//print(pos);
return label; return label;
} }
List<int?> dijkstra(int from) { void dijkstra(int source) {
List<int?> d = List<int?>.filled(_amount, intMax); /*
List<int> p = List<int>.filled(_amount, -1); create vertex set Q;
d[from - 1] = 0; for each vertex v in Graph{
List<bool> u = List<bool>.filled(_amount, false); dist[v] INFINITY ;
for (int i = 0; i < _amount; ++i) { prev[v] UNDEFINED ;
int v = -1; add v to Q;}
for (int j = 0; j < _amount; ++j) { dist[source] 0;
// int t;
if (!u[j] && (v == -1 || d[j]! < d[v]!)) {
v = j;
}
}
if (d[v] == intMax) break;
u[v] = true;
for (int to in _dots[v].getL().keys) {
int len = _dots[v].getL()[to]!;
if (!_useLength && len == 0) len = 1;
if (d[v]! + len < d[to - 1]!) {
d[to - 1] = d[v]! + len;
p[to - 1] = v;
}
}
}
for (int i = 0; i < d.length; i++) {
// подумать как убрать эту часть
if (d[i] == intMax) d[i] = null;
}
return d;
}
List<int?>? prim() { while Q is not empty{
// работоспособность? u vertex in Q with min dist[u]
List<bool> used = List<bool>.filled(_amount, false);
List<int> minE = List<int>.filled(_amount, intMax);
List<int> selE = List<int>.filled(_amount, -1);
minE[0] = 0;
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
if (!used[j] && (v == -1 || minE[j] < minE[v])) {
v = j;
}
}
if (minE[v] == intMax) {
return null;
}
used[v] = true;
if (selE[v] != -1) {
print("${v + 1} ${selE[v]}");
}
for (int to in _dots[v].getL().keys) {
if (_dots[v].getL()[to]! < minE[to - 1]) {
minE[to] = _dots[v].getL()[to]!;
selE[to] = v;
}
}
}
}
Graphs? kruskal() { remove u from Q
//List<pair<int,pair<int,int>>> g (m); // вес - вершина 1 - вершина 2
List<LenDotPath> g = getSortedPathList(); for each neighbor v of u still in Q{
//int cost = 0; alt dist[u] + length(u, v);
List<Dot> res = <Dot>[]; if alt < dist[v]: {
for (int i = 0; i < _amount; i++) { dist[v] alt;
res.add(Dot(_dots[i].getName(), _dots[i].num)); prev[v] u;}
} }}
List<int> treeId = List<int>.filled(_amount, 0); return dist[], prev[]*/
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
//_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
} }
//************Алгоритмы************ //************Алгоритмы************
} }
class LenDotPath {
late int l;
late int d;
late int p;
LenDotPath(int len, dot, path) {
l = len;
d = dot;
p = path;
}
}

View File

@ -1,10 +0,0 @@
star
Ориентированный
НеВзвешенный
1: 2|0
2: 4|0
3: 5|0
4: 6|0
5: 2|0
6: 3|0
END

View File

@ -1,11 +0,0 @@
1
НеОриентированный
Взвешенный
A: 4|5 2|7
B: 5|7 1|7
C: 5|5
D: 6|6 1|5
E: 7|9 2|7 3|5
F: 4|6
G: 5|9
END

View File

@ -0,0 +1,395 @@
import 'package:arrow_path/arrow_path.dart';
import 'package:graphs/src/graph.dart';
import 'package:flutter/material.dart';
import 'dart:math';
class CurvePainter extends CustomPainter {
CurvePainter({
Key? key,
required this.graphData,
required this.bfsPath,
});
List<int>? bfsPath;
Graphs graphData;
final double dotRad = 6;
final double lineWidth = 2;
final Color lineColor = Colors.black;
final double aboveHeight = 5;
double circleRad = 100;
final TextStyle textStyle = TextStyle(
color: Colors.green.shade900,
fontSize: 20,
);
void drawLine(Canvas canvas, Offset p1, Offset p2) {
Paint p = Paint();
p.color = lineColor;
p.strokeWidth = lineWidth;
canvas.drawLine(p1, p2, p);
}
void drawDot(Canvas canvas, Offset p1) {
var p = Paint();
p.color = Colors.yellow.shade900;
p.strokeWidth = lineWidth + 2;
canvas.drawCircle(p1, dotRad, p);
}
void drawSelfConnect(Canvas canvas, Offset p1) {
var p = Paint();
p.color = lineColor;
p.strokeWidth = lineWidth;
p.style = PaintingStyle.stroke;
canvas.drawCircle(Offset(p1.dx + dotRad + 20, p1.dy), dotRad + 20, p);
}
TextSpan _getTextSpan(String s) => TextSpan(text: s, style: textStyle);
TextPainter _getTextPainter(String s) => TextPainter(
text: _getTextSpan(s),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
void drawDotNames(Canvas canvas, Offset place, String s) {
var textPainter = _getTextPainter(s);
textPainter.layout();
textPainter.paint(
canvas,
Offset((place.dx - textPainter.width),
(place.dy - textPainter.height) - aboveHeight));
}
void drawAboveText(Canvas canvas, Offset size, String s) {
var textPainter = _getTextPainter(s);
textPainter.layout();
textPainter.paint(
canvas,
Offset((size.dx - textPainter.width),
(size.dy - textPainter.height) - aboveHeight));
}
int getHighInputConnections() {
if (graphData.getDots().length != 1 && graphData.getDots().length <= 3) {
return -1;
}
int higest = -1;
for (var i in graphData.getDots()) {
if (i.getL().length > higest) higest = i.num;
}
return higest;
}
Map<int, Offset> getDotPos(int dotsAm, Size size, [int? exclude]) {
Map<int, Offset> off = <int, Offset>{};
var width = size.width / 2;
var height = size.height / 2;
int add = 0;
int h = getHighInputConnections();
for (int i = 0; i < dotsAm; i++) {
if ((i + 1) != h) {
double x = cos(2 * pi * (i - add) / (dotsAm - add)) * circleRad + width;
double y =
sin(2 * pi * (i - add) / (dotsAm - add)) * circleRad + height;
off[i + 1] = Offset(x, y);
} else if ((i + 1) == h) {
off[i + 1] = Offset(width + 2, height - 2);
add = 1;
h = 0;
} else {
print("GetDotPos error");
}
//print(off.length);
}
//print(off);
return off;
}
void drawHArrow(Canvas canvas, Size size, Offset from, Offset to,
[bool doubleSided = false]) {
Path path;
// The arrows usually looks better with rounded caps.
Paint paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = lineWidth;
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
/// Draw a single arrow.
path = Path();
path.moveTo(from.dx, from.dy);
path.relativeCubicTo(
0,
0,
-(from.dx + to.dx) / (length * 2) - 40,
-(from.dy + to.dy) / (length * 2) - 40,
to.dx - from.dx,
to.dy - from.dy);
path =
ArrowPath.make(path: path, isDoubleSided: doubleSided, tipLength: 16);
canvas.drawPath(path, paint);
}
void drawHighArrow(Canvas canvas, Size size, Offset from, Offset to,
[bool doubleSided = false]) {
Path path;
// The arrows usually looks better with rounded caps.
Paint paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = lineWidth;
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
/// Draw a single arrow.
path = Path();
path.moveTo(from.dx, from.dy);
path.relativeCubicTo(
0,
0,
(from.dx + to.dx) / (length * 3) + 40,
(from.dy + to.dy) / (length * 2) + 40,
to.dx - from.dx,
to.dy - from.dy);
path = ArrowPath.make(
path: path,
isDoubleSided: doubleSided,
tipLength: 13,
isAdjusted: false);
canvas.drawPath(path, paint);
}
void drawConnections(
Canvas canvas, Size size, List<Dot> dots, Map<int, Offset> off) {
for (var i in dots) {
var list = i.getL();
var beg = off[i.num];
for (var d in list.keys) {
if (d == i.num) {
drawSelfConnect(canvas, off[d]!);
} else {
if (graphData.getDoubleSidedBool()) {
if (d > i.num) {
drawHArrow(canvas, size, beg!, off[d]!, false);
if (graphData.getUseLengthBool()) {
drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2 - 18,
(off[d]!.dy + beg.dy) / 2 - 18),
i.getL()[d].toString());
}
} else {
drawHighArrow(canvas, size, beg!, off[d]!, false);
if (graphData.getUseLengthBool()) {
drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2 + 30,
(off[d]!.dy + beg.dy) / 2 + 30),
i.getL()[d].toString());
}
}
} else {
drawLine(canvas, beg!, off[d]!);
if (graphData.getUseLengthBool()) {
drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2, (off[d]!.dy + beg.dy) / 2),
i.getL()[d].toString());
}
}
}
}
}
}
@override
void paint(Canvas canvas, Size size) {
if (size.width > size.height) {
circleRad = size.height / 3;
} else {
circleRad = size.width / 3;
}
//var paint = Paint();
//drawLine(canvas, Offset(0, size.height / 2),
//Offset(size.width, size.height / 2));
//gr = getGraph();
//int higest = getHighConnections();
//if (higest > -1) {
var off = getDotPos(graphData.getDotAmount(), size); //, higest);
//off[higest] = Offset(size.width / 2, size.height / 2);
for (int i in off.keys) {
drawDot(canvas, off[i]!);
drawDotNames(
canvas, off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]");
}
//var g = gr.getNoRepeatDots();
//print(g);
var g = graphData.getDots();
drawConnections(canvas, size, g, off);
//pringArr(canvas, size);
//drawArrow(canvas, Offset(size.width / 2, size.height / 2),
// Offset(size.width / 2 + 50, size.height / 2 + 200));
//}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
/*void _drawTextAt(String txt, Offset position, Canvas canvas) {
final textPainter = getTextPainter(txt);
textPainter.layout(minWidth: 0, maxWidth: 0);
Offset drawPosition =
Offset(position.dx, position.dy - (textPainter.height / 2));
textPainter.paint(canvas, drawPosition);
}*/
}
void pringArr(Canvas canvas, Size size) {
TextSpan textSpan;
TextPainter textPainter;
Path path;
// The arrows usually looks better with rounded caps.
Paint paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 3.0;
/// Draw a single arrow.
path = Path();
path.moveTo(size.width * 0.25, size.height * 0.10);
path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 0);
path = ArrowPath.make(path: path);
canvas.drawPath(path, paint..color = Colors.blue);
textSpan = const TextSpan(
text: 'Single arrow',
style: TextStyle(color: Colors.blue),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
textPainter.layout(minWidth: size.width);
textPainter.paint(canvas, Offset(0, size.height * 0.06));
/*
/// Draw a double sided arrow.
path = Path();
path.moveTo(size.width * 0.25, size.height * 0.2);
path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 0);
path = ArrowPath.make(path: path, isDoubleSided: true);
canvas.drawPath(path, paint..color = Colors.cyan);
textSpan = const TextSpan(
text: 'Double sided arrow',
style: TextStyle(color: Colors.cyan),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
textPainter.layout(minWidth: size.width);
textPainter.paint(canvas, Offset(0, size.height * 0.16));
/// Use complex path.
path = Path();
path.moveTo(size.width * 0.25, size.height * 0.3);
path.relativeCubicTo(0, 0, size.width * 0.25, 0, size.width * 0.5, 50);
path.relativeCubicTo(0, 0, -size.width * 0.25, 50, -size.width * 0.5, 50);
//path.relativeCubicTo(0, 0, size.width * 0.125, 10, size.width * 0.25, -10);
path = ArrowPath.make(path: path, isDoubleSided: true);
canvas.drawPath(path, paint..color = Colors.blue);
textSpan = const TextSpan(
text: 'Complex path',
style: TextStyle(color: Colors.blue),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
textPainter.layout(minWidth: size.width);
textPainter.paint(canvas, Offset(0, size.height * 0.28));
/// Draw several arrows on the same path.
path = Path();
path.moveTo(size.width * 0.25, size.height * 0.53);
path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 50);
path = ArrowPath.make(path: path);
path.relativeCubicTo(0, 0, -size.width * 0.25, 0, -size.width * 0.5, 50);
path = ArrowPath.make(path: path);
Path subPath = Path();
subPath.moveTo(size.width * 0.375, size.height * 0.53 + 100);
subPath.relativeCubicTo(0, 0, size.width * 0.125, 10, size.width * 0.25, -10);
subPath = ArrowPath.make(path: subPath, isDoubleSided: true);
path.addPath(subPath, Offset.zero);
canvas.drawPath(path, paint..color = Colors.cyan);
textSpan = const TextSpan(
text: 'Several arrows on the same path',
style: TextStyle(color: Colors.cyan),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
textPainter.layout(minWidth: size.width);
textPainter.paint(canvas, Offset(0, size.height * 0.49));
/// Adjusted
path = Path();
path.moveTo(size.width * 0.1, size.height * 0.8);
path.relativeCubicTo(0, 0, size.width * 0.3, 50, size.width * 0.25, 75);
path = ArrowPath.make(path: path, isAdjusted: true);
canvas.drawPath(path, paint..color = Colors.blue);
textSpan = const TextSpan(
text: 'Adjusted',
style: TextStyle(color: Colors.blue),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(canvas, Offset(size.width * 0.2, size.height * 0.77));
/// Non adjusted.
path = Path();
path.moveTo(size.width * 0.6, size.height * 0.8);
path.relativeCubicTo(0, 0, size.width * 0.3, 50, size.width * 0.25, 75);
path = ArrowPath.make(path: path, isAdjusted: false);
canvas.drawPath(path, paint..color = Colors.blue);
textSpan = const TextSpan(
text: 'Non adjusted',
style: TextStyle(color: Colors.blue),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(canvas, Offset(size.width * 0.65, size.height * 0.77));*/
}

View File

@ -1,21 +1,15 @@
import 'package:graphs/pages/drawing_page.dart'; import 'package:graphs/pages/drawing_page.dart';
import 'package:desktop_window/desktop_window.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
void main() { void main() {
runApp(const MyApp()); runApp(const MyApp());
} }
Future setupWindow() async {
await DesktopWindow.setMinWindowSize(const Size(850, 700));
}
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
setupWindow();
return const MaterialApp( return const MaterialApp(
title: "Graphs", title: "Graphs",
home: DrawingPage(), home: DrawingPage(),

View File

@ -1,17 +1,17 @@
import 'package:graphs/src/graph.dart'; import 'package:graphs/src/graph.dart';
import 'package:graphs/src/curve_painter.dart'; import 'package:graphs/curve_painter.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Graphs getGraph() { Graphs getGraph() {
List<Dot> d = <Dot>[]; List<Dot> d = <Dot>[];
d.add(Dot.fromTwoLists("1", [2], [3])); d.add(Dot.fromTwoLists("1", [2, 3], [5, 1]));
d.add(Dot.fromTwoLists("2", [3], [1])); d.add(Dot.fromTwoLists("2", [1, 3], [1, 1]));
d.add(Dot.fromTwoLists("3", [1], [2])); d.add(Dot.fromTwoLists("3", [1, 2], [1, 2]));
//d.add(Dot.fromTwoLists("Name1", [], [])); //d.add(Dot.fromTwoLists("Name1", [], []));
//d.add(Dot.fromTwoLists("Name2", [], [])); //d.add(Dot.fromTwoLists("Name2", [], []));
return Graphs.fromList("Имя графа", d, true, true); return Graphs.fromList("UMYA", d, true, true);
} }
class DrawingPage extends StatefulWidget { class DrawingPage extends StatefulWidget {
@ -24,90 +24,138 @@ class DrawingPage extends StatefulWidget {
class _DrawingPageState extends State<DrawingPage> { class _DrawingPageState extends State<DrawingPage> {
double screenSize = 0; double screenSize = 0;
Graphs graphData = getGraph(); Graphs graphData = getGraph();
List<int?>? intListPath; List<int>? bfsPath;
List<bool>? dfsAccessTable;
//List<int?>? dijkstraTable;
String op = Operations.none;
int? startDot;
int? endDot;
String? dropdownValue1;
String? dropdownValue2;
String currOp = "";
final _textNameController = TextEditingController(); final _textNameController = TextEditingController();
final _textNumbController = TextEditingController();
final _textDestController = TextEditingController();
final _textLnthController = TextEditingController(); final _textLnthController = TextEditingController();
final _textGrNmController = TextEditingController(); final _textGrNmController = TextEditingController();
void clearInputData() { void clearTextControllers() {
setState(() { _textDestController.clear();
_textLnthController.clear(); _textNumbController.clear();
_textNameController.clear(); _textLnthController.clear();
dropdownValue1 = null; _textNameController.clear();
dropdownValue2 = null;
});
} }
void clearDropDownVals() { @override
setState(() { Widget build(BuildContext context) {
startDot = null; screenSize = MediaQuery.of(context).size.width;
intListPath = null; _textGrNmController.text = graphData.getName();
dfsAccessTable = null; return MaterialApp(
endDot = null; home: Scaffold(
currOp = ""; appBar: AppBar(
op = Operations.none; title: const Align(
}); alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.blue,
child: Column(children: <Widget>[
const SizedBox(height: 5),
Row(children: [
addSpaceW(screenSize / 8 + 19),
createButton("\nAdd dot\n", addDotPushed),
createInputBox("Dot name", screenSize / 4 - 25, Icons.label,
_textNameController),
addSpaceW(8),
createButton("\nAdd path\n", addPathPushed),
createInputBox("Input length", screenSize / 4 - 25,
Icons.arrow_right_alt_outlined, _textLnthController),
]),
addSpaceH(3),
Row(children: [
addSpaceW(5),
createInputBox(
"Name", screenSize / 8 - 25, null, _textGrNmController),
//addSpaceW(screenSize / 8 - 4),
createButton("\nDel dot \n", delDotPushed),
createInputBox("Dot number", screenSize / 4 - 25,
Icons.fiber_manual_record, _textNumbController),
addSpaceW(13),
createButton("\nDel path\n", delPathPushed),
createInputBox("Destination number", screenSize / 4 - 25,
Icons.fiber_manual_record, _textDestController),
]),
]),
),
actions: [
IconButton(
onPressed: clearScreen,
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(graphData: graphData, bfsPath: bfsPath),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
createButton("Bfs", bfsPushed),
createButton("Dfs", () {}),
createButton("Clear dfs or bfs", () => bfsPath = null),
createButton(graphData.getUseLengthStr(), changeLength),
createButton(graphData.getDoubleSidedStr(), changeOriented),
/*Text(_textGrNmController.text,
style:
TextStyle(fontSize: 15, color: Colors.blueGrey.shade900)),*/
createButton("Save to file", fileSaver),
createButton("Load from file", fileOpener),
createButton("Help", () {
String out =
" В поле \"Graph name\" можно сменить имя графу.\n";
out +=
" Для добавления точки необходимо ввести имя в \"Dot name\" и нажать на \"Add dot\".\n";
out +=
" Для удаления точки необходимо ввести номер в \"Dot number\" и нажать на \"Del dot\".\n";
out +=
" Для добавления пути необходимо ввести: номер выходной вершины в \"Dot number\", номер входной вершины в \"Destination number\" ";
out +=
"и, если граф взвешенный, то ввести длину пути в \"Input length\". Затем нажать \"Add path\".\n";
out +=
" Для удаления пути необходимо ввести номер выходной вершины в \"Dot number\" и номер входной вершины в \"Destination number\". Затем нажать \"Del path\".\n\n";
out +=
" Кнопки \"Bfs\" и \"Dfs\" нумеруют точки в зависимости от послежовательности, в которой они будут пройдены.\n";
out +=
" Кнопки \"Взвешенный\" и \"Ориентированный\" позволяют сменить эти значения перед построением графа (т.е. для их работы граф должен быть пустым).\n";
out +=
" Кнопки \"Save to file\" и \"Load from file\" позволяют вывести информацию в файл и загрузить информацию из файла соответственно.\n";
out +=
" Кнопка \"Help\" описывает работу с интерфейсом программы.";
showPopUp("Help:", out);
})
],
),
),
),
));
} }
// *************buttons************* // ignore: avoid_types_as_parameter_names, non_constant_identifier_names, use_function_type_syntax_for_parameters
ElevatedButton createButton(String txt, void Function() onPressing) { ElevatedButton createButton(String txt, void onPressing()) {
return ElevatedButton( return ElevatedButton(
onPressed: onPressing, onPressed: onPressing,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(states) => Colors.green.shade700)),
child: Text(txt, child: Text(txt,
style: const TextStyle( style: const TextStyle(
fontSize: 15, fontSize: 15,
color: Colors.white70, color: Colors.white,
height: 1, height: 1,
)), )),
); );
} }
void showPopUp(String alertTitle, String err) => showDialog<String>( Container createInputBox(String text, double wid, IconData? icon,
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle, style: const TextStyle(fontSize: 26)),
content: Text(
err,
style: const TextStyle(fontSize: 18),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
// *************buttons*************
// ***addSpace***
SizedBox addSpaceH(double h) {
return SizedBox(height: h);
}
SizedBox addSpaceW(double w) {
return SizedBox(width: w);
}
// ***addSpace***
// *************inputs*************
Container createInputBox(String text, double width, IconData? icon,
TextEditingController? controller) { TextEditingController? controller) {
if (icon == null) { if (icon == null) {
return Container( return Container(
width: width, width: wid,
height: 40, height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField( child: TextField(
@ -127,7 +175,7 @@ class _DrawingPageState extends State<DrawingPage> {
)); ));
} }
return Container( return Container(
width: width, width: wid,
height: 40, height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField( child: TextField(
@ -146,116 +194,73 @@ class _DrawingPageState extends State<DrawingPage> {
)); ));
} }
SizedBox dropList1(double width) { SizedBox addSpaceH(double h) {
var button = DropdownButton( return SizedBox(height: h);
hint: const Text(
'Select dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
//icon: const Icon(Icons.fiber_manual_record, color: Colors.white),
alignment: AlignmentDirectional.bottomEnd,
value: dropdownValue1,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue1 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
return SizedBox(
child: button,
width: width,
);
} }
SizedBox dropList2(double width) { SizedBox addSpaceW(double w) {
var button = DropdownButton( return SizedBox(width: w);
hint: const Text(
'Select Dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
alignment: AlignmentDirectional.centerEnd,
value: dropdownValue2,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue2 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
return SizedBox(
child: button,
width: width,
);
} }
// *************inputs*************
void showPopUp(String alertTitle, String err) => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle),
content: Text(err),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
//*********ButtonsFunctions********* //*********ButtonsFunctions*********
void addDotPushed() { void addDotPushed() {
clearDropDownVals(); //showPopUp("Test", "Test message");
if (_textNameController.text == "") { //var inp = int.tryParse(_textNameController.text);
showPopUp("Error", "No name in \"Dot name\" box"); setState(() {
} else { if (_textNameController.text == "") {
setState(() { showPopUp("Error", "No name in \"Dot name\" box");
} else {
String? res = graphData.addIsolated(_textNameController.text); String? res = graphData.addIsolated(_textNameController.text);
if (res != null) { if (res != null) {
showPopUp("Error", res); showPopUp("Error", res);
} }
}); }
} clearTextControllers();
clearInputData(); });
} }
void addPathPushed() { void addPathPushed() {
clearDropDownVals(); setState(() {
if (dropdownValue1 == null) { if (_textNumbController.text == "") {
showPopUp("Error", "No dot in first box selected"); showPopUp("Error", "No number in \"Dot number\" box");
} else if (dropdownValue2 == null) { } else if (_textDestController.text == "") {
showPopUp("Error", "No dot in second box selected"); showPopUp("Error", "No name in \"Destination number\" box");
} else if (_textLnthController.text == "" && graphData.getUseLengthBool()) { } else if (_textLnthController.text == "" &&
showPopUp("Error", "No length in \"Input length\" box"); graphData.getUseLengthBool()) {
} else { showPopUp("Error", "No length in \"Input length\" box");
int? from = int.parse(dropdownValue1!);
int? to = int.parse(dropdownValue2!);
int? len = int.tryParse(_textLnthController.text);
if (len == null && graphData.getUseLengthBool()) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Input length\"");
} else { } else {
len ??= 0; int? from = int.tryParse(_textNumbController.text);
setState(() { int? to = int.tryParse(_textDestController.text);
String? res = graphData.addPath(from, to, len!); int? len = int.tryParse(_textLnthController.text);
if (res != null) showPopUp("Error", res); if (from == null ||
}); to == null ||
(len == null && graphData.getUseLengthBool())) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Dot number\", \"Destination number\" and \"Input length\"");
} else {
len ??= 0;
String? res = graphData.addPath(from, to, len);
if (res != null) {
showPopUp("Error", res);
}
}
} }
} clearTextControllers();
clearInputData(); });
} }
void changeOriented() { void changeOriented() {
@ -273,64 +278,65 @@ class _DrawingPageState extends State<DrawingPage> {
} }
void delPathPushed() { void delPathPushed() {
clearDropDownVals(); setState(() {
if (dropdownValue1 == null) { if (_textNumbController.text == "") {
showPopUp("Error", "No dot in first box selected"); showPopUp("Error", "No number in \"Dot number\" box");
} else if (dropdownValue2 == null) { } else if (_textDestController.text == "") {
showPopUp("Error", "No dot in second box selected"); showPopUp("Error", "No name in \"Dot name\" box");
} else {
int? from = int.tryParse(dropdownValue1!);
int? to = int.tryParse(dropdownValue2!);
if (from == null || to == null) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\"");
} else { } else {
setState(() { int? from = int.tryParse(_textNumbController.text);
int? to = int.tryParse(_textDestController.text);
if (from == null || to == null) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\"");
} else {
String? res = graphData.delPath(from, to); String? res = graphData.delPath(from, to);
if (res != null) { if (res != null) {
showPopUp("Error", res); showPopUp("Error", res);
} }
}); }
} }
} clearTextControllers();
clearInputData(); });
} }
void delDotPushed() { void delDotPushed() {
if (dropdownValue1 != null) { setState(() {
setState(() { if (_textNumbController.text == "") {
String? res = graphData.delDot(int.parse(dropdownValue1!)); showPopUp("Error", "No number in \"Dot number\" box");
if (res != null) { } else {
showPopUp("Error", res); int? dot = int.tryParse(_textNumbController.text);
if (dot == null) {
showPopUp("Error", "Can't parse input.\nInts only allowed");
} else {
String? res = graphData.delDot(dot);
if (res != null) {
showPopUp("Error", res);
}
} }
}); }
} else { clearTextControllers();
showPopUp("Error", "Nothing in input"); });
}
clearDropDownVals();
clearInputData();
} }
void fileOpener() async { void fileOpener() async {
FilePickerResult? result = FilePickerResult? result =
await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]); await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]);
setState(() {
if (result != null) { if (result != null) {
if (!result.files.single.path!.endsWith(".txt")) { if (!result.files.single.path!.endsWith(".txt")) {
showPopUp("Error", "Can open only \".txt\" files"); showPopUp("Error", "Can open only \".txt\" files");
} else { } else {
setState(() { //print(result.files.single.path!);
String? res = String? res =
graphData.replaceDataFromFile(result.files.single.path!); graphData.replaceDataFromFile(result.files.single.path!);
if (res != null) showPopUp("Error", res); if (res != null) showPopUp("Error", res);
}); }
} else {
showPopUp("Error", "No file selected");
// User canceled the picker
} }
} else { });
showPopUp("Error", "No file selected");
}
clearDropDownVals();
clearInputData();
} }
void fileSaver() async { void fileSaver() async {
@ -340,208 +346,39 @@ class _DrawingPageState extends State<DrawingPage> {
allowedExtensions: ["txt"]); allowedExtensions: ["txt"]);
if (outputFile == null) { if (outputFile == null) {
showPopUp("Error", "Save cancelled"); showPopUp("Error", "Save cancelled");
// User canceled the picker
} else { } else {
if (!outputFile.endsWith(".txt")) { if (!outputFile.endsWith(".txt")) {
outputFile += ".txt"; outputFile += ".txt";
} }
graphData.printToFile(outputFile); graphData.printToFile(outputFile);
} }
clearDropDownVals();
clearInputData();
} }
void bfsPushed() { void bfsPushed() {
clearDropDownVals(); setState(() {
if (dropdownValue1 == null) { if (_textNumbController.text == "") {
showPopUp("Error", "No dot in first box selected"); showPopUp("Error", "No number in \"Dot number\" box");
} else if (dropdownValue2 == null) { } else if (_textDestController.text == "") {
showPopUp("Error", "No dot in second box selected"); showPopUp("Error", "No name in \"Destination number\" box");
} else { } else {
setState(() { int? from = int.tryParse(_textNumbController.text);
startDot = int.parse(dropdownValue1!); int? to = int.tryParse(_textDestController.text);
endDot = int.parse(dropdownValue2!); if (from == null || to == null) {
currOp = "OP: BFS from $startDot to $endDot"; showPopUp("Error",
op = Operations.bfs; "Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\"");
intListPath = graphData.bfsPath(startDot!, endDot!); } else {
}); bfsPath = graphData.bfsPath(from, to);
if (intListPath == null) { if (bfsPath == null) {
showPopUp("Info", "There is no path"); showPopUp("Info", "There is no path");
}
print(bfsPath);
}
} }
//print(intListPath); clearTextControllers();
} });
clearInputData();
} }
void dfsPushed() { void clearScreen() => graphData.flushData();
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
op = Operations.dfs;
currOp = "OP: DFS from $startDot";
dfsAccessTable = graphData.dfsIterative(startDot!);
});
if (dfsAccessTable == null) {
showPopUp("Err", "report this error.");
}
//print(dfsAccessTable);
}
clearInputData();
}
void dijkstraPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No number in \"Dot number\" box");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
currOp = "OP: Dijkstra from $startDot";
op = Operations.dijkstra;
intListPath = graphData.dijkstra(startDot!);
});
if (intListPath == null) {
showPopUp("Err", "report this error.");
}
//print(intListPath);
}
clearInputData();
}
//*********ButtonsFunctions********* //*********ButtonsFunctions*********
// build
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
_textGrNmController.text = graphData.getName();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Align(
alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.green.shade900,
child: Column(children: <Widget>[
const SizedBox(height: 5),
Row(children: [
addSpaceW(screenSize / 8 + 19),
createButton("\nAdd dot\n", addDotPushed),
createInputBox("Dot name", screenSize / 4 - 25, Icons.label,
_textNameController),
addSpaceW(8),
createButton("\nAdd path\n", addPathPushed),
createInputBox("Input length", screenSize / 4 - 25,
Icons.arrow_right_alt_outlined, _textLnthController),
]),
addSpaceH(3),
Row(children: [
addSpaceW(6),
createInputBox(
"Name", screenSize / 8 - 25, null, _textGrNmController),
//addSpaceW(screenSize / 8 - 4),
createButton("\nDel dot \n", delDotPushed),
//createInputBox("Dot number", screenSize / 4 - 25, Icons.fiber_manual_record, _textNumbController),
addSpaceW(54),
dropList1(screenSize / 4 - 80),
addSpaceW(53),
createButton("\nDel path\n", delPathPushed),
addSpaceW(54),
dropList2(screenSize / 4 - 80),
//createInputBox("Destination number", screenSize / 4 - 25, Icons.fiber_manual_record, _textDestController),
]),
]),
),
actions: [
IconButton(
onPressed: () {
setState(() {
clearDropDownVals();
graphData.flushData();
clearInputData();
});
},
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(
graphData: graphData,
intListPath: intListPath,
dfsAccessTable: dfsAccessTable,
start: startDot,
end: endDot,
op: op),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
currOp,
style: const TextStyle(fontSize: 14, color: Colors.black),
),
createButton("Bfs", bfsPushed),
createButton("Dfs", dfsPushed),
createButton("Dijkstra", dijkstraPushed),
createButton("Kruskal", () {
clearDropDownVals();
setState(() {
currOp = "OP: Kruscal algo";
graphData.kruskal();
});
clearInputData();
}),
createButton("Clear OP", () {
clearDropDownVals();
clearInputData();
}),
createButton(graphData.getUseLengthStr(), changeLength),
createButton(graphData.getDoubleSidedStr(), changeOriented),
createButton("Save to file", fileSaver),
createButton("Load from file", fileOpener),
createButton("Help", () {
String out =
" В поле \"Graph name\" можно сменить имя графу.\n";
out +=
" Для добавления точки необходимо ввести имя в \"Dot name\" и нажать на \"Add dot\".\n";
out +=
" Для удаления точки необходимо выбрать номер в левом выпадающем списке и нажать на \"Del dot\".\n";
out +=
" Для добавления пути необходимо выбрать: первую вершину в левом выпадающем списке, вторую вершину в правом выпадающем списке, ";
out +=
"и, если граф взвешенный, то ввести длину пути в \"Input length\". Затем нажать \"Add path\".\n";
out +=
" Для удаления пути необходимо выбрать первую вершину из левого списка и вторую вершину в правом списке. Затем нажать \"Del path\".\n\n";
out +=
" Кнопка \"Bfs\" отмечает точки в пути из вершины, выбранной слева, в вершину, выбранную справа.\n";
out +=
" Кнопка \"Dfs\" помечает точки, которые доступны из вершины, выбранной слева.";
out +=
" Кнопка \"Dijkstra\" ищет кратчайший путь из вершины, выбранной слева. во все другие.";
out +=
" Кнопка \"Clear op\" отменяет \"Bfs\", \"Dfs\" и \"Dijkstra\". Все эти отображения так же отменяются при любых действиях с графом.";
out +=
" Кнопки \"Взвешенный\" и \"Ориентированный\" позволяют сменить эти значения перед построением графа (т.е. для их работы граф должен быть пустым).\n";
out +=
" Кнопки \"Save to file\" и \"Load from file\" позволяют вывести информацию в файл и загрузить информацию из файла соответственно.\n";
out +=
" Кнопка \"Help\" описывает работу с интерфейсом программы.";
showPopUp("Help:", out);
})
],
),
),
),
));
}
} }

View File

@ -1,334 +0,0 @@
import 'package:arrow_path/arrow_path.dart';
import 'package:graphs/src/graph.dart';
import 'package:flutter/material.dart';
import 'dart:math';
class Operations {
static const String bfs = "bfs";
static const String dfs = "dfs";
static const String dijkstra = "djkstr";
static const String none = "none";
}
class CurvePainter extends CustomPainter {
CurvePainter({
Key? key,
required this.graphData,
required this.intListPath,
required this.dfsAccessTable,
required this.start,
required this.end,
required this.op,
});
List<int?>? intListPath;
List<bool>? dfsAccessTable;
String op;
int? start;
int? end;
Graphs graphData;
final double _dotRad = 7;
final double _lineWidth = 1.5;
final Color _lineColor = Colors.black;
final double _aboveHeight = 5;
double _circleRad = 100;
final TextStyle _textStyle = TextStyle(
color: Colors.red.shade900,
decorationColor: Colors.green.shade900,
decorationThickness: 10,
decorationStyle: TextDecorationStyle.dashed,
fontSize: 20,
);
Map<int, Offset> _off = <int, Offset>{};
void _drawLine(Canvas canvas, Offset p1, Offset p2) {
Paint p = Paint();
p.color = _lineColor;
p.strokeWidth = _lineWidth;
canvas.drawLine(p1, p2, p);
}
void _drawDot(Canvas canvas, Offset p1, [double plusRad = 0, Color? col]) {
col ??= Colors.yellow.shade900;
var p = Paint();
p.color = col;
p.strokeWidth = _lineWidth + 2;
canvas.drawCircle(p1, _dotRad + plusRad, p);
}
void _drawSelfConnect(Canvas canvas, Offset p1) {
var p = Paint();
p.color = _lineColor;
p.strokeWidth = _lineWidth;
p.style = PaintingStyle.stroke;
canvas.drawCircle(Offset(p1.dx + _dotRad + 20, p1.dy), _dotRad + 20, p);
}
TextSpan _getTextSpan(String s) => TextSpan(text: s, style: _textStyle);
TextPainter _getTextPainter(String s) => TextPainter(
text: _getTextSpan(s),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
void _drawDotNames(Canvas canvas, Offset place, String s) {
var textPainter = _getTextPainter(s);
textPainter.layout();
textPainter.paint(
canvas,
Offset((place.dx - textPainter.width),
(place.dy - textPainter.height) - _aboveHeight));
}
void _drawDotNum(Canvas canvas, Offset size, String s) {
var textPainter = TextPainter(
text: TextSpan(
text: s,
style: const TextStyle(
color: Colors.black,
fontSize: 17,
)),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
textPainter.layout();
textPainter.paint(
canvas,
Offset((size.dx - textPainter.width) + 25,
(size.dy - textPainter.height) + _aboveHeight + 30));
}
int _getHighInputConnections() {
if (graphData.getDots().length != 1 && graphData.getDots().length <= 3) {
return -1;
}
int higest = -1;
for (var i in graphData.getDots()) {
if (i.getL().length > higest) higest = i.num;
}
return higest;
}
Map<int, Offset> _getDotPos(int dotsAm, Size size) {
Map<int, Offset> off = <int, Offset>{};
var width = size.width / 2;
var height = size.height / 2;
int add = 0;
int h = _getHighInputConnections();
for (int i = 0; i < dotsAm; i++) {
if ((i + 1) != h) {
double x =
cos(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + width;
double y =
sin(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + height;
off[i + 1] = Offset(x, y);
} else if ((i + 1) == h) {
off[i + 1] = Offset(width + 2, height - 2);
add = 1;
h = 0;
} else {
print("GetDotPos error");
}
//print(off.length);
}
//print(off);
return off;
}
void _drawHArrow(Canvas canvas, Size size, Offset from, Offset to,
[bool doubleSided = false]) {
Path path;
// The arrows usually looks better with rounded caps.
Paint paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = _lineWidth;
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
/// Draw a single arrow.
path = Path();
path.moveTo(from.dx, from.dy);
path.relativeCubicTo(
0,
0,
-(from.dx + to.dx + length) / (length) - 40,
-(from.dy + to.dy + length) / (length) - 40,
to.dx - from.dx,
to.dy - from.dy);
path =
ArrowPath.make(path: path, isDoubleSided: doubleSided, tipLength: 16);
canvas.drawPath(path, paint);
}
void _drawHighArrow(Canvas canvas, Size size, Offset from, Offset to,
[bool doubleSided = false]) {
Path path;
// The arrows usually looks better with rounded caps.
Paint paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = _lineWidth;
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
/// Draw a single arrow.
path = Path();
path.moveTo(from.dx, from.dy);
path.relativeCubicTo(
0,
0,
(from.dx + to.dx) / (length * 2) + 40,
(from.dy + to.dy) / (length * 2) + 40,
to.dx - from.dx,
to.dy - from.dy);
path = ArrowPath.make(
path: path,
isDoubleSided: doubleSided,
tipLength: 13,
isAdjusted: false);
canvas.drawPath(path, paint);
}
void _drawConnections(
Canvas canvas, Size size, List<Dot> dots, Map<int, Offset> off) {
for (var i in dots) {
var list = i.getL();
var beg = off[i.num];
for (var d in list.keys) {
if (d == i.num) {
_drawSelfConnect(canvas, off[d]!);
} else {
if (graphData.getDoubleSidedBool()) {
if (d > i.num) {
_drawHArrow(canvas, size, beg!, off[d]!, false);
if (graphData.getUseLengthBool()) {
_drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2 - 18,
(off[d]!.dy + beg.dy) / 2 - 18),
i.getL()[d].toString());
}
} else {
_drawHighArrow(canvas, size, beg!, off[d]!, false);
if (graphData.getUseLengthBool()) {
_drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2 + 30,
(off[d]!.dy + beg.dy) / 2 + 30),
i.getL()[d].toString());
}
}
} else {
_drawLine(canvas, beg!, off[d]!);
if (graphData.getUseLengthBool()) {
_drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2, (off[d]!.dy + beg.dy) / 2),
i.getL()[d].toString());
}
}
}
}
}
}
void _drawBFS(Canvas canvas) {
if (intListPath != null) {
for (int i = 0; i < intListPath!.length; i++) {
_drawDot(canvas, _off[intListPath![i]]!, 8, Colors.yellow);
}
_drawDot(canvas, _off[start]!, 9, Colors.green);
_drawDot(canvas, _off[end]!, 7, Colors.red.shade200);
for (int i = 0; i < intListPath!.length; i++) {
_drawDotNum(canvas, _off[intListPath![i]]!, "bfs: №${i + 1}");
}
}
}
void _drawDFS(Canvas canvas) {
if (dfsAccessTable != null) {
for (int i = 0; i < dfsAccessTable!.length; i++) {
if (dfsAccessTable![i]) {
_drawDot(canvas, _off[i + 1]!, 8, Colors.green.shade500);
_drawDotNum(canvas, _off[i + 1]!, "dfs: visible");
} else {
_drawDot(canvas, _off[i + 1]!, 7, Colors.red.shade500);
_drawDotNum(canvas, _off[i + 1]!, "dfs: not visible");
}
}
_drawDot(canvas, _off[start]!, 9, Colors.green.shade900);
}
}
void _drawDijkstra(Canvas canvas) {
if (intListPath != null) {
_drawDot(canvas, _off[start]!, 9, Colors.green);
for (int i = 0; i < intListPath!.length; i++) {
if (intListPath![i] == null) {
_drawDotNum(canvas, _off[i + 1]!, "len: INF");
} else {
_drawDotNum(canvas, _off[i + 1]!, "len: ${intListPath![i]}");
}
}
}
}
@override
void paint(Canvas canvas, Size size) {
//print("${size.width}, ${size.height}");
if (size.width > size.height) {
_circleRad = size.height / 3;
} else {
_circleRad = size.width / 3;
}
_off = _getDotPos(graphData.getDotAmount(), size); //, higest);
for (int i in _off.keys) {
_drawDot(canvas, _off[i]!);
//drawDotNames(canvas, off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]");
}
var g = graphData.getDots();
switch (op) {
case Operations.bfs:
{
_drawBFS(canvas);
break;
}
case Operations.dfs:
{
_drawDFS(canvas);
break;
}
case Operations.dijkstra:
{
_drawDijkstra(canvas);
break;
}
default:
{
break;
}
}
_drawConnections(canvas, size, g, _off);
for (int i in _off.keys) {
//drawDot(canvas, off[i]!);
_drawDotNames(
canvas, _off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]");
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

View File

@ -89,7 +89,6 @@ class Dot {
} }
class Graphs { class Graphs {
static const int intMax = 0x7fffffffffffffff;
//Data //Data
String _name = "Undefined"; //Имя String _name = "Undefined"; //Имя
int _amount = 0; //Количество вершин int _amount = 0; //Количество вершин
@ -152,9 +151,6 @@ class Graphs {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) { if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return "Can't find specified path"; return "Can't find specified path";
} }
if (!_dots[from - 1].hasConnection(to)) {
return "Already no connection between $from and $to";
}
_dots[from - 1].delPath(to); _dots[from - 1].delPath(to);
if (!_oriented) { if (!_oriented) {
_dots[to - 1].delPath(from); _dots[to - 1].delPath(from);
@ -367,7 +363,6 @@ class Graphs {
List<Dot> getDots() => _dots; List<Dot> getDots() => _dots;
String getName() => _name; String getName() => _name;
String? getNameByNum(int n) => _nameTable[n]; String? getNameByNum(int n) => _nameTable[n];
Map<int, String> getNameTable() => _nameTable;
int getDotAmount() => _dots.length; int getDotAmount() => _dots.length;
int? getNumByName(String n) { int? getNumByName(String n) {
for (var i in _nameTable.keys) { for (var i in _nameTable.keys) {
@ -404,52 +399,6 @@ class Graphs {
return out; return out;
} }
List<int>? getLongestPath([int start = 1]) {
start--;
if (start < 0 || start >= _amount) {
return null;
}
int max = -1;
int inD = -1;
int out = -1;
List<int>? res = <int>[];
for (int i = start; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
if (lens[d]! > max) {
max = lens[d]!;
inD = i + 1;
out = d;
}
}
}
if (inD == -1) {
return null;
}
res.add(inD);
res.add(out);
res.add(max);
return res;
}
List<LenDotPath> getSortedPathList() {
int max = -1;
int inD = -1;
int out = -1;
List<LenDotPath> result = <LenDotPath>[];
for (int i = 0; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
max = lens[d]!;
inD = i + 1;
out = d;
result.add(LenDotPath(max, inD, out));
}
}
result.sort((a, b) => a.l.compareTo(b.l));
return result;
}
/*List<Dot> getNoRepeatDots() { /*List<Dot> getNoRepeatDots() {
List<Dot> ret = <Dot>[]; List<Dot> ret = <Dot>[];
for (int i = 0; i < _amount; i++) { for (int i = 0; i < _amount; i++) {
@ -504,7 +453,7 @@ class Graphs {
mode: FileMode.append); mode: FileMode.append);
} }
for (int i = 0; i < _amount; i++) { for (int i = 0; i < _amount; i++) {
file.writeAsStringSync(_dots[i].getName() + Separators.dotToConnections, file.writeAsStringSync((i + 1).toString() + Separators.dotToConnections,
mode: FileMode.append); mode: FileMode.append);
var d = _dots[i].getL(); var d = _dots[i].getL();
for (var j in d.keys) { for (var j in d.keys) {
@ -588,7 +537,7 @@ class Graphs {
} }
//************Алгоритмы************ //************Алгоритмы************
/* bool bfsHasPath(int startDot, int goalDot) { /* bool bfsHasPath(int startDot, int goalDot) {
// обход в ширину // обход в ширину
startDot--; startDot--;
goalDot--; goalDot--;
@ -618,7 +567,6 @@ class Graphs {
}*/ }*/
List<int>? bfsPath(int startDot, int goalDot) { List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
//if (!bfsHasPath(startDot, goalDot)) return null; //if (!bfsHasPath(startDot, goalDot)) return null;
startDot--; startDot--;
goalDot--; goalDot--;
@ -658,6 +606,7 @@ class Graphs {
//Восстановим кратчайший путь //Восстановим кратчайший путь
//Для восстановления пути пройдём его в обратном порядке, и развернём. //Для восстановления пути пройдём его в обратном порядке, и развернём.
List<int> path = <int>[]; List<int> path = <int>[];
int cur = goalDot; //текущая вершина пути int cur = goalDot; //текущая вершина пути
@ -680,128 +629,46 @@ class Graphs {
List<bool>? dfsIterative(int v) { List<bool>? dfsIterative(int v) {
v--; v--;
//List<int>? pos = <int>[];
List<bool> label = <bool>[]; List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) { for (int i = 0; i < _amount; i++) {
label.add(false); label.add(false);
} }
List<int> stack = <int>[]; List<int> stack = <int>[];
stack.add(v); stack.add(v);
//pos.add(v);
while (stack.isNotEmpty) { while (stack.isNotEmpty) {
v = stack.removeLast(); v = stack.removeLast();
if (!label[v]) { if (!label[v]) {
label[v] = true; label[v] = true;
for (int i in _dots[v].getL().keys) { for (int i in _dots[v].getL().keys) {
stack.add(i - 1); stack.add(i - 1);
//pos.add(i);
} }
} }
} }
//print(pos);
return label; return label;
} }
List<int?> dijkstra(int from) { void dijkstra(int source) {
List<int?> d = List<int?>.filled(_amount, intMax); /*
List<int> p = List<int>.filled(_amount, -1); create vertex set Q;
d[from - 1] = 0; for each vertex v in Graph{
List<bool> u = List<bool>.filled(_amount, false); dist[v] INFINITY ;
for (int i = 0; i < _amount; ++i) { prev[v] UNDEFINED ;
int v = -1; add v to Q;}
for (int j = 0; j < _amount; ++j) { dist[source] 0;
// int t;
if (!u[j] && (v == -1 || d[j]! < d[v]!)) {
v = j;
}
}
if (d[v] == intMax) break;
u[v] = true;
for (int to in _dots[v].getL().keys) {
int len = _dots[v].getL()[to]!;
if (!_useLength && len == 0) len = 1;
if (d[v]! + len < d[to - 1]!) {
d[to - 1] = d[v]! + len;
p[to - 1] = v;
}
}
}
for (int i = 0; i < d.length; i++) {
// подумать как убрать эту часть
if (d[i] == intMax) d[i] = null;
}
return d;
}
List<int?>? prim() { while Q is not empty{
// работоспособность? u vertex in Q with min dist[u]
List<bool> used = List<bool>.filled(_amount, false);
List<int> minE = List<int>.filled(_amount, intMax);
List<int> selE = List<int>.filled(_amount, -1);
minE[0] = 0;
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
if (!used[j] && (v == -1 || minE[j] < minE[v])) {
v = j;
}
}
if (minE[v] == intMax) {
return null;
}
used[v] = true;
if (selE[v] != -1) {
print("${v + 1} ${selE[v]}");
}
for (int to in _dots[v].getL().keys) {
if (_dots[v].getL()[to]! < minE[to - 1]) {
minE[to] = _dots[v].getL()[to]!;
selE[to] = v;
}
}
}
}
Graphs? kruskal() { remove u from Q
//List<pair<int,pair<int,int>>> g (m); // вес - вершина 1 - вершина 2
List<LenDotPath> g = getSortedPathList(); for each neighbor v of u still in Q{
//int cost = 0; alt dist[u] + length(u, v);
List<Dot> res = <Dot>[]; if alt < dist[v]: {
for (int i = 0; i < _amount; i++) { dist[v] alt;
res.add(Dot(_dots[i].getName(), _dots[i].num)); prev[v] u;}
} }}
List<int> treeId = List<int>.filled(_amount, 0); return dist[], prev[]*/
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
} }
//************Алгоритмы************ //************Алгоритмы************
} }
class LenDotPath {
late int l;
late int d;
late int p;
LenDotPath(int len, dot, path) {
l = len;
d = dot;
p = path;
}
}

View File

@ -57,13 +57,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.0.3"
desktop_window:
dependency: "direct main"
description:
name: desktop_window
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.8.0+1 version: 1.0.0+1
environment: environment:
sdk: ">=2.15.0-100.0.dev<3.0.0" sdk: ">=2.15.0-100.0.dev<3.0.0"
@ -35,7 +35,6 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
desktop_window: ^0.4.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -1,11 +0,0 @@
1
НеОриентированный
Взвешенный
A: 2|7 4|5
B: 1|7 4|9 3|8 5|7
C: 2|8 5|5
D: 1|5 2|9 5|15 6|6
E: 3|5 2|7 4|15 6|8 7|9
F: 4|6 5|8 7|11
G: 6|11 5|9
END

View File

@ -6,9 +6,6 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <desktop_window/desktop_window_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
DesktopWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopWindowPlugin"));
} }

View File

@ -3,7 +3,6 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
desktop_window
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)

View File

@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project); FlutterWindow window(project);
Win32Window::Point origin(10, 10); Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720); Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"graphs", origin, size)) { if (!window.CreateAndShow(L"grafs", origin, size)) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
window.SetQuitOnClose(true); window.SetQuitOnClose(true);

View File

@ -1,915 +0,0 @@
\LoadClass[14pt]{extarticle}
%\RequirePackage[14pt]{extsizes}
\RequirePackage[
a4paper, mag=1000,
left=2.5cm, right=1.5cm, top=2cm, bottom=2cm, bindingoffset=0cm,
headheight=0cm, footskip=1cm, headsep=0cm
]{geometry}
\RequirePackage{setspace}
\RequirePackage{calc}
\RequirePackage{titlesec}
\RequirePackage{titletoc}
\RequirePackage{caption}
\RequirePackage[inline]{enumitem}
% --------------------------------------------------------------------------%
% Input data
% --------------------------------------------------------------------------%
\def\chair#1{\gdef\@chair{#1}}\chair{\hbox to 3cm{\hrulefill}}
\def\worktype#1{\gdef\@worktype{#1}}
\def\worktitle{\@title}
\def\typework#1{\gdef\@typework{#1}}
\def\disserform#1{\gdef\@disserform{#1}}
\def\disserformP#1{\gdef\@disserformP{#1}}
\def\disserformR#1{\gdef\@disserformR{#1}}
\def\disserformV#1{\gdef\@disserformV{#1}}
\def\course#1{\gdef\@course{#1}}\course{2}
\def\group#1{\gdef\@group{#1}}\group{211}
\def\department#1{\gdef\@department{#1}}\department{\cyr\cyrf\cyra\cyrk%
\cyru\cyrl\cyrsftsn\cyrt\cyre\cyrt\cyra\ \CYRK\CYRN\cyri\CYRI\CYRT}
\def\otdelenie#1{\gdef\@otdelenie{#1}}
\def\studentName{\@author}
%\def\studentName#1{\gdef\@studentName{#1}}
\def\satitle#1{\gdef\@satitle{#1}}\satitle{\hbox to 3cm{\hrulefill}}
\def\saname#1{\gdef\@saname{#1}}\saname{\hbox to 3cm{\hrulefill}}
\def\critictitle#1{\gdef\@critictitle{#1}}\critictitle{\hbox to 3cm{\hrulefill}}
\def\criticname#1{\gdef\@criticname{#1}}\criticname{\hbox to 3cm{\hrulefill}}
\def\secrname#1{\gdef\@secrname{#1}}\secrname{\hbox to 3cm{\hrulefill}}
\def\chtitle#1{\gdef\@chtitle{#1}}\chtitle{\hbox to 3cm{\hrulefill}}
\def\chname#1{\gdef\@chname{#1}}\chname{\hbox to 3cm{\hrulefill}}
%\def\year#1{\gdef\@year{#1}}
\def\spectype#1{\gdef\@spectype{#1}}
\def\spectyperod#1{\gdef\@spectyperod{#1}}
\def\workform#1{\gdef\@workform{#1}}
\def\practtype#1{\gdef\@practtype{#1}}\practtype{\cyr\cyru\cyrch\cyre\cyrb%
\cyrn\cyra\cyrya}
\def\term#1{\gdef\@term{#1}}\term{2}
\def\duration#1{\gdef\@duration{#1}}\duration{2}
\def\protnum#1{\gdef\@protnum{#1}}\protnum{\hbox to 1cm{\hrulefill}}
\def\protdate#1{\gdef\@protdate{#1}}\protdate{\hbox to 3cm{\hrulefill}}
\def\practStart#1{\gdef\@practStart{#1}}\practStart{\hbox to 3cm{\hrulefill}}
\def\practFinish#1{\gdef\@practFinish{#1}}\practFinish{\hbox to 3cm{\hrulefill}}
\def\reviewtype#1{\gdef\@reviewtype{#1}}\reviewtype{\CYRO\CYRT%
\CYRZ\CYRERY\CYRV}
\def\patitle#1{\gdef\@patitle{#1}}\patitle{\@satitle}
\def\paname#1{\gdef\@paname{#1}}\paname{\@saname}
\def\napravlenie#1{\gdef\@napravlenie{#1}}\napravlenie{\hbox to 3cm{\hrulefill}}
\def\Napravlenie{\@napravlenie}
\def\studenttitle#1{\gdef\@studenttitle{#1}}\studenttitle{\cyr\cyrs\cyrt%
\cyru\cyrd\cyre\cyrn\cyrt\cyra}
\def\studentdone#1{\gdef\@studentdone{#1}}\studentdone{\cyrp\cyrr\cyro%
\cyrsh\cyre\cyrd\cyrsh\cyre\cyrg\cyro}
\def\studentfemale{\studenttitle{\cyrs\cyrt\cyru\cyrd\cyre\cyrn\cyrt%
\cyrk\cyri}\studentdone{\cyrp\cyrr\cyro\cyrsh\cyre\cyrd\cyrsh%
\cyre\cyrishrt}}
%\newcommand{\MakeTitle}{}
\def\workname#1{\gdef\@workname{#1}}
%\hbox to 3cm{\hrulefill}
% --------------------------------------------------------------------------%
\newcommand{\signature}[2]{
\hbox to 7cm{#1\hfill} \hbox to 3cm{\hrulefill} \hbox to 6cm{\hfill #2}}
\newcommand{\inlinesignature}[2]{%
#1\qquad \hbox to 3cm{\hrulefill}\quad #2}
\newcommand{\signatureline}{}
% --------------------------------------------------------------------------%
\newcommand{\scaleUnivName}{0.97}
\DeclareOption{times}{%
\renewcommand{\rmdefault}{ftm}
\renewcommand{\scaleUnivName}{1.0}
}
\DeclareOption{spec}{%
\spectype{\cyr\cyrs\cyrp\cyre\cyrc\cyri\cyra\cyrl\cyrsftsn\cyrn\cyro%
\cyrs\cyrt\cyri}
\spectyperod{\cyrs\cyrp\cyre\cyrc\cyri\cyra\cyrl\cyrsftsn\cyrn\cyro%
\cyrs\cyrt\cyri}
\workform{\cyr\CYRS\CYRP\CYRE\CYRC\CYRI\CYRA\CYRL\CYRI\CYRS\CYRT\CYRA}
\disserform{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRA\CYRYA\ \CYRR\CYRA%
\CYRB\CYRO\CYRT\CYRA}
\disserformP{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRO\CYRISHRT\ \CYRR%
\CYRA\CYRB\CYRO\CYRT\CYRE}
\disserformR{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRO\CYRISHRT\ \CYRR%
\CYRA\CYRB\CYRO\CYRT\CYRERY}
\disserformV{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRU\CYRYU\ \CYRR\CYRA%
\CYRB\CYRO\CYRT\CYRU}
}
\DeclareOption{bachelor}{%
\spectype{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri\cyrya}
\spectyperod{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri \cyryu}
\workform{\cyr\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRA}
\disserform{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRA%
\CYRYA\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRA}
\disserformP{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRE}
\disserformR{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRERY}
\disserformV{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRU%
\CYRYU\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRU}
}
\DeclareOption{master}{%
\spectype{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri\cyrya}
\spectyperod{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri \cyryu}
\workform{\cyr\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRR\CYRA}
\disserform{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRA%
\CYRYA\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRA}
\disserformP{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRE}
\disserformR{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRERY}
\disserformV{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRU%
\CYRYU\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRU}
}
\DeclareOption{coursework}{%
\worktype{\cyr\CYRK\cyru\cyrr\cyrs\cyro\cyrv\cyra\cyrya\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
\workname{\MakeUppercase{\@worktype}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{documentation}{%
\worktype{\cyr\CYRK\cyru\cyrr\cyrs\cyro\cyrv\cyra\cyrya\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
\workname{\MakeUppercase{}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{diploma}{%
\worktype{\cyr\CYRV\cyrery\cyrp\cyru\cyrs\cyrk\cyrn\cyra\cyrya\ \cyrk\cyrv%
\cyra\cyrl\cyri\cyrf\cyri\cyrk\cyra\cyrc\cyri\cyro\cyrn\cyrn\cyra%
\cyrya\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\worktype{\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
\workname{\MakeUppercase{\@disserform}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{autoref}{%
\workname{\cyr\CYRA\CYRV\CYRT\CYRO\CYRR\CYRE\CYRF\CYRE\CYRR\CYRA\CYRT\ %
\MakeUppercase{\@disserformR}}
\worktype{\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
%\workname{\MakeUppercase{\@disserform}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{nir}{%
\workname{\cyr\CYRO\CYRT\CYRCH\CYRE\CYRT\ \CYRO\ \CYRN\CYRA\CYRU\CYRCH%
\CYRN\CYRO-\CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
\CYRSFTSN\CYRS\CYRK\CYRO\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRE}
\worktype{\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
%\workname{\MakeUppercase{\@disserform}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{pract}{%
\worktype{\cyr\CYRO\cyrt\cyrch\cyre\cyrt\ \cyro\ \cyrp\cyrr\cyra\cyrk\cyrt%
\cyri\cyrk\cyre}
\renewcommand{\maketitle}{\MakeTitlePr}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn}
}
\DeclareOption{review}{%
\reviewtype{\CYRO\CYRT\CYRZ\CYRERY\CYRV}
\worktype{\cyrn\cyra\cyru\cyrch\cyrn\cyro\cyrg\cyro\ \cyrr\cyru\cyrk%
\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrya\ \cyro\ \cyrv\cyrery%
\cyrp\cyru\cyrs\cyrk\cyrn\cyro\cyrishrt\ \cyrk\cyrv\cyra\cyrl\cyri\cyrf%
\cyri\cyrk\cyra\cyrc\cyri\cyro\cyrn\cyrn\cyro\cyrishrt\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyre}
\workname{\cyr\cyrn\cyra\cyru\cyrch\cyrn\cyro\cyrg\cyro\ \cyrr\cyru\cyrk%
\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrya\ \cyro\ \MakeLowercase{\@disserformP}}
%\workname{\@worktype\ \MakeLowercase{\@workform}}
\renewcommand{\maketitle}{\MakeTitleReview}
\renewcommand{\signatureline}{%
\par\noindent%
\CYRN\cyra\cyru\cyrch\cyrn\cyrery\cyrishrt\ \cyrr\cyru\cyrk\cyro\cyrv%
\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\\%
\signature{\@satitle}{\@saname}\\%
}
}
\DeclareOption{assignment}{%
\reviewtype{\CYRZ\CYRA\CYRD\CYRA\CYRN\CYRI\CYRE}
\worktype{\cyrn\cyra\ \cyrv\cyrery\cyrp\cyru\cyrs\cyrk\cyrn\cyru%
\cyryu\ \cyrk\cyrv\cyra\cyrl\cyri\cyrf\cyri\cyrk\cyra\cyrc\cyri\cyro%
\cyrn\cyrn\cyru\cyryu\ \cyrr\cyra\cyrb\cyro\cyrt\cyru}
\workname{\cyr\cyrn\cyra\ \MakeLowercase{\@disserformV}}
%\workname{\@worktype\ \MakeLowercase{\@workform}}
\renewcommand{\maketitle}{\MakeTitleAssign}
\renewcommand{\signatureline}{%
\vfill%
\noindent%
\textbf{\CYRS\cyrr\cyro\cyrk\ \cyrp\cyrr\cyre\cyrd\cyro\cyrs\cyrt\cyra%
\cyrv\cyrl\cyre\cyrn\cyri\cyrya\ \cyrr\cyra\cyrb\cyro\cyrt\cyrery:}\ \@practFinish
\vspace{2em}\raggedright
\noindent \CYRR\cyra\cyrs\cyrs\cyrm\cyro\cyrt\cyrr\cyre\cyrn\cyro\ %
\cyrn\cyra\ \cyrz\cyra\cyrs\cyre\cyrd\cyra\cyrn\cyri\cyri\ \cyrk\cyra%
\cyrf\cyre\cyrd\cyrr\cyrery\ \@chair
\vspace{1em}
\CYRP\cyrr\cyro\cyrt\cyro\cyrk\cyro\cyrl\ \textnumero\ \@protnum\ \cyro%
\cyrt\ \@protdate
\vspace{1em}
\raggedright
\noindent
\inlinesignature{\CYRS\cyre\cyrk\cyrr\cyre\cyrt\cyra\cyrr\cyrsftsn}{\@secrname}
\vspace{2em}
\noindent\raggedright
\CYRD\cyra\cyrt\cyra\ \cyrv\cyrery\cyrd\cyra\cyrch\cyri\ \cyrz\cyra%
\cyrd\cyra\cyrn\cyri\cyrya\ \@practStart
\vspace{1em}
\noindent\raggedright
\inlinesignature{\CYRZ\cyra\cyrd\cyra\cyrn\cyri\cyre\ \cyrp\cyro\cyrl%
\cyru\cyrch\cyri\cyrl}{\hbox to 3cm{\hrulefill}}
\vspace{1cm}
}
}
\DeclareOption{critique}{%
\reviewtype{\CYRR\CYRE\CYRC\CYRE\CYRN\CYRZ\CYRI\CYRYA}
\worktype{\cyrn\cyra\ \cyrv\cyrery\cyrp\cyru\cyrs\cyrk\cyrn\cyru%
\cyryu\ \cyrk\cyrv\cyra\cyrl\cyri\cyrf\cyri\cyrk\cyra\cyrc\cyri\cyro%
\cyrn\cyrn\cyru\cyryu\ \cyrr\cyra\cyrb\cyro\cyrt\cyru}
\workname{\cyr\cyrn\cyra\ \MakeLowercase{\@disserformV}}
%\workname{\@worktype\ \MakeLowercase{\@workform}}
\renewcommand{\maketitle}{\MakeTitleReview}
\renewcommand{\signatureline}{%
\par\noindent%
\CYRR\cyre\cyrc\cyre\cyrn\cyrz\cyre\cyrn\cyrt\\%
\signature{\@critictitle}{\@criticname}\\%
}
}
\DeclareOption{referat}{%
\worktype{\cyr\CYRR\cyre\cyrf\cyre\cyrr\cyra\cyrt}
\workname{\MakeUppercase{\@worktype}}
\renewcommand{\maketitle}{\RefMakeTitle}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn}
}
\DeclareOption{och}{%
\otdelenie{\cyr\cyro\cyrch\cyrn\cyro\cyrishrt\ \cyrf\cyro\cyrr\cyrm%
\cyrery\ \cyro\cyrb\cyru\cyrch\cyre\cyrn\cyri\cyrya}
}
\DeclareOption{zaoch}{%
\otdelenie{\cyr\cyrz\cyra\cyro\cyrch\cyrn\cyro\cyrishrt\ \cyrf\cyro\cyrr%
\cyrm\cyrery\ \cyro\cyrb\cyru\cyrch\cyre\cyrn\cyri\cyrya}
}
\ExecuteOptions{coursework,och,bachelor}
\ProcessOptions
% --------------------------------------------------------------------------%
\newcommand*{\hm}[1]{#1\nobreak\discretionary{}%
{\hbox{$\mathsurround=0pt #1$}}{}}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
\onehalfspacing
\parindent=1.25cm
\pagestyle{headings}
\renewcommand{\@oddhead}{}
\renewcommand{\@oddfoot}{\hfil \thepage}
% --------------------------------------------------------------------------%
% Table and figure numbering by sections
% --------------------------------------------------------------------------%
\newif\if@secNumbering\@secNumberingfalse
\newcommand{\secNumbering}{
\renewcommand{\thefigure}{\arabic{section}.\arabic{figure}}
\renewcommand{\thetable}{\arabic{section}.\arabic{table}}
\renewcommand{\theequation}{\arabic{section}.\arabic{equation}}
\@addtoreset{figure}{section}
\@addtoreset{table}{section}
\@addtoreset{equation}{section}
\@secNumberingtrue
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Table and figure captions
% --------------------------------------------------------------------------%
\def\CaptionName#1{\gdef\@captionname{#1}}
\newlength\tmp %10cm
\setlength{\tmp}{1ex}
\setlength{\belowcaptionskip}{1ex}
\setlength{\abovecaptionskip}{1ex}
\captionsetup[figure]{name=\CYRR\cyri\cyrs\cyru\cyrn\cyro\cyrk, labelsep=endash,
justification=centering, font={small}, skip=\abovecaptionskip, position=below}
\captionsetup[table]{name=\CYRT\cyra\cyrb\cyrl\cyri\cyrc\cyra, labelsep=endash, format=plain,
justification=RaggedRight, singlelinecheck=false, font={small}, position=top}
% --------------------------------------------------------------------------%
% Table of contents
% --------------------------------------------------------------------------%
\renewcommand{\tableofcontents}%
{\structformat\section*{\uppercase{\cyr\CYRS\CYRO\CYRD\CYRE\CYRR\CYRZH\CYRA%
\CYRN\CYRI\CYRE}}\secformat\@starttoc{toc}
\thispagestyle{empty}}
\renewcommand{\@dotsep}{1.5}
\renewcommand{\@pnumwidth}{1.0em}
\newcommand{\l@abcd}[2]{{\@dottedtocline{0}{0pt}{0pt}{#1}{#2}}}
\renewcommand{\l@section}{\@dottedtocline{1}{0em}{1.5em}}
\renewcommand{\l@subsection}{\@dottedtocline{2}{1.5em}{2.3em}}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Sections, subsections
% --------------------------------------------------------------------------%
% Numbering
\renewcommand{\thesection}{\arabic{section}}
\renewcommand{\thesubsection}{\arabic{section}.\arabic{subsection}}
\renewcommand{\thesubsubsection}{\arabic{section}.\arabic{subsection}.\arabic{subsubsection}}
\newcommand{\sectionbreak}{\clearpage}
% Contents, intro, conclusion
\newcommand{\structformat}
{
\titlespacing{\section}
{0cm}{3ex plus 1ex minus .2ex}{1.4ex plus.2ex}
\titleformat{\section}[block]
{\centering\bfseries}
{\thesection}{0ex}{}
}
% Sections, subsections
\newcommand{\secformat}
{
\titlespacing{\section}
{0cm}{3ex plus 1ex minus .2ex}{0.4ex plus.2ex}
\titleformat{\section}[block]
{\hspace{1.25cm}\raggedright\bfseries}
{\thesection}{1ex}{}
}
\newif\if@hyperrefloaded\@hyperrefloadedfalse
\AtBeginDocument{\@ifpackageloaded{hyperref}%
{\@hyperrefloadedtrue}{\@hyperrefloadedfalse}%
}
%\RequirePackage{ifthen}
\newcommand{\starsection}[1]{
\structformat
\section*{#1}%
\if@hyperrefloaded
\phantomsection
\fi
\addcontentsline{toc}{section}{#1}
\setcounter{section}{0}
\secformat
}
\setcounter{section}{0}
\secformat
\newcommand{\intro}{\starsection{\cyr\CYRV\CYRV\CYRE\CYRD\CYRE%
\CYRN\CYRI\CYRE}}
\newcommand{\abbreviations}{\starsection{\CYRO\CYRB\CYRO\CYRZ\CYRN\CYRA%
\CYRCH\CYRE\CYRN\CYRI\CYRYA\ \CYRI\ \CYRS\CYRO\CYRK\CYRR\CYRA\CYRSHCH%
\CYRE\CYRN\CYRI\CYRYA}}
\newcommand{\definitions}{\starsection{\CYRO\CYRP\CYRR\CYRE\CYRD\CYRE%
\CYRL\CYRE\CYRN\CYRI\CYRYA}}
\newcommand{\defabbr}{\starsection{\CYRO\CYRP\CYRR\CYRE\CYRD\CYRE\CYRL%
\CYRE\CYRN\CYRI\CYRYA, \CYRO\CYRB\CYRO\CYRZ\CYRN\CYRA\CYRCH\CYRE\CYRN%
\CYRI\CYRYA\ \CYRI\ \CYRS\CYRO\CYRK\CYRR\CYRA\CYRSHCH\CYRE\CYRN\CYRI\CYRYA}}
\newcommand{\conclusion}{\starsection{\cyr\CYRZ\CYRA\CYRK\CYRL\CYRYU%
\CYRCH\CYRE\CYRN\CYRI\CYRE}}
% Section and subsection parameters
\titlespacing{\section}
{0cm}{3ex plus 1ex minus .2ex}{0.4ex plus.2ex}
\titleformat{\subsection}[block]
{\hspace{1.25cm}\normalfont\bfseries}
{\thesubsection}{1ex}{}
\titlespacing{\subsection}
{0cm}{2ex plus 1ex minus .2ex}{.4ex plus.2ex}
\titleformat{\subsubsection}[block]
{\hspace{1.25cm}\normalfont}
{\thesubsubsection}{1ex}{}
\titlespacing{\subsubsection}
{0cm}{2ex plus 1ex minus .2ex}{.4ex plus.2ex}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
%\AddEnumerateCounter{\Asbuk}{\@Asbuk}{\CYRM}
%\AddEnumerateCounter{\asbuk}{\@asbuk}{\cyrm}
\makeatletter
\def\redeflsection{\def\l@section{\@dottedtocline{1}{0em}{10em}}}
\renewcommand{\appendix}{\par%
\renewcommand{\secNumbering}{
\renewcommand{\thefigure}{\Asbuk{section}.\arabic{figure}}
\renewcommand{\thetable}{\Asbuk{section}.\arabic{table}}
\renewcommand{\theequation}{\Asbuk{section}.\arabic{equation}}
\@addtoreset{figure}{section}
\@addtoreset{table}{section}
\@addtoreset{equation}{section}
}
\if@secNumbering
\secNumbering
\fi
\setcounter{section}{0}%
\setcounter{subsection}{0}%
\renewcommand{\appendixname}{\cyr\CYRP\CYRR\CYRI\CYRL\CYRO\CYRZH\CYRE%
\CYRN\CYRI\CYRE}%
\def\sectionname{\appendixname}%
\addtocontents{toc}{\protect\redeflsection}%
\gdef\thesection{\Asbuk{section}}%
\titlespacing{\section}
%{0cm}{1ex plus 0.1ex minus .2ex}{1.1ex plus.1ex}
{0cm}{3ex plus 1ex minus .2ex}{0.4ex plus.2ex}
\titleformat{\section}[display]
{\centering\normalfont\bfseries}
{\appendixname\hspace{1ex}\thesection}{0ex}{}
\titlecontents{section}
[3ex]
{\hspace{-3ex}}
{\appendixname~\thecontentslabel\hspace{2ex}}
{\hspace{2.3em}}
{\titlerule*[0.98ex]{.}\contentspage}
}
% --------------------------------------------------------------------------%
% Title pages
% --------------------------------------------------------------------------%
%\newcommand{\shapka}{{\centering \CYRM\CYRI\CYRN\CYRO\CYRB\CYRR\CYRN\CYRA%
%\CYRU\CYRK\CYRI\ \CYRR\CYRO\CYRS\CYRS\CYRI\CYRI\\ %
%\CYRF\cyre\cyrd\cyre\cyrr\cyra\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyrg\cyro%
%\cyrs\cyru\cyrd\cyra\cyrr\cyrs\cyrt\cyrv\cyre\cyrn\cyrn\cyro\cyre\ %
%\cyrb\cyryu\cyrd\cyrzh\cyre\cyrt\cyrn\cyro\cyre\ \cyro\cyrb\cyrr\cyra%
%\cyrz\cyro\cyrv\cyra\cyrt\cyre\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyru%
%\cyrch\cyrr\cyre\cyrzh\cyrd\cyre\cyrn\cyri\cyre\ \cyrv\cyrery\cyrs%
%\cyrsh\cyre\cyrg\cyro\ \cyro\cyrb\cyrr\cyra\cyrz%
%\cyro\cyrv\cyra\cyrn\cyri\cyrya\\
%\textbf{<<\CYRS\CYRA\CYRR\CYRA\CYRT\CYRO\CYRV\CYRS\CYRK\CYRI\CYRISHRT\ %
%\CYRN\CYRA\CYRC\CYRI\CYRO\CYRN\CYRA\CYRL\CYRSFTSN\CYRN\CYRERY%
%\CYRISHRT\ \CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
%\CYRSFTSN\CYRS\CYRK\CYRI\CYRISHRT\ %
%\CYRG\CYRO\CYRS\CYRU\CYRD\CYRA\CYRR\CYRS\CYRT\CYRV\CYRE\CYRN\CYRN\CYRERY%
%\CYRISHRT\ \CYRU\CYRN\CYRI\CYRV\CYRE\CYRR\CYRS\CYRI\CYRT\CYRE\CYRT\ %
%\CYRI\CYRM\CYRE\CYRN\CYRI~\CYRN.\,\CYRG.\,\CYRCH\CYRE\CYRR\CYRN\CYRERY%
%\CYRSH\CYRE\CYRV\CYRS\CYRK\CYRO\CYRG\CYRO>>}\\}}
%\newcommand{\shapka}{{\centering \CYRM\CYRI\CYRN\CYRO\CYRB\CYRR\CYRN\CYRA%
%\CYRU\CYRK\CYRI\ \CYRR\CYRO\CYRS\CYRS\CYRI\CYRI\\ \hspace{-1em}%
%\CYRF\cyre\cyrd\cyre\cyrr\cyra\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyrg\cyro%
%\cyrs\cyru\cyrd\cyra\cyrr\cyrs\cyrt\cyrv\cyre\cyrn\cyrn\cyro\cyre\ %
%\cyrb\cyryu\cyrd\cyrzh\cyre\cyrt\cyrn\cyro\cyre\ \cyro\cyrb\cyrr\cyra%
%\cyrz\cyro\cyrv\cyra\cyrt\cyre\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyru%
%\cyrch\cyrr\cyre\cyrzh\cyrd\cyre\cyrn\cyri\cyre\ \\\cyrv\cyrery\cyrs%
%\cyrsh\cyre\cyrg\cyro\ \cyro\cyrb\cyrr\cyra\cyrz%
%\cyro\cyrv\cyra\cyrn\cyri\cyrya\\\hspace{-2em}
%{
%\textbf{<<\CYRS\CYRA\CYRR\CYRA\CYRT\CYRO\CYRV\CYRS\CYRK\CYRI\CYRISHRT\ %
%\CYRN\CYRA\CYRC\CYRI\CYRO\CYRN\CYRA\CYRL\CYRSFTSN\CYRN\CYRERY%
%\CYRISHRT\ \CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
%\CYRSFTSN\CYRS\CYRK\CYRI\CYRISHRT}} \\%
%{\textbf{\CYRG\CYRO\CYRS\CYRU\CYRD\CYRA\CYRR\CYRS\CYRT\CYRV\CYRE\CYRN\CYRN\CYRERY%
%\CYRISHRT\ \CYRU\CYRN\CYRI\CYRV\CYRE\CYRR\CYRS\CYRI\CYRT\CYRE\CYRT}} \\%
%{\textbf{\CYRI\CYRM\CYRE\CYRN\CYRI~\CYRN.\,\CYRG.\,\CYRCH\CYRE\CYRR\CYRN\CYRERY%
%\CYRSH\CYRE\CYRV\CYRS\CYRK\CYRO\CYRG\CYRO>>}}\\}}
\newcommand{\shapka}{{\centering \CYRM\CYRI\CYRN\CYRO\CYRB\CYRR\CYRN\CYRA%
\CYRU\CYRK\CYRI\ \CYRR\CYRO\CYRS\CYRS\CYRI\CYRI\\ %
\CYRF\cyre\cyrd\cyre\cyrr\cyra\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyrg\cyro%
\cyrs\cyru\cyrd\cyra\cyrr\cyrs\cyrt\cyrv\cyre\cyrn\cyrn\cyro\cyre\ %
\cyrb\cyryu\cyrd\cyrzh\cyre\cyrt\cyrn\cyro\cyre\ \cyro\cyrb\cyrr\cyra%
\cyrz\cyro\cyrv\cyra\cyrt\cyre\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyru%
\cyrch\cyrr\cyre\cyrzh\cyrd\cyre\cyrn\cyri\cyre\ \\\cyrv\cyrery\cyrs%
\cyrsh\cyre\cyrg\cyro\ \cyro\cyrb\cyrr\cyra\cyrz%
\cyro\cyrv\cyra\cyrn\cyri\cyrya\\[0.2em]
\centerline{\scalebox{\scaleUnivName}[1.0]{\parbox[t]{1.1\textwidth}
{\centering
\textbf{<<\CYRS\CYRA\CYRR\CYRA\CYRT\CYRO\CYRV\CYRS\CYRK\CYRI\CYRISHRT\ %
\CYRN\CYRA\CYRC\CYRI\CYRO\CYRN\CYRA\CYRL\CYRSFTSN\CYRN\CYRERY%
\CYRISHRT\ \CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
\CYRSFTSN\CYRS\CYRK\CYRI\CYRISHRT\ \\%
\CYRG\CYRO\CYRS\CYRU\CYRD\CYRA\CYRR\CYRS\CYRT\CYRV\CYRE\CYRN\CYRN\CYRERY%
\CYRISHRT\ \CYRU\CYRN\CYRI\CYRV\CYRE\CYRR\CYRS\CYRI\CYRT\CYRE\CYRT\ \\%
\CYRI\CYRM\CYRE\CYRN\CYRI~\CYRN.\,\CYRG.\,\CYRCH\CYRE\CYRR\CYRN\CYRERY%
\CYRSH\CYRE\CYRV\CYRS\CYRK\CYRO\CYRG\CYRO>>}}}}}}
\newcommand{\CDMakeTitle}
{
\thispagestyle{empty}
\shapka
%\vspace{0.5cm}
\begin{center}
%\parbox{8cm}{
%\raggedright
\CYRK\cyra\cyrf\cyre\cyrd\cyrr\cyra\ \@chair
%}
\end{center}
\vspace{14pt}
\vspace{1cm}
{\centering
\textbf{\MakeUppercase{\@title}}
\\[0.3cm]
{\@workname}
}
\vspace{1.5cm}
\begin{flushleft}
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ \@group\ \cyrg%
\cyrr\cyru\cyrp\cyrp\cyrery\\
\@spectype\ \@napravlenie\\
\@department\\
\@author
\end{flushleft}
\vfill
\noindent
\CYRN\cyra\cyru\cyrch\cyrn\cyrery\cyrishrt\ \cyrr\cyru\cyrk\cyro\cyrv%
\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\\
\signature{\@satitle}{\@saname}\\[14pt]
\CYRZ\cyra\cyrv\cyre\cyrd\cyru\cyryu\cyrshch\cyri\cyrishrt\ \cyrk\cyra%
\cyrf\cyre\cyrd\cyrr\cyro\cyrishrt\\
\signature{\@chtitle}{\@chname}
\vfill
{\centering{\cyr\CYRS\cyra\cyrr\cyra\cyrt\cyro\cyrv\ \@date}
}
\newpage
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Title page of internship
% --------------------------------------------------------------------------%
\newcommand{\MakeTitlePr}
{
\thispagestyle{empty}
\begin{center}
\shapka
\end{center}
\vspace{0.5cm}
\begin{flushright}
\parbox{7cm}{
\begin{flushleft}
\CYRU\CYRT\CYRV\CYRE\CYRR\CYRZH\CYRD\CYRA\CYRYU\\
\CYRZ\cyra\cyrv.\cyrk\cyra\cyrf\cyre\cyrd\cyrr\cyro\cyrishrt,\\
\@chtitle\\
\hbox to 7cm{\hrulefill\ \@chname}
\end{flushleft}
}
\end{flushright}
\vspace{1cm}
\begin{center}\textbf{\MakeUppercase{\@worktype}}\end{center}
\begin{flushleft}
\vspace{12pt}
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ \@group\ \cyrg%
\cyrr\cyru\cyrp\cyrp\cyrery\ \@department\\
\@author
\vspace{0.5cm}
\cyrv\cyri\cyrd\ \cyrp\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri: \@practtype\\
\cyrk\cyra\cyrf\cyre\cyrd\cyrr\cyra: \@chair\\
\cyrk\cyru\cyrr\cyrs: \@course\\
\cyrs\cyre\cyrm\cyre\cyrs\cyrt\cyrr: \@term\\
\cyrp\cyrr\cyro\cyrd\cyro\cyrl\cyrzh\cyri\cyrt\cyre\cyrl\cyrsftsn\cyrn%
\cyro\cyrs\cyrt\cyrsftsn: \@duration\ \cyrn\cyre\cyrd., \cyrs\ \@practStart\ \cyrg. \cyrp\cyro\ \@practFinish\ \cyrg.
\end{flushleft}
%\parindent=-0.2cm
\vspace{1cm}
\noindent
\CYRR\cyru\cyrk\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\ \cyrp%
\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri\ \cyro\cyrt\ \cyru\cyrn\cyri\cyrv%
\cyre\cyrr\cyrs\cyri\cyrt\cyre\cyrt\cyra,\\[12pt]%
\signature{\@satitle}{\@saname}\\[14pt]
\CYRR\cyru\cyrk\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\ \cyrp%
\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri\ \cyro\cyrt\ \cyro\cyrr\cyrg\cyra%
\cyrn\cyri\cyrz\cyra\cyrc\cyri\cyri\ (\cyru\cyrch\cyrr\cyre\cyrzh\cyrd%
\cyre\cyrn\cyri\cyrya, \cyrp\cyrr\cyre\cyrd\cyrp\cyrr\cyri\cyrya\cyrt%
\cyri\cyrya),\\[12pt]%
\signature{\@patitle}{\@paname}
\newpage
\thispagestyle{empty}
\vspace*{11cm}
\CYRT\cyre\cyrm\cyra\ \cyrp\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri:<<\@title>>
\parindent=1.25cm
\newpage
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Title page of review
% --------------------------------------------------------------------------%
\newcommand{\MakeTitleReview}
{
\pagestyle{empty}
\begin{center}
\shapka
\end{center}
{
\centering
\textbf{\MakeUppercase{\@reviewtype}}\\[-0.3em]
\textbf{\@workname}\\[0.3em]
<<{\MakeUppercase{\@title}}>>
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ %
\@department\\
\centering
{\@author}\\
\centering
\@studentdone\ \cyro\cyrb\cyru\cyrch\cyre\cyrn\cyri\cyre\ \cyrp%
\cyro\ \@spectyperod\ \@napravlenie
}
\vspace{2em}
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Title page of assignment
% --------------------------------------------------------------------------%
\newcommand{\MakeTitleAssign}
{
\pagestyle{empty}
\begin{center}
\shapka
\end{center}
{
\centering
\CYRK\cyra\cyrf\cyre\cyrd\cyrr\cyra\ \@chair
\vspace{6em}
\centering
\textbf{\MakeUppercase{\@reviewtype}\\%[-0.3em]
\@workname}
\vspace{0.3em}
\raggedright
\cyrp\cyro\ \@spectyperod\ \@napravlenie\\
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ %
\@department\\
\MakeUppercase{\@author}\\
\textbf{\CYRT\cyre\cyrm\cyra\ \cyrr\cyra\cyrb\cyro\cyrt\cyrery:} <<{\MakeUppercase{\@title}}>>
}
\vfill
\noindent
\CYRN\cyra\cyru\cyrch\cyrn\cyrery\cyrishrt\ \cyrr\cyru\cyrk\cyro\cyrv%
\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\\
\signature{\@satitle}{\@saname}\\[14pt]
\CYRZ\cyra\cyrv\cyre\cyrd\cyru\cyryu\cyrshch\cyri\cyrishrt\ \cyrk\cyra%
\cyrf\cyre\cyrd\cyrr\cyro\cyrishrt\\
\signature{\@chtitle}{\@chname}
\vfill
{\centering{\cyr\CYRS\cyra\cyrr\cyra\cyrt\cyro\cyrv\ \@date}
}
\newpage
\begin{center}\bf
C\cyro\cyrd\cyre\cyrr\cyrzh\cyra\cyrn\cyri\cyre\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyrery
\end{center}
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Referat title page
% --------------------------------------------------------------------------%
\newcommand{\RefMakeTitle}
{
\thispagestyle{empty}
\shapka
\vspace{3cm}
{\centering
\textbf{\MakeUppercase{\@title}}
\\[0.3cm]
{\@workname}
}
\vspace{1.5cm}
\begin{flushleft}
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ \@group\ \cyrg%
\cyrr\cyru\cyrp\cyrp\cyrery\\
\@spectype\ \@napravlenie\\
\@department\\
\@author
\end{flushleft}
\vfill
\noindent
\CYRP\cyrr\cyro\cyrv\cyre\cyrr\cyri\cyrl\\
\signature{\@satitle}{\@saname}
\vfill
{\centering{\cyr\CYRS\cyra\cyrr\cyra\cyrt\cyro\cyrv\ \@date}
}
\newpage
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Last page
% --------------------------------------------------------------------------%
\newcommand{\lastpage}
{
\newpage
\thispagestyle{empty}
\vspace*{11cm}
\@worktype\ <<\@title>>\ \@typework\ \cyrm\cyrn\cyro\cyrishrt\ %
\cyrs\cyra\cyrm\cyro\cyrs\cyrt\cyro\cyrya\cyrt\cyre\cyrl\cyrsftsn\cyrn%
\cyro, \cyri\ \cyrn\cyra\ \cyrv\cyrs\cyre\ \cyri\cyrs\cyrt\cyro\cyrch%
\cyrn\cyri\cyrk\cyri, \cyri\cyrm\cyre\cyryu\cyrshch\cyri\cyre\cyrs%
\cyrya\ \cyrv\ \cyrr\cyra\cyrb\cyro\cyrt\cyre, \cyrd\cyra\cyrn\cyrery\ %
\cyrs\cyro\cyro\cyrt\cyrv\cyre\cyrs\cyrt\cyrv\cyru\cyryu\cyrshch\cyri%
\cyre\ \cyrs\cyrs\cyrery\cyrl\cyrk\cyri.\par
\parindent=9cm
\parbox{8cm}{
\begin{flushleft}
\hbox to 6cm{\hbox to 3.5cm{\hrulefill}/\hbox to 3.5cm{\hrulefill}/}
\end{flushleft}
}
}
\AddEnumerateCounter{\Asbuk}{\@Asbuk}{\CYRM}
\AddEnumerateCounter{\asbuk}{\@asbuk}{\cyrm}
% --------------------------------------------------------------------------%
% enumerations
% --------------------------------------------------------------------------%
\setlist{noitemsep}
%\setlist[1]{labelindent=\parindent} % < Usually a good idea
\setlist[itemize]{
%leftmargin=52pt,
rightmargin=0pt,
labelsep=7pt,
labelwidth=20pt,
itemindent=0pt,
listparindent=0pt,
topsep=0pt,%4pt plus 2pt minus 4pt,
partopsep=0pt,% plus 1pt minus 1pt,
parsep=0pt,% plus 1pt,
itemsep=0 pt%\parsep
}
\setlist[enumerate]{
%leftmargin=52pt,
rightmargin=0pt,
labelsep=5pt,
labelwidth=20pt,
itemindent=0pt,
listparindent=0pt,
topsep=0pt,%4pt plus 2pt minus 4pt,
partopsep=0pt,% plus 1pt minus 1pt,
parsep=0pt,% plus 1pt,
itemsep=0pt%\parsep
}
\setlist[itemize,1]{label={\normalfont\bfseries\textemdash}}
%\setlist[enumerate]{labelsep=*, leftmargin=1.5pc}
\setlist[enumerate,1]{label=\arabic*., ref=\arabic*}
\setlist[enumerate,2]{label=\emph{\asbuk*}), ref=\theenumi.\emph{\asbuk*}}
\setlist[enumerate,3]{label=\roman*., ref=\theenumii.\roman*}
\setlist[enumerate,4]{label=\Asbuk*., ref=\theenumiii.\Asbuk*}
%\setlist[description]{font=\sffamily\bfseries}
%%%\renewcommand{\@listI}{%
%%%\leftmargin=52pt
%%%\rightmargin=0pt
%%%\labelsep=7pt
%%%\labelwidth=20pt
%%%\itemindent=0pt
%%%\listparindent=0pt
%%%\topsep=4pt plus 2pt minus 4pt
%%%\partopsep=0pt plus 1pt minus 1pt
%%%\parsep=0pt plus 1pt
%%%\itemsep=\parsep}
%%%\renewcommand\theenumi {\@arabic\c@enumi}
%%%\renewcommand\theenumii {\asbuk{enumii}}
%%%\renewcommand\theenumiii{\@roman\c@enumiii}
%%%\renewcommand\theenumiv {\Asbuk{enumiv}}
%%%\newcommand\atheenumi{\asbuk{enumi}}
%%%\newcommand\atheenumii{\asbuk{enumii}}
%%%\renewcommand\labelenumi {\theenumi.}
%%%\renewcommand\labelenumii {\theenumii.}
%%%\renewcommand\labelenumiii{\theenumiii.}
%%%\renewcommand\labelenumiv {\theenumiv.}
%%%\renewcommand\p@enumii {\theenumi}
%%%\renewcommand\p@enumiii {\theenumi.\theenumii}
%%%\renewcommand\p@enumiv {\p@enumiii.\theenumiii}
%%%\renewcommand\labelitemi {\normalfont\bfseries\textemdash}
%%%\renewcommand\labelitemii {\normalfont\bfseries\textendash}
%%%\renewcommand\labelitemiii{\textperiodcentered}
%%%\renewcommand\labelitemiv {\textasteriskcentered}
%%%
%%%\renewcommand{\@listI}{%
%%%\leftmargin=52pt
%%%\rightmargin=0pt
%%%\labelsep=7pt
%%%\labelwidth=20pt
%%%\itemindent=0pt
%%%\listparindent=0pt
%%%\topsep=4pt plus 2pt minus 4pt
%%%\partopsep=0pt plus 1pt minus 1pt
%%%\parsep=0pt plus 1pt
%%%\itemsep=\parsep}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% References
% --------------------------------------------------------------------------%
\makeatletter
\def\@biblabel#1{#1 }
\renewenvironment{thebibliography}[1]
{
\starsection{\cyr\CYRS\CYRP\CYRI\CYRS\CYRO\CYRK\ \CYRI\CYRS\CYRP\CYRO\CYRL%
\CYRSFTSN\CYRZ\CYRO\CYRV\CYRA\CYRN\CYRN\CYRERY\CYRH\ \CYRI\CYRS\CYRT%
\CYRO\CYRCH\CYRN\CYRI\CYRK\CYRO\CYRV}
\list{\@biblabel{\@arabic\c@enumiv}}%
{\settowidth\labelwidth{\@biblabel{#1}}%
\leftmargin\labelwidth
\advance\leftmargin\labelsep
\setlength{\itemsep}{0pt}
\@openbib@code
\usecounter{enumiv}%
\let\p@enumiv\@empty
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
\sloppy
\clubpenalty4000
\@clubpenalty \clubpenalty
\widowpenalty4000%
\sfcode`\.\@m}
{\def\@noitemerr
{\@latex@warning{Empty `thebibliography' environment}}%
\endlist}
\makeatother
% --------------------------------------------------------------------------%

View File

@ -1,25 +0,0 @@
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('Hello World'),
]),
)),
);
}
}

View File

@ -1,52 +0,0 @@
List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
startDot--;
goalDot--;
List<List<int>>? graph = getLenTable();
List<bool> used = <bool>[];
List<int> dst = <int>[];
List<int> pr = <int>[];
for (int i = 0; i < _amount; i++) {
dst.add(-1);
used.add(false);
pr.add(0);
}
List<int> q = <int>[];
q.add(startDot);
used[startDot] = true;
dst[startDot] = 0;
pr[startDot] = -1; //ó âåðøèíû íåò ïðåäûäóùåé.
while (q.isNotEmpty) {
int cur = q.removeAt(0);
int x = 0;
for (int neighbor in graph![cur]) {
if (neighbor != -1) {
if (!used[x]) {
q.add(x);
used[x] = true;
dst[x] = dst[cur] + 1;
pr[x] = cur; //ñîõðàíåíèå ïðåäûäóùåé âåðøèíû
}
}
x++;
}
}
//Âîññòàíîâëåíèå êðàò÷àéøèåãî ïóòüè
List<int> path = <int>[];
int cur = goalDot;
path.add(cur + 1);
while (pr[cur] != -1) {
cur = pr[cur];
path.add(cur + 1);
}
path = path.reversed.toList();
if (path[0] == (startDot + 1) &&
path[1] == (goalDot + 1) &&
!_dots[startDot].hasConnection(goalDot + 1)) return null;
return path;
}

View File

@ -1,19 +0,0 @@
List<bool>? dfsIterative(int v) {
v--;
List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) {
label.add(false);
}
List<int> stack = <int>[];
stack.add(v);
while (stack.isNotEmpty) {
v = stack.removeLast();
if (!label[v]) {
label[v] = true;
for (int i in _dots[v].getL().keys) {
stack.add(i - 1);
}
}
}
return label;
}

View File

@ -1,29 +0,0 @@
List<int?> dijkstra(int from) {
List<int?> d = List<int?>.filled(_amount, intMax);
List<int> p = List<int>.filled(_amount, -1);
d[from - 1] = 0;
List<bool> u = List<bool>.filled(_amount, false);
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
if (!u[j] && (v == -1 || d[j]! < d[v]!)) {
v = j;
}
}
if (d[v] == intMax) break;
u[v] = true;
for (int to in _dots[v].getL().keys) {
int len = _dots[v].getL()[to]!;
if (!_useLength && len == 0) len = 1;
if (d[v]! + len < d[to - 1]!) {
d[to - 1] = d[v]! + len;
p[to - 1] = v;
}
}
}
for (int i = 0; i < d.length; i++) {
if (d[i] == intMax) d[i] = null;
}
return d;
}

View File

@ -1,6 +0,0 @@
class Dot {
//Data
String _name = "";
int num = -1;
Map<int, int> _ln = <int, int>{};
***

View File

@ -1,24 +0,0 @@
Dot([String name = "Undefined", int n = -1]) {
_name = name;
num = n;
_ln = <int, int>{};
}
Dot.fromTwoLists(String name, List<int> num0, List<int> length,
[int n = -1]) {
_name = name;
num = n;
Map<int, int> nw = <int, int>{};
if (num0.length != length.length) {
print("Error in lists");
} else {
for (var i = 0; i < num0.length; i++) {
nw[num0[i]] = length[i];
_ln = nw;
}
}
}
Dot.fromMap(String name, Map<int, int> l, [int n = -1]) {
_name = name;
num = n;
_ln = l;
}

View File

@ -1,5 +0,0 @@
void setName(String n) => _name = n;
void addPath(int inp, int length) => _ln[inp] = length;
void delPath(int n) => _ln.removeWhere((key, value) => key == n);

View File

@ -1,6 +0,0 @@
String _name = "Undefined"; //Èìÿ
int _amount = 0; //Êîëè÷åñòâî âåðøèí
List<Dot> _dots = <Dot>[]; //Ñïèñîê ñìåæíîñòè âåðøèí
Map<int, String> _nameTable = <int, String>{}; //Ñïèñîê âåðøèí ïî èìåíàì
bool _useLength = false; //Âçâåøåííîñòü
bool _oriented = false; //Îðèåíòèðîâàííîñòü

View File

@ -1,28 +0,0 @@
Graphs? kruskal() {
List<LenDotPath> g = getSortedPathList();
//int cost = 0;
List<Dot> res = <Dot>[];
for (int i = 0; i < _amount; i++) {
res.add(Dot(_dots[i].getName(), _dots[i].num));
}
List<int> treeId = List<int>.filled(_amount, 0);
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
}

View File

@ -1 +0,0 @@
void main() => print("Hello World");

View File

@ -1,3 +0,0 @@
void main() {
print("Hello World");
}

View File

@ -1,58 +0,0 @@
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
_textGrNmController.text = graphData.getName();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Align(
alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.green.shade900,
child: Column(children: <Widget>[
/*
Îïèñàíèå ýëåìåíòîâ íà ïàíåëå ïðèëîæåíèÿ
*/
]),
),
actions: [
IconButton(
onPressed: () {
setState(() {
clearDropDownVals();
graphData.flushData();
clearInputData();
});
},
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(
graphData: graphData,
intListPath: intListPath,
dfsAccessTable: dfsAccessTable,
start: startDot,
end: endDot,
op: op),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
/*
Îïèñàíèå êíîïîê äåéñòâèÿ
*/
],
),
),
),
));
}

View File

@ -1,14 +0,0 @@
ElevatedButton createButton(String txt, void Function() onPressing) {
return ElevatedButton(
onPressed: onPressing,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(states) => Colors.green.shade700)),
child: Text(txt,
style: const TextStyle(
fontSize: 15,
color: Colors.white70,
height: 1,
)),
);
}

View File

@ -1,22 +0,0 @@
void _drawHArrow(Canvas canvas, Size size, Offset from, Offset to, [bool doubleSided = false]) {
Path path;
// The arrows usually looks better with rounded caps.
Paint paint = ... // çàäàåòñÿ ñòèëü
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
path = Path();
path.moveTo(from.dx, from.dy);
path.relativeCubicTo(
0,
0,
-(from.dx + to.dx + length) / (length) - 40,
-(from.dy + to.dy + length) / (length) - 40,
to.dx - from.dx,
to.dy - from.dy);
path =
ArrowPath.make(path: path, isDoubleSided: doubleSided, tipLength: 16);
canvas.drawPath(path, paint);
}

View File

@ -1,9 +0,0 @@
CurvePainter({
Key? key,
required this.graphData,
required this.intListPath,
required this.dfsAccessTable,
required this.start,
required this.end,
required this.op,
});

View File

@ -1,24 +0,0 @@
Map<int, Offset> _getDotPos(int dotsAm, Size size) {
Map<int, Offset> off = <int, Offset>{};
var width = size.width / 2;
var height = size.height / 2;
int add = 0;
int h = _getHighInputConnections();
for (int i = 0; i < dotsAm; i++) {
if ((i + 1) != h) {
double x =
cos(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + width;
double y =
sin(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + height;
off[i + 1] = Offset(x, y);
} else if ((i + 1) == h) {
off[i + 1] = Offset(width + 2, height - 2);
add = 1;
h = 0;
} else {
print("GetDotPos error");
}
}
return off;
}

View File

@ -1,10 +0,0 @@
BFS(G, s):
queue Q
s <- visited
Q.add(s)
while (Q.not_empty):
u = Q.pop
for a in u.connections:
if a is unvisited:
Q.add(a)
a <- visited

View File

@ -1,12 +0,0 @@
function doDfs(G[n]: Graph):
visited = array[n, false]
function DFS(u: int):
visited[u] = true
for v: (u, v) in G:
if not visited[v]:
DFS(v)
for i = 1 to n:
if not visited[i]:
DFS(i)

View File

@ -1,9 +0,0 @@
dijkstra (G, w, s):
source(G,s)
S <- null
Q <- V[G]
while Q!=null:
u <- Extract_min(Q)
s <- S + u
äë˙ ęŕćäîé âĺđřčíű v čç Adj[u]:
relax(u, v, w)

View File

@ -1,10 +0,0 @@
kruscal(G, w):
A <- null
for v èç V[G]:
create_set(v)
sort(E)
for (u, v) èç E:
if find_set(u) != find_set(v):
A <- A + {(u,v)}
return A

View File

@ -1,491 +0,0 @@
\documentclass[bachelor, och, coursework]{SCWorks}
% параметр - тип обучения - одно из значений:
% spec - специальность
% bachelor - бакалавриат (по умолчанию)
% master - магистратура
% параметр - форма обучения - одно из значений:
% och - очное (по умолчанию)
% zaoch - заочное
% параметр - тип работы - одно из значений:
% referat - реферат
% coursework - курсовая работа (по умолчанию)
% diploma - дипломная работа
% pract - отчет по практике
% pract - отчет о научно-исследовательской работе
% autoref - автореферат выпускной работы
% assignment - задание на выпускную квалификационную работу
% review - отзыв руководителя
% critique - рецензия на выпускную работу
% параметр - включение шрифта
% times - включение шрифта Times New Roman (если установлен)
% по умолчанию выключен
\usepackage[T2A]{fontenc}
\usepackage[cp1251]{inputenc}
\usepackage{graphicx}
\usepackage{minted}
\usepackage{float}
\usepackage[sort,compress]{cite}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsthm}
\usepackage{fancyvrb}
\usepackage{longtable}
\usepackage{array}
\usepackage[english,russian]{babel}
\usepackage[colorlinks=true]{hyperref}
\usepackage{caption}
\captionsetup[figure]{font=normalsize,labelfont=normalsize}
%\newcommand{\eqdef}{\stackrel {\rm def}{=}}
%\newtheorem{lem}{Лемма}
\begin{document}
% Кафедра (в родительном падеже)
\chair{дискретной математики и информационных технологий}
% Тема работы
\title{Создание приложения для отрисовки графов и алгоритмов для работы с ними}
% Курс
\course{2}
% Группа
\group{221}
% Факультет (в родительном падеже) (по умолчанию "факультета КНиИТ")
%\department{факультета КНиИТ}
% Специальность/направление код - наименование
%\napravlenie{02.03.02 "--- Фундаментальная информатика и информационные технологии}
%\napravlenie{02.03.01 "--- Математическое обеспечение и администрирование информационных систем}
\napravlenie{09.03.01 "--- Информатика и вычислительная техника}
%\napravlenie{09.03.04 "--- Программная инженерия}
%\napravlenie{10.05.01 "--- Компьютерная безопасность}
% Для студентки. Для работы студента следующая команда не нужна.
%\studenttitle{Студентки}
% Фамилия, имя, отчество в родительном падеже
\author{Морозова Андрея Денисовича}
% Заведующий кафедрой
\chtitle{к.\,ф.-м.\,н., доцент} % степень, звание
\chname{Л.\,Б.\,Тяпаев}
%Научный руководитель (для реферата преподаватель проверяющий работу)
\satitle{доцент каф. ДМиИТ} %должность, степень, звание
\saname{О.\,В.\,Мещерякова}
% Семестр (только для практики, для остальных
% типов работ не используется)
\term{2}
% Год выполнения отчета
\date{2021}
\maketitle
% Включение нумерации рисунков, формул и таблиц по разделам
% (по умолчанию - нумерация сквозная)
% (допускается оба вида нумерации)
%\secNumbering
\tableofcontents
% Раздел "Обозначения и сокращения". Может отсутствовать в работе
%\abbreviations
%\begin{description}
% \item SQL "--- англ. structured query language — «язык структурированных запросов;
% \item $\det B$ "--- определитель матрицы $B$;
% \item ИНС "--- Искусственная нейронная сеть;
% \item FANN "--- Feedforward Artifitial Neural Network
%\end{description}
% Раздел "Определения". Может отсутствовать в работе
%\definitions
% Раздел "Определения, обозначения и сокращения". Может отсутствовать в работе.
% Если присутствует, то заменяет собой разделы "Обозначения и сокращения" и "Определения"
%\defabbr
% Раздел "Введение"
\intro
Целью настоящей работы является изучение работы фреймворка для кроссплатформенной разработки ''Flutter'' и разработка приложения для создания графов и взаимодействия с ними.
Поставлены задачи:
\begin{itemize}
\item разбор алгоритмов на графах;
\item разбор работы с Flutter;
\item построение приложения.
\end{itemize}
\section{Введение}
\subsection{Графы}
Граф --- математический объект, состоящий из двух множеств. Одно из
них --- любое конечное множество, его элементы называются \textit{вершинами}
графа. Другое множество состоит из пар вершин, эти пары называются
\textit{ребрами} графа~\cite{IITMMM_2017}.
\subsection{Основные определения}
\textbf{Ориентированный граф} определяется как пара \textit{(V, E)}, где \textit{V} --- конечное множество, а \textit{E} --- бинарное отношение на \textit{V}, т.~е. подмножество множества ${V \times V}$. Ориентированный граф для краткости называют \textbf{орграфом}. Множество $V$ называют \textbf{множеством вершин графа}, а его элемент называют \textbf{вершиной} графа. Множество $E$ называют \textbf{множеством рёбер}, а его элементы называют \textbf{рёбрами}. Граф может содержать \textbf{рёбра-циклы}, соединяющие вершину с собой. На рисунке~\ref{fig:orgrapf_example} изображен ориентированный граф с множеством вершин \{0, 1, 2, 3, 4\}.~\cite{Algo_2013}
\begin{figure}[!ht]
\centering
\includegraphics[width=9cm]{./pic/orgraph.png}
\caption{\label{fig:orgrapf_example}
Пример орграфа}
\end{figure}
В \textbf{неориентированном} графе $G = (V, E)$ множество ребер состоит из \textbf{неупорядоченных} пар вершин: парами являются множества $\{u, v\}$, где $u, v \in V$ и $u \neq v$. Для неориентированного графа $\{u, v\}$ и $\{v, u\}$ обозначают одно и то же ребро. Неориентированный граф не может содержать рёбер-циклов, и каждое ребро состоит из двух различных вершин. На рисунке~\ref{fig:grapf_example} изображен неориентированный граф с множеством вершин \{0, 1, 2, 2, 4\}
\begin{figure}[!ht]
\centering
\includegraphics[width=9cm]{./pic/graph.png}
\caption{\normalsize\label{fig:grapf_example}
Пример неориентированного графа}
\end{figure}
Вершина \textit{v} \textbf{смежна} с вершиной \textit{u}, если в графе имеется ребро $\{u, v\}$. Для неориентированных графов отношение смежности является симметричным, но для ориентированных графов это не обязательно.
\textbf{Степенью} вершины в неориентированном графе называется число инцидентных ей рёбер. Для ориентированного графа различают исходящую степень, определяемую как число выходящих из неё рёбер, и входящую степень, определяемую как число входящих в неё рёбер. Сумма исходящей и входящей степеней называется степенью вершины.
\textbf{Маршрутом} в графе называется конечная чередующаяся последовательность смежных вершин и ребер, соединяющих эти вершины.
Маршрут называется открытым, если его начальная и конечная вершины различны, в противном случае он называется замкнутым.
Маршрут называется \textbf{цепью}, если все его ребра различны. Открытая цепь называется \textbf{путем}, если все ее вершины различны.
Замкнутая цепь называется \textbf{циклом}, если различны все ее вершины, за исключением концевых.
Граф называется \textbf{связным}, если для любой пары вершин существует соединяющий их путь.~\cite{intuit}
\subsection{Представление графа}
Граф можно описать несколькими способами.
\begin{enumerate}
\item Перечисление элементов:~\ref{ref:list}.
\item Матрица смежности:~\ref{ref:smej}.
\item Матрица инцидентности:~\ref{ref:incident}.
\item Список смежности:~\ref{ref:spisok}.
\end{enumerate}
Рассмотрим на примере графа на рисунке~\ref{fig:graph_prim1}.
\begin{figure}
\centering
\includegraphics[width=9cm]{./pic/prim1.png}
\caption{\label{fig:graph_prim1}
Рассматриваемый граф}
\end{figure}
\subsubsection{Перечисление элементов}\label{ref:list}
Исходя из определения, для того, чтобы задать граф, достаточно перечислить его вершины и ребра (т.е. пары вершин).
Пример:
$$ V = \{a, b, c, d, e\} $$
$$ E = \{(a, b), (a, c), (a, e), (b, c), (b, d), (c, e), (d, e)\}$$
\subsubsection{Матрица смежности}\label{ref:smej}
Пусть $G$ --- граф с $n$ вершинами, пронумерованными числами от 1 до $n$. \textbf{Матрица смежности} --- это таблица с $n$ строками и $n$ столбцами, в которой элемент, стоящий на пересечении строки с номером $i$ и столбца с номером $j$, равен 1, если вершины с номерами $i$ и $j$ смежны, и 0 в противоположном случае.
\begin{table}[!ht]
%\centering
\caption{Пример матрицы смежности}
\begin{tabular}{|l|c|c|c|c|}
\hline 0 & 1 & 1 & 0 & 1\\
\hline 1 & 0 & 1 & 1 & 0\\
\hline 1 & 1 & 0 & 0 & 1\\
\hline 0 & 1 & 0 & 0 & 1\\
\hline 1 & 0 & 1 & 1 & 0\\
\hline
\end{tabular}
\end{table}
\subsubsection{Матрица инцидентности}\label{ref:incident}
Пусть $G$ --- граф с вершинами, пронумерованными числами от 1 до $n$, и ребрами, пронумерованными от 1 до $m$. В матрице инцидентности строки соответствуют вершинам, а столбцы рёбрам. На пересечении строки с номером $i$ и столбца с номером $j$ стоит 1, если вершина с номером $i$ инцидентна ребру с номером $j$, и 0 в противном случае.
\begin{table}[!ht]
%\centering
\caption{Пример матрицы инцидентности}
\begin{tabular}{|c|c|c|c|c|c|c|}
\hline 1 & 1 & 1 & 0 & 0 & 0 & 0\\
\hline 1 & 0 & 0 & 1 & 1 & 0 & 0\\
\hline 0 & 1 & 0 & 1 & 0 & 1 & 0\\
\hline 0 & 0 & 0 & 0 & 1 & 0 & 1\\
\hline 0 & 0 & 1 & 0 & 0 & 1 & 1\\
\hline
\end{tabular}
\end{table}
\subsubsection{Списки смежности}\label{ref:spisok}
Списки смежности часто используются для компьютерного представления графов. Для каждой вершины задается список всех смежных с ней вершин. В структурах данных, применяемых в программировании, списки смежности могут быть реализованы как массив линейных списков.
Пример:
\begin{itemize}
\item[1] : 2, 3, 5
\item[2] : 1, 3, 4
\item[3] : 1, 2, 5
\item[4] : 2, 5
\item[5] : 1, 3, 4
\end{itemize}
\subsection{Алгоритмы}
\subsubsection{Обход в ширину}
Обход в ширину --- один из основных алгоритмов на графах.
Пусть задан граф $G = (V, E)$ и выделена исходная вершина $s$. Алгоритм поиска в ширину систематически обходит все ребра $G$ для «открытия» всех вершин, достижимых из $s$, вычисляя минимальное количество рёбер от $s$ s до каждой достижимой из $s$ вершины.
Поиск в ширину имеет такое название потому, что в процессе обхода мы идём вширь, то есть перед тем как приступить к поиску вершин на расстоянии $k + 1$, выполняется обход вершин на расстоянии $k$~\cite{Algo_2013}.
Приведенная ниже процедура поиска в ширину BFS предполагает, что входной граф $G = (V, E)$ представлен при помощи списков смежности. Псевдокод алгоритма:
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoBFS.txt}
Воспользуемся групповым анализом. Операции внесения в очередь и удаление из нее требует $O(1)$ времени. Следовательно на очередь потребуется $O(V)$ времени. Т.к. максимальная длина списков смежности $\theta(E)$, то время, необходимое для сканирования списков, равно $O(E)$. Расходы на инициализацию: $O(V)$. Таким образом, общее время работы алгоритма: $O(V + E)$. Время поиска в ширину линейно зависит от размера представления графа.
\subsubsection{Обход в глубину}
Стратегия обхода в ширину состоит в том, чтобы идти ''вглубь'' графа. Сначала исследуются ребра, выходящие из вершины, открытой последней, и покидаем вершину, когда не остается неисследованных ребер --- при этом происходит возврат в вершину, из которой была открыта вершина $v$. Процесс продолжается, пока не будут открыты все вершины, достижимые из исходной.
Псевдо код алгоритма:
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoDFS.txt}
\subsubsection{Алгоритм Дейкстры}
Алгоритм был предложен голландским исследователем Эдсгером Дейкстрой в 1959 году. Используется для поиска кратчайшего пути от одной вершины до всех остальных. Используется только если вес ребер неотрицательный.
В алгоритме поддерживается множество вершин $U$, для которых уже вычислены длины кратчайших путей до них из $s$. На каждой итерации основного цикла выбирается вершина, которой на текущий момент соответствует минимальная оценка кратчайшего пути. Вершина $u$ добавляется в множество $U$ и производится релаксация всех исходящих из неё рёбер.
Псевдокод алгоритма:
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoDijkstra.txt}
\subsubsection{Алгоритм Краскала}
Алгоритм Краскала — алгоритм поиска минимального остовного дерева во взвешенном неориентированном связном графе. Был описан Джозефом Краскалом в 1956 году.
Алгоритм Краскала изначально помещает каждую вершину в своё дерево, а затем постепенно объединяет эти деревья, объединяя на каждой итерации два некоторых дерева некоторым ребром. Перед началом выполнения алгоритма, все рёбра сортируются по весу (в порядке неубывания). Затем начинается процесс объединения: перебираются все рёбра от первого до последнего (в порядке сортировки), и если у текущего ребра его концы принадлежат разным поддеревьям, то эти поддеревья объединяются, а ребро добавляется к ответу. По окончании перебора всех рёбер, все вершины окажутся принадлежащими одному поддереву.\cite{krusc}
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoKruscal.txt}
\section{Инструменты}
Рассмотрим используемый язык и библиотеку для отрисовки.
\subsection{Dart}
В качестве основы используется язык \textbf{Dart}, разработанный компанией Google, и широко используемый для кросс-платформенной разработки~\cite{dart_web}.
Dart позиционируется в качестве замены/альтернативы JavaScript. Один из разработчиков языка Марк Миллер (Mark S. Miller) писал, что JavaScript «имеет фундаментальные изъяны»\cite{futureOfJavascript}, которые невозможно исправить.
Версия 1.0 вышла в 14 ноября 2013 года.
Вторая версия была выпущена в августе 2018 года. В языке появилась надежная система типов, т.~е. во время выполнении программы все переменные будут гарантированно указанному типу~\cite{dart_sound}.
В Dart используется сборщик мусора. Синтаксис похож на языки: JavaScript, C\#, Java~\cite{dartInAction}.
Пример HelloWorld на Dart:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/helloWorld.dart}
Dart поддерживает сокращенную запись. Код примера HelloWorld можно записать так:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/hW.dart}
Концепты языка~\cite{dart_tour}:
\begin{itemize}
\item Все, что можно поместить в переменную, является объектом, а каждый объект является частью класса.
\item Dart -- язык со строгой типизацией, но объявления типов опциональны, т.к. поддерживается определение типа при компиляции.
\item В версии языка 2.12 появилась Null безопасность. Каждый объект не может быть Null, если не указано обратное.
\item Поддерживаются дженерики.
\item Для объявления локальных функций и переменных необходимо начать имя со знака ''\_''.
\end{itemize}
.
\subsection{Flutter}
Для отрисовки информации используется фреймворк c открытым исходным кодом ''Flutter'', разработанный компанией Google.
Flutter не использует нативные компоненты для отрисовки интерфейса. В его основе лежит графический движок ''Skia'', написанный преимущественно на С++.
Skia --- библиотека для работы с 2D графикой с открытым исходным кодом, поддерживающая работу на большом количестве платформ~\cite{skia_docs}
Первая версия выпущена в 2015 году под названием ''Sky'', работала только для Android-приложений. Основная заявленная особенность --- высокая графическая производительность (возможность отображения 120 кадров в секунду). Полная поддержка создания веб-приложений появилась в версии 2.0 (март 2021 года), также разрабатывается поддержка создания настольных приложений для Windows, macOS и Linux и Google Fuchsia.
Простейший пример приложения с использованием Flutter:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/flutter.dart}
Из кода видно, что интерфейс описывается с помощью виджетов.
Результат приложения изображен на рисунке~\ref{fig:flutter_example}.
\newpage
\begin{figure}[!ht]
\centering
\includegraphics[width=11cm]{./pic/flutter0.png}
\caption{\label{fig:flutter_example}
Простейшее приложение на Flutter}
\end{figure}
\section{Реализация}
Структура программы разбита на 4 файла:
\begin{itemize}
\item \textbf{main.dart} --- точка входа в программу;
\item \textbf{drawing\_page.dart} --- страница с описанием работы кнопок;
\item \textbf{curve\_painter.dart} --- функционал для отрисовки графа;
\item \textbf{graph.dart} --- класс для хранения графа и манипуляции с ним.
\end{itemize}
\subsection{Графы}
Информация хранится в виде списков смежности.
Полный код можно посмотреть в приложении~\ref{graph}.
\subsubsection{Класс для хранения вершины}
Класс \textbf{Dot} используется для хранения информации о ребрах, исходящих из данной вершины.
Основная информация:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot.dart}
\textit{String \_name} используется для хранения имени, \textit{int num} используется для хранения номера вершины.
Информация о ребрах задается в виде \textit{Map<int, int> \_ln}, где каждой вершине можно присвоить вес ребра.
Создать вершину можно тремя способами:
\begin{enumerate}
\item пустая точка;
\item из двух списков, где в первом список вершин, а во втором - длины пути;
\item из \textit{Map<int, int>}.
\end{enumerate}
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot_constr.dart}
Для манипуляций с информацией доступно:
\begin{enumerate}
\item изменение имени;
\item добавление пути;
\item удаление пути.
\end{enumerate}
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot_manip.dart}
\subsubsection{Класс для графов}
Класс \textbf{Graphs} используется для хранения точек и манипуляции с ними.
Основная информация:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/graph.dart}
\textit{String \_name} исользуется для имени графа, \textit{int \_amount} --- количество вершин, \textit{List<Dot> \_dots} --- массив точек, \textit{Map<int, String> \_nameTable} --- отслеживание имени каждой вершины, \textit{bool \_useLength} --- отслеживание взвешенности графа, \textit{bool \_oriented} --- отслеживание ориентированности графа.
Для работы графом доступны функции:
\begin{enumerate}
\item \textbf{String? addDot(Dot a)} --- добавление вершины;
\item \textbf{String? addIsolated(String name)} --- добавление изолированной точки;
\item \textbf{String? addPath(int from, int to, [int len = 0])} --- добавление пути;
\item \textbf{String? delPath(int from, int to)} --- удаление пути;
\item \textbf{String? delDot(int inn)} --- удаление вершины;
\item \textbf{void flushData()} --- удаление информации из графа;
\item \textbf{bool checkDots([bool verbose = false])} --- проверка графа на ошибки;
\item \textbf{void \_fixAfterDel(int inn}) --- испралвение нумерации после удаления;
\item \textbf{void \_syncNameTable()} --- синхронизация таблицы имен вершин;
\item \textbf{void setName(String name)} --- изменение имени;
\item \textbf{String? replaceDataFromFile(String path)} --- заменяет информацию графа на информацию из файла;
\item \textbf{List<LenDotPath> getSortedPathList()} --- возвращает список всех путей, отсортированный в порядке неубывания;
\item \textbf{void printG()} --- выводит информацию о графе в консоль;
\item \textbf{void printToFile(String name)} --- выводит информацию о графе в файл.
\end{enumerate}
Создать граф можно тремя способами:
\begin{enumerate}
\item пустой граф;
\item из списка смежности;
\item из файла.
\end{enumerate}
\subsubsection{Алгоритмы}
Алгоритмы описаны в классе графа в приложении~\ref{graph} .\newline
Обход в ширину:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/bfs.dart}
Обход в глубину:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dfs.dart}
Алгоритм Дейкстры:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dijkstra.dart}
Алгоритм Краскала:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/kruscal.dart}
\subsubsection{Интерфейс}
Интерфейс описан в файле drawing\_page.dart. Полный код доступен в приложении~\ref{buttons}.
Интерфейс программы изображен на рисунке~\ref{fig:programm}.
\begin{figure}[!ht]
\centering
\includegraphics[width=13cm]{./pic/grafs.png}
\caption{\label{fig:programm}
Интерфейс программы}
\end{figure}
\newpage
В качестве основы выбран виджет, сохраняющий состояние.
Построение интерфейса:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/page/build.dart}
Запуск работы алгоритмов происходит по нажатию соответствующих кнопок, затем страница отрисовки, используя полученные данные, выводит на экране информацию.
\subsubsection{Отрисовка графа}
Отрисовка элементов графа описана в файле curve\_painter.dart. Код доступен в приложении~\ref{painter}.
Для отрисовки информации используется виджет CustomPaint~\cite{custompaint}.
Для передачи информации графа используется конструктор CurvePainter.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/constr.dart}
Отрисовка соединений происходит с помощью пакета ''arrow\_path''.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/arrow.dart}
Для упрощения отрисовки, точки располагаются на окружности с центом, совпадающим с центром экрана. Сначала вычисляется их местоположение на экране, точка с максимальным количеством соединений располагается в центре:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/pos.dart}
Процесс вывода на экран:
\begin{enumerate}
\item вывод вершин на экран;
\item определение текущей операции и вывод работы текущего алгоритма;
\item вывод ребер;
\item вывод длин ребер;
\item вывод имен вершин.
\end{enumerate}
\subsubsection{Итоговый вид приложения}
Итоговый вид приложения отображен на картинке~\ref{fig:result}
\begin{figure}[!ht]
\centering
\includegraphics[width=15cm]{./pic/result.png}
\caption{\label{fig:result}
Интерфейс программы}
\end{figure}
\newpage
% Раздел "Заключение"
\conclusion
Было разработано простое приложение для создания и работы с графами с использованием Dart и Flutter.
В приложении возможно с помощью графического интерфейса:
\begin{itemize}
\item создать пустой граф;
\item добавить вершину;
\item удалить вершину;
\item добавить/изменить путь;
\item удалить путь;
\item сохранить граф;
\item загрузить граф;
\item построить минимальное остовное дерево с помощью алгоритма Краскала;
\item найти минимальный путь из выбранной вершины в другие с помощью алгоритма Дейкстры;
\item проверить доступность вершин с помощью обхода в глубину;
\item построить путь из одной вершины в другую с помощью обхода в ширину.
\end{itemize}
%Библиографический список, составленный с помощью BibTeX
%
\bibliographystyle{gost780uv}
\bibliography{thesis}
% Окончание основного документа и начало приложений
% Каждая последующая секция документа будет являться приложением
\appendix
\section{Код main.dart}\label{mainD}
Точка входа в программу.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/main.dart}
\section{Код страницы отрисовки}\label{buttons}
Описание интерфейса и базовые функции для взаимодействия с информацией.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/pages/drawing_page.dart}
\section{Код отрисовки графа}\label{painter}
Вывод на экран информации из графа.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/src/curve_painter.dart}
\section{Код класса для работы с графом}\label{graph}
Основной класс для хранения и взаимодействия с информацией.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/src/graph.dart}
\noindent
\end{document}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
import 'package:graphs/pages/drawing_page.dart';
import 'package:desktop_window/desktop_window.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
Future setupWindow() async {
await DesktopWindow.setMinWindowSize(const Size(850, 700));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
setupWindow();
return const MaterialApp(
title: "Graphs",
home: DrawingPage(),
);
}
}

View File

@ -1,508 +0,0 @@
import 'package:graphs/src/graph.dart';
import 'package:graphs/src/curve_painter.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
Graphs getGraph() {
List<Dot> d = <Dot>[];
d.add(Dot.fromTwoLists("1", [2], [3]));
d.add(Dot.fromTwoLists("2", [3], [1]));
d.add(Dot.fromTwoLists("3", [1], [2]));
return Graphs.fromList("Èìÿ ãðàôà", d, true, true);
}
class DrawingPage extends StatefulWidget {
const DrawingPage({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _DrawingPageState();
}
class _DrawingPageState extends State<DrawingPage> {
double screenSize = 0;
Graphs graphData = getGraph();
List<int?>? intListPath;
List<bool>? dfsAccessTable;
String op = Operations.none;
int? startDot;
int? endDot;
String? dropdownValue1;
String? dropdownValue2;
String currOp = "";
final _textNameController = TextEditingController();
final _textLnthController = TextEditingController();
final _textGrNmController = TextEditingController();
void clearInputData() {
setState(() {
_textLnthController.clear();
_textNameController.clear();
dropdownValue1 = null;
dropdownValue2 = null;
});
}
void clearDropDownVals() {
setState(() {
startDot = null;
intListPath = null;
dfsAccessTable = null;
endDot = null;
currOp = "";
op = Operations.none;
});
}
// *************buttons*************
ElevatedButton createButton(String txt, void Function() onPressing) {
return ElevatedButton(
onPressed: onPressing,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(states) => Colors.green.shade700)),
child: Text(txt,
style: const TextStyle(
fontSize: 15,
color: Colors.white70,
height: 1,
)),
);
}
void showPopUp(String alertTitle, String err) => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle, style: const TextStyle(fontSize: 26)),
content: Text(
err,
style: const TextStyle(fontSize: 18),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
// *************buttons*************
// ***addSpace***
SizedBox addSpaceH(double h) {
return SizedBox(height: h);
}
SizedBox addSpaceW(double w) {
return SizedBox(width: w);
}
// ***addSpace***
// *************inputs*************
Container createInputBox(String text, double width, IconData? icon,
TextEditingController? controller) {
if (icon == null) {
return Container(
width: width,
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField(
controller: controller,
textAlign: TextAlign.center,
onChanged: (name) => graphData.setName(name),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
filled: true,
fillColor: Colors.white,
//prefixIcon: Icon(icon, color: Colors.black),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(40))),
hintStyle: const TextStyle(color: Colors.black38),
hintText: text),
));
}
return Container(
width: width,
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField(
controller: controller,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
filled: true,
fillColor: Colors.white,
prefixIcon: Icon(icon, color: Colors.black),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(40))),
hintStyle: const TextStyle(color: Colors.black38),
hintText: text),
));
}
SizedBox dropList1(double width) {
var button = DropdownButton(
hint: const Text(
'Select dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
alignment: AlignmentDirectional.bottomEnd,
value: dropdownValue1,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue1 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
return SizedBox(
child: button,
width: width,
);
}
SizedBox dropList2(double width) {
var button = DropdownButton(
hint: const Text(
'Select Dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
alignment: AlignmentDirectional.centerEnd,
value: dropdownValue2,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue2 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
return SizedBox(
child: button,
width: width,
);
}
// *************inputs*************
//*********ButtonsFunctions*********
void addDotPushed() {
clearDropDownVals();
if (_textNameController.text == "") {
showPopUp("Error", "No name in \"Dot name\" box");
} else {
setState(() {
String? res = graphData.addIsolated(_textNameController.text);
if (res != null) {
showPopUp("Error", res);
}
});
}
clearInputData();
}
void addPathPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else if (_textLnthController.text == "" && graphData.getUseLengthBool()) {
showPopUp("Error", "No length in \"Input length\" box");
} else {
int? from = int.parse(dropdownValue1!);
int? to = int.parse(dropdownValue2!);
int? len = int.tryParse(_textLnthController.text);
if (len == null && graphData.getUseLengthBool()) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Input length\"");
} else {
len ??= 0;
setState(() {
String? res = graphData.addPath(from, to, len!);
if (res != null) showPopUp("Error", res);
});
}
}
clearInputData();
}
void changeOriented() {
setState(() {
String? res = graphData.flipUseOrientation();
if (res != null) showPopUp("Error", res);
});
}
void changeLength() {
setState(() {
String? res = graphData.flipUseLength();
if (res != null) showPopUp("Error", res);
});
}
void delPathPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else {
int? from = int.tryParse(dropdownValue1!);
int? to = int.tryParse(dropdownValue2!);
if (from == null || to == null) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\"");
} else {
setState(() {
String? res = graphData.delPath(from, to);
if (res != null) {
showPopUp("Error", res);
}
});
}
}
clearInputData();
}
void delDotPushed() {
if (dropdownValue1 != null) {
setState(() {
String? res = graphData.delDot(int.parse(dropdownValue1!));
if (res != null) {
showPopUp("Error", res);
}
});
} else {
showPopUp("Error", "Nothing in input");
}
clearDropDownVals();
clearInputData();
}
void fileOpener() async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]);
if (result != null) {
if (!result.files.single.path!.endsWith(".txt")) {
showPopUp("Error", "Can open only \".txt\" files");
} else {
setState(() {
String? res =
graphData.replaceDataFromFile(result.files.single.path!);
if (res != null) showPopUp("Error", res);
});
}
} else {
showPopUp("Error", "No file selected");
}
clearDropDownVals();
clearInputData();
}
void fileSaver() async {
String? outputFile = await FilePicker.platform.saveFile(
dialogTitle: 'Please select an output file:',
fileName: 'output-file.txt',
allowedExtensions: ["txt"]);
if (outputFile == null) {
showPopUp("Error", "Save cancelled");
} else {
if (!outputFile.endsWith(".txt")) {
outputFile += ".txt";
}
graphData.printToFile(outputFile);
}
clearDropDownVals();
clearInputData();
}
void bfsPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
endDot = int.parse(dropdownValue2!);
currOp = "OP: BFS from $startDot to $endDot";
op = Operations.bfs;
intListPath = graphData.bfsPath(startDot!, endDot!);
});
if (intListPath == null) {
showPopUp("Info", "There is no path");
}
}
clearInputData();
}
void dfsPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
op = Operations.dfs;
currOp = "OP: DFS from $startDot";
dfsAccessTable = graphData.dfsIterative(startDot!);
});
if (dfsAccessTable == null) {
showPopUp("Err", "report this error.");
}
}
clearInputData();
}
void dijkstraPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No number in \"Dot number\" box");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
currOp = "OP: Dijkstra from $startDot";
op = Operations.dijkstra;
intListPath = graphData.dijkstra(startDot!);
});
if (intListPath == null) {
showPopUp("Err", "report this error.");
}
}
clearInputData();
}
//*********ButtonsFunctions*********
// build
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
_textGrNmController.text = graphData.getName();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Align(
alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.green.shade900,
child: Column(children: <Widget>[
const SizedBox(height: 5),
Row(children: [
addSpaceW(screenSize / 8 + 19),
createButton("\nAdd dot\n", addDotPushed),
createInputBox("Dot name", screenSize / 4 - 25, Icons.label,
_textNameController),
addSpaceW(8),
createButton("\nAdd path\n", addPathPushed),
createInputBox("Input length", screenSize / 4 - 25,
Icons.arrow_right_alt_outlined, _textLnthController),
]),
addSpaceH(3),
Row(children: [
addSpaceW(6),
createInputBox(
"Name", screenSize / 8 - 25, null, _textGrNmController),
createButton("\nDel dot \n", delDotPushed),
addSpaceW(54),
dropList1(screenSize / 4 - 80),
addSpaceW(53),
createButton("\nDel path\n", delPathPushed),
addSpaceW(54),
dropList2(screenSize / 4 - 80),
]),
]),
),
actions: [
IconButton(
onPressed: () {
setState(() {
clearDropDownVals();
graphData.flushData();
clearInputData();
});
},
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(
graphData: graphData,
intListPath: intListPath,
dfsAccessTable: dfsAccessTable,
start: startDot,
end: endDot,
op: op),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
currOp,
style: const TextStyle(fontSize: 14, color: Colors.black),
),
createButton("Bfs", bfsPushed),
createButton("Dfs", dfsPushed),
createButton("Dijkstra", dijkstraPushed),
createButton("Kruskal", () {
clearDropDownVals();
setState(() {
currOp = "OP: Kruscal algo";
graphData.kruskal();
});
clearInputData();
}),
createButton("Clear OP", () {
clearDropDownVals();
clearInputData();
}),
createButton(graphData.getUseLengthStr(), changeLength),
createButton(graphData.getDoubleSidedStr(), changeOriented),
createButton("Save to file", fileSaver),
createButton("Load from file", fileOpener),
],
),
),
),
));
}
}

View File

@ -1,327 +0,0 @@
import 'package:arrow_path/arrow_path.dart';
import 'package:graphs/src/graph.dart';
import 'package:flutter/material.dart';
import 'dart:math';
class Operations {
static const String bfs = "bfs";
static const String dfs = "dfs";
static const String dijkstra = "djkstr";
static const String none = "none";
}
class CurvePainter extends CustomPainter {
CurvePainter({
Key? key,
required this.graphData,
required this.intListPath,
required this.dfsAccessTable,
required this.start,
required this.end,
required this.op,
});
List<int?>? intListPath;
List<bool>? dfsAccessTable;
String op;
int? start;
int? end;
Graphs graphData;
final double _dotRad = 7;
final double _lineWidth = 1.5;
final Color _lineColor = Colors.black;
final double _aboveHeight = 5;
double _circleRad = 100;
final TextStyle _textStyle = TextStyle(
color: Colors.red.shade900,
decorationColor: Colors.green.shade900,
decorationThickness: 10,
decorationStyle: TextDecorationStyle.dashed,
fontSize: 20,
);
Map<int, Offset> _off = <int, Offset>{};
void _drawLine(Canvas canvas, Offset p1, Offset p2) {
Paint p = Paint();
p.color = _lineColor;
p.strokeWidth = _lineWidth;
canvas.drawLine(p1, p2, p);
}
void _drawDot(Canvas canvas, Offset p1, [double plusRad = 0, Color? col]) {
col ??= Colors.yellow.shade900;
var p = Paint();
p.color = col;
p.strokeWidth = _lineWidth + 2;
canvas.drawCircle(p1, _dotRad + plusRad, p);
}
void _drawSelfConnect(Canvas canvas, Offset p1) {
var p = Paint();
p.color = _lineColor;
p.strokeWidth = _lineWidth;
p.style = PaintingStyle.stroke;
canvas.drawCircle(Offset(p1.dx + _dotRad + 20, p1.dy), _dotRad + 20, p);
}
TextSpan _getTextSpan(String s) => TextSpan(text: s, style: _textStyle);
TextPainter _getTextPainter(String s) => TextPainter(
text: _getTextSpan(s),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
void _drawDotNames(Canvas canvas, Offset place, String s) {
var textPainter = _getTextPainter(s);
textPainter.layout();
textPainter.paint(
canvas,
Offset((place.dx - textPainter.width),
(place.dy - textPainter.height) - _aboveHeight));
}
void _drawDotNum(Canvas canvas, Offset size, String s) {
var textPainter = TextPainter(
text: TextSpan(
text: s,
style: const TextStyle(
color: Colors.black,
fontSize: 17,
)),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
textPainter.layout();
textPainter.paint(
canvas,
Offset((size.dx - textPainter.width) + 25,
(size.dy - textPainter.height) + _aboveHeight + 30));
}
int _getHighInputConnections() {
if (graphData.getDots().length != 1 && graphData.getDots().length <= 3) {
return -1;
}
int higest = -1;
for (var i in graphData.getDots()) {
if (i.getL().length > higest) higest = i.num;
}
return higest;
}
Map<int, Offset> _getDotPos(int dotsAm, Size size) {
Map<int, Offset> off = <int, Offset>{};
var width = size.width / 2;
var height = size.height / 2;
int add = 0;
int h = _getHighInputConnections();
for (int i = 0; i < dotsAm; i++) {
if ((i + 1) != h) {
double x =
cos(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + width;
double y =
sin(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + height;
off[i + 1] = Offset(x, y);
} else if ((i + 1) == h) {
off[i + 1] = Offset(width + 2, height - 2);
add = 1;
h = 0;
} else {
print("GetDotPos error");
}
}
return off;
}
void _drawHArrow(Canvas canvas, Size size, Offset from, Offset to,
[bool doubleSided = false]) {
Path path;
// The arrows usually looks better with rounded caps.
Paint paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = _lineWidth;
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
/// Draw a single arrow.
path = Path();
path.moveTo(from.dx, from.dy);
path.relativeCubicTo(
0,
0,
-(from.dx + to.dx + length) / (length) - 40,
-(from.dy + to.dy + length) / (length) - 40,
to.dx - from.dx,
to.dy - from.dy);
path =
ArrowPath.make(path: path, isDoubleSided: doubleSided, tipLength: 16);
canvas.drawPath(path, paint);
}
void _drawHighArrow(Canvas canvas, Size size, Offset from, Offset to,
[bool doubleSided = false]) {
Path path;
Paint paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = _lineWidth;
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
/// Draw a single arrow.
path = Path();
path.moveTo(from.dx, from.dy);
path.relativeCubicTo(
0,
0,
(from.dx + to.dx) / (length * 2) + 40,
(from.dy + to.dy) / (length * 2) + 40,
to.dx - from.dx,
to.dy - from.dy);
path = ArrowPath.make(
path: path,
isDoubleSided: doubleSided,
tipLength: 13,
isAdjusted: false);
canvas.drawPath(path, paint);
}
void _drawConnections(
Canvas canvas, Size size, List<Dot> dots, Map<int, Offset> off) {
for (var i in dots) {
var list = i.getL();
var beg = off[i.num];
for (var d in list.keys) {
if (d == i.num) {
_drawSelfConnect(canvas, off[d]!);
} else {
if (graphData.getDoubleSidedBool()) {
if (d > i.num) {
_drawHArrow(canvas, size, beg!, off[d]!, false);
if (graphData.getUseLengthBool()) {
_drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2 - 18,
(off[d]!.dy + beg.dy) / 2 - 18),
i.getL()[d].toString());
}
} else {
_drawHighArrow(canvas, size, beg!, off[d]!, false);
if (graphData.getUseLengthBool()) {
_drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2 + 30,
(off[d]!.dy + beg.dy) / 2 + 30),
i.getL()[d].toString());
}
}
} else {
_drawLine(canvas, beg!, off[d]!);
if (graphData.getUseLengthBool()) {
_drawDotNames(
canvas,
Offset((off[d]!.dx + beg.dx) / 2, (off[d]!.dy + beg.dy) / 2),
i.getL()[d].toString());
}
}
}
}
}
}
void _drawBFS(Canvas canvas) {
if (intListPath != null) {
for (int i = 0; i < intListPath!.length; i++) {
_drawDot(canvas, _off[intListPath![i]]!, 8, Colors.yellow);
}
_drawDot(canvas, _off[start]!, 9, Colors.green);
_drawDot(canvas, _off[end]!, 7, Colors.red.shade200);
for (int i = 0; i < intListPath!.length; i++) {
_drawDotNum(canvas, _off[intListPath![i]]!, "bfs: <20>${i + 1}");
}
}
}
void _drawDFS(Canvas canvas) {
if (dfsAccessTable != null) {
for (int i = 0; i < dfsAccessTable!.length; i++) {
if (dfsAccessTable![i]) {
_drawDot(canvas, _off[i + 1]!, 8, Colors.green.shade500);
_drawDotNum(canvas, _off[i + 1]!, "dfs: visible");
} else {
_drawDot(canvas, _off[i + 1]!, 7, Colors.red.shade500);
_drawDotNum(canvas, _off[i + 1]!, "dfs: not visible");
}
}
_drawDot(canvas, _off[start]!, 9, Colors.green.shade900);
}
}
void _drawDijkstra(Canvas canvas) {
if (intListPath != null) {
_drawDot(canvas, _off[start]!, 9, Colors.green);
for (int i = 0; i < intListPath!.length; i++) {
if (intListPath![i] == null) {
_drawDotNum(canvas, _off[i + 1]!, "len: INF");
} else {
_drawDotNum(canvas, _off[i + 1]!, "len: ${intListPath![i]}");
}
}
}
}
@override
void paint(Canvas canvas, Size size) {
if (size.width > size.height) {
_circleRad = size.height / 3;
} else {
_circleRad = size.width / 3;
}
_off = _getDotPos(graphData.getDotAmount(), size); //, higest);
for (int i in _off.keys) {
_drawDot(canvas, _off[i]!);
}
var g = graphData.getDots();
switch (op) {
case Operations.bfs:
{
_drawBFS(canvas);
break;
}
case Operations.dfs:
{
_drawDFS(canvas);
break;
}
case Operations.dijkstra:
{
_drawDijkstra(canvas);
break;
}
default:
{
break;
}
}
_drawConnections(canvas, size, g, _off);
for (int i in _off.keys) {
_drawDotNames(
canvas, _off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]");
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

View File

@ -1,685 +0,0 @@
import 'dart:io';
class Separators {
static const String dotToConnections = ": ";
static const String dotToLength = "|";
static const String space = " ";
static const String hasLength = "Âçâåøåííûé";
static const String hasNoLength = "ÍåÂçâåøåííûé";
static const String isOriented = "Îðèåíòèðîâàííûé";
static const String isNotOriented = "ÍåÎðèåíòèðîâàííûé";
static const String nL = "\n";
static const String end = "END";
Separators();
}
class Dot {
//Data
// ignore: prefer_final_fields
String _name = "";
int num = -1;
Map<int, int> _ln = <int, int>{};
//****Get****
String getName() => _name;
bool hasConnection(int n) => _ln.containsKey(n);
Map<int, int> getL() => _ln;
int getLength(int x) {
if (hasConnection(x)) {
return _ln[x]!;
}
return -1;
}
//****Get****
//Set
void setName(String n) => _name = n;
//Add
void addPath(int inp, int length) => _ln[inp] = length;
//Del
void delPath(int n) => _ln.removeWhere((key, value) =>
key == n); // óäàëèòü îáðàòíûé ïóòü åñëè íå îðèåíòèðîâàííûé
//Print
void printD() {
stdout.write("$_name: ¹$num => ");
for (var i in _ln.keys) {
stdout.write("$i|${_ln[i]} ");
}
stdout.write("\n");
}
//******Constructor******
Dot([String name = "Undefined", int n = -1]) {
_name = name;
num = n;
_ln = <int, int>{};
}
Dot.fromTwoLists(String name, List<int> num0, List<int> length,
[int n = -1]) {
_name = name;
num = n;
Map<int, int> nw = <int, int>{};
if (num0.length != length.length) {
print("Error in lists");
} else {
for (var i = 0; i < num0.length; i++) {
nw[num0[i]] = length[i];
_ln = nw;
}
}
}
Dot.fromMap(String name, Map<int, int> l, [int n = -1]) {
_name = name;
num = n;
_ln = l;
}
//******Constructor******
//Copy
Dot.clone(Dot a) {
_name = a.getName();
num = a.num;
_ln = a.getL();
}
}
class Graphs {
static const int intMax = 0x7fffffffffffffff;
//Data
String _name = "Undefined"; //Èìÿ
int _amount = 0; //Êîëè÷åñòâî âåðøèí
List<Dot> _dots = <Dot>[]; //Ñïèñîê ñìåæíîñòè âåðøèí
Map<int, String> _nameTable = <int, String>{}; //Ñïèñîê âåðøèí ïî èìåíàì
bool _useLength = false; //Âçâåøåííîñòü
bool _oriented = false; //Îðèåíòèðîâàííîñòü
//*********************Add************************
String? addDot(Dot a) {
if (getNumByName(a.getName()) != null) {
return ("Dot name \"${a.getName()}\" already in use. Change name or use addPath");
}
_amount++;
a.num = _amount;
_dots.add(a);
_syncNameTable();
checkDots(false);
if (!_oriented) _fullFix();
return null;
}
bool addDotFromToLists(String name, List<int> num0, List<int> length,
[int n = -1]) {
var a = Dot.fromTwoLists(name, num0, length, n);
if (getNumByName(a.getName()) != null) {
print(
"Dot name ${a.getName()} already in use. Change name or use addPath");
return false;
}
_amount++;
a.num = _amount;
_dots.add(a);
_syncNameTable();
checkDots(false);
if (!_oriented) _fixPathAfterInsert(a);
return true;
}
String? addIsolated(String name) {
var res = addDot(Dot.fromTwoLists(name, [], []));
_syncNameTable();
return res;
}
String? addPath(int from, int to, [int len = 0]) {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return "Index out of range. Have dots 1..$_amount";
}
_dots[from - 1].addPath(to, len);
if (!_oriented) {
_dots[to - 1].addPath(from, len);
}
return null;
}
//*********************Add************************
//*********Delete*********
String? delPath(int from, int to) {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return "Can't find specified path";
}
if (!_dots[from - 1].hasConnection(to)) {
return "Already no connection between $from and $to";
}
_dots[from - 1].delPath(to);
if (!_oriented) {
_dots[to - 1].delPath(from);
}
return null;
}
String? delDot(int inn) {
if (inn > _amount || inn < 1) {
return "Index out of range. Allowed 1..$_amount";
}
List<int> toDel = <int>[];
for (int i in _dots[inn - 1].getL().keys) {
toDel.add(i);
}
for (int i in toDel) {
delPath(i, inn);
}
_dots.removeAt(inn - 1);
_syncNum();
_syncNameTable();
_fixAfterDel(inn);
return null;
}
void flushData() {
_dots = <Dot>[];
_amount = 0;
_nameTable = <int, String>{};
}
//*********Delete*********
//******Helper*******
bool checkDots([bool verbose = false]) {
for (var a in _dots) {
for (var i in a.getL().keys) {
try {
if (!_dots[i - 1].getL().containsKey(a.num)) {
if (verbose) print("Can't find ${a.num}");
return false;
}
} catch (e) {
if (verbose) {
print("Can't find Dot $i for path ${a.num}->$i. Exception $e");
}
_dots[a.num - 1].getL().remove(i);
return false;
}
}
}
return true;
}
void _fixAfterDel(int inn) {
for (int i = 0; i < _dots.length; i++) {
Map<int, int> l = <int, int>{};
for (int j in _dots[i].getL().keys) {
if (j >= inn) {
l[j - 1] = _dots[i].getL()[j]!;
} else {
l[j] = _dots[i].getL()[j]!;
}
}
_dots[i] = Dot.fromMap(_dots[i].getName(), l, _dots[i].num);
}
}
void _fixPathAfterInsert(Dot a) {
//Äëÿ íåîðèåíòèðîâàííîãî
for (var i in a.getL().keys) {
if (!_dots[i - 1].getL().containsKey(a.num)) {
addPath(i, a.num, a.getL()[i]!);
}
}
}
void _fullFix() {
for (var i in _dots) {
_fixPathAfterInsert(i);
}
}
void _syncNameTable() {
_nameTable = <int, String>{};
for (var i in _dots) {
_nameTable[i.num] = i.getName();
}
}
void _syncNum() {
_amount = 0;
for (var i in _dots) {
i.num = ++_amount;
}
_syncNameTable();
}
//******Helper*******
//*****Setters*******
void setName(String name) => _name = name;
String? flipUseOrientation() {
if (_amount != 0) {
return "Can change use of orientation only in empty graph";
}
_oriented = !_oriented;
return null;
}
String? flipUseLength() {
if (_amount != 0) {
return "Can change use of length only in empty graph";
}
_useLength = !_useLength;
return null;
}
String? replaceDataFromFile(String path) {
File file = File(path);
List<String> lines = file.readAsLinesSync();
if (lines.length < 3) {
return "Not enough lines in file";
}
String name = lines.removeAt(0);
bool oriented;
switch (lines.removeAt(0)) {
case Separators.isOriented:
oriented = true;
break;
case Separators.isNotOriented:
oriented = false;
break;
default:
return "Error on parsing \"IsOriented\"";
}
bool useLength;
switch (lines.removeAt(0).trim()) {
case Separators.hasLength:
useLength = true;
break;
case Separators.hasNoLength:
useLength = false;
break;
default:
return "Error on parsing \"HasLength\"";
}
List<Dot> dots = <Dot>[];
for (var l in lines) {
l = l.trimRight();
if (l != Separators.end) {
var spl = l.split(Separators.space);
List<int> dot = <int>[];
List<int> len = <int>[];
String name = spl.removeAt(0);
name = name.substring(0, name.length - 1);
for (var splitted in spl) {
if (splitted != "") {
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
int? parsed = int.tryParse(dt[0]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[0]}\"";
}
dot.add(parsed);
if (useLength) {
parsed = int.tryParse(dt[1]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[1]}\"";
}
len.add(parsed);
} else {
len.add(0);
}
} else if (dt.length == 1) {
int? parsed = int.tryParse(splitted);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"$splitted\"";
}
dot.add(parsed);
len.add(0);
}
}
}
dots.add(Dot.fromTwoLists(name, dot, len));
}
}
_name = name;
_oriented = oriented;
_useLength = useLength;
_dots = dots;
_syncNum();
_syncNameTable();
if (!_oriented) _fullFix();
return null;
}
//*****Setters*******
//*****Getters*******
bool getDoubleSidedBool() => _oriented;
String getDoubleSidedStr() {
if (_oriented) return Separators.isOriented;
return Separators.isNotOriented;
}
bool getUseLengthBool() => _useLength;
String getUseLengthStr() {
if (_useLength) return Separators.hasLength;
return Separators.hasNoLength;
}
List<Dot> getDots() => _dots;
String getName() => _name;
String? getNameByNum(int n) => _nameTable[n];
Map<int, String> getNameTable() => _nameTable;
int getDotAmount() => _dots.length;
int? getNumByName(String n) {
for (var i in _nameTable.keys) {
if (_nameTable[i] == n) return i;
}
return null;
}
List<List<int>>? getLenTable() {
List<List<int>>? out = <List<int>>[];
for (int i = 0; i < _amount; i++) {
List<int> xx = <int>[];
for (int j = 1; j <= _amount; j++) {
xx.add(_dots[i].getLength(j));
}
out.add(xx);
}
return out;
}
List<List<int>>? getPathTable() {
List<List<int>>? out = <List<int>>[];
for (int i = 0; i < _amount; i++) {
List<int> xx = <int>[];
for (int j = 1; j <= _amount; j++) {
if (_dots[i].getLength(j) != -1) {
xx.add(i);
} else {
xx.add(-1);
}
}
out.add(xx);
}
return out;
}
List<int>? getLongestPath([int start = 1]) {
start--;
if (start < 0 || start >= _amount) {
return null;
}
int max = -1;
int inD = -1;
int out = -1;
List<int>? res = <int>[];
for (int i = start; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
if (lens[d]! > max) {
max = lens[d]!;
inD = i + 1;
out = d;
}
}
}
if (inD == -1) {
return null;
}
res.add(inD);
res.add(out);
res.add(max);
return res;
}
List<LenDotPath> getSortedPathList() {
int max = -1;
int inD = -1;
int out = -1;
List<LenDotPath> result = <LenDotPath>[];
for (int i = 0; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
max = lens[d]!;
inD = i + 1;
out = d;
result.add(LenDotPath(max, inD, out));
}
}
result.sort((a, b) => a.l.compareTo(b.l));
return result;
}
//*****Getters*******
//******Print******
void printG() {
stdout.write("$_name: ");
if (_oriented) {
stdout.write("Îðèåíòèðîâàííûé, ");
} else {
stdout.write("Íå îðèåíòèðîâàííûé, ");
}
if (_useLength) {
print("Âçâåøåííûé");
} else {
print("Íå âçâåøåííûé");
}
for (var i in _dots) {
i.printD();
}
}
void printToFile(String name) {
var file = File(name);
file.writeAsStringSync("$_name\n");
if (_oriented) {
file.writeAsStringSync("${Separators.isOriented}\n",
mode: FileMode.append);
} else {
file.writeAsStringSync("${Separators.isNotOriented}\n",
mode: FileMode.append);
}
if (_useLength) {
file.writeAsStringSync("${Separators.hasLength}\n",
mode: FileMode.append);
} else {
file.writeAsStringSync("${Separators.hasNoLength}\n",
mode: FileMode.append);
}
for (int i = 0; i < _amount; i++) {
file.writeAsStringSync(_dots[i].getName() + Separators.dotToConnections,
mode: FileMode.append);
var d = _dots[i].getL();
for (var j in d.keys) {
file.writeAsStringSync(
j.toString() + Separators.dotToLength + d[j].toString() + " ",
mode: FileMode.append);
}
file.writeAsStringSync(Separators.nL, mode: FileMode.append);
}
file.writeAsStringSync(Separators.end, mode: FileMode.append);
}
//******Print******
//*******Constructor********
Graphs(
[String name = "Undefined",
bool hasLen = false,
bool isOriented = false]) {
_name = name;
_dots = <Dot>[];
_useLength = hasLen;
_oriented = isOriented;
_amount = 0;
_nameTable = <int, String>{};
}
Graphs.fromList(String name, List<Dot> dots, bool hasLen, bool oriented) {
_name = name;
_dots = dots;
_useLength = hasLen;
_amount = _dots.length;
_oriented = oriented;
_syncNum();
if (!_oriented) _fullFix();
}
Graphs.fromFile(String path) {
replaceDataFromFile(path);
}
//*******Constructor********
//Copy
Graphs.clone(Graphs a) {
_name = a.getName();
_dots = a.getDots();
_oriented = a.getDoubleSidedBool();
_useLength = a.getUseLengthBool();
_amount = _dots.length;
_syncNameTable();
}
//************Àëãîðèòìû************
List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
startDot--;
goalDot--;
List<List<int>>? graph = getLenTable();
List<bool> used = <bool>[];
List<int> dst = <int>[];
List<int> pr = <int>[];
for (int i = 0; i < _amount; i++) {
dst.add(-1);
used.add(false);
pr.add(0);
}
List<int> q = <int>[];
q.add(startDot);
used[startDot] = true;
dst[startDot] = 0;
pr[startDot] = -1; //ó âåðøèíû íåò ïðåäûäóùåé.
while (q.isNotEmpty) {
int cur = q.removeAt(0);
int x = 0;
for (int neighbor in graph![cur]) {
if (neighbor != -1) {
if (!used[x]) {
q.add(x);
used[x] = true;
dst[x] = dst[cur] + 1;
pr[x] = cur; //ñîõðàíåíèå ïðåäûäóùåé âåðøèíû
}
}
x++;
}
}
//Âîññòàíîâëåíèå êðàò÷àéøèåãî ïóòüè
List<int> path = <int>[];
int cur = goalDot;
path.add(cur + 1);
while (pr[cur] != -1) {
cur = pr[cur];
path.add(cur + 1);
}
path = path.reversed.toList();
if (path[0] == (startDot + 1) &&
path[1] == (goalDot + 1) &&
!_dots[startDot].hasConnection(goalDot + 1)) return null;
return path;
}
List<bool>? dfsIterative(int v) {
v--;
List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) {
label.add(false);
}
List<int> stack = <int>[];
stack.add(v);
while (stack.isNotEmpty) {
v = stack.removeLast();
if (!label[v]) {
label[v] = true;
for (int i in _dots[v].getL().keys) {
stack.add(i - 1);
}
}
}
return label;
}
List<int?> dijkstra(int from) {
List<int?> d = List<int?>.filled(_amount, intMax);
List<int> p = List<int>.filled(_amount, -1);
d[from - 1] = 0;
List<bool> u = List<bool>.filled(_amount, false);
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
if (!u[j] && (v == -1 || d[j]! < d[v]!)) {
v = j;
}
}
if (d[v] == intMax) break;
u[v] = true;
for (int to in _dots[v].getL().keys) {
int len = _dots[v].getL()[to]!;
if (!_useLength && len == 0) len = 1;
if (d[v]! + len < d[to - 1]!) {
d[to - 1] = d[v]! + len;
p[to - 1] = v;
}
}
}
for (int i = 0; i < d.length; i++) {
if (d[i] == intMax) d[i] = null;
}
return d;
}
Graphs? kruskal() {
List<LenDotPath> g = getSortedPathList();
//int cost = 0;
List<Dot> res = <Dot>[];
for (int i = 0; i < _amount; i++) {
res.add(Dot(_dots[i].getName(), _dots[i].num));
}
List<int> treeId = List<int>.filled(_amount, 0);
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
}
//************Àëãîðèòìû************
}
class LenDotPath {
late int l;
late int d;
late int p;
LenDotPath(int len, dot, path) {
l = len;
d = dot;
p = path;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

View File

@ -1,74 +0,0 @@
% Encoding: windows-1251
@Book{IITMMM_2017,
author = {Çàõàðîâà, Â. Å. and Àëåêñååâ, Ä. Â.},
title = {ÒÅÎÐÈß ÃÐÀÔÎÂ: Ó÷åáíîå ïîñîáèå},
adress = {Íèæíèé Íîâãîðîä},
publisher = {Íèæåãîðîäñêèé óíèâåðñèòåò},
year = {2017},
pages = {119},
}
@BOOK{Algo_2013,
author={Êîðìåí, Òîìàñ Õ. and Ëåéçåðñîí, ×àðëüç È. and Ðèâåñò, Ðîíàëä Ë. and Øòàéí, Êëèôôîðä},
title={Àëãîðèòìû: ïîñòðîåíèå è àíàëèç, 2-å èçäàíèå},
year={2013},
pages={1296},
publisher={Èçäàòåëüñêèé äîì ''Âèëüÿìñ''},
}
@Misc{intuit,
title={Ëåêöèÿ 45: Àëãîðèòìû íà ãðàôàõ. Àëãîðèòìû îáõîäà ãðàôa. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://intuit.ru/studies/courses/648/504/lecture/11474} (Äàòà îáðàùåíèÿ 1.11.2021). Çàãë. ñ ýêð. ßç. ðóñ.},
}
@TechReport{repo,
title={Ðåïîçèòîðèé ñ èõîäíûì êîäîì ïðîãðàììû. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://morozovad.ddns.net/lnd212/Graphs_dart}}
}
@Manual{dart_web,
title={Dart overview. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://dart.dev/overview} (Äàòà îáðàùåíèÿ 11.11.2021). Çàãë. ñ ýêð. ßç. àíãë.}
}
@Book{dartInAction,
author={Áàêêåò, Ê.},
title={Dart â äåéñòâèè},
year={2013},
pages={528},
publisher={ÄÌÊ Ïðåññ},
}
@TechReport{futureOfJavascript,
title={Future of Javascript. [{Ý}ëåêòðîííîå ïèñüìî]},
note={URL:~\url{https://markmail.org/message/uro3jtoitlmq6x7t}},
year={2010}
}
@Manual{dart_sound,
title={The Dart type system. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://dart.dev/guides/language/type-system} (Äàòà îáðàùåíèÿ 2.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Manual{dart_tour,
title={A tour of the Dart language. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://dart.dev/guides/language/language-tour} (Äàòà îáðàùåíèÿ 4.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Manual{skia_docs,
title={Skia documentation. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://skia.org/docs/} (Äàòà îáðàùåíèÿ 3.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Manual{krusc,
title={Ìèíèìàëüíîå îñòîâíîå äåðåâî. Àëãîðèòì Êðóñêàëà. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://e-maxx.ru/algo/mst_kruskal} (Äàòà îáðàùåíèÿ 27.10.2021). Çàãë. ñ ýêð. ßç. ðóñ.},
}
@Manual{custompaint,
title={CustomPaint class. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://api.flutter.dev/flutter/widgets/CustomPaint-class.html} (Äàòà îáðàùåíèÿ 6.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Comment{jabref-meta: databaseType:bibtex;}