text: // Utils
  
  function assert(val, msg) {
    if (!val)
      throw new Error(msg || 'Assertion failed');
  }
  
  function assertEqual(l, r, msg) {
    if (l != r)
      throw new Error(msg || ('Assertion failed: ' + l + ' != ' + r));
  }
  
  // Could use `inherits` module, but don't want to move from single file
  // architecture yet.
  function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    var TempCtor = function () {}
    TempCtor.prototype = superCtor.prototype
    ctor.prototype = new TempCtor()
    ctor.prototype.constructor = ctor
  }
  
  // BN
  
  function BN(number, base) {
    // May be `new BN(bn)` ?
    if (number !== null &&
        typeof number === 'object' &&
        Array.isArray(number.words)) {
      return number;
    }
  
    this.sign = false;
    this.words = null;
    this.length = 0;
  
    // Reduction context
    this.red = null;
  
    if (number !== null)
      this._init(number || 0, base || 10);
  }
  if (typeof module === 'object')
    module.exports = BN;
  
  BN.BN = BN;
  BN.wordSize = 26;
  
  BN.prototype._init = function init(number, base) {
    if (typeof number === 'number') {
      if (number < 0) {
        this.sign = true;
        number = -number;
      }
      if (number < 0x4000000) {
        this.words = [ number & 0x3ffffff ];
        this.length = 1;
      } else {
        this.words = [
          number & 0x3ffffff,
          (number / 0x4000000) & 0x3ffffff
        ];
        this.length = 2;
      }
      return;
    } else if (typeof number === 'object') {
      // Perhaps a Uint8Array
      assert(typeof number.length === 'number');
      this.length = Math.ceil(number.length / 3);
      this.words = new Array(this.length);
      for (var i = 0; i < this.length; i++)
        this.words[i] = 0;
  
      // Assume big-endian
      var off = 0;
      for (var i = number.length - 1, j = 0; i >= 0; i -= 3) {
        var w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16);
        this.words[j] |= (w << off) & 0x3ffffff;
        this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff;
        off += 24;
        if (off >= 26) {
          off -= 26;
          j++;
        }
      }
  
      return this.strip();
    }
    if (base === 'hex')
      base = 16;
    assert(base === (base | 0) && base >= 2 && base <= 36);
  
    number = number.toString().replace(/\s+/g, '');
    var start = 0;
    if (number[0] === '-')
      start++;
  
    if (base === 16)
      this._parseHex(number, start);
    else
      this._parseBase(number, base, start);
  
    if (number[0] === '-')
      this.sign = true;
  
    this.strip();
  };
  
  BN.prototype._parseHex = function parseHex(number, start) {
    // Create possibly bigger array to ensure that it fits the number
    this.length = Math.ceil((number.length - start) / 6);
    this.words = new Array(this.length);
    for (var i = 0; i < this.length; i++)
      this.words[i] = 0;
  
    // Scan 24-bit chunks and add them to the number
    var off = 0;
    for (var i = number.length - 6, j = 0; i >= start; i -= 6) {
      var w = parseInt(number.slice(i, i + 6), 16);
      this.words[j] |= (w << off) & 0x3ffffff;
      this.words[j + 1] |= w >>> (26 - off) & 0x3fffff;
      off += 24;
      if (off >= 26) {
        off -= 26;
        j++;
      }
    }
    if (i + 6 !== start) {
      var w = parseInt(number.slice(start, i + 6), 16);
      this.words[j] |= (w << off) & 0x3ffffff;
      this.words[j + 1] |= w >>> (26 - off) & 0x3fffff;
    }
    this.strip();
  };
  
  BN.prototype._parseBase = function parseBase(number, base, start) {
    // Initialize as zero
    this.words = [ 0 ];
    this.length = 1;
  
    var word = 0;
    var q = 1;
    var p = 0;
    var bigQ = null;
    for (var i = start; i < number.length; i++) {
      var digit;
      var ch = number[i];
      if (base === 10 || ch <= '9')
        digit = ch | 0;
      else if (ch >= 'a')
        digit = ch.charCodeAt(0) - 97 + 10;
      else
        digit = ch.charCodeAt(0) - 65 + 10;
      word *= base;
      word += digit;
      q *= base;
      p++;
  
      if (q > 0xfffff) {
        assert(q <= 0x3ffffff);
        if (!bigQ)
          bigQ = new BN(q);
        this.mul(bigQ).copy(this);
        this.iadd(new BN(word));
        word = 0;
        q = 1;
        p = 0;
      }
    }
    if (p !== 0) {
      this.mul(new BN(q)).copy(this);
      this.iadd(new BN(word));
    }
  };
  
  BN.prototype.copy = function copy(dest) {
    dest.words = new Array(this.length);
    for (var i = 0; i < this.length; i++)
      dest.words[i] = this.words[i];
    dest.length = this.length;
    dest.sign = this.sign;
    dest.red = this.red;
  };
  
  BN.prototype.clone = function clone() {
    var r = new BN(null);
    this.copy(r);
    return r;
  };
  
  // Remove leading `0` from `this`
  BN.prototype.strip = function strip() {
    while (this.length > 1 && this.words[this.length - 1] === 0)
      this.length--;
    return this._normSign();
  };
  
  BN.prototype._normSign = function _normSign() {
    // -0 = 0
    if (this.length === 1 && this.words[0] === 0)
      this.sign = false;
    return this;
  };
  
  BN.prototype.inspect = function inspect() {
    return (this.red ? '<BN-R: ' : '<BN: ') + this.toString(16) + '>';
  };
  
  /*
  
  var zeros = [];
  var groupSizes = [];
  var groupBases = [];
  
  var s = '';
  var i = -1;
  while (++i < BN.wordSize) {
    zeros[i] = s;
    s += '0';
  }
  groupSizes[0] = 0;
  groupSizes[1] = 0;
  groupBases[0] = 0;
  groupBases[1] = 0;
  var base = 2 - 1;
  while (++base < 36 + 1) {
    var groupSize = 0;
    var groupBase = 1;
    // TODO: <=
    while (groupBase < (1 << BN.wordSize) / base) {
      groupBase *= base;
      groupSize += 1;
    }
    groupSizes[base] = groupSize;
    groupBases[base] = groupBase;
  }
  
  */
  
  var zeros = [
    '',
    '0',
    '00',
    '000',
    '0000',
    '00000',
    '000000',
    '0000000',
    '00000000',
    '000000000',
    '0000000000',
    '00000000000',
    '000000000000',
    '0000000000000',
    '00000000000000',
    '000000000000000',
    '0000000000000000',
    '00000000000000000',
    '000000000000000000',
    '0000000000000000000',
    '00000000000000000000',
    '000000000000000000000',
    '0000000000000000000000',
    '00000000000000000000000',
    '000000000000000000000000',
    '0000000000000000000000000'
  ];
  
  var groupSizes = [
    0, 0,
    25, 16, 12, 11, 10, 9, 8,
    8, 7, 7, 7, 7, 6, 6,
    6, 6, 6, 6, 6, 5, 5,
    5, 5, 5, 5, 5, 5, 5,
    5, 5, 5, 5, 5, 5, 5
  ];
  
  var groupBases = [
    0, 0,
    33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216,
    43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625,
    16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632,
    6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149,
    24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176
  ];
  
  BN.prototype.toString = function toString(base, padding) {
    base = base || 10;
    if (base === 16 || base === 'hex') {
      var out = '';
      var off = 0;
      var padding = padding | 0 || 1;
      var carry = 0;
      for (var i = 0; i < this.length; i++) {
        var w = this.words[i];
        var word = (((w << off) | carry) & 0xffffff).toString(16);
        carry = (w >>> (24 - off)) & 0xffffff;
        if (carry !== 0 || i !== this.length - 1)
          out = zeros[6 - word.length] + word + out;
        else
          out = word + out;
        off += 2;
        if (off >= 26) {
          off -= 26;
          i--;
        }
      }
      if (carry !== 0)
        out = carry.toString(16) + out;
      while (out.length % padding !== 0)
        out = '0' + out;
      if (this.sign)
        out = '-' + out;
      return out;
    } else if (base === (base | 0) && base >= 2 && base <= 36) {
      // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base));
      var groupSize = groupSizes[base];
      // var groupBase = Math.pow(base, groupSize);
      var groupBase = groupBases[base];
      var out = '';
      var c = this.clone();
      c.sign = false;
      while (c.cmpn(0) !== 0) {
        var r = c.modn(groupBase).toString(base);
        c = c.idivn(groupBase);
  
        if (c.cmpn(0) !== 0)
          out = zeros[groupSize - r.length] + r + out;
        else
          out = r + out;
      }
      if (this.cmpn(0) === 0)
        out = '0' + out;
      if (this.sign)
        out = '-' + out;
      return out;
    } else {
      assert(false, 'Base should be between 2 and 36');
    }
  };
  
  BN.prototype.toJSON = function toJSON() {
    return this.toString(16);
  };
  
  BN.prototype.toArray = function toArray() {
    this.strip();
    var res = new Array(this.byteLength());
    res[0] = 0;
  
    var q = this.clone();
    for (var i = 0; q.cmpn(0) !== 0; i++) {
      var b = q.andln(0xff);
      q.ishrn(8);
  
      // Assume big-endian
      res[res.length - i - 1] = b;
    }
  
    return res;
  };
  
  /*
  function genCountBits(bits) {
    var arr = [];
  
    for (var i = bits - 1; i >= 0; i--) {
      var bit = '0x' + (1 << i).toString(16);
      arr.push('w >= ' + bit + ' ? ' + (i + 1));
    }
  
    return new Function('w', 'return ' + arr.join(' :\n') + ' :\n0;');
  };
  
  BN.prototype._countBits = genCountBits(26);
  */
  
  // Sadly chrome apps could not contain `new Function()` calls
  BN.prototype._countBits = function _countBits(w) {
    return w >= 0x2000000 ? 26 :
           w >= 0x1000000 ? 25 :
           w >= 0x800000 ? 24 :
           w >= 0x400000 ? 23 :
           w >= 0x200000 ? 22 :
           w >= 0x100000 ? 21 :
           w >= 0x80000 ? 20 :
           w >= 0x40000 ? 19 :
           w >= 0x20000 ? 18 :
           w >= 0x10000 ? 17 :
           w >= 0x8000 ? 16 :
           w >= 0x4000 ? 15 :
           w >= 0x2000 ? 14 :
           w >= 0x1000 ? 13 :
           w >= 0x800 ? 12 :
           w >= 0x400 ? 11 :
           w >= 0x200 ? 10 :
           w >= 0x100 ? 9 :
           w >= 0x80 ? 8 :
           w >= 0x40 ? 7 :
           w >= 0x20 ? 6 :
           w >= 0x10 ? 5 :
           w >= 0x8 ? 4 :
           w >= 0x4 ? 3 :
           w >= 0x2 ? 2 :
           w >= 0x1 ? 1 :
           0;
  };
  
  // Return number of used bits in a BN
  BN.prototype.bitLength = function bitLength() {
    var hi = 0;
    var w = this.words[this.length - 1];
    var hi = this._countBits(w);
    return (this.length - 1) * 26 + hi;
  };
  
  BN.prototype.byteLength = function byteLength() {
    var hi = 0;
    var w = this.words[this.length - 1];
    return Math.ceil(this.bitLength() / 8);
  };
  
  // Return negative clone of `this`
  BN.prototype.neg = function neg() {
    if (this.cmpn(0) === 0)
      return this.clone();
  
    var r = this.clone();
    r.sign = !this.sign;
    return r;
  };
  
  // Add `num` to `this` in-place
  BN.prototype.iadd = function iadd(num) {
    // negative + positive
    if (this.sign && !num.sign) {
      this.sign = false;
      var r = this.isub(num);
      this.sign = !this.sign;
      return this._normSign();
  
    // positive + negative
    } else if (!this.sign && num.sign) {
      num.sign = false;
      var r = this.isub(num);
      num.sign = true;
      return r._normSign();
    }
  
    // a.length > b.length
    var a;
    var b;
    if (this.length > num.length) {
      a = this;
      b = num;
    } else {
      a = num;
      b = this;
    }
  
    var carry = 0;
    for (var i = 0; i < b.length; i++) {
      var r = a.words[i] + b.words[i] + carry;
      this.words[i] = r & 0x3ffffff;
      carry = r >>> 26;
    }
    for (; carry !== 0 && i < a.length; i++) {
      var r = a.words[i] + carry;
      this.words[i] = r & 0x3ffffff;
      carry = r >>> 26;
    }
  
    this.length = a.length;
    if (carry !== 0) {
      this.words[this.length] = carry;
      this.length++;
    // Copy the rest of the words
    } else if (a !== this) {
      for (; i < a.length; i++)
        this.words[i] = a.words[i];
    }
  
    return this;
  };
  
  // Add `num` to `this`
  BN.prototype.add = function add(num) {
    if (num.sign && !this.sign) {
      num.sign = false;
      var res = this.sub(num);
      num.sign = true;
      return res;
    } else if (!num.sign && this.sign) {
      this.sign = false;
      var res = num.sub(this);
      this.sign = true;
      return res;
    }
  
    if (this.length > num.length)
      return this.clone().iadd(num);
    else
      return num.clone().iadd(this);
  };
  
  // Subtract `num` from `this` in-place
  BN.prototype.isub = function isub(num) {
    // this - (-num) = this + num
    if (num.sign) {
      num.sign = false;
      var r = this.iadd(num);
      num.sign = true;
      return r._normSign();
  
    // -this - num = -(this + num)
    } else if (this.sign) {
      this.sign = false;
      this.iadd(num);
      this.sign = true;
      return this._normSign();
    }
  
    // At this point both numbers are positive
    var cmp = this.cmp(num);
  
    // Optimization - zeroify
    if (cmp === 0) {
      this.sign = false;
      this.length = 1;
      this.words[0] = 0;
      return this;
    }
  
    // a > b
    if (cmp > 0) {
      var a = this;
      var b = num;
    } else {
      var a = num;
      var b = this;
    }
  
    var carry = 0;
    for (var i = 0; i < b.length; i++) {
      var r = a.words[i] - b.words[i] - carry;
      if (r < 0) {
        r += 0x4000000;
        carry = 1;
      } else {
        carry = 0;
      }
      this.words[i] = r;
    }
    for (; carry !== 0 && i < a.length; i++) {
      var r = a.words[i] - carry;
      if (r < 0) {
        r += 0x4000000;
        carry = 1;
      } else {
        carry = 0;
      }
      this.words[i] = r;
    }
  
    // Copy rest of the words
    if (carry === 0 && i < a.length && a !== this)
      for (; i < a.length; i++)
        this.words[i] = a.words[i];
    this.length = Math.max(this.length, i);
  
    if (a !== this)
      this.sign = true;
  
    return this.strip();
  };
  
  // Subtract `num` from `this`
  BN.prototype.sub = function sub(num) {
    return this.clone().isub(num);
  };
  
  /*
  // NOTE: This could be potentionally used to generate loop-less multiplications
  function _genCombMulTo(alen, blen) {
    var len = alen + blen - 1;
    var src = [
      'var a = this.words, b = num.words, o = out.words, c = 0, w, ' +
          'mask = 0x3ffffff, shift = 0x4000000;',
      'out.length = ' + len + ';'
    ];
    for (var k = 0; k < len; k++) {
      var minJ = Math.max(0, k - alen + 1);
      var maxJ = Math.min(k, blen - 1);
  
      for (var j = minJ; j <= maxJ; j++) {
        var i = k - j;
        var mul = 'a[' + i + '] * b[' + j + ']';
  
        if (j === minJ) {
          src.push('w = ' + mul + ' + c;');
          src.push('c = (w / shift) | 0;');
        } else {
          src.push('w += ' + mul + ';');
          src.push('c += (w / shift) | 0;');
        }
        src.push('w &= mask;');
      }
      src.push('o[' + k + '] = w;');
    }
    src.push('if (c !== 0) {',
             '  o[' + k + '] = c;',
             '  out.length++;',
             '}',
             'return out;');
  
    return src.join('\n');
  }
  */
  
  BN.prototype._smallMulTo = function _smallMulTo(num, out) {
    out.sign = num.sign !== this.sign;
    out.length = this.length + num.length;
  
    var carry = 0;
    for (var k = 0; k < out.length - 1; k++) {
      // Sum all words with the same `i + j = k` and accumulate `ncarry`,
      // note that ncarry could be >= 0x3ffffff
      var ncarry = carry >>> 26;
      var rword = carry & 0x3ffffff;
      var maxJ = Math.min(k, num.length - 1);
      for (var j = Math.max(0, k - this.length + 1); j <= maxJ; j++) {
        var i = k - j;
        var a = this.words[i] | 0;
        var b = num.words[j] | 0;
        var r = a * b;
  
        var lo = r & 0x3ffffff;
        ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0;
        lo = (lo + rword) | 0;
        rword = lo & 0x3ffffff;
        ncarry = (ncarry + (lo >>> 26)) | 0;
      }
      out.words[k] = rword;
      carry = ncarry;
    }
    if (carry !== 0) {
      out.words[k] = carry;
    } else {
      out.length--;
    }
  
    return out.strip();
  };
  
  BN.prototype._bigMulTo = function _bigMulTo(num, out) {
    out.sign = num.sign !== this.sign;
    out.length = this.length + num.length;
  
    var carry = 0;
    var hncarry = 0;
    for (var k = 0; k < out.length - 1; k++) {
      // Sum all words with the same `i + j = k` and accumulate `ncarry`,
      // note that ncarry could be >= 0x3ffffff
      var ncarry = hncarry;
      hncarry = 0;
      var rword = carry & 0x3ffffff;
      var maxJ = Math.min(k, num.length - 1);
      for (var j = Math.max(0, k - this.length + 1); j <= maxJ; j++) {
        var i = k - j;
        var a = this.words[i] | 0;
        var b = num.words[j] | 0;
        var r = a * b;
  
        var lo = r & 0x3ffffff;
        ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0;
        lo = (lo + rword) | 0;
        rword = lo & 0x3ffffff;
        ncarry = (ncarry + (lo >>> 26)) | 0;
  
        hncarry += ncarry >>> 26;
        ncarry &= 0x3ffffff;
      }
      out.words[k] = rword;
      carry = ncarry;
      ncarry = hncarry;
    }
    if (carry !== 0) {
      out.words[k] = carry;
    } else {
      out.length--;
    }
  
    return out.strip();
  };
  
  BN.prototype.mulTo = function mulTo(num, out) {
    var res;
    if (this.length + num.length < 63)
      res = this._smallMulTo(num, out);
    else
      res = this._bigMulTo(num, out);
    return res;
  };
  
  // Multiply `this` by `num`
  BN.prototype.mul = function mul(num) {
    var out = new BN(null);
    out.words = new Array(this.length + num.length);
    return this.mulTo(num, out);
  };
  
  // In-place Multiplication
  BN.prototype.imul = function imul(num) {
    if (this.cmpn(0) === 0 || num.cmpn(0) === 0) {
      this.words[0] = 0;
      this.length = 1;
      return this;
    }
  
    var tlen = this.length;
    var nlen = num.length;
  
    this.sign = num.sign !== this.sign;
    this.length = this.length + num.length;
    this.words[this.length - 1] = 0;
  
    var lastCarry = 0;
    for (var k = this.length - 2; k >= 0; k--) {
      // Sum all words with the same `i + j = k` and accumulate `carry`,
      // note that carry could be >= 0x3ffffff
      var carry = 0;
      var rword = 0;
      var maxJ = Math.min(k, nlen - 1);
      for (var j = Math.max(0, k - tlen + 1); j <= maxJ; j++) {
        var i = k - j;
        var a = this.words[i];
        var b = num.words[j];
        var r = a * b;
  
        var lo = r & 0x3ffffff;
        carry += (r / 0x4000000) | 0;
        lo += rword;
        rword = lo & 0x3ffffff;
        carry += lo >>> 26;
      }
      this.words[k] = rword;
      this.words[k + 1] += carry;
      carry = 0;
    }
  
    // Propagate overflows
    var carry = 0;
    for (var i = 1; i < this.length; i++) {
      var w = this.words[i] + carry;
      this.words[i] = w & 0x3ffffff;
      carry = w >>> 26;
    }
  
    return this.strip();
  };
  
  // `this` * `this`
  BN.prototype.sqr = function sqr() {
    return this.mul(this);
  };
  
  // `this` * `this` in-place
  BN.prototype.isqr = function isqr() {
    return this.mul(this);
  };
  
  // Shift-left in-place
  BN.prototype.ishln = function ishln(bits) {
    assert(typeof bits === 'number' && bits >= 0);
    var r = bits % 26;
    var s = (bits - r) / 26;
    var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r);
  
    var o = this.clone();
    if (r !== 0) {
      var carry = 0;
      for (var i = 0; i < this.length; i++) {
        var newCarry = this.words[i] & carryMask;
        var c = (this.words[i] - newCarry) << r;
        this.words[i] = c | carry;
        carry = newCarry >>> (26 - r);
      }
      if (carry) {
        this.words[i] = carry;
        this.length++;
      }
    }
  
    if (s !== 0) {
      for (var i = this.length - 1; i >= 0; i--)
        this.words[i + s] = this.words[i];
      for (var i = 0; i < s; i++)
        this.words[i] = 0;
      this.length += s;
    }
  
    return this.strip();
  };
  
  // Shift-right in-place
  // NOTE: `hint` is a lowest bit before trailing zeroes
  // NOTE: if `extended` is true - { lo: ..., hi: } object will be returned
  BN.prototype.ishrn = function ishrn(bits, hint, extended) {
    assert(typeof bits === 'number' && bits >= 0);
    if (hint)
      hint = (hint - (hint % 26)) / 26;
    else
      hint = 0;
  
    var r = bits % 26;
    var s = Math.min((bits - r) / 26, this.length);
    var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r);
    var maskedWords = extended;
  
    hint -= s;
    hint = Math.max(0, hint);
  
    // Extended mode, copy masked part
    if (maskedWords) {
      for (var i = 0; i < s; i++)
        maskedWords.words[i] = this.words[i];
      maskedWords.length = s;
    }
  
    if (s === 0) {
      // No-op, we should not move anything at all
    } else if (this.length > s) {
      this.length -= s;
      for (var i = 0; i < this.length; i++)
        this.words[i] = this.words[i + s];
    } else {
      this.words[0] = 0;
      this.length = 1;
    }
  
    var carry = 0;
    for (var i = this.length - 1; i >= 0 && (carry !== 0 || i >= hint); i--) {
      var word = this.words[i];
      this.words[i] = (carry << (26 - r)) | (word >>> r);
      carry = word & mask;
    }
  
    // Push carried bits as a mask
    if (maskedWords && carry !== 0)
      maskedWords.words[maskedWords.length++] = carry;
  
    if (this.length === 0) {
      this.words[0] = 0;
      this.length = 1;
    }
  
    this.strip();
    if (extended)
      return { hi: this, lo: maskedWords };
  
    return this;
  };
  
  // Shift-left
  BN.prototype.shln = function shln(bits) {
    return this.clone().ishln(bits);
  };
  
  // Shift-right
  BN.prototype.shrn = function shrn(bits) {
    return this.clone().ishrn(bits);
  };
  
  // Test if n bit is set
  BN.prototype.testn = function testn(bit) {
    assert(typeof bit === 'number' && bit >= 0);
    var r = bit % 26;
    var s = (bit - r) / 26;
    var q = 1 << r;
  
    // Fast case: bit is much higher than all existing words
    if (this.length <= s) {
      return false;
    }
  
    // Check bit and return
    var w = this.words[s];
  
    return !!(w & q);
  };
  
  // Return only lowers bits of number (in-place)
  BN.prototype.imaskn = function imaskn(bits) {
    assert(typeof bits === 'number' && bits >= 0);
    var r = bits % 26;
    var s = (bits - r) / 26;
  
    assert(!this.sign, 'imaskn works only with positive numbers');
  
    if (r !== 0)
      s++;
    this.length = Math.min(s, this.length);
  
    if (r !== 0) {
      var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r);
      this.words[this.length - 1] &= mask;
    }
  
    return this.strip();
  };
  
  // Return only lowers bits of number
  BN.prototype.maskn = function maskn(bits) {
    return this.clone().imaskn(bits);
  };
  
  // Add plain number `num` to `this`
  BN.prototype.iaddn = function iaddn(num) {
    assert(typeof num === 'number');
    if (num < 0)
      return this.isubn(-num);
  
    // Possible sign change
    if (this.sign) {
      if (this.length === 1 && this.words[0] < num) {
        this.words[0] = num - this.words[0];
        this.sign = false;
        return this;
      }
  
      this.sign = false;
      this.isubn(num);
      this.sign = true;
      return this;
    }
    this.words[0] += num;
  
    // Carry
    for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) {
      this.words[i] -= 0x4000000;
      if (i === this.length - 1)
        this.words[i + 1] = 1;
      else
        this.words[i + 1]++;
    }
    this.length = Math.max(this.length, i + 1);
  
    return this;
  };
  
  // Subtract plain number `num` from `this`
  BN.prototype.isubn = function isubn(num) {
    assert(typeof num === 'number');
    if (num < 0)
      return this.iaddn(-num);
  
    if (this.sign) {
      this.sign = false;
      this.iaddn(num);
      this.sign = true;
      return this;
    }
  
    this.words[0] -= num;
  
    // Carry
    for (var i = 0; i < this.length && this.words[i] < 0; i++) {
      this.words[i] += 0x4000000;
      this.words[i + 1] -= 1;
    }
  
    return this.strip();
  };
  
  BN.prototype.addn = function addn(num) {
    return this.clone().iaddn(num);
  };
  
  BN.prototype.subn = function subn(num) {
    return this.clone().isubn(num);
  };
  
  BN.prototype.iabs = function iabs() {
    this.sign = false;
  
    return this
  };
  
  BN.prototype.abs = function abs() {
    return this.clone().iabs();
  };
  
  BN.prototype._wordDiv = function _wordDiv(num, mode) {
    var shift = this.length - num.length;
  
    var a = this.clone();
    var b = num;
  
    var q = mode !== 'mod' && new BN(0);
    var sign = false;
  
    // Approximate quotient at each step
    while (a.length > b.length) {
      // NOTE: a.length is always >= 2, because of the condition .div()
      var hi = a.words[a.length - 1] * 0x4000000 + a.words[a.length - 2];
      var sq = (hi / b.words[b.length - 1]);
      var sqhi = (sq / 0x4000000) | 0;
      var sqlo = sq & 0x3ffffff;
      sq = new BN(null);
      sq.words = [ sqlo, sqhi ];
      sq.length = 2;
  
      // Collect quotient
      var shift = (a.length - b.length - 1) * 26;
      if (q) {
        var t = sq.shln(shift);
        if (a.sign)
          q.isub(t);
        else
          q.iadd(t);
      }
  
      sq = sq.mul(b).ishln(shift);
      if (a.sign)
        a.iadd(sq)
      else
        a.isub(sq);
    }
    // At this point a.length <= b.length
    while (a.ucmp(b) >= 0) {
      // NOTE: a.length is always >= 2, because of the condition above
      var hi = a.words[a.length - 1];
      var sq = new BN((hi / b.words[b.length - 1]) | 0);
      var shift = (a.length - b.length) * 26;
  
      if (q) {
        var t = sq.shln(shift);
        if (a.sign)
          q.isub(t);
        else
          q.iadd(t);
      }
  
      sq = sq.mul(b).ishln(shift);
  
      if (a.sign)
        a.iadd(sq);
      else
        a.isub(sq);
    }
  
    if (a.sign) {
      if (q)
        q.isubn(1);
      a.iadd(b);
    }
    return { div: q ? q : null, mod: a };
  };
  
  BN.prototype.divmod = function divmod(num, mode) {
    assert(num.cmpn(0) !== 0);
  
    if (this.sign && !num.sign) {
      var res = this.neg().divmod(num, mode);
      var div;
      var mod;
      if (mode !== 'mod')
        div = res.div.neg();
      if (mode !== 'div')
        mod = res.mod.cmpn(0) === 0 ? res.mod : num.sub(res.mod);
      return {
        div: div,
        mod: mod
      };
    } else if (!this.sign && num.sign) {
      var res = this.divmod(num.neg(), mode);
      var div;
      if (mode !== 'mod')
        div = res.div.neg();
      return { div: div, mod: res.mod };
    } else if (this.sign && num.sign) {
      return this.neg().divmod(num.neg(), mode);
    }
  
    // Both numbers are positive at this point
  
    // Strip both numbers to approximate shift value
    if (num.length > this.length || this.cmp(num) < 0)
      return { div: new BN(0), mod: this };
  
    // Very short reduction
    if (num.length === 1) {
      if (mode === 'div')
        return { div: this.divn(num.words[0]), mod: null };
      else if (mode === 'mod')
        return { div: null, mod: new BN(this.modn(num.words[0])) };
      return {
        div: this.divn(num.words[0]),
        mod: new BN(this.modn(num.words[0]))
      };
    }
  
    return this._wordDiv(num, mode);
  };
  
  // Find `this` / `num`
  BN.prototype.div = function div(num) {
    return this.divmod(num, 'div').div;
  };
  
  // Find `this` % `num`
  BN.prototype.mod = function mod(num) {
    return this.divmod(num, 'mod').mod;
  };
  
  // Find Round(`this` / `num`)
  BN.prototype.divRound = function divRound(num) {
    var dm = this.divmod(num);
  
    // Fast case - exact division
    if (dm.mod.cmpn(0) === 0)
      return dm.div;
  
    var mod = dm.div.sign ? dm.mod.isub(num) : dm.mod;
  
    var half = num.shrn(1);
    var r2 = num.andln(1);
    var cmp = mod.cmp(half);
  
    // Round down
    if (cmp < 0 || r2 === 1 && cmp === 0)
      return dm.div;
  
    // Round up
    return dm.div.sign ? dm.div.isubn(1) : dm.div.iaddn(1);
  };
  
  BN.prototype.modn = function modn(num) {
    assert(num <= 0x3ffffff);
    var p = (1 << 26) % num;
  
    var acc = 0;
    for (var i = this.length - 1; i >= 0; i--)
      acc = (p * acc + this.words[i]) % num;
  
    return acc;
  };
  
  // In-place division by number
  BN.prototype.idivn = function idivn(num) {
    assert(num <= 0x3ffffff);
  
    var carry = 0;
    for (var i = this.length - 1; i >= 0; i--) {
      var w = this.words[i] + carry * 0x4000000;
      this.words[i] = (w / num) | 0;
      carry = w % num;
    }
  
    return this.strip();
  };
  
  BN.prototype.divn = function divn(num) {
    return this.clone().idivn(num);
  };
  
  BN.prototype._egcd = function _egcd(x1, p) {
    assert(!p.sign);
    assert(p.cmpn(0) !== 0);
  
    var a = this;
    var b = p.clone();
  
    if (a.sign)
      a = a.mod(p);
    else
      a = a.clone();
  
    var x2 = new BN(0);
    while (b.isEven())
      b.ishrn(1);
    var delta = b.clone();
    while (a.cmpn(1) > 0 && b.cmpn(1) > 0) {
      while (a.isEven()) {
        a.ishrn(1);
        if (x1.isEven())
          x1.ishrn(1);
        else
          x1.iadd(delta).ishrn(1);
      }
      while (b.isEven()) {
        b.ishrn(1);
        if (x2.isEven())
          x2.ishrn(1);
        else
          x2.iadd(delta).ishrn(1);
      }
      if (a.cmp(b) >= 0) {
        a.isub(b);
        x1.isub(x2);
      } else {
        b.isub(a);
        x2.isub(x1);
      }
    }
    if (a.cmpn(1) === 0)
      return x1;
    else
      return x2;
  };
  
  BN.prototype.gcd = function gcd(num) {
    if (this.cmpn(0) === 0)
      return num.clone();
    if (num.cmpn(0) === 0)
      return this.clone();
  
    var a = this.clone();
    var b = num.clone();
    a.sign = false;
    b.sign = false;
  
    // Remove common factor of two
    for (var shift = 0; a.isEven() && b.isEven(); shift++) {
      a.ishrn(1);
      b.ishrn(1);
    }
  
    while (a.isEven())
      a.ishrn(1);
  
    do {
      while (b.isEven())
        b.ishrn(1);
  
      // Swap `a` and `b` to make `a` always bigger than `b`
      if (a.cmp(b) < 0) {
        var t = a;
        a = b;
        b = t;
      }
      a.isub(a.div(b).mul(b));
    } while (a.cmpn(0) !== 0 && b.cmpn(0) !== 0);
    if (a.cmpn(0) === 0)
      return b.ishln(shift);
    else
      return a.ishln(shift);
  };
  
  // Invert number in the field F(num)
  BN.prototype.invm = function invm(num) {
    return this._egcd(new BN(1), num).mod(num);
  };
  
  BN.prototype.isEven = function isEven(num) {
    return (this.words[0] & 1) === 0;
  };
  
  BN.prototype.isOdd = function isOdd(num) {
    return (this.words[0] & 1) === 1;
  };
  
  // And first word and num
  BN.prototype.andln = function andln(num) {
    return this.words[0] & num;
  };
  
  // Increment at the bit position in-line
  BN.prototype.bincn = function bincn(bit) {
    assert(typeof bit === 'number');
    var r = bit % 26;
    var s = (bit - r) / 26;
    var q = 1 << r;
  
    // Fast case: bit is much higher than all existing words
    if (this.length <= s) {
      for (var i = this.length; i < s + 1; i++)
        this.words[i] = 0;
      this.words[s] |= q;
      this.length = s + 1;
      return this;
    }
  
    // Add bit and propagate, if needed
    var carry = q;
    for (var i = s; carry !== 0 && i < this.length; i++) {
      var w = this.words[i];
      w += carry;
      carry = w >>> 26;
      w &= 0x3ffffff;
      this.words[i] = w;
    }
    if (carry !== 0) {
      this.words[i] = carry;
      this.length++;
    }
    return this;
  };
  
  BN.prototype.cmpn = function cmpn(num) {
    var sign = num < 0;
    if (sign)
      num = -num;
  
    if (this.sign && !sign)
      return -1;
    else if (!this.sign && sign)
      return 1;
  
    num &= 0x3ffffff;
    this.strip();
  
    var res;
    if (this.length > 1) {
      res = 1;
    } else {
      var w = this.words[0];
      res = w === num ? 0 : w < num ? -1 : 1;
    }
    if (this.sign)
      res = -res;
    return res;
  };
  
  // Compare two numbers and return:
  // 1 - if `this` > `num`
  // 0 - if `this` == `num`
  // -1 - if `this` < `num`
  BN.prototype.cmp = function cmp(num) {
    if (this.sign && !num.sign)
      return -1;
    else if (!this.sign && num.sign)
      return 1;
  
    var res = this.ucmp(num);
    if (this.sign)
      return -res;
    else
      return res;
  };
  
  // Unsigned comparison
  BN.prototype.ucmp = function ucmp(num) {
    // At this point both numbers have the same sign
    if (this.length > num.length)
      return 1;
    else if (this.length < num.length)
      return -1;
  
    var res = 0;
    for (var i = this.length - 1; i >= 0; i--) {
      var a = this.words[i];
      var b = num.words[i];
  
      if (a === b)
        continue;
      if (a < b)
        res = -1;
      else if (a > b)
        res = 1;
      break;
    }
    return res;
  };
  
  //
  // A reduce context, could be using montgomery or something better, depending
  // on the `m` itself.
  //
  BN.red = function red(num) {
    return new Red(num);
  };
  
  BN.prototype.toRed = function toRed(ctx) {
    assert(!this.red, 'Already a number in reduction context');
    assert(!this.sign, 'red works only with positives');
    return ctx.convertTo(this)._forceRed(ctx);
  };
  
  BN.prototype.fromRed = function fromRed() {
    assert(this.red, 'fromRed works only with numbers in reduction context');
    return this.red.convertFrom(this);
  };
  
  BN.prototype._forceRed = function _forceRed(ctx) {
    this.red = ctx;
    return this;
  };
  
  BN.prototype.forceRed = function forceRed(ctx) {
    assert(!this.red, 'Already a number in reduction context');
    return this._forceRed(ctx);
  };
  
  BN.prototype.redAdd = function redAdd(num) {
    assert(this.red, 'redAdd works only with red numbers');
    return this.red.add(this, num);
  };
  
  BN.prototype.redIAdd = function redIAdd(num) {
    assert(this.red, 'redIAdd works only with red numbers');
    return this.red.iadd(this, num);
  };
  
  BN.prototype.redSub = function redSub(num) {
    assert(this.red, 'redSub works only with red numbers');
    return this.red.sub(this, num);
  };
  
  BN.prototype.redISub = function redISub(num) {
    assert(this.red, 'redISub works only with red numbers');
    return this.red.isub(this, num);
  };
  
  BN.prototype.redShl = function redShl(num) {
    assert(this.red, 'redShl works only with red numbers');
    return this.red.shl(this, num);
  };
  
  BN.prototype.redMul = function redMul(num) {
    assert(this.red, 'redMul works only with red numbers');
    this.red._verify2(this, num);
    return this.red.mul(this, num);
  };
  
  BN.prototype.redIMul = function redIMul(num) {
    assert(this.red, 'redMul works only with red numbers');
    this.red._verify2(this, num);
    return this.red.imul(this, num);
  };
  
  BN.prototype.redSqr = function redSqr() {
    assert(this.red, 'redSqr works only with red numbers');
    this.red._verify1(this);
    return this.red.sqr(this);
  };
  
  BN.prototype.redISqr = function redISqr() {
    assert(this.red, 'redISqr works only with red numbers');
    this.red._verify1(this);
    return this.red.isqr(this);
  };
  
  // Square root over p
  BN.prototype.redSqrt = function redSqrt() {
    assert(this.red, 'redSqrt works only with red numbers');
    this.red._verify1(this);
    return this.red.sqrt(this);
  };
  
  BN.prototype.redInvm = function redInvm() {
    assert(this.red, 'redInvm works only with red numbers');
    this.red._verify1(this);
    return this.red.invm(this);
  };
  
  // Return negative clone of `this` % `red modulo`
  BN.prototype.redNeg = function redNeg() {
    assert(this.red, 'redNeg works only with red numbers');
    this.red._verify1(this);
    return this.red.neg(this);
  };
  
  BN.prototype.redPow = function redPow(num) {
    assert(this.red && !num.red, 'redPow(normalNum)');
    this.red._verify1(this);
    return this.red.pow(this, num);
  };
  
  // Prime numbers with efficient reduction
  var primes = {
    k256: null,
    p224: null,
    p192: null,
    p25519: null
  };
  
  // Pseudo-Mersenne prime
  function MPrime(name, p) {
    // P = 2 ^ N - K
    this.name = name;
    this.p = new BN(p, 16);
    this.n = this.p.bitLength();
    this.k = new BN(1).ishln(this.n).isub(this.p);
  
    this.tmp = this._tmp();
  }
  
  MPrime.prototype._tmp = function _tmp() {
    var tmp = new BN(null);
    tmp.words = new Array(Math.ceil(this.n / 13));
    return tmp;
  };
  
  MPrime.prototype.ireduce = function ireduce(num) {
    // Assumes that `num` is less than `P^2`
    // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P)
    var r = num;
    var rlen;
  
    do {
      var pair = r.ishrn(this.n, 0, this.tmp);
      r = this.imulK(pair.hi);
      r = r.iadd(pair.lo);
      rlen = r.bitLength();
    } while (rlen > this.n);
  
    var cmp = rlen < this.n ? -1 : r.cmp(this.p);
    if (cmp === 0) {
      r.words[0] = 0;
      r.length = 1;
    } else if (cmp > 0) {
      r.isub(this.p);
    } else {
      r.strip();
    }
  
    return r;
  };
  
  MPrime.prototype.imulK = function imulK(num) {
    return num.imul(this.k);
  };
  
  function K256() {
    MPrime.call(
      this,
      'k256',
      'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f');
  }
  inherits(K256, MPrime);
  
  K256.prototype.imulK = function imulK(num) {
    // K = 0x1000003d1 = [ 0x40, 0x3d1 ]
    num.words[num.length] = 0;
    num.words[num.length + 1] = 0;
    num.length += 2;
    for (var i = num.length - 3; i >= 0; i--) {
      var w = num.words[i];
      var hi = w * 0x40;
      var lo = w * 0x3d1;
      hi += (lo / 0x4000000) | 0;
      var uhi = (hi / 0x4000000) | 0;
      hi &= 0x3ffffff;
      lo &= 0x3ffffff;
  
      num.words[i + 2] += uhi;
      num.words[i + 1] += hi;
      num.words[i] = lo;
    }
    var w = num.words[num.length - 2];
    if (w >= 0x4000000) {
      num.words[num.length - 1] += w >>> 26;
      num.words[num.length - 2] = w & 0x3ffffff;
    }
    if (num.words[num.length - 1] === 0)
      num.length--;
    if (num.words[num.length - 1] === 0)
      num.length--;
    return num;
  };
  
  function P224() {
    MPrime.call(
      this,
      'p224',
      'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001');
  }
  inherits(P224, MPrime);
  
  function P192() {
    MPrime.call(
      this,
      'p192',
      'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff');
  }
  inherits(P192, MPrime);
  
  function P25519() {
    // 2 ^ 255 - 19
    MPrime.call(
      this,
      '25519',
      '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed');
  }
  inherits(P25519, MPrime);
  
  P25519.prototype.imulK = function imulK(num) {
    // K = 0x13
    var carry = 0;
    for (var i = 0; i < num.length; i++) {
      var hi = num.words[i] * 0x13 + carry;
      var lo = hi & 0x3ffffff;
      hi >>>= 26;
  
      num.words[i] = lo;
      carry = hi;
    }
    if (carry !== 0)
      num.words[num.length++] = carry;
    return num;
  };
  
  // Exported mostly for testing purposes, use plain name instead
  BN._prime = function prime(name) {
    // Cached version of prime
    if (primes[name])
      return primes[name];
  
    var prime;
    if (name === 'k256')
      prime = new K256();
    else if (name === 'p224')
      prime = new P224();
    else if (name === 'p192')
      prime = new P192();
    else if (name === 'p25519')
      prime = new P25519();
    else
      throw new Error('Unknown prime ' + name);
    primes[name] = prime;
  
    return prime;
  }
  
  //
  // Base reduction engine
  //
  function Red(m) {
    if (typeof m === 'string') {
      var prime = BN._prime(m);
      this.m = prime.p;
      this.prime = prime;
    } else {
      this.m = m;
      this.prime = null;
    }
  }
  
  Red.prototype._verify1 = function _verify1(a) {
    assert(!a.sign, 'red works only with positives');
    assert(a.red, 'red works only with red numbers');
  };
  
  Red.prototype._verify2 = function _verify2(a, b) {
    assert(!a.sign && !b.sign, 'red works only with positives');
    assert(a.red && a.red === b.red,
           'red works only with red numbers');
  };
  
  Red.prototype.imod = function imod(a) {
    if (this.prime)
      return this.prime.ireduce(a)._forceRed(this);
    return a.mod(this.m)._forceRed(this);
  };
  
  Red.prototype.neg = function neg(a) {
    var r = a.clone();
    r.sign = !r.sign;
    return r.iadd(this.m)._forceRed(this);
  };
  
  Red.prototype.add = function add(a, b) {
    this._verify2(a, b);
  
    var res = a.add(b);
    if (res.cmp(this.m) >= 0)
      res.isub(this.m);
    return res._forceRed(this);
  };
  
  Red.prototype.iadd = function iadd(a, b) {
    this._verify2(a, b);
  
    var res = a.iadd(b);
    if (res.cmp(this.m) >= 0)
      res.isub(this.m);
    return res;
  };
  
  Red.prototype.sub = function sub(a, b) {
    this._verify2(a, b);
  
    var res = a.sub(b);
    if (res.cmpn(0) < 0)
      res.iadd(this.m);
    return res._forceRed(this);
  };
  
  Red.prototype.isub = function isub(a, b) {
    this._verify2(a, b);
  
    var res = a.isub(b);
    if (res.cmpn(0) < 0)
      res.iadd(this.m);
    return res;
  };
  
  Red.prototype.shl = function shl(a, num) {
    this._verify1(a);
    return this.imod(a.shln(num));
  };
  
  Red.prototype.imul = function imul(a, b) {
    this._verify2(a, b);
    return this.imod(a.imul(b));
  };
  
  Red.prototype.mul = function mul(a, b) {
    this._verify2(a, b);
    return this.imod(a.mul(b));
  };
  
  Red.prototype.isqr = function isqr(a) {
    return this.imul(a, a);
  };
  
  Red.prototype.sqr = function sqr(a) {
    return this.mul(a, a);
  };
  
  Red.prototype.sqrt = function sqrt(a) {
    if (a.cmpn(0) === 0)
      return a.clone();
  
    var mod3 = this.m.andln(3);
    assert(mod3 % 2 === 1);
  
    // Fast case
    if (mod3 === 3) {
      var pow = this.m.add(new BN(1)).ishrn(2);
      var r = this.pow(a, pow);
      return r;
    }
  
    // Tonelli-Shanks algorithm (Totally unoptimized and slow)
    //
    // Find Q and S, that Q * 2 ^ S = (P - 1)
    var q = this.m.subn(1);
    var s = 0;
    while (q.cmpn(0) !== 0 && q.andln(1) === 0) {
      s++;
      q.ishrn(1);
    }
    assert(q.cmpn(0) !== 0);
  
    var one = new BN(1).toRed(this);
    var nOne = one.redNeg();
  
    // Find quadratic non-residue
    // NOTE: Max is such because of generalized Riemann hypothesis.
    var lpow = this.m.subn(1).ishrn(1);
    var z = this.m.bitLength();
    z = new BN(2 * z * z).toRed(this);
    while (this.pow(z, lpow).cmp(nOne) !== 0)
      z.redIAdd(nOne);
  
    var c = this.pow(z, q);
    var r = this.pow(a, q.addn(1).ishrn(1));
    var t = this.pow(a, q);
    var m = s;
    while (t.cmp(one) !== 0) {
      var tmp = t;
      for (var i = 0; tmp.cmp(one) !== 0; i++)
        tmp = tmp.redSqr();
      assert(i < m);
      var b = this.pow(c, new BN(1).ishln(m - i - 1));
  
      r = r.redMul(b);
      c = b.redSqr();
      t = t.redMul(c);
      m = i;
    }
  
    return r;
  };
  
  Red.prototype.invm = function invm(a) {
    var inv = a._egcd(new BN(1), this.m);
    if (inv.sign) {
      inv.sign = false;
      return this.imod(inv).redNeg();
    } else {
      return this.imod(inv);
    }
  };
  
  Red.prototype.pow = function pow(a, num) {
    var w = [];
    var q = num.clone();
    while (q.cmpn(0) !== 0) {
      w.push(q.andln(1));
      q.ishrn(1);
    }
  
    // Skip leading zeroes
    var res = a;
    for (var i = 0; i < w.length; i++, res = this.sqr(res))
      if (w[i] !== 0)
        break;
  
    if (++i < w.length) {
      for (var q = this.sqr(res); i < w.length; i++, q = this.sqr(q)) {
        if (w[i] === 0)
          continue;
        res = this.mul(res, q);
      }
    }
  
    return res;
  };
  
  Red.prototype.convertTo = function convertTo(num) {
    return num.clone();
  };
  
  Red.prototype.convertFrom = function convertFrom(num) {
    var res = num.clone();
    res.red = null;
    return res;
  };
  
  //
  // Montgomery method engine
  //
  
  BN.mont = function mont(num) {
    return new Mont(num);
  };
  
  function Mont(m) {
    Red.call(this, m);
  
    this.shift = this.m.bitLength();
    if (this.shift % 26 !== 0)
      this.shift += 26 - (this.shift % 26);
    this.r = new BN(1).ishln(this.shift);
    this.r2 = this.imod(this.r.sqr());
    this.rinv = this.r.invm(this.m);
  
    // TODO(indutny): simplify it
    this.minv = this.rinv.mul(this.r)
                         .sub(new BN(1))
                         .div(this.m)
                         .neg()
                         .mod(this.r);
  }
  inherits(Mont, Red);
  
  Mont.prototype.convertTo = function convertTo(num) {
    return this.imod(num.shln(this.shift));
  };
  
  Mont.prototype.convertFrom = function convertFrom(num) {
    var r = this.imod(num.mul(this.rinv));
    r.red = null;
    return r;
  };
  
  Mont.prototype.imul = function imul(a, b) {
    if (a.cmpn(0) === 0 || b.cmpn(0) === 0) {
      a.words[0] = 0;
      a.length = 1;
      return a;
    }
  
    var t = a.imul(b);
    var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m);
    var u = t.isub(c).ishrn(this.shift);
    var res = u;
    if (u.cmp(this.m) >= 0)
      res = u.isub(this.m);
    else if (u.cmpn(0) < 0)
      res = u.iadd(this.m);
  
    return res._forceRed(this);
  };
  
  Mont.prototype.mul = function mul(a, b) {
    if (a.cmpn(0) === 0 || b.cmpn(0) === 0)
      return new BN(0)._forceRed(this);
  
    var t = a.mul(b);
    var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m);
    var u = t.isub(c).ishrn(this.shift);
    var res = u;
    if (u.cmp(this.m) >= 0)
      res = u.isub(this.m);
    else if (u.cmpn(0) < 0)
      res = u.iadd(this.m);
  
    return res._forceRed(this);
  };
  
  Mont.prototype.invm = function invm(a) {
    // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R
    var res = this.imod(a.invm(this.m).mul(this.r2));
    return res._forceRed(this);
  };
  