mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
148 lines
No EOL
2.8 KiB
JavaScript
148 lines
No EOL
2.8 KiB
JavaScript
import {HashTable} from './hashmap'
|
|
|
|
/** @constructor */
|
|
function Graph(data) {
|
|
|
|
this.connections = function(e) {
|
|
return data[e];
|
|
};
|
|
|
|
this.at = function(index) {
|
|
return index;
|
|
};
|
|
|
|
this.size = function() {
|
|
return data.length;
|
|
};
|
|
}
|
|
|
|
Graph.findAllLoops = function(graph, hashCode, equals) {
|
|
|
|
let loops = [];
|
|
const visited = new HashTable(hashCode, equals);
|
|
function step(vertex, comesFrom, path) {
|
|
let i;
|
|
visited.put(vertex, true);
|
|
for (i = path.length - 1; i >= 0; --i) {
|
|
if (equals(vertex, path[i])) {
|
|
loops.push(path.slice(i));
|
|
return;
|
|
}
|
|
}
|
|
|
|
const next = graph.connections(vertex);
|
|
|
|
path.push(vertex);
|
|
let needClone = false;
|
|
|
|
for (i = 0; i < next.length; i++) {
|
|
const v = next[i];
|
|
if (equals(v, comesFrom)) {
|
|
continue;
|
|
}
|
|
|
|
const p = needClone ? path.slice(0) : path;
|
|
needClone = true;
|
|
step(v, vertex, p);
|
|
}
|
|
path.pop();
|
|
}
|
|
|
|
for (i = 0; i < graph.size(); i++) {
|
|
const vertex = graph.at(i);
|
|
if (visited.get(vertex) !== true) {
|
|
step(vertex, -1, []);
|
|
}
|
|
}
|
|
|
|
//filter duplicates
|
|
|
|
function sameLoop(a, b) {
|
|
const first = a[0];
|
|
let bShift;
|
|
for (bShift = 0; bShift < a.length; bShift++) {
|
|
if (equals(b[bShift], first)) {
|
|
break;
|
|
}
|
|
}
|
|
if (bShift == a.length) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < a.length; i++) {
|
|
const bUp = (bShift + i) % a.length;
|
|
let bDown = bShift - i;
|
|
if (bDown < 0) {
|
|
bDown = a.length + bDown;
|
|
}
|
|
// console.log("up: " + bUp + "; down: " + bDown);
|
|
const curr = a[i];
|
|
if ( !equals(curr, b[bUp]) && !equals(curr, b[bDown]) ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
let i, duplicates = 0;
|
|
for (i = 0; i < loops.length; i++) {
|
|
const a = loops[i];
|
|
if (a == null) continue;
|
|
for (let j = i + 1; j < loops.length; j++) {
|
|
const b = loops[j];
|
|
if (b == null || a.length !== b.length) {
|
|
continue;
|
|
}
|
|
if (sameLoop(a, b)) {
|
|
loops[j] = null;
|
|
++ duplicates;
|
|
}
|
|
}
|
|
}
|
|
if (duplicates != 0) {
|
|
const filtered = [];
|
|
for (i = 0; i < loops.length; i++) {
|
|
if (loops[i] != null) filtered.push(loops[i]);
|
|
}
|
|
loops = filtered;
|
|
}
|
|
|
|
return loops;
|
|
};
|
|
|
|
|
|
const test = function() {
|
|
const data = [
|
|
[],
|
|
[2],
|
|
[1, 3, 9],
|
|
[2, 4],
|
|
[3, 9, 5, 8],
|
|
[4, 6],
|
|
[5, 8, 7],
|
|
[6],
|
|
[4, 6],
|
|
[2, 4]
|
|
];
|
|
|
|
const graph = new Graph(data);
|
|
console.log(Graph.findAllLoops(graph));
|
|
};
|
|
|
|
const test0 = function() {
|
|
const data = [
|
|
[3, 1],
|
|
[0, 2, 8],
|
|
[1, 3, 7, 5],
|
|
[0, 2, 4],
|
|
[3, 5],
|
|
[4, 2, 6],
|
|
[5, 7],
|
|
[2, 6, 8],
|
|
[1, 7]
|
|
];
|
|
|
|
const graph = new Graph(data);
|
|
console.log(Graph.findAllLoops(graph));
|
|
};
|
|
|
|
export {Graph} |