|
@@ -4,18 +4,33 @@
|
|
* @author David Sehnal <david.sehnal@gmail.com>
|
|
* @author David Sehnal <david.sehnal@gmail.com>
|
|
*/
|
|
*/
|
|
|
|
|
|
-import computeRings from './rings/compute'
|
|
|
|
|
|
+import { computeRings, getFingerprint, createIndex } from './rings/compute'
|
|
import Unit from '../unit';
|
|
import Unit from '../unit';
|
|
import StructureElement from '../element';
|
|
import StructureElement from '../element';
|
|
|
|
|
|
|
|
+type UnitRing = ReadonlyArray<StructureElement.UnitIndex>
|
|
|
|
+
|
|
interface UnitRings {
|
|
interface UnitRings {
|
|
/** Each ring is specified as an array of indices in Unit.elements. */
|
|
/** Each ring is specified as an array of indices in Unit.elements. */
|
|
- readonly all: ReadonlyArray<ReadonlyArray<StructureElement.UnitIndex>>,
|
|
|
|
- readonly byFingerprint: ReadonlyMap<string, ReadonlyArray<number>>
|
|
|
|
|
|
+ readonly all: ReadonlyArray<UnitRing>,
|
|
|
|
+ readonly byFingerprint: ReadonlyMap<string, ReadonlyArray<UnitRings.Index>>,
|
|
|
|
+
|
|
|
|
+ readonly index: {
|
|
|
|
+ /** Maps atom index inside a Unit to the smallest ring index (an atom can be part of more than one ring) */
|
|
|
|
+ readonly elementRingIndices: ReadonlyMap<StructureElement.UnitIndex, UnitRings.Index[]>,
|
|
|
|
+
|
|
|
|
+ /** Maps UnitRings.Index to index to ringComponents */
|
|
|
|
+ readonly ringComponentIndex: ReadonlyArray<UnitRings.ComponentIndex>,
|
|
|
|
+ readonly ringComponents: ReadonlyArray<ReadonlyArray<UnitRings.Index>>
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
namespace UnitRings {
|
|
namespace UnitRings {
|
|
- export function getRingFingerprint(unit: Unit.Atomic, ring: ArrayLike<number>) {
|
|
|
|
|
|
+ /** Index into UnitRings.all */
|
|
|
|
+ export type Index = { readonly '@type': 'unit-ring-index' } & number
|
|
|
|
+ export type ComponentIndex = { readonly '@type': 'unit-ring-component-index' } & number
|
|
|
|
+
|
|
|
|
+ export function getRingFingerprint(unit: Unit.Atomic, ring: UnitRing) {
|
|
const { elements } = unit;
|
|
const { elements } = unit;
|
|
const { type_symbol } = unit.model.atomicHierarchy.atoms;
|
|
const { type_symbol } = unit.model.atomicHierarchy.atoms;
|
|
|
|
|
|
@@ -26,9 +41,9 @@ namespace UnitRings {
|
|
|
|
|
|
export function create(unit: Unit.Atomic): UnitRings {
|
|
export function create(unit: Unit.Atomic): UnitRings {
|
|
const rings = computeRings(unit);
|
|
const rings = computeRings(unit);
|
|
- const byFingerprint = new Map<string, number[]>();
|
|
|
|
|
|
+ const byFingerprint = new Map<string, Index[]>();
|
|
|
|
|
|
- let idx = 0;
|
|
|
|
|
|
+ let idx = 0 as Index;
|
|
for (const r of rings) {
|
|
for (const r of rings) {
|
|
const fp = getRingFingerprint(unit, r);
|
|
const fp = getRingFingerprint(unit, r);
|
|
if (byFingerprint.has(fp)) byFingerprint.get(fp)!.push(idx);
|
|
if (byFingerprint.has(fp)) byFingerprint.get(fp)!.push(idx);
|
|
@@ -36,73 +51,19 @@ namespace UnitRings {
|
|
idx++;
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
|
|
- return { all: rings, byFingerprint };
|
|
|
|
|
|
+ console.log(createIndex(rings));
|
|
|
|
+
|
|
|
|
+ let _index: UnitRings['index'] | undefined = void 0;
|
|
|
|
+ return {
|
|
|
|
+ all: rings,
|
|
|
|
+ byFingerprint,
|
|
|
|
+ get index() {
|
|
|
|
+ if (_index) return _index;
|
|
|
|
+ _index = createIndex(rings);
|
|
|
|
+ return _index;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-export { UnitRings }
|
|
|
|
-
|
|
|
|
-function getFingerprint(elements: string[]) {
|
|
|
|
- const len = elements.length;
|
|
|
|
- const reversed: string[] = new Array(len);
|
|
|
|
-
|
|
|
|
- for (let i = 0; i < len; i++) reversed[i] = elements[len - i - 1];
|
|
|
|
-
|
|
|
|
- const rotNormal = getMinimalRotation(elements);
|
|
|
|
- const rotReversed = getMinimalRotation(reversed);
|
|
|
|
-
|
|
|
|
- let isNormalSmaller = false;
|
|
|
|
-
|
|
|
|
- for (let i = 0; i < len; i++) {
|
|
|
|
- const u = elements[(i + rotNormal) % len], v = reversed[(i + rotReversed) % len];
|
|
|
|
- if (u !== v) {
|
|
|
|
- isNormalSmaller = u < v;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (isNormalSmaller) return buildFinderprint(elements, rotNormal);
|
|
|
|
- return buildFinderprint(reversed, rotReversed);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-function getMinimalRotation(elements: string[]) {
|
|
|
|
- // adapted from http://en.wikipedia.org/wiki/Lexicographically_minimal_string_rotation
|
|
|
|
-
|
|
|
|
- const len = elements.length;
|
|
|
|
- const f = new Int32Array(len * 2);
|
|
|
|
- for (let i = 0; i < f.length; i++) f[i] = -1;
|
|
|
|
-
|
|
|
|
- let u = '', v = '', k = 0;
|
|
|
|
-
|
|
|
|
- for (let j = 1; j < f.length; j++) {
|
|
|
|
- let i = f[j - k - 1];
|
|
|
|
- while (i !== -1) {
|
|
|
|
- u = elements[j % len]; v = elements[(k + i + 1) % len];
|
|
|
|
- if (u === v) break;
|
|
|
|
- if (u < v) k = j - i - 1;
|
|
|
|
- i = f[i];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (i === -1) {
|
|
|
|
- u = elements[j % len]; v = elements[(k + i + 1) % len];
|
|
|
|
- if (u !== v) {
|
|
|
|
- if (u < v) k = j;
|
|
|
|
- f[j - k] = -1;
|
|
|
|
- } else f[j - k] = i + 1;
|
|
|
|
- } else f[j - k] = i + 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return k;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-function buildFinderprint(elements: string[], offset: number) {
|
|
|
|
- const len = elements.length;
|
|
|
|
- const ret: string[] = [];
|
|
|
|
- let i;
|
|
|
|
- for (i = 0; i < len - 1; i++) {
|
|
|
|
- ret.push(elements[(i + offset) % len]);
|
|
|
|
- ret.push('-');
|
|
|
|
- }
|
|
|
|
- ret.push(elements[(i + offset) % len]);
|
|
|
|
- return ret.join('');
|
|
|
|
-}
|
|
|
|
|
|
+export { UnitRing, UnitRings }
|