Files
enviPy-bayer/static/js/ketcher2/script/chem/smiles/dfs.js
2025-06-23 20:13:54 +02:00

178 lines
5.0 KiB
JavaScript

/****************************************************************************
* Copyright 2017 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
var Set = require('../../util/set');
function Dfs(mol, atomData, components, nReactants) {
this.molecule = mol;
this.atom_data = atomData;
this.components = components;
this.nComponentsInReactants = -1;
this.nReactants = nReactants;
this.vertices = new Array(this.molecule.atoms.count()); // Minimum size
this.molecule.atoms.each(function (aid) {
this.vertices[aid] = new Dfs.VertexDesc();
}, this);
this.edges = new Array(this.molecule.bonds.count()); // Minimum size
this.molecule.bonds.each(function (bid) {
this.edges[bid] = new Dfs.EdgeDesc();
}, this);
this.v_seq = [];
}
Dfs.VertexDesc = function () {
this.dfs_state = 0; // 0 -- not on stack
// 1 -- on stack
// 2 -- removed from stack
this.parent_vertex = 0; // parent vertex in DFS tree
this.parent_edge = 0; // edge to parent vertex
this.branches = 0; // how many DFS branches go out from this vertex}
};
Dfs.EdgeDesc = function () {
this.opening_cycles = 0; // how many cycles are
// (i) starting with this edge
// and (ii) ending in this edge's first vertex
this.closing_cycle = 0; // 1 if this edge closes a cycle
};
Dfs.SeqElem = function (vIdx, parVertex, parEdge) {
this.idx = vIdx; // index of vertex in _graph
this.parent_vertex = parVertex; // parent vertex in DFS tree
this.parent_edge = parEdge; // edge to parent vertex
};
Dfs.prototype.walk = function () { // eslint-disable-line max-statements
var vStack = [];
var i, j;
var cid = 0;
var component = 0;
while (true) { // eslint-disable-line no-constant-condition
if (vStack.length < 1) {
var selected = -1;
var findFunc = function (aid) { // eslint-disable-line func-style
if (this.vertices[aid].dfs_state == 0) {
selected = aid;
return true;
}
return false;
};
while (cid < this.components.length && selected == -1) {
selected = Set.find(this.components[cid], findFunc, this);
if (selected === null) {
selected = -1;
cid++;
}
if (cid == this.nReactants)
this.nComponentsInReactants = component;
}
if (selected < -1)
this.molecule.atoms.find(findFunc, this);
if (selected == -1)
break;
this.vertices[selected].parent_vertex = -1;
this.vertices[selected].parent_edge = -1;
vStack.push(selected);
component++;
}
var vIdx = vStack.pop();
var parentVertex = this.vertices[vIdx].parent_vertex;
var seqElem = new Dfs.SeqElem(vIdx, parentVertex, this.vertices[vIdx].parent_edge);
this.v_seq.push(seqElem);
this.vertices[vIdx].dfs_state = 2;
var atomD = this.atom_data[vIdx];
for (i = 0; i < atomD.neighbours.length; i++) {
var neiIdx = atomD.neighbours[i].aid;
var edgeIdx = atomD.neighbours[i].bid;
if (neiIdx == parentVertex)
continue; // eslint-disable-line no-continue
if (this.vertices[neiIdx].dfs_state == 2) {
this.edges[edgeIdx].closing_cycle = 1;
j = vIdx;
while (j != -1 && this.vertices[j].parent_vertex != neiIdx)
j = this.vertices[j].parent_vertex;
if (j == -1)
throw new Error('cycle unwind error');
this.edges[this.vertices[j].parent_edge].opening_cycles++;
this.vertices[vIdx].branches++;
seqElem = new Dfs.SeqElem(neiIdx, vIdx, edgeIdx);
this.v_seq.push(seqElem);
} else {
if (this.vertices[neiIdx].dfs_state == 1) {
j = vStack.indexOf(neiIdx);
if (j == -1) // eslint-disable-line max-depth
throw new Error('internal: removing vertex from stack');
vStack.splice(j, 1);
var parent = this.vertices[neiIdx].parent_vertex;
if (parent >= 0) // eslint-disable-line max-depth
this.vertices[parent].branches--;
}
this.vertices[vIdx].branches++;
this.vertices[neiIdx].parent_vertex = vIdx;
this.vertices[neiIdx].parent_edge = edgeIdx;
this.vertices[neiIdx].dfs_state = 1;
vStack.push(neiIdx);
}
}
}
};
Dfs.prototype.edgeClosingCycle = function (eIdx) {
return this.edges[eIdx].closing_cycle != 0;
};
Dfs.prototype.numBranches = function (vIdx) {
return this.vertices[vIdx].branches;
};
Dfs.prototype.numOpeningCycles = function (eIdx) {
return this.edges[eIdx].opening_cycles;
};
Dfs.prototype.toString = function () {
var str = '';
this.v_seq.each(function (seqElem) {
str += seqElem.idx + ' -> ';
});
str += '*';
return str;
};
module.exports = Dfs;