diff --git a/src/mol-data/util/_spec/combination.spec.ts b/src/mol-data/util/_spec/combination.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a11c12f84964ddece9d1641467c9efd0b373dc27 --- /dev/null +++ b/src/mol-data/util/_spec/combination.spec.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { combinations } from '../combination' + +describe('Combination', () => { + it('test123-2', () => { + const c = combinations([1, 2, 3], 2) + expect(c).toEqual([[1, 2], [1, 3], [2, 3]]); + }); + + it('test1234-2', () => { + const c = combinations([1, 2, 3, 4], 2) + expect(c).toEqual([[1, 2], [1, 3], [2, 3], [1, 4], [2, 4], [3, 4]]); + }); + + it('test1234-1', () => { + const c = combinations([1, 2, 3, 4], 1) + expect(c).toEqual([[1], [2], [3], [4]]); + }); + + it('test1234-4', () => { + const c = combinations([1, 2, 3, 4], 4) + expect(c).toEqual([[1, 2, 3, 4]]); + }); +}); \ No newline at end of file diff --git a/src/mol-data/util/combination.ts b/src/mol-data/util/combination.ts new file mode 100644 index 0000000000000000000000000000000000000000..d37a23eecf9ff4d97b5f83364ae38c918ffbf114 --- /dev/null +++ b/src/mol-data/util/combination.ts @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +// adpated from https://github.com/dankogai/js-combinatorics, MIT 2013-2016 Dan Kogai + +import Iterator from '../iterator' + +function P(m: number, n: number) { + let p = 1 + while (n--) p *= m-- + return p +} + +function C(m: number, n: number) { + if (n > m) return 0 + return P(m, n) / P(n, n) +} + +function nextIndex(n: number) { + const smallest = n & -n + const ripple = n + smallest + const newSmallest = ripple & -ripple + const ones = ((newSmallest / smallest) >> 1) - 1 + return ripple | ones +}; + +export class CombinationIterator<T> implements Iterator<T[]> { + private value: T[] + private index: number + private maxIndex: number + + size: number + hasNext: boolean = false; + + move() { + if (this.hasNext) { + let i = 0, j = 0, n = this.index + for (; n; n >>>= 1, i++) { + if (n & 1) this.value[j++] = this.array[i] + } + this.index = nextIndex(this.index) + this.hasNext = this.index < this.maxIndex + } + return this.value; + } + + constructor(private array: T[], count: number) { + this.index = (1 << count) - 1 + this.size = C(array.length, count) + this.maxIndex = 1 << array.length, + + this.value = new Array(count) + this.hasNext = count > 0 && count <= array.length + } +} + +export function combinations<T>(array: T[], count: number): T[][] { + const out: T[][] = [] + const combinationIt = new CombinationIterator(array, count) + while (combinationIt.hasNext) out.push(combinationIt.move().slice()) + return out +} \ No newline at end of file