client/util/helpers.js

var helpers = require('../../common/helpers.js');

/**
 * Contains helper functions: these may be overriden by extensions to customize behavior
 * @see {@link helpers}
 * @name helpers
 * @alias helpers
 * @namespace
 */

module.exports = function (jiffClient) {
  /**
   * Polyfill for jQuery Deferred
   * From https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred
   * @memberof helpers
   * @constructor Deferred
   */
  jiffClient.helpers.Deferred = function () {
    /**
     * A method to resolve the associate Promise with the value passed.
     * @method resolve
     * @memberof helpers.Deferred
     * @instance
     * @param {*} value - the value to resolve the promise with
     */
    this.resolve = null;

    /**
     * A method to reject the associated Promise with the value passed.
     * If the promise is already settled it does nothing.
     * @method reject
     * @memberof helpers.Deferred
     * @instance
     * @param {*} reason - The reason for the rejection of the Promise.
     * Generally its an Error object. If however a Promise is passed, then the Promise
     * itself will be the reason for rejection no matter the state of the Promise.
     */
    this.reject = null;

    /**
     * A newly created Promise object.
     * Initially in pending state.
     * @memberof helpers.Deferred
     * @member {Promise} promise
     * @instance
     */
    this.promise = new Promise(function (resolve, reject) {
      this.resolve = resolve;
      this.reject = reject;
    }.bind(this));

    Object.freeze(this);
  };

  /**
   * Correct Mod instead of javascript's remainder (%).
   * @memberof helpers
   * @method
   * @param {number} x - the number.
   * @param {number} y - the mod.
   * @return {number} x mod y.
   */
  jiffClient.helpers.mod = helpers.mod;

  /**
   * Ceil of a number.
   * @memberof helpers
   * @method
   * @param {number} x - the number to ceil.
   * @return {number} ceil of x.
   */
  jiffClient.helpers.ceil = Math.ceil;

  /**
   * Floor of a number
   * @memberof helpers
   * @method
   * @param {number} x - the number to floor.
   * @return {number} floor of x.
   */
  jiffClient.helpers.floor = Math.floor;

  /**
   * Fast Exponentiation Mod
   * @memberof helpers
   * @method
   * @param {number} a - the base number
   * @param {number} b - the power
   * @param {number} n - the mod
   * @return {number} (base^pow) mod m
   */
  jiffClient.helpers.pow_mod = function (a, b, n) {
    a = jiffClient.helpers.mod(a, n);
    var result = 1;
    var x = a;
    while (b > 0) {
      var leastSignificantBit = jiffClient.helpers.mod(b, 2);
      b = Math.floor(b / 2);
      if (leastSignificantBit === 1) {
        result = result * x;
        result = jiffClient.helpers.mod(result, n);
      }
      x = x * x;
      x = jiffClient.helpers.mod(x, n);
    }
    return result;
  };

  /**
   * Extended Euclidean for finding inverses.
   * @method
   * @memberof helpers
   * @param {number} a - the number to find inverse for.
   * @param {number} b - the mod.
   * @return {number[]} [inverse of a mod b, coefficient for a, coefficient for b].
   */
  jiffClient.helpers.extended_gcd = function (a, b) {
    if (b === 0) {
      return [1, 0, a];
    }

    var temp = jiffClient.helpers.extended_gcd(b, jiffClient.helpers.mod(a, b));
    var x = temp[0];
    var y = temp[1];
    var d = temp[2];
    return [y, x - y * Math.floor(a / b), d];
  };

  /**
   * Compute Log to a given base.
   * @method
   * @memberof helpers
   * @param {number} value - the number to find log for.
   * @param {number} [base=2] - the base (2 by default).
   * @return {number} log(value) with the given base.
   */
  jiffClient.helpers.bLog = function (value, base) {
    if (base == null) {
      base = 2;
    }
    return Math.log(value) / Math.log(base);
  };

  /**
   * Check that two sorted arrays are equal.
   * @method
   * @memberof helpers
   * @param {Array} arr1 - the first array.
   * @param {Array} arr2 - the second array.
   * @return {boolean} true if arr1 is equal to arr2, false otherwise.
   */
  jiffClient.helpers.array_equals = function (arr1, arr2) {
    if (arr1.length !== arr2.length) {
      return false;
    }

    for (var i = 0; i < arr1.length; i++) {
      if (arr1[i] !== arr2[i]) {
        return false;
      }
    }

    return true;
  };

  /**
   * Check that two Zps are equal. Used to determine if shares can be computed on or not.
   * @method
   * @memberof helpers
   * @param {SecretShare} s1 - the first share.
   * @param {SecretShare} s2 - the second share.
   * @return {boolean} true both shares have the same Zp, false otherwise.
   */
  jiffClient.helpers.Zp_equals = function (s1, s2) {
    return s1.Zp === s2.Zp;
  };

  /**
   * Generate a random integer between 0 and max-1 [inclusive].
   * Modify this to change the source of randomness and how it is generated.
   * @method
   * @memberof helpers
   * @param {number} max - the maximum number.
   * @return {number} the random number.
   */
  jiffClient.helpers.random = helpers.random;

  /**
   * Get the party number from the given party_id, the number is used to compute/open shares.
   * If party id was a number (regular party), that number is returned,
   * If party id refers to the ith server, then party_count + i is returned (i > 0).
   * @method
   * @memberof helpers
   * @param {number|string} party_id - the party id from which to compute the number.
   * @return {number} the party number (> 0).
   */
  jiffClient.helpers.get_party_number = helpers.get_party_number;

  /**
   * Transforms the given number to an array of bits (numbers).
   * Lower indices in the returned array corresponding to less significant bits.
   * @memberof helpers
   * @method
   * @param {number} number - the number to transform to binary
   * @param {length} [length=ceil(log2(number))] - if provided, then the given array will be padded with zeros to the length.
   * @return {number[]} the array of bits.
   */
  jiffClient.helpers.number_to_bits = helpers.number_to_bits;

  /**
   * Transforms the given array of bits to a number.
   * @memberof helpers
   * @method
   * @param {number[]} bits - the array of bits to compose as a number, starting from least to most significant bits.
   * @param {number} [length = bits.length] - if provided, only the first 'length' bits will be used
   * @return {number} the array of bits.
   */
  jiffClient.helpers.bits_to_number = function (bits, length) {
    if (length == null || length > bits.length) {
      length = bits.length;
    }
    return parseInt(bits.slice(0, length).reverse().join(''), 2);
  };

  /**
   * Checks if the given number is prime using AKS primality test
   * @method
   * @memberof helpers
   * @param {number} p - the number to check
   * @return {boolean} true if p is prime, false otherwise
   */
  jiffClient.helpers.is_prime = function (p) {
    // AKS Primality Test

    if (p === 2) {
      return true;
    } else if (p === 3) {
      return true;
    } else if (p % 2 === 0) {
      return false;
    } else if (p % 3 === 0) {
      return false;
    }

    var i = 5;
    var n = 2;
    while (i * i <= p) {
      if (p % i === 0) {
        return false;
      }
      i += n;
      n = 6 - n;
    }

    return true;
  };

  /**
   * sorts an array of ids (in place) according to a consistent ordering
   * @method
   * @memberof helpers
   * @param {array} ids - array of ids containing numbers or "s1"
   */
  jiffClient.helpers.sort_ids = function (ids) {
    if (ids.__jiff_sorted) {
      return;
    }

    ids.sort(function (e1, e2) {
      if (e1 === e2) {
        throw new Error('ids array has duplicated: ' + ids.toString());
      }
      if (e1 === 's1') {
        return 1;
      }
      if (e2 === 's1') {
        return -1;
      }
      return e1 - e2;
    });
    ids.__jiff_sorted = true;
  }
};