From 024c82ff92b0b9a708920591d3eb0ac066164014 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 27 Jul 2018 09:51:47 -0700 Subject: [PATCH] added combination iterator --- src/mol-data/util/_spec/combination.spec.ts | 29 +++++++++ src/mol-data/util/combination.ts | 65 +++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/mol-data/util/_spec/combination.spec.ts create mode 100644 src/mol-data/util/combination.ts 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 000000000..a11c12f84 --- /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 000000000..d37a23eec --- /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 -- GitLab