Graphs_dart/flutter/lib/src/graph.dart

680 lines
18 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
//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";
}
_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<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*******
//******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((i + 1).toString() + 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);
/*File file = File(path);
List<String> lines = file.readAsLinesSync();
_name = lines.removeAt(0);
_oriented = lines.removeAt(0) == Separators.isOriented.trim();
_useLength = lines.removeAt(0) == Separators.hasLength.trim();
_dots = <Dot>[];
for (var l in lines) {
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) {
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
dot.add(int.parse(dt[0]));
if (_useLength) {
len.add(int.parse(dt[1]));
} else {
len.add(0);
}
} else if (dt.length == 1) {
dot.add(int.parse(splitted));
len.add(0);
}
}
_dots.add(Dot.fromTwoLists(name, dot, len));
}
}
_syncNum();
_syncNameTable();
if (!_oriented) _fullFix();*/
}
//*******Constructor********
//Copy
Graphs.clone(Graphs a) {
_name = a.getName();
_dots = a.getDots();
_oriented = a.getDoubleSidedBool();
_useLength = a.getUseLengthBool();
_amount = _dots.length;
_syncNameTable();
}
//************Алгоритмы************
/* bool bfsHasPath(int startDot, int goalDot) {
// обход в ширину
startDot--;
goalDot--;
List<bool> visited = <bool>[];
List<int> queue = <int>[];
for (int i = 0; i < _amount; i++) {
visited.add(false);
} // изначально список посещённых узлов пуст
queue.add(startDot); // начиная с узла-источника
visited[startDot] = true;
while (queue.isNotEmpty) {
// пока очередь не пуста
int node = queue.removeAt(0); // извлечь первый элемент в очереди
if (node == goalDot) {
return true; // проверить, не является ли текущий узел целевым
}
for (int child in _dots[node].getL().keys) {
// все преемники текущего узла, ...
if (!visited[child - 1]) {
// ... которые ещё не были посещены ...
queue.add(child - 1); // ... добавить в конец очереди...
visited[child - 1] = true; // ... и пометить как посещённые
}
}
}
return false; // Целевой узел недостижим
}*/
List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
//if (!bfsHasPath(startDot, goalDot)) return null;
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; //Пометка, означающая, что у вершины startDot нет предыдущей.
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();
//print("Shortest path between vertices ${startDot+1} and ${goalDot+1} is: $path");
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<int>? pos = <int>[];
List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) {
label.add(false);
}
List<int> stack = <int>[];
stack.add(v);
//pos.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);
//pos.add(i);
}
}
}
//print(pos);
return label;
}
void dijkstra(int source) {
/*
create vertex set Q;
for each vertex v in Graph{
dist[v] ← INFINITY ;
prev[v] ← UNDEFINED ;
add v to Q;}
dist[source] ← 0;
while Q is not empty{
u ← vertex in Q with min dist[u]
remove u from Q
for each neighbor v of u still in Q{
alt ← dist[u] + length(u, v);
if alt < dist[v]: {
dist[v] ← alt;
prev[v] ← u;}
}}
return dist[], prev[]*/
}
//************Алгоритмы************
}