Home Reference Source Repository

lib/identifier.js

'use strict';

const BI = require('BigInt');
const Triple = require('./triple.js');
const LSeqNode = require('./lseqnode.js');

/**
 * Unique and immutable identifier composed of digit, sources, counters.
 */
class Identifier {
    
    /**
     * @param {Base} base The base of identifiers.
     * @param {Number[]} digits The digit (position in dense space).
     * @param {Object[]} sites The list of sources.
     * @param {Number[]} counters The list of counters.
     */
    constructor (base, digits, sites = [], counters = []) {
        this._d = digits;
        this._s = sites;
        this._c = counters;
        
        this._base = base;
    };


    /**
     * Set the d,s,c values according to the node in argument
     * @param {LSeqNode} node The lseqnode containing the path in the tree
     * structure.
     * @return {Identifier} This identifier modified.
     */
    fromNode (node) {
        // #1 process the length of the path
        let length = 1, tempNode = node;
        
        while (!tempNode.isLeaf) {
	    ++length;
            tempNode = tempNode.child;
        };
        // #2 copy the values contained in the path
        this._d = BI.int2bigInt(0, this._base.getSumBit(length - 1));
        
        for (let i = 0; i < length ; ++i) {
            // #1a copy the site id
            this._s.push(node.t.s);
            // #1b copy the counter
            this._c.push(node.t.c);
            // #1c copy the digit
            BI.addInt_(this._d, node.t.p);
            if (i !== length - 1) {
                BI.leftShift_(this._d, this._base.getBitBase(i+1));
            };
            node = node.child;
        };
        
        return this;
    };
    
    /**
     * Convert the identifier into a node without element.
     * @param {Object} e The element associated with the node.
     * @return {LSeqNode} An LSeqNode containing the element and the path
     * extracted from this identifier.
     */
    toNode (e) {
        const dBitLength = this._base.getSumBit(this._c.length - 1);
        let resultPath = [], mine;
        
        // #1 deconstruct the digit 
        for (let i = 0; i < this._c.length; ++i) {
            // #1 truncate mine
            mine = BI.dup(this._d);
            // #1a shift right to erase the tail of the path
            BI.rightShift_(mine, dBitLength - this._base.getSumBit(i));
            // #1b copy value in the result
            resultPath.push(
                new Triple(BI.modInt(mine,
                                     Math.pow(2, this._base.getBitBase(i))),
                           this._s[i],
                           this._c[i]));
        };
        return new LSeqNode(resultPath, e);
    };


    /**
     * Compare two identifiers.
     * @param {Identifier} o The other identifier.
     * @return {Integer} -1 if this is lower, 0 if they are equal, 1 if this is
     * greater.
     */
    compareTo (o) {
        let dBitLength = this._base.getSumBit(this._c.length - 1),
            odBitLength = this._base.getSumBit(o._c.length - 1),
            comparing = true,
            result = 0, i = 0,
            sum, mine, other;
        
        // #1 Compare the list of <d,s,c>
        while (comparing && i < Math.min(this._c.length, o._c.length) ) {
            // can stop before the end of for loop wiz return
            sum = this._base.getSumBit(i);
            // #1a truncate mine
            mine = BI.dup(this._d);
            BI.rightShift_(mine, dBitLength - sum);
            // #1b truncate other
            other = BI.dup(o._d);
            BI.rightShift_(other, odBitLength - sum);
            // #2 Compare triples
            // #A digit
            if (!BI.equals(mine, other)) {
                if (BI.greater(mine, other)) {
                    result = 1;
                } else {
                    result = -1;
                };
                comparing = false;
            } else {
                // #B source
                result = this._s[i] - o._s[i]; 
                if (result !== 0) {
                    comparing = false;
                } else {
                    // #C counter
                    result = this._c[i] - o._c[i];
                    if (result !== 0) {
                        comparing = false;
                    };
                };
            };
            ++i;
        };
        
        // #3 compare list size
        if (result === 0){
            result = this._c.length - o._c.length;
        };
        
        return result;
    };    
};


module.exports = Identifier;