"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DicingStoreImpl = exports.DicingStore = void 0;
var DicingStore;
(function (DicingStore) {
    function create(rep) {
        return DicingStoreImpl.FromRep(rep);
    }
    DicingStore.create = create;
})(DicingStore || (exports.DicingStore = DicingStore = {}));
class DicingStoreImpl {
    constructor() {
        this.clsDefX = new Map();
        this.coClsDefX = [];
        this.clsDefY = new Map();
        this.coClsDefY = [];
        this.dataMatrix = [];
    }
    static FromRep(rep) {
        const store = new DicingStoreImpl();
        if (!rep)
            return store;
        store.clsDefX = toClassMap(rep.xClasses);
        store.coClsDefX = duplicateArray2(rep.xClasses);
        store.clsDefY = toClassMap(rep.yClasses);
        store.coClsDefY = duplicateArray2(rep.yClasses);
        store.dataMatrix = duplicateArray2(rep.data);
        return store;
    }
    toRep() {
        return {
            xClasses: duplicateArray2(this.coClsDefX),
            yClasses: duplicateArray2(this.coClsDefY),
            data: duplicateArray2(this.dataMatrix)
        };
    }
    getData(kx, ky) {
        if (!this.dataMatrix[kx])
            return undefined;
        return this.dataMatrix[kx][ky];
    }
    putData(kx, ky, d) {
        if (!this.dataMatrix[kx])
            this.dataMatrix[kx] = [];
        this.dataMatrix[kx][ky] = d;
    }
    diceOneDimension(cd, coCd, mdf) {
        const plans = [];
        let inSet;
        let outSet;
        const nExistingClasses = coCd.length;
        let nTotalClasses = coCd.length;
        for (let cl = 0; cl < nExistingClasses; cl++) {
            inSet = [];
            outSet = [];
            const kg = coCd[cl];
            if (!kg || !kg.length)
                continue;
            for (const g of kg) {
                if (mdf.has(g)) {
                    inSet.push(g);
                    mdf.delete(g);
                }
                else {
                    outSet.push(g);
                }
            }
            if (inSet.length) {
                if (outSet.length) {
                    const clsNew = nTotalClasses++;
                    for (const x of inSet)
                        cd.set(x, clsNew);
                    coCd[clsNew] = inSet;
                    coCd[cl] = outSet;
                    plans.push({ cls: clsNew, from: cl, inSet: true });
                    plans.push({ cls: cl, from: null, inSet: false });
                }
                else {
                    plans.push({ cls: cl, from: null, inSet: true });
                }
            }
        }
        if (mdf.size) {
            const clsNew = nTotalClasses++;
            for (const x of mdf)
                cd.set(x, clsNew);
            coCd[clsNew] = [...mdf];
            plans.push({ cls: clsNew, from: null, inSet: true });
        }
        return plans;
    }
    update(mdfX, mdfY, fn) {
        const mdfXSet = new Set(mdfX);
        const mdfYSet = new Set(mdfY);
        if (!mdfXSet.size || !mdfYSet.size)
            return;
        const nOldClsX = this.coClsDefX.length;
        const nOldClsY = this.coClsDefY.length;
        const planX = this.diceOneDimension(this.clsDefX, this.coClsDefX, mdfXSet);
        const planY = this.diceOneDimension(this.clsDefY, this.coClsDefY, mdfYSet);
        for (const px of planX) {
            if (px.from == null)
                continue;
            for (let cy = 0; cy < nOldClsY; cy++) {
                this.putData(px.cls, cy, this.getData(px.from, cy));
            }
        }
        for (const py of planY) {
            if (py.from == null)
                continue;
            for (let cx = 0; cx < nOldClsX; cx++) {
                this.putData(cx, py.cls, this.getData(cx, py.from));
            }
        }
        for (const px of planX) {
            for (const py of planY) {
                const cx = px.from == null ? px.cls : px.from;
                const cy = py.from == null ? py.cls : py.from;
                const orig = this.getData(cx, cy);
                if (px.inSet && py.inSet) {
                    this.putData(px.cls, py.cls, fn(orig));
                }
                else {
                    this.putData(px.cls, py.cls, orig);
                }
            }
        }
    }
    get(x, y) {
        const clX = this.clsDefX.get(x);
        if (clX == null)
            return undefined;
        const clY = this.clsDefY.get(y);
        if (clY == null)
            return undefined;
        return this.getData(clX, clY);
    }
    getByClass(cx, cy) {
        if (cx < 0 || cy < 0)
            return undefined;
        return this.getData(cx, cy);
    }
    getClassDefImpl(cd) {
        const a = [];
        for (const [x, cl] of cd) {
            if (!a[cl])
                a[cl] = [];
            a[cl].push(x);
        }
        for (let cl = 0; cl < a.length; cl++)
            if (!a[cl])
                a[cl] = [];
        return a;
    }
    getXClassDef() {
        return this.getClassDefImpl(this.clsDefX);
    }
    getYClassDef() {
        return this.getClassDefImpl(this.clsDefY);
    }
    *entries() {
        for (const [x, c1] of this.clsDefX) {
            for (const [y, c2] of this.clsDefY) {
                yield [x, y, this.getByClass(c1, c2)];
            }
        }
    }
    set(x, y, v) {
        this.update(x, y, () => v);
    }
    setIfAbsent(x, y, v) {
        this.update(x, y, orig => (orig == null ? v : orig));
    }
}
exports.DicingStoreImpl = DicingStoreImpl;
// util function
function duplicateArray2(a) {
    const r = [];
    for (let cx = 0; cx < a.length; cx++) {
        const row = a[cx] || [];
        r[cx] = [...row];
    }
    return r;
}
function toClassMap(a) {
    const r = new Map();
    for (let cx = 0; cx < a.length; cx++) {
        const row = a[cx];
        if (!row)
            continue;
        for (const x of row)
            r.set(x, cx);
    }
    return r;
}
//# sourceMappingURL=index.js.map