diff --git a/src/mol-data/_spec/equiv-index.spec.ts b/src/mol-data/_spec/equiv-index.spec.ts index 9083c0145d0258be5815f9bc0c6f49c9effa5062..339022a969f1e072aeaa0199be98926106332623 100644 --- a/src/mol-data/_spec/equiv-index.spec.ts +++ b/src/mol-data/_spec/equiv-index.spec.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import EquivalenceClasses from '../util/equivalence-classes' +import { EquivalenceClasses } from '../util' describe('equiv-classes', () => { it('integer mod classes', () => { diff --git a/src/mol-data/db/column.ts b/src/mol-data/db/column.ts index 54fe666de2589facbd59bc5107c4063a3d8cc194..adcd28cc2b69648eb4481a5478534052bf6b27b7 100644 --- a/src/mol-data/db/column.ts +++ b/src/mol-data/db/column.ts @@ -80,6 +80,10 @@ namespace Column { valueKind?: (row: number) => ValueKind, } + export function is(v: any): v is Column<any> { + return !!v && !!(v as Column<any>).schema && !!(v as Column<any>).value; + } + export const enum ValueKind { Present = 0, NotPresent = 1, Unknown = 2 } export function Undefined<T extends Schema>(rowCount: number, schema: T): Column<T['T']> { @@ -306,6 +310,7 @@ function mapToArrayImpl<T, S>(c: Column<T>, f: (v: T) => S, ctor: Column.ArrayCt } function areColumnsEqual(a: Column<any>, b: Column<any>) { + if (a === b) return true; if (a.rowCount !== b.rowCount || a.isDefined !== b.isDefined || a.schema.valueType !== b.schema.valueType) return false; if (!!a['@array'] && !!b['@array']) return areArraysEqual(a, b); return areValuesEqual(a, b); diff --git a/src/mol-data/db/table.ts b/src/mol-data/db/table.ts index d01629bf1650134e148ad469bcc31d37a4517cda..6335e37ed50eef0e761a90549ed53590e219647b 100644 --- a/src/mol-data/db/table.ts +++ b/src/mol-data/db/table.ts @@ -130,6 +130,26 @@ namespace Table { return true; } + + /** Allocate a new object with the given row values. */ + export function getRow<S extends Schema>(table: Table<S>, index: number) { + const row: Row<S> = Object.create(null); + const { _columns: cols } = table; + for (let i = 0; i < cols.length; i++) { + const c = cols[i]; + row[c] = table[c].value(index); + } + return row; + } + + export function getRows<S extends Schema>(table: Table<S>) { + const ret: Row<S>[] = []; + const { _rowCount: c } = table; + for (let i = 0; i < c; i++) { + ret[i] = getRow(table, i); + } + return ret; + } } export default Table \ No newline at end of file diff --git a/src/mol-data/int.ts b/src/mol-data/int.ts index 54c2795bbc6f772ebd25ee58501119ded48b35fe..24b005bd24adbc9949770670c2c6fb2a4535485b 100644 --- a/src/mol-data/int.ts +++ b/src/mol-data/int.ts @@ -10,6 +10,7 @@ import Segmentation from './int/segmentation' import SortedArray from './int/sorted-array' import Tuple from './int/tuple' import LinkedIndex from './int/linked-index' +import IntMap from './int/map' import Iterator from './iterator' -export { Interval, OrderedSet, Segmentation, SortedArray, Tuple, LinkedIndex, Iterator } \ No newline at end of file +export { Interval, OrderedSet, Segmentation, SortedArray, Tuple, LinkedIndex, IntMap, Iterator } \ No newline at end of file diff --git a/src/mol-data/int/map.ts b/src/mol-data/int/map.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ef86a532e7f382cd13c47fe9de7a93a1f151895 --- /dev/null +++ b/src/mol-data/int/map.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { iterableToArray } from '../util' + +/** Immutable by convention IntMap */ +interface IntMap<T> { + keys(): IterableIterator<number>, + values(): IterableIterator<T>, + get(key: number): T, + readonly size: number +} + +namespace IntMap { + export interface Mutable<T> extends IntMap<T> { + set(key: number, value: T): void; + } + + export function keyArray<T>(map: IntMap<T>): number[] { + return iterableToArray(map.keys()); + } + + export function ofObj<T>(obj: { [key: number]: T }): IntMap<T> { + const keys = Object.keys(obj); + const ret = new Map<number, T>(); + for (let i = 0, _i = keys.length; i < _i; i++) { + const k = keys[i]; + ret.set(+k, obj[k as any]); + } + return ret as IntMap<T>; + } + + export function Mutable<T>(): Mutable<T> { + return new Map<number, T>() as Mutable<T>; + } + + export function asImmutable<T>(map: IntMap<T>): IntMap<T> { + return map; + } +} + +export default IntMap; \ No newline at end of file diff --git a/src/mol-data/util.ts b/src/mol-data/util.ts index ab0c73fcfc7be65d2974c403b7fc010dc118b95a..629b3b517d6f1ad80151a734f369083529c00ba5 100644 --- a/src/mol-data/util.ts +++ b/src/mol-data/util.ts @@ -4,12 +4,11 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import ChunkedArray from './util/chunked-array' -import EquivalenceClasses from './util/chunked-array' -import HashSet from './util/hash-set' -import UniqueArray from './util/unique-array' - +export * from './util/chunked-array' +export * from './util/unique-array' +export * from './util/hash-set' +export * from './util/equivalence-classes' export * from './util/hash-functions' export * from './util/sort' - -export { ChunkedArray, EquivalenceClasses, HashSet, UniqueArray } \ No newline at end of file +export * from './util/grouping' +export * from './util/array' \ No newline at end of file diff --git a/src/mol-data/util/array.ts b/src/mol-data/util/array.ts new file mode 100644 index 0000000000000000000000000000000000000000..664138327b5b67fab885efd6e0ec035f6efaf895 --- /dev/null +++ b/src/mol-data/util/array.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * from https://github.com/dsehnal/CIFTools.js + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export function arrayFind<T>(array: ArrayLike<T>, f: (v: T) => boolean): T | undefined { + for (let i = 0, _i = array.length; i < _i; i++) { + if (f(array[i])) return array[i]; + } + return void 0; +} + +export function iterableToArray<T>(it: IterableIterator<T>): T[] { + if (Array.from) return Array.from(it); + + const ret = []; + while (true) { + const { done, value } = it.next(); + if (done) break; + ret[ret.length] = value; + } + return ret; +} \ No newline at end of file diff --git a/src/mol-data/util/chunked-array.ts b/src/mol-data/util/chunked-array.ts index 0664a4c12ce99348f609b0d8558e28836f0b8dc2..f9b977571f543a317d0c12a618f61442af8fd3c3 100644 --- a/src/mol-data/util/chunked-array.ts +++ b/src/mol-data/util/chunked-array.ts @@ -134,4 +134,4 @@ namespace ChunkedArray { } } -export default ChunkedArray \ No newline at end of file +export { ChunkedArray } \ No newline at end of file diff --git a/src/mol-data/util/equivalence-classes.ts b/src/mol-data/util/equivalence-classes.ts index 936d39b299ed33956605642cd7b2c8a7041ef368..cc5f4af62e60d3bc252a4c254a0e67b01e19b383 100644 --- a/src/mol-data/util/equivalence-classes.ts +++ b/src/mol-data/util/equivalence-classes.ts @@ -41,8 +41,6 @@ class EquivalenceClassesImpl<K, V> { constructor(private getHash: (v: V) => any, private areEqual: (a: V, b: V) => boolean) { } } -function EquivalenceClasses<K, V>(getHash: (x: V) => any, areEqual: (a: V, b: V) => boolean) { +export function EquivalenceClasses<K, V>(getHash: (x: V) => any, areEqual: (a: V, b: V) => boolean) { return new EquivalenceClassesImpl<K, V>(getHash, areEqual); } - -export default EquivalenceClasses; \ No newline at end of file diff --git a/src/mol-data/util/grouping.ts b/src/mol-data/util/grouping.ts index 213f3cf541e4ed9ae5e988b316be5c5423dcc490..71e9ce5a045ce14238ae3a1ffece9e08114a196b 100644 --- a/src/mol-data/util/grouping.ts +++ b/src/mol-data/util/grouping.ts @@ -4,31 +4,34 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { Column } from '../db' + export interface Grouping<V, K> { + map: Map<K, V[]>, keys: ReadonlyArray<K>, groups: ReadonlyArray<ReadonlyArray<V>> } class GroupingImpl<K, V> { - private byKey = new Map<K, V[]>(); + readonly map = new Map<K, V[]>(); readonly keys: K[] = []; readonly groups: V[][] = []; add(a: V) { const key = this.getKey(a) as any; - if (!!this.byKey.has(key)) { - const group = this.byKey.get(key)!; + if (!!this.map.has(key)) { + const group = this.map.get(key)!; group[group.length] = a; } else { const group = [a]; - this.byKey.set(key, group); + this.map.set(key, group); this.keys[this.keys.length] = key; this.groups[this.groups.length] = group; } } getGrouping(): Grouping<V, K> { - return { keys: this.keys, groups: this.groups }; + return { keys: this.keys, groups: this.groups, map: this.map }; } constructor(private getKey: (v: V) => K) { } @@ -38,10 +41,13 @@ export function Grouper<V, K>(getKey: (x: V) => K) { return new GroupingImpl<K, V>(getKey); } -function groupBy<V, K>(values: ArrayLike<V>, getKey: (x: V) => K) { +export function groupBy<V, K>(values: ArrayLike<V> | Column<V>, getKey: (x: V) => K) { const gs = Grouper(getKey); - for (let i = 0, _i = values.length; i < _i; i++) gs.add(values[i]); + if (Column.is(values)) { + const v = values.value; + for (let i = 0, _i = values.rowCount; i < _i; i++) gs.add(v(i)); + } else { + for (let i = 0, _i = values.length; i < _i; i++) gs.add(values[i]); + } return gs.getGrouping(); -} - -export default groupBy; \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-data/util/hash-set.ts b/src/mol-data/util/hash-set.ts index 297b8eeb6b9c09c121739a7c9a468d3443a343dc..0e41ad62f323deef6a486af6de38464f69919dbc 100644 --- a/src/mol-data/util/hash-set.ts +++ b/src/mol-data/util/hash-set.ts @@ -45,8 +45,6 @@ class HashSetImpl<T> implements SetLike<T> { } // TODO: add implementations with multilevel hashing support? -function HashSet<T>(getHash: (v: T) => any, areEqual: (a: T, b: T) => boolean): SetLike<T> { +export function HashSet<T>(getHash: (v: T) => any, areEqual: (a: T, b: T) => boolean): SetLike<T> { return new HashSetImpl<T>(getHash, areEqual); -} - -export default HashSet; \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-data/util/unique-array.ts b/src/mol-data/util/unique-array.ts index f628baa9601ff5d14c01a8af256b996fcf150786..5a5caa501af2ba0ba4098c9cf0904ee8d9296859 100644 --- a/src/mol-data/util/unique-array.ts +++ b/src/mol-data/util/unique-array.ts @@ -21,4 +21,4 @@ namespace UniqueArray { } } -export default UniqueArray \ No newline at end of file +export { UniqueArray } \ No newline at end of file diff --git a/src/mol-io/common/binary-cif/array-encoder.ts b/src/mol-io/common/binary-cif/array-encoder.ts index 344171b8a8a910f45d32d6c6d885ab9c46b6200f..67a9084ca0a578179bcf589a3e4a47907ed2e6ee 100644 --- a/src/mol-io/common/binary-cif/array-encoder.ts +++ b/src/mol-io/common/binary-cif/array-encoder.ts @@ -7,7 +7,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import ChunkedArray from 'mol-data/util/chunked-array' +import { ChunkedArray } from 'mol-data/util' import { Encoding, EncodedData } from './encoding' export interface ArrayEncoder { diff --git a/src/mol-model/structure/model/properties/symmetry.ts b/src/mol-model/structure/model/properties/symmetry.ts index 38303de0e1951c84397c3aad3621a8a6b4055730..dfe599c2a6aec7c4a32cc8c5499e0cb7112d8a18 100644 --- a/src/mol-model/structure/model/properties/symmetry.ts +++ b/src/mol-model/structure/model/properties/symmetry.ts @@ -5,6 +5,7 @@ */ import SymmetryOperator from 'mol-math/geometry/symmetry-operator' +import { arrayFind } from 'mol-data/util' import { Query } from '../../query' import { Model } from '../../model' @@ -47,12 +48,8 @@ namespace Symmetry { export const Empty: Symmetry = { assemblies: [] }; export function findAssembly(model: Model, id: string): Assembly | undefined { - const { assemblies } = model.symmetry; const _id = id.toLocaleLowerCase(); - for (let i = 0; i < assemblies.length; i++) { - if (assemblies[i].id.toLowerCase() === _id) return assemblies[i]; - } - return void 0; + return arrayFind(model.symmetry.assemblies, a => a.id.toLowerCase() === _id); } } diff --git a/src/mol-model/structure/query/selection.ts b/src/mol-model/structure/query/selection.ts index 8504e425237b3761fdb7c3704aef40f3b8fd1432..c1e0f9fb179d72184e14e492c440bf7d60e6c0b5 100644 --- a/src/mol-model/structure/query/selection.ts +++ b/src/mol-model/structure/query/selection.ts @@ -5,7 +5,7 @@ */ import Iterator from 'mol-data/iterator' -import HashSet from 'mol-data/util/hash-set' +import { HashSet } from 'mol-data/util' import { Structure, Atom, AtomSet } from '../structure' type Selection = diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 317ea029f1593ae17ff62f7cc3e60222c786f7b5..8d5af1dcb383aafde5854ecbdaf787a01b6f734a 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -5,7 +5,7 @@ */ import { OrderedSet, Iterator } from 'mol-data/int' -import UniqueArray from 'mol-data/util/unique-array' +import { UniqueArray } from 'mol-data/util' import SymmetryOperator from 'mol-math/geometry/symmetry-operator' import { Model, Format } from '../model' import Unit from './unit' diff --git a/src/perf-tests/chunked-array-vs-native.ts b/src/perf-tests/chunked-array-vs-native.ts index 1b145425d76503a02dcd3e6ddad59d9496273707..e7843412a322591b87789205f0dbae2ce4de27dc 100644 --- a/src/perf-tests/chunked-array-vs-native.ts +++ b/src/perf-tests/chunked-array-vs-native.ts @@ -1,5 +1,5 @@ import * as B from 'benchmark' -import ChunkedArray from 'mol-data/util/chunked-array' +import { ChunkedArray } from 'mol-data/util' function testNative(size: number) { const xs = new Array(size); diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts index 699c9037cafc14c7502954b09df33cee123854d4..07bb5919a28ed67f6b0427a68ba894c37ff3b5a1 100644 --- a/src/perf-tests/sets.ts +++ b/src/perf-tests/sets.ts @@ -331,7 +331,120 @@ export namespace ObjectVsMap { } } -ObjectVsMap.run(); +export namespace IntVsStringIndices { + type WithKeys<K> = { keys: K[], data: { [key: number]: number } } + type MapWithKeys = { keys: number[], map: Map<number, number> } + + function createCacheKeys(n: number): WithKeys<number> { + const data = Object.create(null), keys = []; + data.__ = void 0; + delete data.__; + for (let i = 1; i <= n; i++) { + const k = i * (i + 1); + keys[keys.length] = k; + data[k] = i + 1; + } + return { data, keys }; + } + + function createMapKeys(n: number): MapWithKeys { + const map = new Map<number, number>(), keys = []; + for (let i = 1; i <= n; i++) { + const k = i * (i + 1); + keys[keys.length] = k; + map.set(k, i + 1); + } + return { map, keys }; + } + + export function createInt(n: number) { + const ret = Object.create(null); + ret.__ = void 0; + delete ret.__; + for (let i = 1; i <= n; i++) ret[i * (i + 1)] = i + 1; + return ret; + } + + export function createStr(n: number) { + const ret = Object.create(null); + ret.__ = void 0; + delete ret.__; + for (let i = 1; i <= n; i++) ret['' + (i * (i + 1))] = i + 1; + return ret; + } + + export function createMap(n: number) { + const map = new Map<number, number>(); + for (let i = 1; i <= n; i++) map.set(i * (i + 1), i + 1); + return map; + } + + export function sumInt(xs: { [key: number]: number }) { + let s = 0; + const keys = Object.keys(xs); + for (let i = 0, _i = keys.length; i < _i; i++) s += xs[+keys[i]]; + return s; + } + + export function sumStr(xs: { [key: string]: number }) { + let s = 0; + const keys = Object.keys(xs); + for (let i = 0, _i = keys.length; i < _i; i++) s += xs[keys[i]]; + return s; + } + + export function sumMap(map: Map<number, number>) { + let s = 0; + const values = map.keys(); + while (true) { + const { done, value } = values.next(); + if (done) break; + s += value; + } + return s; + } + + export function sumCached(xs: WithKeys<number>) { + let s = 0; + const keys = xs.keys, data = xs.data; + for (let i = 0, _i = keys.length; i < _i; i++) s += data[keys[i]]; + return s; + } + + export function sumKeyMap(xs: MapWithKeys) { + let s = 0; + const keys = xs.keys, map = xs.map; + for (let i = 0, _i = keys.length; i < _i; i++) s += map.get(keys[i])!; + return s; + } + + export function run() { + const N = 1000; + //const int = createInt(N); + const map = createMap(N); + //const str = createStr(N); + const keys = createCacheKeys(N); + const keyMap = createMapKeys(N); + console.log(sumMap(map), sumCached(keys), sumKeyMap(keyMap)); + new B.Suite() + //.add('c int', () => createInt(N)) + .add('q map', () => sumMap(map)) + .add('c map', () => createMap(N)) + .add('c mk', () => createMapKeys(N)) + //.add('c str', () => createStr(N)) + .add('c cc', () => createCacheKeys(N)) + //.add('q int', () => sumInt(int)) + .add('q mk', () => sumKeyMap(keyMap)) + //.add('q str', () => sumStr(str)) + .add('q cc', () => sumCached(keys)) + .on('cycle', (e: any) => console.log(String(e.target))) + .run(); + } +} + +IntVsStringIndices.run(); + +//ObjectVsMap.run(); //testSegments();